commit f4c020391466f0a7b61bfd1ec7a62667f6c3eb5b Author: Darrin Massena Date: Sun Jul 6 17:47:28 2014 -0700 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..afa54b4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +*.pidb +*.userprefs +*/bin/ +*/*/bin/ +/game/wi.xcodeproj/project.xcworkspace/ +/game/wi.xcodeproj/xcuserdata/ +/DerivedData/ diff --git a/AniMax/.cvsignore b/AniMax/.cvsignore new file mode 100644 index 0000000..ba077a4 --- /dev/null +++ b/AniMax/.cvsignore @@ -0,0 +1 @@ +bin diff --git a/AniMax/AboutForm.cs b/AniMax/AboutForm.cs new file mode 100644 index 0000000..5301876 --- /dev/null +++ b/AniMax/AboutForm.cs @@ -0,0 +1,204 @@ +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; +using System.Drawing.Imaging; + +namespace SpiffCode +{ + /// + /// Summary description for AboutForm. + /// + public class AboutForm : Form + { + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.Label label6; + private System.Windows.Forms.Panel panel2; + private System.Windows.Forms.Button btnOK; + private System.Windows.Forms.Button btnSystemInfo; + private System.Windows.Forms.PictureBox pictureBox1; + private SpiffCode.ScBorder scBorder1; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + public AboutForm() + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + +// SetBitmap(new Bitmap(@"c:\code\ht\animax\shp.png")); + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(AboutForm)); + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.label5 = new System.Windows.Forms.Label(); + this.label6 = new System.Windows.Forms.Label(); + this.panel2 = new System.Windows.Forms.Panel(); + this.btnOK = new System.Windows.Forms.Button(); + this.btnSystemInfo = new System.Windows.Forms.Button(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.scBorder1 = new SpiffCode.ScBorder(); + this.panel2.SuspendLayout(); + this.SuspendLayout(); + // + // label1 + // + this.label1.Location = new System.Drawing.Point(112, 8); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(200, 16); + this.label1.TabIndex = 1; + this.label1.Text = "SpiffCode AniMax 2002 (Version 0.31)"; + // + // label2 + // + this.label2.Location = new System.Drawing.Point(112, 24); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(336, 16); + this.label2.TabIndex = 2; + this.label2.Text = "Copyright © 2002 SpiffCode Incorporated. All Rights Reserved."; + // + // label3 + // + this.label3.Location = new System.Drawing.Point(112, 56); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(168, 16); + this.label3.TabIndex = 3; + this.label3.Text = "This program is licensed to:"; + // + // label4 + // + this.label4.Location = new System.Drawing.Point(8, 8); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(100, 16); + this.label4.TabIndex = 0; + this.label4.Text = "Mark Soderwall"; + // + // label5 + // + this.label5.Location = new System.Drawing.Point(8, 24); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(128, 16); + this.label5.TabIndex = 1; + this.label5.Text = "Extreme Illustrations"; + // + // label6 + // + this.label6.Location = new System.Drawing.Point(16, 200); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(352, 64); + this.label6.TabIndex = 5; + this.label6.Text = @"Warning: This computer program is protected by copyright law and international treaties. Unauthorized reproduction or distribution of this program, or any portion of it, may result in severe civil and criminal penalties, and will be prosecuted to the maximum extent possible under the law."; + this.label6.UseMnemonic = false; + // + // panel2 + // + this.panel2.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.panel2.Controls.AddRange(new System.Windows.Forms.Control[] { + this.label5, + this.label4}); + this.panel2.Location = new System.Drawing.Point(112, 72); + this.panel2.Name = "panel2"; + this.panel2.Size = new System.Drawing.Size(352, 48); + this.panel2.TabIndex = 4; + // + // btnOK + // + this.btnOK.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnOK.FlatStyle = System.Windows.Forms.FlatStyle.System; + this.btnOK.Location = new System.Drawing.Point(376, 203); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(88, 23); + this.btnOK.TabIndex = 6; + this.btnOK.Text = "OK"; + // + // btnSystemInfo + // + this.btnSystemInfo.FlatStyle = System.Windows.Forms.FlatStyle.System; + this.btnSystemInfo.Location = new System.Drawing.Point(376, 234); + this.btnSystemInfo.Name = "btnSystemInfo"; + this.btnSystemInfo.Size = new System.Drawing.Size(88, 23); + this.btnSystemInfo.TabIndex = 7; + this.btnSystemInfo.Text = "&System Info..."; + // + // pictureBox1 + // + this.pictureBox1.Image = ((System.Drawing.Bitmap)(resources.GetObject("pictureBox1.Image"))); + this.pictureBox1.Location = new System.Drawing.Point(-24, 0); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(168, 192); + this.pictureBox1.TabIndex = 8; + this.pictureBox1.TabStop = false; + // + // scBorder1 + // + this.scBorder1.Location = new System.Drawing.Point(16, 190); + this.scBorder1.Name = "scBorder1"; + this.scBorder1.Size = new System.Drawing.Size(448, 3); + this.scBorder1.TabIndex = 9; + this.scBorder1.Text = "scBorder1"; + // + // AboutForm + // + this.AcceptButton = this.btnOK; + this.AutoScale = false; + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.BackColor = System.Drawing.Color.FromArgb(((System.Byte)(200)), ((System.Byte)(204)), ((System.Byte)(206))); + this.CancelButton = this.btnOK; + this.ClientSize = new System.Drawing.Size(480, 272); + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.scBorder1, + this.btnSystemInfo, + this.btnOK, + this.label6, + this.panel2, + this.label3, + this.label2, + this.label1, + this.pictureBox1}); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "AboutForm"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "About SpiffCode AniMax"; + this.panel2.ResumeLayout(false); + this.ResumeLayout(false); + + } + #endregion + } +} diff --git a/AniMax/AboutForm.resources b/AniMax/AboutForm.resources new file mode 100644 index 0000000..3e84c20 Binary files /dev/null and b/AniMax/AboutForm.resources differ diff --git a/AniMax/AboutForm.resx b/AniMax/AboutForm.resx new file mode 100644 index 0000000..2f37599 --- /dev/null +++ b/AniMax/AboutForm.resx @@ -0,0 +1,879 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + iVBORw0KGgoAAAANSUhEUgAAAMcAAADvCAIAAADJmFfKAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAlwSFlzAAALEwAA + CxMBAJqcGAAAtHZJREFUeF7svXV01Ff3PfyUUqDUqTvurnF3d3d3m7hM3N3d3d3d3UkgWKEUlwjy/a33 + r3ff+YSUUqB9nhoFuu6aNQ0hJJM955x7ztl7vzEwMvaf1/+9fgX+3FcAqPqLTv/wKE7f0Ejv4HDPwBAO + nuDgI/j4X/SPvv6yL8Ir8J+/4psAaICe7r6B9u7e5vbOhpa2huYWnKa29paOro6evu7+wdfY+ite+Rfk + a/7JqKKCE/DU1tXd2NpeU99YUVVTWllVWlFVXlVdWVtf19Tc3NbR3t2DzwHyXgetFwQHf+638WeiChBB + mkMoQkwCnsqqqotKywuKSwpLSotKy8oqKiura2oaGhpaWlo7Ojt7+3sGfhGxqIxJglz/YFffAL4ODj6N + wt/r2Pbn/uL/0q/2p6FqJUTVN7cgLBUWl+bk5Wfn5eXmF+QXFRWXlZdVVlbW1FTXI1w1tQBV3b09/YP9 + w6TGWim/gCGkSGTM2sbm6roGHDxBzGvt7MYfvcbWXwqFP/GL/zmooiAFQNQ2NAFA2Xn5GVnZ6VlZOfn5 + RWVl2bm50VHhQf4+sTFRRaUlTa2trZ2IVX29A0DVMqSopNnQ0lpVV48gV1JWUVxeXlpRWVFdU1vf2NiC + vwJg9SEWvg5af+Kv/y/6Un8CqoAM5CwKUkVl5Vm5eWkZmRnZ2QVFRXkF+R4ONiFqXO0W3ON23E0WPP5q + vLFR4R3d3f3DwwMjuAkSVCHfoahHWCqvRtIsQ9IsKCpG3iwpryivrqmuq0f8Q3jrQDX235f5K4Hw8cSK + 3Ip/9HVufUFRRVVCCDPABKIUIJWakZGZk1NQXBwfF+OiyDPiJHw/ROYBTqDEAz/he178Jcb8yQkxY1NT + IxMTg6NjqK6ASEAHiMwrKETSRIRD0iwqLS0tLydJs7aurrGxua2d1Pj9A78zVlFgQmxD6kScQxrFRQHV + HmIhzkpufX0n/SuA9UdjFX5z+LWhEkLaQiGVlpkJSOXk57rZWSTp8f3gJ/swSvVhpMrDYJkHfiIPvPju + e3AtenBF6Ip0dHcBWIhVnT29NfUNiExZeXk4eUVFJeXlKalp9pb2egq6jtaOmdnZNfX1bZ1dnb29QBVK + MUaQe2abbfke2j/Y3tVD3UPxvQHxuDogEFKJFR+prKlFbiVRsL2jo7v39Z30T4TXH0IVdelD8qqqrc8v + Ks7KySXleUGerb56kZnw9RDFB7HaD2M0H4QpP/ATv+/Gfd+V7b4Ly1039k5boZLigvHp6d7BoabWNvya + SYjKywOeCktKlAWVYyX8+0TTTwpk9YqkRUv4mqmbtnd19Q8N4yC8DY6OP+slWLmHAk8ISKjP8gqLUOfh + IBDiiwNVZZVVSKxVtbVAc11jU2NrWyuB7JN30j/xVX7VvtQfQhUCFQoUvN1Lyitx10PmKi4rc7UxLbMQ + vhOh+jBB72GczoNwlQe+Eved2e9ZH7htfei67bFrjqxjjgJZ6Smjk5MIVLUNDfhNI+WVV1XFxSeZSFt2 + ymYtcOcu4rBlLzBlzh9LrxGLt9K26h8aYuTNyeHxiaGxJ4FF5WJUS6TCa2xGhwx4ysjOQfjEvSErNxf/ + BFCLfwVZFQeoqq6rw79OdToQrlbupK8aCP70n/d/RxVVpKOlWVlTV1hckltQQDJXSlKKvsD1UKWHCboE + UohSnsL3bE/MG+04r7t9Rn/vKbND52hMLdYCxUUFQ6OjbZ0o0hvJr7m2JjQswkzZqVO1cFGggByO3IVj + mQsH0+b3p9zdnxInF1hZWTUxfXJscmp0YnJoDDXZMrAeb702NLfi+0G9n5mTm5KenpyaClQhCqLOyy3I + T81I8w3xd/Nzt3N38Azwjk2Kr6iuam5rY6RXXDDJnfRPf4lfwS/4v6MKgQoDmbqmFuQvqriurK62leM5 + 7yf7IFbrYaw2A1KC96yP3DTYPqOxpV9te5/2njHjwzM2zOG6oq3tbX1Dg0BVc3tbc1urf0CIlb5fk1HF + XfHiReGiRZ78hRPZ87tS5rckzm+Jv7UroUcoNdg7eGpmhgLW8PgkULVSkuM7oVIe6qec/IK0rCxACveG + bPTLCgui4mPk9OWl7KVVYtS0S/R1a4216wzVK3WV0lUl7KXCYiI6e3qQW1GuPT+9voL4+N9+5P8RVY8F + qlpGL6C4qqYmLiai3kbkfpQaqaVClR54Cd+zOXpDn0CqR2Vrq9rObt0Do+bH8w1546LCRsbH+gcHe/r7 + Wjva7Z08bG1j620brsmXLwoXL3DlLxzKnN+UdHdD9PWPw698E3FlW9QUZ5qLmdPJU6cmZ2bHp0+OTEwB + VUh5SMFUVwIpj9R26GtkZqVnZiE+FZYUp2VnSOvJKPgoGRQb2/Q62A07Ww85mg3YGvZb6vaaaHUbaHTp + yYUrxSTFIbciIyO3Dj8WBf+31/T13/ofUYVfJ25Y+F2ipQRIoQVQU1fnqsR7PUjuQaTagzClBz5iSHy3 + DbfNMiDVrLqzVWt/l9GxFG0uLyebgeGhsckJAKt3oM/V3dfJNbXUvfWKciWBFFvuwr70u1/F314TdmGt + 7/SHXie/9D27NaSHPzHEJ2Tm9Omp2VMIV6iugGy0DOqbm8urapDycH9cbr3m5RWXlxWUFNHcbGWcZY2K + TR36XZxG3R1G6DZDzuYDdoZ9VlrdxiqdusqdOiqd2qptWvKuij0D/bg9jJIo+HNufY2P/+0V+F9QRQJV + 3wCGfWhako5lcTHK3uiI0DZbEWS9h4CUnyTK8yXj7We0tvarbm1R2dGitb9O/3iwGleov1dPf+/UzPT0 + 7MnJk1Oh4dEu9JQiv46L6tWLIsULrLnzu9PufBZ7e3XomdVew2+79G5wHvrG49S24BTp4PrGpplTp6cQ + q6ZOoq5CVY4SCl0upLzM7ByqJEciRj0enRQraiKula5H63VwGXN3GfUApKyHnE0H7PR6LdS7jOTatSRb + VSRbleVa1JRaNNTSNQvKiidPnpyYmh6dmMIX/99ezdd/i3oF/mtUrbQ9UcQg9+GehSK9sqbaQ5n3ZqD0 + w3DFB0Gy99357lkeuKyzbVRta7vqjhbNfY0Gx/1UuKPCQhClpmdnZk7P4qD57uQUU+LTdl63dlG0eIE9 + b35P2p3PY2+/GXp2tffIOpf29+3aP3ca3uTVzhZpJKWHogoZcOLkDIoq9NmB6dyCQtzykO+Ap8Li4oqq + qvTsTCEdEbV4DasuW8dhOhWiaEMuZgP2Or0Wip16Eq1q/I1y7LXiXKViAqVSEpUK8nVq6sXaiRlJ00iv + J2fGJqdfh6s/+Pb4r1FFdRNw1UKRjl8q6vTqutpAL3qPHQKV4oNQ+Qc+ovfsme4abpvR3NqjtqNVfXer + 3pFIdc6wQL++wYGpmZMzp08BUp3d3bbmXhVuDT+pVy49DqlVBFLD65xb36M1fmrb+Z1Lx5EguqBFa0fH + LEl/syjVu/r6MR9ECwr3u4ysrLzCwvLqKvTJUEIphqqYNFvaDbsATHYjbjZDLib9tlrdprLt2oJN8uy1 + YiwlQkw5AkypfGzJArzpomJFsvJ1qhqZOkXlJYAsFQhfo+pvRdVykd7eidSDJjg1PC6tKA9Q470TxAhU + AVL33XiWzPde1Nk6rL6tXX1Xi86BMn0WEyXxlraWienJk6cQpU4Nj47YaNvU2ZRcU6kkUYozH7XUSpSi + IFX3qU3ztw6d+3xCuK2yMrJQUZ2cRZ0+jfZ6VW0d/mkq5aGEKiwttnC2lPGUM6oxow042Y+42g67Wg85 + GQNPPWaybVoCTXLs1aIs+YLM6fzMyXwsCfxs8fxciUKCmRLSZYqqzVrK7mqDI8OMog2oeh2r/uh68H8X + q6huAop0TD/QDc8rKEDScaeZDzuLPQxXIoHKW+Se3fFbBtumNbZ2qm5vUt/TqHfUQ4k7Ny8btTlqqdm5 + U6jTQ90Da/XTrimXL4ozILU//c4XcUh851b7DK91bnmPVvOJdcO39p27PePYaWG+IeNTU4DUxMmT6MVX + 1zcA0NiDQJgEoEOiQsWtJXXyDaz77O2GXXGAJ5MBgie5Dh0Sn2rEmAuECJ6S+Fjj+dnjBbgThXiTRIQy + JKWK5ZXq1RXDVeJSE2bPnDl5+jS5YE5Ov66r/r5YRRXpaAthiIbcxyiNS7Kzs6J1+OdD5QmqAqXvu3It + mew6pbGlXXFLufz2YpW9yRrMVoZaCFQj46OIUuOAlLNvvWr0HYXSRYkS0pc6mHn36wSqPO9f61jzrkXR + xyZFX5tXbbNPYbJ2MaQNjY5Mn5pFKY3eOpZiyFQHt86KivSsDHEDcbV4LfN2GwfgieQ7Z1I/9ZgrdugK + Nytx1IkxFwoxpfMxJ/KxxPFxxAlwxwsJJIkJpUqIp8vKZinKJStJ2cskpCVNz8wQVJ06DeCOvkbVH6Yy + /BexitpNQJFOjfyQgxAqrNWlp92lMEImRRUqKusjF7W3dihuyZPeEiexPUZun4kUZ2JSYntn+8TUxMjo + qJ+Zc5dy3B2FkiXJkkW+woUjmfMbk+68E3FxtR8SX837ljmfGqV/a5i/0yr+uLmhsHJXdzejR3VyYGQE + TfDq2tryysqSijIbuq2yn4pxpZn9oIvjKCmhLAcdDfusVToNxFpUOerFCZ5S+U7EcjNH8rBG8XHFCgqm + iItnyEiGy4hZSWpZ6/hHBOYW5Xf39Z4+e/bUmTMzp+dQVI29Tn9/GFL/xR2Q2oLCVh01CUagQkMhNjY6 + 1UhoMUrlIXpUuPq5cqFIH1Dekiu5OUhoi5vgdhvxo6ZGesWlRX2DfQNDA15aNt0acQsqFUuyZYtCRQsn + cua3pdzZEHPjzeDZNR5d7zlUfGqR951Z4R6baDZzfQFFLL+cOnMaEW5iaqq7v6+lra2ppTk5I1XBTlEv + yxBdA1zxHEfdUZKbDthr95jLt+sKNimwVYkyZwuciOY+HsBxwoud2YeTM5hPIEpULFhS1EwsPC4Ck6LZ + udMITgATHmfn5k4yIIU6HW2w1+nvD6a//wJVCFRoOYLOgJoGkMK1q6Ss1Eae74yv3MMYjQfhyoxAdfi0 + xpYSyY1+/BvNOTbpcGxXEOIICg6sra/G6rmHmuWgbvKiVvU9xXJy6WPJnd+RSlpTa8J+fNN/4m16xwaH + mm9syvfYx3PYmEppgowzd3bu9Nk5gqrpKbQkWtrbnfxctCK0LZptnEbozqMe9sMkRBn0Wal1GUu1avA3 + ybECUrjixfMyBXKxeHByeHDz+QiIeImKmoq5+dCxfoNkioMqbXqW3PjQSkCnHiGKGlo/Zxvij7/Wr85X + +F0ZEIGKLFE1tzK2h/NQKReXlUYEBxRbii1Fqz+M1ngQJIdAdcdwe7v8plD+7w2Zv5M+8r042wFhAd7k + lIT6xlpPA9qQefqCce099aolyVI00Od3pd39Mu7OO5G33gi+sNp34h1636cuTdvcojlt1flksNF+5vyZ + M+fOzp1FbjoFHNQ3Nag7aBgXmdoNODmNkkYUulBU10C+QxdZj7dBlqVWlKlIkDmNnzWajyOQl89XQNxX + XNxCjOZCa25rYTTJ0NQ4NT07uzJPHBmfwMIW7pVYCURbFQdvHtxIVtatXs+b/4c3w2+jimp7Uo1s9Dyp + lZKCokI3Zf4fAhUfxmo9iFAhgcrq0KzG5gzRjRas34of+Ib34CZhXnYRYSFJCQkrLcNWx+R7bq33TRuX + FCoWcenbQ2Yyd96NvL067MYbQSiq5t71GdsUnMXlb82vrSUolxAekx6fnJmYlp2cjif+7p62Pmb2NbbO + w+6OI+52jBCl32el3Gko2aoh0KTA1SDFWifKXCnMlC/Imk6qctFgEQmamLCicGl5KToac+fOIOydmjt9 + koEq1P6Y+vUNDaGbinINW1a4WmKPDz0L8DXqkWjbOzCSer3K9z9A6ndlwJUlKsxGqN26sooKTyebejvJ + e9GaJP0Fy9935rhlsL1ZbqMX97dyB7/i2vsNN9N+EWFBCQkJXQ3NQq+ou1GdD9w77uvXkUnf/oy7X8ff + eY9A6vYbITffCL66OvDyeyGXvom6sDv6LFfCWcXMOb2cOcP80wa5p3Szxg2TK/1DA5u8nEbc7RmDF+N+ + mma3qVy7jkizEk+DLHudBIFUrQhQxVIsJJQtYhMm5GMtoiwtLi0o1t3bhZJu9vQpAAvl1NTszMjEeO/A + ANqqDc3YOa7HohVWcSpwqmvQrwe2gLBlYL3mLf5PxftvxKqVbc/K6lrsUpLBSElxUUlxoKbAT0HKJFCh + ovISwXxmSm1zivD3Bse/5tz5BeehLfw87GKiIgoKCj4OLucymh7G9D9wbr+nXLVwPPvutwkrkLr9n5Bb + /wlGtX57XdidT6LntyctsGQvSRSj9rqnUrmgXPaTZkFtYIx7K8DkhPiElIfBi1KngUSrukCjAke9JGud + GBIfgVSNCHuZqEqkbJyuwRCv/SketwYBr3x5ryR1eqiGg48GLYjmnhAQlRGfmpeRk0susblkr6G4BAs8 + GA/gdom1C7LNh035uvq6Bgaxp6Ors4fsXf3OZfn/7Z398v2t30AVFaiotieWAvBrqKiqdDbT63KRJktU + 0eoPAqTvO7Fd19tWJ7PRjfNbsb1fcO37npvlkJAQv5SUlJ62TmFc8kLp8P9F9d+3blkULZnfnnrng6jb + b5EoBUgtHzxfF373k+i7mxLnD2Ussucu8hbcFcq/oJCZFxps02KPloFenyUamypdhjJtWowQJYMQtYyn + WhGWGlHRbCk3K52mI87nPw+8/WX0/L7keYG8u2rld4yrb5rX3DCp/Emn+Lxi9knJ5GGR6DbBwHIhrzQh + pxBhC1dhfbqBbWRAaEZ6RlFxMWaa9U1N4F+Awo882NFDaqw+LDe/Xuj73XHreahaWaICq52x/Z1XVFaK + yVusnuCNcJWHsVhIV3rgIYD5zJjK5gSh77WPfs256wuuI9t5udjExESVlZVtrKw6iioelE3+X2jffcOG + Rfa8u1/E31kb/gtIAVurQm6vDQfaUL/Pb0me35t+62j6KfGEpCBPgxZz9S5jgEmhQ0+6TVOkRYWvUe4X + IapWhLtU3NBPtZCXduoTn2trgu+sD7/7WQw2/hbYcpbEi+8plN9TqsDjknTpEvYB+fIXeHIXeHPv8Obc + Fsy7LpxzRTDzglDqKH9ULY93ApeNI692hF9wRXV1E3ZEu7oYdLGBvkGKZ/ZHRxmvyFd4Hqoem8+UETZB + UVFVbY2Fstiou+yDGK2HUQhUUvfsWbCbUC2z0YXjW/H9X3Ht/46b7YiwkIC0tLSOjg7dza2/qulB4fhD + 7+57KlWoqEigWhX6c5SiwhVi1ZuhQBuuhLc/ir7+ZfQQa6CXr6V0gzoudyLNKmiUI98hPgFPbHXiP4eo + ahGpVNkYJbOxbzx/WhOIEo18KRL2Yua/T5zfl77AnLPIlbfIU4Am/iJn3iKWt1hyFpizyWHNWeDIWeTJ + W+TNX+TPB9rmeXJucWVd4UgdYguN4LFIjIrr7gMVFnz8oYHXgeq/eUc9E1VPzGdIkV5ZkZOTmWokfCuS + se0JCo07/5Lp7lGVzYnCG42ZvxPY9zXX0Z1C/NwS4mIIVJaWlpGRkaPtQ0t5EyiqliRK725JvvN2xJOB + 6vE8+EbItbXBA7s8aHQd7koCIOqgeEI9DjCtlFDMNcJsZaKGdOWKg7Tp9e5zq7zOvOF1/g3vH9/wu/5G + 0O01jMj3Vfz85iQsbGHOiLkQ42Tgcf5gBjlHMheOZS0wAWE5BGoAGQ4BXCYoGLePp5VyuCZGxA2Ojg6P + jw+NjoF/gf3jVyTY/MEf8+mooop0dG4wn8FeHgIVY4e42lKO76S3/MM4LbLw6SeBjZeretua5DeHCGwy + ZN8sz7ZTVoRTTERIRkZGT0/Pw8OjtKx8avLChdKTSyiqeArIvA8V1QqMfvXk5qrg8e88nWk67JVipAB/ + 6qkhdz2uPFE7U/WarbSB9U4Da50G1jkNv+08sY4+t8br4lv+11YHIfiRrPpOJOCF9v2dT2Pvfh5398t4 + fA+4LtzdlDS/NXl+Zyo2uhDS5veno56bP5y5cAiwAwTTFg6m3jqcmsRri6qd7IhOTL5uu/9+qD0FVVSD + imI6EIY7ofgV4HKUlBhXZCE6H6VOdRNIoLLYd1pza6Xc1nDxHY6ie81k2RSkRSTExVVVVWk0m8TExNHx + qdGZK22NZ6ZcOm4z59z5OIZ0E56BKrRDZz/z8TTX4yx/NqTIXU+YN1fc1UCreaN9/7tOmPO0f2DXucFh + YIPLxEceZ973u7w+8Pra4FurGYkVFRseyZNQgjP86whjQNv6yDvvRd35IBrfEgEc0PZtAiaS81uS7m7B + Y+L8LpR3KWePRvmZuaCzhfUYAOv1SvvvBNaTqKIgRfbyMPKrrAIdmewwlZVV1VY7KfFdRNsTkELb01f8 + ngPLgiHZeKlR2pGuuD9Gm81OU0xKQlReXs7czCQ+Jnx0oOfU2Stdo5cL6+biYoaydGo696fPfBx95v3I + s+9Fnn0/8sf3gm6943XnHc876z1vv+tx/iuXWA0DviLxZ0YpRvtAMFsyQsFw8AvXsXfcej90av3EvvVL + h65vXIa/9Zj9yv/ip0FXPwy9vT6MBEXACKh6dmhcBhwDbVRVR3D2ftSd9yPufBl9a1Ps9d1xaQKO3b19 + 6JpSU52hZxNcf+cr/ip82pOootRdmtvbSYOqsAj74FiiQi/Hx5nW5yxxP1qDDJIDZe678d6zPHRDd+tJ + zW1dWnubjJhyrUT0FMVUFWXdHa3qi1J+muz48ezZgcmfKtrOxxZMuUYNmLi3G1jWGxvWmOpVm+lVhWpH + zSkLzSseXlQ4tKBw6Ira0SIXVpESoedAiqVGRD5NPENLaWa/zukt+qOb9bu26zfvMWzcZ1G7z65tL310 + h9+ZjcFXvgi/uiHsxrtht9aG3lrFqN+fA6xf/dHVNwLOv+17eoPf7Df+M9sCm1i8ivKLkASxgzoyjtkz + ISFSUjavAj7+t5/xF6h6vJyC8AEUEzDywz5TUnxstrHQIkIU7n0hCoQ4as+MPaobelvP6e08bXJw0pYz + xkiUbqxUF0+/2p3/8GTjHWxDjV6obD+fVHzSI37I3K9Ly7VVyb5JwbYRx8CmqMVE4Y7+jiX9zYv6m28Y + b61zP6CSz/s8SNWKKObzF7sfv6C/94rq7vMq+2ZUDo9pMBUr8ziIiJvyyNtyq0dxWTaweE3uD728Kfrm + J1G33wl/sjH2bHihGYtz7T+B51f5YNQ9+LFb77duPdvd65jdU2ISBkdGBoaH+waJFgiIYjhYCiJSlK8V + 2552N/wFqh7fSUc5hUCFvbzE+NgwTb7rwfJkikx2E8TvOXHeszy4ZLjtlt7WG0Y7b1gfGrXl7fNQvJHj + +H9NUf9fd8ZST/Fw13BRwxyilEfckIV/tw69VdmhSdGOQErZti7M0vu8IVZGd90w2H3ZaE+Ty1GtbF5W + lOHPqNARpRTzBfM92SdNWE7ps8zqsJ/U5hrVEcqXV4wQMMkQcmuXjDwvk3VLquA2X+780SxU4tiGIHPG + J9qtz0XVjf8EIVDNrfYcfsel+zPn9o3OHXvoeVyO+Xn52BzsHRwEfx/0eWg0EPGP6lpoy6A/jKE7QIah + ITaFXuu2PYVjQ02RIcVB0eswRYYAFV2Z95SXDGmjI/ehk47cZ3MUDYUlgy3zBtuWTHfeph2768H/MFL1 + /zKt/1+xx/3ywOny/LSi0aD0MafIfhPfLm23VhWHZgpSOPq0okxz6xpTjVIzvRxz0zAnE7lMRTTHnxOo + eAtUNHw9rS1jXAzi/FWi4iXD8/hDm1nCpo/HXWJL/VE887JKzkXN3B9Ucy5IpZ/ljT9zJGJ2V/Cp7wNO + feN35mPfsx/5XXjf/+K7AZfeCbj8duCVtYHX3grCmOjWo+RIBSq0JH5aFXDqLc+B95w7v3Ru3+bWccTT + Q8gAa17jU1B2GO3p78cYB0qCpeUVIEJCHgKPZZA8JTOfRyJbr7V0H2duPd6ggigZQ+uizFJVqs9JnEFH + 1ngQqvjAQ/CeHfM9870IVEAVOaa77tOOQO8FHdGHkeoPYvV/jHNITqh1ieyzDOjW82hTd2pRtl+OUoCU + om2DGq1Gk1ahSatUo1WrOJSIRfqwVD+vQmerFFcJ19X115VLUJRIlxXPkJXIkJVPV9BN1vD1t0pzc4+J + 9/Apd3Ovd3Oud7KuphmVGWsU6ihnqymmKqnGKemGqJh4q9k6adEtdQMMDGOUTdPFrYq5bRuPOPXuoE9+ + 4zXzufepT73nNvjOfeRz8iPv0Y/oXV86d21x6zjgHsltpiImO014QeD2TPQNDEAoENfhcqjKVFZCkxLw + YmCLqBcRbNU3NDa3UppYr7Is4M8ZEKiiRn4Q4ikqIYpkkOLINRG8E6ZIAhXJfWL3nDjuWRxYMt5B8KS/ + eclw65LJToyW79kx3XPmvO/GN08XrPdwNvNu1qE/iScqUP3i2DXIuBXyZJk879LHyIms1b+IZKy1Ypz1 + kgJN8hKtqsrt+hrdJrq9Fjh4It+hJ96iio1Qrnpp0j6tEcVBAsVXwI4o2mBoW3CXiPOUiAsUSArlSkmm + yyjEy2uEKxr4qZh7qNs4aTiYa7kZ63obG1qbamlYKCubKtl72UcmRmbmZ1fVV0PtA9No8kh2HKoZwGKg + ipwKhDHs0tQ1NDW1Eg03Kie+gnX9z6hCUYUKFJJOKBrwGpVXVRpL85zzknwQqfQQkKJyn/WRJeOdSwZb + CaRwgC2j7T8fw61XDPcHWvgo0WoUaHXytHoF24angIlErEZlmwZ1qzp1hyz+HI3fRNUTnwBUcdRJ8jbK + CDYpYqSDFSupNk1sMWC2g10r7gbplUHhymzn+f8EgV2VKA5blRhnpRh/tZRUg7Jmi75Fp7VLD92zy8u1 + 1d2uwtEs3UIrREfZSUXTWss72Cc6ITa7AGuxBSUQaK6pRvQCbRoRC6s1tQhaLZDFIlqmryCwfoEqvApA + FfSr8UaELk+MNt/dQMkHoXIPAiQfeAqR3Ge2l4IULm7zepvv6G66ob3pstams2obT6psHFPe2Km8l6ZH + lzHNlbUslbeuBrYeB5YijYBJy6zOQrvKW7o4lT2rmDlUO1jtv0UVsIIBDkIR1hYAIOrgOWO282iq86zW + /LM/Tr5srShHnThvg7Rkm6p6l4HpAA3sQtcxT9cxD+w0g3lh1Wdv0U0zbbYyKDPRStNTClaVc5KXMJB0 + 86PjFauqrq5pqGfs0kB1kuitMVQnB181YD2JqrrGZkiiQyssMTGhzIxvwV/kgY/gfTeu+/YnkOwoGF3R + 3HhJY+N59Y2nVb+fVN7Yp7CxVW5TjcymStkt5Qp7XHRM5c0ylaxLlWyrle0blexJ1gOe1CzrTfSr/SWK + ig+nD30Vd/6diKtvhaKIDtUyRqj4b4H1v3w+/pXnHmrOyA5UNcqKt6goduho95iZ9NMsBh0sBx0g+2E6 + YGvSb2M6YGM2QDMfoFn025n10sy7rI3qzXVz9ZUDVKUMpMAna2xprm9qJLs0LaixOrFLQ9VY/1vv59/4 + t36BKiKkCXeQlpbGlpbk5MQyM4F5KHnS2Zccjt823/ej7tZT6ptPqm6cUNk0orxpUHlTj+LmNsXN1bJb + imW25spsz5XbWaK8L0lfTtsmQ8OhQsOpUcWxCaW6mnU9glMCd+7g13GX14bfWBV669EN//rqoI69rvKJ + cv8LSv6raPQznoSZq0TIqRRhqhBmKscRYipjHDypEGapFkHM42uUFWtRAY9epdMQ5Zpmt4lWjylAptNj + pttjrt9rYdBnadRnZdxvbdJvbTZgY9lPM+u2MqgwVg1WN3QwKi4raWppwe4yER0lqHq19NZ+Ua0Tud+O + ThyIlWEzHXtUN70F5z24bzgy/Whx8LTernGNbUNq2/pVt/WobOtS3d6huqNBeUep4s5s+V2p8ntSlQ7k + aR9L0+O3sfIz9GjQc2/TdWo2N6qJ48ub+Czm5q8XYBjYwm0/TdJSKE/yrwUWA1UkKFYTSLFUCTNXCDOX + CzOXCjEVCzIVCDLlCZzI4T+RTc7xbL4TefxMBQL4U9YqgEwMtRpuAKjhZNu1FDp0VLogfGUMqAFner3m + QJhxvxXgZd5vbdRuppOvr+yqEhwV0tTa0tXX19NPdmkG/5tNkn9jfHr8e/4FqlABoLrs6kXLGPDqsFEW + HXIRvevNf8OF/UfaiTmTA+N6e4a0d/dr7+nV3tuts69da1+9xv4i9QOZ6oeSNE4EqLFbyfPoSgsEGqlG + +qU5e7ZHaFf17kq5tuZ5ewoYKp/Z4ButYiqcK/XsLujTu1lACWH//WbQWoaU6CNIkUDFgrBUKsxSIsxa + JMyaL8SaI8iSIcCSws+UwHs8iutYGOfRYPYjQexH8BjGcTSa81gSDwAHCOI6yVUvJdSkKNmqDtK9Uoe+ + WreRVo+JTo+pQZ+5fp+ZUa+ZQYOxRrCmd6gPWKwj4+PDYzjoeL0qAkZP9tYZFcDw4Mjo2ORkbV1tgL70 + kIv4NQ/+n5zYz1ifmDI5Mmx0qM/gcI/BkR6jox0mTDUGTBk67L6qPAYyfDKiAgoSwnGW8lejtK8Hmc3q + hv24J+k2GtzPiFIr4zkA6/yHfpVs9s62OsqxCtKp6EjJ4M6PJ8oxCvYO2sE6RnZOWkpx8lLpMhKZ0hJZ + UlKZkvKpMprhOkpB9qKJ5twF6uwlSuylijhsZYps5fJs5XKsFTKsldIsVVIslRIsVWLULY8cQKpShK1C + hK1MhL1EhL1QhCNfhDNHmDNNmCNJkCOany2Ul8WPm9md44QT63F7lqN2zEfsmY66sBzzYjseysmUwMOS + I8BaRmIYD+6hzeQeij1VhQ5dlS591S59zW59vV4j/WYjzSCtjLxMEK8xRiR8w1dGxe/JOSCaK9hNw0sw + MT0NFl5jUwPdWC3PVqHbWXLUXqDPhrvRjLPAkCtJhydInddRkc9GUzYqNAD1/cnZk9MzU64Giqfjzf8v + xeq+vfsCVxIWMsl652+hinAi3ghBjfXDe/6nPvU5+YX39Ffe09+4T3/nNved88Xvba9sN7l0TP2MkOSs + HP9JTbZBA+40SyNj2xRluwpF+2p5p0pZ1xIZtyJ0v8ihF0i750l5Zkv4JYsFxYiGBStG2ltFydNjeO0S + OUxSuNQyeGSyBUVyhflyRXhyRLmzRHmyxHjSRXlSRfkSRHmjhHiDBHi9+LlceTjtOdmtOdgs2dis2Nls + 2TncuLkC+LjjBLmzRLiKxdirxAAs3D0566Wwp4oUKdqiLNaqLNOmCgk/nW4D/RJ9bUdtyJZMzsxglwav + 6ivCYn3KfhV+cgzn8SqAN0fY4rOzYJqUlxZlpyenxEfnZKWXFBWA2tTR1TU0MgyJs1Nn5sAFPXP+bHd3 + R6ab7o0M24eRrveU0rBpiRW5O+t+taX+rGEc9oPX+N151+POZ3Z3d+jOH5dd4ONflGJaVDy0qLp3SXPn + ovbOq3oHeo3EfS2D0aB/WicM7THqNCrYNSjY1avZlXvQghstZM+ZHvjJbMdls+2XzHf8YLXrLG3PJG3f + kMOBBsdjKU4sni485m4Cau6isnQxCXcxMbqYmLOoqL2IsI2QsKWAkBW/EE1AzFlYwldMKgphUk68RE64 + RpanQYqzXoK9nuypYlsVLTT0ZjnqJARqZKQblFWaNfXqDfQD9EErJFIR2KWZfFVU/J6CKix7IFaPQW3j + 5Axo49Aigx7Br8/pM2dwADuQ7E4BVefONDc3VPgazafT7jsGkzVwSFJ9EvP85c/lJLgq6M47Hnc3mswf + k1sQ5sJuzJL67iXt7Uu6W5f0GE18/c23DXacMWQuNDO2sslQskV/daVN3yBvUydnXStrVSVjWSljUUGO + ZaWcZaW2VV6Cqd2kIedNg10L+oz5Eg7mAUbbSOfWZMei6a4ly33ztIM37Y5edTpx1pmt24En00LQw0BE + X11UXlFMSlFCWllCRl1S3lBG2UZey1tFO15TK09LpVpDpllFpEmRr1EGYELEWm63VoswFQtxFIgIF0vL + Vipr1urqh+qDhv9IGYsIT/7bK/Hf8/0/fcMY4QoqiXhvAVhQdz2Jc5pIXEA4hXokZ+7MDNG9OI03Il41 + RCyEtAJ3gxuRpvdUo+ePZNz9LuH2+giMbK//J+gmY3z7vD0nAGu9150vbeb3qy3w8y0qHF7S3LWksw2o + WtTfct1g74CxaISFpw6tFJPEX0GqWtayQsa8RMq0QNo0T8okR9Y0x9I4uFhXZlpz349aW67rbLqpu/mO + 3uZ5/S2LFLYALAwJgCrzPfesDt6zO3rfgem+K/sDT95FX5EbgVJzAQpNniphVsqWukra6gqqKgrKqgrK + msoqusoaNho6ITo6pYbKrXpiLcrIfehE4ILJVCl8oljweCYfW4oAX4aYZKGcepWWjo8uFCLw3sP7E4IO + eLv+nt/Kv/1znokqLD0iD0LMCa/FxEkIE8xChw4vzfI5Be0DIicM9QsoCiO8Q7kAl50Qa+3TnlqLYiHz + 2xOufRHxw7sBWCSfe5PwFC6+4fvTf/yv/ifwN0C2Kuj2Wt87nzjM71Vf4BG8K83ygxpnpaGBnVWK8i9C + FIlV8rQGeVotmviylmUyZoWyZsBTloZpor+hZaMO14T2rlnNrac0ts5pbDmrsfm8xuZLWpuuaG26oUMQ + RuBltIMAy+LAfZvDaPPed2a7T+d+4Cv0IET6QbTqgxSDhWyrH7IdWuMcU/zsfJ0s7WxMac4Wnql0/54A + 5xEPCNeiSEfLFBOkExVCx4sEjmXyHovnZo3hE0gRky5QUC/W0ncwmDl16tTcmdeoWmaSIGIhYkMpBYvb + 0GAdmzoJLZ5HByLSRO4XIQ3gQx0KTWlIisFTMtda84as2/Xd8Rc3hs185jP6ofvIO66jb7tOrHGbWk2f + Xe0BPgxFhrn6n4DnIYxRZl3Z4DWwIySVM9NNoRSteW3TOlWrBiVaAyaJqJ8wapS3qZGzqpSzKJE3z1c0 + zzIzDUkx0uzSZ5nU3z+jt3tGd9eMzs5pre1TGtsm1LeOq22ZUN08rbp5TmPzRc3N2GVdxPKFya57lvvv + 2Ry778AMDZL7noIPAiSgRvkwQef/Ms3/X5HT/1X7LjRFXmyO6+tKLpvIyjiTGXoyEnIPaFaJt6px1Eug + d3qigP9YOoEUUwQ3Z6SASJqUQrmqcqhaRXXl3Llzs3NniKIpQ3r53x6Hfs/3/9vqHcAWgRcDYT8fxgdX + bjQoxQAsCOEjYpUWF5WqW//EFHZxZ+TspoCxr72GP3Uf/tht6EPXofddht91HVnvOrbOdWoNfWa1+5k3 + vUgMe8MfS5hYmntqlrz5Rui11WEX10eMfBlXdjA9SiDfRbHMyKBGw6JOxapW3qpa3qpM3rJIxTLL3sQz + x1Cm04Bp1OjIlOmhGeODM8YHZgz3TertHdXZPai1q1d9V4/6zn71HaMaO2a0tl/U3XZDfxsBltF2jDgh + QHLP9vg9J3ZQZyHujTWNh/F6DzKt75a6nW4NbpxIzj2bmXY2I+pUnOdEgPmgg2KHHuSQmStE0DI9lsJ7 + PJabKZybPZxPMEFMrlBRKUHFys0agQqqaygYENSJit+rsfb+26j6Pdgk10aganKKyEFNTRXk5OZKOJ9k + Dj21J2xqm//YRu+Rbz2Hv/IY+oI++KkblncHN7gOfeA6/J7r6Hq3qbXuM295AF4/vOFz+Q2SIp8FL5Rl + QNhPa8LAp+j9LiGbKdNDpshEr0LdolLFpkTNLFZTx0FPTc9GTTFAWyrLULjejK/fnH3M9MSo8dFho8MD + +gd7dA906+wf0DswYbD/tOG+H4323DTaBY8dUsIDW1TcstiP1Yx7jmz33AVuhshMp+kUNjiEjfgHTUeE + nIwKnA53H/e3HHQCo1qoWZG1UhSKyCeSeE9E87BE8HJG8gsmisvkKsiFK+ja6GGDFIHq1Jmz0NyeINLI + r3Bn4ffA6InPQYsLdSjFcJoiN8dZ7EoGSVlVcXv1HQ8YOxA0utt3eJvP0Gavwe89Br9zH/zGffBL+tDn + 9OFP6MMfuo287wbCzNQ6BrxWe11c5XfljWcmR2qGiP4W5onn3ouo350cJFVgalGt69Jo6Nlo7tNg7Vdr + 41tuSc+0cIiytPB0NjYPMlRNNRArMeRqMmTuNGHuN2Oasjh+weroNcuD8+Z775nuJFdCAIsq5A1IWrxt + tnvG+Vh2rIB1pbJ6h6FOL2YythAdNSKKWeaYD2JQyFouypwlwJzAxxzNyx7JxxsrLJIiKZuuIO0tZ+dh + D0idOX+eEahOLweqVyP9/S6lod8JMqofQaqraVK542I4NDwC3wgvPTs/UbNYHuscNoey485NB+ndezz7 + d3oNbfOCPP/I917DX3sMf+FO4PURHfCCPNrsOs8zb3lffNMPW+TPiVuUngwRVXs/ooQly8G0zsSr3Tqo + yyG81yWqzz2mnx7d6xLZ5RDSSvOrNnfNMrHwMdfVd9WUiNETrLbgn7LjumrPfJt2dNHq4KLZ7mVg6W9e + MNh82WxbI/2AUxK7aIUIWlC45YGPL9SkhAOVB6IaUiHClCcI3VHmOD4osHHFCELEVjJDRjZGQdxCEhqT + aPIRSDEqKugiE7nRV6ax/meiCl+LVFdUP4IAaxZNh1O4Uc/MDI+OQjS2proG3o/xoTE0EW1/bqNMFtuG + o/ThfX4TO/xGNnsNf+85+LX7wOdug5+4IXqNv+sObJ17y/fyKv9rbwQ+ryuB3fPVoTfXR7Yez3KhNVr6 + d9KCexzCeh0j+pwi+xwjyBMc50hArdM1vJ5Gjzc2trTWVgk3km2wlTjjLHjLgX3J+jC5DBpuu2O09ZT1 + zpSgYyD8sFcLPz5hZKoWPlFOLnqYPR9P4T0Rz4MQxRHFzxcvIpomLZMkL+ki7ezjDL+nufPnzpw7d/os + o0gnkHqFct9T2BC/Myw969MYd0bS6ELEQj+CAOsUaaKiWwN4LfdRGTgbGhkpLigKsPNwEdBL4rBpOOre + u8+7Z4dHx2bX1u+cWr927ITRyAb6zHteF9b6QTMNshzPBBYDVXfWR9zcEF0qXGjr1mbh32Ud3G0T3EML + IceWnG7H8F7X6D7P+IHA1MGYtI6y5OypBI+bEbpLvpL3XLnB71gw233FYlcz/YBNKgd31SNaIhlLC5NG + VJkgwVMO/7E03uPxPEzRvNBFxuhGOEVcOk1OykdW2US5sbUJVdTc+fOkljp7Fm+qFUi9IkX6CjD+nGp9 + 5ctRt0USsdCaJ30s0p0Htkjv9BTUiBmHtOMJzmbPzI1NTGABKTYo0klAJ4zDNO24VfEB2+pdDk3bnAe+ + 95j93O/SB0E31jHI7M8ii1Jsdwb5+NznsZ66VXpurfqe7YZeHcY+Haa+naAigpdhHdSNAOYWM+CVMOSf + OhyUOhSd2FYWmTTla3LNSfCWzdEx58NhUSxipYI/xyc0NqsQnwiejuXwH0/nO57IcyKGmyWKlyNGAClP + NkNaKURGTE+sur4GSZ9A6lGIgnT75PQMpbP9qkHqT86AFLaWOxFjE7DwQ5cL2CINejRRZ2ancahO/TLC + 5hDJiJUI1P2nJisrK0P9g0zFNdz49bOFvLvZo07vib76dfSdDyIhHvQb3HaGXBFK+ByWbE3rBnAPVR2b + Qe/RdGkBb0zfox0IQwyzDe11juqnxw64xw0CYU7h3fSAqmgfr5gwGe18Yc6VlVTgCfmuQoj0yvOAJ16C + p1ge4Ik9ip87XlgiRdwyScrcQcQn0Gt0YmLu7FkGnohuOxWiGCLbZOr3ioyTn0hff3KsejxorTRRGT36 + KaqJik7pcqee0azHwTwfrfnh8TEYH7d3dmApt7yyQldJ388qOcOiukaudPRoxsXPY2+sCVuh7z1n8tO9 + MUHXpJYa6YCBiAPCNNiIGs4EXghgZn5dVoHIj91WIZ3GYTXqCUkSBTa81Qo/y2IxZi9YDUVjE+OX48m8 + TLE8zFE8bJGkKhdOFNVLEPcNErUxEMjKTseWC8D0sw8A8S6EaxdpDiNEvZqQ+kti1a+rrsf7qMQslAGy + n88EpSXcDbJTZ0/X8Njw+NS4lqK6vYZ1gkFUk2ruKbasq9/GoR7/PYoJ2IjXN15G1cq4kIIXdp1JAHNu + UqPXqfoXycSHCRea8dQoYjy8LC66nPIYJfnPJRQPSyQvV7SAQLyofJKkcZiEuZWIqaFKe2cnhafTZ86i + KicpDy5wjML8lQ1Rf1Vd9XvqffACsBsI7iEoPViTr2tqJsSBpqa2zg4YX3V1dQbZeqTr+wwZp1/XLJkX + Lrx7KJ1oHkNO7XfsaTXvjjM2LVWnVanY1v5itcGuXt6hWsa1WMI/USDRmaNYjbX6Z1lRSqyWVOWlQscL + BI5n8J3A5idCVCTjlhcnIposKRcrI+soaWFn0tjSBCFkZD0SooCn1ynvV8vTf1UGfAJeK7bbABNMUCG2 + VlpeSdTbiYJtPgxIQXjCMm5+Vk6Yiv2QUdptWu1988Z7MDjlL4RkGUzeyJ7Wb6Hq1jrf83zKfYaSlaba + aea2vlaBNjYp2vYFcq6Fkj5pwpEBPFmmrBWyT9lIpiBVIojZC6ryEwm8zDG8bLjlxQqhsSmdLiftJyOi + LVJTX4sLLFWVo2OOEDU9y6jKGb66r3LK+5vqqpV/hsITNuIJgYdh415SVpGdVwB3P2Ljnp4OEVuQM0vL + yugalhUSvtfViu4b1N03arinVQPRR8grQn8WYnkwJvmNDLjGb36f+qLqvgX9rbcMd14y3jNifTTfg8sx + SkI6W5GzVP6ZvHtG4mMUUgInMviYE3hZonk5YwT4E0XFM2Wko+VETcXTszOwcv2oEcUIUUh5j1nfvLIl + 1FOz018bqyjpopX4BDzl5hdSeEpOS0vPzISaA0jSod4BwdymJ4XjFmVLiN6wcuWSXAXDPimbAalodBZg + IYEFh2ftOGA3a/6Q8qLSQexjoTn+g/n2Uu9DRplcPJVCz5GaWQ5aJFCR6x7IDvCkxB4LCVHJkpJJsuKO + kl5B3oMjQ3PwPqFC1Nmzs6exLEWFKDQOXumq/FkFz1+FquUQ1TeA4glKkGWV1ch38HCn8ARnWzB9YQSX + nZVtxqlcxet1VSyLKFfLlC6JlUBsneAJ3kmfxECIkfiRrPY8vdoTzrmXVjHGOG88tt3wlv/dry0XuAUX + 1fYyVvw2/2i2LSn4mHDZY52n55NwgCqQARlOp2yJaBwICSdIintLCmuKtHa2U10DPBI8IUQBTycZjYPX + VfmzuWh/FaoeafZ11tQ3EnHR3LyUtPTElJQUICo3B7aRBYWFPnb0YC7TUZGYu1JFi9IlyHeoooi92/YU + SFtfXxOCjT8sZvW949j7rtPguy6T77jPrfUGyK6uCrr5ZuCtdd53v7GYZ5ZelD22pE0E1nDuGm5pcduv + nM/L8pt0rpVPqCEVFVOuADHASRIVjlQQtZKEjAJYDARSjKqcuuW9bkT9ntvYX9VZoOT/IbhT29AEH3nY + uCelpialpKRnZWbn5aanpTvpWPpwGTYI+l2VyluSLyMhSqR4gSMP+v0Q8kcJdX6tL/DU+Z5984e0xo9p + zZ/ZdXzmiBWaiQ30uQ3Ol76yvr5f9TYP34Lc0UWNXSu77UDVdeOt0WEnhMp/d6BiyNcylQqy5glyZonw + p0lI0NX9gwNHJyeohf3lrsGjxuYrNST+nRj69af9JbGKkv+vb26BqlNmTi4ghao8KzcnIT5Bn1M+nsem + RyTsikzuomLZPflyGLuREHWclFB3P4z6aW0AVmJ633Fs/oBW+6l1zVc2td/Z1m+yr9lO6zxiOMypMCXG + N6dw5KLariuaW29ob4H0A3REkPioWPWT6Ta/aBaUU79NPX0sVhFU5Qtx54oJ5UlJhiokpCTC8I0MmuAa + T7pQr+un/05o/s9H1XKgau+orCU+8iihUJWnpae7WzmGc5kOCkddl8sneKIsQISKF9gfhai1oUh5g+ud + W9+3bfjYpvYLWv339g07nHIOWXuwatOFpKu1+KdN2aaMj04Z7Dups/OU1tZzWlsua2/BHvpdBrDAm7hk + sscvjJ+n/LlCyE8kRwxqyoXYioR5iyRES2Rl0xSCI4MZqCJ9f3QNwGeH7wjUXYgQaP9rp6TfRthfgiq8 + +ghUUFeD5Sn0arNzcsyltQu4nH+SyFwCnpTgkMuwM+XKXzicOf990u13In5Y6zfytkvru7SajyyrPrNq + /Naucyu97qCLF7uej70b5BFjI8MidAQn7XlO0VjGTY8O6+8f0d49rb3znM72K7rbbulvvUOoXSw5Fiaa + PsGchZrMz5WE/EUkQ7VeIQy/Lt5ySbFKOcU8ZTtXG7inTk5jIRgG5n2Nra0MnRzil9TCkAB97Rv4/OT4 + 56NqWV2N9Dkr4CgB5zd3G6c8Lucbkrn3lGBUVLkkU8aoyhm3vE9jr64JRsprXU8r/cA05xOjgq9MqzbZ + tu2hpzBZ2SoatrS0UMRDbDeE+nnEGol20/hGrFn7TI4PGB6eNDxw1nDPVcNdPxkd7DaVdreORj9dzqlS + JCKIvUT59wILsQoSHVVivLVSEg2KxoUKXsaiwR5O4G33DPRjlARjekhSQUSPyMu+xtbvkCH581EFBTBo + +sIRFHKGaG8CWJ6ceudEUu/JlZKsh4sevEwPZN79LhF6+didggx103u0vE+Mkr7USfxeL2eHedpRK1cO + rZTYxMmT02TQRnxycRE7Mzoxnp2Z6q4rk2EqWm/J02/FcdKa+QerY+esOCppJjT7bGX7ZdaynFOFmH8c + b7I1W74yc7EUc4kES5kkS5kUM5Qgn6aVRbT2akR56qUkGuVDc6XP+gr1OUumWsoF2JtWVJS1drQ3tBB/ + SmidMcxOSdyC7OzruPX39atQeTS1dUCzD0KG+E3gxlfM5XpbLH9JsoRc9Njy5nem3fksDnO966uCT6/x + 7H7XofwTi/RvDBO2GyYfMgthMzTkVSwrLSM27oxNLAapdXlhBrVOV3dXUlyko45svJlki53ouKtsvruH + rWelpkszFmAYJMF6OesaGetSSatcMVqyqF28mFOcqHsEV7IpDHBw43vqxIbSGpWrkW6M5F/w4b8fKHEz + SGbSW77QVp6uJ19QkNfYjFFlK+IWgVd9A3IiUdNjYAvKl69lsR9H2J8fq1BzAFV4NzNe9JbY8JgBnrBF + oUJizMeSA0OiO5/EYsMO++bYTJ9cR2//yL78a6vsHWaxx00ceTXdbZ2gInbmLLFJpojk2NAiNu6TU5DS + 7+rt7ert6R3o6+ru1NVQpZmaefmmuEZ2WAV169LbVByawI6XtaqUMiuSNMoRN0gX10+WMEiWhmyxWxhH + jjykYJ6l6wdKu0CNWGQCz0U31ntunPd9hB8ESt4PlbsTKj/nK1dhK03XkshMS8VPhN0KzMIhRwhgUdhC + vYUfmdL1f5Wli//CnQUKVajWISLd0t4e5hU4yxW7yJm7cDxzfm8aMfFm7LSAKY9+5vR6j97PXMp32AYz + G+rxyksIiJ6amz33w7mzF7AENwdRGkjTwJQB8vn4asiqECxtaGoEqiamJ4xNbXwjCsOzhnyShu3CevU8 + 2rHuAmq8lEmBuEGamE6cmHakuG6klH6MnHmCeKAzZ7EENIzZah9pIvzyJihQIeSWzNXsfeKC8/HbLmxL + btz3vYRAN30QIvMgTH4+RO6inyyspn20xRNiIhqasWHRCYRR2CKhC2kR7yLIYr8OXY/rrf/PLa8n/iKF + KkZq6MBL7+/ocY4lfuFYxvzeVLj1kTkxw3YLU5crqwMvrPc/+YXvwC7vIjZnfyFjUwmNzJS0/v6+U3On + INyAugpXsGbGFQwlGkyaMDRsaW8dGR+Fv453aHpK6WR0/oRv8rBtSK+WS6ucdZWkca64XrKIVoSIeoC4 + pr+UTpCcQaimRaBGpCp/iRR3lQQ0W5DsnkiCCGCc1SJCFUJKRUJWOYIpsbxD/pzXPHmWPPjvews/8Bd/ + ECz9IFR2MUTmiq9Uh4NEsK54VIhfY3Nze1cXsFXXgHRfV1UDV3C4zFMVPfnxH78tvlL62H9BBmR01eFY + 3NbZhRfd38nz7PGYhX0psEcjgQoiaVgFZjCurr8ZfHVd8LWPw+9uip9nzbgpmzern91oFJOs5+Wra5eT + mtnU2FRbD1ePBoQoQAr2J9W1NUgyzW2tNk7+CQWDKWXTkbkTngnEJ0fZoVHaJFdUJ1ZQLUBQyV1IyUVc + nS6n4yGjYi6tICcXLSteIQcNIJTkRGzjWdYmtaIIZry1EvLlEv7pQt3BPD95ci968gFb9/3ESOgKlr4X + LHPTX2rQWSxSTzTUh17f3Ejs5puaYA9BBP4hwo5rCvGar6uth+Bs2yso6v/no4rhLdiDIgOPnd3dyIDT + R8Pmdybe/Sb2zkdRN9eGYDyMgwx4Y1XwrTWhWEuf35SwcCJzSaz4nkbVkkHNLf3KH7ULx1RTqpSC4hSd + XeVMw70DidV2SQk25voGB2gOrr6RpYnFU7H5k4FpoyBm6Xu0KViXSxqkiGgECSvThZUcxVTsJZXMhMTk + /QMD9Uz0FGIUZcuURCtkeasl2WvEIOWIzfRlSeNfIYxS3kZUE6+WcM0WaQzlPe/FddedZ8lT8L63yH1g + CyVXoOTdAMlJF7F4AxF/F9uKqirIy1I9CFx+ibo/bo9V0PuvQXIkoYuIGb8qVdefjyr0q6Bai0oItnow + fslIy2hl9r6zJ+H6pujLX4Sef9+fqMSs9jz/pg+4fjdWB995N+LuN/Hz+9IW2XIWRYrIVRE9d6mSRani + u6KFN0VzLoilDgtHlol4RojZOIjrmchoO1j7JmYNxOVOBKWNgtSAVXTU6bJm+RL6ceKaAZKaHvLadGkl + Q2lZhfT0dNjMuLq7qAarKOUqi+fI8OWIs+UKkQXiXH6y9lksAFYWiA9kV/2XOFtRdReqkbQsEC2O5J3x + 4brhwbPoyX/PS+i+nyjJjAHii/5ip+ii6cYiToZqBYX5NZDwb2xgYAuuJFVoBcNCgvJRam7rIPLrr4Bh + xJ+PKsZO1SAOfM/6h4ebWlqxO3XjQOK1nbFQiTn9uR8cR0ffpaNNNbvG49wa36tvBaG3fveLOKwqwA4Z + RsiLrDmLHHlw4V4gRtzEantBMO+uSP5N2YJLKrnTWhlD1mXtvl3FAd2x3l1u9HYjhxYViyo5kywZgzhU + UcpGPnKqxkbGptU1VT393Z09HcmpSVruGlrp6nLJcsKxYtBsOR7GdSSE/XAY++EojiPxnEdSuI9CJCiX + n2xZlQtiiY80IB51tgAvJE2BGknDItG0OL5RP85rdK4ldx6ox5Crop/ofT/xe/5iFz1Fyi1FXdREgn3c + CwoLYCiMHgSBFxxKGE1U1FvYg2Xo+r/kTiR/CaoQrvqJaD18sCcg5pEWl1LC4Xplf/zFHRGnNwVCJWbo + M/rQx4SjPPoeHdfAs2t9Lq8JgCrfnQ+jsLPAsOBOXTbchts2MXPPXeTNWxIpJDFMrnRJrvyuVOk13sKz + zDn9x7Py2HJ8RLOslFP19ZL1jKMUlIxEhcUHhgdOgid2+iS4F/WNtXo2OmZJhroZGnKxckLBIuwe3Mcd + WaEhe9juxCEnpkNuzId9WY+EchyN5yY80jx+sm2MAEZFL0aKZIQuMb5aCf1CkbxI7nN0tgVn1vuu3Pfd + UXUJAVuIW0iON/0le50lEo1EbVVF46IjYIGO5Wmqg4o7B67GJBUyDCPIq/SSWlf++ajClZB6sSj5IWKF + PTmZGpuUJGA/dDxwdm/o5Ha/kU3eQ995Dn5FCXi4j3xIBwUe8CLrU2v8b7wVchtmtZ/Gzn+bOL8tmRhu + w2f7RPYiey4swclOHwaITDkwf0f5f/v9yJurQ+feDRr6OrjpcHSBcESwsLWduK6RlHpqQnJnZzu+Cyjo + mpgb2wRY0LLMDNO1FSPlhDwEOWw4mE1ZjhkcP2pw7Ijh8SOmx4/YMB11ZTkWwHE8mut4Ki922IGt5cbp + L7AlKlQlFpjAc9r52JLt0fuOrPddOO+78zJui2IPAqXQjLgbIjfnLVtNkwjWEfN2tMzPz0N3HhV9fVMz + WiSMrmn/a1T99pj6qYQtQmKemgYHECoxaGAmBEeFCVuWsLl2HvYZ3u07st13ZLP3yHdeECEa/pxoXFH6 + MBDwmHnb4+wa7x/fCrgJU+71EQyEJRBL971pC/BzB542JyOqYeYDd270Ka69GXR1ffDNzyPv7Eq+KZB9 + WbPgpHF2l2lygVlIlLmnj5Wzs429vQPNMcDWPsHKJE5X1U9e3E6Yz5iLS5edTZOZVZOJSesEkx4TsxkL + iwM7qy83CO9Y4mMtFGKB0j+MJB6f8zDU2/nLBSPCmS5b7Fo03X3P+tB9e6b7zuyMuCVMropB0uhyLYXJ + XwuUHXGTSjMRczHWKC0rbWlrQ7elnYGq3gFUCC+nDclfEqsohC2rxEwsay5Awwm9cqg8FuYWOkobhvFZ + FLE6dR71Ht8bML7Nj+jDMOAFjashCHhscBv+wG3kPaJANP22Owp8xLCrq4MINR622+vIWfbrZvQpbq4K + ubM27M6nMfPbkxfZspekSpdUKxd1qhaNam9aVZ2yL2x3T89yDw9y9nRzdrJ1tjK2M9SyUFMykpfRE5fQ + FhbR4hfS5hU04BWy4hN2ERQJEBGOFxfIkeAtk+CoFv9F43SFg1okIJ7KWWa956zm5ivaW+7qbyUSo7Rj + 9104fsYWulwhsvdCZG8FSI26ikcaiCcnxOLd1TuAugoeJC8npP6qXdCVuPW4mAdDcwE62kTPA6oEUInJ + Sc9yVbcM5DMpYHHqOewzsttvdLsPohf0YYa/8Rj60n3wczokYgY/dh340AU6fVCCHHvblVJRm1vtBYPk + FRlIdL9urw27uyH67sbE+YPpJFcKFqJVsSRbek+98r5x7ZJz0x3fpkuBtd0+uTluEUG2dHtTSwM9XVV1 + FUUVBQVlOXlVWUVtWTVLRR1/Vb1kTY1CDYVKFfF6Bb5Gaa6GRyrFFA21RJBcIZN5mMPZba0OVipsbpff + NKyy+YzWllswdwW2IAaJuEXnQ3ee0eUSfxAofi9A7KK3RIa5ZEZKImxRwaMnZK8x8Jv/lzzwZ7Ws/6Kv + 8xfGKkpzASy5FZUYAAtzPUrzeJaobRN9GBiEwhXUQ5cWxGuaz+rYfNRjaJ/vyA7f4a3eQ5u8hr7zIBJq + X9EHl3X6XAc/dB1+3xUIwy1yZg3g5fnDm75ogKG5CmUYkis3othPWziSCdbXInf+kmjRkkL5Pb2aB9aN + D5xb77m33fVuvuhbPeCTW+AWGWDjYmNsZqinr62tramlqamjoWuuZeZrYFVsbtxlotqhI9GqAplGiH8S + 7WugqlyIcAaTeY+GcTC5s6qaHI6T3Vkgt6NRZeew5s7zujtuGkJzeye8OYkYpAPKeS5ScnnyPfAVvOcv + fMlLLNpUFlU72Qdk6Pq/lEqhfy2qKGBRggtEzIPUWDMMMb5TyxIxj/RhKPmhwuz8QHtPyA/FcFmWMDs3 + HXLv2ec1tMt7aLv34GaPoe89hr71GPrKHVmSoTVKH/3AffwdOlLk6be8fljte3VVIEQ+CLY2xKCQJ3dJ + 1GEo83kLlsTBCau8p11z36jugVXjA8eW+x5ti0Ht16KaxqLKS4OTYrwCvZzdHGi25ubmxqZGxjZGltGW + ls02Gl1GcEyFOxJQRSQ9EKgy+aEMc8KXg9WRXcXweJTywUKNwy36RyZNDl8y3X/LdM8ivF4huw0xSPN9 + RAzS7gQlu33fi/e+n+CAq1SEt8v4NBHnJfTUl1Hh4y9HFRVjl4MWxPhQZjGEYkDR/IU+DAWvM2cAL/A5 + EcBKCouBMHsB7VAesyxWu9pjrl0HvPp3+Qxt8x7c4gklyN6v3bq/cOn6xKnnI6fB911Q46P8+nG1PxZs + iIDMmnCYndz5KPrul/Eo7Rf2Z5DQxVeINXkCL53a+2YNDxxaHnp3PgzrvpfUfyO791ROc1tKcXZEQkRI + cFh6cFRvlPu4j3GfDXy2iPx1nShJf8UCTOl8LJE8bN5cnA5c0prcdoqc2UZcfXY855w4b8JF0ebgPXMo + 9+1Ytng1gar77iUCrwP3nE7cd+eEjVmkuRK4/2Qd4yW1IfmbULWCrUdCMQwld0jEMOBFRIh+lh9aXqti + qKjNUQhDQxEaVzQxbU9eg1g2i8Jj9hX7HKt2OFRttavcRKv71q7jK+eRT93nPvS9vD7wOhoTlNjVY9JW + mD8iOWJVkCTH44zoJVUGxv09o4b7di0PPIGtvocpQ/MFwxcaerpG64rOFsSdTqSP++r1WUJUHSKOJFaB + 4lxMrLk44wT5Q4XFXMWE5QXFhPl15UTCjSSG6BI3vAQXsO9AO7IEw/OftUYhZbt13njHvNWBJccT9zw4 + q22l0WEgqKLC1fjLJpf9t6JqpTZckdpmlFyA13L0egSvFYErsqxH1PoYW3vYiukfHKyvr0+IibPXNLPm + U/fkMozldChi8Wg57De+PfDcN6HXPgwj18PVoZTeFa6HoDtD2B3qyDhXVgXeejOEIAz5cWcqSY4CRYuy + 5Qs6NTcd687E1XQ0l5VMF2afzUk4nRIwHWoz5AqtYtEWFXiDk7oKLYZSIbZ8Yf5cCekUOWVbKWU5EXU5 + cUN1eQdDtQgbzTo3tTkPqdvOnEs2R0iIgigyQ8EWfieQ4L5usueG9eFbjsyjDgKlRQXQDkU9QBF4XjJC + /T+Dqsfh9Xj0wktMrCimZ/ByQ9QKjwy9K0ZII0/wp5Aogk7GcFdvd11DfUlZqQ/dy1TB1FcvIk0nr0E8 + f/ho+tymxJ8+jLr5FtG7uvafINwTYU4xsdptaI3TwDqn0XWuWEAlrXyA79OYG1viz7HFd+tFpyUE+bf6 + eY8G+U2F+k2FQP6aNuym12sFuWKYaS1X69VwFRSBy7xkmYxvnGSjj/RYmP6pVIdLhT43Sv3uFLgvppov + hSjf8+C/Z3tsyXQPCVdAFbivupvQfbhksOtHs/1XbI6dtOdJjo0AqhhC4i+hF9c/jKpfS8egL08OmWaQ + A7NCPA4Ow7JwBAekl+GxMaAK2gfDeDo5Nj17cmBwQFlZ08DIxd4xzcOtwd+iPkKxLI81q/fbuKn3gifW + efWvc2pab13+vln5R+bNH9HQp5h71+fK24GzX/nmyNgaRWlKVarKNGkoduird8M80gI62Jrd5rCThPs8 + TOdh1IZZDaVGxFItKlwuGREmdd5DcilY6WGq6f8rdv2/Mq//V+r1fzkODxP0HwTK3HfhQpGOap1yPgdd + EWbV8Ds5pb0DIu/nLI6ctOWIDw8iRlwgr04TVb7XserP7K+syDFgiAHFGHCdK6trwU3FwbQfS3BYI8EC + IBaYOnt6sAHRPzQEVI1OjGFxD75gUFHLzM4xs3SwdQ32iSj2S+ikR/fZBHYZurVq0xr09SttpdI9WYOD + d9qFfKMX8qV63Dc6Rd9ZdH7tjFXBwd2eth7aXFUSlA/bI5s/WWhf46A8x70P1vM/a/BhFFgtIporHaln + evqYx10xx/tW5g9Dzf4v1eb/suz+L8P6YaLRg1DlB55C9+yZcftDLQUG7HXtTT9qbjql+j2cqgfUto/q + 7p01Pzphy5kQGUIorIz96deo+pMhhWUsbB2Bk4NFEcz2i0rLQUzNLSjMBf2gsAjPi0pKS8rLsUwC5bSW + djLuqG9qwgafb2ComQ3d2TsuKLYsPq87rWwyoWgyJGMMizGWAV3abm3wppexLBfTTRaS9xYQNBdkV5M+ + IWdySCbqoEHDPpfBI16u/kZ8DbJEPB0F02MrVpRT/BMfJNPlGlEYq+YL2Z79MvD2hqj57xIWjsUuyfvf + t3F44G30MFD9gb/MfXfBew4s9ywPAFIw+jqnsWlSZWOfwsZW2e/rZDc3Ku/o19t/yupEizlvcWEB2cqf + AapmXqPqT0MVotQjSDFErSCSBjzlF0AkLRfD2MJCUL6AnuKy0tLycggSJaemObl5qunRjG2DHXxzfGKb + wjIH4grGsLuXXDqdUDQVkTPukzRkH9Zn7N0BpUYssIvpp/Mq+bIKmxzjlD/CIsrJIyHMJ6QpIJEip1vu + ZqXapgMfNq4GqV8D6NebogIFkl6m+oNb3K+sC7y1hlwIMIIkl8qv4u5uiVxgcl2U1FrSEbxnemzJeB9q + qRu6W85obh5W3tSisLlMZnOu5JY82e3VansHTI6etWMP1BSEBByD68Gwd3tdrf8pPf7HIVVT11BcVp6T + XwDdGCikAVvAE4AFmavgsAhdQysFdUsdMx8reppLWJ1PQqdf8mBA6lBQ2khQ+mhw+hjiU0jmWGDqCCSv + Idtv7t+l7dqqaFsnZZInrBnBKWV7gkf5GJsYK7e4sJiMkKCguZrEULhGRr2lYa+FXLsWTwNpRD1HlAFu + 4RoRigV8tLkNPjdXFI4e9SzIDj511vvd/c5qnkluXpLpptqui5pbJtU2tyttLZXbliG9I1F6Z4bSvhr9 + Y0PWrBn6PFnpKcQkFpZJRBTkJbSN+GeqdaLF0DeATSMCqXICKeCpoKgIijE+/oHisloquo76NqHWXrlu + EfU+8Z1+Sf1Akn/KMKTS/ZKHfRKHvRKHPOMHPeIGIXPtEjXgEN4HRXVj707kPrAC5awqpAwzhNWDeaSs + 2AXVeEWVpRW1xCSkpCXE0r0Mr5S5ZvV6WA86KHXq8f4mqipFdQNV6o46XlxHXCpw0KqgfFB+bSxwa43v + jS+srxyRPSPCPKSwu1ZxZ678rkT5fXHKh9O1WbIMeTxU+TJSkzAGRaCiHAPRFn75hjb/DKrIbntXD0OL + oTq/qJjUT6Wl3n6B8pq2lh75btFtPgl9iEn+KUMBqSNPIMk9dtA1ut8xst8utNcqsAc6/Qae7VC9hvY1 + 9owV7WCYWy1rXihpmCKqESCkYCuqYKqkaSavpCYiIqKhotCa5rnQHFrdG2A/5AorNsSq38iANXCTFzVz + U23b4/LDOr8rbwZiLnRtVRBRZsNI+5ce0qQ99kbQ5XXep7+y6T4iXyzKEyPH5iHPbqPAqyfNHx0W3DvQ + M4MJOwwy4MIFSDFaoC/ZBfAv31l4arpcDlTtHeD3oRIvLC4h1XdAkLZFGD2mxzdpOSb5Jv0ckxhIGoAA + v314n01wLxg1UE5HpkP9BGEFRVqtgk2NAkxN4UBpXU1MKM0LZUxS5Qwj5HQ9VXRosLYVF5eQkpIyNtAd + Ko5+0B4/1ejrNOgiT1BFmubPkyXCqnGVCGu5iJ2d1vC37mh0YSX6+mqye/NrnVKyk/NGMGj+P7ztP/WZ + V/NOpwRWE5qgirKIhISIyBwkIH84D9tzpD9KbealhNQ/hirc+0A7Af8EqAIXBbW5kp6LW0yPT9IgIhP4 + fV4Jg8tIiiRIooX0WgZ2m/p2ISzp0FvVnJpxxVOwgd9kqZxlsYJVCQg2SjbVGhbVRvoV1hpFdmo5VmqJ + 5qoBRoo0fVldHRlVAxUtayMzfxf6aHHGw5qU29k+RUWW8i0a6B08D1UMkwjmShEc9NMzha1+eD/g2trg + G2tCIC1JNJV/5YOyzEhbG3T5w5DL30dcPZIwJxhXoeTnoWJRmJM3OT155vw5yBihtTs8Po4l7EedObI9 + +9KcfyADUqIxdY3NldU1hIJSU6NnZEnzrwGSgCeEKODJkSCpxyKg24SBJF33Nk0XAiYo8RMlBZtaEpAs + S+UtCxWsipRpEFivtNEvy2dJm/086vK7YRffC774acjFLRGXjydcVsy54lB5I7r9RkbfzezBxZT+Bz4t + S3qFP0pFlujZGEcqChWKc1aIsqFv/muSINqeOFUioNJDisjARan/O7er7wTfWovpNYr05bnQ43mQojre + fCv01nvhd7+MWTiQuihcMK9Z9oNZUZNJbLi+a0Vx+fDIyNjUJNgivQOD2Ah9+VaN/xlUQYIW/HEQycER + KCkrU9F3o8fAEGvQI37QObIfoglIcDr0Ng2XFuQ4VN9wDQHbneH10KBAq5OzqpIjrrjFitaAVJEarZim + l9ezM/bWu2F33g7H75vshYJm+F08aPhL0iVYT3jo1fV/wf0Pg/ofuHTe067D/vudbSnXPok49YVv+36X + LDFLd0tdmVSZX6swoE1FKcZwVknoB6hV7bIf+dzz4oagG++G3Vob9nTbJsoGDIy0r+MW9qctcueR5UHt + 6gW96stahV0a8VHabtWV1dgIRVOXwUR62ZZC/xlUIVahVKd0L+KTkowcUmGzBgs/29AekPuQ4+A8o2i3 + giQKTz+jCvWTgnWFMq1Y2TpbySRSVZ2ee9zz1lcxdz+NuftBFPbcb78dcRc7MGhUHspY5C9YUq68r193 + 36Txnn79PcXKRe6C+V1ptz+Pvf122NW1Qec/8O/e7RaqZSydKvsUbQ8SrsDcEuOulfR0ML74TejNTyJv + bYi4Bc+mdxhbzr82A8NHwBd6J/Lu57GQpZw/lLnImQf9EuymLgoXzvPnnRNIzpSlV5RVkI3QMWJ/95L5 + cv0zqGrv7gblsrmtvaWjIzQswtQtzz4UpkWdRt7tWi4txPwddbdNLTSDSHAi5zFUkf+tU7arUXOo1XKs + 0LLNMtMMnD4UfQeMexiTfBxz571IyBjd+YBBAtuWsnAoc4EtF0ha5ClcZMvD/97Zknzzy9gfvggZ3epV + zGPnZqUrlySHmfFz5GKAKr4KqUIphxufRIBehi9OqP3rIkgSfKrFHAlXYcv7g1/Hz28lTCFAHNvPC8fS + F46n/ciSEClr19bR8WgZ5qVaCv1nUIVqva2rB7Q4UAOSUtOMHJNpwTDya9OjN6jao2AqljYtkDYrkrEo + RYscYlTQD5KnUfBqQO8AjyoOjRrOzXoebeY+Hb5OTZcPZ8xvTSI6Dh9FE8cbhAqQJrC1h1//J7F3P48D + ixWP6Ibf/CTqwlfBHcfcI7XNtMOU+YokqBz3nGsgYQLWihmEqE9u97n9bjhBLb4y4195asG+XGYBWBS2 + 0IjHt4FG/JdxyIl3NyfM70qcP5TSwxecm5aN3hXpWjFsul6aFsM/gCpKjhbUJcJvHhrC5FhVz8k2uMPS + v9XAvUbToVTRMlfWNFPaGCcLghxQDpI2L8FQj5RTDIQBVSjbgSpoVmE+4+7ccv5gxt1vE/CbIyGE8iZ5 + /OCyxjjX3wqe/cInVdpKLUqJo+JJZZjnAEsqQ66Ux/6nd4JIcALPZxW8CIMpu4pr/wmkDmVdQfVIqXMT + jQaqWYpvBn9rdSjoZZc/DflpY8RPe2LOMEWH2HgyNheWd9hfo+oPXYNx6+kbHMa9emhsbGRiIj4pRZ8W + aRXQbOJVq+dapk7LU7bIVLTIkDdLlzPNJAgzzZI2zZU2zacCGOClaFuP2kvLFY6SbZaurU18+bc/i73z + YTRZ2XuG8SlwcO5Dv2QZC/FM6d+vm41IhvyYJm6Fvwu5ETQ50QW99KYfdCJgVzGz2n3qTfrkm254nF5F + n1nlfuoN99NveKycuTc8qIOPzLzpju0uOP9Ofut7ZmfY+ROx3rp2iFXoiE5STfZR9Br+0Av7gvz1fyBW + PUFuHpvCIt5kfGKqnrmPqXueiUeZnkuhjkOOhm22mk22qnWWsmW2okWWvHmWnFmWjGm2jEmOtGmerHkx + mgtogSrDUdKxia5dNfV9AmML9JkmSmhdth1w0Q1S+f2QwhBQNVqhgJd29kPfa6vQVQ+6/Kb/hbf8sCAP + lcCBt5063rFretem4V1rPLa/Y9f9jkP/eqeht12Gf3nwEXxyzzsObe/bdXzuCGra3O6wKebw+MAIcI0o + b0HsxL5G1Z/wlkLAX/aUPwmb0ymM8cMj47SMHHWtAvVt4/QcM/Scc3Ud87XsctRpOWo2OSpW2UqWWQrm + WfJmWbKmWTJmOTJmeTLmRbIWZYqmZc5y+Q1b4y6sC32W9fdP6wKzRK1Fs39voAKkNMOVyjjsLrznd+3N + QBKl3gy89Jb/ubU+M+s9wU9s+8i++hOr4s/M8j83Kf7CDILeTZ/adn7s2L/BZXADYhK4svRHh7Aaez52 + BqR6v3Ob2h5w8WBMNp9TU1MzMdVFrGKg6nUG/FNQNQbeEmHdMAzlKXIzLtsNTc2R0XHKGiZq+k7almEG + Din6zjl6TgXaDvkatjlqNMArS8kiU8EcKZLkRxkTVGDZckY52prZDpIZUWwpBfsTmjfFjn8ade6d8Mtr + w6+tDsW28Y9vBybJWmCn5ffEKtTv7JWiMimyTrY6eQK0wc3uZz7wPb/e99zbPrPveo9/4NH9sXPtl7ZF + 35pnbDJO2WKYsc20YKtF6Sbruu9sm792aPvKqetL576v3AbAZIScxFeE0tj3NX3ge3cQtc/ti2xm9gaD + CEvGiFXTp7CS/1Ltw/wzGfBxcjNxZaaABZ4gRUAlFC7CrukfGIC8GBb0VLQtNYzddayj9J2y9Z3ztR1y + Ne2y1WnZqjbZypakAlMwy0AFRo5xprxhtpJ+rqpunrZ2oblaobtkfix3VsX+1L7vozJE7SQyZH4PqlY+ + BxGLp1hcMV6ebqlbIGDbtN+5eatT1Sa7/K2WqbvNog8Yhh8xCD2hH81snHzCPP2oZd4hm4I91kU7bUp3 + 0Cq22VZvc2jY5tS83aVzm3v/Tu+Rvf69R3zj2awCnbzwAxJ33dNzeEcRTsRLRAz8h1H1JLmZ4SNPOcgT + bDEYghTC2rq6SsorHF09lbVttMwDdG3j9R2z9IAw+xwNBsJUrDKVEL1M02SMUyQNEiX0kyT0UySNMqSN + chWMitSNyw31ym0M4hUjdVihuv77HbkomSE0QmvEBIql1BOU6A76aYq21RzOlUzO8FAx4ldUEpRSEpaO + CA4N8w8M8vDxc/F0t3aiWzl6O9A9bV38nDx97d39bOmepo6eFo5JUfFQ5UOUoqy8sA8zQQzlic/gC1Jr + //Fv4x9G1QoBlSI3MyhcM08QUAlzi8FBpQ5SJODl6ROgpGGmYeyhaxONCkzHPlPTNl3FIlHeOEZaL0xc + K1BMM0BcO0xSL0baKFXOPE/eqozsNdiWy/qH8OT/FwX7ingVpV8FWUc02cXL5TWz1L2DzUtpnsVGXlYa + OvLysmfOnTl/8fy5C9hMwDsBbwy8PWbxOHOKPCFeS6dPPXqrEDdU4l6JfZiTaFZNv2QG4P88qn5Fbp6E + RyghoII7v0JAZXBQ4ZtNiX8w4EUqMPSm09Kz1HXMlLVpKgZuSgY+SoYBsjpe0toe0trecgbBSqZxKMLU + bEtU7WtU7OuVHBrknSvEgiO4c3VZKyX+q4iFRigOe704V70UX6OMYLOcXJuGQYexV7NzeImXc6ydsZtR + Wk5a/2D/5MkpyHrjOySQYrgsQbaEOpSPFw5qKbK0PkUg9TLlPuq3+aKg6hcE1DFMxyCZQgioYwx+M97Q + uCUtnxmgjYg1kEfGgcRKU2tzWkaGrYOzvIquoqa5qr6Tpqmvrm20kUuGkVuxoXutthvpmqo6NKnZ1mtY + V6o7pCn5u4sna/MWysDB5vfAi6HjSKg4WHXHVpZQs4JMu7pmj5HloC19zDNoIjh0ONSnycc5zcnSxzI8 + Pry2vm54dGR65iQVtHDgLE99w/hBGIOaZfWOl+bqt/JLfOFQ9SQBlaH8gQOQYccN9Qfj4H/HQeFicAMJ + QxBcLjAEe/t6G5obC4oLoVusrK6nZWhn6hBm411k69dkQ29xoNW76VaHixXln8hs35Iw+XlY/1bvIl47 + TzN99UhFxujmeXObRyKORCMUDEG+RlnxVlXsKBv0WdoMObmNe/tMBvrhTAT5DPm7NbjbZdqbB1nQg92r + 62sGR4dh4gVUgSvLEB+cotRgKGmTP17HvGhf4cVFFV4ptOApM3BIloMtiAWHhhZYTrS1EpVpQg/sGxyE + cwSARTEEh8BCnRibnoEJ24SqvLKukoapioGHul2KWnC9TOI4c/KlTQlXP4i6vpbQmglf/o2Qq2uCzn/o + 17/NvYjX1tNcD210rjJx3Pueg7BHutmSvI0yos0qch06Gt3GBn1W5gP2VoOOQJjtsLPdiIvtgLNtj5N1 + g51phoWul35IXGgDkfWGGNpwP4M6i9HCSwmpFzEDrrztKOdm7MxgaxSK+PCXLywpxYGLc1lVFRazGpqb + MUPEfBrAGh4bHRweghtDeUlZYnBUgK59tnlQm0PqaaeS61ZVt9XK7vLmYWuAiD6+E/nE+gqwdXNVMOB1 + 6Z2AyW+8CnlpzrY6CvHyiF64LT5nkQHFOxW0RJqVpVpBfdZV6zLU7DHW7TVHADPqtzLttzEfoJn12Rh1 + WuiXG2kGa/tHBnRCvXFoCJtVhJP9Usxnfh0pX9BYxZDXHqLWsMA+BWMCPEHwcMDoAhUHtByGiDlYzhX5 + OXlpccnumtZh8nYFKn69OgkXzItv2TcsuLc+8Oh44Nh237hhSb58gT0XytswnSe7Bs8YFFLR6/rqoLMf + +fXupOcIW7ta6yokyHOXij9rr4ESzQa2wKoQbFaUbFWDLBFIFmrdRlo9Jgx4WeAYkkdzgx4TrVw9Qycj + QAq9EobW3ktIhXhxYxUCFdltb2kDpMDAgXlzVm5uTn4+HqNCI/ycPNwMaI582tEC1uUCXiOiMRfFM27I + FN5VLF1Sq7ynWwsk3TdvIke/fkmpEsIvCwcz579JIOq0WF9haMU8lXq1siuM6HVlbSDgNbTFvZCP5mKj + oxgnz19I/JV+XdpTOZEU8vXSYNMLNytLtKjJtjHg1WWI/KjRbajVY6jdY6TbbaSRpx0cE4ICi9Lae/ku + gC8oqqhARawr6+phMZ/NYDPn5OWhrxjCbNTLGjDHlXCZP/2aUPZN0dx50QKi0ShatCjMOCLFkNWDNtWS + TDkeF+HrzJaL3Hfn87jbb4XCjeLcG95n3vDCOf+G94//8YMIEXZXnoUwKjkiev3wnj+Iy/kCNDCYtcKU + AK+nJscVOwlELzANEb3EW9SQHGXaNeXbtZQ6tdS6dDU6dVX9NeCSSpXtLyXN5kXMgEAVTBwpEk4B2IKF + hYhSFlLa7Sd8FjiyFgULlnAECrA6TA5vPhFh584nXhIceQusuZBiXziWTYLTztS7XyfceSfi8lsBE2+5 + da61q1lnXr7OpOxtk6p1Zs1rrXvX2I+vdj29yvPiG75Xfgkv4IkBqdDrb4ZeWx125a2wH9eFnXs/dOKL + oJa9fvGS7voetnzZWmwVMizVT/FaehxeaEOg9hJolBVtUZRtV1Vu01DP1SosLSLNhanly+CLdon7g9/P + C4qq9u5ekHDAwIF6R35xsZ+bVzULfZETImb5S8IwsCwgm+BsucQ2gjln4QRglAUYLaAe35pyd2MSWftE + snszFGKhWGlqeMc6+339yA9VvTfIun4sieP1sWzIBqWkD7UK3jOqX2/Zu9528h3Hs+84/vSBw/VP7G5+ + Qbv+Le2nzbandjl3H/IvZY5P4s4IE87zli52Ui6z0q7QNa1Stq2SdSsSD4wTSHDhLNBiqXr60JqCF2mf + 1ojD1xmiyLINKipFGokZScuoQnXFaC68TD4RLyKqlv3A6xuIKxrq8cJCZ3bNqxwpMLRZEiwkTuDseQtH + sxYOZCzsSScBaXPS3W8TCZI2YGk9CrvFZO3zjaDzq30m1tFb3qPlfGwY8rmy/Zfiel/zqnzNrvg1m+o3 + HHrf8Vh9L+i5WTR6t1DBcd4WbrZx8aPn5Q5dV95/R3XfAo7avjvqB65qHZ3V5akz1Io087S1TNW0qVRc + WaK3a5B1LhcJD2EvVv2NPiooFRUiXBXiQlUyktWKSilqWQU5kzMkVo0yGleU4tJLA6wXF1XQHiJWjrW1 + aalpGay2d7gyF7lyF9hJWIK5PElt2CemYLQ+kiAJ+3qPaJ/Y7kUJhd26gXecqj62TPhKh75RTn+LoPw2 + duFtJ/h2HBbYc1DswD6l43sNOfa6CO6NkdpfprxvQHvPOf2dNwy2LUA2naGRh7OovwXnjv6OywaHpoy4 + 601VIizcjWj5WBiUd6gVD4hHoPrt1jwI0AyRPv5KafFKeWV/46S0dEyccBNER3doFIEK+m+vUfVXNlqo + WIW6qr65GU6h8bFxJewud1jTFw6nze9MJt416yOwLvzMEpsSO3jTH644vR86l35jGbFTx+awvDqzmBSn + oAgPvxAfnzA/j7gAl5wQi444k4McU4Qmc4Uh87DZ8UvmB+fN9iyZ7IAA1QqwVuC1oL8V8LphsGfWiL3A + 3MjULZQv04S55ndsQDCoquxVMFeSlsulGfqm65h6QqMLUwGYZUDIn0gKvkbVH6zmnv/XkQhIP729Az30 + 1s5ONKnSOW2vn0i+uzfp+sbonz4JPfe2LwTWz7zpdeENH2jIXv1PICXSQvUF8AQ8Bextnl/nO/qJZ+U2 + W28mHW0eGWlBUVEhEVEREUlJCWkpKXk5OWVFOS01eWs9+VBLuUonyRm64DVXnsv2vGes+OdMOU8bsZ0x + ZMY5Z3jiPOOsPJkDBM3ZXCNFOH7fDJFIP9aIcFRLiuXQtIKKwCayCWo2oIWFRsYMjo5geX8ArfaXyH/r + RcyAlLxHRw/s4Pt6+vrgMOjKp3uaLebWoaTLO6POfht88hPv0ffpo++4TcIe4i2PM6u9fljl+9Mb/iC6 + UPDCkjGx310b9OMHQRe+DTl9KHxQKCxLme6qYWakqa2rpa2ro2NA/aevb2igZ2Gs62apF+1GSw+JCQkq + sfGoNnSq0rUt06WVGNgUGdoUGtkUGNnkGzMOnuvb5SgEBnKUyv927sNuVpUkW5kCT6aJRHiYskeZjnuL + kVebVVCnXUi7qWN8QnIqJk4kDzLopi+HAcmLiCpEMkgR9fQP9g4O4U2MQXJWSnqqgMNFprgrB+LO7Qw/ + +Z3f8JcesIcY2UAHvKDff3KdB6IXyvNLbxD3EWRAaPnDEe4mOPKfR8/vTlrky1nSKbvpWj0SUlAQFBcT + EBTg4+vl4enu7u7q6uri4hIQFJGS3wLxPrg4W/h3abq0UJoOvz7ydvVSXpm/XU7ViLJWSnNnG4uEB0t5 + ZqOuV3SsU3NsBo0W5GwT7zarwHZaYIuhTSAc3iD6DUrIS2NA8oKiilIyxhuXOAxOwapkEmOZGHG7fq6Q + uaORM3tCxrf5jm70Hvnaa+QLj9FPPUY/dB9/z31qvQf8UWGcdOFN359WBYBUA9mFu5/HzO9OWeTKWVIu + f2DT8DCo615a39WinvGypubiiqLs3NSk5PT0rJqmnpqOM6llJ32Shy38u4EqiDs8BVV2DTL0At40m+eX + U4hPnPk6QjFewJO8QzX1dSAVAc0IKEeoOTRou9YbeTZY+DWYu+f6BYYxuKbT0B7CvvVLMHJ+QVFFFV5o + 5BBUTU7hRYfYemtbW5xveJC0dQGfawuTV98Bn/FdfhNbfMe/9xn92nPkc4+Rj+kwsBx7lzjbEPO3t4iB + 5d0Po2Fos3A8C6bOMIO4b9X00LPj/6L6H+aMLlRPXGkam2roGWjqHeifq287l1Z80ofhOA+NNQg9/BpV + si4lAvGuLFXP2/hjK1UUjKVLeqXLOlbI46pIawC1n/FInVospirbFGk7lhrRyy19ynWMbBGrsMRHkZhf + glXjFxpViFWEhDM5hcU94gLHsIAbn5qsr6tPCIp2EdGP4rasYHId2O83uSNgfJPPyLeexGHwMzplnDT1 + rsfcOu+f1gbcRsSC5gK8RtjylsTh7VZ937zxoUfn/0UN/l/62MP8yaX8iWuZ41Nxw3UBvfHO7U62TQZW + DepW9co2DYq0n5OgnGOVcJQfW/mzyykI8xUpCQUFSNrly1iWSJoWSpjkixvlihtmLx+jHDwR00uRMUpS + tc7Qdy6w8qvQMbZHl4GSX8dw8CUQdHyhUUXCFfbZlw1R4QIHpy7G6vcjigTEeooKCp1VzaI4LZqOuoPB + MgZ/VNjvwprrS3cUXsiMk+95zL3tfWmt/601oXc2MOLWkUy0UjF1xhD6gUP7A7euB26d9yybl1Sqb/EU + /Lg/Y2pbct2+tBTOHF+pYhuNSgOjGi2zOhVatURALHuJ8jMrdCz9VUgIhrlL0nKgdSuunyqiFSOoFsqn + 7Mer6M2r6MWr6MOvHCCoFiSsFiylG6FqmWJML7bxLTWzcgQ5YlnSmLHD/pdesf+GL/6io4rhR0LyIOW0 + u0wbZJBwZh7xI9CkHhgajg2K8OEzKmJx6jrIMLDc5juy0WuYkRnHPnafeN999h2vc2t9sUeFdhcJXRBp + Yc3FOgOi15JYKYaJC4ezMO2B/gfsUm+tCsH479K68NMfRPV+l1B4PNVdI1o4Xft5l75qUd5ka0mHbBnz + AknDdDGdaBH1AAFFN15ZO24pS25JC0jf8snaCSq6iKphpz5cxy7T0qfa2C4iOzd/jvBtGBSu16j6G1C/ + QsLB3RupECScyV+ScAhF4tFBRxFLWA5COoncNvVM7sP7AyZ3Bkxs9h3/1nv4C3fYhhNXekbVBf7x9TUM + m6TPiREXGR1iTwbN+vcg/kEGPo/L50FeYew7LydbXeyIPgdVnHk6Uu6pslYlMqZ5oI6Ja4eKqnkKKzkI + ylnwSxkLSBsJypgIy1uIq9jL6niqWUSZuOcbOSZB+htFFVhcMxDKPjn7cvDiX/RY9SRLYhzYWiHh/OwC + xzCCoxiqp2Gf1NjY5E9z9+U3zmV36D/kO7UrcHSzd9939PavnJo/s2v92K7rQ0fA68w6H0jHkiU+yMgA + TJj5/GqhD03805/4Bugbcpc8ZTdhBWRCOUomTr40gzRT/WQ9g1h13WAFLU9xVQcxJUsxeWNxBQMpJUNZ + NTNFLZq6kRso/wa0UDUDZ5q9y9DIMEUMJIr+U2Awvwx7fP8aVD2LhIPohasT8d4FzYsqvBjULsp7t7S4 + xFHBOILbovq4a9seet0Op5JN1mXfWzd+a9f3uev0Bu9L7wQiaD1Hhgprx+kSVkK5Us+JUsK5UhnS5ue/ + cbn0rc3pfQYj3AqtMhJ+klLyQtKSotJiYlISEtISUjJS0nIysgoy8ko+/gFgBMEo/iTIgmfOUNYjDEgt + txX+7WPmfx+qniThLHO8GAxVBrZA3SRpEVUXo6jHtREN+rjwGNiihrEapx61KDpIa9jvNrTDZ+7boKsb + lqVEKT/BJ861t4IajzhBbeF5ia9cLEzT+MwGPzhHoPv60zr/Sx/5Xtjk2X/YvYzTNUHMzlvOlKakb6is + qaWkpqakrKyo+MOl8xcvXzz/w/m5c0h8pxn6VbDdGoOgFxbLcLC0+K8eNv+LUfWL6PXI2plhv/uI/Qxi + KkXpnJ0FXaKmvjYmOtpIVNVbyLCI32WULfjyPhRVcXc+iUbr4ddajGiijn7vaUXXeA7fBgofdk5ak197 + YWV0eVK0OvjGeqIdOr8lfp4t445s4Q3jsh9cyiZ8i9p8M3PpkeG2nqnR8d1dneAJMlAFh4hpcG9g/AR3 + MezpN7a0gVOESSi04/6l2HoZUPU4+5li1hN/VEZaRAcIN0eEMVBxGlua6hvrG5sbVOQUTWW1ghUdyuUj + JoQyL+1NuQFlR9z7sP/5aJ/9BpFQ842XN3+ynEL7gBxhHMhla4Uote91vbZ6ebZNNinglbou7M7H0fNb + ExeZs5bEi+5pV923bXrg3fEgrGcpuvdGVPuMf1WTc0q8pV95YSk8zHHJgE9dY2srbOvI8k9dPRztGptb + gS1sL/4bsfUvQxW13YYpIcbPnb39eNGxMwOdbfwC8IjnMDLBx5FBGAMfMqztHRxshg9FbW1NXW17V8fE + 9LiGlo6FjUtAWE5cUmdsaG8srTldprT6cEbfN3En3w89uzbg3Ft+51f7Nu901AxUhNcygRGl9rECKVjl + VokoxsiVcNAuvu3/C8UsZFJ0Lj6JIQ71pDFWcE+uHASNB9bND5zaH7h3PvREe6x9wbrhqmHZkGZSmoZn + emRiU2trU2sLTOrgl4GVsqqaWuzs1z2Orb4B/NT/lnrrX4MqyqYLNQflT4nVK/i/gScIl8ryquonTkVV + TWVtHUwr4ZMDmheM4HBq6uvaOtuCwiJ8gpPSi7uzqk/C/80/ecQW8u7OLTpmtbraReZy6c78cSHHQ1P3 + +XkqmHJmijCXE8eRZWBRqKomkBLMlcgQtQIJB30HgqoVTxvwVyH4Djnaz2Lvbkpa2Je+wJKzyF9EzMMV + K++pVd9Tr7mnUnVPAWSNknmRwhv8WSNCUUkSjiFOvlXV1ZCLwbC5pr4ehMequjpI0tc2NIJe29TWgR+c + esO8+GnxX4Cqx/1OG1paASZsHkMWBnDBwZMyGJ8yvE/JWXmCnffSsryCwqy8vOzcXPgM1jXWm1s7eoVm + ZVWO59efyqyajcqdcI8bhPmbunOLvHW1tHGuhG6ihEakpFqonHqQkLcua44Qa4kwK6jMlNcNY00KkOIo + Fw3QM5r91Afl/I3VJOsti9tS9T6lLbsmnCypUurFWIDekoy1erJcfzwbfI1FrNszZy+w5Syy5YDicZ0z + Y4I3KlXQwV2HBl/ElrY2RC8YbS6Hrrp6Ai8iT4+02EnSYt/Ai4ytfwGq8PIhRFF+p2VV1aAvI/CAe1NY + UgJUAUa/CFTV1cAcWXivqqKiFOiEcMupa2zw8Q+y80xMKBgBntIrZuMKpvxSRuBSaejZgVUCGYsSSaM0 + cZ1IOHWJa/hJGfkIhCnx5Ihxl4mDyLDsIUgZkFSLCORK+ukYNBx0PvWF97W3g2+9RWkk/+oWSekoE/Vi + hlI3mq7Qw0Xf9esEkh93pszvIyLsMLBYZMlaYM+6yZU5wx2bze9kJ65XUFDY2Nzc3NZGsFVXB7oRfig8 + VtU1gCeCnI5c/8Ji60VHFbXBRyDV0ISwBLNTEE0RfhCEYP4GrgRIzJU1Nb8++Dh0+uCDij9qaW9vbm3W + MLALSm6PK5zECc+e8E4ctgvrNfLuxHoC9ghkzPIl9BMkdEIktXxkdX3kDB2kYhQlyxRE6mDDLMNJmSgx + fCJYwVeGAUm5hHCGlJ6XYpqS/uQhh0tfelx9z/cmyvanivqvxLAVBXas238SQ+C1JWl+V+oC1P0Z2Frk + zLnDlX2ON6mEj24vqJ2XmwcIIW4RYNXA9qcS7xZgC6ELaZG6Kr6AOfFFRxXlJFjX1FJaUQW6KQxOs3Jy + QDrNzMyMDo/yd/HysaN7Wbv42tMDXXz8HTz87D18aG4Bjp547u/gied+du4hbn40bQsf5+TU1KHElJGI + +CEfuC/5dRq5tWk4toAzI2tVSZS3DeIxnlM09JNRNRGRkVaJVdXs1FXo0BBvVQGhD8x3ShaGkkdjJ0R4 + Sa46KZ5qSZlCCa9Q0TJTqQEhtbM7La997Hprne/t1QG3f2kg+DNhmlihMlxJkCLhjIKh5EYSuhYOwDki + E5lxkSNnnivnEldSDY83XcAgOTaxvKqqtqEBdw48Ka+sLGcAC20I7GF3vHgJ8YVG1aNA1Y7SGykPUQpS + C6np6VbyerEsFv1sgXNc8Re4E3/gSsIjznmuxPPcSed5knEucCf9wJ18kTf1gkjaBcmMswpZP+hWnDWs + m9Su6VaurJYtS5UqCZAvdVItN9Wp1DUsVzfM1zDJ0DCNVFCzFBWTFpEQ1fbVsh10gESCcqeecLMSR90y + 429ZyKqegap6Ke4GKTCVQYcXqBRVy+V3j+bOd+IcUhX4gVvu2m6Dm1/Y3X7P4/Zb/rdXIYwtbz+jusdm + /XKN/2Yo8Uf5MAruFfPfJ87vSJnfT2Era5E9e4Ej+ypnaht3QCC/aYCDJyQmqmqqUcvjnkhutQ0EWCQb + dpNs+OJcEl90VCHCMyxPq6h7HEKUGbtSL1vgAl8unIYIg5knf4GTweh6/HA8+l+uPMJvFmIw5YWLFrjy + 5w9n3tmafPPzuGvvRV1+O+LC+oiZj6LaN8UWH05MFkqN0coPsy3ytI4w07UysjR0a6RDNki7x1SsRRXQ + oWp2SnQPoYujXgLAwgGkcKCZRgBXI8xbKSRXyGebxJnmy9xmzTSjzv6jkPCtg+q3vqTdWu9xYzUWoAOx + AE0dXCGxH0EKLwa27qxgCyXXkYwFpqxFFhT12dc50ge4Q+MEaA4KRukpaUAV2Ef1jY1oRuAGgxIe9i3d + /S8KsF5oVFEULtx9UKQzrnvlDroWLew+iwJ5y9oKvAWLoC8fz1o4mvnzAY+ZOvg4U/YiSw6hyePxWPb8 + 3rS73yfB2Qa+RaSIXuHkQGloXciVTyKvbU+8LJB3UbfqtFNDu2tBQlSoYxddp9tcslUdyhwUqqgkCFRR + YHoEKXHq4490REldz1klLF4ioJ/F7RPNWk4/Ompy7Lwc7+XD6pc+dbq8jixAU+fmmyEEWFROJNiKvvNZ + 3Px3ifPbGJ5KB1HOZy2yZi9wZt/kzhzni8oUdLYT0cnOyGxsaW5tb0fV1UxpehFgYdn/n29rvdCowguE + gpR0nGtqUXTng8fAZnSdP3MRch1wRRMsgjIHVvAW9mXM701/6sEfLezHH4FICIoz4RLe+SiGQOqXFhKk + 2wRHP9h0bUpcOJGFL76kWrWoXXPJsKgkINy0ykKsWXUFVSvAWma7g/BeR/D0LE9UmFkigHFVCUkX89sl + cSb4sTSYc48Ky09sheWEz7V1hLXxszMqhS0YKr3/qJzfSpXzGfjGkBMXuXNv82VPCsWkCDv6WbshXHX2 + oAncgXCFdyDcgV6j6jesABio6kSLGQVEbWNjUnxiHavnIh+SWj6sHNH4md+RRrajYKmFluMzDtmaQnCC + Fxd+T+sfrU9RWnsrPu+Uox9Q9U08IAj5hkWBwiXBonnu/Is8KeW63sbxOnyV0vCe/D1Ured8DpDHVSUi + UiakkSvoFS6QZiZbwm0ytNnj0gcBNwm2GO2Jx8260Iz4OAZvBnJVxIb04QxSb3HkLPLk3eTJ7OMP9RE1 + LcwvbO/q6ujCeAdzhT5CTBr5h/12X/RYhTkMZq4QbmxubQ33Dx1lDl1gRb7LWED4+S6RSHSs7EUhoz31 + PDLcWmlUUmACc5ByzAJJlRIbIuEKF354vm3CjSwVv0UipPZdwpVvowYPeEdrmmtEKkGA7/m7e78Ju0cJ + VIyzVlygUlwhT8o4Ut7LQreU22F8o/el9+DrHHRrFYMxS/W6KEs6CluUzyDCM7DFmj3PnnWJMylHwCXM + 3R/D6d5+BCoMpIf/cQm/FxpVRButuwfhCgxmnLjImAGmgPkDqVgHAB8LXL+fVgf8+IYfRIJW+KW/3mZ5 + 4iMUpIgpzRsB0GL44U3fs296z73phXNulQ++Gv6IwGt9BIltOIgWb0fcWBcKG7exjZ5QSHO10VWOVeAt + /m112mch7JHINnS2Sb3PVSfJWy0lXCqrEafibW4Iy7ihrR5QzELJRbpfpA0RSpmjYu+etCFI3EJOROhK + Wziafv1EUhm/R5RvSHdf3wgEeYmEH1nL/ns2dZ/6r7zoqEKhgAEFsAV5WbSbczgcbh1MurIz+sL3oXOf + +E2/6zG11h305bk3PYnK2Ru/oXJGZT149pFFqFUBF1b7guAF3ZjBt5171jt0v+3Qv85xbI0rLNourvK7 + 9kbgcsB4ZDWI3zFGNFCnhb5jAT+Nbqknl0jUaf9bswnKxhJ3RuoiSR3GLVKcvVZMsEBKM1zZx9igitVh + +ktvmDphzeZnbAHuaHEhoH6XML8xYX5b4vyBlKvHExNF7SvKK4hGMkPC75/VhnyhUUWJ7uHCjHW27v5+ + BHk7Ae1x5rDLB2LAYJ79zn/sc8/hj+jD77uOvOM6/rbb9Bo6AHGeEXKeJaJHqTBceSPg0mq/s2u8J9e7 + 977n1PQRreJji9JPzMo3mDd+ZIOPYLcdjm2wbvt5bPxop498hVVBP60JPPee79D37vk8Nk5WWgoxcryF + 4myVK3sNv3zyK4OTlTy4cpfEE6o3QQ7xZhITypPUC1KJVTLr2Os697Ev4MWYOTLiFtqnJJRG3Pgy6vr2 + 2KsH43q5g8OcfMHIXdaG/EfZqi80qihxbByGiDSRT6mprg0XshpmDz1/OOrUrpCpTX7D33gOfUEf+hS2 + aW7DH7iNvOs2AfGFNR4UvJ6q7YH245VVgRfW+k++69H7kXPj53aFX5lnfGucttEwZ5NJxUabzq9dpj/2 + uvQuY/n4lxMYkj3/EwTIAriIjnOrPE++RR/4wLlqq00sr4GtgYq8ryRXshBzjsDxfIFjhfzHiviPFwsc + L8ERPFHGOOVCTJVCjAUbsmPzqFn/80Xy8bskZf0lmi1lSddIlbKEv+HsZz4/vR2IeTYOhCQufBz0w8bQ + C3siZpgjPeTMRsbHCCN3ahqyWP8gB/pFRxW1UfSIvEVIzDWV1T6SZtUC3sMswdMHQiZ2+Q9v9oKP49A3 + hANIUUwBL5AdoO0Bgjy0PZDOiPgCw7KW1FXQ9lgV/NNbgWfe8en8zDltq4nvfi2vw1rBxwyTmWzKj7v1 + 7fc9vSXk6qfhxAWe8tt91NnCFwFSYWQ6u9pjYo3bCMNOsm+9Y8+7Dm0f2dV9YVO01SKOycBZQVXNXlok + UJQ9TuBEGt/xDL7jmfwnsviOZ/Efz+Y7nssPzB0vEDhRIshUBpAtI+z5lT5SLeTg7Ry1sEfft51+7n2/ + c+t8T3/oO/dNwOldIdPM4R6yZiCxTc3OgpSL1UWg6p/ax3rRUbWy50n5UyK8Y2MY+5PZKRlusqbRorQC + LufG4569B32Gd/kMb/MeBgeQQTFlwMtt5AOi7TG7zvPMW94X3/RD4kNFhVhFRGNWB/+0PnDuI9++790L + DtIcOTXUBGT1JDXcNd3T9NNqlYv6uHKmdqec/yL2x3cjrr4Vdh0MQchiveGP6v7kGvfht52RKLs/dOze + 4Nj9sVP3J+R0kePY8Zlj61cOtVttc46YRwghgKkp+EgJxIhwJguxpQmwpQuwZAgwp/MzZ/EzZfMz5Qkw + FTGwVSHMVP3YhuAzXMFwA8U9VClO3sNYt/gErf87OrRMTu0MgQhvkK3H1MzJ6VNk/RUsN6Dqn9qW+Xeg + Ctj62Z+SQfMFzQHU+IHBwbq6+sSwGJqwdgiPWT6LY9dB79E9fiPbfYc2eQ196z74JX34UyK+MPGu++zb + nmfW/Iwt1Cg31oZAigjv9ekDQV08/j4SpgriMoryykbGDu4++T5BHXSXVnfDWj+5knie3OKDqY2bojs/ + D+r5yKvjQ5fmDfbNn9q1fOHQ9rVT5zcuPd+54XR/54rH3m/d+r+h931D7/8GH6S3b3Gu3+OQzWUVqKpv + 5qCqECormCTGkyHKmSHMkSnMni3EmivEXCDEVCzEXCHETCqz38YWMiNbmYhQmoSFs3qOpG0vs08iv01Z + SSmIa1jSf4QqUj/8I+HqX4MqvPMovySEdwR5sGgeUeNBpDlD8bQKsvM8De39eI3BYO485A0G8/B2n6GN + XkiOw5+7j21wn37PE+z4H9b4XVkdCIEr7EXdeDfsyhdh13bFLXBlzqqmOqia6BuYegQkRGf3BaRi+6rX + wLNd3b5R1aJOzaRcUztPWznFWCraTijIh9Mn6oRz+h6L0t12dXucm3a7tu52a9lNb99N79rl0bPLs3en + Z+92z75tnv1bPAe2eA1u8ejZ6dG2362K1SlZ0pJuoqfnrSwbJS2QKsadKcqRK8KWL8RcKMRcyghaj681 + PyNokcBWIcxdLiFZqmCYqK3jqm7jbtPa0QbOKiI66ioUo5Rl+t/fYvg3oYoCFvIgocbD+JRh70aYpZS3 + 25kzFA1waHgY8LIT143gsShjcelF9NrlO7LZe/Rbr4kvvGY/8jn/rh8kPRCroMBOmAsbou5uS4RA8pJq + WYVVfGJabW7tbGLxNFCFBSxd9zYoBGFVRsqsSMIwQ0I/UVovRtEwTts8WVWFFuwdFBscGezi42XsaC9t + YMen6cKr481jGMxtEsFlFs1uAdvSZBarDCZa7gm7wmOOxYcdyg47lh1xKD5hW8JtmStjEqWnZ+usKh8t + LZAhxplDsMVSIkTWmn8zaDH2BzlrJQQb5aRalNU7dAwqjbT8tRPTk4ZHR0EFg9zoPwKpF1TF/1nvLcqh + Cp0Y4qhLBa0VA0EQlykDQUp8gUED7OzujvYL9RAwyGF36DniO7E7cGqL3+w3/uc/Cfzp3aCba8mC+W1M + 3LCFsjkR2wFYJJ+zhLvJVErZycicCc/4IYuALjWnFgIpkwJxg3RxvXjYWMqbxGvQMnRoSQ7O7sg4iJQg + H0MoAQockEMaGR3thwXS4GB3by/sCzH9BZG6rra+tra2sryytKi0ILugIDs/Iyktyj/YRlIyVVO4w4y/ + 1Zo/xZWfFiYolyLMmyvMVizMXAZgPbfSYqwQctSLY6lQvFVZqUNbt8fEsNtMJ9EgISORhKtlU5N/QL/v + XxOrfs3+YyRECKZRBoKzFHd5OYD9bE45B90YYnQjbJTH6dh3xHduV9il78Kvfhx+C/c7KB+TnnUEJomY + 4ELA/UelypTE4ZAMorhHC+nRcWtVsKmGwIuEQaq4XpyUfoyCSbymbZaBU7a8uiX8mE6dPXPq7NnThM66 + fPD88UN9nIqjOAA9OQz7TDxC7Q07w47KgjWmPFO2rEN2zJUuLJF+7Bax3BI5AhwlQiy4HoJ88QyLOfQg + 4IIJ6y/hZgWpVjXlTm2tbgOddgPtMJ2mttZ/UL/v34eqp3CXH1lUEgdU0AApeDH4pY/y42nMyNLikz1F + jEs4XM/uj7jzbcydjzGNiSDhCsAiMh5x2CX/gSUnwLuTFtJr6tup5doCSDGkOFIk9eNkDOOUzBK07DL1 + HTOUtB2wu4wQhQNUEbicnkOwZOiIrECH8cHTJIIun2UXU/K9Ud8edQaGhuLDg6N0hfptuE7bsU5aH+ux + OVrleDTcn0kvlVugWJC9QvipTFdqIQcumPxNskJN8hItygodmjA10azWs/Swguw2MTWBVc7frt3wL0bV + 44lyJTlSFf2yfzOj9iK/P1wbgbA5gq0Y39A6bp/5PSl3v0+4++kjCy5qsem9yNObkmws6iGyqGhbJ2dZ + LGWciWV2Kf1YOeN4VatUXcccbZtYWWWj6tq6uXPnkPVg8g7onJw9DRwz0jEJlhRoyGHETuqQT5hlBNSZ + WYL7ZWGIZdtcJCysdrpqiFdYCEzbsZ+yOj5jdvikyb5Ri73VLocCIlnUc3j4KwSxUfNET4sCFvZUOesl + eBukxJoV5VvV1dq0NEK0uvp7VzyY/mb9vpcEVU9jxzPgNQ3vUBLAUGZRgaGkoHhQIApbD/PbGetW70Yu + 68AwhrhdGxO1TSplLMukTHIk9JPFdaOl9KJQSMEqXMsmQUHLNiomHlrWBE8MIRfgCeIOEJ1CqYfGI6FN + U2eS8KepA+UtxoETxM8HYZV8Ywx2NbnSMry+e/v7ooN94w1E+m24T1kznbE48oPp/isme3403zVhv7fA + 94hLAptMET87bn+/vhhWCrOXiPCXScElQKFWXSNLG2Q1qiNKuYX/nTfBlw1VT0jHMLx0iTYwEb4iomoz + qZGJ5wSSF5ky53elYECL1ZcVtSoMjxPZMxVMSgApcf1EMe1wCe1gecNIVYtoBW0Hd29/2FtSWY8KUdMz + lDbQU0a5VOx8/CBakMNw+yWGv4ysDRQCcIS8T/yYSdyCkW5paYm7tlS7Nf9FJ47rdsfvWh+8Z7573nTX + jxY721z3AVgiZQI/J8QaEbQYTlQInSgUQFuVK0tUuEBKtlxFPU8rKz8b8RKyAPgmscjwdw5wXlpUrTTl + 8QTDaVh6YIyIUiMpNAYUiYUTmRA2vvs9Y0PrkQba5TWhDkqFsmZIfBm460nphSkYhSoZeEvKa4PfApmN + 5Yr7NDqN5LeF2PNUSP3OqEBhjphJM9olRDIJ6wbjAN8IiMo+9qYNNNE77lyLLiw3HY/POh8pCDhuns7F + h07pSqACpKqEmcqFThQJYCjEnMjHmSIkkiMlV66smqBeVV+LzgtDw2j6by6tXnJUMSA11NYJ/YVurB/h + PhjpHvQDe9L8YdgqJZNY9Riqfng73EK1SM6sUNYkQ844QcksWsXIR1peo6mlGZDCQZ5iSFijxziFYEPt + MFEi3n+khY3BOfbssHAH44KO7u6e/j64R8Nf3tZQuchTsSdGJjtN2q5QSqJS/Mll1BphtExPFAucyOY/ + kcjLGsPHmyQikS+rWK4mb684ODK8rDU6/XeLrb38qAJBoLWji6x1d3ahgxXlE3rhRNz83hRottz9Op5I + Nj4aHl9dHRLFma5uUKBonqtqlalmFi4hp9Ha3nbqDBkQzZ4m5Ta6i/Dixi5vSzuEgdqg+ABG3ooCAlai + KXERssBD7C36KH0R6mCv9YkD9mxTW3t9Uwto/uCOgvQBCjz0YTCMyivOMw0wdqyys+m20ekykmpRh8/g + MoX6UaxCoMJ8+gQmial8zLG8nDECIhmScoVKckEKadkZDL01ojWK3t7ruuo3ltl/Z35Z+TSGf3MPKChA + FZa7I3xCzhyJXtiVhJW3u5/Holn1+ErCxIbwMP5UfbMiPadSDYswaQXN+qbGk+iwnkHj4BSanLhFNre3 + Q1GDQdCoBRQqqmvB1wBbv7G1DRBpbusg4iJNLfgI0ReprScSIwyVkScOJEbA5Qchu6C4NIdwskHILqiq + rUFMTcpMNkswpw94uY55WQ46aHabwNH5SVTViKCcYioUZMriZ0nk44jmF0wSl06Xl6LLRCXGnJwhKn7I + gLgQ/P1aoy9/rKIsnBFd2ru7kV8SYxJGjgfP70gkemjoLKwPB6ooZyVq8/j0h6GpQtm2DvU0/1Zb3wIz + h2AdI5u0zGw0yrt6se7cDs0WMDzB+QFFHbAARR0xBueRvkN1aWVVKREUYaiJ4PzyT3/xmeUVRGKksAhe + 5fBrBTUNmjAZuZna4brOfW7u4z4Oo+7G/TTlTn3Y0K8QEldkj7CthZUHbEBwJAkKx4vIBEsK64sWlZVQ + Vwoiijx7ilH8/a2l+r9sYvPfRinq84EqbJOCNt7ZQw7EGfLZnW7vTry1KfbW51G33g3HSgy2Q6l9dsIq + xobMhugqwXx/t1bvGMyYBwKTOj2jqu08Es1o3k5uvinpGeXVVaB3ooQH25NQ1AEsaDpUQNaBKNUUlZGD + 50RZhCG58cRBkMMBqx2YI59fUgLNiIqaavCSHb2dVYLVab2OTqPu9iN080F7zW5TODfzN8lhBfkX5LAa + kv6wHsidLqgZzRfgwaOgzN/R0006++jNnjlLAtUUGTP/zenvlUDVypoysIU15daODid+3TOHo67tiLn2 + XeTVDaFX1wdfxQrDI2ChfYW0ePWb+BbRonB6m3dMv0/SkH/KkE/igGdcl0tYjbV7up6Fj7aBdVRMHDyh + oXpQVUf46cvwqgCYKogUAk4VURYhKmePDj6NHAhTQZ6KITqCgIePgIhcXVcrbSCjlaZL63eyH3GjDbuY + 9NtqdJvItGkJNiuAjQhxh8fbVOT2Vyggmszj7M9W7MjUasXq42qHASgDUqSiwi2V+JeM/d2B6pVAFRWu + UEGDKIdCe3BkJC8nN4/L6ceDsVe3Rf/4deilD4N/fDuA7MYAWKCbgpGHPvtH0be+SxjgLYi1a/YI6XWL + HnCPHfCIH/BKGPSI6/eM63WLaqX5FOjbhKro0KztnDOysqC61tDcDGEgpMiK6qq0tJSwQN8gH/cQX49A + L9dQP69gH3qIj3sA3QmPOPhf6iMRgb7eLjQdMynzAhP7QRe7ETfrISfDPmvVLiOJVnX+RnmOekmKyLqC + Kmwr8Bbz60WxxjsebjXZP2ZyaNSKLdzfC/XfoyIduY8U6X9nm2olmbz8ddXK5R9rIWRTeRxOE+Ohrn7V + nJ6X9sX8uDXy/FdB5z4MgBDj5dX+4NUs85jJLgO0puIu7UqrkSsLdmpxDex2iuhzie53ixmgxw54xuMM + usf10iNb7fyLDWnhKloWGdnZ7Z0dSTGRwRr8bdYCZ+kiFz3FL3mKMx4lfvSSuOQledlb8kdvPErh8RKO + j9QP/nJD8ZpRjVYOw262Qy7mAw56vZbKnQbiLap8jXKg3zyxyc5TIaSczRPie7TOeGeP1o4Rvb3TpkdG + rDmTYqMwxsb0Gu00tNRXdLb/t8rhj/ytVwJVFLColiP2Qxj0pqkwZ79yXo8f9kVd2BJ25qvAMxv8L6z3 + /3FNwDKvhhKaYgwHb38Se2FHSqVUSZBdi5N/l0NoryPgFdXnykCYZ9ygZ3y/V3wvPaLRxj3Z2sy23k1z + PkzxQZjiw1C5h8EyDwKlHgRKPgiQIAdPfnnuB0mdj1WKbDA1H7QzG7Az6qNp9ZjJd+hBL4S3QfbxWoq1 + RkSkVMAwgysq5Fgrbc+YztZB1c3dajsGdPedtDxebcqNpjwqdMpl6e+f0jyOwlcFVfiZiYMXGtmM6Q2a + mXAQTQiOLuajzx0MP78tbO7rgLkNfufe9YOPEtx1qTKLkCDQeQe81kXcfi/y3PeJFSJFflZNdt6dUBO1 + De2xD+91Brxi+j3ikBwHfJMGQxNaKuNST0fZ3wlVux8o88BP4oGP6ANv4ceOyANvHHxE5J6v6OUQqdRi + Lf1eS51ec+AJIQqFFG58PL+EFGZ/tins1e4HJqx2nNPfclpj07Dypg6lrW3quwYND520YfNV4xsYGsQC + IwIV+rT/YKB6VeqqlbcRGcCNw5mdWMMRhtP0dGp0Ypag8+nD4ee2h53+NhDmInPv+V5Y6/fjan+KlrOM + rUfaB1jJOvtFbAlPnqdJvZVbm4Vfp3VQD+CF6IXQ5R5Hai+f2N646Iq+EPotH4X7niIPPAQe0Pl+fe7T + +a74Chany2u36St16CM+4a4n0gIFNrnlJgJDghtjPt4yQa8o5hGrnZd1N13U2jSrumlQaVOb4uYG5R3t + OgfGLJlLjXmW0x/V9sSI5h+qqKiX+hWKVdQPzBBkZ6wpPwJWcW5hrLDtxLGQczvD5r4PPPW57+wHPlhv + P7fG99Kby5SvFatwis0MqufZj6OL2HLc9arNnFpMvNohWWsT0oM9d4fwPhy7kB7vgMpuuvldB857tOP3 + bI7esz7yxLlld6wqhFunXBaSawJNCryNsqjKUUKRjnmp4HGM9gr4j+fxC6VyhXgc6tffelp944zKpjHl + Tb1Km1oUt9Yr72jV3j9scaLGjNtWS35sYoLU6aTt+Q+nv1cRVdSO8jKwTp7EnjsiFnpKIWJWg8zBP+yO + OLM5eOZL35Mfe8+853V6ndfZt3xA+fpF3FohMa8KOfNhVMmJLB+1CjvTOkfrJmdak4tdsyutyc62ydyu + yd0+Y9aMf8Fo55LhNnIMtj46W+YNtw077DXM5mYjepDkcoeYxFTOGOrl8R/P5DuexsucwqscypZmtb9X + c9uo2pYR1S2DKlt7VLa2K29vVd/dqXtgzOJEgzmXuSwfVmiw73Vq7gyWFNCj+mfT36uIKkaB9TiwYGZJ + 1pva2to8pUy7Wfx/3Bd9ZlvIyW/8Jj/zmvjIY/pdz9NvPw9bCF0/rQ278H4kotfZT2POfRKDx9nPYzu3 + JccK5JdrWd3W272kv5k6i4/Oj6bbQiKZIGpFCW6T5lMZtlkEMScmQ70kXpYkfq0gjnyLg/26u8e1d41p + 7xrR2jmouatXa3evzl5SS1kzNZlzWygK9w70nWGsEJIe1QxjPvOPpr9XFFW/AhYiFgEWWlnRXiGZfI5T + h0LO7Ayd2eQ//rX32Gce4x95TDGwdf4t38tvku4DONC/KT6DT7ixKnR4S+hVOc4l3a2Po2recEuf0z6F + Qn4Ipi2jCqsHRQxIJfOyxPGxxfDLh3JnWR7pMzgwpr9/0nD/uMF+PBnF/xodmrY4NmrFmqLD5eNkgzn0 + mfNwWWIEqpl/Zj7z6x7EK1dXrbwEyy6pDPtdstDHiFh4AqEsH0OHLC6HkQOBJ3cEjm3yGf7aY/gz9/GP + PE+96/XDOn/0SzHh+T2oIrPFN0JubLJcVNm/pLdlGVgGW64ZbY8KOsFdCZEFgiqydgdqfC4/UwofcxQP + awiPmB9PoBVzmfaRZr1jnYbH+4xP9BufGDBjGjBnbTFlT9TmNlcQhtvN9OwMiVLnzmErlVz9qH2v0X9S + Y+gVrdYff2NRwPqZvIqIBY7haRS80w0Nja6qFimctB5w7Xf6DG72gFX47Ge+Fz4IuArZ/tUMGc9fOb89 + /SOrghbYxZZ0ti3nQYMtP5ptt0nmZKsmCjAEVUh/IC5nCbAm8XNE8fOHCEpaCaoribnaWXq52PnTHYM9 + XYJwvFyjQvxzsjPauzqxiY+Uh0O4GLj3MSCF7b9/PPe9RhW5Ej5ayCQkMMqmC2u+jC3y06jiQeKzkzOM + 57JqP+IxuTcAhfyPX4Tc+CD8ztOc334NqRWB9Tvfmy2q70ZRRQHrvOVO7Ry+FSlRBqoEWbIFudKFBRLF + RTzFjWnGHd2dE9OTcHsjC4MUG+cRnxbJjkEIO4s2OkbIDGf5P7SV+kfa6E/9u69uBnwiaDEapMtb5NQK + OXIibojQ7ikrK7OVM0jltZs+FnF7c+zdL2PvbiCO30+IED2OqmXtNYYm0eX/+P/0kf11hf23dDff0d20 + oLd5zmoHSA2/RJUQW74wDE5EM6RFTUUrqisnkZPniOEv5W+I7hoOnlBewAAT2XJGa4qQHabwzb8Iie/V + mgP+nvciyYajmBJOYnsYb30GOQf0aOyS4yPj2NZVl1fz0gts0Cy9cCzzzjcMcs5jfoI/+z4w0iLKeTQj + Lr7he+ZNr5nV7tMfW52W2X9eY/Nl7c03dDbPWu6QLOZfGRUTVYUyYc5SMYFSKcliBUmaVElFKcM8F8RU + EAyXUYWaD1IA5DBYQ0h5uO79kcX53/Oy/G+f8zpW/WL1dKXpAHghAAyMjGCwg3AF6ouOgblXeE1AVG+0 + a1ulTOnpbxNvrgolerVvBOJgwrOypAWEQcEWollQZhtf69a/3qn3G8MhuQOTGltPa269pLN13GKnRAHf + z9ovNbD1EuGsFhesl5VpVtWu1FbzUdO20c7MzewfGoB40EkiHgTu1xTYHGANUcqf/8gywu8E2WtUPWWh + mfqFEUlSBocCgap3oN/aOSQkfcgvedg1ss/Os91JvzqBK7tpS+zIx2FTH4aefi8Ua++UiQjUkSFzNbfa + c2yda9d7Ds0baM171TqVDgxo7Z7U2TWnu2PEbLdsFi/RFSLOg0RXiLVWhAOeJU2ysm3qOj3Glj00Wh3N + PMvSKMjIjG5eVFGCCr29u6utqwsmb81t7S+UZv/rzsLvXYqHzDQ8ausam+ClBmBFxSZ4RJSHZIz4Jg07 + R/aDL6/u2KxiWaNlXG6oV2KuXZLBlv7TWobFAxRp/xMI8cipNfSedx0bPrap/MYynVWuTvNYv+HhccOD + M4b7Rkz2msRwsUCi45GuEHQ4YLfE1yQr0aKi1Kmr22tqOmBj1W9n1WNr3mhlkGmi5a3jGeJNXA6b4PLb + CKlw8Ds6X1T/3Nex6ik4o5xOKBdkbOHhaBk6+MR3BqQOY6fKIbzX0KtDyb5JkVarYFOlaFNpZFzeuSXm + JlwCIcP/RggU/ZD7hte7tG+wr/+KVryTZsIl5SzPVWXMOU1jnbM8Nm12KNGLhbNAhK3wka5QlQjmNvBa + EmxSkGpThx+TVo+JQZ+lSb+NWT/NtM/GuMtCt8xQzU/dN8wfXrrYaAWzo6uX2Lj9EdLY78xo/+2nvUbV + 01HFcDpphEctIJVbUGBgHeQZ14N9BOfIPqugbh06RK0aFG1rFWnVqjaVrmLhFz8IhhQW0QJZFXpplR/U + lAfed2n/3LF1i0vscbPkhCRoDTnoyBeYCs7Yc52zZRmwO0GL5efOF2MvEGEtImJoLFUi0DDmapAWblaU + blNX6NBV7zKCMZN+n4V+n7lBn7l+n4lep7FmmpayuQoiFmzl+4ex2kp0eP/b3/pf/fmvUfVMVCH9Eau0 + xsbE5BRD20i36C7nyF5aSDfSH4wqIZWmZFevYt+gZVGUdtDl5kdQ/Y+8/U4kUHV+tc/4enr/BpfOr50h + vWfJqYJ6Hxc6lPwFedmBOuIdVnxz9hxtdA7reEH+PHHOfFG2AmGWEmGWcqL9gk097C9gxUqyVUOhQ0e1 + 00C921Cj21Crx0ir21Cn01A7W1fVQq27vxe7h49Eqv5WGYXfBOVrVD0zA6J2gYkceAqJKSn6thHOkV32 + YT2WgV2GXu1qTs2IVcpQdnRsMrao6dwZeueT6LsfESMJlFbn3vIdf9d98DN630b3wiN2ybEJDPG0uTmI + XZ2Z6x/oj/BxTTMWHbHnafHmsksUFsqV4M4V48gTZi1gJESiK4QySwIEQCRE0WZlqVZ12XYthQ5tlS4d + rR59vU4j7QTd0NhQyF8tawn90+PkJ3D2GlVPQRUqFVAIwU4GoRTuugVFRVqmXs4RnXah3eb+nfoe7WqQ + IiKoalJ1arY0r58CDfpjuC9FwqcEd0AsZk2/7wm1yJFtPn6sBqiqT6GfSXgKDDkrBlu1qanRVU++2FKw + yYuPnigqmSvJlyvOnSfKUSDMyhDaQ0Ikbrx1YtjgA7wEmuSFmhUkW1WhUKXVradXb6jpptU/PEi0hBhd + UPxe/yAx/zcj0O//hNeoejqqwGdHwY7OAoipoP4pa1vaB9fZElR16Hm0qTk1Kdk34kDpytqk7gz0GuAF + j77o2nA0F35Y6zeHvb8vAya2+1txqU5OTz3qZxKBYapLPjU7Mzw2Wlxc6KEtnu8gkBIiYJYsJpUjgYTI + XSTOXioCmwkQaQAs4nNZR0IXaBG8jdJiLYqKHZqaHXo6sXqNzU1A1fD4OOhDPf2DuLe+II6mr1H19FIX + TK8u/JIYFMLu/r6gsEhT53jH0C5L/w4DjxZ1pwYUVQhXuAlaGtWcBbMevIl1RA8SqLq8JuDS+0GXvwg9 + sz3Mll8XjQlGiKKEhKB0NYHMRZSGp0lXs72rIzTAO0BfuMieN9OLhx4loJEuIlYgxl0uxl4NuxvGqswj + q0t22HTVy0q2Kiu3a2oW6OYU5ABV0D8CexY+ufD3bmhpg3YDAu0KvP6RG+JrVD0dVRSFEK5o4HsNj43h + Gq+hZ03zr7YJajPybNBwqlGmVSnQahRs6830q89+GEVsLOEww5jhXFsddOOdsDufRt3ZGNcimEBXcfNw + 9obsLLqpUFTrGxocGBkGgQyzIKBqdHwMm5wFhXm26pJJhgK1Vmy1zqxp3uzwb1bL5ueuIJYkK7Md2DBx + 10qKNMihBa9apeUb6T95chprYVAlgd8d/L1BkoZHdW1DU0NzKxQfGD6UxDb3bxZef42qp6OKUaOMULQc + QvaaOQkjWkMrX1pgvZlPg65LpYptqYJViZxVmYFOydRH4einr4g1wPGWCG5/HD3/ffz8wbQfBfLrtQsC + zKItjJwTklJBWQawhsZGR8ZGSdyahCTzOIRrG1sao0ICfLXFay14TzmwnXQ43u9yLDaSnbvmMcoypLBr + GOGqSUmpQUPVTb2prQWZtKu3F/UfGPpAFQjTlOIDZEWgMFPX1AJNkb8ZXq9R9bxmD6EQEiFuctXCNT63 + oNDMKcbav9bYo0rLoVjZpkDeIl9DL7cG/kSrfH92YgIBeg3DGRU+kTuSiSk3S9Yl4YJG3bJw21w7y1B9 + PdvI6DhEL0RBRsTCVBsIG8Opb6yn68icpQveo7MvORyboR+XqSReXMt5EHfDWjHeBmmRZgW5NnWNUm11 + L03fCP/ahvqWdijStKIVApY9IEVUQ6AgwhAOgRYNOroUvCB1RPJjP8wp/0LbiNeo+o0WIllkACeHsS86 + Pjnp6RNs4Zpo5Vdj7F6u41CkapOnapoVzBU19bb7qTWeZ1d7owV6FcaCYBG+HY6FGUhkQYB0AYbv8F9l + zroiUDCgVl5mVhFjle1oEmSobZ2dk4cMODgyhNUXbFPNzp2CAkOVk9yiv8gDd+5L7mz6JcQ6cDkPkomh + GFe9pFCzvHSbmma3gWGHucH/396ZBzd9nnm8m6ZJUzYptE2bDLtpNtm2aWfZbruLiYkL+D7lQ5dv+ZAl + 67Ksw/JtI9nGB+Ywlx18SD4k3zY2vrDBJ75wjG8w0LTd2clm0+xOgJ3Z//f7vj/JCEMwycAkMJp5x2PA + 1mQmn3ne532O77dZIjyWEKmNqqj+EHihxYSgRdhiZEXwlYYuHOCF6AUJpKGnfDk6qNqCKvvdVOwazH40 + l5lboMg8o8rrUBi6hBktgpRGRVzN4LuFiz/E6oR+/WWyOoHx9r/CKhLmtttPkYj1DrHcvfMvJhjZ336/ + /n8OWD7xaVnjtl9K7KxMrs+WFSmlqQSv2RlQNb8wf1wt+PeS0P8r9P9c71ZURzo5du7zRLkfUtgBRLk/ + PmFKLptRJU1qkgaTRbWJEXlRMerYszWV0Bvq7unpG7wAaRCrTAhRXSOyR7glz2M8+QKaiSNoJj6N0OWg + aut2h22Vnjid4B2HpBtKxhJtkTK3LimvIzG3XaxrKuaenXr70NJPDMvbDWt0dQJVKwg3wMWP0QKhzrZV + 8Aq4/c/UlNup/rZzwxf7LZ96Ny9GdXZouk9nmYvSTxnU+Tmy9NKklPEc7d180Z003lAhi3We7AluPAah + 3O96IcSHNHYE6BjGTcokM8lJs1rlTArwkg8p4+sSQvPC2Qp29qEcc3Njb1/fhYtDiFJEggayR/SKhNQR + 2MLNiJcj3o9PNvFyULU1VcxuKll6xj1IFbZX1tZQc0/NzBOrCiRp5bJsszqtpSKkcvq9kqU3C5Zfz1vZ + YVh7NW8dK18v5cM0FXaEcB0nNa03ymFCfvsX1cQq/Leme3i5Wj4Pavs0quszUd9/SXs/k/X8Nbr9iwDz + beeKP+0uKsgUHehibVCF8hUE1j0GyXoqe0QQNp4guCwVTiUlTidLZ1QYdgBhCuCF6NWYGHE4iq3kFh4r + aulo6zzfDT0j3I9WwaM+qB1BBakfj8e+waEnGLocVD0WVRvTfFSjwer4hXFNPL6qjLXR8cp4uUGlrDgd + bby858S1nxet7jy08tP85R/loch+bZvh5sv5aA5+8t1iIjjz/TKiNsPg9Us7vPaa77o33fVrvRvY/r9B + HXe9W+84NeAHPn/j9JVdecn6GJiabgILEct7KJQ1HMkdjQ0bF0VNSDDpQPFSSmfUYCt5Vpc0rZWPqcQd + 0qhjAl4KT5ImrW2o6+7rRWqPxjkCGLS1umzSbdaqxANFr8evqju2IR6LJ/tNL8Y5B8PHBCxiNkHEomBl + g6omKkaW5tboKHlGTGGT/+n53x5ff7uErKr+LH/5x4blHxpW/la/9or+xkuGj18sgDc9iqUk69pB8fp5 + JcFrF7Ku+jvO5jsuFnL+rR6p2Bc7UbU/9em2I2O7cpL00Uyp3Z4tJPJgC/0ctAvRjeaNxUPuEXjFTSoS + CF4q4KVA6MKZ1sjHVaJOqaA8lpfK1xnSMMcMwUgiSNkP5VJakqBVCdySoA3SprgZRycmv0a93hGrvgJb + 1gSLLk1AxRX7CNA1gAzLOvWRw1dMA6PJk52hT+Wmmr1Kr+46vv4PJatg6838xdcNCzv0C68Rb1VYhQMv + Er1eKEbhlNTlf3TamtQDr18bb/+q5ot3qHUFotr3j//3i0dhsTz5Xo4qR7DJj97mCk685pl2IelGj8Rg + 2CFiAsMOVrzE00p6P6rlMxpELwXw6pJGHI0OkbBr6k3QCkTi1Yu4ReX/upHO06yr/8IQiqsYCoISM9h6 + /GKEg6qvQNX9S8/U0wF2NFh6oXhRayRysGI1MzdXZ6pXB0obDhiWdh1df+fw6luHyCb0G4arr+vhf4kH + 4/K2g4he698zwBn6Ly8cwqwfqaNCPxIxDBbwONQSzGqI8p0jn714ePXN/EqOMrScBwf5B/2SQBi60Qxe + UOijeAk4o3GhYwlRE4kxk9L4KQXwkjDp14xGPq0WX5RH18RydTzD4fyW9jZoBRK8kHgNDBCNSZpy9Q9d + ZNh6/Neig6qvRhVzITJpls2Jyc7oy2aNxHjmoNnX3d2j9I2r3pc29fuS5V+XLr5buPBWwcLOvMU38jai + 19VtOVd/kLPwSjbswVe/dxBzpH98IR/7OZ98pwjDyhtVe2YT+j9fLln6uzwTSyUujvRqDkKy9WV4MZcj + dIt8LoYFXopij8Tcw2tSnjClwDAgJSxZMpYU3ywKL4jgy0NrGkyQokTRi9yMNOvCIWwNDkH6Gxrj6GFv + 2QJyUPV1qLrH1n142aLXhhcms7C6vj48MlqaWWDwkDX8IfvCnsK5XYeX/7F45a1DyzsLgNfC6/p5+Nrv + INb286/lzG/LXvhBztLLuQxhGIH/j78pxF4hI6ZFPe6PfPrS4es/LehyST2oTIg8xfdsCYTL95fgRUy5 + ME4D5T7gxbLiJYwYFwsuk+yepl/JslmVYlIl7VHEnhKF6qIjJfHllWdRRMWDkZFqxkEMQxkCrgVbBi0H + VV+fqvskG2w6MzYfOeKnZTXCtG0ef4SF1fZOfVJmho/kQ9eMiX8tvv6rUmiELL2VP78zj6g5vEn8oWE/ + PvfjnCvbs+deywJhi6/kYF75Qbyozf3hP20vnP5lbpOXVqeT+tYKXLpC9tr3De9P7Ynn20AQVPy8L4ZC + ITKEpF/CSHo5gi0Jqql4Nk6nJo9mJXcVyMqLIzVqQ1EhBoFIuYvqLpPh2McAy0HVE6DqYXhhyoXaFNq8 + 2ohNIc26kN1jdqV/YMCQlHnUVdnjnD/5u6KpXSVj/1Q4tqto9DeFI+8dGvlFwci7+ZfeMQy/bbj094bh + nYbRn+WN/SRvekfBR9sLl14tWX219Marx9ZfO35tx8krO8+Y9xp1MU3h6Y2BxRVeFTkHLCKXc1znR+IF + qW1UvFBKhUwybzQufEIUe5nMyCfO4Nmoks2oZROp8j69sDydK4+oNdf1wuV3aBAZPUIX0iz0E3EVMrsY + Dw7bOKh6klRt3Iy2+hZxCbzHFqMOQh1WN/Bqa247UXhUI1BKgsUSnkoZk6+WlmsVNRqpSS2rSZIYFWKj + LKEmESfOKIo1iWJqxYL6xJjGxNhWSVyHNOGcUNIrUPaHaS7wUwb5ugFueg87t9n/SJlLR+hDbATvi14Y + NEXoYjNscUfjIsZF0ZeluBbjJ+XCKdQmFIkTalGvMrJMwJFxG5osqERgdgOyOVhIxFDXl3WpHVQ9earu + C120KE/sx4kP4DX6ZiRaCVYD33sevsR+HJNSeIWVHjvBj5ZHS/Tx6hMJ6SZRZmNCRlNMaqMgxRKhNoer + zKHJFsxK8JStPFUHT9XN0/TyNP38FEqV7bCz2/a1CB5B1aail3VG/lIErkVa9CLPRiReaAeJphWiCbmw + OzHqsCBFn4p7EPOxY5jcmoKH2cNnHxxUPUWq7JN6TNRg9oGxV12+h9f1DQPmjdoErkgQNjg8fKTsVGiU + JEqcKdScEGfUUryaY3VN0drGCLUlTNUYqmrlqzq4agIWX3sfWJzMTtd62ZZU2Q+a2rIuUpVAL4g1HIUV + jPBxIdZ7iPs84taILK46np8UOjh8afrK7OQMZi1wA847dpefOkOPaG7Y6hFkYIvBa4niZbUf3xTASGX1 + JoZFp2dnW9rbk9Tp0aJ0obJUllopyzQL05tjdI0RmqZwdWuouoOvPgfjcYDF0yJikaCFe9C7PHdvj7V7 + +Jh4MaPMpOjVH+Q2iHYQP5C2gyKRdU1KEqZk4gl5QnNiuDoCQ4IYDpuDs+FVfN2sw+aIVd8AZ1a86Dwg + TeoZsRfi8m1ncE9zL1vVHppHmE6GAVN6tkEoVGapcw9mnUrOqBMStlrCNe0ELHUPT9PH0w4ALF5qP6u4 + Yl9b5NY8oQtk1wiyuxlJ1oXNRMQtrFOHYjNxQoiFRNFlmahFnJKvw+wGHcMnUmybDJgcVH0DVD34ZmTM + odFkZGyYmZcjI1VlvRmBFz24HLGdUVdXlxUf2qSLr89IKdQVJmnPCtRNoap2nrqbqyZBC2k7O7vduyJ3 + 7/mgrcF60Bvctn/B1LpQSvW/FBY4HM4bi46eEMaPi+PK4olbOHF2XcV/+SaNPwdV3yRVm65LRkOLmWm2 + xTDq8k0fj6vrZJeQKU/gXBy+lBIV1KvyW0j1GdMENyqFOclF4Sqk8J1M0OLqeoP19e5Gzd7zgZCdoQf6 + M8xh/mhtVG/1VCR7Y5gcdB0I8hvgcS9GRg3HxbeL0gszyKYQcQsnEcte+chB1beIqofGMFqbsDWFaGXV + muDfWMeifVmhvlHuezPd9ZbWZTzZT6apClNbU3iu+jxXdz44t869Uvt+B2tPh4/VGQDmAK1exFr3nA8R + ZGMge1i4sv9LsvLa6evWxvLv5PJ7ImJ74+My47HKwdjQY9PV/hJ0UPVtpMoeL7ue473KKqPmiNCFen1D + bXW5JGBR5/7HNNcKXaYgFWsaAKuNm9zGUXaGKNtYWpNbkc6pynd3ldvus25OlW5O1e5OJg+neuI94dTs + tafNG24Ue855E8vdbl+iU4oDVwHmm25f/BMhss7DpcbLs44V3MoX9MTFZcZBOYIYhlOPEwdV32qSHvqK + ZFRxiauFtfpFEnzClrWHfa5UFDyV4j2fyTFkncH+T4S2hZ/cxFFYQqT1gYlGP+lJ93T1B6Us5zIPlzOe + LhWeLlVeH9R4ORs93zd57AFhdZ4EMrPnbgtQ88BX2/HYbfbYXee+u9pt98kDH5xw96wKYDfxYzrjxGni + a9exvkEVlFfXHFQ9e1Rtuhyt84MYTF27BpVsCDdAFVAvF3SpA2ay+JXZ+uT0upgUS7iqga8wciRnQ8Sn + giWlfjqt52GeR7k/yPA0Brib/KGavN/o+wejt4vJ+wOT116jJ+GsxuO+U+W+50P3Paddncvc9p/08jcF + h3VFRVfHGs21kOamDwiIKDtiFZ1meaYPnR8kz0ZmlJ7MD9LaaXtrU7GYPaz1X0hnXU4J7leGdEtZbSJf + S4JfXYJvpSRQk8Rh53OCqjmBDRyWme1nDvZuCPSsZ3nUB7jVB7hin6fOb38tOftqfckx+eyr8YFb+P5y + b9dyH9/qQF5bRFRbDE/Nn70yS2xOQBXjx2RXtXLkVc8qXrYLkYBFB1OhzE5a11cXF2urPyxLCJhSH7iV + 4nw92WlRsfuqwmlRtbcl8YCM4x7A9g5SBPOPh4WZIzhNYSEWXqCF429mB1jYPuYgb3OgVwMLnG0cDxMJ + bN41rIDaEK4lLMwYydFxO8+fgy0FPJ7xMn3QN9VB1bNKlW14kKh54wnGTNMjzWLqDrPzc+XHSo4L/fqU + nlNqN+iwVSZ4pokjIfsIT4C5hfnG1katXstT8blZPO6hUE4Zn1POD6nmB9XwAo0cFjlsnAATm2ViB9Vy + Q6p47GPckCw2W8SF0t+tPxPzHDidEA8B6sdkH/sdVD3DVFnBorchbWBj4pkY8linbm7ehHwDLOuNleWW + etPIyPDSygqslKzn44+BF/4GwiQYn+roOmc0m05XnSk5UVpw9BDOwRID8zW/FBbk+pNnTzW2tYxfnlhc + JR9i9WOymedskul2UPVsU2U/8cyYHlqn6WnhFNyQRaCbN2/cuoXbiljfUDclfA/DEmpbYnfoTzIV/E0H + f4+f3PgQ/C7jxwQvQqZj49Daex4wevCpca/0wKwAkbhl3dQg5XgKCgxwQAMOnARxYFuCdhD+dQ3f00O6 + Qw89N27Cg+kaftf2i7RGBXOKh/sxOWLVcwUZ0/OhZq20ZEq2gKghz/X1lWs41/FHpPaYlbCNS1jHoFFz + wo+RkWhm6H7jED8fWJ5YDz6EmuegmP4osxMHVc8VVQ9b0wBeiF4rSLyAGjIw9BnJghDdEbL59lDrHnqo + SY4VO/tviLHPyho+h/mQR1vGOah6Dqna1PBhLseN82BLeyPC2Wgj86v0wMyH+PkwIJJDP2fLUp+DqueZ + qi3/9z+lH/h/MtLZz1XXTuUAAAAASUVORK5CYII= + + + + AboutForm + + \ No newline at end of file diff --git a/AniMax/AniMax.cs b/AniMax/AniMax.cs new file mode 100644 index 0000000..86a106f --- /dev/null +++ b/AniMax/AniMax.cs @@ -0,0 +1,39 @@ +using System; +using System.Windows.Forms; + +namespace SpiffCode +{ + /// + /// Summary description for AniMax. + /// + public class AniMax + { + private static Form s_frmMain; + + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main(string[] astrArgs) { + string strOpenFileName = null; + if (astrArgs.Length != 0) + strOpenFileName = astrArgs[0]; + + Globals.NullDocument = new AnimDoc(Globals.TileSize, + Globals.FrameRate); + Globals.NullDocument.Dirty = false; + Globals.ActiveDocument = Globals.NullDocument; + + s_frmMain = new MainForm(strOpenFileName); +#if true + Application.Run(s_frmMain); +#else + try { + Application.Run(s_frmMain); + } catch (Exception ex) { + MessageBox.Show(ex.ToString()); + } +#endif + } + } +} diff --git a/AniMax/AniMax.csproj b/AniMax/AniMax.csproj new file mode 100644 index 0000000..a76ab21 --- /dev/null +++ b/AniMax/AniMax.csproj @@ -0,0 +1,161 @@ + + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {FBB48F3B-5FB4-4D87-AD16-E17C2FB9FEFA} + WinExe + SpiffCode + AniMax + + + true + full + false + bin\Debug\ + prompt + 4 + false + -unsafe + + + none + false + bin\Release\ + prompt + 4 + false + -unsafe + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + misc.cs + + + palette.cs + + + tbitmap.cs + + + + + AboutForm.cs + + + BitmapsForm.cs + + + CombinerForm.cs + + + FrameControl.cs + + + MainForm.cs + + + MiscControls.cs + + + OptionsForm.cs + + + PerPixelAlphaForm.cs + + + PreviewControl.cs + + + PreviewPanel.cs + + + ReplaceColorsForm.cs + + + StripControl.cs + + + StripForm.cs + + + StripProperties.cs + + + StripsForm.cs + + + WallPreviewForm.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + False + MagicLibrary.dll + + + False + SharpZipLib.dll + + + + + + + + + \ No newline at end of file diff --git a/AniMax/AniMax.doc b/AniMax/AniMax.doc new file mode 100644 index 0000000..b5437be Binary files /dev/null and b/AniMax/AniMax.doc differ diff --git a/AniMax/AniMax.exe.manifest b/AniMax/AniMax.exe.manifest new file mode 100644 index 0000000..af3f84b --- /dev/null +++ b/AniMax/AniMax.exe.manifest @@ -0,0 +1,22 @@ + + + +Animation Editor + + + + + + diff --git a/AniMax/AniMax.sln b/AniMax/AniMax.sln new file mode 100644 index 0000000..61b9260 --- /dev/null +++ b/AniMax/AniMax.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AniMax", "AniMax.csproj", "{FBB48F3B-5FB4-4D87-AD16-E17C2FB9FEFA}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {FBB48F3B-5FB4-4D87-AD16-E17C2FB9FEFA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBB48F3B-5FB4-4D87-AD16-E17C2FB9FEFA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBB48F3B-5FB4-4D87-AD16-E17C2FB9FEFA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBB48F3B-5FB4-4D87-AD16-E17C2FB9FEFA}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(MonoDevelopProperties) = preSolution + StartupItem = AniMax.csproj + EndGlobalSection +EndGlobal diff --git a/AniMax/AnimDoc.cs b/AniMax/AnimDoc.cs new file mode 100644 index 0000000..bae71a1 --- /dev/null +++ b/AniMax/AnimDoc.cs @@ -0,0 +1,690 @@ +using System; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters.Soap; +using System.IO; +using System.Collections; +using System.Windows.Forms; +using System.Drawing; +using System.Text; // For ASCIIEncoding +using SpiffLib; +using ICSharpCode.SharpZipLib.Zip; +using System.Collections.Specialized; +using System.Text.RegularExpressions; +using System.Diagnostics; + +namespace SpiffCode +{ + /// + /// Summary description for AnimDoc. + /// + [Serializable] + public class AnimDoc : ISerializable + { + // Persistable state + + private int m_nTileSize; + private XBitmapSet m_xbms; + private StripSet m_stps; + private int m_msFrameRate; + + // + + private bool m_fDirty = false; + private bool m_fHires = false; + private string m_strFileName = "untitled.amx"; + private Strip m_stpActive; + + // Public properties + + public XBitmapSet XBitmapSet { + get { + return m_xbms; + } + } + + public StripSet StripSet { + get { + return m_stps; + } + } + + public bool Hires { + get { + return m_fHires; + } + set { + m_fHires = value; + } + } + + // Exposed for anyone who wants to keep track of this AnimDoc's ActiveStrip + + public event EventHandler ActiveStripChanged; + + public Strip ActiveStrip { + get { + return m_stpActive; + } + set { + m_stpActive = value; + if (ActiveStripChanged != null) + ActiveStripChanged(this, EventArgs.Empty); + } + } + + public bool Dirty { + get { + return m_fDirty; + } + set { + m_fDirty = value; + } + } + + public string FileName { + get { + return m_strFileName; + } + set { + m_strFileName = value; + Dirty = true; + } + } + + public int FrameRate { + get { + return m_msFrameRate; + } + set { + m_msFrameRate = value; + } + } + + public int TileSize { + get { + return m_nTileSize; + } + set { + m_nTileSize = value; + Dirty = true; + } + } + + // + + public AnimDoc(int nTileSize, int cmsFrameRate) { + m_nTileSize = nTileSize; + m_msFrameRate = cmsFrameRate; + m_xbms = new XBitmapSet(); + m_stps = new StripSet(); + m_fDirty = true; + } + + public static AnimDoc Load(string strFileName) { + if (!File.Exists(strFileName)) + throw new FileNotFoundException("File Not found", strFileName); + + return Load(strFileName, null); + } + + public static AnimDoc Load(string strFileName, Stream stmZamx) { + string strFileNameOrig = strFileName; + string strExt = Path.GetExtension(strFileName).ToLower(); + bool fZip = strExt == ".zip" || strExt == ".zamx"; + + string strCurrentDirSav = null; + string strTempDir = null; + + // Remember current dir + + strCurrentDirSav = Directory.GetCurrentDirectory(); + + if (fZip) { + // Change current dir to temp dir + + strTempDir = Path.Combine(Path.GetTempPath(), "AniMax_temp_extract_dir"); + Directory.CreateDirectory(strTempDir); + Directory.SetCurrentDirectory(strTempDir); + + // Extract the .zip to the temp dir + + ZipInputStream zipi = new ZipInputStream(stmZamx != null ? stmZamx : File.OpenRead(strFileName)); + + ZipEntry zipe; + while ((zipe = zipi.GetNextEntry()) != null) { + + string strDir = Path.GetDirectoryName(zipe.Name); + if (Path.GetExtension(zipe.Name).ToLower() == ".amx") + strFileName = zipe.Name; + + if (strDir != null && strDir != "") { + if (!Directory.Exists(strDir)) + Directory.CreateDirectory(strDir); + } + + FileStream stm = File.Create(zipe.Name); + byte[] abT = new byte[zipe.Size]; + + // For some reason due to its implementation, ZipInputStream.Read can return + // fewer than the requested number of bytes. Loop until we have them all. + + while (true) { + int cbRead = zipi.Read(abT, 0, abT.Length); + if (cbRead <= 0) + break; + stm.Write(abT, 0, cbRead); + } + + stm.Close(); + } + + zipi.Close(); + + // Convert filename from, say, c:\ht\data\foo.zip or foo.zamx to foo.amx + + strFileName = Path.GetFileNameWithoutExtension(strFileName) + ".amx"; + } + + FileStream stmAmx = File.Open(strFileName, FileMode.Open, FileAccess.Read); + SoapFormatter spfmt = new SoapFormatter(); + spfmt.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple; + spfmt.Binder = new RelaxedSerializationBinder(); + + if (!fZip) { + // If .amx being loaded is in a directory other than the current one, + // change to it so deserialization will find the contained bitmaps in + // their proper place. + + string strPath = Path.GetDirectoryName(strFileName); + if (strPath != null && strPath != "") + Directory.SetCurrentDirectory(strPath); + } + + AnimDoc doc = null; + try { + doc = (AnimDoc)spfmt.Deserialize(stmAmx); + } catch (Exception ex) { + MessageBox.Show(ex.ToString()); + Console.WriteLine(ex); + } + stmAmx.Close(); + + // Restore current dir (NOTE: can't delete temp dir until it isn't current) + + Directory.SetCurrentDirectory(strCurrentDirSav); + + if (fZip) { + + // Delete temp extraction dir and its contents + + Directory.Delete(strTempDir, true); + } + + if (doc == null) + return null; + + doc.m_strFileName = strFileNameOrig; + return doc; + } + + public void Save(string strFileName) { + string strExt = Path.GetExtension(strFileName).ToLower(); + bool fZip = strExt == ".zip" || strExt == ".zamx"; + + // Update the XBitmaps to have paths relative to the specified file + // in a subdirectory named after the file. + + m_strFileName = strFileName; + + // Create a sub-directory for all the Bitmaps + // UNDONE: clean it out? + + string strName = Path.GetFileNameWithoutExtension(strFileName); + string strBitmapDir = Path.Combine(Path.GetDirectoryName(strFileName), strName); + + foreach (XBitmap xbm in XBitmapSet) + xbm.FileName = Path.Combine(strName, Path.GetFileName(xbm.FileName)); + + string strAmxFileName = fZip ? strFileName + ".temporary_file" : strFileName; + + FileStream stm = File.Open(strAmxFileName, FileMode.Create, FileAccess.Write); + SoapFormatter spfmt = new SoapFormatter(); + + // This cuts down on the verbosity of the generated XML file + + spfmt.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple; + + spfmt.Serialize(stm, this); + stm.Close(); + + if (fZip) { + ZipOutputStream zipo = null; + zipo = new ZipOutputStream(File.Create(strFileName)); + zipo.SetLevel(9); // maximum compression + + // Read temporary .amx file + + stm = File.OpenRead(strAmxFileName); + byte[] abT = new byte[stm.Length]; + stm.Read(abT, 0, abT.Length); + stm.Close(); + + // Delete temporary .amx file + + File.Delete(strAmxFileName); + + // Write .amx file to .zip + + ZipEntry zipe = new ZipEntry(strName + ".amx"); + zipo.PutNextEntry(zipe); + zipo.Write(abT, 0, abT.Length); + + // Write the bitmaps too + + foreach (XBitmap xbm in m_xbms) { + + // Write temporary bitmap file + + xbm.Save(strAmxFileName); + + // Read temporary bitmap file + + stm = File.OpenRead(strAmxFileName); + abT = new byte[stm.Length]; + stm.Read(abT, 0, abT.Length); + stm.Close(); + + // Delete the temporary bitmap file + + File.Delete(strAmxFileName); + + // Write bitmap to .zip + + zipe = new ZipEntry(Path.Combine(strName, Path.GetFileName(xbm.FileName))); + zipo.PutNextEntry(zipe); + zipo.Write(abT, 0, abT.Length); + } + + zipo.Finish(); + zipo.Close(); + } else { + + // Save the Bitmaps too + + Directory.CreateDirectory(strBitmapDir); + + foreach (XBitmap xbm in m_xbms) + xbm.Save(Path.Combine(strBitmapDir, Path.GetFileName(xbm.FileName))); + } + m_fDirty = false; + } + + public static bool ParseNameValueString(string str, out string strName, out string strValue) { + int ichEquals = str.IndexOf('='); + if (ichEquals == -1) { + strName = null; + strValue = null; + return false; + } + strName = str.Substring(0, ichEquals).Trim(); + strValue = str.Substring(ichEquals + 1); + return true; + } + + public bool Import(string[] astrFileNames) { + + // Is this a SideWinder framedata.txt file? + + if (astrFileNames.Length == 1 && Path.GetFileName(astrFileNames[0]).ToLower() == "framedata.txt") { + + // Yep, open it up and parse it + + StreamReader stmr = new StreamReader(astrFileNames[0]); + int iLine = 0; + string str; + + do { + iLine++; + str = stmr.ReadLine(); + } while (str == ""); // skip blank lines + + if (str == null) { + MessageBox.Show(null, "Reached the end of the file before it was expected", "Error"); + return false; + } + + string strName, strValue; + if (!ParseNameValueString(str, out strName, out strValue)) { + MessageBox.Show(null, String.Format("Syntax error on line %d: %s", iLine, str), "Error"); + return false; + } + + if (strName != "cfrm") { + MessageBox.Show(null, "Expected a 'cfrm =' statement but didn't find it", "Error"); + return false; + } + + // Find a unique name for this strip + + int iStrip = 0; + while (StripSet["strip" + iStrip] != null) + iStrip++; + Strip stp = new Strip("strip" + iStrip); + StripSet.Add(stp); + + int cfr = int.Parse(strValue); + for (int ifr = 0; ifr < cfr; ifr++) { + + // 1. Read the bitmap from it and add it to the Document's XBitmapSet + + XBitmap xbm; + string strBitmap = "frame" + ifr + ".bmp"; + try { + xbm = new XBitmap(strBitmap, true); + } catch { + MessageBox.Show(null, String.Format("Can't load \"{0}\"", strBitmap), "Error"); + return false; + } + XBitmapSet.Add(xbm); + + // 2. Create a Frame to go with the Bitmap and add it to the appropriate + // Strip. If no strip exists, create one. + + Frame fr = new Frame(); + fr.BitmapPlacers.Add(new BitmapPlacer()); + fr.BitmapPlacers[0].XBitmap = xbm; + stp[ifr] = fr; + + bool fDone = false; + while (!fDone) { + do { + iLine++; + str = stmr.ReadLine(); + } while (str == ""); // skip blank lines + + if (!ParseNameValueString(str, out strName, out strValue)) { + MessageBox.Show(null, String.Format("Syntax error on line %d: %s", iLine, str), "Error"); + return false; + } + switch (strName) { + case "flags": + Debug.Assert(strValue.Trim() == "0"); + break; + + case "xCenter": + fr.BitmapPlacers[0].X = int.Parse(strValue); + break; + + case "yCenter": + fr.BitmapPlacers[0].Y = int.Parse(strValue); + break; + + case "xGrab": + fr.SpecialPoint = new Point(int.Parse(strValue) - fr.BitmapPlacers[0].X, fr.SpecialPoint.Y); + break; + + case "yGrab": + fr.SpecialPoint = new Point(fr.SpecialPoint.X, int.Parse(strValue) - fr.BitmapPlacers[0].Y); + break; + + case "xWidth": + Debug.Assert(int.Parse(strValue.Trim()) == xbm.Width); + break; + + case "yHeight": + Debug.Assert(int.Parse(strValue.Trim()) == xbm.Height); + fDone = true; + break; + } + } + } + + } else { + // XBitmap encapsulates special filename rules + + astrFileNames = XBitmap.FilterFileNames(astrFileNames); + + // By sorting the filenames we introduce a useful bit of + // determinism. + + Array.Sort(astrFileNames); + + // Enumerate all the filenames and for each one: + + foreach (string strFile in astrFileNames) { + + // 0. Verify the filename fits the pattern we're expecting + + string[] astr = strFile.Substring(strFile.LastIndexOf('\\') + 1).Split('_', '.'); + if (astr.Length != 5) { + MessageBox.Show(null, String.Format("File {0} does not match the requisite naming pattern. Skipping and continuing.", + strFile), "Error"); + continue; + } + string strAnimDoc = astr[0]; + string strStripA = astr[1]; + string strStripB = astr[2]; + int ifr = Convert.ToInt32(astr[3]); + + // 1. Read the bitmap from it and add it to the Document's XBitmapSet + + XBitmap xbm; + try { + xbm = new XBitmap(strFile); + } catch { + MessageBox.Show(null, String.Format("Can't load \"{0}\"", strFile), "Error"); + return false; + } + XBitmapSet.Add(xbm); + + // 2. Create a Frame to go with the Bitmap and add it to the appropriate + // Strip. If no strip exists, create one. + + Frame fr = new Frame(); + fr.BitmapPlacers.Add(new BitmapPlacer()); + fr.BitmapPlacers[0].XBitmap = xbm; + fr.BitmapPlacers[0].X = xbm.Width / 2; + fr.BitmapPlacers[0].Y = xbm.Height / 2; + + string strStripName = strStripA + " " + strStripB; + Strip stp = StripSet[strStripName]; + if (stp == null) { + stp = new Strip(strStripName); + StripSet.Add(stp); + } + stp[ifr] = fr; + } + } + + Dirty = true; + return true; + } + + public bool WriteAnir(Palette pal, string strExportPath, string strAnimName) { + Color clrTransparent = Color.FromArgb(0xff, 0, 0xff); + SolidBrush brTransparent = new SolidBrush(clrTransparent); + + ASCIIEncoding enc = new ASCIIEncoding(); + + FileStream stm = new FileStream(strExportPath + Path.DirectorySeparatorChar + strAnimName + ".anir", FileMode.Create, FileAccess.Write); + BinaryWriter stmw = new BinaryWriter(stm); + + // Count the number of Strips + + ushort cstpd = (ushort)StripSet.Count; + + // Write AnimationFileHeader.cstpd + + stmw.Write(Misc.SwapUShort(cstpd)); + + // Write array of offsets to StripDatas (AnimationFileHeader.aoffStpd) + + ushort offStpd = (ushort)(2 + (2 * cstpd)); + ArrayList albm = new ArrayList(); + + foreach (Strip stp in StripSet) { + stmw.Write(Misc.SwapUShort(offStpd)); + + // Advance offset to where the next StripData will be + + offStpd += (ushort)((26+1+1+2) /* sizeof(StripData) - sizeof(FrameData) */ + + ((1+1+1+1+1+1+1+1+1) /* sizeof(FrameData) */ * stp.Count)); + + // Force word alignment of StripDatas + + if ((offStpd & 1) == 1) + offStpd++; + } + + // Write array of StripDatas + + foreach (Strip stp in StripSet) { + + // Write StripData.Name + + byte[] abT = new byte[26]; + enc.GetBytes(stp.Name, 0, Math.Min(stp.Name.Length, 25), abT, 0); + abT[25] = 0; + stmw.Write(abT); + + // Write StripData.cHold + + stmw.Write((byte)stp.DefHoldCount); + + // Write StripData.bfFlags + + stmw.Write((byte)0); + + // Write StripData.cfrmd + + ushort cfrmd = (ushort)stp.Count; + stmw.Write(Misc.SwapUShort(cfrmd)); + + // Write array of FrameDatas + + foreach (Frame fr in stp) { + + // Add the Frame's Bitmap for output + + int ibm = -1; + Bitmap bm; + if (fr.BitmapPlacers.Count > 0) { + bm = fr.BitmapPlacers[0].XBitmap.Bitmap; + ibm = albm.IndexOf(bm); + if (ibm == -1) + ibm = albm.Add(bm); + } + + // Write FrameData.ibm (the index of the Bitmap as it will be in the Bitmap array) + + stmw.Write((byte)ibm); + + ibm = -1; + if (fr.BitmapPlacers.Count > 1) { + // Add the Frame's Bitmap for output + + bm = fr.BitmapPlacers[1].XBitmap.Bitmap; + ibm = albm.IndexOf(bm); + if (ibm == -1) + ibm = albm.Add(bm); + } + + // Write FrameData.ibm2 (the index of the Bitmap as it will be in the Bitmap array) + + stmw.Write((byte)ibm); + + // Write FrameData.cHold + + stmw.Write((byte)fr.HoldCount); + + // Write FrameData.xOrigin, FrameData.yOrigin + + if (fr.BitmapPlacers.Count > 0) { + stmw.Write((byte)fr.BitmapPlacers[0].X); + stmw.Write((byte)fr.BitmapPlacers[0].Y); + } else { + stmw.Write((byte)0); + stmw.Write((byte)0); + } + + if (fr.BitmapPlacers.Count > 1) { + stmw.Write((byte)fr.BitmapPlacers[1].X); + stmw.Write((byte)fr.BitmapPlacers[1].Y); + } else { + stmw.Write((byte)0); + stmw.Write((byte)0); + } + + // Write FrameData.bCustomData1, FrameData.bCustomData2 + + stmw.Write((byte)fr.SpecialPoint.X); + stmw.Write((byte)fr.SpecialPoint.Y); + +#if false + // Write FrameData.bCustomData3 + + stmw.Write((byte)0); +#endif + } + + // Force word alignment of StripDatas given that FrameDatas are an odd + // number of bytes long and there may be an odd number of frames. + + if ((cfrmd & 1) == 1) + stmw.Write((byte)0); + } + + stmw.Close(); + + // Write out .tbm + + if (albm.Count != 0) { + string strFileName = strExportPath + Path.DirectorySeparatorChar + strAnimName + ".tbm"; +// if (gfSuperVerbose) +// Console.WriteLine("Crunching and writing " + strFileName); + TBitmap.Save((Bitmap[])albm.ToArray(typeof(Bitmap)), pal, strFileName); + } + + return true; + } + + // ISerializable interface implementation + + private AnimDoc(SerializationInfo seri, StreamingContext stmc) { + m_xbms = (XBitmapSet)seri.GetValue("Bitmaps", typeof(XBitmapSet)); + m_stps = (StripSet)seri.GetValue("Strips", typeof(StripSet)); + m_msFrameRate = seri.GetInt32("FrameRate"); + + try { + bool fHires = seri.GetBoolean("Hires"); + if (fHires) { + m_nTileSize = 24; + } else { + m_nTileSize = 16; + } + } catch { + m_nTileSize = -1; + } + + if (m_nTileSize == -1) { + m_nTileSize = seri.GetInt32("TileSize"); + } + } + + void ISerializable.GetObjectData(SerializationInfo seri, StreamingContext stmc) { + seri.AddValue("Bitmaps", m_xbms); + seri.AddValue("Strips", m_stps); + seri.AddValue("FrameRate", m_msFrameRate); + seri.AddValue("TileSize", m_nTileSize); + } + } + + // This class is implemented to allow one Assembly read a .amx file written by a + // different Assembly -- what a concept! + + public class RelaxedSerializationBinder : SerializationBinder { + public override Type BindToType(string strAssemblyName, string strTypeName) { + return Type.GetType(strTypeName); + } + } +} diff --git a/AniMax/App.ico b/AniMax/App.ico new file mode 100644 index 0000000..f1282c6 Binary files /dev/null and b/AniMax/App.ico differ diff --git a/AniMax/AssemblyInfo.cs b/AniMax/AssemblyInfo.cs new file mode 100644 index 0000000..9f89a32 --- /dev/null +++ b/AniMax/AssemblyInfo.cs @@ -0,0 +1,58 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.*")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] diff --git a/AniMax/BitmapsForm.cs b/AniMax/BitmapsForm.cs new file mode 100644 index 0000000..ee7cf58 --- /dev/null +++ b/AniMax/BitmapsForm.cs @@ -0,0 +1,394 @@ +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; +using System.IO; + +namespace SpiffCode +{ + /// + /// Summary description for BitmapsForm. + /// + public class BitmapsForm : System.Windows.Forms.Form + { + private AnimDoc m_doc; + private XBitmapSet m_xbms; + private System.Windows.Forms.ListView lstv; + private System.Windows.Forms.ColumnHeader columnHeader1; + private System.Windows.Forms.ColumnHeader columnHeader2; + private System.Windows.Forms.ColumnHeader columnHeader3; + private System.Windows.Forms.ColumnHeader columnHeader4; + private System.Windows.Forms.ColumnHeader columnHeader5; + private System.Windows.Forms.ContextMenu mnuListView; + private System.Windows.Forms.MenuItem mniViewList; + private System.Windows.Forms.MenuItem mniViewDetails; + private System.Windows.Forms.MenuItem mniViewThumbnails; + private System.Windows.Forms.MenuItem mniView; + private System.Windows.Forms.MenuItem mniAddBitmap; + private System.Windows.Forms.MenuItem menuItem3; + private System.Windows.Forms.OpenFileDialog openFileDialog; + private System.Windows.Forms.MenuItem mniDelete; + private System.Windows.Forms.MenuItem menuItem2; + private System.Windows.Forms.MenuItem mniRename; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + public BitmapsForm(AnimDoc doc) + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + // My constructor code + + Globals.ActiveDocumentChanged += new EventHandler(OnActiveDocumentChanged); + m_doc = doc; + if (m_doc != null) + m_xbms = doc.XBitmapSet; + RefreshView(); + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.openFileDialog = new System.Windows.Forms.OpenFileDialog(); + this.mniViewList = new System.Windows.Forms.MenuItem(); + this.columnHeader3 = new System.Windows.Forms.ColumnHeader(); + this.columnHeader2 = new System.Windows.Forms.ColumnHeader(); + this.mniView = new System.Windows.Forms.MenuItem(); + this.mniViewThumbnails = new System.Windows.Forms.MenuItem(); + this.mniViewDetails = new System.Windows.Forms.MenuItem(); + this.columnHeader1 = new System.Windows.Forms.ColumnHeader(); + this.columnHeader5 = new System.Windows.Forms.ColumnHeader(); + this.columnHeader4 = new System.Windows.Forms.ColumnHeader(); + this.mnuListView = new System.Windows.Forms.ContextMenu(); + this.mniAddBitmap = new System.Windows.Forms.MenuItem(); + this.menuItem3 = new System.Windows.Forms.MenuItem(); + this.mniDelete = new System.Windows.Forms.MenuItem(); + this.menuItem2 = new System.Windows.Forms.MenuItem(); + this.lstv = new System.Windows.Forms.ListView(); + this.mniRename = new System.Windows.Forms.MenuItem(); + this.SuspendLayout(); + // + // openFileDialog + // + this.openFileDialog.DefaultExt = "png"; + this.openFileDialog.Filter = "All bitmaps (*.bmp,*.jpg,*.png,*.gif,*.tif)|*.png;*.tif;*.gif;*.jpg;*.bmp|All fil" + + "es|*.*"; + this.openFileDialog.Multiselect = true; + this.openFileDialog.Title = "Add Bitmaps"; + // + // mniViewList + // + this.mniViewList.Index = 0; + this.mniViewList.Text = "List"; + this.mniViewList.Click += new System.EventHandler(this.mniViewList_Click); + // + // columnHeader3 + // + this.columnHeader3.Text = "Height"; + this.columnHeader3.Width = 44; + // + // columnHeader2 + // + this.columnHeader2.Text = "Width"; + this.columnHeader2.Width = 40; + // + // mniView + // + this.mniView.Index = 5; + this.mniView.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.mniViewList, + this.mniViewThumbnails, + this.mniViewDetails}); + this.mniView.Text = "View"; + this.mniView.Popup += new System.EventHandler(this.mniView_Popup); + // + // mniViewThumbnails + // + this.mniViewThumbnails.Index = 1; + this.mniViewThumbnails.Text = "Thumbnails"; + this.mniViewThumbnails.Click += new System.EventHandler(this.mniViewThumbnails_Click); + // + // mniViewDetails + // + this.mniViewDetails.Index = 2; + this.mniViewDetails.Text = "Details"; + this.mniViewDetails.Click += new System.EventHandler(this.mniViewDetails_Click); + // + // columnHeader1 + // + this.columnHeader1.Text = "Name"; + this.columnHeader1.Width = 125; + // + // columnHeader5 + // + this.columnHeader5.Text = "Date Modified"; + this.columnHeader5.Width = 120; + // + // columnHeader4 + // + this.columnHeader4.Text = "Size"; + this.columnHeader4.Width = 46; + // + // mnuListView + // + this.mnuListView.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.mniAddBitmap, + this.menuItem3, + this.mniDelete, + this.mniRename, + this.menuItem2, + this.mniView}); + this.mnuListView.Popup += new System.EventHandler(this.mnuListView_Popup); + // + // mniAddBitmap + // + this.mniAddBitmap.Index = 0; + this.mniAddBitmap.Text = "Add Bitmap..."; + this.mniAddBitmap.Click += new System.EventHandler(this.mniAddBitmap_Click); + // + // menuItem3 + // + this.menuItem3.Index = 1; + this.menuItem3.Text = "-"; + // + // mniDelete + // + this.mniDelete.Index = 2; + this.mniDelete.Shortcut = System.Windows.Forms.Shortcut.Del; + this.mniDelete.Text = "Delete"; + this.mniDelete.Click += new System.EventHandler(this.mniDelete_Click); + // + // menuItem2 + // + this.menuItem2.Index = 4; + this.menuItem2.Text = "-"; + // + // lstv + // + this.lstv.AllowColumnReorder = true; + this.lstv.AllowDrop = true; + this.lstv.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.columnHeader1, + this.columnHeader2, + this.columnHeader3, + this.columnHeader4, + this.columnHeader5}); + this.lstv.ContextMenu = this.mnuListView; + this.lstv.Dock = System.Windows.Forms.DockStyle.Fill; + this.lstv.FullRowSelect = true; + this.lstv.LabelEdit = true; + this.lstv.Name = "lstv"; + this.lstv.Size = new System.Drawing.Size(312, 389); + this.lstv.Sorting = System.Windows.Forms.SortOrder.Ascending; + this.lstv.TabIndex = 0; + this.lstv.View = System.Windows.Forms.View.Details; + this.lstv.DragDrop += new System.Windows.Forms.DragEventHandler(this.lstv_DragDrop); + this.lstv.AfterLabelEdit += new System.Windows.Forms.LabelEditEventHandler(this.lstv_AfterLabelEdit); + this.lstv.DragEnter += new System.Windows.Forms.DragEventHandler(this.lstv_DragEnter); + this.lstv.ItemDrag += new System.Windows.Forms.ItemDragEventHandler(this.lstv_ItemDrag); + this.lstv.BeforeLabelEdit += new System.Windows.Forms.LabelEditEventHandler(this.lstv_BeforeLabelEdit); + // + // mniRename + // + this.mniRename.Index = 3; + this.mniRename.Text = "Rename"; + // + // BitmapsForm + // + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.ClientSize = new System.Drawing.Size(312, 389); + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.lstv}); + this.Name = "BitmapsForm"; + this.ShowInTaskbar = false; + this.Text = "Bitmaps"; + this.ResumeLayout(false); + + } + #endregion + + private void OnActiveDocumentChanged(object obSender, EventArgs e) { + m_doc = Globals.ActiveDocument; + m_xbms = m_doc != null ? m_doc.XBitmapSet : null; + RefreshView(); + } + + private void mnuListView_Popup(object sender, System.EventArgs e) { + mniDelete.Enabled = lstv.SelectedItems.Count != 0; + } + + private void mniViewDetails_Click(object sender, System.EventArgs e) { + lstv.View = View.Details; + } + + private void mniViewThumbnails_Click(object sender, System.EventArgs e) { + lstv.View = View.LargeIcon; + } + + private void mniViewList_Click(object sender, System.EventArgs e) { + lstv.View = View.List; + } + + private void mniView_Popup(object sender, System.EventArgs e) { + mniViewList.Checked = false; + mniViewThumbnails.Checked = false; + mniViewDetails.Checked = false; + + switch (lstv.View) { + case View.List: + mniViewList.Checked = true; + break; + + case View.LargeIcon: + mniViewThumbnails.Checked = true; + break; + + case View.Details: + mniViewDetails.Checked = true; + break; + } + } + + private void lstv_DragEnter(object sender, System.Windows.Forms.DragEventArgs e) { + bool fFileNamePresent = e.Data.GetDataPresent(DataFormats.FileDrop); + if (fFileNamePresent) + e.Effect = DragDropEffects.All; + else + e.Effect = DragDropEffects.None; + } + + private void lstv_DragDrop(object sender, System.Windows.Forms.DragEventArgs e) { + bool fFileNamePresent = e.Data.GetDataPresent(DataFormats.FileDrop); + if (fFileNamePresent) { + string[] astr = (string[])e.Data.GetData(DataFormats.FileDrop); + foreach (string strT in astr) { + m_xbms.Add(strT); + } + m_doc.Dirty = true; + } + RefreshView(); + } + + private void RefreshView() { + lstv.Items.Clear(); + + // Prep LargeImageList + + if (lstv.LargeImageList != null) + lstv.LargeImageList.Dispose(); + lstv.LargeImageList = new ImageList(); + lstv.LargeImageList.ColorDepth = ColorDepth.Depth32Bit; + lstv.LargeImageList.ImageSize = new Size(64, 64); + + // Prep SmallImageList + + if (lstv.SmallImageList != null) + lstv.SmallImageList.Dispose(); + lstv.SmallImageList = new ImageList(); + lstv.SmallImageList.ColorDepth = ColorDepth.Depth32Bit; + lstv.SmallImageList.ImageSize = new Size(16, 16); + + int i = 0; + foreach (XBitmap xbm in m_xbms) { + ListViewItem lvi = lstv.Items.Add(Path.GetFileName(xbm.FileName)); + lvi.Tag = xbm; + lvi.SubItems.Add(xbm.Width.ToString()); + lvi.SubItems.Add(xbm.Height.ToString()); + try { + FileInfo fili = new FileInfo(xbm.FileName); + lvi.SubItems.Add(fili.Length.ToString("#,##0")); + lvi.SubItems.Add(fili.LastWriteTime.ToShortDateString() + " " + fili.LastWriteTime.ToShortTimeString()); + } catch (FileNotFoundException) { + // UNDONE: deal with this! + } + + // Make a better thumbnail + + Bitmap bmLarge = xbm.MakeThumbnail(64, 64, false); + + lstv.LargeImageList.Images.Add(bmLarge); + lstv.SmallImageList.Images.Add(bmLarge); + lvi.ImageIndex = i++; + } + } + + private void mniAddBitmap_Click(object sender, System.EventArgs e) { + if (openFileDialog.ShowDialog() != DialogResult.OK) + return; + + foreach (string strFileName in openFileDialog.FileNames) { + m_xbms.Add(strFileName); + } + m_doc.Dirty = true; + RefreshView(); + } + + private void mniDelete_Click(object sender, System.EventArgs e) { + XBitmap xbm = (XBitmap)lstv.SelectedItems[0].Tag; + int i = m_xbms.IndexOf(xbm); + // UNDONE: undo + m_xbms.RemoveAt(i); + lstv.Items.Remove(lstv.SelectedItems[0]); + m_doc.Dirty = true; + } + + private void lstv_ItemDrag(object sender, System.Windows.Forms.ItemDragEventArgs e) { + XBitmap[] axbm = new XBitmap[lstv.SelectedItems.Count]; + for (int i = 0; i < lstv.SelectedItems.Count; i++) + axbm[i] = (XBitmap)lstv.SelectedItems[i].Tag; + DoDragDrop(axbm, DragDropEffects.All); + } + + private void lstv_BeforeLabelEdit(object sender, System.Windows.Forms.LabelEditEventArgs e) { + // We do this so the user can press the del key while in label editing mode + // without it being intercepted by the context menu and used to delete the + // entire Strip + + lstv.ContextMenu = null; + } + + private void lstv_AfterLabelEdit(object sender, System.Windows.Forms.LabelEditEventArgs e) { + + // Restore the context menu so the command keys, etc will work again + + lstv.ContextMenu = mnuListView; + + XBitmap xbm = (XBitmap)lstv.Items[e.Item].Tag; + + // No change or an invalid change + + if (e.Label == null || e.Label == "") { + e.CancelEdit = true; + return; + } + + xbm.FileName = e.Label; + m_doc.Dirty = true; + } + } +} diff --git a/AniMax/BitmapsForm.resources b/AniMax/BitmapsForm.resources new file mode 100644 index 0000000..f8125d5 Binary files /dev/null and b/AniMax/BitmapsForm.resources differ diff --git a/AniMax/BitmapsForm.resx b/AniMax/BitmapsForm.resx new file mode 100644 index 0000000..2d0b4fd --- /dev/null +++ b/AniMax/BitmapsForm.resx @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 131, 17 + + + 17, 17 + + + BitmapsForm + + \ No newline at end of file diff --git a/AniMax/CombinerForm.cs b/AniMax/CombinerForm.cs new file mode 100644 index 0000000..233e888 --- /dev/null +++ b/AniMax/CombinerForm.cs @@ -0,0 +1,282 @@ +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; + +namespace SpiffCode +{ + /// + /// Summary description for CombinerForm. + /// + public class CombinerForm : System.Windows.Forms.Form + { + private Point m_ptOffset; + private FrameControl[] m_afrc = new FrameControl[20]; + private Label[] m_albl = new Label[20]; + private Strip[] m_astp = new Strip[20]; + private int[] m_aifr = new int[20]; + private int m_iSelected = 0; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private SpiffCode.FrameControl frc2; + private SpiffCode.FrameControl frc1; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + public CombinerForm() + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + m_afrc[0] = frc1; + m_afrc[1] = frc2; + m_albl[0] = label1; + m_albl[1] = label2; + + Globals.ActiveDocumentChanged += new EventHandler(OnInvalidatingChange); + Globals.ActiveFrameChanged += new EventHandler(OnInvalidatingChange); + Globals.PreviewScaleChanged += new EventHandler(OnInvalidatingChange); + Globals.GridChanged += new EventHandler(OnInvalidatingChange); + Globals.SideColorMappingOnChanged += new EventHandler(OnInvalidatingChange); + Globals.ShowOriginPointChanged += new EventHandler(OnInvalidatingChange); + Globals.ShowSpecialPointChanged += new EventHandler(OnInvalidatingChange); + ((StripControl)Globals.StripControl).FrameOffsetChanged += + new FrameOffsetEventHandler(OnFrameOffsetChanged); + Globals.FrameContentChanged += new EventHandler(OnInvalidatingChange); + m_ptOffset = new Point(0, 0); + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.frc2 = new SpiffCode.FrameControl(); + this.frc1 = new SpiffCode.FrameControl(); + this.SuspendLayout(); + // + // label1 + // + this.label1.Location = new System.Drawing.Point(8, 80); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(80, 24); + this.label1.TabIndex = 2; + this.label1.Text = ""; + this.label1.TextAlign = System.Drawing.ContentAlignment.TopCenter; + // + // label2 + // + this.label2.Location = new System.Drawing.Point(96, 80); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(80, 24); + this.label2.TabIndex = 3; + this.label2.Text = ""; + this.label2.TextAlign = System.Drawing.ContentAlignment.TopCenter; + // + // frc2 + // + this.frc2.AllowDrop = true; + this.frc2.BorderColor = System.Drawing.Color.Black; + this.frc2.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.frc2.Frame = null; + this.frc2.FrameIndex = 0; + this.frc2.Location = new System.Drawing.Point(96, 8); + this.frc2.Name = "frc2"; + this.frc2.OffsetPoint = new System.Drawing.Point(0, 0); + this.frc2.Size = new System.Drawing.Size(80, 72); + this.frc2.Strip = null; + this.frc2.TabIndex = 4; + this.frc2.Text = "frameControl1"; + this.frc2.MouseDown += new System.Windows.Forms.MouseEventHandler(this.frc2_MouseDown); + // + // frc1 + // + this.frc1.AllowDrop = true; + this.frc1.BorderColor = System.Drawing.Color.Black; + this.frc1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.frc1.Frame = null; + this.frc1.FrameIndex = 0; + this.frc1.Location = new System.Drawing.Point(8, 8); + this.frc1.Name = "frc1"; + this.frc1.OffsetPoint = new System.Drawing.Point(0, 0); + this.frc1.Size = new System.Drawing.Size(80, 72); + this.frc1.Strip = null; + this.frc1.TabIndex = 5; + this.frc1.Text = "frameControl1"; + this.frc1.MouseDown += new System.Windows.Forms.MouseEventHandler(this.frc1_MouseDown); + // + // CombinerForm + // + this.AutoScale = false; + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.BackColor = System.Drawing.Color.FromArgb(((System.Byte)(172)), ((System.Byte)(235)), ((System.Byte)(172))); + this.ClientSize = new System.Drawing.Size(280, 302); + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.frc1, + this.frc2, + this.label2, + this.label1}); + this.Name = "CombinerForm"; + this.Text = "Combiner"; + this.ResumeLayout(false); + + } + #endregion + + private void OnInvalidatingChange(object obSender, EventArgs e) { + SelectStrip(m_iSelected, Globals.ActiveStrip, Globals.ActiveFrame); + if (Globals.ActiveStrip != null && Globals.ActiveStrip.Count != 0) + m_afrc[m_iSelected].Frame = Globals.ActiveStrip[Globals.ActiveFrame]; + } + + public void OnFrameOffsetChanged(object obSender, FrameOffsetEventArgs e) { + m_ptOffset.X = e.X; + m_ptOffset.Y = e.Y; + frc1.OffsetPoint = m_ptOffset; + frc2.OffsetPoint = m_ptOffset; + Invalidate(); + } + + private void RefreshView() { + Invalidate(); + } + + override protected void OnResize(EventArgs e) { + Invalidate(); + base.OnResize(e); + } + + protected override void OnPaintBackground(System.Windows.Forms.PaintEventArgs e) { + } + + protected override void OnPaint(System.Windows.Forms.PaintEventArgs e) { + if (Globals.ActiveStrip == null || Globals.ActiveStrip.Count == 0) { + e.Graphics.FillRectangle(new SolidBrush(BackColor), e.ClipRectangle); + return; + } + + Frame.DrawArgs drwa = new Frame.DrawArgs(); + drwa.clrBackground = BackColor; + drwa.fDrawBackground = true; + drwa.fMapSideColors = Globals.SideColorMappingOn; + drwa.fShowGrid = Globals.GridOn; + drwa.cxGrid = Globals.GridWidth; + drwa.cyGrid = Globals.GridHeight; + drwa.fShowOrigin = Globals.ShowOriginPoint; + drwa.fShowSpecialPoint = Globals.ShowSpecialPoint; + drwa.nScale = Globals.PreviewScale; + + Rectangle rcClient = new Rectangle(0, 0, ClientRectangle.Width, ClientRectangle.Height); + + Graphics g = e.Graphics; + + int xCenter = rcClient.Width / 2; + int yCenter = rcClient.Height / 2; + + int cxT = ((rcClient.Width + drwa.nScale - 1) / drwa.nScale) + 2; + int cyT = ((rcClient.Height + drwa.nScale - 1) / drwa.nScale) + 2; + int xCenterT = cxT / 2; + int yCenterT = cyT / 2; + + // Create a temporary bitmap for compositing the grid, frames, origin indicator, etc into + + using (Bitmap bmT = new Bitmap(cxT, cyT)) { + Point ptSpecial = new Point(0, 0); + + for (int i = 0; i < m_astp.Length; i++) { + + // Draw the frame and its indicators (grid, center point, special point, etc) + + Strip stp = m_astp[i]; + if (stp == null) + continue; + + Frame fr = stp[m_aifr[i]]; + Point ptOffset = new Point(m_ptOffset.X + ptSpecial.X, m_ptOffset.Y + ptSpecial.Y); + fr.DrawUnscaled(bmT, cxT, cyT, drwa, ptOffset); + + drwa.fDrawBackground = false; + drwa.fShowGrid = false; + + ptSpecial = fr.SpecialPoint; + } + + // Force a nice simple fast old-school stretchblt + + InterpolationMode imOld = g.InterpolationMode; + g.InterpolationMode = InterpolationMode.NearestNeighbor; + + // NOTE: _without_ this the first row and column are only scaled by half! + + PixelOffsetMode pomOld = g.PixelOffsetMode; + g.PixelOffsetMode = PixelOffsetMode.Half; + + // StretchBlt the temporary composite to the passed-in Graphic + + g.DrawImage(bmT, rcClient.Left - ((xCenterT * drwa.nScale) - xCenter), + rcClient.Top - ((yCenterT * drwa.nScale) - yCenter), + cxT * drwa.nScale, cyT * drwa.nScale); + + // Restore the Graphics' state + + g.PixelOffsetMode = pomOld; + g.InterpolationMode = imOld; + } + } + + private void SelectStrip(int i, Strip stp, int ifr) { + // Deselect any selected strip's FrameControl + + for (int j = 0; j < m_astp.Length; j++) { + if (m_afrc[j] != null) + m_afrc[j].BorderColor = Color.Black; + } + + m_iSelected = i; + m_afrc[m_iSelected].BorderColor = Color.Red; + + m_astp[m_iSelected] = stp; + m_aifr[m_iSelected] = stp == null ? 0 : stp.ActiveFrame; + m_afrc[m_iSelected].Frame = stp == null ? null : stp.Count == 0 ? null : stp[stp.ActiveFrame]; + m_albl[m_iSelected].Text = stp == null ? "" : stp.Name; + Invalidate(); + } + + private void frc1_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e) { + SelectStrip(0, m_astp[0], m_aifr[0]); + Globals.ActiveStrip = m_astp[m_iSelected]; + Globals.ActiveFrame = m_aifr[m_iSelected]; + } + + private void frc2_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e) { + SelectStrip(1, m_astp[1], m_aifr[1]); + Globals.ActiveStrip = m_astp[m_iSelected]; + Globals.ActiveFrame = m_aifr[m_iSelected]; + } + } +} diff --git a/AniMax/CombinerForm.resources b/AniMax/CombinerForm.resources new file mode 100644 index 0000000..dcb62ac Binary files /dev/null and b/AniMax/CombinerForm.resources differ diff --git a/AniMax/CombinerForm.resx b/AniMax/CombinerForm.resx new file mode 100644 index 0000000..bd52095 --- /dev/null +++ b/AniMax/CombinerForm.resx @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + CombinerForm + + \ No newline at end of file diff --git a/AniMax/Frame.cs b/AniMax/Frame.cs new file mode 100644 index 0000000..83d6994 --- /dev/null +++ b/AniMax/Frame.cs @@ -0,0 +1,348 @@ +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.Drawing.Drawing2D; +using System.Runtime.Serialization; +using System.Collections; + +namespace SpiffCode +{ + /// + /// Summary description for Frame. + /// + [Serializable] + public class Frame : ISerializable, ICloneable + { + private int m_cHold = 0; + private Point m_ptSpecial; + private BitmapPlacerList m_plcl; + + public Frame() { + m_plcl = new BitmapPlacerList(); + } + + public int HoldCount { + set { + m_cHold = value; + } + get { + return m_cHold; + } + } + + public Point SpecialPoint { + set { + m_ptSpecial = value; + } + get { + return m_ptSpecial; + } + } + + public BitmapPlacerList BitmapPlacers { + get { + return m_plcl; + } + } + + // Deep copy + + public object Clone() { + Frame fr = new Frame(); + fr.m_cHold = m_cHold; + fr.m_ptSpecial = new Point(m_ptSpecial.X, m_ptSpecial.Y); + fr.m_plcl = (BitmapPlacerList)m_plcl.Clone(); + return fr; + } + + public struct DrawArgs { // drwa + public int nScale; + public bool fShowGrid; + public bool fShowOrigin; + public bool fShowSpecialPoint; + public bool fMapSideColors; + public bool fDrawBackground; + public Color clrBackground; + public int cxGrid; + public int cyGrid; + } + + public void Draw(Graphics g, Rectangle rcClient, DrawArgs drwa, Point ptOffset) { + int xCenter = rcClient.Width / 2; + int yCenter = rcClient.Height / 2; + + int cxT = ((rcClient.Width + drwa.nScale - 1) / drwa.nScale) + 2; + int cyT = ((rcClient.Height + drwa.nScale - 1) / drwa.nScale) + 2; + int xCenterT = cxT / 2; + int yCenterT = cyT / 2; + + // NOTE: these 'using' statements (a 'shortcut' for calling .Dispose()) are + // absolutely necessary or we chew up all virtual memory while animating + + // Create a temporary bitmap for compositing the grid, frames, origin indicator, etc into + + using (Bitmap bmT = new Bitmap(cxT, cyT)) { + + // Draw the frame and its indicators (grid, center point, special point, etc) + + DrawUnscaled(bmT, cxT, cyT, drwa, ptOffset); + + // Force a nice simple fast old-school stretchblt + + InterpolationMode imOld = g.InterpolationMode; + g.InterpolationMode = InterpolationMode.NearestNeighbor; + + // NOTE: _without_ this the first row and column are only scaled by half! + + PixelOffsetMode pomOld = g.PixelOffsetMode; + g.PixelOffsetMode = PixelOffsetMode.Half; + + // StretchBlt the temporary composite to the passed-in Graphic + + g.DrawImage(bmT, rcClient.Left - ((xCenterT * drwa.nScale) - xCenter), + rcClient.Top - ((yCenterT * drwa.nScale) - yCenter), + cxT * drwa.nScale, cyT * drwa.nScale); + + g.PixelOffsetMode = pomOld; + g.InterpolationMode = imOld; + } + } + + public void DrawUnscaled(Bitmap bmDst, int cx, int cy, DrawArgs drwa, Point ptOffset) { + Graphics gDst = Graphics.FromImage(bmDst); + + int xCenter = cx / 2; + int yCenter = cy / 2; + + // Draw background (if enabled) + + if (drwa.fDrawBackground) + gDst.Clear(drwa.clrBackground); + +#if false + // Draw background bitmap, if any + + if (m_bmBackground != null) + gT.DrawImage(m_bmBackground, xCenter - (m_bmBackground.Width / 2) + m_ptBackgroundOffset.X, + yCenter - (m_bmBackground.Height / 2) + m_ptBackgroundOffset.Y, + m_bmBackground.Width, m_bmBackground.Height); +#endif + // Draw grid (if enabled) + // UNDONE: use alpha to draw grid (e.g., brighten or darken) + + if (drwa.fShowGrid) { + int cxGrid = drwa.cxGrid; + int cyGrid = drwa.cyGrid; +// Brush br = new SolidBrush(Color.FromKnownColor(KnownColor.LightGray)); + Brush br = new SolidBrush(Color.FromArgb(256 / 3, 255, 255, 255)); + for (int x = (xCenter + ptOffset.X) % cxGrid; x < cx; x += cxGrid) + gDst.FillRectangle(br, x, 0, 1, cy); + + for (int y = (yCenter + ptOffset.Y) % cyGrid; y < cy; y += cyGrid) + gDst.FillRectangle(br, 0, y, cx, 1); + } + + BitmapData bmdDst = bmDst.LockBits( + new Rectangle(0, 0, bmDst.Width, bmDst.Height), + ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); + + // Draw bitmaps from bottom up + + for (int i = BitmapPlacers.Count - 1; i >= 0; i--) { + BitmapPlacer plc = BitmapPlacers[i]; + XBitmap xbm = plc.XBitmap; + xbm.SuperBlt(0, 0, bmdDst, + xCenter - plc.X + ptOffset.X, + yCenter - plc.Y + ptOffset.Y, + xbm.Bitmap.Width, xbm.Bitmap.Height, + drwa.fMapSideColors); + } + + bmDst.UnlockBits(bmdDst); + + // Draw origin point (if enabled) + + if (drwa.fShowOrigin) { + + // This is really weird but if we don't do our own clipping then SetPixel will + // raise an exception! + + int x = xCenter + ptOffset.X; + int y = yCenter + ptOffset.Y; + if (x >= 0 && y >= 0 && x < bmDst.Width && y < bmDst.Height) + bmDst.SetPixel(x, y, Color.FromKnownColor(KnownColor.Orange)); + } + + // Draw special point (if enabled) + + if (drwa.fShowSpecialPoint) { + + // This is really weird but if we don't do our own clipping then SetPixel will + // raise an exception! + + int x = xCenter + ptOffset.X + m_ptSpecial.X; + int y = yCenter + ptOffset.Y + m_ptSpecial.Y; + if (x >= 0 && y >= 0 && x < bmDst.Width && y < bmDst.Height) + bmDst.SetPixel(x, y, Color.FromArgb(0, 255, 0)); + } + + gDst.Dispose(); + } + + // ISerializable interface implementation + + private Frame(SerializationInfo seri, StreamingContext stmc) : this() { + try { + m_plcl = (BitmapPlacerList)seri.GetValue("BitmapPlacers", typeof(BitmapPlacerList)); + } catch { + m_plcl = new BitmapPlacerList(); + m_plcl.Add(new BitmapPlacer()); + m_plcl[0].X = seri.GetInt32("BitmapX"); + m_plcl[0].Y = seri.GetInt32("BitmapY"); + m_plcl[0].XBitmap = (XBitmap)seri.GetValue("XBitmap", typeof(XBitmap)); + } + + try { + m_cHold = seri.GetInt32("HoldCount"); + } catch {} + + try { + m_ptSpecial = (Point)seri.GetValue("SpecialPoint", typeof(Point)); + } catch {} + } + + void ISerializable.GetObjectData(SerializationInfo seri, StreamingContext stmc) { + seri.AddValue("BitmapPlacers", m_plcl); + seri.AddValue("HoldCount", m_cHold); + seri.AddValue("SpecialPoint", m_ptSpecial); + } + } + + [Serializable] + public class BitmapPlacerList : CollectionBase, ISerializable, ICloneable { + public BitmapPlacerList() { + } + + // Automatically expand the collection to include as many + // elements as the caller is expecting. + + public BitmapPlacer this[int i] { + get { +// while (i >= InnerList.Count) +// InnerList.Add(new BitmapPlacer()); + return (BitmapPlacer)InnerList[i]; + } + set { +// while (i >= InnerList.Count) +// InnerList.Add(new BitmapPlacer()); + InnerList[i] = value; + } + } + + public int Add(BitmapPlacer plc) { + return ((IList)this).Add(plc); + } + + public void Insert(int iplc, BitmapPlacer plc) { + InnerList.Insert(iplc, plc); + } + + public int Index(BitmapPlacer plc) { + for (int i = 0; i < InnerList.Count; i++) { + if (plc == (BitmapPlacer)InnerList[i]) { + return i; + } + } + return -1; + } + + // Deep copy + + public object Clone() { + BitmapPlacerList plcl = new BitmapPlacerList(); + for (int i = 0; i < InnerList.Count; i++) { + plcl.Add((BitmapPlacer)((BitmapPlacer)InnerList[i]).Clone()); + } + return plcl; + } + + // ISerializable interface implementation + + private BitmapPlacerList(SerializationInfo seri, StreamingContext stmc) : this() { + for (int i = 0; i < seri.MemberCount; i++) { + BitmapPlacer plc = (BitmapPlacer)seri.GetValue(i.ToString(), typeof(BitmapPlacer)); + InnerList.Add(plc); + } + } + + void ISerializable.GetObjectData(SerializationInfo seri, StreamingContext stmc) { + int i = 0; + foreach (BitmapPlacer plc in InnerList) { + seri.AddValue(i.ToString(), plc); + i++; + } + } + } + + [Serializable] + public class BitmapPlacer : ISerializable, ICloneable { + private Point m_pt; + private XBitmap m_xbm; + + public BitmapPlacer() { + } + + public Point Point { + get { + return m_pt; + } + set { + m_pt = value; + } + } + + public int X { + get { + return m_pt.X; + } + set { + m_pt.X = value; + } + } + + public int Y { + get { + return m_pt.Y; + } + set { + m_pt.Y = value; + } + } + + public XBitmap XBitmap { + get { + return m_xbm; + } + set { + m_xbm = value; + } + } + + public object Clone() { + BitmapPlacer plc = new BitmapPlacer(); + plc.m_pt = new Point(m_pt.X, m_pt.Y); + plc.m_xbm = m_xbm.Clone(); + return plc; + } + + private BitmapPlacer(SerializationInfo seri, StreamingContext stmc) : this() { + m_pt = (Point)seri.GetValue("Point", typeof(Point)); + m_xbm = (XBitmap)seri.GetValue("XBitmap", typeof(XBitmap)); + } + + void ISerializable.GetObjectData(SerializationInfo seri, StreamingContext stmc) { + seri.AddValue("Point", m_pt); + seri.AddValue("XBitmap", XBitmap); + } + } +} diff --git a/AniMax/FrameControl.cs b/AniMax/FrameControl.cs new file mode 100644 index 0000000..29a8ae7 --- /dev/null +++ b/AniMax/FrameControl.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections; +using System.ComponentModel; +using System.Drawing; +using System.Data; +using System.Windows.Forms; + +namespace SpiffCode +{ + /// + /// Summary description for FrameControl. + /// + public class FrameControl : System.Windows.Forms.Control + { + private Point m_ptOffset; + private Strip m_stp; + private Frame m_fr; + private int m_ifr; + private int m_nScale = 2; + private BorderStyle m_bdrs = BorderStyle.None; + private Color m_clrBorder = Color.Black; + + public FrameControl() + { + m_ptOffset = new Point(0, 0); + } + + protected override void OnPaintBackground(System.Windows.Forms.PaintEventArgs e) { + } + + protected override void OnPaint(PaintEventArgs pe) + { + if (m_fr == null) { + pe.Graphics.FillRectangle(new SolidBrush(BackColor), pe.ClipRectangle); + } else { + Frame.DrawArgs drwa = new Frame.DrawArgs(); + drwa.clrBackground = BackColor; + drwa.fDrawBackground = true; + drwa.fMapSideColors = Globals.SideColorMappingOn; + drwa.fShowGrid = false; + drwa.fShowOrigin = Globals.ShowOriginPoint; + drwa.fShowSpecialPoint = Globals.ShowSpecialPoint; + drwa.nScale = m_nScale; + + m_fr.Draw(pe.Graphics, ClientRectangle, drwa, m_ptOffset); + } + + switch (m_bdrs) { + case BorderStyle.FixedSingle: + Rectangle rc = ClientRectangle; + rc.Width--; + rc.Height--; + pe.Graphics.DrawRectangle(new Pen(m_clrBorder), rc); + break; + + case BorderStyle.Fixed3D: + ControlPaint.DrawBorder3D(pe.Graphics, ClientRectangle, Border3DStyle.Raised); + break; + } + + // Calling the base class OnPaint +// base.OnPaint(pe); + } + + public Strip Strip { + get { + return m_stp; + } + set { + m_stp = value; + Invalidate(); + } + } + + public Frame Frame { + get { + return m_fr; + } + set { + m_fr = value; + Invalidate(); + } + } + + public BorderStyle BorderStyle { + get { + return m_bdrs; + } + set { + m_bdrs = value; + Invalidate(); + } + } + + public Color BorderColor { + get { + return m_clrBorder; + } + set { + m_clrBorder = value; + if (m_bdrs != BorderStyle.None) + Invalidate(); + } + } + + public int FrameIndex { + get { + return m_ifr; + } + set { + m_ifr = value; + if (m_stp != null) { + m_fr = m_stp[m_ifr]; + Invalidate(); + } + } + } + + public Point OffsetPoint { + get { + return m_ptOffset; + } + set { + m_ptOffset = value; + Invalidate(); + } + } + } +} diff --git a/AniMax/FrameControl.resources b/AniMax/FrameControl.resources new file mode 100644 index 0000000..06c24d0 Binary files /dev/null and b/AniMax/FrameControl.resources differ diff --git a/AniMax/FrameControl.resx b/AniMax/FrameControl.resx new file mode 100644 index 0000000..7e32396 --- /dev/null +++ b/AniMax/FrameControl.resx @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.0.0.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3102.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3102.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + diff --git a/AniMax/Globals.cs b/AniMax/Globals.cs new file mode 100644 index 0000000..e36f3c2 --- /dev/null +++ b/AniMax/Globals.cs @@ -0,0 +1,365 @@ +using System; +using System.Windows.Forms; +using System.Reflection; +using System.IO; + +namespace SpiffCode +{ + /// + /// Summary description for Globals. + /// + public class Globals + { + private static AnimDoc s_doc; + private static Strip s_stpActive = null; + private static int s_ifrActive = 0; + private static int s_cfrActive = 1; + private static int s_msFrameRate = 80; + private static int s_nPreviewScale = 4; + private static int s_nStripScale = 1; + private static bool s_fSideColorMappingOn = true; + private static bool s_fGridOn = true; + private static bool s_fShowOriginPoint = false; + private static bool s_fShowSpecialPoint = false; + private static AnimDoc s_docNull; + private static Control s_ctlStrip; + private static Control s_ctlPreview; + private static Form s_frmStrips; + private static Form s_frmStrip; + private static Form s_frmMain; + private static Cursor s_crsrHand; + private static Cursor s_crsrGrab; + private static int s_nTileSize = 32; + private static int s_cxGrid = 32; + private static int s_cyGrid = 32; + + public static event EventHandler ActiveDocumentChanged; + + public static AnimDoc ActiveDocument { + get { + return s_doc; + } + set { + // Keep track of the ActiveDoc's ActiveStrip + + if (s_doc != null) + s_doc.ActiveStripChanged -= new EventHandler(OnActiveStripChanged); + + s_doc = value; + if (ActiveDocumentChanged != null) + ActiveDocumentChanged(null, EventArgs.Empty); + ActiveStrip = s_doc == null ? null : s_doc.ActiveStrip; + + if (s_doc != null) + s_doc.ActiveStripChanged += new EventHandler(OnActiveStripChanged); + } + } + + private static void OnActiveStripChanged(object obSender, EventArgs e) { + ActiveStrip = s_doc.ActiveStrip; + } + + public static event EventHandler ActiveStripChanged; + + public static Strip ActiveStrip { + get { + return s_stpActive; + } + set { + // Keep track of the ActiveStrip's ActiveFrame + + if (s_stpActive != null) { + s_stpActive.ActiveFrameChanged -= new EventHandler(OnActiveFrameChanged); + s_stpActive.ActiveFrameCountChanged -= new EventHandler(OnActiveFrameCountChanged); + } + + s_stpActive = value; + if (ActiveStripChanged != null) + ActiveStripChanged(null, EventArgs.Empty); + ActiveFrame = s_stpActive == null ? 0 : s_stpActive.ActiveFrame; + + if (s_stpActive != null) { + s_stpActive.ActiveFrameChanged += new EventHandler(OnActiveFrameChanged); + s_stpActive.ActiveFrameCountChanged += new EventHandler(OnActiveFrameCountChanged); + } + } + } + + private static void OnActiveFrameChanged(object obSender, EventArgs e) { + ActiveFrame = s_stpActive.ActiveFrame; + } + + private static void OnActiveFrameCountChanged(object obSender, EventArgs e) { + ActiveFrameCount = s_stpActive.ActiveFrameCount; + } + + public static event EventHandler ActiveFrameChanged; + + public static int ActiveFrame { + get { + return s_ifrActive; + } + set { + s_ifrActive = value; + if (ActiveFrameChanged != null) + ActiveFrameChanged(null, EventArgs.Empty); + } + } + + public static event EventHandler ActiveFrameCountChanged; + + public static int ActiveFrameCount { + get { + return s_cfrActive; + } + set { + s_cfrActive = value; + if (ActiveFrameCountChanged != null) + ActiveFrameCountChanged(null, EventArgs.Empty); + } + } + + public static int FrameRate { + get { + return s_msFrameRate; + } + set { + s_msFrameRate = value; + } + } + + public static event EventHandler GridChanged; + + public static bool GridOn { + get { + return s_fGridOn; + } + set { + s_fGridOn = value; + if (GridChanged != null) + GridChanged(null, EventArgs.Empty); + } + } + + public static int GridWidth { + get { + return s_cxGrid; + } + set { + s_cxGrid = value; + if (GridChanged != null) + GridChanged(null, EventArgs.Empty); + } + } + + public static int GridHeight { + get { + return s_cyGrid; + } + set { + s_cyGrid = value; + if (GridChanged != null) + GridChanged(null, EventArgs.Empty); + } + } + + public static event EventHandler TileSizeChanged; + + public static int TileSize { + set { + s_nTileSize = value; + GridWidth = s_nTileSize; + GridHeight = s_nTileSize; + if (s_nTileSize < 24) { + StripScale = 2; + } else { + StripScale = 1; + } + if (TileSizeChanged != null) + TileSizeChanged(null, EventArgs.Empty); + } + get { + return s_nTileSize; + } + } + + public static Cursor HandCursor { + get { + if (s_crsrHand == null) { + Assembly ass = Assembly.GetAssembly(typeof(Globals)); +// string[] astr = ass.GetManifestResourceNames(); + Stream stm = ass.GetManifestResourceStream("SpiffCode.Resources.hand.cur"); + s_crsrHand = new Cursor(stm); + stm.Close(); + } + return s_crsrHand; + } + } + + public static Cursor GrabCursor { + get { + if (s_crsrGrab == null) { + Assembly ass = Assembly.GetAssembly(typeof(Globals)); + Stream stm = ass.GetManifestResourceStream("SpiffCode.Resources.grab.cur"); + s_crsrGrab = new Cursor(stm); + stm.Close(); + } + return s_crsrGrab; + } + } + + public static event EventHandler SideColorMappingOnChanged; + + public static bool SideColorMappingOn { + get { + return s_fSideColorMappingOn; + } + set { + s_fSideColorMappingOn = value; + if (SideColorMappingOnChanged != null) + SideColorMappingOnChanged(null, EventArgs.Empty); + } + } + + public static event EventHandler ShowOriginPointChanged; + + public static bool ShowOriginPoint { + get { + return s_fShowOriginPoint; + } + set { + s_fShowOriginPoint = value; + if (ShowOriginPointChanged != null) + ShowOriginPointChanged(null, EventArgs.Empty); + } + } + + public static event EventHandler ShowSpecialPointChanged; + + public static bool ShowSpecialPoint { + get { + return s_fShowSpecialPoint; + } + set { + s_fShowSpecialPoint = value; + if (ShowSpecialPointChanged != null) + ShowSpecialPointChanged(null, EventArgs.Empty); + } + } + + public static event EventHandler PreviewScaleChanged; + + public static int PreviewScale { + get { + return s_nPreviewScale; + } + set { + s_nPreviewScale = value; + if (PreviewScaleChanged != null) + PreviewScaleChanged(null, EventArgs.Empty); + } + } + + public static event EventHandler StripScaleChanged; + + public static int StripScale { + get { + return s_nStripScale; + } + set { + s_nStripScale = value; + if (StripScaleChanged != null) + StripScaleChanged(null, EventArgs.Empty); + } + } + + public static AnimDoc NullDocument { + get { + return s_docNull; + } + set { + s_docNull = value; + } + } + + public static Control StripControl { + get { + return s_ctlStrip; + } + set { + s_ctlStrip = value; + } + } + + public static Control PreviewControl { + get { + return s_ctlPreview; + } + set { + s_ctlPreview = value; + } + } + + public static Form StripsForm { + get { + return s_frmStrips; + } + set { + s_frmStrips = value; + } + } + + public static Form StripForm { + get { + return s_frmStrip; + } + set { + s_frmStrip = value; + } + } + + public static Form MainForm { + get { + return s_frmMain; + } + set { + s_frmMain = value; + } + } + + public static event KeyPressEventHandler KeyPress; + + // UNDONE: this will send to every handler even after one has handled + // the event. + + public static void OnKeyPress(Object sender, KeyPressEventArgs e) { + if (KeyPress != null) + KeyPress(sender, e); + } + + public static event KeyEventHandler KeyDown; + + public static void OnKeyDown(Object sender, KeyEventArgs e) { + if (KeyDown != null) + KeyDown(sender, e); + } + + public static event KeyEventHandler KeyUp; + + public static void OnKeyUp(Object sender, KeyEventArgs e) { + if (KeyUp != null) + KeyUp(sender, e); + } + + public static event EventHandler FrameContentChanged; + + // UNDONE: this will send to every handler even after one has handled + // the event. + + public static void OnFrameContentChanged(Object sender, EventArgs e) { + if (FrameContentChanged != null) + FrameContentChanged(sender, e); + } + } +} + diff --git a/AniMax/MainForm.cs b/AniMax/MainForm.cs new file mode 100644 index 0000000..bf44bf3 --- /dev/null +++ b/AniMax/MainForm.cs @@ -0,0 +1,814 @@ +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; +using System.Data; +using Crownwood.Magic.Docking; +using Crownwood.Magic.Common; +using System.Diagnostics; +using System.IO; +using System.Drawing.Imaging; + +namespace SpiffCode +{ + /// + /// Summary description for MainForm. + /// + public class MainForm : System.Windows.Forms.Form + { + private string gstrAniMax = "AniMax"; + private PreviewPanel m_ctlPreviewPanel; + private Form m_frmStrips; + private Content m_tntStrips; + private Form m_frmBitmaps; + private Content m_tntBitmaps; + private StripForm m_frmFrames; + private Content m_tntFrames; + private Form m_frmCombiner; + private Content m_tntCombiner; + private DockingManager m_dkm; + private AnimDoc m_doc; + private WindowContent m_wcStrips; + private System.Windows.Forms.MenuItem mniFile; + private System.Windows.Forms.MenuItem menuItem9; + private System.Windows.Forms.MenuItem menuItem10; + private System.Windows.Forms.MenuItem menuItem11; + private System.Windows.Forms.MenuItem menuItem12; + private System.Windows.Forms.MenuItem menuItem13; + private System.Windows.Forms.MenuItem menuItem14; + private System.Windows.Forms.MenuItem mniNew; + private System.Windows.Forms.MenuItem mniOpen; + private System.Windows.Forms.MenuItem mniSave; + private System.Windows.Forms.MenuItem mniExit; + private System.Windows.Forms.MenuItem mniSaveAs; + private System.Windows.Forms.MenuItem mniClose; + private System.Windows.Forms.MenuItem mniImport; + private System.Windows.Forms.MenuItem mniExport; + private System.Windows.Forms.MainMenu mnuMain; + private System.Windows.Forms.SaveFileDialog saveFileDialog; + private System.Windows.Forms.OpenFileDialog openFileDialog; + private System.Windows.Forms.MenuItem mniViewBitmaps; + private System.Windows.Forms.MenuItem mniView; + private System.Windows.Forms.MenuItem mniViewStrips; + private System.Windows.Forms.MenuItem mniViewTracks; + private System.Windows.Forms.MenuItem mniTools; + private System.Windows.Forms.MenuItem mniOptions; + private System.Windows.Forms.OpenFileDialog importFileDialog; + private System.Windows.Forms.MenuItem mniRepairSideColors; + private System.Windows.Forms.MenuItem mniNormalizeBitmaps; + private System.Windows.Forms.MenuItem mniUndo; + private System.Windows.Forms.MenuItem mniEdit; + private System.Windows.Forms.MenuItem mniHelp; + private System.Windows.Forms.MenuItem mniViewCombiner; + private System.Windows.Forms.MenuItem menuItem1; + private System.Windows.Forms.MenuItem mniHelpAbout; + private System.Windows.Forms.MenuItem mniWallPreview; + private System.Windows.Forms.MenuItem menuItem3; + private System.Windows.Forms.MenuItem mniReplaceColors; + private System.ComponentModel.IContainer components = null; + + public MainForm(string strOpenFileName) + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + m_dkm = new DockingManager(this, VisualStyle.IDE); + Globals.ActiveDocumentChanged += new EventHandler(OnActiveDocumentChanged); + m_doc = Globals.ActiveDocument; + Globals.MainForm = this; + + // Create all the "Contents" used to display the various animation components + + m_ctlPreviewPanel = new PreviewPanel(m_doc); + Globals.PreviewControl = m_ctlPreviewPanel.PreviewControl; + m_ctlPreviewPanel.Dock = DockStyle.Fill; + Controls.Add(m_ctlPreviewPanel); + m_dkm.InnerControl = m_ctlPreviewPanel; + + m_frmStrips = new StripsForm(m_doc); + Globals.StripsForm = m_frmStrips; + m_tntStrips = m_dkm.Contents.Add(m_frmStrips, m_frmStrips.Text); + m_tntStrips.DisplaySize = new Size(ClientSize.Width / 4, ClientSize.Height / 2); + m_wcStrips = m_dkm.AddContentWithState(m_tntStrips, State.DockLeft); + + m_frmBitmaps = new BitmapsForm(m_doc); + m_tntBitmaps = m_dkm.Contents.Add(m_frmBitmaps, m_frmBitmaps.Text); + m_tntBitmaps.DisplaySize = new Size(ClientSize.Width / 4, ClientSize.Height / 2); + m_dkm.AddContentWithState(m_tntBitmaps, State.DockTop); + + // Add the Bitmaps form to the StripForm's Zone + + m_dkm.AddContentToZone(m_tntBitmaps, m_wcStrips.ParentZone, 1); + + m_frmFrames = new StripForm(m_doc); + Globals.StripForm = m_frmFrames; + m_tntFrames = m_dkm.Contents.Add(m_frmFrames, m_frmFrames.Text); + m_frmFrames.Content = m_tntFrames; + int cx = ClientSize.Width - (ClientSize.Width / 4); + int cy = ClientSize.Height / 3; + m_tntFrames.DisplaySize = new Size(cx, cy); + m_dkm.AddContentWithState(m_tntFrames, State.DockBottom); + + m_frmCombiner = new CombinerForm(); + m_tntCombiner = m_dkm.Contents.Add(m_frmCombiner, m_frmCombiner.Text); + m_tntCombiner.DisplaySize = new Size(ClientSize.Width / 2, ClientSize.Height / 2); +// m_dkm.AddContentWithState(m_tntCombiner, State.Floating); +// m_dkm.HideContent(m_tntCombiner); + + // Do a little wiring + + ((StripControl)Globals.StripControl).FrameOffsetChanged += + new FrameOffsetEventHandler(((PreviewControl)Globals.PreviewControl).OnFrameOffsetChanged); + ((PreviewControl)Globals.PreviewControl).FrameOffsetChanged += + new FrameOffsetEventHandler(((StripControl)Globals.StripControl).OnFrameOffsetChanged); + + // We always have a document around + + if (strOpenFileName == null) + NewDocument(); + else + OpenDocument(strOpenFileName); + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if (components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(MainForm)); + this.mniFile = new System.Windows.Forms.MenuItem(); + this.mniNew = new System.Windows.Forms.MenuItem(); + this.mniOpen = new System.Windows.Forms.MenuItem(); + this.mniClose = new System.Windows.Forms.MenuItem(); + this.menuItem9 = new System.Windows.Forms.MenuItem(); + this.mniSave = new System.Windows.Forms.MenuItem(); + this.mniSaveAs = new System.Windows.Forms.MenuItem(); + this.menuItem10 = new System.Windows.Forms.MenuItem(); + this.mniImport = new System.Windows.Forms.MenuItem(); + this.mniExport = new System.Windows.Forms.MenuItem(); + this.menuItem14 = new System.Windows.Forms.MenuItem(); + this.menuItem13 = new System.Windows.Forms.MenuItem(); + this.menuItem12 = new System.Windows.Forms.MenuItem(); + this.menuItem11 = new System.Windows.Forms.MenuItem(); + this.mniExit = new System.Windows.Forms.MenuItem(); + this.mniOptions = new System.Windows.Forms.MenuItem(); + this.saveFileDialog = new System.Windows.Forms.SaveFileDialog(); + this.mniViewTracks = new System.Windows.Forms.MenuItem(); + this.mniViewBitmaps = new System.Windows.Forms.MenuItem(); + this.openFileDialog = new System.Windows.Forms.OpenFileDialog(); + this.mniViewStrips = new System.Windows.Forms.MenuItem(); + this.mnuMain = new System.Windows.Forms.MainMenu(); + this.mniEdit = new System.Windows.Forms.MenuItem(); + this.mniUndo = new System.Windows.Forms.MenuItem(); + this.mniView = new System.Windows.Forms.MenuItem(); + this.mniViewCombiner = new System.Windows.Forms.MenuItem(); + this.mniTools = new System.Windows.Forms.MenuItem(); + this.mniNormalizeBitmaps = new System.Windows.Forms.MenuItem(); + this.mniRepairSideColors = new System.Windows.Forms.MenuItem(); + this.mniWallPreview = new System.Windows.Forms.MenuItem(); + this.menuItem3 = new System.Windows.Forms.MenuItem(); + this.mniHelp = new System.Windows.Forms.MenuItem(); + this.menuItem1 = new System.Windows.Forms.MenuItem(); + this.mniHelpAbout = new System.Windows.Forms.MenuItem(); + this.importFileDialog = new System.Windows.Forms.OpenFileDialog(); + this.mniReplaceColors = new System.Windows.Forms.MenuItem(); + // + // mniFile + // + this.mniFile.Index = 0; + this.mniFile.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.mniNew, + this.mniOpen, + this.mniClose, + this.menuItem9, + this.mniSave, + this.mniSaveAs, + this.menuItem10, + this.mniImport, + this.mniExport, + this.menuItem14, + this.menuItem13, + this.menuItem12, + this.menuItem11, + this.mniExit}); + this.mniFile.Text = "&File"; + this.mniFile.Popup += new System.EventHandler(this.mniFile_Popup); + // + // mniNew + // + this.mniNew.Index = 0; + this.mniNew.Shortcut = System.Windows.Forms.Shortcut.CtrlN; + this.mniNew.Text = "&New"; + this.mniNew.Click += new System.EventHandler(this.mniNew_Click); + // + // mniOpen + // + this.mniOpen.Index = 1; + this.mniOpen.Shortcut = System.Windows.Forms.Shortcut.CtrlO; + this.mniOpen.Text = "&Open..."; + this.mniOpen.Click += new System.EventHandler(this.mniOpen_Click); + // + // mniClose + // + this.mniClose.Index = 2; + this.mniClose.Text = "&Close"; + this.mniClose.Click += new System.EventHandler(this.mniClose_Click); + // + // menuItem9 + // + this.menuItem9.Index = 3; + this.menuItem9.Text = "-"; + // + // mniSave + // + this.mniSave.Index = 4; + this.mniSave.Shortcut = System.Windows.Forms.Shortcut.CtrlS; + this.mniSave.Text = "&Save"; + this.mniSave.Click += new System.EventHandler(this.mniSave_Click); + // + // mniSaveAs + // + this.mniSaveAs.Index = 5; + this.mniSaveAs.Text = "Save &As..."; + this.mniSaveAs.Click += new System.EventHandler(this.mniSaveAs_Click); + // + // menuItem10 + // + this.menuItem10.Index = 6; + this.menuItem10.Text = "-"; + // + // mniImport + // + this.mniImport.Index = 7; + this.mniImport.Text = "&Import..."; + this.mniImport.Click += new System.EventHandler(this.mniImport_Click); + // + // mniExport + // + this.mniExport.Enabled = false; + this.mniExport.Index = 8; + this.mniExport.Text = "&Export..."; + // + // menuItem14 + // + this.menuItem14.Index = 9; + this.menuItem14.Text = "-"; + // + // menuItem13 + // + this.menuItem13.Enabled = false; + this.menuItem13.Index = 10; + this.menuItem13.Text = "Recent Strips"; + // + // menuItem12 + // + this.menuItem12.Enabled = false; + this.menuItem12.Index = 11; + this.menuItem12.Text = "Recent Files"; + // + // menuItem11 + // + this.menuItem11.Index = 12; + this.menuItem11.Text = "-"; + // + // mniExit + // + this.mniExit.Index = 13; + this.mniExit.Text = "E&xit"; + // + // mniOptions + // + this.mniOptions.Index = 5; + this.mniOptions.Text = "&Options..."; + this.mniOptions.Click += new System.EventHandler(this.mniOptions_Click); + // + // saveFileDialog + // + this.saveFileDialog.DefaultExt = "amx"; + this.saveFileDialog.FileName = "untitled.amx"; + this.saveFileDialog.Filter = "AniMax files (*.amx)|*.amx|Zipped AniMax files (*.zamx)|*.zamx|All files|*.*"; + this.saveFileDialog.Title = "Save AniMax File"; + // + // mniViewTracks + // + this.mniViewTracks.Index = 2; + this.mniViewTracks.Text = "&Frames Window"; + this.mniViewTracks.Click += new System.EventHandler(this.mniViewTracks_Click); + // + // mniViewBitmaps + // + this.mniViewBitmaps.Index = 0; + this.mniViewBitmaps.Text = "&Bitmaps Window"; + this.mniViewBitmaps.Click += new System.EventHandler(this.mniViewBitmaps_Click); + // + // openFileDialog + // + this.openFileDialog.DefaultExt = "amx"; + this.openFileDialog.Filter = "AniMax files (*.amx)|*.amx|Zipped AniMax files (*.zamx)|*.zamx|All files|*.*"; + this.openFileDialog.Title = "Open AniMax File"; + // + // mniViewStrips + // + this.mniViewStrips.Index = 1; + this.mniViewStrips.Text = "&Strips Window"; + this.mniViewStrips.Click += new System.EventHandler(this.mniViewStrips_Click); + // + // mnuMain + // + this.mnuMain.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.mniFile, + this.mniEdit, + this.mniView, + this.mniTools, + this.mniHelp}); + // + // mniEdit + // + this.mniEdit.Index = 1; + this.mniEdit.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.mniUndo}); + this.mniEdit.Text = "&Edit"; + this.mniEdit.Popup += new System.EventHandler(this.mniEdit_Popup); + // + // mniUndo + // + this.mniUndo.Index = 0; + this.mniUndo.Shortcut = System.Windows.Forms.Shortcut.CtrlZ; + this.mniUndo.Text = "&Undo"; + this.mniUndo.Click += new System.EventHandler(this.mniUndo_Click); + // + // mniView + // + this.mniView.Index = 2; + this.mniView.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.mniViewBitmaps, + this.mniViewStrips, + this.mniViewTracks, + this.mniViewCombiner}); + this.mniView.Text = "&View"; + this.mniView.Popup += new System.EventHandler(this.mniView_Popup); + // + // mniViewCombiner + // + this.mniViewCombiner.Index = 3; + this.mniViewCombiner.Text = "&Combiner Window"; + this.mniViewCombiner.Click += new System.EventHandler(this.mniViewCombiner_Click); + // + // mniTools + // + this.mniTools.Index = 3; + this.mniTools.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.mniNormalizeBitmaps, + this.mniRepairSideColors, + this.mniWallPreview, + this.mniReplaceColors, + this.menuItem3, + this.mniOptions}); + this.mniTools.Text = "&Tools"; + // + // mniNormalizeBitmaps + // + this.mniNormalizeBitmaps.Enabled = false; + this.mniNormalizeBitmaps.Index = 0; + this.mniNormalizeBitmaps.Text = "Normalize Bitmaps"; + // + // mniRepairSideColors + // + this.mniRepairSideColors.Index = 1; + this.mniRepairSideColors.Text = "Repair Side Colors"; + this.mniRepairSideColors.Click += new System.EventHandler(this.mniRepairSideColors_Click); + // + // mniWallPreview + // + this.mniWallPreview.Index = 2; + this.mniWallPreview.Text = "&Wall Preview"; + this.mniWallPreview.Click += new System.EventHandler(this.mniWallPreview_Click); + // + // menuItem3 + // + this.menuItem3.Index = 4; + this.menuItem3.Text = "-"; + // + // mniHelp + // + this.mniHelp.Index = 4; + this.mniHelp.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.menuItem1, + this.mniHelpAbout}); + this.mniHelp.Text = "&Help"; + // + // menuItem1 + // + this.menuItem1.Index = 0; + this.menuItem1.Shortcut = System.Windows.Forms.Shortcut.F1; + this.menuItem1.Text = "&Help"; + this.menuItem1.Click += new System.EventHandler(this.mniHelp_Click); + // + // mniHelpAbout + // + this.mniHelpAbout.Index = 1; + this.mniHelpAbout.Text = "&About SpiffCode AniMax..."; + this.mniHelpAbout.Click += new System.EventHandler(this.mniHelpAbout_Click); + // + // importFileDialog + // + this.importFileDialog.Filter = "Bitmap files (*.bmp,*.png,*.gif,*.jpg,*.exif,*.tif)|*.bmp;*.png;*.gif;*.exif;*.jp" + + "g|Framedata files|*.txt"; + this.importFileDialog.Multiselect = true; + this.importFileDialog.Title = "Import"; + // + // mniReplaceColors + // + this.mniReplaceColors.Index = 3; + this.mniReplaceColors.Text = "Replace Colors..."; + this.mniReplaceColors.Click += new System.EventHandler(this.mniReplaceColors_Click); + // + // MainForm + // + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.ClientSize = new System.Drawing.Size(712, 478); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.KeyPreview = true; + this.Menu = this.mnuMain; + this.Name = "MainForm"; + this.Text = "AniMax"; + this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.MainForm_KeyDown); + this.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.MainForm_KeyPress); + this.KeyUp += new System.Windows.Forms.KeyEventHandler(this.MainForm_KeyUp); + + } + #endregion + + private void OnActiveDocumentChanged(object obSender, EventArgs e) { + m_doc = Globals.ActiveDocument; + if (m_doc.FileName == null) + Text = gstrAniMax; + else + Text = gstrAniMax + " - " + m_doc.FileName; + } + + private void mniNew_Click(object sender, System.EventArgs e) { + NewDocument(); + } + + private void NewDocument() { + // First close the open document (if any) + + if (!CloseDocument()) + return; + + // Create a new one and show its views + + Globals.ActiveDocument = new AnimDoc(Globals.TileSize, + Globals.FrameRate); + ShowViews(); + + // I'm tired of being asked if I want to save untitled on exit even though I + // haven't done anything to it. + + m_doc.Dirty = false; + } + + private void mniSave_Click(object sender, System.EventArgs e) { + SaveDocument(); + } + + private void mniSaveAs_Click(object sender, System.EventArgs e) { + SaveAsDocument(); + } + + private void mniOpen_Click(object sender, System.EventArgs e) { + + // If the current doc is dirty give the user a chance to save it + // before loading the new doc. + + if (!GiveUserChanceToSaveChangesOrCancel()) + return; + + if (openFileDialog.ShowDialog() != DialogResult.OK) + return; + + OpenDocument(openFileDialog.FileName); + } + + public void CloseAndOpenDocument(string strFileName) { + if (!GiveUserChanceToSaveChangesOrCancel()) + return; + + OpenDocument(strFileName); + } + + private void OpenDocument(string strFileName) { + // + + Cursor.Current = Cursors.WaitCursor; + Directory.SetCurrentDirectory(Path.GetDirectoryName(strFileName)); + AnimDoc doc = AnimDoc.Load(strFileName); + Cursor.Current = Cursors.Arrow; + if (doc == null) { + MessageBox.Show(this, String.Format("Unexpected error loading {0} " + + "or one of the bitmap files it depends on. Sorry.", strFileName), "AniMax"); + return; + } + + Globals.ActiveDocument = doc; + Globals.TileSize = doc.TileSize; + Globals.ActiveStrip = doc.StripSet[0]; + ShowViews(); + } + + private void mniClose_Click(object sender, System.EventArgs e) { + CloseDocument(); + } + + protected override void OnClosing(System.ComponentModel.CancelEventArgs e) { + if (!CloseDocument()) + e.Cancel = true; + } + + private bool SaveDocument() { + if (m_doc.FileName == null) { + return SaveAsDocument(); + } else { + Cursor.Current = Cursors.WaitCursor; + m_doc.Save(m_doc.FileName); + Cursor.Current = Cursors.Arrow; + return true; + } + } + + private bool SaveAsDocument() { + saveFileDialog.FileName = m_doc.FileName; + if (saveFileDialog.ShowDialog() != DialogResult.OK) + return false; + + Cursor.Current = Cursors.WaitCursor; + m_doc.Save(saveFileDialog.FileName); + Cursor.Current = Cursors.Arrow; + Text = "AniMax - " + m_doc.FileName; + return true; + } + + private bool GiveUserChanceToSaveChangesOrCancel() { + if (m_doc.Dirty) { + DialogResult dlgr = MessageBox.Show(this, + String.Format("Do you want to save the changes to {0}?", m_doc.FileName), + "AniMax", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Exclamation); + if (dlgr == DialogResult.Cancel) + return false; + else if (dlgr == DialogResult.Yes) + return SaveDocument(); + } + return true; + } + + private bool CloseDocument() { + if (!GiveUserChanceToSaveChangesOrCancel()) + return false; + + Globals.ActiveDocument = Globals.NullDocument; + return true; + } + + private void mniViewBitmaps_Click(object sender, System.EventArgs e) { + ShowContent(m_tntBitmaps); + } + + private void mniViewStrips_Click(object sender, System.EventArgs e) { + ShowContent(m_tntStrips); + } + + private void mniViewTracks_Click(object sender, System.EventArgs e) { + ShowContent(m_tntFrames); + } + + private void mniViewCombiner_Click(object sender, System.EventArgs e) { + ShowContent(m_tntCombiner); + } + + private void ShowViews() { + ShowContent(m_tntStrips); + ShowContent(m_tntBitmaps); + ShowContent(m_tntFrames); + } + + private void ShowContent(Content tnt) { + if (!tnt.Visible) + m_dkm.ShowContent(tnt); + } + + private void mniView_Popup(object sender, System.EventArgs e) { + bool fDocExists = m_doc != null; + mniViewBitmaps.Enabled = fDocExists; + mniViewStrips.Enabled = fDocExists; + mniViewTracks.Enabled = fDocExists; + } + + private void mniFile_Popup(object sender, System.EventArgs e) { + bool fDocExists = m_doc != null; + mniClose.Enabled = fDocExists; + mniSave.Enabled = fDocExists; + mniSaveAs.Enabled = fDocExists; + mniExport.Enabled = fDocExists; + } + + private void mniOptions_Click(object sender, System.EventArgs e) { + // UNDONE: the options form includes a mishmash of items, some of which are + // scoped to the application, some to the current document. Document options + // should be moved to a properties dialog or something. + // CONSIDER: ye olde PropertyGrid? + OptionsForm frm = new OptionsForm(); + + frm.FrameRate = m_doc != null ? m_doc.FrameRate : Globals.FrameRate; + frm.GridWidth = Globals.GridWidth; + frm.GridHeight = Globals.GridHeight; + + if (frm.ShowDialog(this) != DialogResult.OK) + return; + + if (m_doc != null) + m_doc.FrameRate = frm.FrameRate; + else + Globals.FrameRate = frm.FrameRate; + Globals.GridWidth = frm.GridWidth; + Globals.GridHeight = frm.GridHeight; + + frm.Dispose(); + } + + private void mniImport_Click(object sender, System.EventArgs e) { + if (importFileDialog.ShowDialog() != DialogResult.OK) + return; + + string[] astrFileNames = importFileDialog.FileNames; + + // If a document doesn't already exist create a new one and show its views + + if (m_doc == Globals.NullDocument) { + m_doc = new AnimDoc(Globals.TileSize, Globals.FrameRate); + ShowViews(); + } + + Cursor.Current = Cursors.WaitCursor; + bool fSuccess = m_doc.Import(astrFileNames); + Cursor.Current = Cursors.Arrow; + if (!fSuccess) + return; + + Globals.ActiveDocument = m_doc; + Globals.ActiveStrip = m_doc.StripSet[0]; + } + + // We have a universal key handler that any control or child form can register + // with to take a crack at processing the key press. + + private void MainForm_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e) { + Globals.OnKeyPress(sender, e); + } + + private void MainForm_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e) { + Globals.OnKeyDown(sender, e); + } + + private void MainForm_KeyUp(object sender, System.Windows.Forms.KeyEventArgs e) { + Globals.OnKeyUp(sender, e); + } + + private void mniRepairSideColors_Click(object sender, System.EventArgs e) { + if (m_doc.XBitmapSet.Count == 0) { + MessageBox.Show(this, "Hey dork, no Bitmaps have been loaded yet.", "Error"); + return; + } + + foreach (XBitmap xbm in m_doc.XBitmapSet) { + bool fDirty = ReplaceColor(xbm.Bitmap, Color.FromArgb(0, 114, 232), Color.FromArgb(0, 116, 232), true); + fDirty |= ReplaceColor(xbm.Bitmap, Color.FromArgb(0, 112, 232), Color.FromArgb(0, 116, 232), true); + fDirty |= ReplaceColor(xbm.Bitmap, Color.FromArgb(0, 96, 192), Color.FromArgb(0, 96, 196), true); + fDirty |= ReplaceColor(xbm.Bitmap, Color.FromArgb(0, 48, 88), Color.FromArgb(0, 48, 92), true); + + if (fDirty) { + xbm.Dirty = fDirty; + m_doc.Dirty = true; + } + } + + // UNDONE: this is a hack. Decide on the right way to force selective refreshes + + Globals.StripControl.Invalidate(); + Globals.PreviewControl.Invalidate(); + } + + #region Handy Helpers + + static public bool ReplaceColor(Bitmap bm, Color clrOld, Color clrNew, bool f6bit) { + bool fFound = false; + + if (f6bit) { + clrOld = Color.FromArgb(clrOld.R & 0xfc, clrOld.G & 0xfc, clrOld.B & 0xfc); + clrNew = Color.FromArgb(clrNew.R & 0xfc, clrNew.G & 0xfc, clrNew.B & 0xfc); + } + + for (int y = 0; y < bm.Height; y++) { + for (int x = 0; x < bm.Width; x++) { + Color clr = bm.GetPixel(x, y); + if (f6bit) + clr = Color.FromArgb(clr.R & 0xfc, clr.G & 0xfc, clr.B & 0xfc); + + if (clr == clrOld) { + fFound = true; + bm.SetPixel(x, y, clrNew); + } + } + } + + return fFound; + } + #endregion + + private void mniUndo_Click(object sender, System.EventArgs e) { + UndoManager.Undo(); + } + + private void mniEdit_Popup(object sender, System.EventArgs e) { + mniUndo.Enabled = UndoManager.AnyUndos(); + } + + private void mniHelp_Click(object sender, System.EventArgs e) { + Process.Start("http://www.tinybit.org/AniMax"); + } + + private void mniToolsHack_Click(object sender, System.EventArgs e) { + foreach (Strip stp in Globals.ActiveDocument.StripSet) { + if (!stp.Name.ToLower().StartsWith("turret")) + continue; + + foreach (Frame fr in stp) { + Point pt = new Point(fr.SpecialPoint.X - 7, fr.SpecialPoint.Y - 3); + fr.SpecialPoint = pt; + foreach (BitmapPlacer plc in fr.BitmapPlacers) { + plc.X += 7; + plc.Y += 3; + } + } + } + + // Force a redraw of everything + + Globals.ActiveDocument = Globals.ActiveDocument; + } + + private void mniHelpAbout_Click(object sender, System.EventArgs e) { + new AboutForm().ShowDialog(this); + } + + private void mniWallPreview_Click(object sender, System.EventArgs e) { + new WallPreviewForm().ShowDialog(this); + } + + private void mniGenerateCompass_Click(object sender, System.EventArgs e) { + Bitmap bm = new Bitmap(200, 200); + Graphics g = Graphics.FromImage(bm); + + Pen pen = new Pen(Color.White); + int xC = 100; + int yC = 100; + for (int i = 0; i < 16; i++) { + double nAngle = (Math.PI * 2) * (i / 16.0); + int xE = (int)(Math.Sin(nAngle) * 100); + int yE = (int)(Math.Cos(nAngle) * 100); + g.DrawLine(pen, xC, yC, xC + xE, yC + yE); + } + g.Dispose(); + bm.Save(@"c:\compass.png", ImageFormat.Png); + } + + private void mniReplaceColors_Click(object sender, System.EventArgs e) { + if (m_doc.XBitmapSet.Count == 0) { + MessageBox.Show(this, "Hey dork, no Bitmaps have been loaded yet.", "Error"); + return; + } + + new ReplaceColorsForm(m_doc).ShowDialog(this); + } + } +} diff --git a/AniMax/MainForm.resources b/AniMax/MainForm.resources new file mode 100644 index 0000000..067238d Binary files /dev/null and b/AniMax/MainForm.resources differ diff --git a/AniMax/MainForm.resx b/AniMax/MainForm.resx new file mode 100644 index 0000000..6911e56 --- /dev/null +++ b/AniMax/MainForm.resx @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 114, 17 + + + 236, 17 + + + 17, 17 + + + 360, 17 + + + MainForm + + + + AAABAAIAEBAQAAAAAAAoAQAAJgAAACAgEAAAAAAA6AIAAE4BAAAoAAAAEAAAACAAAAABAAQAAAAAAMAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAgAAAAICAAIAAAACAAIAAgIAAAICAgADAwMAAAAD/AAD/ + AAAA//8A/wAAAP8A/wD//wAA////AAAAAAAAAAAAAH//j/j/jwAAf/+P+P+PAAB4iIiIiIgAAH93h3h3 + jwAAf3eHeHePAAB4iIiIiIgAAH93h3h3jwAAf3eHeHePAAB4iIiIiIgAAH93h3h3jwAAf3eHeHePAAB4 + iIiIgAAAAH//j/j39wAAf/+P+PdwAAB3d3d3dwAAwAEAAMABAADAAQAAwAEAAMABAADAAQAAwAEAAMAB + AADAAQAAwAEAAMABAADAAQAAwAEAAMADAADABwAAwA8AACgAAAAgAAAAQAAAAAEABAAAAAAAgAIAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAACAAACAAAAAgIAAgAAAAIAAgACAgAAAgICAAMDAwAAAAP8AAP8AAAD/ + /wD/AAAA/wD/AP//AAD///8AAAAAAAAAAAAAAAAAAAAAAAAHiIiIiIiIiIiIiIiIAAAAB/////j///j/ + //j/+AAAAAf////4///4///4//gAAAAH////+P//+P//+P/4AAAAB/////j///j///j/+AAAAAf////4 + ///4///4//gAAAAHiIiIiIiIiIiIiIiIAAAAB/94eHh4eHh4eHj/+AAAAAf/h4eIh4eIh4eI//gAAAAH + /3h4eHh4eHh4eP/4AAAAB/+Hh4iHh4iHh4j/+AAAAAf/eHh4eHh4eHh4//gAAAAHiIiIiIiIiIiIiIiI + AAAAB/94eHh4eHh4eHj/+AAAAAf/h4eIh4eIh4eI//gAAAAH/3h4eHh4eHh4eP/4AAAAB/+Hh4iHh4iH + h4j/+AAAAAf/eHh4eHh4eHh4//gAAAAHiIiIiIiIiIiIiIiIAAAAB/94eHh4eHh4eHj/+AAAAAf/h4eI + h4eIh4eI//gAAAAH/3h4eHh4eHh4eP/4AAAAB/+Hh4iHh4iHh4j/+AAAAAf/eHh4eHh4eHh4//gAAAAH + iIiIiIiIiIiIcAAAAAAAB/////j///j//3/4cAAAAAf////4///4//9/hwAAAAAH////+P//+P//eHAA + AAAAB/////j///j//3cAAAAAAAf////4///4//9wAAAAAAAHd3d3d3d3d3d3cAAAAADgAAAH4AAAB+AA + AAfgAAAH4AAAB+AAAAfgAAAH4AAAB+AAAAfgAAAH4AAAB+AAAAfgAAAH4AAAB+AAAAfgAAAH4AAAB+AA + AAfgAAAH4AAAB+AAAAfgAAAH4AAAB+AAAAfgAAAH4AAAB+AAAA/gAAAf4AAAP+AAAH/gAAD/4AAB/w== + + + \ No newline at end of file diff --git a/AniMax/MiscControls.cs b/AniMax/MiscControls.cs new file mode 100644 index 0000000..4c83f40 --- /dev/null +++ b/AniMax/MiscControls.cs @@ -0,0 +1,38 @@ +using System.Windows.Forms; +using System.Drawing; + +namespace SpiffCode { + public class ScToggleButton : CheckBox { + new public bool CanFocus { + get { + return false; + } + } + + override protected bool ShowFocusCues { + get { + return false; + } + } + + new public bool IsDefault { + get { + base.IsDefault = false; + return false; + } + set { + base.IsDefault = false; + } + } + } + + public class ScBorder : Control { + override protected void OnPaint(PaintEventArgs e) { + ControlPaint.DrawBorder(e.Graphics, ClientRectangle, + Color.Black, 0, ButtonBorderStyle.None, + SystemColors.ControlDark, 1, ButtonBorderStyle.Solid, + Color.Black, 0, ButtonBorderStyle.None, + SystemColors.ControlLight, 1, ButtonBorderStyle.Solid); + } + } +} diff --git a/AniMax/MiscControls.resources b/AniMax/MiscControls.resources new file mode 100644 index 0000000..06c24d0 Binary files /dev/null and b/AniMax/MiscControls.resources differ diff --git a/AniMax/MiscControls.resx b/AniMax/MiscControls.resx new file mode 100644 index 0000000..7e32396 --- /dev/null +++ b/AniMax/MiscControls.resx @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.0.0.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3102.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3102.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + diff --git a/AniMax/OptionsForm.cs b/AniMax/OptionsForm.cs new file mode 100644 index 0000000..c63fa97 --- /dev/null +++ b/AniMax/OptionsForm.cs @@ -0,0 +1,198 @@ +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; + +namespace SpiffCode +{ + /// + /// Summary description for OptionsForm. + /// + public class OptionsForm : System.Windows.Forms.Form + { + private System.Windows.Forms.Button btnCancel; + private System.Windows.Forms.Button btnOK; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.TextBox tbFrameRate; + private System.Windows.Forms.TextBox tbGridWidth; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.TextBox tbGridHeight; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + public OptionsForm() + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + // + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.btnOK = new System.Windows.Forms.Button(); + this.tbFrameRate = new System.Windows.Forms.TextBox(); + this.btnCancel = new System.Windows.Forms.Button(); + this.tbGridWidth = new System.Windows.Forms.TextBox(); + this.label3 = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.tbGridHeight = new System.Windows.Forms.TextBox(); + this.SuspendLayout(); + // + // label1 + // + this.label1.Location = new System.Drawing.Point(8, 16); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(72, 16); + this.label1.TabIndex = 4; + this.label1.Text = "Frame Rate:"; + // + // label2 + // + this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 7F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0))); + this.label2.Location = new System.Drawing.Point(136, 16); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(80, 16); + this.label2.TabIndex = 5; + this.label2.Text = "(in milliseconds)"; + // + // btnOK + // + this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK; + this.btnOK.Location = new System.Drawing.Point(79, 112); + this.btnOK.Name = "btnOK"; + this.btnOK.TabIndex = 1; + this.btnOK.Text = "OK"; + // + // tbFrameRate + // + this.tbFrameRate.Location = new System.Drawing.Point(80, 13); + this.tbFrameRate.Name = "tbFrameRate"; + this.tbFrameRate.Size = new System.Drawing.Size(48, 20); + this.tbFrameRate.TabIndex = 6; + this.tbFrameRate.Text = ""; + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(207, 112); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.TabIndex = 3; + this.btnCancel.Text = "Cancel"; + // + // tbGridWidth + // + this.tbGridWidth.Location = new System.Drawing.Point(80, 40); + this.tbGridWidth.Name = "tbGridWidth"; + this.tbGridWidth.Size = new System.Drawing.Size(48, 20); + this.tbGridWidth.TabIndex = 7; + this.tbGridWidth.Text = ""; + // + // label3 + // + this.label3.Location = new System.Drawing.Point(8, 44); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(64, 16); + this.label3.TabIndex = 8; + this.label3.Text = "Grid Width:"; + // + // label4 + // + this.label4.Location = new System.Drawing.Point(8, 68); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(72, 16); + this.label4.TabIndex = 9; + this.label4.Text = "Grid Height:"; + // + // tbGridHeight + // + this.tbGridHeight.Location = new System.Drawing.Point(80, 64); + this.tbGridHeight.Name = "tbGridHeight"; + this.tbGridHeight.Size = new System.Drawing.Size(48, 20); + this.tbGridHeight.TabIndex = 10; + this.tbGridHeight.Text = ""; + // + // OptionsForm + // + this.AcceptButton = this.btnOK; + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(362, 143); + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.tbGridHeight, + this.label4, + this.label3, + this.tbGridWidth, + this.tbFrameRate, + this.label2, + this.label1, + this.btnCancel, + this.btnOK}); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "OptionsForm"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Options"; + this.ResumeLayout(false); + + } + #endregion + + public int FrameRate { + get { + return Convert.ToInt32(tbFrameRate.Text); + } + set { + tbFrameRate.Text = value.ToString(); + } + } + + public int GridWidth { + get { + return Convert.ToInt32(tbGridWidth.Text); + } + set { + tbGridWidth.Text = value.ToString(); + } + } + + public int GridHeight { + get { + return Convert.ToInt32(tbGridHeight.Text); + } + set { + tbGridHeight.Text = value.ToString(); + } + } + } +} diff --git a/AniMax/OptionsForm.resources b/AniMax/OptionsForm.resources new file mode 100644 index 0000000..63b84da Binary files /dev/null and b/AniMax/OptionsForm.resources differ diff --git a/AniMax/OptionsForm.resx b/AniMax/OptionsForm.resx new file mode 100644 index 0000000..a23a3d5 --- /dev/null +++ b/AniMax/OptionsForm.resx @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + OptionsForm + + \ No newline at end of file diff --git a/AniMax/PerPixelAlphaForm.cs b/AniMax/PerPixelAlphaForm.cs new file mode 100644 index 0000000..023c63f --- /dev/null +++ b/AniMax/PerPixelAlphaForm.cs @@ -0,0 +1,241 @@ +// +// Copyright © 2002 Rui Godinho Lopes +// All rights reserved. +// +// This source file(s) may be redistributed unmodified by any means +// PROVIDING they are not sold for profit without the authors expressed +// written consent, and providing that this notice and the authors name +// and all copyright notices remain intact. +// +// Any use of the software in source or binary forms, with or without +// modification, must include, in the user documentation ("About" box and +// printed documentation) and internal comments to the code, notices to +// the end user as follows: +// +// "Portions Copyright © 2002 Rui Godinho Lopes" +// +// An email letting me know that you are using it would be nice as well. +// That's not much to ask considering the amount of work that went into +// this. +// +// THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED. USE IT AT YOUT OWN RISK. THE AUTHOR ACCEPTS NO +// LIABILITY FOR ANY DATA DAMAGE/LOSS THAT THIS PRODUCT MAY CAUSE. +// + +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.Windows.Forms; +using System.Runtime.InteropServices; + +namespace SpiffCode { + + // a static class to expose needed win32 gdi functions. + class Win32 { + + public enum Bool { + False= 0, + True + }; + + [StructLayout(LayoutKind.Sequential)] + public struct Point { + public Int32 x; + public Int32 y; + + public Point(Int32 x, Int32 y) { this.x= x; this.y= y; } + } + + [StructLayout(LayoutKind.Sequential)] + public struct Size { + public Int32 cx; + public Int32 cy; + + public Size(Int32 cx, Int32 cy) { this.cx= cx; this.cy= cy; } + } + + [StructLayout(LayoutKind.Sequential, Pack=1)] + struct ARGB { + public byte Blue; + public byte Green; + public byte Red; + public byte Alpha; + } + + [StructLayout(LayoutKind.Sequential, Pack=1)] + public struct BLENDFUNCTION { + public byte BlendOp; + public byte BlendFlags; + public byte SourceConstantAlpha; + public byte AlphaFormat; + } + + public const Int32 ULW_COLORKEY = 0x00000001; + public const Int32 ULW_ALPHA = 0x00000002; + public const Int32 ULW_OPAQUE = 0x00000004; + + public const byte AC_SRC_OVER = 0x00; + public const byte AC_SRC_ALPHA = 0x01; + + + [DllImport("user32.dll", ExactSpelling=true, SetLastError=true)] + public static extern Bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst, ref Point pptDst, ref Size psize, IntPtr hdcSrc, ref Point pprSrc, Int32 crKey, ref BLENDFUNCTION pblend, Int32 dwFlags); + + [DllImport("user32.dll", ExactSpelling=true, SetLastError=true)] + public static extern IntPtr GetDC(IntPtr hWnd); + + [DllImport("user32.dll", ExactSpelling=true)] + public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC); + + [DllImport("gdi32.dll", ExactSpelling=true, SetLastError=true)] + public static extern IntPtr CreateCompatibleDC(IntPtr hDC); + + [DllImport("gdi32.dll", ExactSpelling=true, SetLastError=true)] + public static extern Bool DeleteDC(IntPtr hdc); + + [DllImport("gdi32.dll", ExactSpelling=true)] + public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject); + + [DllImport("gdi32.dll", ExactSpelling=true, SetLastError=true)] + public static extern Bool DeleteObject(IntPtr hObject); + + [DllImport("user32.dll", ExactSpelling=true, SetLastError=true)] + public static extern Bool PrintWindow(IntPtr hwnd, IntPtr hdcBlt, UInt32 fl); + + [DllImport("gdi32.dll", ExactSpelling=true, SetLastError=true)] + public static extern Bool MoveToEx(IntPtr hdc, Int32 x, Int32 y, IntPtr lpPoint); + + [DllImport("gdi32.dll", ExactSpelling=true, SetLastError=true)] + public static extern Bool LineTo(IntPtr hdc, Int32 x, Int32 y); + + [DllImport("gdi32.dll", ExactSpelling=true, SetLastError=true)] + public static extern Bool GdiFlush(); + + [DllImport("user32.dll", ExactSpelling=true, SetLastError=true)] + public static extern Int32 SetWindowLong(IntPtr hwnd, Int32 index, Int32 value); + } + + /// PerPixel forms should derive from this base class + /// Rui Godinho Lopesrui@ruilopes.com + public class PerPixelAlphaForm : Form { + + /// Changes the current bitmap. + public void SetBitmap(Bitmap bitmap) { + SetBitmap(bitmap, 255); + } + +#if false + /// Changes the current bitmap with a custom opacity level. Here is where all happens! + public void SetBitmap(Bitmap bitmap, byte opacity) { + if (bitmap.PixelFormat != PixelFormat.Format32bppArgb) + throw new ApplicationException("The bitmap must be 32bpp with alpha-channel."); + + Bitmap bmWindow = new Bitmap(Width, Height, PixelFormat.Format32bppArgb); // NEW: + // Bitmap bmWindow = bitmap; + Graphics g = Graphics.FromImage(bmWindow); + g.Clear(Color.FromArgb(0, 0, 0, 0)); + g.DrawImage(bitmap, 0, 0, bmWindow.Width, bmWindow.Height); + g.Flush(); + + IntPtr memDc = g.GetHdc(); + + // Win32.Bool fSuccess = Win32.PrintWindow(Handle, memDc, 0); // NEW: + + int err = Marshal.GetLastWin32Error(); + if (err != 1400) { + Win32.Size size = new Win32.Size(bmWindow.Width, bmWindow.Height); + Win32.Point pointSource = new Win32.Point(0, 0); + Win32.Point topPos = new Win32.Point(Left, Top); + Win32.BLENDFUNCTION blend = new Win32.BLENDFUNCTION(); + blend.BlendOp = Win32.AC_SRC_OVER; + blend.BlendFlags = 0; + blend.SourceConstantAlpha = opacity; + blend.AlphaFormat = Win32.AC_SRC_ALPHA; + Win32.GdiFlush(); + // BackgroundImage = bmWindow; + IntPtr screenDc = Win32.GetDC(IntPtr.Zero); + Win32.UpdateLayeredWindow(Handle, screenDc, ref topPos, ref size, memDc, ref pointSource, 0, ref blend, Win32.ULW_ALPHA); + Win32.ReleaseDC(IntPtr.Zero, screenDc); + } + g.ReleaseHdc(memDc); + g.Dispose(); + } +#else + public void SetBitmap(Bitmap bitmap, byte opacity) { + if (bitmap.PixelFormat != PixelFormat.Format32bppArgb) + throw new ApplicationException("The bitmap must be 32bpp with alpha-channel."); + IntPtr screenDc = Win32.GetDC(IntPtr.Zero); + IntPtr memDc = Win32.CreateCompatibleDC(screenDc); + IntPtr hBitmap = IntPtr.Zero; + IntPtr oldBitmap = IntPtr.Zero; + + try { + hBitmap = bitmap.GetHbitmap(Color.FromArgb(0)); // grab a GDI handle from this GDI+ bitmap + oldBitmap = Win32.SelectObject(memDc, hBitmap); + + Win32.Size size = new Win32.Size(bitmap.Width, bitmap.Height); + Win32.Point pointSource = new Win32.Point(0, 0); + Win32.Point topPos = new Win32.Point(Left, Top); + Win32.BLENDFUNCTION blend = new Win32.BLENDFUNCTION(); + blend.BlendOp = Win32.AC_SRC_OVER; + blend.BlendFlags = 0; + blend.SourceConstantAlpha = opacity; + blend.AlphaFormat = Win32.AC_SRC_ALPHA; + + Win32.UpdateLayeredWindow(Handle, screenDc, ref topPos, ref size, memDc, ref pointSource, 0, ref blend, Win32.ULW_ALPHA); + } + finally { + Win32.ReleaseDC(IntPtr.Zero, screenDc); + if (hBitmap != IntPtr.Zero) { + Win32.SelectObject(memDc, oldBitmap); + //Windows.DeleteObject(hBitmap); // The documentation says that we have to use the Windows.DeleteObject... but since there is no such method I use the normal DeleteObject from Win32 GDI and it's working fine without any resource leak. + Win32.DeleteObject(hBitmap); + } + Win32.DeleteDC(memDc); + } + } +#endif + + protected override CreateParams CreateParams { + get { + CreateParams cp = base.CreateParams; + cp.ExStyle |= 0x00080000; // This form has to have the WS_EX_LAYERED extended style + return cp; + } + } + + public Bitmap SnapshotWindow() { + Bitmap bm = new Bitmap(Width, Height, PixelFormat.Format32bppArgb); +#if false + IntPtr screenDc = Win32.GetDC(IntPtr.Zero); + IntPtr memDc = Win32.CreateCompatibleDC(screenDc); + IntPtr hBitmap = IntPtr.Zero; + IntPtr oldBitmap = IntPtr.Zero; + hBitmap = bm.GetHbitmap(); // grab a GDI handle from this GDI+ bitmap + oldBitmap = Win32.SelectObject(memDc, hBitmap); + Win32.PrintWindow(Handle, memDc, 0); + Win32.ReleaseDC(IntPtr.Zero, screenDc); + Win32.SelectObject(memDc, oldBitmap); + // Win32.DeleteObject(hBitmap); + Win32.DeleteDC(memDc); + bm = Bitmap.FromHbitmap(hBitmap); +#else + Graphics g = Graphics.FromImage(bm); + IntPtr hdcMem = g.GetHdc(); + Win32.PrintWindow(Handle, hdcMem, 0); + g.ReleaseHdc(hdcMem); + g.Dispose(); +#endif + return bm; + } + + protected override void OnPaint(PaintEventArgs e) { + // base.OnPaint(e); + } + + protected override void OnPaintBackground(PaintEventArgs e) { + // base.OnPaintBackground(e); + } + } +} diff --git a/AniMax/PerPixelAlphaForm.resources b/AniMax/PerPixelAlphaForm.resources new file mode 100644 index 0000000..06c24d0 Binary files /dev/null and b/AniMax/PerPixelAlphaForm.resources differ diff --git a/AniMax/PerPixelAlphaForm.resx b/AniMax/PerPixelAlphaForm.resx new file mode 100644 index 0000000..7e32396 --- /dev/null +++ b/AniMax/PerPixelAlphaForm.resx @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.0.0.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3102.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3102.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + diff --git a/AniMax/PreviewControl.cs b/AniMax/PreviewControl.cs new file mode 100644 index 0000000..7ccacfc --- /dev/null +++ b/AniMax/PreviewControl.cs @@ -0,0 +1,452 @@ +using System; +using System.Collections; +using System.ComponentModel; +using System.Drawing; +using System.Data; +using System.Windows.Forms; + +namespace SpiffCode +{ + /// + /// Summary description for PreviewControl. + /// + public class PreviewControl : System.Windows.Forms.UserControl + { + private bool m_fFrameRecentering = false; + private bool m_fSpaceDown = false; + private bool m_fAllFrames; + private bool m_fRepositionBitmaps = false; + private BitmapPlacer m_plcSelected = null; + private Point m_ptPreSetSpecialPoint; + private bool m_fSetSpecialPoint = false; + private PreviewControlMode m_mode = PreviewControlMode.RepositionBitmap; +#if false + private bool m_fOnionSkin = true; +#endif + private bool m_fDragging = false; + private Point m_ptDragStart; + private Point[] m_aptPreDragBitmap; + private Point m_ptOffset; + private Point m_ptInitialOffset; + private System.Windows.Forms.Label label1; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + public PreviewControl() + { + // This call is required by the Windows.Forms Form Designer. + InitializeComponent(); + + // My initialization + + ResizeRedraw = true; + Globals.ActiveDocumentChanged += new EventHandler(OnActiveDocumentChanged); + Globals.ActiveFrameChanged += new EventHandler(OnInvalidatingChange); + Globals.GridChanged += new EventHandler(OnInvalidatingChange); + Globals.SideColorMappingOnChanged += new EventHandler(OnInvalidatingChange); + Globals.ShowOriginPointChanged += new EventHandler(OnInvalidatingChange); + Globals.ShowSpecialPointChanged += new EventHandler(OnInvalidatingChange); + Globals.FrameContentChanged += new EventHandler(OnInvalidatingChange); + Globals.KeyDown += new KeyEventHandler(OnGlobalKeyDown); + Globals.KeyUp += new KeyEventHandler(OnGlobalKeyUp); + m_ptOffset = new Point(0, 0); + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Component Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.label1 = new System.Windows.Forms.Label(); + this.SuspendLayout(); + // + // label1 + // + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(56, 16); + this.label1.TabIndex = 1; + this.label1.Text = "Preview"; + // + // PreviewControl + // + this.AllowDrop = true; + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.label1}); + this.Name = "PreviewControl"; + this.DragEnter += new System.Windows.Forms.DragEventHandler(this.PreviewControl_DragEnter); + this.MouseUp += new System.Windows.Forms.MouseEventHandler(this.PreviewControl_MouseUp); + this.DragDrop += new System.Windows.Forms.DragEventHandler(this.PreviewControl_DragDrop); + this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.PreviewControl_MouseMove); + this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.PreviewControl_MouseDown); + this.ResumeLayout(false); + + } + #endregion + + public event FrameOffsetEventHandler FrameOffsetChanged; + + public PreviewControlMode Mode { + set { + m_mode = value; + } + get { + return m_mode; + } + } + + private void OnGlobalKeyDown(object obSender, KeyEventArgs e) { + if (e.KeyCode == Keys.Space) { + Cursor = m_fFrameRecentering ? Globals.GrabCursor : Globals.HandCursor; + m_fSpaceDown = true; + } + } + + private void OnGlobalKeyUp(object obSender, KeyEventArgs e) { + if (e.KeyCode == Keys.Space) { + if (!m_fFrameRecentering) + Cursor = Cursors.Default; + m_fSpaceDown = false; + } + } + + private void OnActiveDocumentChanged(object obSender, EventArgs e) { + Invalidate(); + } + + public void OnFrameOffsetChanged(object obSender, FrameOffsetEventArgs e) { + m_ptOffset.X = e.X; + m_ptOffset.Y = e.Y; + Invalidate(); + } + + private void OnInvalidatingChange(object obSender, EventArgs e) { + Invalidate(); + } + + protected override void OnPaint(System.Windows.Forms.PaintEventArgs e) { + Frame.DrawArgs drwa = new Frame.DrawArgs(); + drwa.clrBackground = BackColor; + drwa.fDrawBackground = true; + drwa.fMapSideColors = Globals.SideColorMappingOn; + drwa.fShowGrid = Globals.GridOn; + drwa.cxGrid = Globals.GridWidth; + drwa.cyGrid = Globals.GridHeight; + drwa.fShowOrigin = Globals.ShowOriginPoint; + drwa.fShowSpecialPoint = Globals.ShowSpecialPoint || (m_mode == PreviewControlMode.SetSpecialPoint); + drwa.nScale = Globals.PreviewScale; + + if (Globals.ActiveStrip != null && Globals.ActiveStrip.Count != 0) { + Frame fr; + Rectangle rc = new Rectangle(0, 0, ClientRectangle.Width, ClientRectangle.Height); + +#if false + if (m_fOnionSkin && Globals.ActiveFrame > 0) { + drwa.fShowGrid = false; + drwa.fShowOrigin = false; + drwa.fShowSpecialPoint = false; + drwa.nAlpha = 128; + fr = Globals.ActiveStrip[Globals.ActiveFrame - 1]; + fr.Draw(e.Graphics, rc, drwa, m_ptOffset); + } +#endif + fr = Globals.ActiveStrip[Globals.ActiveFrame]; + fr.Draw(e.Graphics, rc, drwa, m_ptOffset); + +#if false + if (m_fOnionSkin && Globals.ActiveFrame < Globals.ActiveStrip.Count - 1) { + fr = Globals.ActiveStrip[Globals.ActiveFrame + 1]; + fr.Draw(e.Graphics, rc, drwa, m_ptOffset); + } +#endif + +#if false + // Draw selection rectangle + + if (m_plcSelected != null) { + Point ptUpperLeft = WxyFromFxy(FxyFromBxy(m_plcSelected, new Point(0, 0))); + Point ptLowerRight = WxyFromFxy(FxyFromBxy(m_plcSelected, + new Point(m_plcSelected.XBitmap.Width, m_plcSelected.XBitmap.Height))); + e.Graphics.DrawRectangle(new Pen(Color.Black), + ptUpperLeft.X, ptUpperLeft.Y, ptLowerRight.X - ptUpperLeft.X, ptLowerRight.Y - ptUpperLeft.Y); + } +#endif + } else { + e.Graphics.FillRectangle(new SolidBrush(BackColor), e.ClipRectangle); + } + } + + protected override void OnPaintBackground(System.Windows.Forms.PaintEventArgs e) { + if (Globals.ActiveStrip == null) + base.OnPaintBackground(e); + } + + private Point WxyFromFxy(Point ptFrame) { + return new Point(((ptFrame.X + m_ptOffset.X) * Globals.PreviewScale) + (ClientRectangle.Width / 2), + ((ptFrame.Y + m_ptOffset.Y) * Globals.PreviewScale) + (ClientRectangle.Height / 2)); + } + + private Point FxyFromBxy(BitmapPlacer plc, Point ptBitmap) { + return new Point(ptBitmap.X - plc.X, ptBitmap.Y - plc.Y); + } + + private Point FxyFromWxy(Point ptWindow) { + Rectangle rcClient = ClientRectangle; + int xCenter = rcClient.Width / 2; + int yCenter = rcClient.Height / 2; + + int dx = ptWindow.X - xCenter; + if (dx < 0) + dx -= Globals.PreviewScale; + int dy = ptWindow.Y - yCenter; + if (dy < 0) + dy -= Globals.PreviewScale; + return new Point((dx / Globals.PreviewScale) - m_ptOffset.X, (dy / Globals.PreviewScale) - m_ptOffset.Y); + } + + private Point BxyFromFxy(BitmapPlacer plc, Point ptFrame) { + return new Point(ptFrame.X + plc.X, ptFrame.Y + plc.Y); + } + + private BitmapPlacer HitTest(int wx, int wy) { + Color clrTransparent = Color.FromArgb(0xff, 0, 0xff); + Point fpt = FxyFromWxy(new Point(wx, wy)); + + foreach (BitmapPlacer plc in Globals.ActiveStrip[Globals.ActiveFrame].BitmapPlacers) { + Point bpt = BxyFromFxy(plc, fpt); + if (bpt.X < 0 || bpt.Y < 0 || bpt.X >= plc.XBitmap.Width || bpt.Y >= plc.XBitmap.Height) + continue; + + if (plc.XBitmap.Bitmap.GetPixel(bpt.X, bpt.Y) != clrTransparent) + return plc; + } + + return null; + } + + private void PreviewControl_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e) { + Strip stp = Globals.ActiveStrip; + if (stp == null) + return; + + if (m_fSpaceDown) { + m_fFrameRecentering = true; + m_fDragging = true; + m_ptDragStart = FxyFromWxy(new Point(e.X, e.Y)); + m_ptInitialOffset = m_ptOffset; + Cursor = Globals.GrabCursor; + return; + } + + switch (m_mode) { + case PreviewControlMode.SetSpecialPoint: + { + m_fDragging = true; + m_fSetSpecialPoint = true; + Point ptT = FxyFromWxy(new Point(e.X, e.Y)); + Frame fr = stp[Globals.ActiveFrame]; + m_ptPreSetSpecialPoint = fr.SpecialPoint; + fr.SpecialPoint = ptT; + Globals.ActiveDocument.Dirty = true; + Globals.OnFrameContentChanged(this, new EventArgs()); + } + break; + + case PreviewControlMode.RepositionBitmap: + { + BitmapPlacer plc = HitTest(e.X, e.Y); + if (plc == null) + break; + m_plcSelected = plc; + + m_fDragging = true; + m_fAllFrames = (ModifierKeys & Keys.Shift) != 0; + + // UNDONE: across frame operations don't really make sense until we have Tracks + + m_aptPreDragBitmap = new Point[stp.Count]; + for (int ifr = 0; ifr < stp.Count; ifr++) { + Frame fr = stp[ifr]; + if (fr.BitmapPlacers.Count > 0) { + m_aptPreDragBitmap[ifr].X = fr.BitmapPlacers[0].X; + m_aptPreDragBitmap[ifr].Y = fr.BitmapPlacers[0].Y; + } + } + + // Special handling for active frame + + m_aptPreDragBitmap[stp.ActiveFrame].X = m_plcSelected.X; + m_aptPreDragBitmap[stp.ActiveFrame].Y = m_plcSelected.Y; + + m_ptDragStart = FxyFromWxy(new Point(e.X, e.Y)); + } + break; + } + } + + private void PreviewControl_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e) { + if (!m_fDragging) + return; + + Strip stp = Globals.ActiveStrip; + if (stp == null) + return; + + if (m_fFrameRecentering) { + m_ptOffset = m_ptInitialOffset; + Point ptNew = FxyFromWxy(new Point(e.X, e.Y)); + m_ptOffset.X = m_ptInitialOffset.X + (ptNew.X - m_ptDragStart.X); + m_ptOffset.Y = m_ptInitialOffset.Y + (ptNew.Y - m_ptDragStart.Y); + Invalidate(); + Update(); + + // Notify anyone who cares that we've changed the Frame offset + + if (FrameOffsetChanged != null) + FrameOffsetChanged(this, new FrameOffsetEventArgs(m_ptOffset.X, m_ptOffset.Y)); + return; + } + + switch (m_mode) { + case PreviewControlMode.SetSpecialPoint: + { + Point ptT = FxyFromWxy(new Point(e.X, e.Y)); + Frame fr = stp[Globals.ActiveFrame]; + fr.SpecialPoint = ptT; + Globals.ActiveDocument.Dirty = true; + Globals.OnFrameContentChanged(this, new EventArgs()); + } + break; + + case PreviewControlMode.RepositionBitmap: + { + Point ptT = FxyFromWxy(new Point(e.X, e.Y)); + int ipl = stp[Globals.ActiveFrame].BitmapPlacers. + Index(m_plcSelected); + int ifr; + int cfr; + if (m_fAllFrames) { + ifr = 0; + cfr = stp.Count; + } else { + ifr = Globals.ActiveFrame; + cfr = Globals.ActiveFrameCount; + } + + for (int ifrT = ifr; ifrT < ifr + cfr; ifrT++) { + int iplT = ipl; + if (iplT >= stp[ifrT].BitmapPlacers.Count) { + iplT = 0; + } + BitmapPlacer plc = stp[ifrT].BitmapPlacers[iplT]; + plc.X = m_aptPreDragBitmap[ifrT].X + m_ptDragStart.X - + ptT.X; + plc.Y = m_aptPreDragBitmap[ifrT].Y + m_ptDragStart.Y - + ptT.Y; + } + m_fRepositionBitmaps = true; + Globals.ActiveDocument.Dirty = true; + Globals.OnFrameContentChanged(this, new EventArgs()); + } + break; + } + } + + private void PreviewControl_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e) { + m_fDragging = false; + + if (m_fFrameRecentering) { + Cursor = Cursors.Default; + m_fFrameRecentering = false; + } + + if (m_fRepositionBitmaps) { + m_fRepositionBitmaps = false; + Strip stp = Globals.ActiveStrip; + + UndoManager.BeginGroup(); + for (int ifr = 0; ifr < stp.Count; ifr++) { + if (ifr != stp.ActiveFrame) { + if (stp[ifr].BitmapPlacers.Count > 0) { + UndoManager.AddUndo(new UndoDelegate(UndoSetBitmapPosition), + new Object[] { stp[ifr].BitmapPlacers[0], m_aptPreDragBitmap[ifr].X, m_aptPreDragBitmap[ifr].Y }); + } + } + } + UndoManager.AddUndo(new UndoDelegate(UndoSetBitmapPosition), + new Object[] { m_plcSelected, m_aptPreDragBitmap[stp.ActiveFrame].X, m_aptPreDragBitmap[stp.ActiveFrame].Y }); + UndoManager.EndGroup(); + } + + if (m_fSetSpecialPoint) { + m_fSetSpecialPoint = false; + UndoManager.AddUndo(new UndoDelegate(UndoSetSpecialPoint), + new Object[] { Globals.ActiveStrip[Globals.ActiveFrame], m_ptPreSetSpecialPoint.X, m_ptPreSetSpecialPoint.Y }); + } + } + + private void UndoSetBitmapPosition(object[] aobArgs) { + BitmapPlacer plc = (BitmapPlacer)aobArgs[0]; + plc.X = (int)aobArgs[1]; + plc.Y = (int)aobArgs[2]; + Globals.ActiveDocument.Dirty = true; + Globals.OnFrameContentChanged(this, new EventArgs()); + } + + private void UndoSetSpecialPoint(object[] aobArgs) { + Frame fr = (Frame)aobArgs[0]; + fr.SpecialPoint = new Point((int)aobArgs[1], (int)aobArgs[2]); + Globals.ActiveDocument.Dirty = true; + Globals.OnFrameContentChanged(this, new EventArgs()); + } + + private void PreviewControl_DragEnter(object sender, System.Windows.Forms.DragEventArgs e) { + XBitmap[] axbm = (XBitmap[])e.Data.GetData(typeof(XBitmap[])); + if (axbm == null) + e.Effect = DragDropEffects.None; + else + e.Effect = DragDropEffects.Copy; + } + + private void PreviewControl_DragDrop(object sender, System.Windows.Forms.DragEventArgs e) { + XBitmap[] axbm = (XBitmap[])e.Data.GetData(typeof(XBitmap[])); + if (axbm == null) + return; + + BitmapPlacerList plcl = Globals.ActiveStrip[Globals.ActiveFrame].BitmapPlacers; + foreach (XBitmap xbm in axbm) { + BitmapPlacer plc = new BitmapPlacer(); + plc.X = 0; + plc.Y = 0; + plc.XBitmap = xbm; + plcl.Insert(0, plc); + } + + Globals.ActiveDocument.Dirty = true; + Globals.OnFrameContentChanged(this, new EventArgs()); + } + } + + public enum PreviewControlMode { + SetSpecialPoint, + RepositionBitmap, + } +} diff --git a/AniMax/PreviewControl.resources b/AniMax/PreviewControl.resources new file mode 100644 index 0000000..169c035 Binary files /dev/null and b/AniMax/PreviewControl.resources differ diff --git a/AniMax/PreviewControl.resx b/AniMax/PreviewControl.resx new file mode 100644 index 0000000..c5dd1c5 --- /dev/null +++ b/AniMax/PreviewControl.resx @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + PreviewControl + + \ No newline at end of file diff --git a/AniMax/PreviewForm.cs b/AniMax/PreviewForm.cs new file mode 100644 index 0000000..ecd262b --- /dev/null +++ b/AniMax/PreviewForm.cs @@ -0,0 +1,64 @@ +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; + +namespace SpiffCode +{ + /// + /// Summary description for PreviewForm. + /// + public class PreviewForm : System.Windows.Forms.Form + { + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + public PreviewForm(AnimDoc doc) + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + // + // TODO: Add any constructor code after InitializeComponent call + // + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + // + // PreviewForm + // + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.ClientSize = new System.Drawing.Size(292, 266); + this.Name = "PreviewForm"; + this.Text = "Preview"; + + } + #endregion + } +} diff --git a/AniMax/PreviewForm.resx b/AniMax/PreviewForm.resx new file mode 100644 index 0000000..93d4aca --- /dev/null +++ b/AniMax/PreviewForm.resx @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + PreviewForm + + \ No newline at end of file diff --git a/AniMax/PreviewPanel.cs b/AniMax/PreviewPanel.cs new file mode 100644 index 0000000..8d41387 --- /dev/null +++ b/AniMax/PreviewPanel.cs @@ -0,0 +1,277 @@ +using System; +using System.Collections; +using System.ComponentModel; +using System.Drawing; +using System.Data; +using System.Windows.Forms; + +namespace SpiffCode +{ + /// + /// Summary description for PreviewPanel. + /// + public class PreviewPanel : System.Windows.Forms.UserControl + { + private bool m_fLoop = false; + private System.Windows.Forms.NumericUpDown nudHoldCount; + private SpiffCode.PreviewControl ctlPreview; + private System.Windows.Forms.Panel panel1; + private System.Windows.Forms.CheckBox ckbSpecialPoint; + private System.Windows.Forms.Button btnStop; + private System.Windows.Forms.Button btnPlay; + private System.Windows.Forms.CheckBox ckbTogglePlay; + private System.Windows.Forms.Timer tmrAnim; + private System.Windows.Forms.ToolTip toolTip1; + private System.Windows.Forms.TrackBar trkbScale; + private System.ComponentModel.IContainer components; + + public PreviewPanel(AnimDoc doc) + { + // This call is required by the Windows.Forms Form Designer. + InitializeComponent(); + + Globals.ActiveStripChanged += new EventHandler(OnActiveStripChanged); + Globals.ActiveFrameChanged += new EventHandler(OnActiveFrameChanged); + trkbScale.Value = Globals.PreviewScale - 1; + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Component Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(PreviewPanel)); + this.nudHoldCount = new System.Windows.Forms.NumericUpDown(); + this.ctlPreview = new SpiffCode.PreviewControl(); + this.panel1 = new System.Windows.Forms.Panel(); + this.trkbScale = new System.Windows.Forms.TrackBar(); + this.ckbSpecialPoint = new System.Windows.Forms.CheckBox(); + this.btnStop = new System.Windows.Forms.Button(); + this.btnPlay = new System.Windows.Forms.Button(); + this.ckbTogglePlay = new System.Windows.Forms.CheckBox(); + this.tmrAnim = new System.Windows.Forms.Timer(this.components); + this.toolTip1 = new System.Windows.Forms.ToolTip(this.components); + ((System.ComponentModel.ISupportInitialize)(this.nudHoldCount)).BeginInit(); + this.panel1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.trkbScale)).BeginInit(); + this.SuspendLayout(); + // + // nudHoldCount + // + this.nudHoldCount.Anchor = (System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right); + this.nudHoldCount.BackColor = System.Drawing.SystemColors.Control; + this.nudHoldCount.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.nudHoldCount.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0))); + this.nudHoldCount.InterceptArrowKeys = false; + this.nudHoldCount.Location = new System.Drawing.Point(0, 248); + this.nudHoldCount.Name = "nudHoldCount"; + this.nudHoldCount.ReadOnly = true; + this.nudHoldCount.Size = new System.Drawing.Size(32, 15); + this.nudHoldCount.TabIndex = 2; + this.nudHoldCount.TabStop = false; + this.nudHoldCount.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; + this.toolTip1.SetToolTip(this.nudHoldCount, "Hold Count"); + this.nudHoldCount.ValueChanged += new System.EventHandler(this.nudHoldCount_ValueChanged); + // + // ctlPreview + // + this.ctlPreview.AllowDrop = true; + this.ctlPreview.Dock = System.Windows.Forms.DockStyle.Fill; + this.ctlPreview.Mode = SpiffCode.PreviewControlMode.RepositionBitmap; + this.ctlPreview.Name = "ctlPreview"; + this.ctlPreview.Size = new System.Drawing.Size(264, 264); + this.ctlPreview.TabIndex = 3; + // + // panel1 + // + this.panel1.BackColor = System.Drawing.SystemColors.Control; + this.panel1.Controls.AddRange(new System.Windows.Forms.Control[] { + this.trkbScale, + this.ckbSpecialPoint, + this.nudHoldCount}); + this.panel1.Dock = System.Windows.Forms.DockStyle.Right; + this.panel1.Location = new System.Drawing.Point(264, 0); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(32, 264); + this.panel1.TabIndex = 4; + // + // trkbScale + // + this.trkbScale.Location = new System.Drawing.Point(0, 27); + this.trkbScale.Maximum = 20; + this.trkbScale.Name = "trkbScale"; + this.trkbScale.Orientation = System.Windows.Forms.Orientation.Vertical; + this.trkbScale.Size = new System.Drawing.Size(45, 85); + this.trkbScale.TabIndex = 4; + this.trkbScale.TickFrequency = 2; + this.trkbScale.TickStyle = System.Windows.Forms.TickStyle.TopLeft; + this.toolTip1.SetToolTip(this.trkbScale, "Zoom Level"); + this.trkbScale.Value = 1; + this.trkbScale.Scroll += new System.EventHandler(this.trkbScale_Scroll); + // + // ckbSpecialPoint + // + this.ckbSpecialPoint.Appearance = System.Windows.Forms.Appearance.Button; + this.ckbSpecialPoint.Image = ((System.Drawing.Bitmap)(resources.GetObject("ckbSpecialPoint.Image"))); + this.ckbSpecialPoint.Location = new System.Drawing.Point(4, 1); + this.ckbSpecialPoint.Name = "ckbSpecialPoint"; + this.ckbSpecialPoint.Size = new System.Drawing.Size(24, 24); + this.ckbSpecialPoint.TabIndex = 3; + this.toolTip1.SetToolTip(this.ckbSpecialPoint, "Special Point Tool"); + this.ckbSpecialPoint.CheckedChanged += new System.EventHandler(this.ckbSpecialPoint_CheckedChanged); + // + // btnStop + // + this.btnStop.Anchor = (System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right); + this.btnStop.Image = ((System.Drawing.Bitmap)(resources.GetObject("btnStop.Image"))); + this.btnStop.Location = new System.Drawing.Point(192, 240); + this.btnStop.Name = "btnStop"; + this.btnStop.Size = new System.Drawing.Size(24, 24); + this.btnStop.TabIndex = 11; + this.toolTip1.SetToolTip(this.btnStop, "Stop animation"); + this.btnStop.Click += new System.EventHandler(this.btnStop_Click); + // + // btnPlay + // + this.btnPlay.Anchor = (System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right); + this.btnPlay.Image = ((System.Drawing.Bitmap)(resources.GetObject("btnPlay.Image"))); + this.btnPlay.Location = new System.Drawing.Point(216, 240); + this.btnPlay.Name = "btnPlay"; + this.btnPlay.Size = new System.Drawing.Size(24, 24); + this.btnPlay.TabIndex = 10; + this.toolTip1.SetToolTip(this.btnPlay, "Play animation (once through)"); + this.btnPlay.Click += new System.EventHandler(this.btnPlay_Click); + // + // ckbTogglePlay + // + this.ckbTogglePlay.Anchor = (System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right); + this.ckbTogglePlay.Appearance = System.Windows.Forms.Appearance.Button; + this.ckbTogglePlay.Image = ((System.Drawing.Bitmap)(resources.GetObject("ckbTogglePlay.Image"))); + this.ckbTogglePlay.Location = new System.Drawing.Point(240, 240); + this.ckbTogglePlay.Name = "ckbTogglePlay"; + this.ckbTogglePlay.Size = new System.Drawing.Size(24, 24); + this.ckbTogglePlay.TabIndex = 9; + this.toolTip1.SetToolTip(this.ckbTogglePlay, "Play animation (loop forever)"); + this.ckbTogglePlay.Click += new System.EventHandler(this.ckbTogglePlay_CheckedChanged); + // + // tmrAnim + // + this.tmrAnim.Interval = 80; + this.tmrAnim.Tick += new System.EventHandler(this.tmrAnim_Tick); + // + // PreviewPanel + // + this.BackColor = System.Drawing.Color.FromArgb(((System.Byte)(192)), ((System.Byte)(192)), ((System.Byte)(255))); + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.btnStop, + this.btnPlay, + this.ckbTogglePlay, + this.ctlPreview, + this.panel1}); + this.Name = "PreviewPanel"; + this.Size = new System.Drawing.Size(296, 264); + ((System.ComponentModel.ISupportInitialize)(this.nudHoldCount)).EndInit(); + this.panel1.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.trkbScale)).EndInit(); + this.ResumeLayout(false); + + } + #endregion + + public Control PreviewControl { + get { + return ctlPreview; + } + } + + private void OnActiveStripChanged(object obSender, EventArgs e) { + if (Globals.ActiveStrip != null) + tmrAnim.Interval = 80 * (Globals.ActiveStrip.DefHoldCount + 1); + } + + private void OnActiveFrameChanged(object obSender, EventArgs e) { + if (Globals.ActiveStrip != null && Globals.ActiveStrip.Count != 0) + nudHoldCount.Value = Globals.ActiveStrip[Globals.ActiveFrame].HoldCount; + } + + private void nudHoldCount_ValueChanged(object sender, System.EventArgs e) { + if (Globals.ActiveStrip == null) + return; + + Globals.ActiveStrip[Globals.ActiveFrame].HoldCount = (int)nudHoldCount.Value; + + // UNDONE: this is a hack. Determine the best way to do this kind + // of thing. + + Globals.StripControl.Invalidate(); + } + + private void ckbSpecialPoint_CheckedChanged(object sender, System.EventArgs e) { + if (ckbSpecialPoint.Checked) + ctlPreview.Mode = PreviewControlMode.SetSpecialPoint; + else + ctlPreview.Mode = PreviewControlMode.RepositionBitmap; + ctlPreview.Invalidate(); + } + + private void ckbTogglePlay_CheckedChanged(object sender, System.EventArgs e) { + m_fLoop = !m_fLoop; + if (m_fLoop) + tmrAnim.Start(); + else + tmrAnim.Stop(); + } + + private void btnPlay_Click(object sender, System.EventArgs e) { + Globals.ActiveStrip.ActiveFrame = 0; + m_fLoop = false; + ckbTogglePlay.Checked = false; + tmrAnim.Start(); + } + + private void btnStop_Click(object sender, System.EventArgs e) { + tmrAnim.Stop(); + ckbTogglePlay.Checked = false; + m_fLoop = false; + } + + private void tmrAnim_Tick(object sender, System.EventArgs e) { + Strip stp = Globals.ActiveStrip; + if (stp == null) + return; + + int ifr = stp.ActiveFrame + 1; + if (ifr >= stp.Count) { + ifr = 0; + if (!m_fLoop) { + tmrAnim.Stop(); + } + } + tmrAnim.Interval = (stp[ifr].HoldCount * 80) + (80 * (Globals.ActiveStrip.DefHoldCount + 1)); + stp.ActiveFrame = ifr; + } + + private void trkbScale_Scroll(object sender, System.EventArgs e) { + Globals.PreviewScale = trkbScale.Value + 1; + ctlPreview.Invalidate(); + } + } +} diff --git a/AniMax/PreviewPanel.resources b/AniMax/PreviewPanel.resources new file mode 100644 index 0000000..fe3bf0d Binary files /dev/null and b/AniMax/PreviewPanel.resources differ diff --git a/AniMax/PreviewPanel.resx b/AniMax/PreviewPanel.resx new file mode 100644 index 0000000..800b385 --- /dev/null +++ b/AniMax/PreviewPanel.resx @@ -0,0 +1,142 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAlwSFlzAAALEwAA + CxMBAJqcGAAAADNJREFUOE9j/P//PwNFAGQAJZgizWDXU2L7IDeAARi2MIjPmzjDYOANIDZ2hnM0Dp0w + AAAXu8dW6QvQNwAAAABJRU5ErkJggg== + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAlwSFlzAAALEwAA + CxMBAJqcGAAAADFJREFUOE9j/P//PwNFAGQAJZgizWDXI9sO9ArIPzgxNpeOGjD8ApGcFEndlDg0XQAA + cElQzY1k9AEAAAAASUVORK5CYII= + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAlwSFlzAAALEwAA + CxMBAJqcGAAAAFtJREFUOE+lk9sKACAIQ+v/P9pUSDC76QLf8jjH7ETUoCcApEIzqxFJQdZpyA3gIFWA + QRCAQlAA9+/NfnkwDYUB0Aqas+oKFtIKwCX8G5CNNXQHeojZiet/GDAAj65E2bBhT7EAAAAASUVORK5C + YII= + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAlwSFlzAAALEwAA + CxMBAJqcGAAAAGZJREFUOE/VkjEOACAIA/X/j8ZgAioRqCYOujCAV6BUIiovXkpl4UicASEEBbgQCxBF + L/Ka5lxvX0bIPksXS90OMJuxqBmXeg4F7ByGAd55QIDotlwAskytObXRwoeNQrqNSvoX0ACm8lG+/th5 + agAAAABJRU5ErkJggg== + + + + 325, 30 + + + 17, 17 + + + PreviewPanel + + \ No newline at end of file diff --git a/AniMax/ReplaceColorsForm.cs b/AniMax/ReplaceColorsForm.cs new file mode 100644 index 0000000..82866aa --- /dev/null +++ b/AniMax/ReplaceColorsForm.cs @@ -0,0 +1,296 @@ +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; +using System.Runtime.InteropServices; + +namespace SpiffCode +{ + /// + /// Summary description for ReplaceColorsForm. + /// + public class ReplaceColorsForm : System.Windows.Forms.Form + { + private AnimDoc m_doc; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.TextBox tbR1; + private System.Windows.Forms.TextBox tbG1; + private System.Windows.Forms.TextBox tbB1; + private System.Windows.Forms.TextBox tbR2; + private System.Windows.Forms.TextBox tbB2; + private System.Windows.Forms.TextBox tbG2; + private System.Windows.Forms.Button btnOK; + private System.Windows.Forms.Button btnCancel; + private System.Windows.Forms.Button button1; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + public ReplaceColorsForm(AnimDoc doc) + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + // + // TODO: Add any constructor code after InitializeComponent call + // + m_doc = doc; + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.tbR1 = new System.Windows.Forms.TextBox(); + this.tbG1 = new System.Windows.Forms.TextBox(); + this.tbB1 = new System.Windows.Forms.TextBox(); + this.label3 = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.label5 = new System.Windows.Forms.Label(); + this.tbR2 = new System.Windows.Forms.TextBox(); + this.tbB2 = new System.Windows.Forms.TextBox(); + this.tbG2 = new System.Windows.Forms.TextBox(); + this.btnOK = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.button1 = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // label1 + // + this.label1.Location = new System.Drawing.Point(8, 43); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(56, 16); + this.label1.TabIndex = 0; + this.label1.Text = "Replace:"; + // + // label2 + // + this.label2.Location = new System.Drawing.Point(24, 75); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(32, 16); + this.label2.TabIndex = 1; + this.label2.Text = "With:"; + // + // tbR1 + // + this.tbR1.Location = new System.Drawing.Point(72, 40); + this.tbR1.Name = "tbR1"; + this.tbR1.Size = new System.Drawing.Size(32, 20); + this.tbR1.TabIndex = 0; + this.tbR1.Text = "0"; + // + // tbG1 + // + this.tbG1.Location = new System.Drawing.Point(120, 40); + this.tbG1.Name = "tbG1"; + this.tbG1.Size = new System.Drawing.Size(32, 20); + this.tbG1.TabIndex = 1; + this.tbG1.Text = "0"; + // + // tbB1 + // + this.tbB1.Location = new System.Drawing.Point(168, 40); + this.tbB1.Name = "tbB1"; + this.tbB1.Size = new System.Drawing.Size(32, 20); + this.tbB1.TabIndex = 2; + this.tbB1.Text = "0"; + // + // label3 + // + this.label3.Location = new System.Drawing.Point(80, 16); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(16, 16); + this.label3.TabIndex = 3; + this.label3.Text = "R"; + // + // label4 + // + this.label4.Location = new System.Drawing.Point(128, 16); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(16, 16); + this.label4.TabIndex = 3; + this.label4.Text = "G"; + // + // label5 + // + this.label5.Location = new System.Drawing.Point(176, 16); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(16, 16); + this.label5.TabIndex = 3; + this.label5.Text = "B"; + // + // tbR2 + // + this.tbR2.Location = new System.Drawing.Point(72, 72); + this.tbR2.Name = "tbR2"; + this.tbR2.Size = new System.Drawing.Size(32, 20); + this.tbR2.TabIndex = 3; + this.tbR2.Text = "0"; + // + // tbB2 + // + this.tbB2.Location = new System.Drawing.Point(168, 72); + this.tbB2.Name = "tbB2"; + this.tbB2.Size = new System.Drawing.Size(32, 20); + this.tbB2.TabIndex = 5; + this.tbB2.Text = "0"; + // + // tbG2 + // + this.tbG2.Location = new System.Drawing.Point(120, 72); + this.tbG2.Name = "tbG2"; + this.tbG2.Size = new System.Drawing.Size(32, 20); + this.tbG2.TabIndex = 4; + this.tbG2.Text = "0"; + // + // btnOK + // + this.btnOK.Location = new System.Drawing.Point(24, 112); + this.btnOK.Name = "btnOK"; + this.btnOK.TabIndex = 6; + this.btnOK.Text = "OK"; + this.btnOK.Click += new System.EventHandler(this.btnOK_Click); + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(136, 112); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.TabIndex = 7; + this.btnCancel.Text = "Cancel"; + // + // button1 + // + this.button1.Location = new System.Drawing.Point(16, 10); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(32, 24); + this.button1.TabIndex = 8; + this.button1.Text = "Get"; + this.button1.Click += new System.EventHandler(this.button1_Click); + // + // ReplaceColorsForm + // + this.AcceptButton = this.btnOK; + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(232, 142); + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.button1, + this.btnOK, + this.label3, + this.tbR1, + this.label2, + this.label1, + this.tbG1, + this.tbB1, + this.label4, + this.label5, + this.tbR2, + this.tbB2, + this.tbG2, + this.btnCancel}); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "ReplaceColorsForm"; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Replace Colors"; + this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.ReplaceColorsForm_MouseDown); + this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.ReplaceColorsForm_MouseMove); + this.ResumeLayout(false); + + } + #endregion + + private void btnOK_Click(object sender, System.EventArgs e) { + foreach (XBitmap xbm in m_doc.XBitmapSet) { + int nR1 = int.Parse(tbR1.Text); + int nG1 = int.Parse(tbG1.Text); + int nB1 = int.Parse(tbB1.Text); + + int nR2 = int.Parse(tbR2.Text); + int nG2 = int.Parse(tbG2.Text); + int nB2 = int.Parse(tbB2.Text); + + bool fDirty = MainForm.ReplaceColor(xbm.Bitmap, Color.FromArgb(nR1, nG1, nB1), + Color.FromArgb(nR2, nG2, nB2), true); + + if (fDirty) { + xbm.Dirty = fDirty; + m_doc.Dirty = true; + } + } + + // UNDONE: this is a hack. Decide on the right way to force selective refreshes + + Globals.StripControl.Invalidate(); + Globals.PreviewControl.Invalidate(); + + DialogResult = DialogResult.OK; + } + + [DllImport("user32.dll", ExactSpelling=true, SetLastError=true)] + public static extern IntPtr GetDC(IntPtr hWnd); + + [DllImport("user32.dll", ExactSpelling=true)] + public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC); + + [DllImport("gdi32.dll", ExactSpelling=true, SetLastError=true)] + public static extern Int32 GetPixel(IntPtr hdc, Int32 x, Int32 y); + + private void ReplaceColorsForm_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e) { + if (!Capture) + return; + + IntPtr hdc = GetDC(IntPtr.Zero); + Point ptScreen = PointToScreen(new Point(e.X, e.Y)); + Int32 cr = GetPixel(hdc, ptScreen.X, ptScreen.Y); + tbR1.Text = (cr & 0xff).ToString(); + tbG1.Text = ((cr >> 8) & 0xff).ToString(); + tbB1.Text = ((cr >> 16) & 0xff).ToString(); + ReleaseDC(IntPtr.Zero, hdc); + +// tbR2.Text = ptScreen.X.ToString(); +// tbG2.Text = ptScreen.Y.ToString(); + } + + private void button1_Click(object sender, System.EventArgs e) { + Capture = true; + } + + private void ReplaceColorsForm_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e) { + if (Capture) { + Capture = false; + } + } + } +} diff --git a/AniMax/ReplaceColorsForm.resources b/AniMax/ReplaceColorsForm.resources new file mode 100644 index 0000000..9f49bb0 Binary files /dev/null and b/AniMax/ReplaceColorsForm.resources differ diff --git a/AniMax/ReplaceColorsForm.resx b/AniMax/ReplaceColorsForm.resx new file mode 100644 index 0000000..1e225c5 --- /dev/null +++ b/AniMax/ReplaceColorsForm.resx @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ReplaceColorsForm + + \ No newline at end of file diff --git a/AniMax/Resources/about graphic.png b/AniMax/Resources/about graphic.png new file mode 100644 index 0000000..d5aa04c Binary files /dev/null and b/AniMax/Resources/about graphic.png differ diff --git a/AniMax/Resources/grab.cur b/AniMax/Resources/grab.cur new file mode 100644 index 0000000..e1b2de4 Binary files /dev/null and b/AniMax/Resources/grab.cur differ diff --git a/AniMax/Resources/grid icon.png b/AniMax/Resources/grid icon.png new file mode 100644 index 0000000..e5fa335 Binary files /dev/null and b/AniMax/Resources/grid icon.png differ diff --git a/AniMax/Resources/hand.cur b/AniMax/Resources/hand.cur new file mode 100644 index 0000000..cc66a00 Binary files /dev/null and b/AniMax/Resources/hand.cur differ diff --git a/AniMax/Resources/hires toggle icon.png b/AniMax/Resources/hires toggle icon.png new file mode 100644 index 0000000..85f3e1b Binary files /dev/null and b/AniMax/Resources/hires toggle icon.png differ diff --git a/AniMax/Resources/loop forward icon.png b/AniMax/Resources/loop forward icon.png new file mode 100644 index 0000000..4bc2198 Binary files /dev/null and b/AniMax/Resources/loop forward icon.png differ diff --git a/AniMax/Resources/origin point toggle icon.png b/AniMax/Resources/origin point toggle icon.png new file mode 100644 index 0000000..d01c379 Binary files /dev/null and b/AniMax/Resources/origin point toggle icon.png differ diff --git a/AniMax/Resources/play icon.png b/AniMax/Resources/play icon.png new file mode 100644 index 0000000..7b92ae1 Binary files /dev/null and b/AniMax/Resources/play icon.png differ diff --git a/AniMax/Resources/play toggle icon.png b/AniMax/Resources/play toggle icon.png new file mode 100644 index 0000000..7a47b80 Binary files /dev/null and b/AniMax/Resources/play toggle icon.png differ diff --git a/AniMax/Resources/properties icon.png b/AniMax/Resources/properties icon.png new file mode 100644 index 0000000..58f9734 Binary files /dev/null and b/AniMax/Resources/properties icon.png differ diff --git a/AniMax/Resources/set special point icon.png b/AniMax/Resources/set special point icon.png new file mode 100644 index 0000000..6e65b04 Binary files /dev/null and b/AniMax/Resources/set special point icon.png differ diff --git a/AniMax/Resources/side color toggle icon.png b/AniMax/Resources/side color toggle icon.png new file mode 100644 index 0000000..7d6f1ed Binary files /dev/null and b/AniMax/Resources/side color toggle icon.png differ diff --git a/AniMax/Resources/stop icon.png b/AniMax/Resources/stop icon.png new file mode 100644 index 0000000..262e6aa Binary files /dev/null and b/AniMax/Resources/stop icon.png differ diff --git a/AniMax/Strip.cs b/AniMax/Strip.cs new file mode 100644 index 0000000..6d5259c --- /dev/null +++ b/AniMax/Strip.cs @@ -0,0 +1,147 @@ +using System; +using System.Collections; +using System.Runtime.Serialization; + +namespace SpiffCode +{ + /// + /// Summary description for Strip. + /// + [Serializable] + public class Strip : CollectionBase, ISerializable, IComparable, ICloneable + { + private string m_strName = null; + private int m_ifrActive = 0; + private int m_cfrActive = 1; + private int m_cHold = 0; + + public Strip(string strName) + { + m_strName = strName; + } + + public object Clone() + { + Strip stpNew = new Strip(m_strName); + for (int i = 0; i < this.Count; i++) { + stpNew.Add((Frame)this[i].Clone()); + } + stpNew.m_ifrActive = m_ifrActive; + stpNew.m_cfrActive = m_cfrActive; + stpNew.m_cHold = m_cHold; + return stpNew; + } + + // IComparable implementation + + public int CompareTo(object ob) { + return m_strName.CompareTo(((Strip)ob).m_strName); + } + + // Public properties + + public Frame this[int i] { + get { + return (Frame)InnerList[i]; + } + set { + // We allow Frames to be added out-of-range in which case + // we expand the range to include the new Frame. + + while (i >= InnerList.Count) + InnerList.Add(null); + InnerList[i] = value; + } + } + + public string Name { + get { + return m_strName; + } + set { + m_strName = value; + } + } + + // Exposed for anyone who wants to keep track of this Strip's ActiveFrame + + public event EventHandler ActiveFrameChanged; + + public int ActiveFrame { + get { + return m_ifrActive; + } + set { + m_ifrActive = value; + ActiveFrameCount = 1; + if (ActiveFrameChanged != null) + ActiveFrameChanged(this, EventArgs.Empty); + } + } + + public event EventHandler ActiveFrameCountChanged; + + public int ActiveFrameCount { + get { + return m_cfrActive; + } + set { + m_cfrActive = value; + if (m_ifrActive + m_cfrActive > Count) { + m_cfrActive = Count - m_ifrActive; + } + if (ActiveFrameCountChanged != null) + ActiveFrameCountChanged(this, EventArgs.Empty); + } + } + + public int DefHoldCount { + get { + return m_cHold; + } + set { + m_cHold = value; + } + } + + public int Add(Frame fr) { + return ((IList)this).Add(fr); + } + + public int IndexOf(Frame fr) { + return ((IList)this).IndexOf(fr); + } + + public void Insert(int ifr, Frame fr) { + ((IList)this).Insert(ifr, fr); + } + + // ISerializable interface implementation + + private Strip(SerializationInfo seri, StreamingContext stmc) { + m_strName = seri.GetString("Name"); + + try { + m_cHold = seri.GetInt32("DefHoldCount"); + } catch {} + + int cfr = seri.GetInt32("FrameCount"); + for (int i = 0; i < cfr; i++) { + Frame fr = (Frame)seri.GetValue(i.ToString(), typeof(Frame)); + Add(fr); + } + } + + void ISerializable.GetObjectData(SerializationInfo seri, StreamingContext stmc) { + seri.AddValue("Name", m_strName); + seri.AddValue("DefHoldCount", m_cHold); + + seri.AddValue("FrameCount", InnerList.Count); + int i = 0; + foreach (Frame fr in InnerList) { + seri.AddValue(i.ToString(), fr); + i++; + } + } + } +} diff --git a/AniMax/StripControl.cs b/AniMax/StripControl.cs new file mode 100644 index 0000000..4c1ea87 --- /dev/null +++ b/AniMax/StripControl.cs @@ -0,0 +1,716 @@ +using System; +using System.Collections; +using System.ComponentModel; +using System.Drawing; +using System.Data; +using System.Windows.Forms; + +namespace SpiffCode +{ + /// + /// Summary description for StripControl. + /// + public class StripControl : System.Windows.Forms.UserControl + { + private bool m_fSpaceDown = false; + private int m_ifrInsertionPoint = -1; + private bool m_fFrameRecentering = false; + private Point m_ptDown; + private Point m_ptOffset; + private Point m_ptInitialOffset; + private int m_cxFrame = 100; + private bool m_fButtonDown = false; + private bool m_fFrameResizing = false; + private Strip m_stp; + private System.Windows.Forms.HScrollBar sb; + private System.Windows.Forms.ContextMenu mnu; + private System.Windows.Forms.MenuItem mniDelete; + private System.Windows.Forms.MenuItem mniInsert; + private System.Windows.Forms.MenuItem mniCut; + private System.Windows.Forms.MenuItem mniCopy; + private System.Windows.Forms.MenuItem mniPaste; + private System.Windows.Forms.MenuItem menuItem1; + private System.Windows.Forms.MenuItem mniProperties; + private System.Windows.Forms.Timer tmrScroll; + private System.ComponentModel.IContainer components; + + public StripControl() + { + // This call is required by the Windows.Forms Form Designer. + InitializeComponent(); + + // My initialization + + ResizeRedraw = true; + SetStyle(ControlStyles.Selectable, true); + + m_ptOffset = new Point(0, 0); + Globals.ActiveFrameChanged += new EventHandler(OnInvalidatingChange); + Globals.ActiveFrameCountChanged += new EventHandler(OnInvalidatingChange); + Globals.GridChanged += new EventHandler(OnInvalidatingChange); + Globals.SideColorMappingOnChanged += new EventHandler(OnInvalidatingChange); + Globals.ShowOriginPointChanged += new EventHandler(OnInvalidatingChange); + Globals.ShowSpecialPointChanged += new EventHandler(OnInvalidatingChange); + Globals.KeyPress += new KeyPressEventHandler(StripControl_KeyPress); + Globals.FrameContentChanged += new EventHandler(OnInvalidatingChange); + Globals.KeyDown += new KeyEventHandler(OnGlobalKeyDown); + Globals.KeyUp += new KeyEventHandler(OnGlobalKeyUp); + Globals.StripScaleChanged += new EventHandler(OnInvalidatingChange); + mnu.Popup += new EventHandler(StripControl_Popup); + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Component Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.sb = new System.Windows.Forms.HScrollBar(); + this.mnu = new System.Windows.Forms.ContextMenu(); + this.mniCut = new System.Windows.Forms.MenuItem(); + this.mniCopy = new System.Windows.Forms.MenuItem(); + this.mniPaste = new System.Windows.Forms.MenuItem(); + this.mniDelete = new System.Windows.Forms.MenuItem(); + this.mniInsert = new System.Windows.Forms.MenuItem(); + this.menuItem1 = new System.Windows.Forms.MenuItem(); + this.mniProperties = new System.Windows.Forms.MenuItem(); + this.tmrScroll = new System.Windows.Forms.Timer(this.components); + this.SuspendLayout(); + // + // sb + // + this.sb.Dock = System.Windows.Forms.DockStyle.Bottom; + this.sb.Location = new System.Drawing.Point(0, 79); + this.sb.Name = "sb"; + this.sb.Size = new System.Drawing.Size(335, 16); + this.sb.TabIndex = 0; + this.sb.ValueChanged += new System.EventHandler(this.sb_ValueChanged); + // + // mnu + // + this.mnu.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.mniCut, + this.mniCopy, + this.mniPaste, + this.mniDelete, + this.mniInsert, + this.menuItem1, + this.mniProperties}); + // + // mniCut + // + this.mniCut.Index = 0; + this.mniCut.Shortcut = System.Windows.Forms.Shortcut.CtrlX; + this.mniCut.Text = "Cu&t"; + this.mniCut.Click += new System.EventHandler(this.mniCut_Click); + // + // mniCopy + // + this.mniCopy.Index = 1; + this.mniCopy.Shortcut = System.Windows.Forms.Shortcut.CtrlC; + this.mniCopy.Text = "&Copy"; + this.mniCopy.Click += new System.EventHandler(this.mniCopy_Click); + // + // mniPaste + // + this.mniPaste.Index = 2; + this.mniPaste.Shortcut = System.Windows.Forms.Shortcut.CtrlV; + this.mniPaste.Text = "&Paste"; + this.mniPaste.Click += new System.EventHandler(this.mniPaste_Click); + // + // mniDelete + // + this.mniDelete.Index = 3; + this.mniDelete.Shortcut = System.Windows.Forms.Shortcut.Del; + this.mniDelete.Text = "&Delete"; + this.mniDelete.Click += new System.EventHandler(this.mniDelete_Click); + // + // mniInsert + // + this.mniInsert.Index = 4; + this.mniInsert.Shortcut = System.Windows.Forms.Shortcut.Ins; + this.mniInsert.Text = "&Insert"; + this.mniInsert.Click += new System.EventHandler(this.mniInsert_Click); + // + // menuItem1 + // + this.menuItem1.Index = 5; + this.menuItem1.Text = "-"; + // + // mniProperties + // + this.mniProperties.Index = 6; + this.mniProperties.Text = "Properties"; + // + // tmrScroll + // + this.tmrScroll.Interval = 150; + this.tmrScroll.Tick += new System.EventHandler(this.tmrScroll_Tick); + // + // StripControl + // + this.AllowDrop = true; + this.ContextMenu = this.mnu; + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.sb}); + this.DockPadding.Bottom = 1; + this.DockPadding.Right = 1; + this.Name = "StripControl"; + this.Size = new System.Drawing.Size(336, 96); + this.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.StripControl_KeyPress); + this.DragEnter += new System.Windows.Forms.DragEventHandler(this.StripControl_DragEnter); + this.DragDrop += new System.Windows.Forms.DragEventHandler(this.StripControl_DragDrop); + this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.StripControl_KeyDown); + this.DragOver += new System.Windows.Forms.DragEventHandler(this.StripControl_DragOver); + this.ResumeLayout(false); + + } + #endregion + + public Strip Strip { + set { + // Our Undo isn't robust enough to be maintained across strip + // activations so we flush it to be sure strip A's undos aren't + // applied to strip B. + + UndoManager.Flush(); + + m_stp = value; + RecalcScrollbar(); + Invalidate(); + } + get { + return m_stp; + } + } + + public int FrameWidth { + set { + m_cxFrame = value; + RecalcScrollbar(); + Invalidate(); + } + } + + public event FrameOffsetEventHandler FrameOffsetChanged; + + private void OnInvalidatingChange(object obSender, EventArgs e) { + RecalcScrollbar(); + Invalidate(); + } + + public void OnFrameOffsetChanged(object obSender, FrameOffsetEventArgs e) { + m_ptOffset.X = e.X; + m_ptOffset.Y = e.Y; + Invalidate(); + } + + protected override void OnPaintBackground(System.Windows.Forms.PaintEventArgs e) { + if (m_stp == null || m_stp.Count == 0) + base.OnPaintBackground(e); + } + + protected override void OnPaint(System.Windows.Forms.PaintEventArgs e) { + if (m_stp == null || m_stp.Count == 0) + return; + + Frame.DrawArgs drwa = new Frame.DrawArgs(); + drwa.clrBackground = this.BackColor; + drwa.fDrawBackground = true; + drwa.fMapSideColors = Globals.SideColorMappingOn; + drwa.fShowGrid = Globals.GridOn; + drwa.cxGrid = Globals.GridWidth; + drwa.cyGrid = Globals.GridHeight; + drwa.fShowOrigin = Globals.ShowOriginPoint; + drwa.fShowSpecialPoint = Globals.ShowSpecialPoint; + drwa.nScale = Globals.StripScale; + + Pen penInactive = new Pen(Color.Black); + Pen penActive = new Pen(Color.Red); + Font fnt = new Font("Arial", 8); + Brush brBlack = new SolidBrush(Color.Black); + + int cyFrame = sb.Top - 1; + + Bitmap bmT = new Bitmap(ClientRectangle.Width, cyFrame + 1); + Graphics gT = Graphics.FromImage(bmT); + gT.Clear(drwa.clrBackground); + + int ifr = sb.Value / m_cxFrame; + int x = -(sb.Value % m_cxFrame); + + for (; x < ClientRectangle.Width && ifr < m_stp.Count; x += m_cxFrame, ifr++) { + + // Draw a frame around the Frame + + gT.DrawRectangle(penInactive, x, 0, m_cxFrame, cyFrame); + + // Set up a clip box so the scaled frame won't draw anywhere we don't want it to + + gT.SetClip(new Rectangle(x + 1, 1, m_cxFrame - 1, cyFrame - 1)); + + // Draw the scaled Frame + + Frame fr = m_stp[ifr]; + Rectangle rc = new Rectangle(x, 0, m_cxFrame, cyFrame); + fr.Draw(gT, rc, drwa, m_ptOffset); + + // Draw the frame's hold count + + if (fr.HoldCount != 0) { + string strT = fr.HoldCount.ToString(); + SizeF sizf = e.Graphics.MeasureString(strT, fnt); + gT.DrawString(strT, fnt, brBlack, x + m_cxFrame - sizf.Width - 1, cyFrame - fnt.Height); + } + + // Clear out our special clip region + + gT.ResetClip(); + } + + // Draw a special frame around the active Frame + + for (int ifrT = m_stp.ActiveFrame; ifrT < m_stp.ActiveFrame + + m_stp.ActiveFrameCount; ifrT++) { + Rectangle rcT = new Rectangle((ifrT * m_cxFrame) - sb.Value, 0, m_cxFrame, cyFrame); + gT.DrawRectangle(penActive, rcT); + rcT.Inflate(-1, -1); + gT.DrawRectangle(penActive, rcT); + } + + // Draw the insertion point, if any + + if (m_ifrInsertionPoint != -1) { + Brush br = new SolidBrush(Color.Red); + int xT = (m_ifrInsertionPoint * m_cxFrame) - sb.Value; + gT.FillRectangle(br, xT, 0, 1, cyFrame); + gT.FillRectangle(br, xT - 2, 0, 5, 1); + gT.FillRectangle(br, xT - 1, 1, 3, 1); + gT.FillRectangle(br, xT - 1, cyFrame - 2, 3, 1); + gT.FillRectangle(br, xT - 2, cyFrame - 1, 5, 1); + } + + // Copy the buffered Strip to the screen + + e.Graphics.DrawImageUnscaled(bmT, 0, 0); + + gT.Dispose(); + bmT.Dispose(); + } + + protected override void OnMouseMove(System.Windows.Forms.MouseEventArgs e) { + if (m_stp == null || m_stp.Count == 0) + return; + + if (m_fButtonDown) { + if (m_fFrameResizing) { + Cursor = Cursors.VSplit; + m_cxFrame = Math.Max(4, e.X + sb.Value); + RecalcScrollbar(); + Invalidate(); + } else if (m_fFrameRecentering) { + m_ptOffset.X = m_ptInitialOffset.X + (e.X - m_ptDown.X) / Globals.StripScale; + m_ptOffset.Y = m_ptInitialOffset.Y + (e.Y - m_ptDown.Y) / Globals.StripScale; + Invalidate(); + Update(); + + // Notify anyone who cares that we've changed the Frame offset + + if (FrameOffsetChanged != null) + FrameOffsetChanged(this, new FrameOffsetEventArgs(m_ptOffset.X, m_ptOffset.Y)); + } else { + m_fButtonDown = false; // because drag drop will capture the up + DoDragDrop(m_stp[m_stp.ActiveFrame], DragDropEffects.Move | DragDropEffects.Copy | DragDropEffects.Scroll); + } + } else { + if (e.X >= m_cxFrame - sb.Value - 1 && e.X <= m_cxFrame - sb.Value + 1) + Cursor = Cursors.VSplit; + else + Cursor = m_fSpaceDown ? Globals.HandCursor : Cursors.Default; + } + } + + protected override void OnMouseDown(System.Windows.Forms.MouseEventArgs e) { + Focus(); + + if (m_stp == null || m_stp.Count == 0) + return; + + if (e.Button == MouseButtons.Left) { + m_fButtonDown = true; + + // Is user clicking on the frame border? + + if (e.X >= m_cxFrame - sb.Value - 1 && e.X <= m_cxFrame - sb.Value + 1) { + m_fFrameResizing = true; + + // No, activate the frame they clicked + + } else { + int ifr = (e.X + sb.Value) / m_cxFrame; + if (ifr < m_stp.Count) { + if ((ModifierKeys & Keys.Shift) == 0) { + m_stp.ActiveFrame = ifr; + } else { + if (ifr >= m_stp.ActiveFrame) { + m_stp.ActiveFrameCount = ifr - + m_stp.ActiveFrame + 1; + } else { + int cfr = m_stp.ActiveFrame - ifr + 1; + m_stp.ActiveFrame = ifr; + m_stp.ActiveFrameCount = cfr; + } + } + + // If spacebar is held down then prepare to recenter the frame. + + if (m_fSpaceDown) { + m_fFrameRecentering = true; + m_ptInitialOffset = m_ptOffset; + m_ptDown = new Point(e.X, e.Y); + Cursor = Globals.GrabCursor; + } + } + } + } else if (e.Button == MouseButtons.Right) { + int ifr = (e.X + sb.Value) / m_cxFrame; + if (ifr < m_stp.Count) { + if (m_stp.ActiveFrame != ifr) + m_stp.ActiveFrame = ifr; + mnu.Show(this, new Point(e.X, e.Y)); + } + } + } + + protected override void OnMouseUp(System.Windows.Forms.MouseEventArgs e) { + if (e.Button == MouseButtons.Left) { + m_fButtonDown = false; + m_fFrameResizing = false; + m_fFrameRecentering = false; + } + } + + protected override void OnResize(EventArgs e) { + base.OnResize(e); + RecalcScrollbar(); + } + + private void StripControl_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e) { + switch (e.KeyChar) { + case '[': + { + int ifr = m_stp.ActiveFrame - 1; + if (ifr < 0) + ifr = m_stp.Count - 1; + m_stp.ActiveFrame = ifr; + e.Handled = true; + } + break; + + case ']': + { + int ifr = m_stp.ActiveFrame + 1; + if (ifr >= m_stp.Count) + ifr = 0; + m_stp.ActiveFrame = ifr; + e.Handled = true; + } + break; + } + } + + // UNDONE: move _KeyPress handler into this + // UNDONE: this doesn't work for some reason. Somebody is snarfing the arrow keys (but not page up,etc) + private void StripControl_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e) { + switch (e.KeyCode) { + case Keys.Left: + { + int ifr = m_stp.ActiveFrame - 1; + if (ifr < 0) + ifr = m_stp.Count - 1; + m_stp.ActiveFrame = ifr; + e.Handled = true; + } + break; + + case Keys.Right: + { + int ifr = m_stp.ActiveFrame + 1; + if (ifr >= m_stp.Count) + ifr = 0; + m_stp.ActiveFrame = ifr; + e.Handled = true; + } + break; + } + } + + private void OnGlobalKeyDown(object obSender, KeyEventArgs e) { + if (e.KeyCode == Keys.Space && !m_fSpaceDown) { + Cursor = Globals.HandCursor; + m_fSpaceDown = true; + } + } + + private void OnGlobalKeyUp(object obSender, KeyEventArgs e) { + if (e.KeyCode == Keys.Space) { + Cursor = Cursors.Default; + m_fSpaceDown = false; + } + } + + private void StripControl_Popup(object sender, System.EventArgs e) { + mniPaste.Enabled = Clipboard.GetDataObject().GetDataPresent(typeof(Frame)); + } + + private void StripControl_DragEnter(object sender, System.Windows.Forms.DragEventArgs e) { + if (e.Data.GetData(typeof(Frame)) == null || (e.KeyState & 8) != 0) + e.Effect = DragDropEffects.Copy; + else + e.Effect = DragDropEffects.Move; + + tmrScroll.Start(); + } + + private void StripControl_DragOver(object sender, System.Windows.Forms.DragEventArgs e) { + if (e.Data.GetData(typeof(Frame)) == null || (e.KeyState & 8) != 0) + e.Effect = DragDropEffects.Copy; + else + e.Effect = DragDropEffects.Move; + + // Find the closest between-frames border and draw an insertion cursor there + + Point pt = PointToClient(new Point(e.X, e.Y)); + SetInsertionPoint(pt.X); + } + + private void StripControl_DragDrop(object sender, System.Windows.Forms.DragEventArgs e) { + tmrScroll.Stop(); + Focus(); + + // Drop from Bitmaps window + + XBitmap[] axbm = e.Data.GetData(typeof(XBitmap[])) as XBitmap[]; + if (axbm != null) { + if (m_stp == null) + ((StripsForm)Globals.StripsForm).NewStrip(); + + int ifrFirst = m_ifrInsertionPoint; + int ifrInsert = ifrFirst; + + UndoManager.BeginGroup(); + + foreach (XBitmap xbm in axbm) { + Frame frT = new Frame(); + frT.BitmapPlacers.Add(new BitmapPlacer()); + frT.BitmapPlacers[0].XBitmap = xbm; + frT.BitmapPlacers[0].X = xbm.Width / 2; + frT.BitmapPlacers[0].Y = xbm.Height / 2; + + InsertFrame(m_stp, ifrInsert++, frT, true); + } + + UndoManager.EndGroup(); + + m_stp.ActiveFrame = ifrFirst; + } + + // Drop from Strip window + + // UNDONE: wrap the Frame in a data structure that includes Strip and ifr + // and clean this up + + Frame fr = e.Data.GetData(typeof(Frame)) as Frame; + if (fr != null) { + if (m_ifrInsertionPoint != -1) { + if ((ModifierKeys & Keys.Control) == 0) { + + // Don't try to move the frame immediately before or after itself + + if (m_ifrInsertionPoint == m_stp.ActiveFrame || + m_ifrInsertionPoint == m_stp.ActiveFrame + 1) { + m_ifrInsertionPoint = -1; + Invalidate(); + return; + } + } + + UndoManager.BeginGroup(); + + // Insert at new place + + int ifrOld = m_stp.ActiveFrame; + fr = (Frame)m_stp[ifrOld].Clone(); + InsertFrame(m_stp, m_ifrInsertionPoint, fr, true); + m_stp.ActiveFrame = m_ifrInsertionPoint; + + // If the control key is held the frame is duplicated, not moved + + if ((ModifierKeys & Keys.Control) == 0) { + // Remove from old place + + if (m_ifrInsertionPoint <= ifrOld) + ifrOld++; + DeleteFrame(m_stp, ifrOld, true); + } + + UndoManager.EndGroup(); + } + } + + m_ifrInsertionPoint = -1; + Invalidate(); + } + + private void RecalcScrollbar() { + if (m_stp == null) { + sb.Enabled = false; + } else { + int cxScroll = m_stp.Count * m_cxFrame; + if (cxScroll <= ClientRectangle.Width) { + sb.Value = 0; + sb.Enabled = false; + } else { + sb.Enabled = true; + sb.Maximum = cxScroll; + if (sb.Value > cxScroll - ClientRectangle.Width) + sb.Value = cxScroll - ClientRectangle.Width + 1; + sb.LargeChange = ClientRectangle.Width; + sb.SmallChange = m_cxFrame; + } + } + } + + private void SetInsertionPoint(int x) { + x += sb.Value; + if (x < 0) + x = 0; + else if (x > m_cxFrame * m_stp.Count) + x = m_cxFrame * m_stp.Count; + int ifr = x / m_cxFrame; + if (x % m_cxFrame >= m_cxFrame / 2) + ifr++; + m_ifrInsertionPoint = ifr; + Invalidate(); + } + + private void sb_ValueChanged(object sender, System.EventArgs e) { + Invalidate(); + } + + private void mniInsert_Click(object sender, System.EventArgs e) { + if (m_stp == null) + return; + InsertFrame(m_stp, m_stp.ActiveFrame, new Frame(), true); + m_stp.ActiveFrame = m_stp.ActiveFrame; + } + + private void mniDelete_Click(object sender, System.EventArgs e) { + DeleteFrame(m_stp, m_stp.ActiveFrame, true); + } + + private void mniCopy_Click(object sender, System.EventArgs e) { + Frame fr = (Frame)m_stp[m_stp.ActiveFrame].Clone(); + Clipboard.SetDataObject(fr); + } + + private void mniCut_Click(object sender, System.EventArgs e) { + mniCopy_Click(sender, e); + mniDelete_Click(sender, e); + } + + private void mniPaste_Click(object sender, System.EventArgs e) { + IDataObject dta = Clipboard.GetDataObject(); + if (dta.GetDataPresent(typeof(Frame))) { + Frame fr = (Frame)dta.GetData(typeof(Frame)); + InsertFrame(m_stp, m_stp.ActiveFrame, fr, true); + } + } + + private void UndoDelete(object[] aobArgs) { + ((Strip)aobArgs[0]).Insert((int)aobArgs[1], (Frame)aobArgs[2]); + ((Strip)aobArgs[0]).ActiveFrame = (int)aobArgs[3]; + Invalidate(); + RecalcScrollbar(); + } + + private void UndoInsert(object[] aobArgs) { + DeleteFrame((Strip)aobArgs[0], (int)aobArgs[1], false); + ((Strip)aobArgs[0]).ActiveFrame = (int)aobArgs[2]; + Invalidate(); + RecalcScrollbar(); + } + + private void InsertFrame(Strip stp, int ifr, Frame fr, bool fUndoable) { + if (fUndoable) + UndoManager.AddUndo(new UndoDelegate(UndoInsert), new Object [] { stp, ifr, stp.ActiveFrame }); + stp.Insert(ifr, fr); + Globals.ActiveDocument.Dirty = true; + Invalidate(); + RecalcScrollbar(); + } + + private void DeleteFrame(Strip stp, int ifr, bool fUndoable) { + if (fUndoable) + UndoManager.AddUndo(new UndoDelegate(UndoDelete), new object[] { stp, ifr, stp[ifr], stp.ActiveFrame}); + stp.RemoveAt(ifr); + Globals.ActiveDocument.Dirty = true; + if (stp.ActiveFrame >= stp.Count) + stp.ActiveFrame = stp.Count - 1; + RecalcScrollbar(); + } + + private void tmrScroll_Tick(object sender, System.EventArgs e) { + if (m_ifrInsertionPoint == -1) + return; + + Point ptMouse = PointToClient(Control.MousePosition); + int v; + if (ptMouse.X < 0) { + v = Math.Max(0, sb.Value - m_cxFrame); + } else if (ptMouse.X >= ClientRectangle.Width) { + v = Math.Min((m_cxFrame * m_stp.Count) - ClientRectangle.Width + 1, sb.Value + m_cxFrame); + } else { + return; + } + + if (v < sb.Minimum) { + v = sb.Minimum; + } + if (v > sb.Maximum) { + v = sb.Maximum; + } + sb.Value = v; + SetInsertionPoint(ptMouse.X); + } + } + + public delegate void FrameOffsetEventHandler(object sender, FrameOffsetEventArgs e); + + public class FrameOffsetEventArgs : EventArgs { + public int X; + public int Y; + + public FrameOffsetEventArgs(int x, int y) { + X = x; + Y = y; + } + } +} diff --git a/AniMax/StripControl.resources b/AniMax/StripControl.resources new file mode 100644 index 0000000..5b74917 Binary files /dev/null and b/AniMax/StripControl.resources differ diff --git a/AniMax/StripControl.resx b/AniMax/StripControl.resx new file mode 100644 index 0000000..ade835c --- /dev/null +++ b/AniMax/StripControl.resx @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + 90, 17 + + + StripControl + + \ No newline at end of file diff --git a/AniMax/StripForm.cs b/AniMax/StripForm.cs new file mode 100644 index 0000000..86b8cfa --- /dev/null +++ b/AniMax/StripForm.cs @@ -0,0 +1,267 @@ +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; +using Crownwood.Magic.Docking; + +namespace SpiffCode +{ + /// + /// Summary description for StripForm. + /// + public class StripForm : System.Windows.Forms.Form + { + private Content m_tnt; + private AnimDoc m_doc; + private SpiffCode.StripControl stpc; + private System.Windows.Forms.Panel panel1; + private System.Windows.Forms.CheckBox ckbToggleGrid; + private System.Windows.Forms.CheckBox ckbToggleSideColor; + private System.Windows.Forms.CheckBox ckbToggleOriginPoint; + private System.Windows.Forms.CheckBox ckbToggleSpecialPoint; + private System.Windows.Forms.ToolTip toolTip1; + private System.Windows.Forms.Button button1; + private System.ComponentModel.IContainer components; + + public StripForm(AnimDoc doc) + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + // My constructor code + + Globals.ActiveDocumentChanged += new EventHandler(OnActiveDocumentChanged); + Globals.ActiveStripChanged += new EventHandler(OnActiveStripChanged); + Globals.TileSizeChanged += new EventHandler(OnTileSizeChanged); + Globals.StripControl = stpc; + m_doc = doc; + ckbToggleGrid.Checked = Globals.GridOn; + ckbToggleSideColor.Checked = Globals.SideColorMappingOn; + ckbToggleOriginPoint.Checked = Globals.ShowOriginPoint; + ckbToggleSpecialPoint.Checked = Globals.ShowSpecialPoint; + RefreshView(); + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(StripForm)); + this.stpc = new SpiffCode.StripControl(); + this.panel1 = new System.Windows.Forms.Panel(); + this.ckbToggleSpecialPoint = new System.Windows.Forms.CheckBox(); + this.ckbToggleOriginPoint = new System.Windows.Forms.CheckBox(); + this.ckbToggleSideColor = new System.Windows.Forms.CheckBox(); + this.ckbToggleGrid = new System.Windows.Forms.CheckBox(); + this.toolTip1 = new System.Windows.Forms.ToolTip(this.components); + this.button1 = new System.Windows.Forms.Button(); + this.panel1.SuspendLayout(); + this.SuspendLayout(); + // + // stpc + // + this.stpc.AllowDrop = true; + this.stpc.BackColor = System.Drawing.SystemColors.Control; + this.stpc.Dock = System.Windows.Forms.DockStyle.Fill; + this.stpc.DockPadding.Bottom = 1; + this.stpc.DockPadding.Right = 1; + this.stpc.Location = new System.Drawing.Point(0, 26); + this.stpc.Name = "stpc"; + this.stpc.Size = new System.Drawing.Size(648, 100); + this.stpc.Strip = null; + this.stpc.TabIndex = 0; + // + // panel1 + // + this.panel1.Controls.Add(this.button1); + this.panel1.Controls.Add(this.ckbToggleSpecialPoint); + this.panel1.Controls.Add(this.ckbToggleOriginPoint); + this.panel1.Controls.Add(this.ckbToggleSideColor); + this.panel1.Controls.Add(this.ckbToggleGrid); + this.panel1.Dock = System.Windows.Forms.DockStyle.Top; + this.panel1.Location = new System.Drawing.Point(0, 0); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(648, 26); + this.panel1.TabIndex = 1; + // + // ckbToggleSpecialPoint + // + this.ckbToggleSpecialPoint.Appearance = System.Windows.Forms.Appearance.Button; + this.ckbToggleSpecialPoint.Image = ((System.Drawing.Image)(resources.GetObject("ckbToggleSpecialPoint.Image"))); + this.ckbToggleSpecialPoint.Location = new System.Drawing.Point(52, 0); + this.ckbToggleSpecialPoint.Name = "ckbToggleSpecialPoint"; + this.ckbToggleSpecialPoint.Size = new System.Drawing.Size(24, 24); + this.ckbToggleSpecialPoint.TabIndex = 9; + this.toolTip1.SetToolTip(this.ckbToggleSpecialPoint, "Toggle Special Point on/off"); + this.ckbToggleSpecialPoint.CheckedChanged += new System.EventHandler(this.ckbToggleSpecialPoint_CheckedChanged); + // + // ckbToggleOriginPoint + // + this.ckbToggleOriginPoint.Appearance = System.Windows.Forms.Appearance.Button; + this.ckbToggleOriginPoint.Image = ((System.Drawing.Image)(resources.GetObject("ckbToggleOriginPoint.Image"))); + this.ckbToggleOriginPoint.Location = new System.Drawing.Point(26, 0); + this.ckbToggleOriginPoint.Name = "ckbToggleOriginPoint"; + this.ckbToggleOriginPoint.Size = new System.Drawing.Size(24, 24); + this.ckbToggleOriginPoint.TabIndex = 4; + this.toolTip1.SetToolTip(this.ckbToggleOriginPoint, "Toggle Origin Point on/off"); + this.ckbToggleOriginPoint.CheckedChanged += new System.EventHandler(this.ckbToggleOriginPoint_CheckedChanged); + // + // ckbToggleSideColor + // + this.ckbToggleSideColor.Appearance = System.Windows.Forms.Appearance.Button; + this.ckbToggleSideColor.Image = ((System.Drawing.Image)(resources.GetObject("ckbToggleSideColor.Image"))); + this.ckbToggleSideColor.Location = new System.Drawing.Point(78, 0); + this.ckbToggleSideColor.Name = "ckbToggleSideColor"; + this.ckbToggleSideColor.Size = new System.Drawing.Size(24, 24); + this.ckbToggleSideColor.TabIndex = 1; + this.toolTip1.SetToolTip(this.ckbToggleSideColor, "Toggle side color preview on/off"); + this.ckbToggleSideColor.CheckedChanged += new System.EventHandler(this.ckbToggleSideColor_CheckedChanged); + // + // ckbToggleGrid + // + this.ckbToggleGrid.Appearance = System.Windows.Forms.Appearance.Button; + this.ckbToggleGrid.Image = ((System.Drawing.Image)(resources.GetObject("ckbToggleGrid.Image"))); + this.ckbToggleGrid.Location = new System.Drawing.Point(0, 0); + this.ckbToggleGrid.Name = "ckbToggleGrid"; + this.ckbToggleGrid.Size = new System.Drawing.Size(24, 24); + this.ckbToggleGrid.TabIndex = 0; + this.toolTip1.SetToolTip(this.ckbToggleGrid, "Toggle Grid on/off"); + this.ckbToggleGrid.CheckedChanged += new System.EventHandler(this.ckbToggleGrid_CheckedChanged); + // + // button1 + // + this.button1.Location = new System.Drawing.Point(104, 0); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(27, 24); + this.button1.TabIndex = 11; + this.button1.Text = "TS"; + this.button1.Click += new System.EventHandler(this.button1_Click); + // + // StripForm + // + this.AutoScale = false; + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.ClientSize = new System.Drawing.Size(648, 126); + this.Controls.Add(this.stpc); + this.Controls.Add(this.panel1); + this.Name = "StripForm"; + this.Text = "Frames"; + this.panel1.ResumeLayout(false); + this.ResumeLayout(false); + + } + #endregion + + public Content Content { + set { + m_tnt = value; + } + } + + private void OnActiveDocumentChanged(object obSender, EventArgs e) { + m_doc = Globals.ActiveDocument; + RefreshView(); + } + + private void OnActiveStripChanged(object obSender, EventArgs e) { + RefreshView(); + } + + private void RefreshView() { + stpc.Strip = Globals.ActiveStrip; + string strT = Globals.ActiveStrip == null ? "(none)" : Globals.ActiveStrip.Name; + Text = "Frames - " + strT; + if (m_tnt != null) + m_tnt.Title = Text; + } + + private void ckbToggleGrid_CheckedChanged(object sender, System.EventArgs e) { + Globals.GridOn = ckbToggleGrid.Checked; + } + + private void ckbToggleSideColor_CheckedChanged(object sender, System.EventArgs e) { + Globals.SideColorMappingOn = ckbToggleSideColor.Checked; + } + + private void ckbToggleOriginPoint_CheckedChanged(object sender, System.EventArgs e) { + Globals.ShowOriginPoint = ckbToggleOriginPoint.Checked; + } + + private void ckbToggleSpecialPoint_CheckedChanged(object sender, System.EventArgs e) { + Globals.ShowSpecialPoint = ckbToggleSpecialPoint.Checked; + } + +#if false + private void ckbToggleHires_CheckedChanged(object sender, System.EventArgs e) { + bool fHires = ckbToggleHires.Checked; + if (Globals.Hires != fHires) + Globals.Hires = fHires; + if (Globals.ActiveDocument.Hires != fHires) + Globals.ActiveDocument.Hires = fHires; + } +#endif + + private void OnTileSizeChanged(object obSender, EventArgs e) { +#if false + ckbToggleHires.Checked = Globals.Hires; +#endif + } + + private void btnProperties_Click(object sender, System.EventArgs e) { + ShowStripProperties(stpc.Strip); + } + + public void ShowStripProperties(Strip stp) { + Form frm = new StripProperties(stp); + if (frm.ShowDialog(this) == DialogResult.Cancel) + return; + + // UNDONE: animation interval must be updated if Strip.DefHoldCount changes +// if (stp == stpc.Strip) +// tmrAnim.Interval = 80 + (80 * stpc.Strip.DefHoldCount); + } + + private void button1_Click(object sender, System.EventArgs e) { + int nTSNew = 32; + switch (Globals.TileSize) { + case 16: + nTSNew = 24; + break; + case 24: + nTSNew = 32; + break; + case 32: + nTSNew = 16; + break; + } + if (Globals.TileSize != nTSNew) { + Globals.TileSize = nTSNew; + } + if (Globals.ActiveDocument.TileSize != nTSNew) { + Globals.ActiveDocument.TileSize = nTSNew; + } + } + } +} diff --git a/AniMax/StripForm.resources b/AniMax/StripForm.resources new file mode 100644 index 0000000..f839ba5 Binary files /dev/null and b/AniMax/StripForm.resources differ diff --git a/AniMax/StripForm.resx b/AniMax/StripForm.resx new file mode 100644 index 0000000..1067193 --- /dev/null +++ b/AniMax/StripForm.resx @@ -0,0 +1,243 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + False + + + Private + + + Private + + + False + + + True + + + True + + + Private + + + Private + + + 8, 8 + + + False + + + Private + + + Private + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAlwSFlzAAALEwAA + CxMBAJqcGAAAADNJREFUOE9j/P//PwNFAGQAJZgizWDXU2L7IDeAARi2MIjPmzjDYOANIDZ2hnM0Dp0w + AAAXu8dW6QvQNwAAAABJRU5ErkJggg== + + + + False + + + Private + + + Private + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAlwSFlzAAALEwAA + CxMBAJqcGAAAADRJREFUOE9j/P//PwNFAGQAJZgizWDXU2L7IDfga4PCfxjG502cYTDwBhAbO8M5GodO + GAAAjp/MnENk/sEAAAAASUVORK5CYII= + + + + False + + + Private + + + Private + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAlwSFlzAAALEwAA + CxMBAJqcGAAAAFdJREFUOE9j/P//PwNFAGQAJZgizWDXU2I7xPslL/6jY1yGvlBg+I+O8RoANB5oFiKM + SDYAGkNwQ0gyAGQ7MgYZRpIBMKcje4NkA6gSBgQDkeJ0MGoAAwAGCYiV2rv9ugAAAABJRU5ErkJggg== + + + + False + + + Private + + + Private + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAlwSFlzAAALEwAA + CxMBAJqcGAAAAD1JREFUOE9j+P//PwMML1++CshE8NHZ2OThmkGKB8YAkK3kYpCrB4EXhkEsDLwXyE0D + sFQ73NIBvqyMSw4AfNj7z9qm0egAAAAASUVORK5CYII= + + + + Private + + + 138, 23 + + + Private + + + False + + + Private + + + Private + + + False + + + (Default) + + + False + + + StripForm + + + False + + + 8, 8 + + + True + + + 80 + + + True + + + Private + + \ No newline at end of file diff --git a/AniMax/StripProperties.cs b/AniMax/StripProperties.cs new file mode 100644 index 0000000..8e730db --- /dev/null +++ b/AniMax/StripProperties.cs @@ -0,0 +1,120 @@ +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; + +namespace SpiffCode +{ + /// + /// Summary description for StripProperties. + /// + public class StripProperties : System.Windows.Forms.Form + { + private System.Windows.Forms.PropertyGrid prgStrip; + private System.Windows.Forms.Panel panel1; + private System.Windows.Forms.Button btnCancel; + private System.Windows.Forms.Button btnOK; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + public StripProperties(Strip stp) + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + prgStrip.SelectedObject = stp; + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.prgStrip = new System.Windows.Forms.PropertyGrid(); + this.panel1 = new System.Windows.Forms.Panel(); + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.panel1.SuspendLayout(); + this.SuspendLayout(); + // + // prgStrip + // + this.prgStrip.CommandsVisibleIfAvailable = true; + this.prgStrip.Dock = System.Windows.Forms.DockStyle.Fill; + this.prgStrip.HelpVisible = false; + this.prgStrip.LargeButtons = false; + this.prgStrip.LineColor = System.Drawing.SystemColors.ScrollBar; + this.prgStrip.Name = "prgStrip"; + this.prgStrip.Size = new System.Drawing.Size(292, 294); + this.prgStrip.TabIndex = 0; + this.prgStrip.Text = "Strip Properties"; + this.prgStrip.ViewBackColor = System.Drawing.SystemColors.Window; + this.prgStrip.ViewForeColor = System.Drawing.SystemColors.WindowText; + // + // panel1 + // + this.panel1.Controls.AddRange(new System.Windows.Forms.Control[] { + this.btnCancel, + this.btnOK}); + this.panel1.Dock = System.Windows.Forms.DockStyle.Bottom; + this.panel1.Location = new System.Drawing.Point(0, 294); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(292, 40); + this.panel1.TabIndex = 1; + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(169, 8); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.TabIndex = 1; + this.btnCancel.Text = "Cancel"; + // + // btnOK + // + this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK; + this.btnOK.Location = new System.Drawing.Point(49, 8); + this.btnOK.Name = "btnOK"; + this.btnOK.TabIndex = 0; + this.btnOK.Text = "OK"; + // + // StripProperties + // + this.AcceptButton = this.btnOK; + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(292, 334); + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.prgStrip, + this.panel1}); + this.Name = "StripProperties"; + this.Text = "Strip Properties"; + this.panel1.ResumeLayout(false); + this.ResumeLayout(false); + + } + #endregion + } +} diff --git a/AniMax/StripProperties.resources b/AniMax/StripProperties.resources new file mode 100644 index 0000000..caa8578 Binary files /dev/null and b/AniMax/StripProperties.resources differ diff --git a/AniMax/StripProperties.resx b/AniMax/StripProperties.resx new file mode 100644 index 0000000..773414f --- /dev/null +++ b/AniMax/StripProperties.resx @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + StripProperties + + \ No newline at end of file diff --git a/AniMax/StripSet.cs b/AniMax/StripSet.cs new file mode 100644 index 0000000..608c760 --- /dev/null +++ b/AniMax/StripSet.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections; +using System.Runtime.Serialization; + +namespace SpiffCode +{ + /// + /// Summary description for StripSet. + /// + [Serializable] + public class StripSet : CollectionBase, ISerializable + { + public StripSet() { + } + + public Strip this[int i] { + get { + return (Strip)InnerList[i]; + } + set { + InnerList[i] = value; + } + } + + public Strip this[string strStripName] { + get { + foreach (Strip stp in InnerList) { + if (stp.Name == strStripName) + return stp; + } + return null; + } + } + + // For whatever reason the designers of CollectionBase decided to implement + // IList.Add as 'explicit' which means it is effectively hidden from users + // of derived classes. So we must add our own Add method which does have the + // benefit of being type-safe (possibly why they hid IList.Add in the first place) + + public int Add(Strip stp) { + return ((IList)this).Add(stp); + } + + public int IndexOf(Strip stp) { + return ((IList)this).IndexOf(stp); + } + + public void Sort() { + InnerList.Sort(); + } + + // ISerializable interface implementation + + private StripSet(SerializationInfo seri, StreamingContext stmc) { + for (int i = 0; i < seri.MemberCount; i++) { + Strip stp = (Strip)seri.GetValue(i.ToString(), typeof(Strip)); + Add(stp); + } + } + + void ISerializable.GetObjectData(SerializationInfo seri, StreamingContext stmc) { + int i = 0; + foreach (Strip stp in InnerList) { + seri.AddValue(i.ToString(), stp); + i++; + } + } + } +} diff --git a/AniMax/StripsForm.cs b/AniMax/StripsForm.cs new file mode 100644 index 0000000..0cdb1b4 --- /dev/null +++ b/AniMax/StripsForm.cs @@ -0,0 +1,490 @@ +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; + +namespace SpiffCode +{ + /// + /// Summary description for StripsForm. + /// + public class StripsForm : System.Windows.Forms.Form + { + private AnimDoc m_doc; + private StripSet m_stps; + private Strip m_stpActive; + private System.Windows.Forms.ListView lstv; + private System.Windows.Forms.ColumnHeader columnHeader1; + private System.Windows.Forms.ColumnHeader columnHeader2; + private System.Windows.Forms.MenuItem mniView; + private System.Windows.Forms.MenuItem mniViewDetails; + private System.Windows.Forms.MenuItem mniViewList; + private System.Windows.Forms.MenuItem mniViewThumbnails; + private System.Windows.Forms.ContextMenu mnuListView; + private System.Windows.Forms.MenuItem mniNewStrip; + private System.Windows.Forms.MenuItem menuItem2; + private System.Windows.Forms.MenuItem mniDelete; + private System.Windows.Forms.MenuItem mniRename; + private System.Windows.Forms.MenuItem menuItem1; + private System.Windows.Forms.MenuItem menuItem3; + private System.Windows.Forms.MenuItem mniProperties; + private System.Windows.Forms.MenuItem mniStripsSort; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + public StripsForm(AnimDoc doc) + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + // My constructor code + + Globals.ActiveDocumentChanged += new EventHandler(OnActiveDocumentChanged); + Globals.ActiveStripChanged += new EventHandler(OnActiveStripChanged); + m_doc = doc; + m_stps = m_doc.StripSet; + RefreshView(); + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.mniViewList = new System.Windows.Forms.MenuItem(); + this.columnHeader2 = new System.Windows.Forms.ColumnHeader(); + this.mniView = new System.Windows.Forms.MenuItem(); + this.mniViewThumbnails = new System.Windows.Forms.MenuItem(); + this.mniViewDetails = new System.Windows.Forms.MenuItem(); + this.columnHeader1 = new System.Windows.Forms.ColumnHeader(); + this.mniRename = new System.Windows.Forms.MenuItem(); + this.mnuListView = new System.Windows.Forms.ContextMenu(); + this.mniNewStrip = new System.Windows.Forms.MenuItem(); + this.menuItem2 = new System.Windows.Forms.MenuItem(); + this.mniDelete = new System.Windows.Forms.MenuItem(); + this.menuItem1 = new System.Windows.Forms.MenuItem(); + this.menuItem3 = new System.Windows.Forms.MenuItem(); + this.mniProperties = new System.Windows.Forms.MenuItem(); + this.lstv = new System.Windows.Forms.ListView(); + this.mniStripsSort = new System.Windows.Forms.MenuItem(); + this.SuspendLayout(); + // + // mniViewList + // + this.mniViewList.Index = 0; + this.mniViewList.Text = "List"; + this.mniViewList.Click += new System.EventHandler(this.mniViewList_Click); + // + // columnHeader2 + // + this.columnHeader2.Text = "# frames"; + // + // mniView + // + this.mniView.Index = 5; + this.mniView.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.mniViewList, + this.mniViewThumbnails, + this.mniViewDetails}); + this.mniView.Text = "View"; + this.mniView.Popup += new System.EventHandler(this.mniView_Popup); + // + // mniViewThumbnails + // + this.mniViewThumbnails.Index = 1; + this.mniViewThumbnails.Text = "Thumbnails"; + this.mniViewThumbnails.Click += new System.EventHandler(this.mniViewThumbnails_Click); + // + // mniViewDetails + // + this.mniViewDetails.Index = 2; + this.mniViewDetails.Text = "Details"; + this.mniViewDetails.Click += new System.EventHandler(this.mniViewDetails_Click); + // + // columnHeader1 + // + this.columnHeader1.Text = "Name"; + this.columnHeader1.Width = 90; + // + // mniRename + // + this.mniRename.Index = 3; + this.mniRename.Text = "Rename"; + this.mniRename.Click += new System.EventHandler(this.mniRename_Click); + // + // mnuListView + // + this.mnuListView.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.mniNewStrip, + this.menuItem2, + this.mniDelete, + this.mniRename, + this.menuItem1, + this.mniView, + this.mniStripsSort, + this.menuItem3, + this.mniProperties}); + this.mnuListView.Popup += new System.EventHandler(this.mnuListView_Popup); + // + // mniNewStrip + // + this.mniNewStrip.Index = 0; + this.mniNewStrip.Text = "New Strip"; + this.mniNewStrip.Click += new System.EventHandler(this.mniNewStrip_Click); + // + // menuItem2 + // + this.menuItem2.Index = 1; + this.menuItem2.Text = "-"; + // + // mniDelete + // + this.mniDelete.Index = 2; + this.mniDelete.Shortcut = System.Windows.Forms.Shortcut.Del; + this.mniDelete.Text = "Delete"; + this.mniDelete.Click += new System.EventHandler(this.mniDelete_Click); + // + // menuItem1 + // + this.menuItem1.Index = 4; + this.menuItem1.Text = "-"; + // + // menuItem3 + // + this.menuItem3.Index = 7; + this.menuItem3.Text = "-"; + // + // mniProperties + // + this.mniProperties.Index = 8; + this.mniProperties.Text = "&Properties"; + this.mniProperties.Click += new System.EventHandler(this.mniProperties_Click); + // + // lstv + // + this.lstv.AllowColumnReorder = true; + this.lstv.AllowDrop = true; + this.lstv.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.columnHeader1, + this.columnHeader2}); + this.lstv.ContextMenu = this.mnuListView; + this.lstv.Dock = System.Windows.Forms.DockStyle.Fill; + this.lstv.FullRowSelect = true; + this.lstv.HideSelection = false; + this.lstv.LabelEdit = true; + this.lstv.MultiSelect = false; + this.lstv.Name = "lstv"; + this.lstv.Size = new System.Drawing.Size(320, 309); + this.lstv.TabIndex = 0; + this.lstv.View = System.Windows.Forms.View.Details; + this.lstv.ItemActivate += new System.EventHandler(this.lstv_ItemActivate); + this.lstv.DoubleClick += new System.EventHandler(this.lstv_DoubleClick); + this.lstv.DragDrop += new System.Windows.Forms.DragEventHandler(this.lstv_DragDrop); + this.lstv.AfterLabelEdit += new System.Windows.Forms.LabelEditEventHandler(this.lstv_AfterLabelEdit); + this.lstv.DragEnter += new System.Windows.Forms.DragEventHandler(this.lstv_DragEnter); + this.lstv.ItemDrag += new System.Windows.Forms.ItemDragEventHandler(this.lstv_ItemDrag); + this.lstv.BeforeLabelEdit += new System.Windows.Forms.LabelEditEventHandler(this.lstv_BeforeLabelEdit); + this.lstv.SelectedIndexChanged += new System.EventHandler(this.lstv_SelectedIndexChanged); + // + // mniStripsSort + // + this.mniStripsSort.Index = 6; + this.mniStripsSort.Text = "&Sort"; + this.mniStripsSort.Click += new System.EventHandler(this.mniStripsSort_Click); + // + // StripsForm + // + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.ClientSize = new System.Drawing.Size(320, 309); + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.lstv}); + this.Name = "StripsForm"; + this.StartPosition = System.Windows.Forms.FormStartPosition.Manual; + this.Text = "Strips"; + this.ResumeLayout(false); + + } + #endregion + + private void OnActiveDocumentChanged(object obSender, EventArgs e) { + m_doc = Globals.ActiveDocument; + m_stps = m_doc.StripSet; + RefreshView(); + } + + private void OnActiveStripChanged(object obSender, EventArgs e) { + if (m_stpActive == Globals.ActiveStrip) + return; + + m_stpActive = Globals.ActiveStrip; + foreach (ListViewItem lvi in lstv.Items) { + if (lvi.Tag == Globals.ActiveStrip) { + lvi.Selected = true; + break; + } + } + } + + private void mniView_Popup(object sender, System.EventArgs e) { + mniViewList.Checked = false; + mniViewThumbnails.Checked = false; + mniViewDetails.Checked = false; + + switch (lstv.View) { + case View.List: + mniViewList.Checked = true; + break; + + case View.LargeIcon: + mniViewThumbnails.Checked = true; + break; + + case View.Details: + mniViewDetails.Checked = true; + break; + } + } + + private void mniViewDetails_Click(object sender, System.EventArgs e) { + lstv.View = View.Details; + } + + private void mniViewThumbnails_Click(object sender, System.EventArgs e) { + lstv.View = View.LargeIcon; + } + + private void mniViewList_Click(object sender, System.EventArgs e) { + lstv.View = View.List; + } + + private void RefreshView() { + lstv.Items.Clear(); + if (m_stps == null) + return; + + // Prep LargeImageList + + if (lstv.LargeImageList != null) + lstv.LargeImageList.Dispose(); + lstv.LargeImageList = new ImageList(); + lstv.LargeImageList.ColorDepth = ColorDepth.Depth32Bit; + lstv.LargeImageList.ImageSize = new Size(64, 64); + + // Prep SmallImageList + + if (lstv.SmallImageList != null) + lstv.SmallImageList.Dispose(); + lstv.SmallImageList = new ImageList(); + lstv.SmallImageList.ColorDepth = ColorDepth.Depth32Bit; + lstv.SmallImageList.ImageSize = new Size(16, 16); + + int i = 0; + foreach (Strip stp in m_stps) { + ListViewItem lvi = lstv.Items.Add(stp.Name); + lvi.Tag = stp; + + if (stp == Globals.ActiveStrip) + lvi.Selected = true; + + lvi.SubItems.Add(stp.Count.ToString()); + + // UNDONE: cook up better thumbnails + // UNDONE: have a 'blank' thumbnail for empty Strips + + if (stp.Count > 0) { + if (stp[0].BitmapPlacers.Count > 0) { + XBitmap xbm = stp[0].BitmapPlacers[0].XBitmap; + Bitmap bmThumb = xbm.MakeThumbnail(64, 64, false); + lstv.LargeImageList.Images.Add(bmThumb); + bmThumb = xbm.MakeThumbnail(16, 16, false); + lstv.SmallImageList.Images.Add(bmThumb); + lvi.ImageIndex = i++; + } + } + } + } + + private void mniNewStrip_Click(object sender, System.EventArgs e) { + NewStrip(); + } + + public void NewStrip() { + Strip stp = new Strip("untitled " + (m_stps.Count + 1).ToString()); + // UNDONE: undo + m_stps.Add(stp); + Globals.ActiveStrip = stp; + m_doc.Dirty = true; + RefreshView(); + } + + private void lstv_BeforeLabelEdit(object sender, System.Windows.Forms.LabelEditEventArgs e) { + // We do this so the user can press the del key while in label editing mode + // without it being intercepted by the context menu and used to delete the + // entire Strip + + lstv.ContextMenu = null; + } + + private void lstv_AfterLabelEdit(object sender, System.Windows.Forms.LabelEditEventArgs e) { + + // Restore the context menu so the command keys, etc will work again + + lstv.ContextMenu = mnuListView; + + Strip stp = (Strip)lstv.Items[e.Item].Tag; + + // No change or an invalid change + + if (e.Label == null || e.Label == "") { + e.CancelEdit = true; + return; + } + + stp.Name = e.Label; + m_doc.Dirty = true; + } + + private void mnuListView_Popup(object sender, System.EventArgs e) { + bool fAnySelected = lstv.SelectedItems.Count != 0; + mniDelete.Enabled = fAnySelected; + mniRename.Enabled = fAnySelected; + mniProperties.Enabled = fAnySelected; + } + + private void mniDelete_Click(object sender, System.EventArgs e) { + Strip stp = (Strip)lstv.SelectedItems[0].Tag; + int i = m_stps.IndexOf(stp); + // UNDONE: undo + m_stps.RemoveAt(i); + lstv.Items.Remove(lstv.SelectedItems[0]); + m_doc.Dirty = true; + + // Select a new Strip to be active + + i = Math.Min(i, m_stps.Count - 1); + if (i >= 0) + Globals.ActiveStrip = m_stps[i]; + else + Globals.ActiveStrip = null; + } + + private void mniRename_Click(object sender, System.EventArgs e) { + MessageBox.Show(this, "Click on the name of the strip to select it then edit it in place.", + "AniMax"); + } + + private void lstv_ItemActivate(object sender, System.EventArgs e) { +// m_doc.ActiveStrip = (Strip)lstv.SelectedItems[0].Tag; + } + + private void lstv_DragEnter(object sender, System.Windows.Forms.DragEventArgs e) { + if (e.Data.GetDataPresent(DataFormats.FileDrop)) + e.Effect = DragDropEffects.Copy; + } + + private void lstv_DragDrop(object sender, System.Windows.Forms.DragEventArgs e) { + string[] astrFiles = e.Data.GetData(DataFormats.FileDrop) as string[]; + if (astrFiles != null) { + if (astrFiles.Length == 1) + ((MainForm)Globals.MainForm).CloseAndOpenDocument(astrFiles[0]); + else + MessageBox.Show(this, "Please, only drop one animation file.", "Too many files dropped", MessageBoxButtons.OK, MessageBoxIcon.Stop); + } +#if false + XBitmap[] axbm = e.Data.GetData(typeof(XBitmap[])) as XBitmap[]; + if (axbm == null) + return; + + // Use the standard file naming convention to break the bitmaps down into + // animations (e.g., *_fire_*_*), a single track (e.g., track 1), and a set + // of TSprite keys (e.g., *_*_d_n) + + foreach (XBitmap xbm in axbm) { + string strFile = xbm.FileName; + string[] astr = strFile.Substring(strFile.LastIndexOf('\\') + 1).Split('_', '.'); + if (astr.Length != 5) { + MessageBox.Show(this, String.Format("Warning: file {0} does not match the requisite naming pattern", strFile), + "AniMax"); + continue; + } + + // Find the appropriate animation + + Strip stp = null; + Frame fr = null; + string strName = astr[1] + " " + astr[2]; + foreach (Strip stpT in m_stps) { + if (stpT.Name == strName) { + stp = stpT; + fr = stp[0]; + break; + } + } + + // If one isn't found, create a new stpmation and empty track + + if (stp == null) { + stp = new Strip(strName); + m_stps.Add(stp); + tspr = new TSprite("track 1"); + stp.Add(tspr); + } + + Sprite spr = new Sprite(xbm, 0, 0); + Time t = 0; + if (tspr.End != Time.tUndefined) + t = tspr.End + m_doc.FrameRate; + tspr.SetValue(t, spr); + } + + m_doc.Dirty = true; + RefreshView(); +#endif + } + + private void lstv_SelectedIndexChanged(object sender, System.EventArgs e) { + if (lstv.SelectedItems.Count == 0) + return; + Globals.ActiveStrip = (Strip)lstv.SelectedItems[0].Tag; + } + + private void lstv_DoubleClick(object sender, System.EventArgs e) { + ((StripForm)Globals.StripForm).ShowStripProperties((Strip)lstv.SelectedItems[0].Tag); + } + + private void mniProperties_Click(object sender, System.EventArgs e) { + ((StripForm)Globals.StripForm).ShowStripProperties((Strip)lstv.SelectedItems[0].Tag); + } + + private void lstv_ItemDrag(object sender, System.Windows.Forms.ItemDragEventArgs e) { + DoDragDrop(lstv.SelectedItems[0].Tag, DragDropEffects.All); + } + + private void mniStripsSort_Click(object sender, System.EventArgs e) { + m_stps.Sort(); + RefreshView(); + } + } +} diff --git a/AniMax/StripsForm.resources b/AniMax/StripsForm.resources new file mode 100644 index 0000000..b4817c5 Binary files /dev/null and b/AniMax/StripsForm.resources differ diff --git a/AniMax/StripsForm.resx b/AniMax/StripsForm.resx new file mode 100644 index 0000000..2b49ad2 --- /dev/null +++ b/AniMax/StripsForm.resx @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + StripsForm + + \ No newline at end of file diff --git a/AniMax/UndoManager.cs b/AniMax/UndoManager.cs new file mode 100644 index 0000000..c13195c --- /dev/null +++ b/AniMax/UndoManager.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections; +using System.Diagnostics; + +namespace SpiffCode { + public delegate void UndoDelegate(object[] aobArgs); + + public class UndoManager { + private static Stack s_stk = new Stack(); + private static int s_cElements; + private static bool s_fGrouping = false; + + public static void AddUndo(UndoDelegate dgtUndo, object[] aobArgs) { + if (s_fGrouping) + s_cElements++; + + s_stk.Push(aobArgs); + s_stk.Push(dgtUndo); + } + + public static void Flush() { + s_stk.Clear(); + } + + public static void Undo() { + if (s_stk.Count == 0) + return; + + UndoDelegate dgtUndo; + object[] aobArgs; + + object ob = s_stk.Pop(); + if (ob is int) { + int cElements = (int)ob; + for (int i = 0; i < cElements; i++) { + dgtUndo = (UndoDelegate)s_stk.Pop(); + aobArgs = (object[])s_stk.Pop(); + dgtUndo(aobArgs); + } + return; + } + + dgtUndo = (UndoDelegate)ob; + aobArgs = (object[])s_stk.Pop(); + dgtUndo(aobArgs); + } + + public static void Redo() { + // UNDONE: + } + + public static bool AnyUndos() { + return s_stk.Count != 0; + } + + // WARNING: can't do nested groups + + public static void BeginGroup() { + Debug.Assert(!s_fGrouping); + s_fGrouping = true; + s_cElements = 0; + } + + public static void EndGroup() { + s_fGrouping = false; + s_stk.Push(s_cElements); + } + } +} diff --git a/AniMax/WallPreviewForm.cs b/AniMax/WallPreviewForm.cs new file mode 100644 index 0000000..5453f25 --- /dev/null +++ b/AniMax/WallPreviewForm.cs @@ -0,0 +1,178 @@ +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.Drawing.Drawing2D; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; + +namespace SpiffCode +{ + /// + /// Summary description for WallPreviewForm. + /// + public class WallPreviewForm : System.Windows.Forms.Form + { + private const int kctxMap = 64; + private const int kctyMap = 64; + private bool m_fMouseDown = false; + private bool m_fSet; + + private bool[,] m_mpfWall = new bool[kctxMap, kctyMap]; + + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + public WallPreviewForm() + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + // + // TODO: Add any constructor code after InitializeComponent call + // + + m_mpfWall[5, 5] = true; + m_mpfWall[6, 5] = true; + m_mpfWall[6, 6] = true; + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + // + // WallPreviewForm + // + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.ClientSize = new System.Drawing.Size(368, 334); + this.Name = "WallPreviewForm"; + this.Text = "Wall Preview"; + + } + #endregion + + protected override void OnPaint(System.Windows.Forms.PaintEventArgs e) { + Graphics g = e.Graphics; + int nScale = Globals.PreviewScale; + Bitmap bmDst = new Bitmap(Width, Height); + Graphics gDst = Graphics.FromImage(bmDst); + + // Draw background (if enabled) + +// if (drwa.fDrawBackground) + gDst.Clear(BackColor); + + // Draw grid (if enabled) + + if (true) { + Brush br = new SolidBrush(Color.FromArgb(256 / 3, 255, 255, 255)); + for (int x = 0; x < bmDst.Width; x += Globals.GridWidth) + gDst.FillRectangle(br, x, 0, 1, bmDst.Height); + + for (int y = 0; y < bmDst.Height; y += Globals.GridHeight) + gDst.FillRectangle(br, 0, y, bmDst.Width, 1); + } + + BitmapData bmdDst = bmDst.LockBits(new Rectangle(0, 0, bmDst.Width, bmDst.Height), + ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); + + for (int ty = 0; ty < Height / Globals.GridHeight; ty++) { + for (int tx = 0; tx < Width / Globals.GridWidth; tx++) { + if (m_mpfWall[tx, ty]) { + int ifrm = 0; + if (ty > 0) + ifrm |= m_mpfWall[tx, ty - 1] ? 1 : 0; + if (tx < kctxMap - 1) + ifrm |= m_mpfWall[tx + 1, ty] ? 2 : 0; + if (ty < kctyMap - 1) + ifrm |= m_mpfWall[tx, ty + 1] ? 4 : 0; + if (tx > 0) + ifrm |= m_mpfWall[tx - 1, ty] ? 8 : 0; + + XBitmap xbm = Globals.ActiveStrip[ifrm].BitmapPlacers[0].XBitmap; + xbm.SuperBlt(0, 0, bmdDst, + tx * Globals.GridWidth, ty * Globals.GridHeight, + xbm.Bitmap.Width, xbm.Bitmap.Height, + Globals.SideColorMappingOn); + } + } + } + + bmDst.UnlockBits(bmdDst); + + // Force a nice simple fast old-school stretchblt + + InterpolationMode imOld = g.InterpolationMode; + g.InterpolationMode = InterpolationMode.NearestNeighbor; + + // NOTE: _without_ this the first row and column are only scaled by half! + + PixelOffsetMode pomOld = g.PixelOffsetMode; + g.PixelOffsetMode = PixelOffsetMode.Half; + + // StretchBlt the temporary composite to the passed-in Graphic + + g.DrawImage(bmDst, 0, 0, bmDst.Width * nScale, bmDst.Height * nScale); + + g.PixelOffsetMode = pomOld; + g.InterpolationMode = imOld; + +// e.Graphics.DrawImage(bmDst, 0, 0, Width, Height); + bmDst.Dispose(); + gDst.Dispose(); + } + + protected override void OnMouseDown(System.Windows.Forms.MouseEventArgs e) { + int nScale = Globals.PreviewScale; + m_fMouseDown = true; + + int tx = e.X / (Globals.GridWidth * nScale); + int ty = e.Y / (Globals.GridHeight * nScale); + m_fSet = !m_mpfWall[tx, ty]; + m_mpfWall[tx, ty] = m_fSet; + Invalidate(); + } + + protected override void OnMouseMove(System.Windows.Forms.MouseEventArgs e) { + if (!m_fMouseDown) + return; + int nScale = Globals.PreviewScale; + + int tx = e.X / (Globals.GridWidth * nScale); + int ty = e.Y / (Globals.GridHeight * nScale); + m_mpfWall[tx, ty] = m_fSet; + Invalidate(); + } + + protected override void OnPaintBackground(System.Windows.Forms.PaintEventArgs pevent) { + } + + protected override void OnMouseUp(System.Windows.Forms.MouseEventArgs e) { + m_fMouseDown = false; + } + + } +} diff --git a/AniMax/WallPreviewForm.resources b/AniMax/WallPreviewForm.resources new file mode 100644 index 0000000..c699b22 Binary files /dev/null and b/AniMax/WallPreviewForm.resources differ diff --git a/AniMax/WallPreviewForm.resx b/AniMax/WallPreviewForm.resx new file mode 100644 index 0000000..92a3b7e --- /dev/null +++ b/AniMax/WallPreviewForm.resx @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + WallPreviewForm + + \ No newline at end of file diff --git a/AniMax/XBitmap.cs b/AniMax/XBitmap.cs new file mode 100644 index 0000000..4efbdf9 --- /dev/null +++ b/AniMax/XBitmap.cs @@ -0,0 +1,610 @@ +using System; +using System.Collections; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.IO; +using System.Runtime.Serialization; + +namespace SpiffCode +{ + /// + /// Summary description for XBitmap. + /// + + // UNDONE: implement IDisposable and m_bm.Dispose() + + [Serializable] + public class XBitmap : ISerializable { + private Bitmap m_bm; + private Bitmap m_bmBlack; + private string m_strFileName; + private bool m_fDirty = false; + + public XBitmap(string strFileName) { + Load(strFileName, false); + } + + public XBitmap(string strFileName, bool fFrame) { + Load(strFileName, true); + } + + private XBitmap(Bitmap bm, Bitmap bmBlack, string strFileName, + bool fDirty) { + m_bm = bm; + m_bmBlack = bmBlack; + m_strFileName = strFileName; + m_fDirty = fDirty; + } + + public XBitmap Clone() { + return new XBitmap((Bitmap)m_bm.Clone(), + m_bmBlack == null ? null : (Bitmap)m_bmBlack.Clone(), + (string)m_strFileName.Clone(), m_fDirty); + } + + public static string[] FilterFileNames(string[] astrFileNames) { + ArrayList alFilenames = new ArrayList(); + foreach (string strFile in astrFileNames) { + try { + HasBlackCompanion(strFile); + alFilenames.Add(strFile); + } catch (Exception) { + } + } + return (string[])alFilenames.ToArray(typeof(string)); + } + + // Public properties + + public Bitmap Bitmap { + get { + return m_bm; + } + set { + if (m_bmBlack != null) { + throw new Exception("Setting bitmap on XBitmap with black counterpart!"); + } + m_bm = value; + m_fDirty = true; + } + } + + public string FileName { + get { + return m_strFileName; + } + set { + m_strFileName = value; + } + } + + public int Width { + get { + return m_bm.Width; + } + } + + public int Height { + get { + return m_bm.Height; + } + } + + public bool Dirty { + get { + return m_fDirty; + } + set { + m_fDirty = value; + } + } + + private static bool HasBlackCompanion(string strFile) { + // 32 bit XBitmaps have a companion black_, which + // is used for side color extraction. 8 bit XBitmaps + // do not have this. Encapsulate this knowledge here in XBitmap. + + string strPath = Path.GetDirectoryName(strFile); + string strFileName = Path.GetFileName(strFile); + + // Ensure the filename doesn't start with black_, otherwise raise + // an exception. + + if (strFile.StartsWith("black_")) { + throw new Exception("image file with black_: " + strFile); + } + + // File is ok; see if there is a black_ counterpart. If there is, + // this bitmap file is meant to be 32 bit + if (File.Exists(Path.Combine(strPath, "black_" + strFileName))) { + return true; + } + return false; + } + + // + + private void Load(string strFile, bool fUseFirstPaletteEntryAsTransparentColor) { + if (HasBlackCompanion(strFile)) { + Load32(strFile); + } else { + Load8(strFile, fUseFirstPaletteEntryAsTransparentColor); + } + } + + private void Load32(string strFile) { + if (!HasBlackCompanion(strFile)) { + throw new Exception("doesn't have black_ counterpart: " + + strFile); + } + m_strFileName = strFile; + m_bm = new Bitmap(strFile); + string strPath = Path.GetDirectoryName(strFile); + string strFileName = Path.GetFileName(strFile); + m_bmBlack = new Bitmap(Path.Combine(strPath, "black_" + strFileName)); + } + + private void Load8(string strFileName, bool fUseFirstPaletteEntryAsTransparentColor) { + m_strFileName = strFileName; + m_bm = new Bitmap(strFileName); + m_bmBlack = null; + + // All pixels the same color as the upper-left pixel get mapped to the + // transparent color + + Color clrTransparent = Color.FromArgb(0xff, 0, 0xff); + SolidBrush brTransparent = new SolidBrush(clrTransparent); + + m_bm.MakeTransparent(fUseFirstPaletteEntryAsTransparentColor ? m_bm.Palette.Entries[0] : m_bm.GetPixel(0, 0)); + + Bitmap bmT = new Bitmap(m_bm.Width, m_bm.Height, PixelFormat.Format24bppRgb); + using (Graphics g = Graphics.FromImage(bmT)) { + + // Prep the new image by filling with the transparent color + + g.FillRectangle(brTransparent, 0, 0, m_bm.Width, m_bm.Height); + + // Convert the Bitmap to 24-bpp while leaving transparent pixels behind + + g.DrawImageUnscaled(m_bm, 0, 0); + m_bm.Dispose(); + } + + m_bm = bmT; +// m_bm.MakeTransparent(clrTransparent); + } + + public void Save(string strFileName) { + if (strFileName == null) { + strFileName = m_strFileName; + } + m_bm.Save(strFileName); + + if (m_bmBlack != null) { + string strPath = Path.GetDirectoryName(strFileName); + string strFileT = Path.GetFileName(strFileName); + m_bmBlack.Save(Path.Combine(strPath, "black_" + strFileT)); + } + + m_fDirty = false; + } + + /* + * given h,s,l on [0..1], + * return r,g,b on [0..1] + * From Graphics Gems + */ + unsafe static void Hsl2Rgb(double h, double sl, double l, + double *r, double *g, double *b) { + double v; + + v = (l <= 0.5) ? (l * (1.0 + sl)) : (l + sl - l * sl); + if (v <= 0) { + *r = *g = *b = 0.0; + } else { + double m; + double sv; + int sextant; + double fract, vsf, mid1, mid2; + + m = l + l - v; + sv = (v - m ) / v; + h *= 6.0; + sextant = (int)h; + fract = h - sextant; + vsf = v * sv * fract; + mid1 = m + vsf; + mid2 = v - vsf; + switch (sextant) { + case 0: *r = v; *g = mid1; *b = m; break; + case 1: *r = mid2; *g = v; *b = m; break; + case 2: *r = m; *g = v; *b = mid1; break; + case 3: *r = m; *g = mid2; *b = v; break; + case 4: *r = mid1; *g = m; *b = v; break; + case 5: *r = v; *g = m; *b = mid2; break; + } + } + } + + unsafe static void Hsl2Rgb360(double h, double s, double l, + RgbData *prgb) { + // Convert 0..360 to 0..1 + double r, g, b; + Hsl2Rgb(h / 360.0, s, l, &r, &g, &b); + prgb->bRed = (byte)(r * 255.0); + prgb->bGreen = (byte)(g * 255.0); + prgb->bBlue = (byte)(b * 255.0); + } + + struct RgbaData { + public RgbaData(byte bAlpha, byte bRed, byte bGreen, byte bBlue) { + this.bAlpha = bAlpha; + this.bRed = bRed; + this.bGreen = bGreen; + this.bBlue = bBlue; + } + + public byte bBlue; + public byte bGreen; + public byte bRed; + public byte bAlpha; + } + + struct RgbData { + public RgbData(byte bRed, byte bGreen, byte bBlue) { + this.bRed = bRed; + this.bGreen = bGreen; + this.bBlue = bBlue; + } + + public byte bBlue; + public byte bGreen; + public byte bRed; + } + + static RgbData[] argbSide = { + new RgbData(232, 32, 0), + new RgbData(196, 28, 0), + new RgbData(128, 8, 0), + new RgbData(92, 8, 0), + new RgbData(64, 8, 0), + }; + + public unsafe void SuperBlt(int xSrc, int ySrc, + BitmapData bmdDst, int xDst, int yDst, int cx, int cy, + bool fMapSideColors) { + if (m_bmBlack == null) { + SuperBlt8(xSrc, ySrc, bmdDst, xDst, yDst, cx, cy, + fMapSideColors); + } else { + SuperBlt32(xSrc, ySrc, bmdDst, xDst, yDst, cx, cy, + fMapSideColors); + } + } + + public unsafe void SuperBlt32(int xSrc, int ySrc, + BitmapData bmdDst, int xDst, int yDst, int cx, int cy, + bool fMapSideColors) { + + // If completely off dst bounds, just return. + + if ((xDst >= bmdDst.Width || xDst + cx < 0) || + (yDst >= bmdDst.Height) || (yDst + cy < 0)) { + return; + } + + // Dst clip + + if (xDst + cx > bmdDst.Width) + cx = bmdDst.Width - xDst; + if (yDst + cy > bmdDst.Height) + cy = bmdDst.Height - yDst; + + if (xDst < 0) { + cx += xDst; + xSrc -= xDst; + xDst = 0; + } + + if (yDst < 0) { + cy += yDst; + ySrc -= yDst; + yDst = 0; + } + + BitmapData bmdSrc = m_bm.LockBits( + new Rectangle(0, 0, m_bm.Width, m_bm.Height), + ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); + BitmapData bmdSrcBlack = m_bmBlack.LockBits( + new Rectangle(0, 0, m_bmBlack.Width, m_bmBlack.Height), + ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); + + RgbaData* prgbSrc = (RgbaData*)((byte*)bmdSrc.Scan0 + + (ySrc * bmdSrc.Stride) + (xSrc * sizeof(RgbaData))); + RgbaData* prgbSrcBlack = (RgbaData*)((byte*)bmdSrcBlack.Scan0 + + (ySrc * bmdSrcBlack.Stride) + (xSrc * sizeof(RgbaData))); + RgbData* prgbDst = (RgbData*)((byte*)bmdDst.Scan0 + + (yDst * bmdDst.Stride) + (xDst * sizeof(RgbData))); + + while (cy-- > 0) { + RgbaData* prgbSrcT = prgbSrc; + RgbaData* prgbSrcBlackT = prgbSrcBlack; + RgbData* prgbDstT = prgbDst; + + for (int x = 0; x < cx; x++) { + RgbaData rgbSrc = *prgbSrcT++; + RgbaData rgbSrcBlack = *prgbSrcBlackT++; + + // rgbSrc = huemap(rgbSrc - rgbSrcBlack) + rgbSrcBlack + + RgbData rgbT; + int v; + v = rgbSrc.bRed - rgbSrcBlack.bRed; + if (v < 0) { + v = 0; + } + rgbT.bRed = (byte)v; + + v = rgbSrc.bGreen - rgbSrcBlack.bGreen; + if (v < 0) { + v = 0; + } + rgbT.bGreen = (byte)v; + + v = rgbSrc.bBlue - rgbSrcBlack.bBlue; + if (v < 0) { + v = 0; + } + rgbT.bBlue = (byte)v; + + Color clrT = Color.FromArgb(rgbT.bRed, rgbT.bGreen, + rgbT.bBlue); + double hue = clrT.GetHue(); + if (fMapSideColors) { + hue -= 235.0; + if (hue < 0.0) { + hue += 360.0; + } + } + Hsl2Rgb360(hue, clrT.GetSaturation(), clrT.GetBrightness(), + &rgbT); + + // Add new rgb back to black + v = rgbSrcBlack.bRed + rgbT.bRed; + if (v > 255) { + v = 255; + } + rgbSrc.bRed = (byte)v; + + v = rgbSrcBlack.bGreen + rgbT.bGreen; + if (v > 255) { + v = 255; + } + rgbSrc.bGreen = (byte)v; + + v = rgbSrcBlack.bBlue + rgbT.bBlue; + if (v > 255) { + v = 255; + } + rgbSrc.bBlue = (byte)v; + + // Alpha blend into dest + double alpha = (double)rgbSrc.bAlpha / 255.0; + double d; + d = (double)rgbSrc.bRed * alpha + + (double)prgbDstT->bRed * (1.0 - alpha); + if (d > 255.0) { + d = 255.0; + } + prgbDstT->bRed = (byte)d; + + d = (double)rgbSrc.bGreen * alpha + + (double)prgbDstT->bGreen * (1.0 - alpha); + if (d > 255.0) { + d = 255.0; + } + prgbDstT->bGreen = (byte)d; + + d = (double)rgbSrc.bBlue * alpha + + (double)prgbDstT->bBlue * (1.0 - alpha); + if (d > 255.0) { + d = 255.0; + } + prgbDstT->bBlue = (byte)d; + prgbDstT++; + } + + // Advance to next scan line + + prgbDst = (RgbData*)(((byte*)prgbDst) + bmdDst.Stride); + prgbSrc = (RgbaData*)(((byte*)prgbSrc) + bmdSrc.Stride); + prgbSrcBlack = (RgbaData*)(((byte*)prgbSrcBlack) + + bmdSrcBlack.Stride); + } + + m_bm.UnlockBits(bmdSrc); + m_bmBlack.UnlockBits(bmdSrcBlack); + } + + // Skips dst where src has transparent color. + // Darkens dst where src has shadow color. + // Translates side colors. + // NOTE: Performs dst but not src clipping!!! + // NOTE: Assumes src and dst BitmapData are PixelFormat.Format24bppRgb + + public unsafe void SuperBlt8(int xSrc, int ySrc, + BitmapData bmdDst, int xDst, int yDst, int cx, int cy, + bool fMapSideColors) { + + // If completely off dst bounds, just return. + + if ((xDst >= bmdDst.Width || xDst + cx < 0) || (yDst >= bmdDst.Height) || (yDst + cy < 0)) + return; + + // Dst clip + + if (xDst + cx > bmdDst.Width) + cx = bmdDst.Width - xDst; + if (yDst + cy > bmdDst.Height) + cy = bmdDst.Height - yDst; + + if (xDst < 0) { + cx += xDst; + xSrc -= xDst; + xDst = 0; + } + + if (yDst < 0) { + cy += yDst; + ySrc -= yDst; + yDst = 0; + } + + BitmapData bmdSrc = m_bm.LockBits( + new Rectangle(0, 0, m_bm.Width, m_bm.Height), + ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); + + RgbData* prgbSrc = (RgbData*)((byte*)bmdSrc.Scan0 + (ySrc * bmdSrc.Stride) + (xSrc * sizeof(RgbData))); + RgbData* prgbDst = (RgbData*)((byte*)bmdDst.Scan0 + (yDst * bmdDst.Stride) + (xDst * sizeof(RgbData))); + + while (cy-- > 0) { + RgbData* prgbDstT = prgbDst; + RgbData* prgbSrcT = prgbSrc; + + for (int x = 0; x < cx; x++) { + RgbData rgbSrc = *prgbSrcT++; + + // Handle shadow color + + if (rgbSrc.bRed == 156 && rgbSrc.bGreen == 212 & rgbSrc.bBlue == 248) { + prgbDstT->bRed = (byte)((prgbDstT->bRed * 60) / 100); + prgbDstT->bGreen = (byte)((prgbDstT->bGreen * 60) / 100); + prgbDstT->bBlue = (byte)((prgbDstT->bBlue * 60) / 100); + prgbDstT++; + + // Handle transparent color + + } else if (rgbSrc.bRed == 255 && rgbSrc.bGreen == 0 && rgbSrc.bBlue == 255) { + prgbDstT++; + + // Handle side colors + + } else if (fMapSideColors) { + if (rgbSrc.bRed == 0 && rgbSrc.bGreen == 116 && rgbSrc.bBlue == 232) { + *prgbDstT++ = argbSide[0]; + } else if (rgbSrc.bRed == 0 && rgbSrc.bGreen == 96 && rgbSrc.bBlue == 196) { + *prgbDstT++ = argbSide[1]; + } else if (rgbSrc.bRed == 0 && rgbSrc.bGreen == 64 && rgbSrc.bBlue == 120) { + *prgbDstT++ = argbSide[2]; + } else if (rgbSrc.bRed == 0 && rgbSrc.bGreen == 48 && rgbSrc.bBlue == 92) { + *prgbDstT++ = argbSide[3]; + } else if (rgbSrc.bRed == 0 && rgbSrc.bGreen == 32 && rgbSrc.bBlue == 64) { + *prgbDstT++ = argbSide[4]; + } else { + *prgbDstT++ = rgbSrc; + } + + // Just copy everything else unaltered + + } else { + *prgbDstT++ = rgbSrc; + } + } + + // Advance to next scan line + + prgbDst = (RgbData*)(((byte*)prgbDst) + bmdDst.Stride); + prgbSrc = (RgbData*)(((byte*)prgbSrc) + bmdSrc.Stride); + } + + m_bm.UnlockBits(bmdSrc); + } + + public static Rectangle CalcRealBounds(Bitmap bm) { + Color clrTransparent = Color.FromArgb(0xff, 0, 0xff); + SolidBrush brTransparent = new SolidBrush(clrTransparent); + + // OPT: this could be made faster by doing four independent edge scans + + int xL = bm.Width; + int xR = 0; + int yT = bm.Height; + int yB = 0; + for (int y = 0; y < bm.Height; y++) { + for (int x = 0; x < bm.Width; x++) { + Color clr = bm.GetPixel(x, y); + if (clr != clrTransparent) { + xL = Math.Min(xL, x); + xR = Math.Max(xR, x); + yT = Math.Min(yT, y); + yB = Math.Max(yB, y); + } + } + } + int cx = xR - xL + 1; + int cy = yB - yT + 1; + +#if false + Bitmap bmT = new Bitmap(cx, cy, PixelFormat.Format24bppRgb); + using (Graphics g = Graphics.FromImage(bmT)) { + Rectangle rcT = new Rectangle(xL, yT, cx, cy); + g.DrawImage(bm, 0, 0, rcT, GraphicsUnit.Pixel); + } +#endif + return new Rectangle(xL, yT, cx, cy); + } + + public Bitmap MakeThumbnail(int cxThumb, int cyThumb, bool fFilter) { + Rectangle rcSrc = XBitmap.CalcRealBounds(m_bm); + if (rcSrc.Width <= 0 || rcSrc.Height <= 0) + return m_bm; + + int cxy = Math.Max(rcSrc.Width, rcSrc.Height); + Bitmap bmDst = new Bitmap(cxy, cxy); + Graphics gDst = Graphics.FromImage(bmDst); + gDst.Clear(Color.White); + gDst.Dispose(); + int xDst = (bmDst.Width - rcSrc.Width) / 2; + int yDst = (bmDst.Height - rcSrc.Height) / 2; + + BitmapData bmdDst = bmDst.LockBits( + new Rectangle(0, 0, bmDst.Width, bmDst.Height), + ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); + + SuperBlt(rcSrc.X, rcSrc.Y, bmdDst, xDst, yDst, + rcSrc.Width, rcSrc.Height, false); + + bmDst.UnlockBits(bmdDst); + + Bitmap bmLarge = new Bitmap(cxThumb, cyThumb); + Graphics g = Graphics.FromImage(bmLarge); + InterpolationMode imOld = g.InterpolationMode; + g.InterpolationMode = fFilter ? InterpolationMode.Bicubic : InterpolationMode.NearestNeighbor; + PixelOffsetMode pomOld = g.PixelOffsetMode; + g.PixelOffsetMode = PixelOffsetMode.Half; + + g.DrawImage(bmDst, 0, 0, cxThumb, cyThumb); + + g.PixelOffsetMode = pomOld; + g.InterpolationMode = imOld; + + g.Dispose(); + bmDst.Dispose(); + + return bmLarge; + } + + // ISerializable interface implementation + + private XBitmap(SerializationInfo seri, StreamingContext stmc) { + string strFileName = seri.GetString("FileName"); + strFileName = strFileName.Replace(@"\", Path.DirectorySeparatorChar.ToString()); + Load(strFileName, false); + } + + void ISerializable.GetObjectData(SerializationInfo seri, StreamingContext stmc) { + seri.AddValue("FileName", m_strFileName); + } + } +} diff --git a/AniMax/XBitmapSet.cs b/AniMax/XBitmapSet.cs new file mode 100644 index 0000000..e561c47 --- /dev/null +++ b/AniMax/XBitmapSet.cs @@ -0,0 +1,58 @@ +using System; +using System.Drawing; +using System.Collections; +using System.Xml; +using System.Runtime.Serialization; + +namespace SpiffCode +{ + /// + /// Summary description for XBitmapSet. + /// UNDONE: act as a collection of extended bitmaps + /// NOTE: CollectionBase implements IList + /// + [Serializable] + public class XBitmapSet : CollectionBase, ISerializable + { + public XBitmapSet() { + } + + public XBitmap this[int i] { + get { + return (XBitmap)InnerList[i]; + } + set { + InnerList[i] = value; + } + } + + public int Add(XBitmap xbm) { + return ((IList)this).Add(xbm); + } + + public int IndexOf(XBitmap xbm) { + return ((IList)this).IndexOf(xbm); + } + + public int Add(string strFileName) { + return Add(new XBitmap(strFileName)); + } + + // ISerializable interface implementation + + private XBitmapSet(SerializationInfo seri, StreamingContext stmc) { + for (int i = 0; i < seri.MemberCount; i++) { + XBitmap xbm = (XBitmap)seri.GetValue(i.ToString(), typeof(XBitmap)); + Add(xbm); + } + } + + void ISerializable.GetObjectData(SerializationInfo seri, StreamingContext stmc) { + int i = 0; + foreach (XBitmap xbm in InnerList) { + seri.AddValue(i.ToString(), xbm); + i++; + } + } + } +} diff --git a/AniMax/amx/AssemblyInfo.cs b/AniMax/amx/AssemblyInfo.cs new file mode 100644 index 0000000..9f89a32 --- /dev/null +++ b/AniMax/amx/AssemblyInfo.cs @@ -0,0 +1,58 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.*")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] diff --git a/AniMax/amx/Class1.cs b/AniMax/amx/Class1.cs new file mode 100644 index 0000000..0e04d64 --- /dev/null +++ b/AniMax/amx/Class1.cs @@ -0,0 +1,17 @@ +using System; + +namespace amx +{ + /// + /// Summary description for Class1. + /// + public class Class1 + { + public Class1() + { + // + // TODO: Add constructor logic here + // + } + } +} diff --git a/AniMax/amx/amx.csproj b/AniMax/amx/amx.csproj new file mode 100644 index 0000000..237ad60 --- /dev/null +++ b/AniMax/amx/amx.csproj @@ -0,0 +1,167 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AniMax/amx/amx.sln b/AniMax/amx/amx.sln new file mode 100644 index 0000000..323a177 --- /dev/null +++ b/AniMax/amx/amx.sln @@ -0,0 +1,21 @@ +Microsoft Visual Studio Solution File, Format Version 7.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "amx", "amx.csproj", "{8A01587F-4FC8-4F11-AA53-B05E3FB792CC}" +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + ConfigName.0 = Debug + ConfigName.1 = Release + EndGlobalSection + GlobalSection(ProjectDependencies) = postSolution + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {8A01587F-4FC8-4F11-AA53-B05E3FB792CC}.Debug.ActiveCfg = Debug|.NET + {8A01587F-4FC8-4F11-AA53-B05E3FB792CC}.Debug.Build.0 = Debug|.NET + {8A01587F-4FC8-4F11-AA53-B05E3FB792CC}.Release.ActiveCfg = Release|.NET + {8A01587F-4FC8-4F11-AA53-B05E3FB792CC}.Release.Build.0 = Release|.NET + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/AniMax/deploy.bat b/AniMax/deploy.bat new file mode 100644 index 0000000..94ac4f2 --- /dev/null +++ b/AniMax/deploy.bat @@ -0,0 +1,2 @@ +copy bin\debug\AniMax.exe ..\bin +copy amx\bin\debug\amx.dll ..\bin diff --git a/AniMax/deploy.sh b/AniMax/deploy.sh new file mode 100755 index 0000000..e3c1e74 --- /dev/null +++ b/AniMax/deploy.sh @@ -0,0 +1,2 @@ +cp bin/Debug/AniMax.exe ../bin +cp amx/bin/Debug/amx.dll ../bin diff --git a/AniMax/grid icon.psd b/AniMax/grid icon.psd new file mode 100644 index 0000000..cc49f33 Binary files /dev/null and b/AniMax/grid icon.psd differ diff --git a/AniMax/licenses.licx b/AniMax/licenses.licx new file mode 100644 index 0000000..5f7ce18 --- /dev/null +++ b/AniMax/licenses.licx @@ -0,0 +1,21 @@ +System.Windows.Forms.OpenFileDialog, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.ListView, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.Form, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.Panel, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.MainMenu, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.ContextMenu, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.Label, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.MenuItem, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.TextBox, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.UserControl, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.Button, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.SaveFileDialog, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.ListBox, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.ToolBarButton, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.HScrollBar, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.Timer, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.NumericUpDown, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.ToolBar, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.MdiClient, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.Splitter, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 diff --git a/AniMax/origin point toggle icon.psd b/AniMax/origin point toggle icon.psd new file mode 100644 index 0000000..608f5ef Binary files /dev/null and b/AniMax/origin point toggle icon.psd differ diff --git a/AniMax/play toggle icon.psd b/AniMax/play toggle icon.psd new file mode 100644 index 0000000..7328059 Binary files /dev/null and b/AniMax/play toggle icon.psd differ diff --git a/AniMax/properties icon.psd b/AniMax/properties icon.psd new file mode 100644 index 0000000..85f8a6a Binary files /dev/null and b/AniMax/properties icon.psd differ diff --git a/AniMax/set special point icon.psd b/AniMax/set special point icon.psd new file mode 100644 index 0000000..bb43118 Binary files /dev/null and b/AniMax/set special point icon.psd differ diff --git a/AniMax/side color toggle icon.psd b/AniMax/side color toggle icon.psd new file mode 100644 index 0000000..9c3cc3a Binary files /dev/null and b/AniMax/side color toggle icon.psd differ diff --git a/AniMax/toggle origin point icon.psd b/AniMax/toggle origin point icon.psd new file mode 100644 index 0000000..d7978ce Binary files /dev/null and b/AniMax/toggle origin point icon.psd differ diff --git a/AniMax/worklist.txt b/AniMax/worklist.txt new file mode 100644 index 0000000..dafcd47 --- /dev/null +++ b/AniMax/worklist.txt @@ -0,0 +1,41 @@ +5/15/02 +- icon +- right click properties on Frame +- zoom control (Frames) +- info (x, y, w, h, # frames, color under pointer, ...) +- spacebar & grab hand ala Photoshop +- multiple bitmaps/Frame +- paint tools & color palette (get minimal list from Mark) +- save/load settings (layout, toggles, ...) + +* version .23 +* add help (invoke browser pointing to http://www.tinybit.org/AniMax) +* fix 'easy to blank out strip name' problem [fixed one way, can't reproduce other] +* new MagicLibrary +* Preview zoom control +* tank + turret preview ("Combiner") + +5/18/02 +- normalize bitmaps +- MRU file list + +* version .24 +* About dialog w/ version # +* expanded Strips and Bitmaps selections to cover whole row +* drag/drop .amx from Explorer [must be dropped in Strips window] +* Save .amx now always builds a bitmap subdir and writes all bitmaps to it + [NOTE: subdir contents are not deleted so it is possible for unused bitmaps + to accumulate. I've left it this way so we can add the bitmap dirs to CVS + and for other minor coveniences. To clean out cruft, save as .zip. Only + referenced bitmaps will be included in the .zip] +* load/save packaged animations (.zips) + +6/6/02 +- drag into but don't drop on Frames StripControl leaves insertion point, + scroll timer state broken +- no way to delete individual bitmaps in a frame +- reimporting altered-sized bitmaps almost certainly hoses the positioning of + the bitmap in existing frames +- need keyboard shortcut for moving forward and backward through strips + +* WriteAnir multiple bitmaps/frame [limited to 2 for now] diff --git a/BmpProof/.cvsignore b/BmpProof/.cvsignore new file mode 100644 index 0000000..0ca40fa --- /dev/null +++ b/BmpProof/.cvsignore @@ -0,0 +1,2 @@ +bin +SpiffLib diff --git a/BmpProof/App.ico b/BmpProof/App.ico new file mode 100644 index 0000000..3a5525f Binary files /dev/null and b/BmpProof/App.ico differ diff --git a/BmpProof/AssemblyInfo.cs b/BmpProof/AssemblyInfo.cs new file mode 100644 index 0000000..9f89a32 --- /dev/null +++ b/BmpProof/AssemblyInfo.cs @@ -0,0 +1,58 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.*")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] diff --git a/BmpProof/BmpProof.cs b/BmpProof/BmpProof.cs new file mode 100644 index 0000000..c25c72a --- /dev/null +++ b/BmpProof/BmpProof.cs @@ -0,0 +1,168 @@ +using System; +using System.IO; +using System.Collections; +using System.Collections.Specialized; +using System.Drawing; +using System.Drawing.Imaging; +using SpiffLib; + +namespace BmpProof +{ + /// + /// Summary description for Class1. + /// + class App + { + static StringCollection gstrcFileNames = new StringCollection(); + static string gstrPalette; + static Palette gpal; + static bool gf6bitRGB = false; + + /// + /// The main entry point for the application. + /// + [STAThread] + static unsafe int Main(string[] astrArgs) + { + // Command-line argument processing + + if (astrArgs.Length == 0) { + PrintHelp(); + return 0; + } + + for (int i = 0; i < astrArgs.Length; i++) { + switch (astrArgs[i]) { + case "-?": + PrintHelp(); + return 0; + + case "-p": + gstrPalette = astrArgs[++i]; + gpal = new Palette(gstrPalette); + break; + + case "-6": + gf6bitRGB = true; + break; + + default: + if (astrArgs[i][0] == '-') { + Console.WriteLine("Error: invalid flag '{0}'", astrArgs[i]); + return -1; + } + + // Assume all 'unassociated' arguments are input filenames (potentially wildcarded) + + string strDir = Path.GetDirectoryName(astrArgs[i]); + if (strDir == "") + strDir = "."; + string[] astrFileNames = Directory.GetFiles(strDir, Path.GetFileName(astrArgs[i])); + + if (astrFileNames.Length == 0) { + gstrcFileNames.Add(astrArgs[i]); + } else { + foreach (string strFileName in astrFileNames) { + if (strFileName.ToLower().EndsWith(".ani")) { + string strT = Path.GetDirectoryName(strFileName) + @"\" + Path.GetFileNameWithoutExtension(strFileName) + @"\*.png"; + string[] astrT = Directory.GetFiles(Path.GetDirectoryName(strT), Path.GetFileName(strT)); + gstrcFileNames.AddRange(astrT); + } else { + gstrcFileNames.Add(strFileName); + } + } + } + break; + } + } + + if (gpal == null) { + Console.WriteLine("A valid palette must be specified via the '-p' switch"); + return -1; + } + + if (gstrcFileNames.Count == 0) { + Console.WriteLine("Error: no files specified"); + return -1; + } + + int nReturnValue = 0; + Color clrShadow = Color.FromArgb(156, 212, 248); + + Console.Write("Verifying bitmap colors..."); + + foreach (string strFileName in gstrcFileNames) { + Hashtable htInvalidColors = new Hashtable(); + Bitmap bm = null; + try { + bm = new Bitmap(strFileName); + } catch { + Console.WriteLine("Error: {0} is not a recognized bitmap or palette file", strFileName); + continue; + } + + Color clrTransparent = bm.GetPixel(0, 0); + if (gf6bitRGB) + clrTransparent = Color.FromArgb(clrTransparent.R & 0xfc, clrTransparent.G & 0xfc, clrTransparent.B & 0xfc); + + // Lock down bits for speed + + Rectangle rc = new Rectangle(0, 0, bm.Width, bm.Height); + BitmapData bmd = bm.LockBits(rc, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); + byte *pbBase = (byte *)bmd.Scan0.ToPointer(); + + for (int y = 0; y < bm.Height; y++) { + for (int x = 0; x < bm.Width; x++) { + byte *pb = pbBase + y * bmd.Stride + x * 3; + Color clr; + if (gf6bitRGB) + clr = Color.FromArgb(pb[2] & 0xfc, pb[1] & 0xfc, pb[0] & 0xfc); + else + clr = Color.FromArgb(pb[2], pb[1], pb[0]); + + int i = gpal.FindClosestEntry(clr); + if (gpal[i] != clr && clr != clrShadow && clr != clrTransparent && !htInvalidColors.ContainsKey(clr)) + htInvalidColors.Add(clr, clr); + } + } + bm.UnlockBits(bmd); + + // Report any invalid colors + + if (htInvalidColors.Count != 0) { + if (nReturnValue == 0) + Console.WriteLine(); + nReturnValue = -1; + + int cclr = htInvalidColors.Count; + + Color[] aclr = new Color[cclr]; + htInvalidColors.Values.CopyTo(aclr, 0); + Console.Write("{0} contains {1} invalid color{2} (", + Path.GetFileName(strFileName), cclr, cclr == 1 ? "" : "s"); + for (int i = 0; i < aclr.Length; i++) { + Color clr = aclr[i]; + Console.Write("{0},{1},{2}", clr.R, clr.G, clr.B); + if (i != aclr.Length - 1) + Console.Write(", "); + } + Console.WriteLine(")"); + } + } + + if (nReturnValue == 0) + Console.WriteLine("done"); + return nReturnValue; + } + + // + + static void PrintHelp() { + Console.WriteLine( + "Usage: BmpProof [-6] <-p filename> file[s]\n" + + "-6: convert colors to 6-bit precision before comparing\n" + + "-p: specify palette of valid colors\n" + + "files[s]: bitmap files to be processed. Wildcards allowed."); + } + } +} diff --git a/BmpProof/BmpProof.csproj b/BmpProof/BmpProof.csproj new file mode 100644 index 0000000..d93480f --- /dev/null +++ b/BmpProof/BmpProof.csproj @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/BmpProof/BmpProof.sln b/BmpProof/BmpProof.sln new file mode 100644 index 0000000..78b2e7c --- /dev/null +++ b/BmpProof/BmpProof.sln @@ -0,0 +1,21 @@ +Microsoft Visual Studio Solution File, Format Version 7.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BmpProof", "BmpProof.csproj", "{C2D0A473-D426-4F01-B8E8-FB4F6BD41A41}" +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + ConfigName.0 = Debug + ConfigName.1 = Release + EndGlobalSection + GlobalSection(ProjectDependencies) = postSolution + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {C2D0A473-D426-4F01-B8E8-FB4F6BD41A41}.Debug.ActiveCfg = Debug|.NET + {C2D0A473-D426-4F01-B8E8-FB4F6BD41A41}.Debug.Build.0 = Debug|.NET + {C2D0A473-D426-4F01-B8E8-FB4F6BD41A41}.Release.ActiveCfg = Release|.NET + {C2D0A473-D426-4F01-B8E8-FB4F6BD41A41}.Release.Build.0 = Release|.NET + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/BmpProof/deploy.bat b/BmpProof/deploy.bat new file mode 100644 index 0000000..5863976 --- /dev/null +++ b/BmpProof/deploy.bat @@ -0,0 +1 @@ +copy bin\debug\bmpproof.exe ..\bin diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..0307eec --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,26 @@ +Copyright (c) 2003-2014, Spiffcode, Inc +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of Spiffcode, Inc. \ No newline at end of file diff --git a/MarkVersion/App.ico b/MarkVersion/App.ico new file mode 100644 index 0000000..3a5525f Binary files /dev/null and b/MarkVersion/App.ico differ diff --git a/MarkVersion/AssemblyInfo.cs b/MarkVersion/AssemblyInfo.cs new file mode 100644 index 0000000..9f89a32 --- /dev/null +++ b/MarkVersion/AssemblyInfo.cs @@ -0,0 +1,58 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.*")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] diff --git a/MarkVersion/Class1.cs b/MarkVersion/Class1.cs new file mode 100644 index 0000000..b2cf003 --- /dev/null +++ b/MarkVersion/Class1.cs @@ -0,0 +1,102 @@ +using System; +using System.IO; + +namespace MarkVersion { + class Class1 { + [STAThread] + static void Main(string[] args) { + int cMarks; + BinaryReader br; + BinaryWriter bw; + FileStream stm; + string strMark; + string strPattern; + try { + cMarks = Int32.Parse(args[0]); + stm = new FileStream(args[1], FileMode.Open, FileAccess.ReadWrite); + br = new BinaryReader(stm); + bw = new BinaryWriter(stm); + strPattern = args[2]; + strMark = null; + switch (args.Length) { + case 4: + strMark = args[3]; + break; + + case 5: + if (args[3] == "-f") { + TextReader tr = new StreamReader(args[4]); + strMark = tr.ReadLine(); + tr.Close(); + } + break; + + case 3: + strMark = Console.In.ReadLine(); + break; + } + strMark.Trim(); + } catch { + Console.WriteLine("Usage:"); + Console.WriteLine("MarkVersion count file pattern text"); + Console.WriteLine("MarkVersion count file pattern -f markfile"); + Console.WriteLine("MarkVersion count file pattern"); + Console.WriteLine("If the mark is left off, stdin will be used."); + Console.WriteLine("If the mark count doesn't match count, it is an error. -1 means unlimited."); + Console.WriteLine("ex: date \"+%m.%d.%y, %l:%M%p\" | markversion 1 file.pdb \"+++DATEDATEDATE+++\""); + return; + } + + int c = 0; + long pos = 0; + while (pos < br.BaseStream.Length) { + pos = FindPattern(br, pos, strPattern); + if (pos == -1) + break; + pos = Mark(bw, pos, strMark, strPattern); + c++; + } + + br.Close(); + bw.Close(); + stm.Close(); + Console.WriteLine("Marked " + c + " \"" + strPattern + "\" with \"" + strMark + "\""); + + // Match mark count or exception + + if (cMarks != -1 && c != cMarks) + throw new Exception("Marked " + c + " times, you asked for " + cMarks); + } + + static long FindPattern(BinaryReader br, long pos, string strPattern) { + br.BaseStream.Position = pos; + while (br.BaseStream.Position < br.BaseStream.Length - strPattern.Length) { + bool fMatch = true; + long posPattern = br.BaseStream.Position; + for (int i = 0; i < strPattern.Length; i++) { + if ((byte)strPattern[i] != br.ReadByte()) { + fMatch = false; + break; + } + } + if (fMatch) + return posPattern; + } + return -1; + } + + static long Mark(BinaryWriter bw, long pos, string strMark, string strPattern) { + bw.BaseStream.Position = pos; + int cch = Math.Min(strMark.Length, strPattern.Length); + for (int n = 0; n < cch; n++) + bw.Write((byte)strMark[n]); + + // Zero terminate if the mark is shorter than strPattern + + if (strMark.Length < strPattern.Length) + bw.Write((byte)0); + + return bw.BaseStream.Position; + } + } +} diff --git a/MarkVersion/MarkVersion.csproj b/MarkVersion/MarkVersion.csproj new file mode 100644 index 0000000..d8709a6 --- /dev/null +++ b/MarkVersion/MarkVersion.csproj @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MarkVersion/MarkVersion.sln b/MarkVersion/MarkVersion.sln new file mode 100644 index 0000000..9c2df7a --- /dev/null +++ b/MarkVersion/MarkVersion.sln @@ -0,0 +1,21 @@ +Microsoft Visual Studio Solution File, Format Version 7.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MarkVersion", "MarkVersion.csproj", "{93109769-385C-4924-87EA-52E974CC1AFE}" +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + ConfigName.0 = Debug + ConfigName.1 = Release + EndGlobalSection + GlobalSection(ProjectDependencies) = postSolution + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {93109769-385C-4924-87EA-52E974CC1AFE}.Debug.ActiveCfg = Debug|.NET + {93109769-385C-4924-87EA-52E974CC1AFE}.Debug.Build.0 = Debug|.NET + {93109769-385C-4924-87EA-52E974CC1AFE}.Release.ActiveCfg = Release|.NET + {93109769-385C-4924-87EA-52E974CC1AFE}.Release.Build.0 = Release|.NET + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/README.md b/README.md new file mode 100644 index 0000000..052a940 --- /dev/null +++ b/README.md @@ -0,0 +1,60 @@ +## About + +*Hostile Takeover* is the open source release of the wildly popular mobile Real Time Strategy game *Warfare Incorporated*. Warfare Incorporated's developers, grateful for all the contributions of the open source community, are delighted to give something back. + +#### Legal Stuff +*Hostile Takeover* and *Warfare Incorporated* are registered trademarks of *Spiffcode, Inc* the developers of Warfare Incorporated. *Hostile Takeover* is copyrighted 2004-2014 by Spiffcode, Inc. The Hostile Takeover source code is made available under the [BSD open source license](LICENSE.txt). + +## Building + +### Prerequisites +- Mac OS X +- Xcode 5 (or later) with *Command Line Tools* installed + - Install *Command Line Tools* from Xcode *Preferences... Downloads*. + +### Building the Hostile Takeover Executable +1. Within Xcode, open `game/wi.xcodeproj`. +- Select a Build Configuration via *Edit Scheme...Info* + - **Debug (default)**. Has extra runtime checks and is typically used during development. Displays "DEV BUILD" and date on Hostile Takeover startup screen. The Debug version requires a local server to be running for multiplayer testing. + - **Release**. W/o extra runtime checks. Displays date on Hostile Takeover startup screen. + - **Distribution**. Same as Release but displays game database version number on Hostile Takeover startup screen. + - **Distribution-Store**. Same as Distribution but is signed with a necessary key for redistribution in the iTunes App Store. NOTE: You'll have to get your own key if you want to distribute through the App Store. + - **Adhoc**. ??? +- Build and run! + - On your device or in the iOS Simulator. + +### Building the Hostile Takeover Data File +The Hostile Takeover iOS executable incorporates a data file, **htdata832.pdb**, which includes all the art, audio, and levels for the game. A prebuilt version of this data file is already in the source tree so you don't need to build it unless you are modifying assets it contains. + +Originally all of Hostile Takeover was developed on Windows using Windows tools like Visual Studio and C#. To build the data file on the Mac, the cross platform Mono framework is used. + +1. Install the Mono runtime environment. + - Available from http://www.go-mono.com/mono-downloads/download.html. Version 3.4 (Mono MRE installer) is known to work. +- `cd data` +- `make IPHONE=1 MULTIPLAYER=1 clean all` + - some harmless warnings will be displayed +- Mark the data file with a version number. + - `cd ..` + - `bin/markversion 1.6` + +### Building and running the Hostile Takeover Multiplayer Server +Hostile Takeover supports single player and multiplayer play. Multiplayer is supported by two server components: a TCP based game server located in the **server** directory, and an HTTP based server located in the **stats** directory used for accounts, leaderboard, game history and game stats. + +1. The game server is a command line server which builds can be built and run on OSX or Linux. The game server is does not (currently) build and run on Windows. The production server environment is expected to be Linux based. +- `cd server` +- make REL=1 clean all (for a release build) +- make clean all (for a debug build) + +Please refer to the README.txt in the **server** directory for more detailed information on how to run the server environment in development and production. + +## Tools +The `bin` directory contains many command line tools used to build the htdata file. There are also GUI tools for editing maps (M) and animations (AniMax). + +The GUI tools have primarily been used on Windows and are best described as 'flaky' on the Mac. If you want to give level editing a try on a Mac: + +1. `mono bin/m.exe` +- File > Open +- Select a .ld (level description) file from the `data` directory. +- There is an m.chm file for M help. m.chm is a Windows specific file format. There is also an overview on the web found here: http://sites.warfareincorporated.com/mission-authoring/ + +See [Building the Hostile Takeover Data File](#building-the-hostile-takeover-data-file) for instructions on how to rebuild the data file after making changes to any level, image, sound files or other game assets. diff --git a/Schemer/.cvsignore b/Schemer/.cvsignore new file mode 100644 index 0000000..ba077a4 --- /dev/null +++ b/Schemer/.cvsignore @@ -0,0 +1 @@ +bin diff --git a/Schemer/App.ico b/Schemer/App.ico new file mode 100644 index 0000000..3a5525f Binary files /dev/null and b/Schemer/App.ico differ diff --git a/Schemer/AssemblyInfo.cs b/Schemer/AssemblyInfo.cs new file mode 100644 index 0000000..9f89a32 --- /dev/null +++ b/Schemer/AssemblyInfo.cs @@ -0,0 +1,58 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.*")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] diff --git a/Schemer/Form1.cs b/Schemer/Form1.cs new file mode 100644 index 0000000..8b9d42e --- /dev/null +++ b/Schemer/Form1.cs @@ -0,0 +1,1497 @@ +using System; +using System.IO; +using System.Drawing; +using System.Collections; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Windows.Forms; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; +using SpiffLib; +using LoMaN.IO; + +namespace Schemer +{ + /// + /// Summary description for Form1. + /// + public class Form1 : System.Windows.Forms.Form, IComparer + { + bool m_fFillingListBox; + bool m_fPlaySound; + ListViewItem m_itemDisplayed; + ListViewItem m_itemForCopy; + CaseInsensitiveComparer m_comparer = new CaseInsensitiveComparer(); + int m_iColumnSort; + bool m_fAscending = true; + string m_strSfxH = null; + string m_strPdbFile = null; + StringCollection m_strcPriorities = null; + ArrayList m_alsNames = new ArrayList(); + ArrayList m_alsSfxEnabled = new ArrayList(); + string m_strSfxFile; + int m_nComPort = 2; + + private System.Windows.Forms.MainMenu mainMenu1; + private System.Windows.Forms.MenuItem menuItem1; + private System.Windows.Forms.Panel paneSfxSoundsParent; + private System.Windows.Forms.Panel panelSfx; + private System.Windows.Forms.Splitter splitter2; + private System.Windows.Forms.Panel panelSfxProperties; + private System.Windows.Forms.ListView listViewSfx; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Splitter splitter1; + private System.Windows.Forms.Panel panelSounds; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.ColumnHeader columnHeaderSound; + private System.Windows.Forms.ColumnHeader columnHeaderChannel; + private System.Windows.Forms.ColumnHeader columnHeaderPriority; + private System.Windows.Forms.ColumnHeader columnHeaderComment; + private System.Windows.Forms.Panel panelMain; + private System.Windows.Forms.StatusBar statusBar1; + private System.Windows.Forms.Label label7; + private System.Windows.Forms.Label label6; + private System.Windows.Forms.Label label8; + private System.Windows.Forms.TextBox textBoxSoundsDir; + private System.Windows.Forms.ListBox listBoxSounds; + private System.Windows.Forms.ColumnHeader columnHeaderSfx; + private System.Windows.Forms.Label labelPropertiesSfx; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.TextBox textBoxPropertiesSound; + private System.Windows.Forms.ComboBox comboBoxPropertiesPriority; + private System.Windows.Forms.TextBox textBoxPropertiesComment; + private System.Windows.Forms.MenuItem menuItemSaveAs; + private System.Windows.Forms.MenuItem menuItem6; + private System.Windows.Forms.MenuItem menuItem7; + private System.Windows.Forms.MenuItem menuItemMakePdb; + private System.Windows.Forms.MenuItem menuItem8; + private System.Windows.Forms.MenuItem menuItemNew; + private System.Windows.Forms.MenuItem menuItemOpen; + private System.Windows.Forms.MenuItem menuItemSave; + private System.Windows.Forms.MenuItem menuItem2; + private System.Windows.Forms.MenuItem menuItemCheckAll; + private System.Windows.Forms.MenuItem menuItemUncheckAll; + private System.Windows.Forms.MenuItem menuItem3; + private System.Windows.Forms.MenuItem menuItemCopy; + private System.Windows.Forms.MenuItem menuItemPaste; + private System.Windows.Forms.MenuItem menuItemClear; + private System.Windows.Forms.MenuItem menuItem4; + private System.Windows.Forms.MenuItem menuItemCheckPdbSize; + private System.Windows.Forms.MenuItem menuItemTestCom2; + private System.Windows.Forms.ContextMenu contextMenuListBox; + private System.Windows.Forms.MenuItem menuItemPlayOnPalm; + private System.Windows.Forms.MenuItem menuItem5; + private System.Windows.Forms.MenuItem menuItemCom1; + private System.Windows.Forms.MenuItem menuItemCom2; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + public Form1() + { + Application.Idle += new System.EventHandler(AppIdleEventHandler); + + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + // + // TODO: Add any constructor code after InitializeComponent call + // + + m_strcPriorities = new StringCollection(); + m_strcPriorities.Add("Voice Status"); + m_strcPriorities.Add("UI Response"); + m_strcPriorities.Add("Voice Reponse"); + m_strcPriorities.Add("High Priority"); + m_strcPriorities.Add("Explosion"); + m_strcPriorities.Add("Missle Impact"); + m_strcPriorities.Add("Tank Impact"); + m_strcPriorities.Add("Missle Shot"); + m_strcPriorities.Add("Tank Shot"); + m_strcPriorities.Add("Machine Gun"); + m_strcPriorities.Add("Background"); + m_strcPriorities.Add("Unknown"); + + comboBoxPropertiesPriority.DataSource = m_strcPriorities; + + if (!LoadSettings()) + Close(); + NewScheme(); + } + + void AppIdleEventHandler(object sender, System.EventArgs e) { + m_fPlaySound = true; + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if (components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.mainMenu1 = new System.Windows.Forms.MainMenu(); + this.menuItem1 = new System.Windows.Forms.MenuItem(); + this.menuItemNew = new System.Windows.Forms.MenuItem(); + this.menuItemOpen = new System.Windows.Forms.MenuItem(); + this.menuItemSave = new System.Windows.Forms.MenuItem(); + this.menuItemSaveAs = new System.Windows.Forms.MenuItem(); + this.menuItem6 = new System.Windows.Forms.MenuItem(); + this.menuItemMakePdb = new System.Windows.Forms.MenuItem(); + this.menuItem8 = new System.Windows.Forms.MenuItem(); + this.menuItem7 = new System.Windows.Forms.MenuItem(); + this.menuItem2 = new System.Windows.Forms.MenuItem(); + this.menuItemCopy = new System.Windows.Forms.MenuItem(); + this.menuItemPaste = new System.Windows.Forms.MenuItem(); + this.menuItemClear = new System.Windows.Forms.MenuItem(); + this.menuItem3 = new System.Windows.Forms.MenuItem(); + this.menuItemCheckAll = new System.Windows.Forms.MenuItem(); + this.menuItemUncheckAll = new System.Windows.Forms.MenuItem(); + this.menuItem4 = new System.Windows.Forms.MenuItem(); + this.menuItemCheckPdbSize = new System.Windows.Forms.MenuItem(); + this.menuItemTestCom2 = new System.Windows.Forms.MenuItem(); + this.panelMain = new System.Windows.Forms.Panel(); + this.paneSfxSoundsParent = new System.Windows.Forms.Panel(); + this.panelSfx = new System.Windows.Forms.Panel(); + this.listViewSfx = new System.Windows.Forms.ListView(); + this.columnHeaderSfx = new System.Windows.Forms.ColumnHeader(); + this.columnHeaderSound = new System.Windows.Forms.ColumnHeader(); + this.columnHeaderChannel = new System.Windows.Forms.ColumnHeader(); + this.columnHeaderPriority = new System.Windows.Forms.ColumnHeader(); + this.columnHeaderComment = new System.Windows.Forms.ColumnHeader(); + this.splitter2 = new System.Windows.Forms.Splitter(); + this.panelSfxProperties = new System.Windows.Forms.Panel(); + this.textBoxPropertiesComment = new System.Windows.Forms.TextBox(); + this.label8 = new System.Windows.Forms.Label(); + this.comboBoxPropertiesPriority = new System.Windows.Forms.ComboBox(); + this.label6 = new System.Windows.Forms.Label(); + this.textBoxPropertiesSound = new System.Windows.Forms.TextBox(); + this.label7 = new System.Windows.Forms.Label(); + this.labelPropertiesSfx = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.label1 = new System.Windows.Forms.Label(); + this.splitter1 = new System.Windows.Forms.Splitter(); + this.panelSounds = new System.Windows.Forms.Panel(); + this.listBoxSounds = new System.Windows.Forms.ListBox(); + this.textBoxSoundsDir = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.statusBar1 = new System.Windows.Forms.StatusBar(); + this.contextMenuListBox = new System.Windows.Forms.ContextMenu(); + this.menuItemPlayOnPalm = new System.Windows.Forms.MenuItem(); + this.menuItem5 = new System.Windows.Forms.MenuItem(); + this.menuItemCom1 = new System.Windows.Forms.MenuItem(); + this.menuItemCom2 = new System.Windows.Forms.MenuItem(); + this.panelMain.SuspendLayout(); + this.paneSfxSoundsParent.SuspendLayout(); + this.panelSfx.SuspendLayout(); + this.panelSfxProperties.SuspendLayout(); + this.panelSounds.SuspendLayout(); + this.SuspendLayout(); + // + // mainMenu1 + // + this.mainMenu1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.menuItem1, + this.menuItem2, + this.menuItem4}); + // + // menuItem1 + // + this.menuItem1.Index = 0; + this.menuItem1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.menuItemNew, + this.menuItemOpen, + this.menuItemSave, + this.menuItemSaveAs, + this.menuItem6, + this.menuItemMakePdb, + this.menuItem8, + this.menuItem7}); + this.menuItem1.Text = "File"; + // + // menuItemNew + // + this.menuItemNew.Index = 0; + this.menuItemNew.Text = "New"; + this.menuItemNew.Click += new System.EventHandler(this.menuItemNew_Click); + // + // menuItemOpen + // + this.menuItemOpen.Index = 1; + this.menuItemOpen.Text = "Open..."; + this.menuItemOpen.Click += new System.EventHandler(this.menuItemOpen_Click); + // + // menuItemSave + // + this.menuItemSave.Index = 2; + this.menuItemSave.Text = "Save"; + this.menuItemSave.Click += new System.EventHandler(this.menuItemSave_Click); + // + // menuItemSaveAs + // + this.menuItemSaveAs.Index = 3; + this.menuItemSaveAs.Text = "Save As..."; + this.menuItemSaveAs.Click += new System.EventHandler(this.menuItemSaveAs_Click); + // + // menuItem6 + // + this.menuItem6.Index = 4; + this.menuItem6.Text = "-"; + // + // menuItemMakePdb + // + this.menuItemMakePdb.Index = 5; + this.menuItemMakePdb.Text = "Make Pdb..."; + this.menuItemMakePdb.Click += new System.EventHandler(this.menuItemMakePdb_Click); + // + // menuItem8 + // + this.menuItem8.Index = 6; + this.menuItem8.Text = "-"; + // + // menuItem7 + // + this.menuItem7.Index = 7; + this.menuItem7.Text = "Exit"; + // + // menuItem2 + // + this.menuItem2.Index = 1; + this.menuItem2.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.menuItemCopy, + this.menuItemPaste, + this.menuItemClear, + this.menuItem3, + this.menuItemCheckAll, + this.menuItemUncheckAll}); + this.menuItem2.Text = "Edit"; + // + // menuItemCopy + // + this.menuItemCopy.Index = 0; + this.menuItemCopy.Shortcut = System.Windows.Forms.Shortcut.CtrlC; + this.menuItemCopy.Text = "Copy"; + this.menuItemCopy.Click += new System.EventHandler(this.menuItemCopy_Click); + // + // menuItemPaste + // + this.menuItemPaste.Index = 1; + this.menuItemPaste.Shortcut = System.Windows.Forms.Shortcut.CtrlV; + this.menuItemPaste.Text = "Paste"; + this.menuItemPaste.Click += new System.EventHandler(this.menuItemPaste_Click); + // + // menuItemClear + // + this.menuItemClear.Index = 2; + this.menuItemClear.Shortcut = System.Windows.Forms.Shortcut.Del; + this.menuItemClear.Text = "Clear"; + this.menuItemClear.Click += new System.EventHandler(this.menuItemClear_Click); + // + // menuItem3 + // + this.menuItem3.Index = 3; + this.menuItem3.Text = "-"; + // + // menuItemCheckAll + // + this.menuItemCheckAll.Index = 4; + this.menuItemCheckAll.Text = "Check All"; + this.menuItemCheckAll.Click += new System.EventHandler(this.menuItemCheckAll_Click); + // + // menuItemUncheckAll + // + this.menuItemUncheckAll.Index = 5; + this.menuItemUncheckAll.Text = "Uncheck All"; + this.menuItemUncheckAll.Click += new System.EventHandler(this.menuItemUncheckAll_Click); + // + // menuItem4 + // + this.menuItem4.Index = 2; + this.menuItem4.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.menuItemCheckPdbSize, + this.menuItemTestCom2}); + this.menuItem4.Text = "Misc"; + // + // menuItemCheckPdbSize + // + this.menuItemCheckPdbSize.Index = 0; + this.menuItemCheckPdbSize.Text = "Check Pdb Size"; + this.menuItemCheckPdbSize.Click += new System.EventHandler(this.menuItemCheckPdbSize_Click); + // + // menuItemTestCom2 + // + this.menuItemTestCom2.Index = 1; + this.menuItemTestCom2.Text = "Test Com2"; + this.menuItemTestCom2.Visible = false; + this.menuItemTestCom2.Click += new System.EventHandler(this.menuItemTestCom2_Click); + // + // panelMain + // + this.panelMain.Controls.AddRange(new System.Windows.Forms.Control[] { + this.paneSfxSoundsParent}); + this.panelMain.Dock = System.Windows.Forms.DockStyle.Fill; + this.panelMain.Name = "panelMain"; + this.panelMain.Size = new System.Drawing.Size(768, 480); + this.panelMain.TabIndex = 1; + // + // paneSfxSoundsParent + // + this.paneSfxSoundsParent.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.paneSfxSoundsParent.Controls.AddRange(new System.Windows.Forms.Control[] { + this.panelSfx, + this.splitter1, + this.panelSounds}); + this.paneSfxSoundsParent.Dock = System.Windows.Forms.DockStyle.Fill; + this.paneSfxSoundsParent.Name = "paneSfxSoundsParent"; + this.paneSfxSoundsParent.Size = new System.Drawing.Size(768, 480); + this.paneSfxSoundsParent.TabIndex = 1; + // + // panelSfx + // + this.panelSfx.Controls.AddRange(new System.Windows.Forms.Control[] { + this.listViewSfx, + this.splitter2, + this.panelSfxProperties, + this.label1}); + this.panelSfx.Dock = System.Windows.Forms.DockStyle.Fill; + this.panelSfx.Name = "panelSfx"; + this.panelSfx.Size = new System.Drawing.Size(610, 476); + this.panelSfx.TabIndex = 0; + // + // listViewSfx + // + this.listViewSfx.Activation = System.Windows.Forms.ItemActivation.OneClick; + this.listViewSfx.AllowDrop = true; + this.listViewSfx.AutoArrange = false; + this.listViewSfx.BackColor = System.Drawing.Color.White; + this.listViewSfx.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.listViewSfx.CheckBoxes = true; + this.listViewSfx.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.columnHeaderSfx, + this.columnHeaderSound, + this.columnHeaderChannel, + this.columnHeaderPriority, + this.columnHeaderComment}); + this.listViewSfx.Dock = System.Windows.Forms.DockStyle.Fill; + this.listViewSfx.FullRowSelect = true; + this.listViewSfx.GridLines = true; + this.listViewSfx.LabelWrap = false; + this.listViewSfx.Location = new System.Drawing.Point(0, 23); + this.listViewSfx.MultiSelect = false; + this.listViewSfx.Name = "listViewSfx"; + this.listViewSfx.Size = new System.Drawing.Size(610, 350); + this.listViewSfx.TabIndex = 3; + this.listViewSfx.View = System.Windows.Forms.View.Details; + this.listViewSfx.MouseDown += new System.Windows.Forms.MouseEventHandler(this.listViewSfx_MouseDown); + this.listViewSfx.ItemActivate += new System.EventHandler(this.listViewSfx_ItemActivate); + this.listViewSfx.MouseUp += new System.Windows.Forms.MouseEventHandler(this.listViewSfx_MouseUp); + this.listViewSfx.DragOver += new System.Windows.Forms.DragEventHandler(this.listViewSfx_DragOver); + this.listViewSfx.DragDrop += new System.Windows.Forms.DragEventHandler(this.listViewSfx_DragDrop); + this.listViewSfx.Leave += new System.EventHandler(this.listViewSfx_Leave); + this.listViewSfx.DragEnter += new System.Windows.Forms.DragEventHandler(this.listViewSfx_DragEnter); + this.listViewSfx.ColumnClick += new System.Windows.Forms.ColumnClickEventHandler(this.listViewSfx_ColumnClick); + this.listViewSfx.ItemDrag += new System.Windows.Forms.ItemDragEventHandler(this.listViewSfx_ItemDrag); + this.listViewSfx.SelectedIndexChanged += new System.EventHandler(this.listViewSfx_SelectedIndexChanged); + // + // columnHeaderSfx + // + this.columnHeaderSfx.Text = "Sound Effect"; + this.columnHeaderSfx.Width = 210; + // + // columnHeaderSound + // + this.columnHeaderSound.Text = "File"; + this.columnHeaderSound.Width = 100; + // + // columnHeaderChannel + // + this.columnHeaderChannel.Text = "Channel"; + this.columnHeaderChannel.Width = 0; + // + // columnHeaderPriority + // + this.columnHeaderPriority.Text = "Priority"; + this.columnHeaderPriority.Width = 90; + // + // columnHeaderComment + // + this.columnHeaderComment.Text = "Comment"; + this.columnHeaderComment.Width = 210; + // + // splitter2 + // + this.splitter2.BackColor = System.Drawing.Color.FromArgb(((System.Byte)(128)), ((System.Byte)(64)), ((System.Byte)(64))); + this.splitter2.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.splitter2.Dock = System.Windows.Forms.DockStyle.Bottom; + this.splitter2.Location = new System.Drawing.Point(0, 373); + this.splitter2.Name = "splitter2"; + this.splitter2.Size = new System.Drawing.Size(610, 3); + this.splitter2.TabIndex = 6; + this.splitter2.TabStop = false; + // + // panelSfxProperties + // + this.panelSfxProperties.BackColor = System.Drawing.Color.FromArgb(((System.Byte)(247)), ((System.Byte)(223)), ((System.Byte)(225))); + this.panelSfxProperties.Controls.AddRange(new System.Windows.Forms.Control[] { + this.textBoxPropertiesComment, + this.label8, + this.comboBoxPropertiesPriority, + this.label6, + this.textBoxPropertiesSound, + this.label7, + this.labelPropertiesSfx, + this.label3}); + this.panelSfxProperties.Dock = System.Windows.Forms.DockStyle.Bottom; + this.panelSfxProperties.Location = new System.Drawing.Point(0, 376); + this.panelSfxProperties.Name = "panelSfxProperties"; + this.panelSfxProperties.Size = new System.Drawing.Size(610, 100); + this.panelSfxProperties.TabIndex = 5; + // + // textBoxPropertiesComment + // + this.textBoxPropertiesComment.Location = new System.Drawing.Point(280, 23); + this.textBoxPropertiesComment.Multiline = true; + this.textBoxPropertiesComment.Name = "textBoxPropertiesComment"; + this.textBoxPropertiesComment.Size = new System.Drawing.Size(288, 68); + this.textBoxPropertiesComment.TabIndex = 11; + this.textBoxPropertiesComment.Text = ""; + this.textBoxPropertiesComment.TextChanged += new System.EventHandler(this.textBoxPropertiesComment_TextChanged); + // + // label8 + // + this.label8.Location = new System.Drawing.Point(277, 5); + this.label8.Name = "label8"; + this.label8.Size = new System.Drawing.Size(128, 13); + this.label8.TabIndex = 10; + this.label8.Text = "Comment:"; + // + // comboBoxPropertiesPriority + // + this.comboBoxPropertiesPriority.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comboBoxPropertiesPriority.Location = new System.Drawing.Point(56, 53); + this.comboBoxPropertiesPriority.Name = "comboBoxPropertiesPriority"; + this.comboBoxPropertiesPriority.Size = new System.Drawing.Size(112, 21); + this.comboBoxPropertiesPriority.TabIndex = 9; + this.comboBoxPropertiesPriority.SelectedIndexChanged += new System.EventHandler(this.comboBoxPropertiesPriority_SelectedIndexChanged); + // + // label6 + // + this.label6.Location = new System.Drawing.Point(16, 56); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(48, 16); + this.label6.TabIndex = 8; + this.label6.Text = "Priority:"; + // + // textBoxPropertiesSound + // + this.textBoxPropertiesSound.Location = new System.Drawing.Point(56, 25); + this.textBoxPropertiesSound.Name = "textBoxPropertiesSound"; + this.textBoxPropertiesSound.Size = new System.Drawing.Size(198, 20); + this.textBoxPropertiesSound.TabIndex = 5; + this.textBoxPropertiesSound.Text = ""; + this.textBoxPropertiesSound.TextChanged += new System.EventHandler(this.textBoxPropertiesSound_TextChanged); + // + // label7 + // + this.label7.Location = new System.Drawing.Point(31, 27); + this.label7.Name = "label7"; + this.label7.Size = new System.Drawing.Size(32, 16); + this.label7.TabIndex = 4; + this.label7.Text = "File:"; + // + // labelPropertiesSfx + // + this.labelPropertiesSfx.Location = new System.Drawing.Point(57, 5); + this.labelPropertiesSfx.Name = "labelPropertiesSfx"; + this.labelPropertiesSfx.Size = new System.Drawing.Size(195, 15); + this.labelPropertiesSfx.TabIndex = 1; + this.labelPropertiesSfx.Text = "LightTankVehicleFire"; + // + // label3 + // + this.label3.Location = new System.Drawing.Point(32, 5); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(26, 16); + this.label3.TabIndex = 0; + this.label3.Text = "Sfx:"; + // + // label1 + // + this.label1.BackColor = System.Drawing.Color.FromArgb(((System.Byte)(185)), ((System.Byte)(190)), ((System.Byte)(240))); + this.label1.Dock = System.Windows.Forms.DockStyle.Top; + this.label1.Font = new System.Drawing.Font("Times New Roman", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0))); + this.label1.ForeColor = System.Drawing.Color.Black; + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(610, 23); + this.label1.TabIndex = 2; + this.label1.Text = "Sound Effects"; + this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // splitter1 + // + this.splitter1.BackColor = System.Drawing.Color.FromArgb(((System.Byte)(128)), ((System.Byte)(64)), ((System.Byte)(64))); + this.splitter1.Dock = System.Windows.Forms.DockStyle.Right; + this.splitter1.Location = new System.Drawing.Point(610, 0); + this.splitter1.Name = "splitter1"; + this.splitter1.Size = new System.Drawing.Size(4, 476); + this.splitter1.TabIndex = 1; + this.splitter1.TabStop = false; + // + // panelSounds + // + this.panelSounds.Controls.AddRange(new System.Windows.Forms.Control[] { + this.listBoxSounds, + this.textBoxSoundsDir, + this.label2}); + this.panelSounds.Dock = System.Windows.Forms.DockStyle.Right; + this.panelSounds.Location = new System.Drawing.Point(614, 0); + this.panelSounds.Name = "panelSounds"; + this.panelSounds.Size = new System.Drawing.Size(150, 476); + this.panelSounds.TabIndex = 2; + // + // listBoxSounds + // + this.listBoxSounds.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.listBoxSounds.Dock = System.Windows.Forms.DockStyle.Fill; + this.listBoxSounds.IntegralHeight = false; + this.listBoxSounds.Location = new System.Drawing.Point(0, 43); + this.listBoxSounds.Name = "listBoxSounds"; + this.listBoxSounds.Size = new System.Drawing.Size(150, 433); + this.listBoxSounds.TabIndex = 3; + this.listBoxSounds.MouseDown += new System.Windows.Forms.MouseEventHandler(this.listBoxSounds_MouseDown); + this.listBoxSounds.SelectedIndexChanged += new System.EventHandler(this.listBoxSounds_SelectedIndexChanged); + // + // textBoxSoundsDir + // + this.textBoxSoundsDir.Dock = System.Windows.Forms.DockStyle.Top; + this.textBoxSoundsDir.Location = new System.Drawing.Point(0, 23); + this.textBoxSoundsDir.Name = "textBoxSoundsDir"; + this.textBoxSoundsDir.Size = new System.Drawing.Size(150, 20); + this.textBoxSoundsDir.TabIndex = 2; + this.textBoxSoundsDir.Text = ""; + this.textBoxSoundsDir.KeyDown += new System.Windows.Forms.KeyEventHandler(this.textBoxSoundsDir_KeyDown); + // + // label2 + // + this.label2.BackColor = System.Drawing.Color.Navy; + this.label2.Dock = System.Windows.Forms.DockStyle.Top; + this.label2.Font = new System.Drawing.Font("Times New Roman", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0))); + this.label2.ForeColor = System.Drawing.Color.White; + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(150, 23); + this.label2.TabIndex = 1; + this.label2.Text = "Library"; + this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // statusBar1 + // + this.statusBar1.Location = new System.Drawing.Point(0, 480); + this.statusBar1.Name = "statusBar1"; + this.statusBar1.Size = new System.Drawing.Size(768, 22); + this.statusBar1.TabIndex = 2; + this.statusBar1.Text = "statusBar1"; + // + // contextMenuListBox + // + this.contextMenuListBox.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.menuItemPlayOnPalm, + this.menuItem5, + this.menuItemCom1, + this.menuItemCom2}); + // + // menuItemPlayOnPalm + // + this.menuItemPlayOnPalm.Index = 0; + this.menuItemPlayOnPalm.Text = "Play on Palm"; + this.menuItemPlayOnPalm.Click += new System.EventHandler(this.menuItemPlayOnPalm_Click); + // + // menuItem5 + // + this.menuItem5.Index = 1; + this.menuItem5.Text = "-"; + // + // menuItemCom1 + // + this.menuItemCom1.Index = 2; + this.menuItemCom1.Text = "Com1"; + this.menuItemCom1.Click += new System.EventHandler(this.menuItemCom1_Click); + // + // menuItemCom2 + // + this.menuItemCom2.Checked = true; + this.menuItemCom2.Index = 3; + this.menuItemCom2.Text = "Com2"; + this.menuItemCom2.Click += new System.EventHandler(this.menuItemCom2_Click); + // + // Form1 + // + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.ClientSize = new System.Drawing.Size(768, 502); + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.panelMain, + this.statusBar1}); + this.Menu = this.mainMenu1; + this.Name = "Form1"; + this.Text = "Sound Scheme Editor"; + this.Closing += new System.ComponentModel.CancelEventHandler(this.Form1_Closing); + this.panelMain.ResumeLayout(false); + this.paneSfxSoundsParent.ResumeLayout(false); + this.panelSfx.ResumeLayout(false); + this.panelSfxProperties.ResumeLayout(false); + this.panelSounds.ResumeLayout(false); + this.ResumeLayout(false); + + } + #endregion + + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + Application.Run(new Form1()); + } + + void NewScheme() { + ListViewItem[] aitem = new ListViewItem[m_alsNames.Count]; + for (int iItem = 0; iItem < m_alsNames.Count; iItem++) { + string strT = ((StringCollection)m_alsNames[iItem])[0]; + ListViewItem item = new ListViewItem(new System.Windows.Forms.ListViewItem.ListViewSubItem[] { + new ListViewItem.ListViewSubItem(null, strT, System.Drawing.SystemColors.WindowText, System.Drawing.SystemColors.Window, new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)))), + new ListViewItem.ListViewSubItem(null, ""), + new ListViewItem.ListViewSubItem(null, "0"), + new ListViewItem.ListViewSubItem(null, "Unknown"), + new ListViewItem.ListViewSubItem(null, "")}, -1); + item.Checked = true; + if (!(bool)m_alsSfxEnabled[iItem]) + item.ForeColor = Color.Salmon; + //item.BackColor = Color.AliceBlue; + aitem[iItem] = item; + } + listViewSfx.Items.Clear(); + listViewSfx.Items.AddRange(aitem); + } + + private void textBoxSoundsDir_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e) { + if (e.KeyCode == Keys.Enter) { + SetSoundsDir(textBoxSoundsDir.Text); + } + } + + void SetSoundsDir(string strDir) { + m_fFillingListBox = true; + try { + textBoxSoundsDir.Text = strDir; + ArrayList alsFiles = new ArrayList(); + alsFiles.AddRange(Directory.GetFiles(strDir, "*.wav")); + alsFiles.AddRange(Directory.GetFiles(strDir, "*.snd")); + ArrayList alsItems = new ArrayList(); + string strPath = Path.GetFullPath(textBoxSoundsDir.Text); + foreach (string strFileFull in alsFiles) { + string strFile = Path.GetFileName(strFileFull); + Pcm pcm = new Pcm(strPath + "\\" + strFile); + pcm.ConvertTo8Bit(); + alsItems.Add(strFile + ", " + pcm.Data8Bit.Length / 2 + " bytes"); + } + listBoxSounds.DataSource = (string[])alsItems.ToArray(typeof(string)); + } catch { + MessageBox.Show("Invalid directory!"); + } + m_fFillingListBox = false; + } + + private void listBoxSounds_SelectedIndexChanged(object sender, System.EventArgs e) { + if (Control.MouseButtons == MouseButtons.None) + PlaySound(GetListboxItemFileName(listBoxSounds.SelectedIndex)); + } + + string GetListboxItemFileName(int iItem) { + string strItem = (string)listBoxSounds.Items[iItem]; + int ichComma = strItem.IndexOf(','); + return strItem.Remove(ichComma, strItem.Length - ichComma); + } + + void PlaySound(string strFile) { + try { + if (!m_fFillingListBox && m_fPlaySound) { + string strPath = Path.GetFullPath(textBoxSoundsDir.Text); + Pcm pcm = new Pcm(strPath + "\\" + strFile); + pcm.Play(); + } + } catch { + } + } + + private void Form1_Closing(object sender, System.ComponentModel.CancelEventArgs e) { + SaveSettings(); + } + + void SaveSettings() { + // Save settings in ini. + + Ini ini = new Ini(); + Ini.Section secGeneral = new Ini.Section("General"); + secGeneral.Add(new Ini.Property("SoundsDir", Path.GetFullPath(textBoxSoundsDir.Text))); + secGeneral.Add(new Ini.Property("Sfx.h", m_strSfxH)); + ini.Add(secGeneral); + + // Place in directory where .exe resides + + ini.Save(Application.ExecutablePath.Replace(".exe", ".ini")); + } + + bool LoadSettings() { + Ini ini; + try { + ini = new Ini(Application.ExecutablePath.Replace(".exe", ".ini")); + } catch { + ini = null; + } + + // Sounds directory + + string strDirSounds = (ini != null) ? ini["General"]["SoundsDir"].Value : Directory.GetCurrentDirectory(); + SetSoundsDir(strDirSounds); + + // Sfx names + + string strSfxH = (ini != null && ini["General"]["Sfx.H"] != null) ? ini["General"]["Sfx.h"].Value : null; + while (true) { + if (strSfxH == null) { + // Get filename + OpenFileDialog frmOpen = new OpenFileDialog(); + frmOpen.Filter = "Include Files (*.h)|*.h"; + frmOpen.Title = "Open SoundEffects.h"; + if (frmOpen.ShowDialog() == DialogResult.Cancel) + return false; + strSfxH = frmOpen.FileName; + } + if (ParseNames(strSfxH)) + break; + strSfxH = null; + } + m_strSfxH = strSfxH; + + return true; + } + + private void menuItemPlayOnPalm_Click(object sender, System.EventArgs e) { + int iItem = listBoxSounds.SelectedIndex; + if (iItem == -1) + return; + string strFile = GetListboxItemFileName(iItem); + try { + string strPath = Path.GetFullPath(textBoxSoundsDir.Text); + Pcm pcm = new Pcm(strPath + "\\" + strFile); + PlayOnPalm(strFile, pcm.GetSndEncoding()); + } catch { + MessageBox.Show("Couldn't load " + strFile); + } + } + + void PlayOnPalm(string strFile, byte[] abPcm) { + try { + SerialStream stm = new SerialStream("com" + m_nComPort + ":", FileAccess.Write); + stm.SetPortSettings(19200, 8, SerialStream.StopBits.One, SerialStream.Parity.None, SerialStream.FlowControl.Hardware); + stm.WriteByte((byte)'s'); + stm.WriteByte((byte)'c'); + stm.WriteByte((byte)'o'); + stm.WriteByte((byte)'t'); + stm.WriteByte((byte)'t'); + stm.WriteByte((byte)'l'); + stm.WriteByte((byte)'u'); + stm.WriteByte(0); + foreach (char ch in strFile) { + stm.WriteByte((byte)ch); + } + stm.WriteByte(0); + stm.WriteByte((byte)((abPcm.Length >> 8) & 0xff)); + stm.WriteByte((byte)(abPcm.Length & 0xff)); + foreach (byte b in abPcm) + stm.WriteByte(b); + stm.Close(); + } catch { + MessageBox.Show("Error sending data to Palm. Hotsync closed?"); + } + } + + private void listBoxSounds_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e) { + for (int iItem = 0; iItem < listBoxSounds.Items.Count; iItem++) { + Rectangle rcItem = listBoxSounds.GetItemRectangle(iItem); + if (rcItem.Contains(e.X, e.Y)) { + string strFile = GetListboxItemFileName(iItem); + switch (e.Button) { + case MouseButtons.Left: + DragDropEffects effects = listBoxSounds.DoDragDrop(strFile, DragDropEffects.Link); + if (effects == DragDropEffects.None) { + // If the mouse came up at e.X, e.Y, play the sound since this is + // just a mouse click + + Point ptNew = listBoxSounds.PointToClient(Control.MousePosition); + if (ptNew.X == e.X && ptNew.Y == e.Y) + PlaySound(strFile); + } + return; + + case MouseButtons.Right: + listBoxSounds.SelectedIndex = iItem; + contextMenuListBox.Show(listBoxSounds, new Point(e.X, e.Y)); + return; + } + } + } + } + + private void listViewSfx_DragEnter(object sender, System.Windows.Forms.DragEventArgs e) { + if (e.Data.GetDataPresent(DataFormats.Text)) { + e.Effect = DragDropEffects.Link; + } else { + e.Effect = DragDropEffects.None; + } + } + + private void listViewSfx_DragDrop(object sender, System.Windows.Forms.DragEventArgs e) { + // Which item are we over? + + Point ptClient = listViewSfx.PointToClient(new Point(e.X, e.Y)); + ListViewItem itemOver = listViewSfx.GetItemAt(ptClient.X, ptClient.Y); + if (itemOver == null) + return; + + // Dragging another item or a string? + + if (e.Data.GetDataPresent(typeof(ListViewItem))) { + ListViewItem item = (ListViewItem)e.Data.GetData(typeof(ListViewItem)); + for (int iSubItem = 1; iSubItem < 5; iSubItem++) + itemOver.SubItems[iSubItem] = item.SubItems[iSubItem]; + } else { + string strFile = e.Data.GetData(DataFormats.Text).ToString(); + itemOver.SubItems[1].Text = strFile; + } + + // Select row, play sound + + SelectListViewSfxItem(itemOver); + UpdateProperties(itemOver); + PlaySound(itemOver.SubItems[1].Text); + m_fPlaySound = false; + } + + private void listViewSfx_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e) { +#if false + // Filter out our WM_LBUTTONDOWN selection hack + + Point ptClient = listViewSfx.PointToClient(Control.MousePosition); + if (ptClient.X != e.X || ptClient.Y != e.Y) + return; +#endif + +#if false + // For dragging sfx + + ListViewItem item = listViewSfx.GetItemAt(e.X, e.Y); + DragDropEffects effects = DragDropEffects.None; + if (item != null) { + UpdateProperties(item); + effects = listViewSfx.DoDragDrop(item, DragDropEffects.Copy); + } + Point ptT = listViewSfx.PointToClient(Control.MousePosition); + item = listViewSfx.GetItemAt(ptT.X, ptT.Y); + if (item != null) { + SelectListViewSfxItem(item); + UpdateProperties(item); + } + if (effects == DragDropEffects.None) + m_fPlaySound = false; +#endif + } + + private void listViewSfx_ItemDrag(object sender, System.Windows.Forms.ItemDragEventArgs e) { + // For dragging sfx + + ListViewItem item = (ListViewItem)e.Item; + UpdateProperties(item); + listViewSfx.DoDragDrop(item, DragDropEffects.Copy); + Point ptT = listViewSfx.PointToClient(Control.MousePosition); + item = listViewSfx.GetItemAt(ptT.X, ptT.Y); + if (item != null) { + SelectListViewSfxItem(item); + UpdateProperties(item); + } + } + + [DllImportAttribute("user32.dll")] + private static extern void PostMessageW(IntPtr hwnd, uint wm, uint wp, uint lp); + + void SelectListViewSfxItem(ListViewItem item) { +#if false + ListView.SelectedListViewItemCollection coll = new ListView.SelectedListViewItemCollection(listViewSfx); + coll.Clear(); + item.Selected = true; +#else + //Rectangle rc = listViewSfx.GetItemRect(item.Index); + //Point pt = new Point(rc.Left + (rc.Right - rc.Left) / 2, rc.Top + (rc.Bottom - rc.Top) / 2); + //uint wp = 0; + //uint lp = (uint)((ushort)pt.X + (uint)(pt.Y << 16)); + //PostMessageW(listViewSfx.Handle, 0x201, wp, lp); +#endif + } + + private void listViewSfx_DragOver(object sender, System.Windows.Forms.DragEventArgs e) { + Point ptClient = listViewSfx.PointToClient(new Point(e.X, e.Y)); + ListViewItem item = listViewSfx.GetItemAt(ptClient.X, ptClient.Y); + if (item == null || !(bool)m_alsSfxEnabled[item.Index]) { + e.Effect = DragDropEffects.None; + return; + } + e.Effect = DragDropEffects.Link | DragDropEffects.Copy; + } + + void UpdateProperties(ListViewItem item) { + // Save the displayed item + +#if false + if (m_itemDisplayed != null) { + m_itemDisplayed.SubItems[1].Text = textBoxPropertiesSound.Text; + m_itemDisplayed.SubItems[2].Text = comboBoxPropertiesChannel.SelectedIndex.ToString(); + m_itemDisplayed.SubItems[3].Text = comboBoxPropertiesPriority.SelectedIndex.ToString(); + m_itemDisplayed.SubItems[4].Text = textBoxPropertiesComment.Text; + } +#endif + + // Display the new item + + m_itemDisplayed = item; + if (m_itemDisplayed != null) { + labelPropertiesSfx.Text = m_itemDisplayed.SubItems[0].Text; + textBoxPropertiesSound.Text = m_itemDisplayed.SubItems[1].Text; + //comboBoxPropertiesChannel.SelectedIndex = int.Parse(m_itemDisplayed.SubItems[2].Text); + comboBoxPropertiesPriority.Text = m_itemDisplayed.SubItems[3].Text; + textBoxPropertiesComment.Text = m_itemDisplayed.SubItems[4].Text; + } else { + labelPropertiesSfx.Text = ""; + textBoxPropertiesSound.Text = ""; + //comboBoxPropertiesChannel.SelectedIndex = -1; + comboBoxPropertiesPriority.SelectedIndex = -1; + textBoxPropertiesComment.Text = ""; + } + } + + private void listViewSfx_SelectedIndexChanged(object sender, System.EventArgs e) { + if (listViewSfx.SelectedItems.Count == 0) { + UpdateProperties(null); + } else { + UpdateProperties(listViewSfx.SelectedItems[0]); + } + } + + private void textBoxPropertiesComment_TextChanged(object sender, System.EventArgs e) { + if (m_itemDisplayed != null) + m_itemDisplayed.SubItems[4].Text = textBoxPropertiesComment.Text; + } + + private void textBoxPropertiesSound_TextChanged(object sender, System.EventArgs e) { + if (m_itemDisplayed != null) + m_itemDisplayed.SubItems[1].Text = textBoxPropertiesSound.Text; + } + +#if false + private void comboBoxPropertiesChannel_SelectedIndexChanged(object sender, System.EventArgs e) { + if (m_itemDisplayed != null) + m_itemDisplayed.SubItems[2].Text = comboBoxPropertiesChannel.SelectedIndex.ToString(); + } +#endif + + private void comboBoxPropertiesPriority_SelectedIndexChanged(object sender, System.EventArgs e) { + if (m_itemDisplayed != null) + m_itemDisplayed.SubItems[3].Text = comboBoxPropertiesPriority.Text; + } + + private void listViewSfx_ItemActivate(object sender, System.EventArgs e) { +#if false + string strSound = listViewSfx.SelectedItems[0].SubItems[1].Text; + PlaySound(strSound); +#endif + } + + private void listViewSfx_Leave(object sender, System.EventArgs e) { + //UpdateProperties(null); + } + + public int Compare(object obj1, object obj2) { + ListViewItem item1 = (ListViewItem)obj1; + ListViewItem item2 = (ListViewItem)obj2; + int n = m_comparer.Compare(item1.SubItems[m_iColumnSort].Text, item2.SubItems[m_iColumnSort].Text); + return m_fAscending ? n : -n; + } + + private void listViewSfx_ColumnClick(object sender, System.Windows.Forms.ColumnClickEventArgs e) { + if (m_iColumnSort == e.Column) { + m_fAscending = !m_fAscending; + } else { + m_fAscending = true; + m_iColumnSort = e.Column; + } + + if (listViewSfx.ListViewItemSorter == null) { + // Setting the property causes Sort() to be called. Special. + + listViewSfx.ListViewItemSorter = this; + } else { + listViewSfx.Sort(); + } + } + + private void listViewSfx_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e) { + ListViewItem itemOver = listViewSfx.GetItemAt(e.X, e.Y); + if (itemOver != null) { + UpdateProperties(itemOver); + PlaySound(itemOver.SubItems[1].Text); + } + } + + private void menuItemSaveAs_Click(object sender, System.EventArgs e) { + SaveSfx(null); + } + + private void menuItemSave_Click(object sender, System.EventArgs e) { + SaveSfx(m_strSfxFile); + } + + private void menuItemOpen_Click(object sender, System.EventArgs e) { + LoadSfx(); + } + + private void menuItemNew_Click(object sender, System.EventArgs e) { + NewScheme(); + } + + void SaveSfx(string strFile) { + if (strFile == null) { + SaveFileDialog frmSave = new SaveFileDialog(); + frmSave.DefaultExt = "sfx"; + frmSave.Filter = "Sound Effect Files (*.sfx)|*.sfx"; + frmSave.Title = "Save As"; + frmSave.FileName = m_strSfxFile; + if (frmSave.ShowDialog() == DialogResult.Cancel) + return; + strFile = frmSave.FileName; + } + + TextWriter tw = new StreamWriter(strFile); + for (int isfx = 0; isfx < m_alsNames.Count; isfx++) { + ListViewItem item = listViewSfx.Items[isfx]; + if (item.Checked) { + tw.WriteLine("checked"); + } else { + tw.WriteLine("unchecked"); + } + for (int iSubItem = 0; iSubItem < 5; iSubItem++) { + string strT = item.SubItems[iSubItem].Text; + if (strT == null) + strT = ""; + tw.WriteLine(strT); + } + } + tw.Close(); + m_strSfxFile = strFile; + } + + void LoadSfx() { + OpenFileDialog frmOpen = new OpenFileDialog(); + frmOpen.Filter = "Sound Effect Files (*.sfx)|*.sfx"; + frmOpen.Title = "Open"; + if (frmOpen.ShowDialog() == DialogResult.Cancel) + return; + string strFile = frmOpen.FileName; + + try { + NewScheme(); + + // Add soundeffects.h entries into list + + for (int iName = 0; iName < m_alsNames.Count; iName++) { + StringCollection strc = (StringCollection)m_alsNames[iName]; + listViewSfx.Items[iName].Checked = true; + listViewSfx.Items[iName].SubItems[0].Text = strc[0]; + listViewSfx.Items[iName].SubItems[1].Text = ""; + listViewSfx.Items[iName].SubItems[2].Text = "0"; + listViewSfx.Items[iName].SubItems[3].Text = "Unknown"; + listViewSfx.Items[iName].SubItems[4].Text = ""; + } + + // Now load sfx into this list + + TextReader tr = new StreamReader(strFile); + while (true) { + // checked or unchecked? + + string strChecked = tr.ReadLine(); + bool fChecked; + if (strChecked == "checked") { + fChecked = true; + } else { + fChecked = false; + } + + // Look up sound effect name. Match against deprecated names too, and if found + // map to new names + + bool fFound = false; + string strName = tr.ReadLine(); + if (strName == null) { + tr.Close(); + break; + } + + int iName; + for (iName = 0; iName < m_alsNames.Count; iName++) { + StringCollection strc = (StringCollection)m_alsNames[iName]; + if (strc.IndexOf(strName) >= 0) { + strName = strc[0]; + fFound = true; + break; + } + } + if (!fFound) { + MessageBox.Show("Unknown sound effect: " + strName); + tr.ReadLine(); + tr.ReadLine(); + tr.ReadLine(); + tr.ReadLine(); + continue; + } + + // Add effect into this spot + + listViewSfx.Items[iName].Checked = fChecked; + listViewSfx.Items[iName].SubItems[0].Text = strName; + listViewSfx.Items[iName].SubItems[1].Text = tr.ReadLine(); + listViewSfx.Items[iName].SubItems[2].Text = tr.ReadLine(); + string strPriority = tr.ReadLine(); + if (m_strcPriorities.IndexOf(strPriority) == -1) + strPriority = "Unknown"; + listViewSfx.Items[iName].SubItems[3].Text = strPriority; + listViewSfx.Items[iName].SubItems[4].Text = tr.ReadLine(); + } + } catch { + MessageBox.Show("Error reading from " + strFile); + } + m_strSfxFile = strFile; + } + + void SavePdb(string strPdbFile) { + // Make a list of unique sound files + + PdbPacker pdbp = new PdbPacker(); + int cSfx = m_alsNames.Count; + StringCollection strcUniqueSounds = new StringCollection(); + ArrayList alsPcm = new ArrayList(); + for (int iSfx = 0; iSfx < cSfx; iSfx++) { + if (!listViewSfx.Items[iSfx].Checked) + continue; + if (!(bool)m_alsSfxEnabled[iSfx]) + continue; + string strFile = listViewSfx.Items[iSfx].SubItems[1].Text; + if (strFile == null) + continue; + strFile.Trim(); + if (strFile.Length == 0) + continue; + int istr = strcUniqueSounds.IndexOf(strFile); + if (istr == -1) + istr = strcUniqueSounds.Add(strFile); + } + + // Serialize names out + + ArrayList alsStringOffsets = new ArrayList(); + BinaryWriter bwtr = new BinaryWriter(new MemoryStream()); + bwtr.Write(Misc.SwapUShort((ushort)strcUniqueSounds.Count)); + for (int iSound = 0; iSound < strcUniqueSounds.Count; iSound++) { + alsStringOffsets.Add(bwtr.BaseStream.Position); + string strFile = Path.ChangeExtension(strcUniqueSounds[iSound], ".snd"); + char[] sz = strFile.ToCharArray(); + bwtr.Write(sz); + bwtr.Write((byte)0); + } + byte[] abSoundFiles = new byte[bwtr.BaseStream.Length]; + bwtr.BaseStream.Seek(0, SeekOrigin.Begin); + bwtr.BaseStream.Read(abSoundFiles, 0, (int)bwtr.BaseStream.Length); + bwtr.Close(); + + // soundfiles file + + PdbPacker.File fileSounds = new PdbPacker.File("soundfiles", abSoundFiles); + pdbp.Add(fileSounds); + + // Now serialize the sfx entries in the order of the names + + bwtr = new BinaryWriter(new MemoryStream()); + for (int iName = 0; iName < m_alsNames.Count; iName++) { + // Need to find the entry in listViewSfx for this name since the persist + // order needs to match soundeffects.h. + + string strName = ((StringCollection)m_alsNames[iName])[0]; + int iSfx; + bool fFound = false; + for (iSfx = 0; iSfx < cSfx; iSfx++) { + if (strName == listViewSfx.Items[iSfx].SubItems[0].Text) { + fFound = true; + break; + } + } + if (!fFound) + throw new Exception("Internal error"); + + string strFile = listViewSfx.Items[iSfx].SubItems[1].Text; + if (!listViewSfx.Items[iSfx].Checked) + strFile = null; + if (!(bool)m_alsSfxEnabled[iSfx]) + strFile = null; + if (strFile == null) { + bwtr.Write((byte)0xff); + } else { + strFile.Trim(); + if (strFile.Length == 0) { + bwtr.Write((byte)0xff); + } else { + bwtr.Write((byte)strcUniqueSounds.IndexOf(strFile)); + } + } + bwtr.Write((byte)0); // bwtr.Write(byte.Parse(listViewSfx.Items[iSfx].SubItems[2].Text)); + int nPriority = m_strcPriorities.IndexOf(listViewSfx.Items[iSfx].SubItems[3].Text); + if (nPriority < 0) { + MessageBox.Show("Warning: " + listViewSfx.Items[iSfx].SubItems[0].Text + " has an unfamiliar priority."); + } + bwtr.Write((byte)nPriority); + } + byte[] abSfxEntries = new byte[bwtr.BaseStream.Length]; + bwtr.BaseStream.Seek(0, SeekOrigin.Begin); + bwtr.BaseStream.Read(abSfxEntries, 0, (int)bwtr.BaseStream.Length); + bwtr.Close(); + + PdbPacker.File fileSfxEntries = new PdbPacker.File("SfxEntries", abSfxEntries); + pdbp.Add(fileSfxEntries); + + // Now add in all the sounds + + for (int istrFile = 0; istrFile < strcUniqueSounds.Count; istrFile++) { + string strFile = Path.GetFullPath(textBoxSoundsDir.Text) + "\\" + strcUniqueSounds[istrFile]; + Pcm pcm = new Pcm(strFile); + PdbPacker.File fileT = new PdbPacker.File(); + fileT.str = Path.ChangeExtension(strcUniqueSounds[istrFile], ".snd"); + fileT.ab = pcm.GetSndEncoding(); + fileT.fCompress = false; + pdbp.Add(fileT); + } + + // Ready to save pdb + + pdbp.Save(strPdbFile, "WARI"); + } + + unsafe static byte[] SerializeStructure(byte[] ab, void *pv, int cb) { + byte *pb = (byte *)pv; + byte[] abNew = null; + if (ab == null) { + abNew = new byte[cb]; + for (int i = 0; i < cb; i++) + abNew[i] = pb[i]; + } else { + abNew = new byte[ab.Length + cb]; + for (int i = 0; i < ab.Length; i++) + abNew[i] = ab[i]; + for (int i = 0; i < cb; i++) + abNew[i + ab.Length] = pb[i]; + } + + return abNew; + } + + private void menuItemMakePdb_Click(object sender, System.EventArgs e) { + SaveFileDialog frmSave = new SaveFileDialog(); + frmSave.DefaultExt = "pdb"; + frmSave.Filter = "Palm Database Files (*.pdb)|*.pdb"; + frmSave.Title = "Save As"; + frmSave.FileName = m_strPdbFile; + if (frmSave.ShowDialog() == DialogResult.Cancel) + return; + SavePdb(frmSave.FileName); + } + + public bool ParseNames(string strFile) { + // Read it + + TextReader tr; + try { + tr = new StreamReader(strFile); + } catch { + return false; + } + + // Find the line that starts with "enum" + + while (true) { + string strT = tr.ReadLine(); + if (strT == null) { + tr.Close(); + return false; + } + if (strT.StartsWith("enum")) + break; + } + + // Start reading names. + + m_alsNames.Clear(); + m_alsSfxEnabled.Clear(); + while (true) { + string strT = tr.ReadLine(); + if (strT == null) + break; + if (strT == "// stop") + break; + + Regex rexNames0 = new Regex(@"^\s*(?[a-zA-Z_0-9]+)\,\s\/\/\s*(?[a-zA-Z]+)\;.*$"); + Match matNames0 = rexNames0.Match(strT); + if (matNames0.Groups["name0"].Value.Length != 0) { + StringCollection strc = new StringCollection(); + strc.Add(matNames0.Groups["name0"].Value); + bool fEnabled; + if (matNames0.Groups["enabled"].Value == "disabled") { + fEnabled = false; + } else { + fEnabled = true; + } + + Regex rexNames1 = new Regex(@"^\s*(?[a-zA-Z_0-9]+)\,\s\/\/\s*(?[a-zA-Z]+)\;\s*(?[a-zA-Z_0-9]+).*$"); + Match matNames1 = rexNames1.Match(strT); + string strMatch = matNames1.Groups["name1"].Value; + if (strMatch.Length != 0) { + strc.Add(matNames1.Groups["name1"].Value); + + Regex rexNames2 = new Regex(@"^\s*(?[a-zA-Z_0-9]+)\,\s\/\/\s*(?[a-zA-Z]+)\;\s*(?[a-zA-Z_0-9]+)\,\s*(?[a-zA-Z_0-9]+).*$"); + Match matNames2 = rexNames2.Match(strT); + strMatch = matNames2.Groups["name2"].Value; + if (strMatch.Length != 0) { + strc.Add(matNames2.Groups["name2"].Value); + + Regex rexNames3 = new Regex(@"^\s*(?[a-zA-Z_0-9]+)\,\s\/\/\s*(?[a-zA-Z]+)\;\s*(?[a-zA-Z_0-9]+)\,\s*(?[a-zA-Z_0-9]+)\,\s*(?[a-zA-Z_0-9]+).*$"); + Match matNames3 = rexNames3.Match(strT); + strMatch = matNames3.Groups["name3"].Value; + if (strMatch.Length != 0) { + strc.Add(matNames3.Groups["name3"].Value); + } + } + } + m_alsSfxEnabled.Add(fEnabled); + m_alsNames.Add(strc); + } + } + tr.Close(); + return (m_alsNames.Count != 0); + } + + private void menuItemCheckAll_Click(object sender, System.EventArgs e) { + foreach (ListViewItem item in listViewSfx.Items) + item.Checked = true; + } + + private void menuItemUncheckAll_Click(object sender, System.EventArgs e) { + foreach (ListViewItem item in listViewSfx.Items) + item.Checked = false; + } + + private void menuItemCopy_Click(object sender, System.EventArgs e) { + if (listViewSfx.SelectedItems == null) + return; + ListViewItem item = listViewSfx.SelectedItems[0]; + if (item == null) + return; + m_itemForCopy = new ListViewItem(new System.Windows.Forms.ListViewItem.ListViewSubItem[] { + new ListViewItem.ListViewSubItem(null, "n/a", System.Drawing.SystemColors.WindowText, System.Drawing.SystemColors.Window, new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)))), + new ListViewItem.ListViewSubItem(null, item.SubItems[1].Text), + new ListViewItem.ListViewSubItem(null, item.SubItems[2].Text), + new ListViewItem.ListViewSubItem(null, item.SubItems[3].Text), + new ListViewItem.ListViewSubItem(null, item.SubItems[4].Text) + } , -1); + m_itemForCopy.Checked = item.Checked; + } + + private void menuItemPaste_Click(object sender, System.EventArgs e) { + if (m_itemForCopy == null) + return; + ListViewItem itemNew = listViewSfx.SelectedItems[0]; + if (itemNew == null) + return; + if (!(bool)m_alsSfxEnabled[itemNew.Index]) + return; + if (itemNew.Index == m_itemForCopy.Index) + return; + itemNew.Checked = m_itemForCopy.Checked; + for (int i = 1; i < 5; i++) { + itemNew.SubItems[i].Text = m_itemForCopy.SubItems[i].Text; + } + PlaySound(itemNew.SubItems[1].Text); + } + + private void menuItemClear_Click(object sender, System.EventArgs e) { + ListViewItem item = listViewSfx.SelectedItems[0]; + if (item == null) + return; + item.SubItems[1].Text = ""; + item.SubItems[2].Text = "0"; + item.SubItems[3].Text = "Highest"; + item.SubItems[4].Text = ""; + } + + private void menuItemCheckPdbSize_Click(object sender, System.EventArgs e) { + SavePdb("checksize.pdb"); + Stream stm = File.OpenRead("checksize.pdb"); + MessageBox.Show(stm.Length + " bytes."); + stm.Close(); + } + + private void menuItemTestCom2_Click(object sender, System.EventArgs e) { +#if false + SerialStream stm = new SerialStream("com2:", FileAccess.Write); + stm.SetPortSettings(19200, 8, SerialStream.StopBits.One, SerialStream.Parity.None, SerialStream.FlowControl.None); + byte[] ab = new Byte[] { (byte)'h', (byte)'e', (byte)'l', (byte)'l', (byte)'o', (byte)' ', (byte)'w', (byte)'o', (byte)'r', (byte)'l', (byte)'d', 0 }; + stm.Write(ab, 0, ab.Length); + stm.Close(); +#endif + } + + private void menuItemCom2_Click(object sender, System.EventArgs e) { + m_nComPort = 2; + menuItemCom1.Checked = false; + menuItemCom2.Checked = true; + } + + private void menuItemCom1_Click(object sender, System.EventArgs e) { + m_nComPort = 1; + menuItemCom1.Checked = true; + menuItemCom2.Checked = false; + } + } +} diff --git a/Schemer/Form1.resx b/Schemer/Form1.resx new file mode 100644 index 0000000..cbc2e27 --- /dev/null +++ b/Schemer/Form1.resx @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + 126, 17 + + + Form1 + + + 44 + + \ No newline at end of file diff --git a/Schemer/Schemer.csproj b/Schemer/Schemer.csproj new file mode 100644 index 0000000..a15d004 --- /dev/null +++ b/Schemer/Schemer.csproj @@ -0,0 +1,158 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Schemer/Schemer.sln b/Schemer/Schemer.sln new file mode 100644 index 0000000..83845b2 --- /dev/null +++ b/Schemer/Schemer.sln @@ -0,0 +1,21 @@ +Microsoft Visual Studio Solution File, Format Version 7.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Schemer", "Schemer.csproj", "{620839F8-630B-434D-8DFF-A8FF2C8D21B3}" +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + ConfigName.0 = Debug + ConfigName.1 = Release + EndGlobalSection + GlobalSection(ProjectDependencies) = postSolution + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {620839F8-630B-434D-8DFF-A8FF2C8D21B3}.Debug.ActiveCfg = Debug|.NET + {620839F8-630B-434D-8DFF-A8FF2C8D21B3}.Debug.Build.0 = Debug|.NET + {620839F8-630B-434D-8DFF-A8FF2C8D21B3}.Release.ActiveCfg = Release|.NET + {620839F8-630B-434D-8DFF-A8FF2C8D21B3}.Release.Build.0 = Release|.NET + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/Schemer/SerialStream.cs b/Schemer/SerialStream.cs new file mode 100644 index 0000000..f75a8c2 --- /dev/null +++ b/Schemer/SerialStream.cs @@ -0,0 +1,517 @@ +/* + * Author: Marcus Lorentzon, 2001 + * d98malor@dtek.chalmers.se + * + * Freeware: Do not remove this header + * + * File: SerialStream.cs + * + * Description: Implements a Stream for asynchronous + * transfers and COMM. + * + * Version: 2.0 beta + * + */ + +namespace LoMaN.IO { + + using System; + using System.IO; + using System.Threading; + using System.Runtime.InteropServices; + + public class SerialStream : Stream { + + public class SerialAsyncResult : IAsyncResult { + public SerialAsyncResult(object asyncObject) { + m_AsyncObject = asyncObject; + m_WaitHandle = new ManualResetEvent(false); + } + + internal void Init(object stateObject, AsyncCallback callback, bool bIsRead) { + m_StateObject = stateObject; + m_Callback = callback; + m_bIsRead = bIsRead; + m_bCompleted = false; + m_WaitHandle.Reset(); + } + + internal void Reset() { + m_StateObject = null; + m_Callback = null; + m_bCompleted = true; + m_WaitHandle.Reset(); + } + + internal bool m_bIsRead; + internal bool m_bCompleted = true; + + public bool IsCompleted { get { return m_bCompleted; } } + public bool CompletedSynchronously { get { return false; } } + + private object m_AsyncObject; + public object AsyncObject { get { return m_AsyncObject; } } + + private object m_StateObject; + public object AsyncState { get { return m_StateObject; } } + + private ManualResetEvent m_WaitHandle; + public WaitHandle AsyncWaitHandle { get { return m_WaitHandle; } } + + private AsyncCallback m_Callback; + public AsyncCallback Callback { get { return m_Callback; } } + } + + private unsafe void AsyncFSCallback(uint errorCode, uint numBytes, NativeOverlapped* pOverlapped) { + SerialAsyncResult sar = (SerialAsyncResult)Overlapped.Unpack(pOverlapped).AsyncResult; + + if (sar.m_bIsRead) + m_iReadCount = (int)numBytes; + else + m_iWriteCount = (int)numBytes; + ((ManualResetEvent)sar.AsyncWaitHandle).Set(); + + if (errorCode == ERROR_OPERATION_ABORTED) + sar.m_bCompleted = false; + else + sar.m_bCompleted = true; + + if (sar.Callback != null) + sar.Callback.Invoke(sar); + } + + private IOCompletionCallback m_IOCompletionCallback; + private int m_hFile = 0; + + private string m_sPort; + public string Port { + get { + return m_sPort; + } + set { + if (m_sPort != value) { + Close(); + Open(value); + } + } + } + + private const uint GENERIC_READ = 0x80000000; + private const uint GENERIC_WRITE = 0x40000000; + private const uint ERROR_OPERATION_ABORTED = 995; + private const uint ERROR_IO_PENDING = 997; + + public void Open(string port) { + m_hFile = CreateFile(port, (uint)((m_bRead?GENERIC_READ:0)|(m_bWrite?GENERIC_WRITE:0)), 0, 0, 3, 0x40000000, 0); + if (m_hFile <= 0) { + m_hFile = 0; + throw new FileNotFoundException("Unable to open " + port); + } + m_sPort = port; + + ThreadPool.BindHandle(new IntPtr(m_hFile)); + } + + public SerialStream(string port, FileAccess access) { + m_bRead = ((int)access & (int)FileAccess.Read) != 0; + m_bWrite = ((int)access & (int)FileAccess.Write) != 0; + Open(port); + unsafe { + m_IOCompletionCallback = new IOCompletionCallback(AsyncFSCallback); + } + } + + private bool m_bRead; + public override bool CanRead { get { return m_bRead; } } + + private bool m_bWrite; + public override bool CanWrite { get { return m_bWrite; } } + + public override bool CanSeek { get { return false; } } + + public bool Closed { get { return m_hFile <= 0; } } + + public override long Length { get { return 0; } } + + public override void SetLength(long nLength) { } + + public override void Close() { + CloseHandle(m_hFile); + m_hFile = 0; + m_sPort = null; + } + + private int m_iReadCount; + public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) { + SerialAsyncResult ar = new SerialAsyncResult(this); + ar.Init(state, callback, true); + Overlapped ov = new Overlapped(0, 0, ar.AsyncWaitHandle.Handle.ToInt32(), ar); + unsafe { fixed (byte* data = &buffer[0]) { + int read = 0; + NativeOverlapped* nov = ov.Pack(m_IOCompletionCallback); + ReadFile(m_hFile, data, count, &read, nov); + } } + + if (GetLastError() == ERROR_IO_PENDING) + return ar; + else + throw new Exception("Unable to initialize read. Errorcode: " + GetLastError().ToString()); + } + + private int m_iWriteCount; + public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) { + SerialAsyncResult ar = new SerialAsyncResult(this); + ar.Init(state, callback, false); + Overlapped ov = new Overlapped(0, 0, ar.AsyncWaitHandle.Handle.ToInt32(), ar); + unsafe { fixed (byte* data = &buffer[0]) { + int write = 0; + NativeOverlapped* nov = ov.Pack(m_IOCompletionCallback); + WriteFile(m_hFile, data, count, &write, nov); + } } + if (GetLastError() == ERROR_IO_PENDING) + return ar; + else + throw new Exception("Unable to initialize write. Errorcode: " + GetLastError().ToString()); + } + + public override int EndRead(IAsyncResult asyncResult) { + SerialAsyncResult sar = (SerialAsyncResult)asyncResult; + if (!sar.m_bIsRead) + throw new Exception("Invalid parameter: IAsyncResult is not from a read"); + sar.AsyncWaitHandle.WaitOne(); + if (!sar.m_bCompleted) { + ((ManualResetEvent)sar.AsyncWaitHandle).Reset(); + sar.AsyncWaitHandle.WaitOne(); + } + sar.Reset(); + + return m_iReadCount; + } + + public override void EndWrite(IAsyncResult asyncResult) { + SerialAsyncResult sar = (SerialAsyncResult)asyncResult; + if (sar.m_bIsRead) + throw new Exception("Invalid parameter: IAsyncResult is from a read"); + sar.AsyncWaitHandle.WaitOne(); + if (!sar.m_bCompleted) { + ((ManualResetEvent)sar.AsyncWaitHandle).Reset(); + sar.AsyncWaitHandle.WaitOne(); + } + sar.Reset(); + } + + public override int Read(byte[] buffer, int offset, int count) { + return EndRead(BeginRead(buffer, offset, count, null, null)); + } + + public override void Write(byte[] buffer, int offset, int count) { + EndWrite(BeginWrite(buffer, offset, count, null, null)); + } + + public override void Flush() { FlushFileBuffers(m_hFile); } + + private const uint PURGE_TXABORT = 0x0001; // Kill the pending/current writes to the comm port. + private const uint PURGE_RXABORT = 0x0002; // Kill the pending/current reads to the comm port. + private const uint PURGE_TXCLEAR = 0x0004; // Kill the transmit queue if there. + private const uint PURGE_RXCLEAR = 0x0008; // Kill the typeahead buffer if there. + + public bool PurgeRead() { return(PurgeComm(m_hFile, PURGE_RXCLEAR)); } + public bool PurgeWrite() { return(PurgeComm(m_hFile, PURGE_TXCLEAR)); } + public bool CancelRead() { return(PurgeComm(m_hFile, PURGE_RXABORT)); } + public bool CancelWrite() { return(PurgeComm(m_hFile, PURGE_TXABORT)); } + + public override long Seek(long offset, SeekOrigin origin) { return 0; } + + public override long Position { get { return 0; } set { } } + + public void SetTimeouts(int ReadIntervalTimeout, int ReadTotalTimeoutMultiplier, int ReadTotalTimeoutConstant, + int WriteTotalTimeoutMultiplier, int WriteTotalTimeoutConstant) { + SerialTimeouts Timeouts = new SerialTimeouts(ReadIntervalTimeout, ReadTotalTimeoutMultiplier, ReadTotalTimeoutConstant, + WriteTotalTimeoutMultiplier, WriteTotalTimeoutConstant); + unsafe { SetCommTimeouts(m_hFile, &Timeouts); } + } + + public enum Parity {None, Odd, Even, Mark, Space}; + public enum StopBits {One, OneAndHalf, Two}; + public enum FlowControl {None, XOnXOff, Hardware}; + + [StructLayout(LayoutKind.Sequential)] + public struct DCB { + public int DCBlength; + public uint BaudRate; + public uint Flags; + public uint fBinary { get { return Flags&0x0001; } + set { Flags = Flags & ~1U | value; } } + public uint fParity { get { return (Flags>>1)&1; } + set { Flags = Flags & ~(1U << 1) | (value << 1); } } + public uint fOutxCtsFlow { get { return (Flags>>2)&1; } + set { Flags = Flags & ~(1U << 2) | (value << 2); } } + public uint fOutxDsrFlow { get { return (Flags>>3)&1; } + set { Flags = Flags & ~(1U << 3) | (value << 3); } } + public uint fDtrControl { get { return (Flags>>4)&3; } + set { Flags = Flags & ~(3U << 4) | (value << 4); } } + public uint fDsrSensitivity { get { return (Flags>>6)&1; } + set { Flags = Flags & ~(1U << 6) | (value << 6); } } + public uint fTXContinueOnXoff { get { return (Flags>>7)&1; } + set { Flags = Flags & ~(1U << 7) | (value << 7); } } + public uint fOutX { get { return (Flags>>8)&1; } + set { Flags = Flags & ~(1U << 8) | (value << 8); } } + public uint fInX { get { return (Flags>>9)&1; } + set { Flags = Flags & ~(1U << 9) | (value << 9); } } + public uint fErrorChar { get { return (Flags>>10)&1; } + set { Flags = Flags & ~(1U << 10) | (value << 10); } } + public uint fNull { get { return (Flags>>11)&1; } + set { Flags = Flags & ~(1U << 11) | (value << 11); } } + public uint fRtsControl { get { return (Flags>>12)&3; } + set { Flags = Flags & ~(3U << 12) | (value << 12); } } + public uint fAbortOnError { get { return (Flags>>13)&1; } + set { Flags = Flags & ~(1U << 13) | (value << 13); } } + public ushort wReserved; + public ushort XonLim; + public ushort XoffLim; + public byte ByteSize; + public byte Parity; + public byte StopBits; + public sbyte XonChar; + public sbyte XoffChar; + public sbyte ErrorChar; + public sbyte EofChar; + public sbyte EvtChar; + public ushort wReserved1; + + public override string ToString() { + return "DCBlength: " + DCBlength + "\r\n" + + "BaudRate: " + BaudRate + "\r\n" + + "fBinary: " + fBinary + "\r\n" + + "fParity: " + fParity + "\r\n" + + "fOutxCtsFlow: " + fOutxCtsFlow + "\r\n" + + "fOutxDsrFlow: " + fOutxDsrFlow + "\r\n" + + "fDtrControl: " + fDtrControl + "\r\n" + + "fDsrSensitivity: " + fDsrSensitivity + "\r\n" + + "fTXContinueOnXoff: " + fTXContinueOnXoff + "\r\n" + + "fOutX: " + fOutX + "\r\n" + + "fInX: " + fInX + "\r\n" + + "fErrorChar: " + fErrorChar + "\r\n" + + "fNull: " + fNull + "\r\n" + + "fRtsControl: " + fRtsControl + "\r\n" + + "fAbortOnError: " + fAbortOnError + "\r\n" + + "XonLim: " + XonLim + "\r\n" + + "XoffLim: " + XoffLim + "\r\n" + + "ByteSize: " + ByteSize + "\r\n" + + "Parity: " + Parity + "\r\n" + + "StopBits: " + StopBits + "\r\n" + + "XonChar: " + XonChar + "\r\n" + + "XoffChar: " + XoffChar + "\r\n" + + "EofChar: " + EofChar + "\r\n" + + "EvtChar: " + EvtChar + "\r\n"; + } + } + + public bool SetPortSettings(uint baudrate, byte databits, StopBits stopbits, Parity parity, FlowControl flowcontrol) { + unsafe { + DCB dcb = new DCB(); + dcb.DCBlength = sizeof(DCB); + dcb.BaudRate = baudrate; + dcb.ByteSize = databits; + dcb.StopBits = (byte)stopbits; + dcb.Parity = (byte)parity; + dcb.fParity = (parity > 0)? 1U : 0U; + dcb.fBinary = dcb.fDtrControl = dcb.fTXContinueOnXoff = 1; + dcb.fOutxCtsFlow = dcb.fAbortOnError = (flowcontrol == FlowControl.Hardware)? 1U : 0U; + dcb.fOutX = dcb.fInX = (flowcontrol == FlowControl.XOnXOff)? 1U : 0U; + dcb.fRtsControl = (flowcontrol == FlowControl.Hardware)? 2U : 1U; + dcb.XonLim = 2048; + dcb.XoffLim = 512; + dcb.XonChar = 0x11; // Ctrl-Q + dcb.XoffChar = 0x13; // Ctrl-S + return SetCommState(m_hFile, &dcb); + } + } + + public bool GetPortSettings(out DCB dcb) { + unsafe { + DCB dcb2 = new DCB(); + dcb2.DCBlength = sizeof(DCB); + bool ret = GetCommState(m_hFile, &dcb2); + dcb = dcb2; + return ret; + } + } + + [DllImport("kernel32.dll", EntryPoint="CreateFileW", SetLastError=true, + CharSet=CharSet.Unicode, ExactSpelling=true)] + static extern int CreateFile(string filename, uint access, uint sharemode, uint security_attributes, uint creation, uint flags, uint template); + + [DllImport("kernel32.dll", SetLastError=true)] + static extern bool CloseHandle(int handle); + + [DllImport("kernel32.dll", SetLastError=true)] + static unsafe extern bool ReadFile(int hFile, byte* lpBuffer, int nNumberOfBytesToRead, int* lpNumberOfBytesRead, NativeOverlapped* lpOverlapped); + + [DllImport("kernel32.dll", SetLastError=true)] + static unsafe extern bool WriteFile(int hFile, byte* lpBuffer, int nNumberOfBytesToWrite, int* lpNumberOfBytesWritten, NativeOverlapped* lpOverlapped); + + [DllImport("kernel32.dll", SetLastError=true)] + static unsafe extern bool SetCommTimeouts(int hFile, SerialTimeouts* lpCommTimeouts); + + [DllImport("kernel32.dll", SetLastError=true)] + static unsafe extern bool SetCommState(int hFile, DCB* lpDCB); + + [DllImport("kernel32.dll", SetLastError=true)] + static unsafe extern bool GetCommState(int hFile, DCB* lpDCB); + + [DllImport("kernel32.dll", SetLastError=true)] + static unsafe extern bool BuildCommDCB(string def, DCB* lpDCB); + + [DllImport("kernel32.dll", SetLastError=true)] + static unsafe extern int GetLastError(); + + [DllImport("kernel32.dll", SetLastError=true)] + static unsafe extern bool FlushFileBuffers(int hFile); + + [DllImport("kernel32.dll", SetLastError=true)] + static unsafe extern bool PurgeComm(int hFile, uint dwFlags); + } + + [StructLayout(LayoutKind.Sequential)] + public struct SerialTimeouts { + public int ReadIntervalTimeout; + public int ReadTotalTimeoutMultiplier; + public int ReadTotalTimeoutConstant; + public int WriteTotalTimeoutMultiplier; + public int WriteTotalTimeoutConstant; + + public SerialTimeouts(int r1, int r2, int r3, int w1, int w2) { + ReadIntervalTimeout = r1; + ReadTotalTimeoutMultiplier = r2; + ReadTotalTimeoutConstant = r3; + WriteTotalTimeoutMultiplier = w1; + WriteTotalTimeoutConstant = w2; + } + + public override string ToString() { + return "ReadIntervalTimeout: " + ReadIntervalTimeout + "\r\n" + + "ReadTotalTimeoutMultiplier: " + ReadTotalTimeoutMultiplier + "\r\n" + + "ReadTotalTimeoutConstant: " + ReadTotalTimeoutConstant + "\r\n" + + "WriteTotalTimeoutMultiplier: " + WriteTotalTimeoutMultiplier + "\r\n" + + "WriteTotalTimeoutConstant: " + WriteTotalTimeoutConstant + "\r\n"; + } + } + + public class SerialSettings { + private bool m_bDirty = false; + public bool Dirty { get { return m_bDirty; } } + + // Timeouts + private SerialTimeouts Timeouts = new SerialTimeouts(1, 0, 0, 0, 0); + + public int ReadIntervalTimeout { + get { return Timeouts.ReadIntervalTimeout; } + set { if (Timeouts.ReadIntervalTimeout != value) { + Timeouts.ReadIntervalTimeout = value; + m_bDirty = true; + } + } + } + + public int ReadTotalTimeoutMultiplier { + get { return Timeouts.ReadTotalTimeoutMultiplier; } + set { if (Timeouts.ReadTotalTimeoutMultiplier != value) { + Timeouts.ReadTotalTimeoutMultiplier = value; + m_bDirty = true; + } + } + } + + public int ReadTotalTimeoutConstant { + get { return Timeouts.ReadTotalTimeoutConstant; } + set { if (Timeouts.ReadTotalTimeoutConstant != value) { + Timeouts.ReadTotalTimeoutConstant = value; + m_bDirty = true; + } + } + } + + public int WriteTotalTimeoutMultiplier { + get { return Timeouts.WriteTotalTimeoutMultiplier; } + set { if (Timeouts.WriteTotalTimeoutMultiplier != value) { + Timeouts.WriteTotalTimeoutMultiplier = value; + m_bDirty = true; + } + } + } + + public int WriteTotalTimeoutConstant { + get { return Timeouts.WriteTotalTimeoutConstant; } + set { if (Timeouts.WriteTotalTimeoutConstant != value) { + Timeouts.WriteTotalTimeoutConstant = value; + m_bDirty = true; + } + } + } + + // Valid stuff + private static readonly object[] s_iValidBitRates = new object[] {75u, 110u, 134u, 150u, 300u, 600u, 1200u, 1800u, 2400u, 4800u, + 7200u, 9600u, 14400u, 19200u, 38400u, 57600u, 115200u, 128000u}; + public static object[] ValidBitRates { + get { return s_iValidBitRates; } + } + + private static readonly object[] s_iValidDataBits = new object[] {(byte)5, (byte)6, (byte)7, (byte)8}; + public static object[] ValidDataBits { + get { return s_iValidDataBits; } + } + + // Port settings + private uint m_iBitRate = 57600; + public uint BitRate { + get { return m_iBitRate; } + set { if (m_iBitRate != value) { + m_iBitRate = value; + m_bDirty = true; + } + } + } + + private byte m_iDataBits = 8; + public byte DataBits { + get { return m_iDataBits; } + set { if (m_iDataBits != value) { + m_iDataBits = value; + m_bDirty = true; + } + } + } + + private SerialStream.Parity m_Parity = SerialStream.Parity.None; + public SerialStream.Parity Parity { + get { return m_Parity; } + set { if (m_Parity != value) { + m_Parity = value; + m_bDirty = true; + } + } + } + + private SerialStream.StopBits m_StopBits = SerialStream.StopBits.One; + public SerialStream.StopBits StopBits { + get { return m_StopBits; } + set { if (m_StopBits != value) { + m_StopBits = value; + m_bDirty = true; + } + } + } + + private SerialStream.FlowControl m_FlowControl = SerialStream.FlowControl.None; + public SerialStream.FlowControl FlowControl { + get { return m_FlowControl; } + set { if (m_FlowControl != value) { + m_FlowControl = value; + m_bDirty = true; + } + } + } + } + +} diff --git a/Schemer/todo.txt b/Schemer/todo.txt new file mode 100644 index 0000000..b871a40 --- /dev/null +++ b/Schemer/todo.txt @@ -0,0 +1,7 @@ ++ clear entry ++ copy / paste ++ click to hear in sfx list +- usage count? would be nice. ++ pdb size calc ++ unused entries (not being used yet) ++ disable entries (for testing purposes) \ No newline at end of file diff --git a/SpiffLib/AudioFormats.cs b/SpiffLib/AudioFormats.cs new file mode 100644 index 0000000..98249a2 --- /dev/null +++ b/SpiffLib/AudioFormats.cs @@ -0,0 +1,1475 @@ +using System; +using System.IO; +using System.Collections; +using System.Runtime.InteropServices; +using System.Diagnostics; + +namespace SpiffLib +{ + /// + /// Summary description for AudioFormats. + /// + public class AudioFormats + { + // Fibonacci + + static int[] s_anFibonacci = { -34, -21, -13, -8, -5, -3, -2, -1, 0, 1, 2, 3, 5, 8, 13, 21 }; + + public static byte[] EncodeFibonacciDelta(byte[] abPcm) { + return Encode8BitDelta(abPcm, s_anFibonacci); + } + + public static byte[] DecodeFibonacciDelta(byte[] abEncoded) { + return Decode8BitDelta(abEncoded, s_anFibonacci); + } + + // Exponential + + static int[] s_anExponential = { -128, -64, -32, -16, -8, -4, -2, -1, 0, 1, 2, 4, 8, 16, 32, 64 }; + + public static byte[] EncodeExponentialDelta(byte[] abPcm) { + return Encode8BitDelta(abPcm, s_anExponential); + } + + public static byte[] DecodeExponentialDelta(byte[] abEncoded) { + return Decode8BitDelta(abEncoded, s_anExponential); + } + + static byte[] Encode8BitDelta(byte[] abPcm, int[] anDelta) { + byte[] abEncoded = new byte[1 + (abPcm.Length + 1) / 2]; + int nLast = (int)abPcm[0]; + abEncoded[0] = abPcm[0]; + for (int i = 1; i < abPcm.Length; i++) { + // Important - the shortest path to the next sample may be by overflowing or underflowing. + // If this isn't done, the results will be poor. + + sbyte sbDelta = (sbyte)(abPcm[i] - nLast); + int iEntry = -1; + int nDiffSmallest = Int32.MaxValue; + for (int j = 0; j < anDelta.Length; j++) { + int nDiff = (anDelta[j] - sbDelta); + if (nDiff < 0) + nDiff = -nDiff; + if (nDiff < nDiffSmallest) { + nDiffSmallest = nDiff; + iEntry = j; + } + } + Debug.Assert(iEntry >= 0 && iEntry <= 15); + + if ((i & 1) != 0) { + abEncoded[(i - 1) / 2 + 1] = (byte)((iEntry << 4) + 8); + } else { + abEncoded[(i - 1) / 2 + 1] = (byte)((byte)(abEncoded[(i - 1) / 2 + 1] & 0xf0) | (byte)iEntry); + } + nLast += anDelta[iEntry]; + } + + return abEncoded; + } + + static byte[] Decode8BitDelta(byte[] abEncoded, int[] anDelta) { + byte[] abPcm = new byte[(abEncoded.Length - 1) * 2 + 1]; + byte bSample = abEncoded[0]; + abPcm[0] = bSample; + for (int i = 1; i < abEncoded.Length; i++) { + bSample += (byte)anDelta[abEncoded[i] >> 4]; + abPcm[(i - 1) * 2 + 1] = bSample; + bSample += (byte)anDelta[abEncoded[i] & 0x0f]; + abPcm[(i - 1) * 2 + 2] = bSample; + } + + return abPcm; + } + + // Wrappers + + public unsafe static byte[] EncodeImaAdpcm(short[] ashPcm, int cBits, bool fPreClamp) { + byte[] abEncoded = new byte[ashPcm.Length / 2]; + short *pshPcm = (short *)Marshal.UnsafeAddrOfPinnedArrayElement(ashPcm, 0); + byte *pbOut = (byte *)Marshal.UnsafeAddrOfPinnedArrayElement(abEncoded, 0); + adpcm_state state; + state.valprev = 0; + state.index = 0; + adpcm_coder(pshPcm, pbOut, ashPcm.Length, &state, cBits, fPreClamp); + return abEncoded; + } + + public unsafe static short[] DecodeImaAdpcm(byte[] abEncoded, bool fPreClamp) { + short[] ashOut = new short[abEncoded.Length * 2]; + byte *pbEncoded = (byte *)Marshal.UnsafeAddrOfPinnedArrayElement(abEncoded, 0); + short *pshOut = (short *)Marshal.UnsafeAddrOfPinnedArrayElement(ashOut, 0); + adpcm_state state; + state.valprev = 0; + state.index = 0; + adpcm_decoder(pbEncoded, pshOut, abEncoded.Length * 2, &state, fPreClamp); + return ashOut; + } + + // IMD ADPCM + // This implementation Copyright 1992 by Stichting Mathematisch Centrum, Amsterdam, The + // Netherlands. + + /* Intel ADPCM step variation table */ + static int[] indexTable = new int[16] { + -1, -1, -1, -1, 2, 4, 6, 8, + -1, -1, -1, -1, 2, 4, 6, 8, + }; + + static int[] stepsizeTable = new int[89] { + 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, + 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, + 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, + 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, + 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, + 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, + 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, + 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, + 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 + }; + + struct adpcm_state { + public int valprev; + public int index; + }; + + unsafe static void adpcm_coder(short *indata, byte *outdata, int len, adpcm_state *state, int cBits, bool fPreClamp) + { + short *inp; /* Input buffer pointer */ + byte *outp; /* output buffer pointer */ + int val; /* Current input sample value */ + int sign; /* Current adpcm sign bit */ + int delta; /* Current adpcm output value */ + int diff; /* Difference between val and valprev */ + int step; /* Stepsize */ + int valpred; /* Predicted output value */ + int vpdiff; /* Current change to valpred */ + int index; /* Current step change index */ + int outputbuffer; /* place to keep previous 4-bit value */ + int bufferstep; /* toggle between outputbuffer/output */ + + outp = outdata; + inp = indata; + + valpred = state->valprev; + index = state->index; + step = stepsizeTable[index]; + + outputbuffer = 0; + bufferstep = 1; + + for ( ; len > 0 ; len-- ) { + val = *inp++; + + /* Step 1 - compute difference with previous value */ + diff = val - valpred; + sign = (diff < 0) ? 8 : 0; + if (sign != 0) diff = (-diff); + + // If we preclamp, then at decode time we don't need to clamp + // at all. It results in some quantization error however, but + // it is so slight it fails the blind test. + + int vpdiffMax = int.MaxValue; + if (fPreClamp) { + if (sign != 0) { + vpdiffMax = valpred - -32768; + } else { + vpdiffMax = 32767 - valpred; + } + } + + /* Step 2 - Divide and clamp */ + /* Note: + ** This code *approximately* computes: + ** delta = diff*4/step; + ** vpdiff = (delta+0.5)*step/4; + ** but in shift step bits are dropped. The net result of this is + ** that even if you have fast mul/div hardware you cannot put it to + ** good use since the fixup would be too expensive. + */ + delta = 0; + + if (fPreClamp) { + vpdiff = 0; + } else { + vpdiff = (step >> 3); + } + + if ( diff >= step ) { + delta = 4; + diff -= step; + vpdiff += step; + + if (vpdiff > vpdiffMax) { + vpdiff -= step; + delta &= ~4; + } + } + + if (cBits > 2) { + step >>= 1; + if ( diff >= step ) { + delta |= 2; + diff -= step; + vpdiff += step; + + if (vpdiff > vpdiffMax) { + vpdiff -= step; + delta &= ~2; + } + } + } + + if (cBits > 3) { + step >>= 1; + if ( diff >= step ) { + delta |= 1; + vpdiff += step; + + if (vpdiff > vpdiffMax) { + vpdiff -= step; + delta &= ~1; + } + } + } + + /* Step 3 - Update previous value */ + if (sign != 0) + valpred -= vpdiff; + else + valpred += vpdiff; + + /* Step 4 - Clamp previous value to 16 bits */ + if ( valpred > 32767 ) { + valpred = 32767; + Debug.Assert(!fPreClamp); + } else if ( valpred < -32768 ) { + valpred = -32768; + Debug.Assert(!fPreClamp); + } + + /* Step 5 - Assemble value, update index and step values */ + delta |= sign; + + index += indexTable[delta]; + if ( index < 0 ) index = 0; + if ( index > 88 ) index = 88; + step = stepsizeTable[index]; + + /* Step 6 - Output value */ + if (bufferstep != 0 ) { + outputbuffer = (delta << 4) & 0xf0; + } else { + *outp++ = (byte)((delta & 0x0f) | outputbuffer); + } + bufferstep ^= 1; + } + + /* Output last step, if needed */ + if (bufferstep == 0) + *outp++ = (byte)outputbuffer; + + state->valprev = valpred; + state->index = index; + } + + unsafe static void adpcm_decoder(byte *indata, short *outdata, int len, adpcm_state *state, bool fPreClamp) + { + byte *inp; /* Input buffer pointer */ + short *outp; /* output buffer pointer */ + int sign; /* Current adpcm sign bit */ + int delta; /* Current adpcm output value */ + int step; /* Stepsize */ + int valpred; /* Predicted value */ + int vpdiff; /* Current change to valpred */ + int index; /* Current step change index */ + int inputbuffer; /* place to keep next 4-bit value */ + int bufferstep; /* toggle between inputbuffer/input */ + + outp = outdata; + inp = indata; + + valpred = state->valprev; + index = state->index; + step = stepsizeTable[index]; + + inputbuffer = 0; + bufferstep = 0; + + for ( ; len > 0 ; len-- ) { + + /* Step 1 - get the delta value */ + if (bufferstep != 0) { + delta = inputbuffer & 0xf; + } else { + inputbuffer = *inp++; + delta = (inputbuffer >> 4) & 0xf; + } + bufferstep ^= 1; + + /* Step 2 - Find new index value (for later) */ + index += indexTable[delta]; + if ( index < 0 ) index = 0; + if ( index > 88 ) index = 88; + + /* Step 3 - Separate sign and magnitude */ + sign = delta & 8; + delta = delta & 7; + + /* Step 4 - Compute difference and new predicted value */ + /* + ** Computes 'vpdiff = (delta+0.5)*step/4', but see comment + ** in adpcm_coder. + */ + + if (fPreClamp) { + vpdiff = 0; + } else { + vpdiff = step >> 3; + } + + if ((delta & 4) != 0) vpdiff += step; + if ((delta & 2) != 0) vpdiff += step>>1; + if ((delta & 1) != 0) vpdiff += step>>2; + + if (sign != 0) + valpred -= vpdiff; + else + valpred += vpdiff; + + /* Step 5 - clamp output value */ + if (!fPreClamp) { + if ( valpred > 32767 ) + valpred = 32767; + else if ( valpred < -32768 ) + valpred = -32768; + } + + /* Step 6 - Update step value */ + step = stepsizeTable[index]; + + /* Step 7 - Output value */ + *outp++ = (short)valpred; + } + + state->valprev = valpred; + state->index = index; + } + + static int[] s_anStepFractions = new int[] { + 1,1,1,1,1,2,2,2,2,2,2,3,3,3,4,4,4,5,5,6,6,7,8,8,9,10,11,12,13,15,16,18,20,22,24,26,29,32,35,38,42,46,51,56,62,68,75,82,91,100,110,120,133,146,160,176,194,213,235,258,284,312,344,378,416,458,503,554,609,670,737,811,892,981,1079,1187,1305,1436,1579,1737,1911,2102,2313,2544,2798,3078,3386,3724,4096, + 2,2,2,3,3,3,3,4,4,4,5,5,6,6,7,8,9,9,10,11,13,14,15,17,18,20,22,24,27,30,33,36,39,43,48,52,58,63,70,77,84,93,102,112,124,136,150,165,181,199,219,241,265,292,321,353,388,427,470,517,568,625,687,756,832,915,1007,1107,1218,1340,1474,1621,1783,1961,2158,2373,2611,2872,3159,3475,3822,4205,4625,5088,5596,6156,6772,7449,8192, + 4,4,5,5,6,6,7,7,8,9,10,11,12,13,14,16,17,19,21,23,25,28,30,33,37,40,44,49,54,59,65,72,79,87,95,105,115,127,140,154,169,186,204,225,247,272,299,329,362,398,438,482,530,583,641,706,776,854,939,1033,1136,1250,1375,1512,1664,1830,2013,2214,2436,2679,2947,3242,3566,3923,4315,4747,5221,5744,6318,6950,7645,8409,9250,10175,11193,12312,13543,14897,16384, + 5,6,7,8,8,9,10,11,12,13,14,16,17,19,21,23,26,28,31,34,38,41,45,50,55,60,66,73,80,89,98,107,118,130,143,157,173,190,209,230,253,278,306,337,371,408,449,494,543,597,657,722,795,875,962,1058,1164,1280,1409,1550,1704,1874,2062,2268,2495,2745,3020,3321,3653,4019,4421,4863,5349,5884,6473,7120,7832,8615,9476,10424,11467,12614,13875,15263,16789,18467,20315,22346,24575, + 7,8,9,10,11,12,13,14,16,17,19,21,23,25,28,31,34,37,41,45,50,55,60,66,73,80,88,97,107,118,130,143,157,173,190,209,230,253,279,307,337,371,408,449,494,544,598,658,724,796,876,963,1060,1166,1282,1411,1552,1707,1878,2066,2272,2499,2749,3024,3327,3660,4026,4428,4871,5358,5894,6484,7132,7845,8630,9493,10442,11487,12635,13899,15289,16818,18500,20350,22385,24623,27086,29794,32767, + 9,10,11,13,14,15,16,18,20,21,24,26,29,31,35,39,43,46,51,56,63,69,75,83,91,100,110,121,134,148,163,179,196,216,238,261,288,316,349,384,421,464,510,561,618,680,748,823,905,995,1095,1204,1325,1458,1603,1764,1940,2134,2348,2583,2840,3124,3436,3780,4159,4575,5033,5535,6089,6698,7368,8105,8915,9806,10788,11866,13053,14359,15794,17374,19111,21023,23125,25438,27981,30779,33858,37243,40959, + 11,12,14,15,17,18,20,21,24,26,29,32,35,38,42,47,51,56,62,68,75,83,90,99,110,120,132,146,161,177,195,215,236,260,285,314,345,380,419,461,506,557,612,674,741,816,897,987,1086,1194,1314,1445,1590,1749,1923,2117,2328,2561,2817,3099,3408,3749,4124,4536,4991,5490,6039,6642,7307,8037,8841,9726,10698,11768,12945,14240,15663,17231,18953,20849,22934,25227,27750,30525,33578,36935,40629,44691,49151, + 12,14,16,18,19,21,23,25,28,30,33,37,40,44,49,54,60,65,72,79,88,96,105,116,128,140,154,170,187,207,228,250,275,303,333,366,403,443,488,537,590,649,714,786,865,952,1047,1152,1267,1393,1533,1685,1855,2041,2244,2469,2716,2987,3287,3616,3976,4373,4811,5292,5822,6405,7046,7749,8524,9377,10315,11347,12481,13729,15103,16613,18274,20102,22111,24323,26756,29432,32375,35613,39174,43090,47401,52140,57342, + -1,-1,-1,-1,-1,-2,-2,-2,-2,-2,-2,-3,-3,-3,-4,-4,-4,-5,-5,-6,-6,-7,-8,-8,-9,-10,-11,-12,-13,-15,-16,-18,-20,-22,-24,-26,-29,-32,-35,-38,-42,-46,-51,-56,-62,-68,-75,-82,-91,-100,-110,-120,-133,-146,-160,-176,-194,-213,-235,-258,-284,-312,-344,-378,-416,-458,-503,-554,-609,-670,-737,-811,-892,-981,-1079,-1187,-1305,-1436,-1579,-1737,-1911,-2102,-2313,-2544,-2798,-3078,-3386,-3724,-4096, + -2,-2,-2,-3,-3,-3,-3,-4,-4,-4,-5,-5,-6,-6,-7,-8,-9,-9,-10,-11,-13,-14,-15,-17,-18,-20,-22,-24,-27,-30,-33,-36,-39,-43,-48,-52,-58,-63,-70,-77,-84,-93,-102,-112,-124,-136,-150,-165,-181,-199,-219,-241,-265,-292,-321,-353,-388,-427,-470,-517,-568,-625,-687,-756,-832,-915,-1007,-1107,-1218,-1340,-1474,-1621,-1783,-1961,-2158,-2373,-2611,-2872,-3159,-3475,-3822,-4205,-4625,-5088,-5596,-6156,-6772,-7449,-8192, + -4,-4,-5,-5,-6,-6,-7,-7,-8,-9,-10,-11,-12,-13,-14,-16,-17,-19,-21,-23,-25,-28,-30,-33,-37,-40,-44,-49,-54,-59,-65,-72,-79,-87,-95,-105,-115,-127,-140,-154,-169,-186,-204,-225,-247,-272,-299,-329,-362,-398,-438,-482,-530,-583,-641,-706,-776,-854,-939,-1033,-1136,-1250,-1375,-1512,-1664,-1830,-2013,-2214,-2436,-2679,-2947,-3242,-3566,-3923,-4315,-4747,-5221,-5744,-6318,-6950,-7645,-8409,-9250,-10175,-11193,-12312,-13543,-14897,-16384, + -5,-6,-7,-8,-8,-9,-10,-11,-12,-13,-14,-16,-17,-19,-21,-23,-26,-28,-31,-34,-38,-41,-45,-50,-55,-60,-66,-73,-80,-89,-98,-107,-118,-130,-143,-157,-173,-190,-209,-230,-253,-278,-306,-337,-371,-408,-449,-494,-543,-597,-657,-722,-795,-875,-962,-1058,-1164,-1280,-1409,-1550,-1704,-1874,-2062,-2268,-2495,-2745,-3020,-3321,-3653,-4019,-4421,-4863,-5349,-5884,-6473,-7120,-7832,-8615,-9476,-10424,-11467,-12614,-13875,-15263,-16789,-18467,-20315,-22346,-24575, + -7,-8,-9,-10,-11,-12,-13,-14,-16,-17,-19,-21,-23,-25,-28,-31,-34,-37,-41,-45,-50,-55,-60,-66,-73,-80,-88,-97,-107,-118,-130,-143,-157,-173,-190,-209,-230,-253,-279,-307,-337,-371,-408,-449,-494,-544,-598,-658,-724,-796,-876,-963,-1060,-1166,-1282,-1411,-1552,-1707,-1878,-2066,-2272,-2499,-2749,-3024,-3327,-3660,-4026,-4428,-4871,-5358,-5894,-6484,-7132,-7845,-8630,-9493,-10442,-11487,-12635,-13899,-15289,-16818,-18500,-20350,-22385,-24623,-27086,-29794,-32767, + -9,-10,-11,-13,-14,-15,-16,-18,-20,-21,-24,-26,-29,-31,-35,-39,-43,-46,-51,-56,-63,-69,-75,-83,-91,-100,-110,-121,-134,-148,-163,-179,-196,-216,-238,-261,-288,-316,-349,-384,-421,-464,-510,-561,-618,-680,-748,-823,-905,-995,-1095,-1204,-1325,-1458,-1603,-1764,-1940,-2134,-2348,-2583,-2840,-3124,-3436,-3780,-4159,-4575,-5033,-5535,-6089,-6698,-7368,-8105,-8915,-9806,-10788,-11866,-13053,-14359,-15794,-17374,-19111,-21023,-23125,-25438,-27981,-30779,-33858,-37243,-40959, + -11,-12,-14,-15,-17,-18,-20,-21,-24,-26,-29,-32,-35,-38,-42,-47,-51,-56,-62,-68,-75,-83,-90,-99,-110,-120,-132,-146,-161,-177,-195,-215,-236,-260,-285,-314,-345,-380,-419,-461,-506,-557,-612,-674,-741,-816,-897,-987,-1086,-1194,-1314,-1445,-1590,-1749,-1923,-2117,-2328,-2561,-2817,-3099,-3408,-3749,-4124,-4536,-4991,-5490,-6039,-6642,-7307,-8037,-8841,-9726,-10698,-11768,-12945,-14240,-15663,-17231,-18953,-20849,-22934,-25227,-27750,-30525,-33578,-36935,-40629,-44691,-49151, + -12,-14,-16,-18,-19,-21,-23,-25,-28,-30,-33,-37,-40,-44,-49,-54,-60,-65,-72,-79,-88,-96,-105,-116,-128,-140,-154,-170,-187,-207,-228,-250,-275,-303,-333,-366,-403,-443,-488,-537,-590,-649,-714,-786,-865,-952,-1047,-1152,-1267,-1393,-1533,-1685,-1855,-2041,-2244,-2469,-2716,-2987,-3287,-3616,-3976,-4373,-4811,-5292,-5822,-6405,-7046,-7749,-8524,-9377,-10315,-11347,-12481,-13729,-15103,-16613,-18274,-20102,-22111,-24323,-26756,-29432,-32375,-35613,-39174,-43090,-47401,-52140,-57342 + }; + + static int[] s_anStepIndexMap = new int[] { + 0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87, + 0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87, + 0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87, + 0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87, + 2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,88,88, + 4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,88,88,88,88, + 6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,88,88,88,88,88,88, + 8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,88,88,88,88,88,88,88,88, + 0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87, + 0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87, + 0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87, + 0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87, + 2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,88,88, + 4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,88,88,88,88, + 6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,88,88,88,88,88,88, + 8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,88,88,88,88,88,88,88,88 + }; + + public static byte[] EncodeImaAdpcmTableDriven(short[] ashPcm) { + ArrayList alsEncoded = new ArrayList(); + + short shPredicted = 0; + int nStepIndex = 0; + + int[] anStep = new int[2]; + for (int i = 0; i < ashPcm.Length; i += 2) { + // Encoded size is a nibble, so 2 per byte + + for (int k = 0; k < 2; k++) { + anStep[k] = 0; + if (i + k == ashPcm.Length) + break; + int nDelta = ashPcm[i + k] - shPredicted; + int iStepSmallest = -1; + int nDiffSmallest = int.MaxValue; + for (int j = 0; j < 16; j++) { + int nStepFraction = s_anStepFractions[j * 89 + nStepIndex]; + if (shPredicted + nStepFraction < -32768 || shPredicted + nStepFraction > 32767) + continue; + int nDiff = Math.Abs(nStepFraction - nDelta); + if (nDiff < nDiffSmallest) { + nDiffSmallest = nDiff; + iStepSmallest = j; + } + } + anStep[k] = iStepSmallest; + shPredicted += (short)s_anStepFractions[iStepSmallest * 89 + nStepIndex]; + nStepIndex = s_anStepIndexMap[iStepSmallest * 89 + nStepIndex]; + } + + // Add 1 byte to output + + alsEncoded.Add((byte)((anStep[0] << 4) | anStep[1])); + } + + return (byte[])alsEncoded.ToArray(typeof(byte)); + } + + public static short[] DecodeImaAdpcmTableDriven(byte[] abIn) { + ArrayList alsPcm = new ArrayList(); + + short shPredicted = 0; + int nStepIndex = 0; + + foreach (byte bT in abIn) { + int nDelta = bT >> 4; + shPredicted += (short)s_anStepFractions[nDelta * 89 + nStepIndex]; + alsPcm.Add(shPredicted); + nStepIndex = s_anStepIndexMap[nDelta * 89 + nStepIndex]; + nDelta = bT & 0xf; + shPredicted += (short)s_anStepFractions[nDelta * 89 + nStepIndex]; + alsPcm.Add(shPredicted); + nStepIndex = s_anStepIndexMap[nDelta * 89 + nStepIndex]; + } + + return (short[])alsPcm.ToArray(typeof(short)); + } + + static int[] s_anStepFractions8BitEncode = new int[] { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,3,3,3,4,4,4,5,5,6,6,7,8,8,9,10,11,12,14,15,16,18,20,22,24,26,29,32, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,3,3,3,3,4,4,5,5,5,6,7,7,8,9,10,11,12,13,14,15,17,19,20,22,25,27,30,33,36,40,44,48,53,58,64, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,3,3,3,3,4,4,4,5,5,6,6,7,8,8,9,10,11,12,13,14,16,17,19,21,23,25,28,31,34,37,41,45,49,54,60,66,72,79,87,96,106,116,128, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,3,3,4,4,5,5,5,5,6,7,8,8,9,10,11,12,14,14,16,18,20,21,23,26,29,32,35,38,42,46,50,56,61,67,74,81,89,98,108,119,131,144,158,174,191, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,3,3,3,3,4,4,5,5,6,6,7,7,8,9,10,11,12,13,15,16,18,19,21,24,26,28,31,34,38,42,46,50,56,61,67,74,81,89,98,108,119,131,144,158,174,192,211,232,255, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,4,4,4,4,5,5,6,6,8,8,9,9,10,11,13,14,15,16,19,20,23,24,26,30,33,35,39,43,48,53,58,63,70,76,84,93,101,111,123,135,149,164,180,198,218,240,264,290,319, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,5,5,5,5,6,6,8,8,9,9,11,11,12,14,15,17,18,20,23,24,27,29,32,36,39,42,47,51,57,63,69,75,84,92,101,111,122,134,147,162,179,197,216,237,261,288,317,348,383, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,5,5,5,5,7,7,9,9,11,11,12,12,14,16,18,19,21,23,26,28,32,33,37,42,46,49,54,60,67,74,81,88,98,107,117,130,142,156,172,189,208,229,252,277,305,336,369,406,446, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-2,-2,-2,-2,-2,-3,-3,-3,-4,-4,-4,-5,-5,-6,-6,-7,-8,-8,-9,-10,-11,-12,-14,-15,-16,-18,-20,-22,-24,-26,-29,-32, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-2,-2,-2,-2,-2,-3,-3,-3,-3,-4,-4,-5,-5,-5,-6,-7,-7,-8,-9,-10,-11,-12,-13,-14,-15,-17,-19,-20,-22,-25,-27,-30,-33,-36,-40,-44,-48,-53,-58,-64, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-2,-2,-2,-2,-2,-3,-3,-3,-3,-4,-4,-4,-5,-5,-6,-6,-7,-8,-8,-9,-10,-11,-12,-13,-14,-16,-17,-19,-21,-23,-25,-28,-31,-34,-37,-41,-45,-49,-54,-60,-66,-72,-79,-87,-96,-106,-116,-128, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-2,-2,-2,-2,-2,-2,-2,-2,-3,-3,-4,-4,-5,-5,-5,-5,-6,-7,-8,-8,-9,-10,-11,-12,-14,-14,-16,-18,-20,-21,-23,-26,-29,-32,-35,-38,-42,-46,-50,-56,-61,-67,-74,-81,-89,-98,-108,-119,-131,-144,-158,-174,-191, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-2,-2,-2,-2,-3,-3,-3,-3,-4,-4,-5,-5,-6,-6,-7,-7,-8,-9,-10,-11,-12,-13,-15,-16,-18,-19,-21,-24,-26,-28,-31,-34,-38,-42,-46,-50,-56,-61,-67,-74,-81,-89,-98,-108,-119,-131,-144,-158,-174,-192,-211,-232,-255, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-3,-3,-3,-3,-3,-4,-4,-4,-4,-5,-5,-6,-6,-8,-8,-9,-9,-10,-11,-13,-14,-15,-16,-19,-20,-23,-24,-26,-30,-33,-35,-39,-43,-48,-53,-58,-63,-70,-76,-84,-93,-101,-111,-123,-135,-149,-164,-180,-198,-218,-240,-264,-290,-319, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-3,-3,-3,-3,-3,-5,-5,-5,-5,-6,-6,-8,-8,-9,-9,-11,-11,-12,-14,-15,-17,-18,-20,-23,-24,-27,-29,-32,-36,-39,-42,-47,-51,-57,-63,-69,-75,-84,-92,-101,-111,-122,-134,-147,-162,-179,-197,-216,-237,-261,-288,-317,-348,-383, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-4,-4,-4,-4,-4,-5,-5,-5,-5,-7,-7,-9,-9,-11,-11,-12,-12,-14,-16,-18,-19,-21,-23,-26,-28,-32,-33,-37,-42,-46,-49,-54,-60,-67,-74,-81,-88,-98,-107,-117,-130,-142,-156,-172,-189,-208,-229,-252,-277,-305,-336,-369,-406,-446 + }; + + public static byte[] EncodeImaAdpcmTableDriven8Bit(byte[] abPcm) { + ArrayList alsEncoded = new ArrayList(); + + byte bPredicted = 128; + int nStepIndex = 0; + + int[] anStep = new int[2]; + for (int i = 0; i < abPcm.Length; i += 2) { + // Encoded size is a nibble, so 2 per byte + + for (int k = 0; k < 2; k++) { + anStep[k] = 0; + if (i + k == abPcm.Length) + break; + int nDelta = abPcm[i + k] - bPredicted; + int iStepSmallest = -1; + int nDiffSmallest = int.MaxValue; + for (int j = 0; j < 16; j++) { + int nStepFraction = s_anStepFractions8BitEncode[j * 89 + nStepIndex]; + if (bPredicted + nStepFraction < 0 || bPredicted + nStepFraction > 255) + continue; + int nDiff = Math.Abs(nStepFraction - nDelta); + if (nDiff <= nDiffSmallest) { + nDiffSmallest = nDiff; + iStepSmallest = j; + } + } + anStep[k] = iStepSmallest; + bPredicted += (byte)s_anStepFractions8BitEncode[iStepSmallest * 89 + nStepIndex]; + nStepIndex = s_anStepIndexMap[iStepSmallest * 89 + nStepIndex]; + } + + // Add 1 byte to output + + alsEncoded.Add((byte)((anStep[0] << 4) | anStep[1])); + } + + return (byte[])alsEncoded.ToArray(typeof(byte)); + } + + static byte[] s_anStepFractions8BitDecode = new byte[] { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,3,3,3,4,4,4,5,5,6,6,7,8,8,9,10,11,12,14,15,16,18,20,22,24,26,29,32, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,3,3,3,3,4,4,5,5,5,6,7,7,8,9,10,11,12,13,14,15,17,19,20,22,25,27,30,33,36,40,44,48,53,58,64, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,3,3,3,3,4,4,4,5,5,6,6,7,8,8,9,10,11,12,13,14,16,17,19,21,23,25,28,31,34,37,41,45,49,54,60,66,72,79,87,96,106,116,128, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,3,3,4,4,5,5,5,5,6,7,8,8,9,10,11,12,14,14,16,18,20,21,23,26,29,32,35,38,42,46,50,56,61,67,74,81,89,98,108,119,131,144,158,174,191, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,3,3,3,3,4,4,5,5,6,6,7,7,8,9,10,11,12,13,15,16,18,19,21,24,26,28,31,34,38,42,46,50,56,61,67,74,81,89,98,108,119,131,144,158,174,192,211,232,255, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,4,4,4,4,5,5,6,6,8,8,9,9,10,11,13,14,15,16,19,20,23,24,26,30,33,35,39,43,48,53,58,63,70,76,84,93,101,111,123,135,149,164,180,198,218,240,255,255,255, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,5,5,5,5,6,6,8,8,9,9,11,11,12,14,15,17,18,20,23,24,27,29,32,36,39,42,47,51,57,63,69,75,84,92,101,111,122,134,147,162,179,197,216,237,255,255,255,255,255, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,5,5,5,5,7,7,9,9,11,11,12,12,14,16,18,19,21,23,26,28,32,33,37,42,46,49,54,60,67,74,81,88,98,107,117,130,142,156,172,189,208,229,252,255,255,255,255,255,255, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,254,254,254,254,254,254,253,253,253,252,252,252,251,251,250,250,249,248,248,247,246,245,244,242,241,240,238,236,234,232,230,227,224, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,254,254,254,254,254,254,253,253,253,253,252,252,251,251,251,250,249,249,248,247,246,245,244,243,242,241,239,237,236,234,231,229,226,223,220,216,212,208,203,198,192, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,254,254,254,254,254,254,253,253,253,253,252,252,252,251,251,250,250,249,248,248,247,246,245,244,243,242,240,239,237,235,233,231,228,225,222,219,215,211,207,202,196,190,184,177,169,160,150,140,128, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,254,254,254,254,254,254,254,254,254,253,253,252,252,251,251,251,251,250,249,248,248,247,246,245,244,242,242,240,238,236,235,233,230,227,224,221,218,214,210,206,200,195,189,182,175,167,158,148,137,125,112,98,82,65, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,254,254,254,254,254,253,253,253,253,252,252,251,251,250,250,249,249,248,247,246,245,244,243,241,240,238,237,235,232,230,228,225,222,218,214,210,206,200,195,189,182,175,167,158,148,137,125,112,98,82,64,45,24,1, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,253,253,253,253,253,252,252,252,252,251,251,250,250,248,248,247,247,246,245,243,242,241,240,237,236,233,232,230,226,223,221,217,213,208,203,198,193,186,180,172,163,155,145,133,121,107,92,76,58,38,16,1,1,1, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,254,254,254,254,254,254,254,254,254,254,254,254,253,253,253,253,253,251,251,251,251,250,250,248,248,247,247,245,245,244,242,241,239,238,236,233,232,229,227,224,220,217,214,209,205,199,193,187,181,172,164,155,145,134,122,109,94,77,59,40,19,1,1,1,1,1, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,254,254,254,254,254,254,254,254,254,254,254,254,252,252,252,252,252,251,251,251,251,249,249,247,247,245,245,244,244,242,240,238,237,235,233,230,228,224,223,219,214,210,207,202,196,189,182,175,168,158,149,139,126,114,100,84,67,48,27,4,1,1,1,1,1,1 + }; + + public static byte[] DecodeImaAdpcmTableDriven8Bit(byte[] abIn) { + ArrayList alsPcm = new ArrayList(); + + byte bPredicted = 128; + int nStepIndex = 0; + + foreach (byte bT in abIn) { + int nDelta = bT >> 4; + bPredicted += s_anStepFractions8BitDecode[nDelta * 89 + nStepIndex]; + alsPcm.Add(bPredicted); + nStepIndex = s_anStepIndexMap[nDelta * 89 + nStepIndex]; + nDelta = bT & 0xf; + bPredicted += s_anStepFractions8BitDecode[nDelta * 89 + nStepIndex]; + alsPcm.Add(bPredicted); + nStepIndex = s_anStepIndexMap[nDelta * 89 + nStepIndex]; + } + + return (byte[])alsPcm.ToArray(typeof(byte)); + } + + public static byte[] EncodeYamahaAdpcm(short[] ashPcm) { + ArrayList alsEncoded = new ArrayList(); + int nPcmPredicted = 0; + int nStep = 0x7f; + byte bOutput = 0; + + for (int ish = 0; ish < ashPcm.Length; ish++) { + //Console.WriteLine("ish: " + ish + " nPcmPredicted: " + nPcmPredicted + " nStep: " + nStep); + + //if (ish == 256) + // Debug.Assert(false); + + int nPcm = ashPcm[ish]; + + int nDiff = nPcm - nPcmPredicted; + bool fNegative = false; + if (nDiff < 0) { + fNegative = true; + nDiff = -nDiff; + } + + // Get fraction in multiples of 1/4ths. Note by first shifting down by two, resolution is lost. + // I.e. x / 4 * 2 != x / 2. + + int cTimes = 0; + int nStep4th = nStep / 4; + int nFracT = nStep4th; + while (cTimes < 7) { + if (nDiff < nFracT) + break; + nFracT += nStep4th; + cTimes++; + } + + // Interestingly this "rebuilding" from bits has more precision that the above + // because of the above nStep / 4. The nStep / 2 below has more precision than + // 2 * (nStep / 4). For example 23 / 4 * 2 == 10 vs. 23 / 2 == 11. + + int nStepFraction = 0; + if ((cTimes & 1) != 0) + nStepFraction += nStep / 4; + if ((cTimes & 2) != 0) + nStepFraction += nStep / 2; + if ((cTimes & 4) != 0) + nStepFraction += nStep; + nStepFraction += nStep / 8; + + // Add to predicted, do range check + + byte b = (byte)cTimes; + if (fNegative) { + nPcmPredicted -= nStepFraction; + b |= 8; + } else { + nPcmPredicted += nStepFraction; + } + if (nPcmPredicted > 32767) { + nPcmPredicted = 32767; + } else if (nPcmPredicted < -32768) { + nPcmPredicted = -32768; + } + + // Yamaha calcs the next step like this. Same as MS ADPCM in terms of constants used + + switch (b & 7) { + case 0: + case 1: + case 2: + case 3: + nStep = nStep * 230 / 256; + break; + + case 4: + nStep = nStep * 307 / 256; + break; + + case 5: + nStep = nStep * 409 / 256; + break; + + case 6: + nStep = nStep * 512 / 256; + break; + + case 7: + nStep = nStep * 614 / 256; + break; + } + + // Bounds check the step + + if (nStep < 0x7f) { + nStep = 0x7f; + } else if (nStep > 0x6000) { + nStep = 0x6000; + } + + // Output a byte. Yamaha uses "little endian" nibble order. + + if ((ish & 1) == 0) { + bOutput = (byte)(b & 0x0f); + } else { + bOutput |= (byte)((b << 4) & 0xf0); + alsEncoded.Add(bOutput); + } + } + + return (byte[])alsEncoded.ToArray(typeof(byte)); + } + + public static byte[] EncodeYamahaAdpcmFaster(short[] ashPcm) { + ArrayList alsEncoded = new ArrayList(); + int nPcmPredicted = 0; + int nStep = 0x7f; + + // Encoding test for "fastest" encoding. Essentially this unrolls the conditionals as much as possible. + // This is an algorithmic test before encoding this in assembly language + + for (int ish = 0; ish < ashPcm.Length; ish += 2) { + //Console.WriteLine("ish: " + ish + " nPcmPredicted: " + nPcmPredicted + " nStep: " + nStep); + + //if (ish == 256) + // Debug.Assert(false); + + byte bOut; + int nDiff; + + // Low nibble (first sample of byte) + + nDiff = ashPcm[ish] - nPcmPredicted; + if (nDiff >= 0) { + // nDiff positive + + int nStepT = nStep & ~3; + if (nDiff >= nStepT) { + nDiff -= nStepT; + nStepT = nStepT >> 1; + if (nDiff >= nStepT) { + nDiff -= nStepT; + nStepT >>= 1; + if (nDiff >= nStepT) { + // case 0x07 + + nPcmPredicted += nStep + (nStep >> 1) + nStepT + (nStepT >> 1); + if (nPcmPredicted > 32767) + nPcmPredicted = 32767; + nStep = (nStep * 614) >> 8; + if (nStep > 0x6000) + nStep = 0x6000; + bOut = 0x07; + } else { + // case 0x06 + + nPcmPredicted += nStep + (nStep >> 1) + (nStepT >> 1); + if (nPcmPredicted > 32767) + nPcmPredicted = 32767; + nStep <<= 1; + if (nStep > 0x6000) + nStep = 0x6000; + bOut = 0x06; + } + } else { + nStepT >>= 1; + if (nDiff >= nStepT) { + // case 0x05 + + nPcmPredicted += nStep + nStepT + (nStepT >> 1); + if (nPcmPredicted > 32767) + nPcmPredicted = 32767; + nStep = (nStep * 409) >> 8; + if (nStep > 0x6000) + nStep = 0x6000; + bOut = 0x05; + } else { + // case 0x04 + + nPcmPredicted += nStep + (nStepT >> 1); + if (nPcmPredicted > 32767) + nPcmPredicted = 32767; + nStep = (nStep * 307) >> 8; + if (nStep > 0x6000) + nStep = 0x6000; + bOut = 0x04; + } + } + } else { + nStepT >>= 1; + if (nDiff >= nStepT) { + nDiff -= nStepT; + nStepT >>= 1; + if (nDiff >= nStepT) { + // case 0x03 + + nPcmPredicted += (nStep >> 1) + nStepT + (nStepT >> 1); + if (nPcmPredicted > 32767) + nPcmPredicted = 32767; + nStep = (nStep * 230) >> 8; + if (nStep < 0x7f) + nStep = 0x7f; + bOut = 0x03; + } else { + // case 0x02 + + nPcmPredicted += (nStep >> 1) + (nStepT >> 1); + if (nPcmPredicted > 32767) + nPcmPredicted = 32767; + nStep = (nStep * 230) >> 8; + if (nStep < 0x7f) + nStep = 0x7f; + bOut = 0x02; + } + } else { + nStepT >>= 1; + if (nDiff >= nStepT) { + // case 0x01 + + nPcmPredicted += nStepT + (nStepT >> 1); + if (nPcmPredicted > 32767) + nPcmPredicted = 32767; + nStep = (nStep * 230) >> 8; + if (nStep < 0x7f) + nStep = 0x7f; + bOut = 0x01; + } else { + // case 0x00 + + nPcmPredicted += (nStepT >> 1); + if (nPcmPredicted > 32767) + nPcmPredicted = 32767; + nStep = (nStep * 230) >> 8; + if (nStep < 0x7f) + nStep = 0x7f; + bOut = 0x00; + } + } + } + } else { + // nDiff negative + + nDiff = -nDiff; + int nStepT = nStep & ~3; + if (nDiff >= nStepT) { + nDiff -= nStepT; + nStepT >>= 1; + if (nDiff >= nStepT) { + nDiff -= nStepT; + nStepT >>= 1; + if (nDiff >= nStepT) { + // case 0x0f + + nPcmPredicted -= nStep + (nStep >> 1) + nStepT + (nStepT >> 1); + if (nPcmPredicted < -32768) + nPcmPredicted = -32768; + nStep = (nStep * 614) >> 8; + if (nStep > 0x6000) + nStep = 0x6000; + bOut = 0x0f; + } else { + // case 0x0e + + nPcmPredicted -= nStep + (nStep >> 1) + (nStepT >> 1); + if (nPcmPredicted < -32768) + nPcmPredicted = -32768; + nStep <<= 1; + if (nStep > 0x6000) + nStep = 0x6000; + bOut = 0x0e; + } + } else { + nStepT >>= 1; + if (nDiff >= nStepT) { + // case 0x0d + + nPcmPredicted -= nStep + nStepT + (nStepT >> 1); + if (nPcmPredicted < -32768) + nPcmPredicted = -32768; + nStep = (nStep * 409) >> 8; + if (nStep > 0x6000) + nStep = 0x6000; + bOut = 0x0d; + } else { + // case 0x0c + + nPcmPredicted -= nStep + (nStepT >> 1); + if (nPcmPredicted < -32768) + nPcmPredicted = -32768; + nStep = (nStep * 307) >> 8; + if (nStep > 0x6000) + nStep = 0x6000; + bOut = 0x0c; + } + } + } else { + nStepT >>= 1; + if (nDiff >= nStepT) { + nDiff -= nStepT; + nStepT >>= 1; + if (nDiff >= nStepT) { + // case 0x0b + + nPcmPredicted -= (nStep >> 1) + nStepT + (nStepT >> 1); + if (nPcmPredicted < -32768) + nPcmPredicted = -32768; + nStep = (nStep * 230) >> 8; + if (nStep < 0x7f) + nStep = 0x7f; + bOut = 0x0b; + } else { + // case 0x0a + + nPcmPredicted -= (nStep >> 1) + (nStepT >> 1); + if (nPcmPredicted < -32768) + nPcmPredicted = -32768; + nStep = (nStep * 230) >> 8; + if (nStep < 0x7f) + nStep = 0x7f; + bOut = 0x0a; + } + } else { + nStepT >>= 1; + if (nDiff >= nStepT) { + // case 0x09 + + nPcmPredicted -= nStepT + (nStepT >> 1); + if (nPcmPredicted < -32768) + nPcmPredicted = -32768; + nStep = (nStep * 230) >> 8; + if (nStep < 0x7f) + nStep = 0x7f; + bOut = 0x09; + } else { + // case 0x08 + + nPcmPredicted -= (nStepT >> 1); + if (nPcmPredicted < -32768) + nPcmPredicted = -32768; + nStep = (nStep * 230) >> 8; + if (nStep < 0x7f) + nStep = 0x7f; + bOut = 0x08; + } + } + } + } + + // High nibble, second sample of byte + + nDiff = ashPcm[ish + 1] - nPcmPredicted; + if (nDiff >= 0) { + // nDiff positive + + int nStepT = nStep & ~3; + if (nDiff >= nStepT) { + nDiff -= nStepT; + nStepT >>= 1; + if (nDiff >= nStepT) { + nDiff -= nStepT; + nStepT >>= 1; + if (nDiff >= nStepT) { + // case 0x07 + + nPcmPredicted += nStep + (nStep >> 1) + nStepT + (nStepT >> 1); + if (nPcmPredicted > 32767) + nPcmPredicted = 32767; + nStep = (nStep * 614) >> 8; + if (nStep > 0x6000) + nStep = 0x6000; + alsEncoded.Add((byte)(0x70 + bOut)); + } else { + // case 0x06 + + nPcmPredicted += nStep + (nStep >> 1) + (nStepT >> 1); + if (nPcmPredicted > 32767) + nPcmPredicted = 32767; + nStep <<= 1; + if (nStep > 0x6000) + nStep = 0x6000; + alsEncoded.Add((byte)(0x60 + bOut)); + } + } else { + nStepT >>= 1; + if (nDiff >= nStepT) { + // case 0x05 + + nPcmPredicted += nStep + nStepT + (nStepT >> 1); + if (nPcmPredicted > 32767) + nPcmPredicted = 32767; + nStep = (nStep * 409) >> 8; + if (nStep > 0x6000) + nStep = 0x6000; + alsEncoded.Add((byte)(0x50 + bOut)); + } else { + // case 0x04 + + nPcmPredicted += nStep + (nStepT >> 1); + if (nPcmPredicted > 32767) + nPcmPredicted = 32767; + nStep = (nStep * 307) >> 8; + if (nStep > 0x6000) + nStep = 0x6000; + alsEncoded.Add((byte)(0x40 + bOut)); + } + } + } else { + nStepT >>= 1; + if (nDiff >= nStepT) { + nDiff -= nStepT; + nStepT >>= 1; + if (nDiff >= nStepT) { + // case 0x03 + + nPcmPredicted += (nStep >> 1) + nStepT + (nStepT >> 1); + if (nPcmPredicted > 32767) + nPcmPredicted = 32767; + nStep = (nStep * 230) >> 8; + if (nStep < 0x7f) + nStep = 0x7f; + alsEncoded.Add((byte)(0x30 + bOut)); + } else { + // case 0x02 + + nPcmPredicted += (nStep >> 1) + (nStepT >> 1); + if (nPcmPredicted > 32767) + nPcmPredicted = 32767; + nStep = (nStep * 230) >> 8; + if (nStep < 0x7f) + nStep = 0x7f; + alsEncoded.Add((byte)(0x20 + bOut)); + } + } else { + nStepT >>= 1; + if (nDiff >= nStepT) { + // case 0x01 + + nPcmPredicted += nStepT + (nStepT >> 1); + if (nPcmPredicted > 32767) + nPcmPredicted = 32767; + nStep = (nStep * 230) >> 8; + if (nStep < 0x7f) + nStep = 0x7f; + alsEncoded.Add((byte)(0x10 + bOut)); + } else { + // case 0x00 + + nPcmPredicted += (nStepT >> 1); + if (nPcmPredicted > 32767) + nPcmPredicted = 32767; + nStep = (nStep * 230) >> 8; + if (nStep < 0x7f) + nStep = 0x7f; + alsEncoded.Add((byte)(0x00 + bOut)); + } + } + } + } else { + // nDiff negative + + nDiff = -nDiff; + int nStepT = nStep & ~3; + if (nDiff >= nStepT) { + nDiff -= nStepT; + nStepT >>= 1; + if (nDiff >= nStepT) { + nDiff -= nStepT; + nStepT >>= 1; + if (nDiff >= nStepT) { + // case 0x0f + + nPcmPredicted -= nStep + (nStep >> 1) + nStepT + (nStepT >> 1); + if (nPcmPredicted < -32768) + nPcmPredicted = -32768; + nStep = (nStep * 614) >> 8; + if (nStep > 0x6000) + nStep = 0x6000; + alsEncoded.Add((byte)(0xf0 + bOut)); + } else { + // case 0x0e + + nPcmPredicted -= nStep + (nStep >> 1) + (nStepT >> 1); + if (nPcmPredicted < -32768) + nPcmPredicted = -32768; + nStep <<= 1; + if (nStep > 0x6000) + nStep = 0x6000; + alsEncoded.Add((byte)(0xe0 + bOut)); + } + } else { + nStepT >>= 1; + if (nDiff >= nStepT) { + // case 0x0d + + nPcmPredicted -= nStep + nStepT + (nStepT >> 1); + if (nPcmPredicted < -32768) + nPcmPredicted = -32768; + nStep = (nStep * 409) >> 8; + if (nStep > 0x6000) + nStep = 0x6000; + alsEncoded.Add((byte)(0xd0 + bOut)); + } else { + // case 0x0c + + nPcmPredicted -= nStep + (nStepT >> 1); + if (nPcmPredicted < -32768) + nPcmPredicted = -32768; + nStep = (nStep * 307) >> 8; + if (nStep > 0x6000) + nStep = 0x6000; + alsEncoded.Add((byte)(0xc0 + bOut)); + } + } + } else { + nStepT >>= 1; + if (nDiff >= nStepT) { + nDiff -= nStepT; + nStepT >>= 1; + if (nDiff >= nStepT) { + // case 0x0b + + nPcmPredicted -= (nStep >> 1) + nStepT + (nStepT >> 1); + if (nPcmPredicted < -32768) + nPcmPredicted = -32768; + nStep = (nStep * 230) >> 8; + if (nStep < 0x7f) + nStep = 0x7f; + alsEncoded.Add((byte)(0xb0 + bOut)); + } else { + // case 0x0a + + nPcmPredicted -= (nStep >> 1) + (nStepT >> 1); + if (nPcmPredicted < -32768) + nPcmPredicted = -32768; + nStep = (nStep * 230) >> 8; + if (nStep < 0x7f) + nStep = 0x7f; + alsEncoded.Add((byte)(0xa0 + bOut)); + } + } else { + nStepT >>= 1; + if (nDiff >= nStepT) { + // case 0x09 + + nPcmPredicted -= nStepT + (nStepT >> 1); + if (nPcmPredicted < -32768) + nPcmPredicted = -32768; + nStep = (nStep * 230) >> 8; + if (nStep < 0x7f) + nStep = 0x7f; + alsEncoded.Add((byte)(0x90 + bOut)); + } else { + // case 0x08 + + nPcmPredicted -= (nStepT >> 1); + if (nPcmPredicted < -32768) + nPcmPredicted = -32768; + nStep = (nStep * 230) >> 8; + if (nStep < 0x7f) + nStep = 0x7f; + alsEncoded.Add((byte)(0x80 + bOut)); + } + } + } + } + } + + return (byte[])alsEncoded.ToArray(typeof(byte)); + } + + public static void Write8BitAdpcmDecodeTable() { + TextWriter tw = new StreamWriter("decode.s"); + + tw.WriteLine("Decode8BitAdpcm:"); + for (int nStepIndex = 0; nStepIndex < 89; nStepIndex++) { + tw.WriteLine("| Step Index " + nStepIndex); + tw.Write(".byte "); + for (int nDelta = 0; nDelta < 16; nDelta++) + tw.Write(s_anStepFractions8BitDecode[nDelta * 89 + nStepIndex] + (nDelta == 15 ? "\n" : ", ")); + tw.Write(".word "); + for (int nDelta = 0; nDelta < 16; nDelta++) { + int offCurrentIndex = 48 * nStepIndex; + int nStepIndexNew = s_anStepIndexMap[nDelta * 89 + nStepIndex]; + int offNewIndex = 48 * nStepIndexNew; + tw.Write("0x" + ((ushort)(offNewIndex - offCurrentIndex)).ToString("x") + (nDelta == 15 ? "\n" : ", ")); + } + } + + tw.Close(); + } + + public static void Write8BitAdpcmDecodeTableC() { + TextWriter tw = new StreamWriter("decode.cpp"); + + tw.WriteLine("byte gabAdpcmSteppings[89][16] = {"); + for (int nStepIndex = 0; nStepIndex < 89; nStepIndex++) { + tw.Write(" "); + for (int nDelta = 0; nDelta < 16; nDelta++) + tw.Write(s_anStepFractions8BitDecode[nDelta * 89 + nStepIndex] + (nDelta == 15 ? ",\n" : ", ")); + } + tw.WriteLine("};"); + + tw.WriteLine("char s_anStepIndexMap[89][16] = {"); + for (int nStepIndex = 0; nStepIndex < 89; nStepIndex++) { + tw.Write(" "); + for (int nDelta = 0; nDelta < 16; nDelta++) + tw.Write(s_anStepIndexMap[nDelta * 89 + nStepIndex] + (nDelta == 15 ? ",\n" : ", ")); + } + tw.WriteLine("};"); + tw.Close(); + } + } + + class Pcm { + int m_cBits; + int m_hz; + byte[] m_ab; + short[] m_ash; + + // See http://www.intersrv.com/~dcross/wavio.html for format info, or + // http://ccrma-www.stanford.edu/courses/422/projects/WaveFormat/ + + [StructLayout(LayoutKind.Sequential)] + struct FourCC { + public byte b0; + public byte b1; + public byte b2; + public byte b3; + + public FourCC(string str) { + b0 = (byte)str[0]; + b1 = (byte)str[1]; + b2 = (byte)str[2]; + b3 = (byte)str[3]; + } + }; + + [StructLayout(LayoutKind.Sequential)] + struct WavFileHeader { + public FourCC fccRIFF; + public uint cbRiffBytes; + public FourCC fccWAVE; + public FourCC fccfmt; + public uint cbfmtBytes; + public ushort wFormatTag; + public ushort nChannels; + public uint nSamplesPerSec; + public uint nAvgBytesPerSec; + public ushort nBlockAlign; + public ushort nBitsPerSample; + public FourCC fccdata; + public uint cbdataBytes; + }; + + unsafe public Pcm (string strWav) { + if (strWav.EndsWith(".wav") || strWav.EndsWith(".WAV")) { + LoadWavFile(strWav); + } + if (strWav.EndsWith(".snd")) { + LoadSndFile(strWav); + } + } + + unsafe void LoadWavFile(string strWav) { + BinaryReader brdr = new BinaryReader(new FileStream(strWav, FileMode.Open, FileAccess.Read, FileShare.Read)); + byte[] ab = brdr.ReadBytes((int)brdr.BaseStream.Length); + brdr.Close(); + + WavFileHeader *pwavhdr = (WavFileHeader *)Marshal.UnsafeAddrOfPinnedArrayElement(ab, 0); + bool fOk = true; + if (pwavhdr->wFormatTag != 1) + fOk = false; + if (pwavhdr->nBitsPerSample != 8 && pwavhdr->nBitsPerSample != 16) + fOk = false; + if (!fOk) + throw new Exception("Only support reading 8 / 16 bit raw pcm!"); + m_cBits = pwavhdr->nBitsPerSample; + m_hz = (int)pwavhdr->nSamplesPerSec; + int cbSizeType = Marshal.SizeOf(pwavhdr->GetType()); + switch (m_cBits) { + case 8: + m_ab = new byte[pwavhdr->cbdataBytes]; + for (int i = 0; i < m_ab.Length; i++) + m_ab[i] = ab[i + cbSizeType]; + break; + + case 16: + m_ash = new short[pwavhdr->cbdataBytes / 2]; + for (int i = 0; i < pwavhdr->cbdataBytes; i += 2) + m_ash[i / 2] = (short)(ab[i + cbSizeType] + ab[1 + i + cbSizeType] * 256); + break; + } + } + + void LoadSndFile(string strSnd) { + BinaryReader brdr = new BinaryReader(new FileStream(strSnd, FileMode.Open, FileAccess.Read, FileShare.Read)); + byte[] ab = brdr.ReadBytes((int)brdr.BaseStream.Length); + brdr.Close(); + + m_cBits = 8; + m_hz = 8000; + m_ab = new byte[ab.Length - 6]; + for (int i = 0; i < m_ab.Length; i++) + m_ab[i] = ab[i + 6]; + } + + public Pcm(int hz, byte[] ab) { + m_cBits = 8; + m_hz = hz; + m_ab = ab; + } + + public Pcm(int hz, short[] ash) { + m_cBits = 16; + m_hz = hz; + m_ash = ash; + } + + public int BitsPerSample { + get { + return m_cBits; + } + } + + public int Hertz { + get { + return m_hz; + } + } + + public byte[] Data8Bit { + get { + return (byte[])m_ab.Clone(); + } + set { + m_cBits = 8; + m_ash = null; + m_ab = (byte[])value.Clone(); + } + } + + public short[] Data16Bit { + get { + return (short[])m_ash.Clone(); + } + set { + m_cBits = 16; + m_ab = null; + m_ash = (short[])value.Clone(); + } + } + + public void ConvertTo16Bit() { + if (m_cBits == 16) + return; + + // 8 bit unsigned (0,127==-128,-1 and 128,255==0,127) to + // 16 bit signed (-32768 -> 32767) + short[] ash = new short[m_ab.Length]; + for (int i = 0; i < ash.Length; i++) { + ash[i] = (short)((short)m_ab[i] * 256); + ash[i] = (short)(ash[i] ^ 0x8000); + } + Data16Bit = ash; + } + + public void ConvertTo8Bit() { + if (m_cBits == 8) + return; + + // 16 bit signed (-32768 -> 32767) to + // 8 bit unsigned (0,127==-128,-1 and 128,255==0,127) + + byte[] ab = new byte[m_ash.Length]; + for (int i = 0; i < ab.Length; i++) + ab[i] = (byte)((m_ash[i] ^ 0x8000) / 256); + Data8Bit = ab; + } + + public void SetMaxSize(int cbMax) { + if (m_cBits == 8) { + if (m_ab.Length > cbMax) { + byte[] abT = new byte[32700]; + Array.Copy(m_ab, 0, abT, 0, abT.Length); + m_ab = abT; + } + } else { + if (m_ash.Length * 2 > cbMax) { + short[] ashT = new short[cbMax / 2]; + Array.Copy(m_ash, 0, ashT, 0, ashT.Length); + m_ash = ashT; + } + } + } + + [DllImportAttribute("winmm.dll")] + private static extern int PlaySoundW(IntPtr pbSound, IntPtr hModule, uint fdwSound); + const UInt32 SND_SYNC = 0x0000; // play synchronously (default) + const UInt32 SND_ASYNC = 0x0001; // play asynchronously + const UInt32 SND_NODEFAULT = 0x0002; // silence (!default) if sound not found + const UInt32 SND_MEMORY = 0x0004; // pszSound points to a memory file + const UInt32 SND_LOOP = 0x0008; // loop the sound until next sndPlaySound + const UInt32 SND_NOSTOP = 0x0010; // don't stop any currently playing sound + const UInt32 SND_NOWAIT = 0x00002000; // don't wait if the driver is busy + const UInt32 SND_ALIAS = 0x00010000; // name is a registry alias + const UInt32 SND_ALIAS_ID = 0x00110000; // alias is a predefined ID + const UInt32 SND_FILENAME = 0x00020000; // name is file name + const UInt32 SND_RESOURCE = 0x00040004; // name is resource name or atom + const UInt32 SND_PURGE = 0x0040; + const UInt32 SND_APPLICATION = 0x0080; // look for application specific association + + unsafe public void Play() { + // Fill in the header + + if (m_ab == null && m_ash == null) + return; + + int cbData = m_cBits == 8 ? m_ab.Length : m_ash.Length * 2; + + WavFileHeader wavhdr; + wavhdr.fccRIFF = new FourCC("RIFF"); + wavhdr.cbRiffBytes = (uint)(Marshal.SizeOf(typeof(WavFileHeader)) - 8 + cbData); + wavhdr.fccWAVE = new FourCC("WAVE"); + wavhdr.fccfmt = new FourCC("fmt "); + wavhdr.cbfmtBytes = 16; + wavhdr.wFormatTag = 1; + wavhdr.nChannels = 1; + wavhdr.nSamplesPerSec = (uint)m_hz; + wavhdr.nAvgBytesPerSec = (uint)(m_cBits / 8 * m_hz); + wavhdr.nBlockAlign = (ushort)(m_cBits / 8); + wavhdr.nBitsPerSample = (ushort)m_cBits; + wavhdr.fccdata = new FourCC("data"); + wavhdr.cbdataBytes = (uint)cbData; + + // Serialize the data + + byte[] ab = SerializeStructure(null, &wavhdr, Marshal.SizeOf(wavhdr.GetType())); + switch (m_cBits) { + case 8: + ab = SerializeStructure(ab, (void *)Marshal.UnsafeAddrOfPinnedArrayElement(m_ab, 0), m_ab.Length); + break; + + case 16: + ab = SerializeStructure(ab, (void *)Marshal.UnsafeAddrOfPinnedArrayElement(m_ash, 0), m_ash.Length * 2); + break; + } + + // Play the sound + + IntPtr ptr = Marshal.UnsafeAddrOfPinnedArrayElement(ab, 0); + int nSuccess = PlaySoundW(ptr, IntPtr.Zero, SND_ASYNC | SND_MEMORY); + Debug.Assert(nSuccess != 0); + } + + unsafe static byte[] SerializeStructure(byte[] ab, void *pv, int cb) { + byte *pb = (byte *)pv; + byte[] abNew = null; + if (ab == null) { + abNew = new byte[cb]; + for (int i = 0; i < cb; i++) + abNew[i] = pb[i]; + } else { + abNew = new byte[ab.Length + cb]; + for (int i = 0; i < ab.Length; i++) + abNew[i] = ab[i]; + for (int i = 0; i < cb; i++) + abNew[i + ab.Length] = pb[i]; + } + + return abNew; + } + + unsafe public static void PlayWavFile(string strWav) { + // Get the bytes + + BinaryReader brdr = new BinaryReader(new FileStream(strWav, FileMode.Open)); + byte[] ab = brdr.ReadBytes((int)brdr.BaseStream.Length); + brdr.Close(); + + // Play the wav + + IntPtr ptr = Marshal.UnsafeAddrOfPinnedArrayElement(ab, 0); + int nSuccess = PlaySoundW(ptr, IntPtr.Zero, SND_ASYNC | SND_MEMORY); + Debug.Assert(nSuccess != 0); + } + + public byte[] GetSndEncoding() { + ConvertTo8Bit(); + SetMaxSize(32700 * 2); + return AudioFormats.EncodeImaAdpcmTableDriven8Bit(Data8Bit); + } + +#if false + static byte[] MassageSample(byte[] abPcm) { + // Need to make sure that the encoded size is a multiple of 2 bytes in size, which means the + // raw size needs to be a multiple of 4 in size, and need to trail the amplitude to 128 to + // reduce click sounds. + + byte[] abPcmNew = new byte[(abPcm.Length + 7) & ~3]; + + // Copy in the existing pcm + + Array.Copy(abPcm, 0, abPcmNew, 0, abPcm.Length); + + // Perform fade + + int cSamplesFade = abPcmNew.Length - abPcm.Length; + int nSampleLast = (int)abPcm[abPcm.Length - 1]; + int nStep = -((nSampleLast - 128) / cSamplesFade); + for (int iSample = abPcm.Length; iSample < abPcmNew.Length; iSample++) { + if (iSample == abPcmNew.Length - 1) { + nSampleLast = 0; + } else { + nSampleLast += nStep; + } + abPcmNew[iSample] = (byte)nSampleLast; + } + + return abPcmNew; + } +#endif + } +} diff --git a/SpiffLib/PdbPacker.cs b/SpiffLib/PdbPacker.cs new file mode 100644 index 0000000..4aabb9e --- /dev/null +++ b/SpiffLib/PdbPacker.cs @@ -0,0 +1,337 @@ +using System; +using System.IO; +using System.Collections; +using System.Diagnostics; + +namespace SpiffLib { + public class PdbPacker { + static int s_cbRecordMax = 32000; + static int s_cbFilenameMax = 29; + ArrayList m_alsFiles = new ArrayList(); + + public class File { + public string str; + public byte[] ab; + public bool fCompress; + int m_cbCompressed; + + public File() { + } + + public File(string strT, byte[] abT, bool fCompressT, int cbCompressed) { + str = strT; + ab = abT; + fCompress = fCompressT; + m_cbCompressed = cbCompressed; + } + + public File(string strT, byte[] abT, bool fCompressT) { + str = strT; + ab = abT; + fCompress = fCompressT; + } + + public File(string strT, byte[] abT) { + str = strT; + ab = abT; + fCompress = true; + } + + public int GetCompressedSize() { + return m_cbCompressed; + } + } + + public PdbPacker() { + } + + public PdbPacker(string strFile) { + Load(strFile); + } + + public int Count { + get { + return m_alsFiles.Count; + } + } + + public void Add(File file) { + m_alsFiles.Add(file); + } + + public File this[string strFile] { + get { + foreach (File file in m_alsFiles) { + if (file.str.ToLower() == strFile.ToLower()) + return file; + } + return null; + } + } + + public File this[int iFile] { + get { + return (File)m_alsFiles[iFile]; + } + } + + struct DirEntry { + public string strFile; + public byte crec; + public ushort irec; + }; + + public void Load(string strFile) { + // Open palm database + + PalmDatabase pdb = new PalmDatabase(); + pdb.Load(strFile); + + // Load directory entries + + ArrayList alsRecordData = pdb.GetRecordData(); + CompressionHeader coh; + DirEntry[] ade = ParseDirectoryRecord(UnpackRecord((byte[])alsRecordData[0], out coh)); + + // Read in the file data + + m_alsFiles.Clear(); + foreach (DirEntry de in ade) { + // Decompress and write out bytes + + ArrayList alsFileBytes = new ArrayList(); + for (int i = 0; i < de.crec; i++) { + byte[] ab = UnpackRecord((byte[])alsRecordData[i + de.irec], out coh); + alsFileBytes.AddRange(ab); + } + + File file = new File(de.strFile, (byte[])alsFileBytes.ToArray(typeof(byte)), coh.fCompressed, coh.cbCompressed); + m_alsFiles.Add(file); + } + } + + // Straight ascii order comparer, since at runtime we use a strcmp based binary search + + public class FilenameComparer : IComparer { + public int Compare(object objA, object objB) { + string strA = (string)objA; + string strB = (string)objB; + int cch = Math.Min(strA.Length, strB.Length); + for (int ich = 0; ich < cch; ich++) { + if (strA[ich] < strB[ich]) + return -1; + if (strA[ich] > strB[ich]) + return 1; + } + + if (strA.Length < strB.Length) + return -1; + if (strA.Length > strB.Length) + return 1; + + return 0; + } + } + + public void Save(string strFilePdb, string strCreatorId, string strTypeId) { + // First sort all the files by name + + string[] astrFileNames = new string[m_alsFiles.Count]; + for (int iFile = 0; iFile < m_alsFiles.Count; iFile++) { + File file = (File)m_alsFiles[iFile]; + astrFileNames[iFile] = file.str.ToLower(); + } + File[] afile = (File[])m_alsFiles.ToArray(typeof(File)); + Array.Sort(astrFileNames, afile, new FilenameComparer()); + + // Create the record bytes + + ArrayList alsDirEntries = new ArrayList(); + ArrayList alsRecords = new ArrayList(); + ushort irec = 1; + foreach (File file in afile) { + // Get filename only, check length + + string strFile = file.str.ToLower(); + if (strFile.Length >= s_cbFilenameMax) + throw new Exception("The file " + strFile + " is too long. Must be " + (s_cbFilenameMax - 1) + "chars max."); + + // Read the file into chunks + + BinaryReader brdr = new BinaryReader(new MemoryStream(file.ab)); + byte crec = 0; + while (brdr.BaseStream.Position < brdr.BaseStream.Length) { + int cbLeft = (int)(brdr.BaseStream.Length - brdr.BaseStream.Position); + int cbRead = cbLeft < s_cbRecordMax ? cbLeft : s_cbRecordMax; + byte[] abChunk = brdr.ReadBytes(cbRead); + alsRecords.Add(PackRecord(abChunk, file.fCompress)); + crec++; + } + brdr.Close(); + + // Make a directory entry for this file + + DirEntry de = new DirEntry(); + de.strFile = strFile; + de.irec = irec; + de.crec = crec; + alsDirEntries.Add(de); + irec += de.crec; + +#if false + Console.WriteLine(de.strFile + ", " + de.crec + " recs"); +#endif + } + + // Insert the record for the directory entries + + alsRecords.Insert(0, PackRecord(MakeDirectoryRecord(alsDirEntries), false)); + + // Create and save a .pdb around these records + + PalmDatabase pdb = new PalmDatabase(); + pdb.SetRecordData(alsRecords); + pdb.Name = Path.GetFileName(Path.GetFullPath(strFilePdb)); + pdb.CreatorId = IdFromString(strCreatorId); + pdb.TypeId = IdFromString(strTypeId); + pdb.Save(strFilePdb); + } + + static byte[] PackRecord(byte[] abChunk, bool fCompress) { + ArrayList alsRecordBytes = new ArrayList(); + byte[] abCompressed = null; + if (fCompress) { + abCompressed = Compressor.CompressChunk(abChunk); + if (abCompressed.Length >= abChunk.Length) + abCompressed = null; + } + if (abCompressed != null) { + alsRecordBytes.Add((byte)0); + alsRecordBytes.Add((byte)1); + alsRecordBytes.Add((byte)((abChunk.Length >> 8) & 0xff)); + alsRecordBytes.Add((byte)(abChunk.Length & 0xff)); + alsRecordBytes.Add((byte)((abCompressed.Length >> 8) & 0xff)); + alsRecordBytes.Add((byte)(abCompressed.Length & 0xff)); + alsRecordBytes.AddRange(abCompressed); + } else { + alsRecordBytes.Add((byte)0); + alsRecordBytes.Add((byte)0); + alsRecordBytes.Add((byte)((abChunk.Length >> 8) & 0xff)); + alsRecordBytes.Add((byte)(abChunk.Length & 0xff)); + alsRecordBytes.Add((byte)((abChunk.Length >> 8) & 0xff)); + alsRecordBytes.Add((byte)(abChunk.Length & 0xff)); + alsRecordBytes.AddRange(abChunk); + } + return (byte[])alsRecordBytes.ToArray(typeof(byte)); + } + + struct CompressionHeader { + public bool fCompressed; + public ushort cbUncompressed; + public ushort cbCompressed; + } + + static byte[] UnpackRecord(byte[] ab, out CompressionHeader coh) { + coh.fCompressed = (ab[1] == 1); + coh.cbUncompressed = (ushort)((ab[2] << 8) + ab[3]); + coh.cbCompressed = (ushort)((ab[4] << 8) + ab[5]); + + byte[] abT = new byte[ab.Length - 6]; + Array.Copy(ab, 6, abT, 0, abT.Length); + if (coh.fCompressed) { + ab = Compressor.DecompressChunk(abT); + } else { + ab = abT; + } + + return ab; + } + + static uint IdFromString(string str) { + str.PadLeft(4, ' '); + uint ui = 0; + for (int i = 0; i < 4; i++) { + ui <<= 8; + ui |= (uint)str[i]; + } + return ui; + } + + static byte[] MakeDirectoryRecord(ArrayList alsDirEntries) { + // Each entry is 32 bytes. Serialize into a byte array. + // + // struct DirEntry { + // char szFn[kcbFilename]; + // byte crec; + // word irec; + // }; + + ArrayList alsPdbDirEntries = new ArrayList(); + foreach (DirEntry de in alsDirEntries) { + byte[] abFilename = new byte[s_cbFilenameMax]; + for (int ich = 0; ich < de.strFile.Length; ich++) + abFilename[ich] = (byte)de.strFile[ich]; + alsPdbDirEntries.AddRange(abFilename); + alsPdbDirEntries.Add((byte)de.crec); + alsPdbDirEntries.Add((byte)((de.irec >> 8) & 0xff)); + alsPdbDirEntries.Add((byte)(de.irec & 0xff)); + } + return (byte[])alsPdbDirEntries.ToArray(typeof(byte)); + } + + static DirEntry[] ParseDirectoryRecord(byte[] ab) { + ArrayList alsPdbDirEntries = new ArrayList(); + for (int ib = 0; ib < ab.Length; ib += 32) { + DirEntry de; + de.strFile = ""; + for (int ich = 0; ich < s_cbFilenameMax; ich++) { + char ch = (char)ab[ib + ich]; + if (ch == 0) + break; + de.strFile += ch; + } + de.crec = (byte)ab[ib + s_cbFilenameMax]; + de.irec = (ushort)((ab[ib + s_cbFilenameMax + 1] << 8) + ab[ib + s_cbFilenameMax + 2]); + alsPdbDirEntries.Add(de); + } + return (DirEntry[])alsPdbDirEntries.ToArray(typeof(DirEntry)); + } + + public static void MergePdbs(PalmDatabase pdb, PalmDatabase [] apdb) + { + // Create directory entry. + + int irec = pdb.Count + 1; // plus one for directory + + ArrayList alsPdbDirEntries = new ArrayList(); + for (int ipdb = 0; ipdb < apdb.Length; ipdb++) { + + byte [] abFilename = new byte[28]; + for (int ich = 0; ich < apdb[ipdb].Name.Length; ich++) + abFilename[ich] = (byte)apdb[ipdb].Name[ich]; + + alsPdbDirEntries.AddRange(abFilename); + alsPdbDirEntries.Add((byte)((irec >> 8) & 0xff)); + alsPdbDirEntries.Add((byte)(irec & 0xff)); + alsPdbDirEntries.Add((byte)((apdb[ipdb].Count >> 8) & 0xff)); + alsPdbDirEntries.Add((byte)(apdb[ipdb].Count & 0xff)); + + irec += apdb[ipdb].Count; + } + + alsPdbDirEntries.Add((byte)0); // end of directory marker + + uint uiType = 0x5041434b; // PACK + + irec = 0; + pdb.Add((byte[])alsPdbDirEntries.ToArray(typeof(byte)), (ushort)irec, uiType); + + for (int ipdb = 0; ipdb < apdb.Length; ipdb++) { + for (int irecAdd = 0; irecAdd < apdb[ipdb].Count; irecAdd++) { + irec += 1; + pdb.Add(apdb[ipdb][irecAdd].Data, (ushort)irec, uiType); + } + } + } + } +} diff --git a/SpiffLib/binarytree.cs b/SpiffLib/binarytree.cs new file mode 100644 index 0000000..1950601 --- /dev/null +++ b/SpiffLib/binarytree.cs @@ -0,0 +1,245 @@ +/* File : BinaryTree.cs (Beta 2 .Net ) + * Binary Tree Implementation. + * Date : 7/1/2001 + * Author : Rafat Sarosh - rafat.sarosh@usa.net + * + * Reference : DataStructure and other Objects Using C++ + * by Michael Main, Walter Savitch. + */ + +using System; + +namespace SpiffLib { + class BinaryTree { + public BinaryTree ( ): this (0) {} + public BinaryTree (int key, int value ) { + Key = key; + Value = value; + Left = null; + Right = null; + } + + public BinaryTree Left; + public BinaryTree Right; + public int Key; + public int Value; + + /// + /// Inserts the Value in Binary Tree. + /// + /// Root of the Binary Tree. + /// Value to be inserted. + + static public void InsertValue ( ref BinaryTree root, int key, int value ) { + BinaryTree Cursor = root; + + + /* + * Remain in loop - keep transversing the tree + * till find out the proper node or fall of the tree + */ + while (Cursor != null) { + if (key <= Cursor.Key ) { + /* if i is smaller then the cursor Value + * GO LEFT + */ + + if (Cursor.Left != null) + Cursor = Cursor.Left ; + else + break; + } + else { + if (Cursor.Right != null) + Cursor = Cursor.Right; + else + break; + } + + } + + /* Reached to the Last node */ + + if ( key <= Cursor.Key ) { + /* if i is small or equal to node Value, add it to the left */ + BinaryTree b = new BinaryTree (key, value); + Cursor.Left = b; + } + else { + BinaryTree b = new BinaryTree (key, value); + Cursor.Right = b; + } + return; + + }//Insert Value + + + /// + /// Prints the Btree in Backward In-Order traversal + /// + /// root node + /// should be one 1 + /// if true puts tab + /// + + static public void Print ( BinaryTree root, int depth, bool Tab ) { + if (root != null) { + Print (root.Right, depth + 1, Tab ); + int i = 0; + while ( i++ < depth && Tab ) { + Console.Write (" "); + } + Console.WriteLine (root.Key ); + Print (root.Left, depth + 1, Tab ); + } + + }//print + + /// + /// Transverse in the right direction and get the biggest node + /// + /// Node from where to start the search + /// + /// return the BinaryTree Node which is biggest by the virtue of + /// binary tree + /// + /// + + static private BinaryTree GetMaxRight (ref BinaryTree Cursor ) { + //As soon as the function gets a Left null of a Node + //it returns the node. + + if (Cursor == null) return null; + if (Cursor.Right != null) { + Cursor = Cursor.Right; + GetMaxRight (ref Cursor); + } + return Cursor; + } + + /// + /// For the Value, delete the node from the tree. + /// + /// Root of the Tree + /// Value to be deleted + /// + /// + + static public bool Delete ( ref BinaryTree Cursor, int i ) { + + /* + * Find the node for the Value, we will come out + * of this loop only after finding the Value or reaching at + * end of tree. + * + */ + + while (Cursor != null) { + if (Cursor.Key == i ) { + break; //got the Value- Get out of the loop + } + + /* Is the Value in question more then Node Value + * Then go towards Right + */ + if (i > Cursor.Key ) { + if (Cursor.Right != null) + Cursor = Cursor.Right ; + else + return false; /* Oouch - going back did't find the Value */ + } + else { + /* + * Value in Question is smaller + * We are heading towards Left + */ + if (Cursor.Left != null) + Cursor = Cursor.Left ; + else + return false; //going back did't find the Value + } + } + + /* got the node only if it is NOT null, if it is null + * get lost + */ + + if (Cursor == null) return false ; + + /* + * Is it a leave + */ + if (Cursor.Left == null && Cursor.Right == null ) { + /* Simply remove it */ + Cursor.Key = 0; + return true; + } + + if (Cursor.Left == null ) { + /* Case 1 + * node does't have a left child, then simpley promote the right + * node to root. + * + * NOTE: Must keep the right node in a temp Node + * Cursor.Right = Cursor.Right.Right + * Moves the whole Cursor, altogether + * + * pittsburgh Seattle + * / \ / \ + * null Seattle => / \ + * / \ Left Right + * Left Right + */ + + BinaryTree TempNode = Cursor.Right ; + Cursor.Key = TempNode.Key ; + Cursor.Right.Key = 0 ; //Mark it for deletion + Cursor.Right = TempNode.Right ; + if (TempNode.Left != null) + Cursor.Left = TempNode.Left ; + else + Cursor.Left = null; + return true; + } + else { + /* case 2 + * Node *DOES* have a LEFT child, find the MAX RIGHT child + * of LEFT NODE and then make that Node as root + * + * To Delete A + * Get the biggest Node from the Right hand side + * + * A R2 + * / \ / \ + * / \ / \ + * L1 R1 => L1 R1 + * / \ / \ / / \ + * L2 R2 RL1 RR2 L2 RL1 RR2 + */ + + //Got the Node in the Question, keep it in Temp + BinaryTree _Node = Cursor; + //Get on the Left Node + Cursor = Cursor.Left ; + //Find out Max Right + BinaryTree BigRNode = GetMaxRight ( ref Cursor ); + _Node.Key = BigRNode.Key ; + BigRNode.Key = 0; //Mark it for deletion + return true; + } + }//Delete + + static public void MakeNull (ref BinaryTree root) { + if (root != null ) { + if ( root.Key == 0 ) { + root = null ; + return; + } + + MakeNull(ref root.Left ); + MakeNull(ref root.Right ); + } + + } + }//BinaryTree +}//nameSpace diff --git a/SpiffLib/bitmapraw.cs b/SpiffLib/bitmapraw.cs new file mode 100644 index 0000000..d3f109e --- /dev/null +++ b/SpiffLib/bitmapraw.cs @@ -0,0 +1,48 @@ +using System; +using System.IO; +using System.Collections; +using System.Drawing; +using System.Drawing.Imaging; +using SpiffLib; + +namespace SpiffLib { + public class BitmapRaw { + public static unsafe void Save(string strFile, Palette pal, string strFileOut) { + Bitmap bm = new Bitmap(strFile); + + // Find the palette index for black + int iclrBlack = pal.FindClosestEntry(Color.FromArgb(0, 0, 0)); + + // Lock down bits for speed + Rectangle rc = new Rectangle(0, 0, bm.Width, bm.Height); + BitmapData bmd = bm.LockBits(rc, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); + byte *pbBase = (byte *)bmd.Scan0.ToPointer(); + + // Map all the pixels in the bitmap to 'nearest' color palette indices + byte[] ab = new byte[((bm.Width + 1) & ~1) * bm.Height]; + int i = 0; + for (int y = 0; y < bm.Height; y++) { + for (int x = 0; x < bm.Width; x++) { + byte *pb = pbBase + y * bmd.Stride + x * 3; + Color clr = Color.FromArgb(pb[2], pb[1], pb[0]); + if (clr == Color.FromArgb(156, 212, 248)) + clr = Color.FromArgb(0, 0, 0); + ab[i++] = (byte)pal.FindClosestEntry(clr); + } + if ((bm.Width & 1) == 1) + ab[i++] = (byte)iclrBlack; + } + bm.UnlockBits(bmd); + + // Write bitmap header, bits + BinaryWriter bwtr = new BinaryWriter(new FileStream(strFileOut, FileMode.Create, FileAccess.Write)); + bwtr.Write(Misc.SwapUShort((ushort)((bm.Width + 1) & ~1))); + bwtr.Write(Misc.SwapUShort((ushort)bm.Height)); + bwtr.Write(ab); + bwtr.Close(); + + // Done + bm.Dispose(); + } + } +} diff --git a/SpiffLib/compressor.cs b/SpiffLib/compressor.cs new file mode 100644 index 0000000..775faec --- /dev/null +++ b/SpiffLib/compressor.cs @@ -0,0 +1,317 @@ +using System; +using System.IO; +using System.Collections; +using System.Diagnostics; + +namespace SpiffLib { + enum CodeType { Literal, Match, Run, End }; + + struct Code { + public byte[] ab; + public int ibSrc; + public int cbSrc; + public int cBitsSavings; + public CodeType cotype; + }; + + public class Compressor { + byte[] m_abSrc; + ArrayList[] m_mpbalsIndexes = new ArrayList[256]; + + public static byte[] CompressChunk(byte[] abChunk) { +#if false + return abChunk; +#else + Compressor comp = new Compressor(); + return comp.Compress(abChunk); +#endif + } + + public Compressor() { + } + + void PushIndexes(int ib, int cb) { + while (cb-- != 0) { + byte b = m_abSrc[ib]; + if (m_mpbalsIndexes[b] == null) + m_mpbalsIndexes[b] = new ArrayList(); + m_mpbalsIndexes[b].Add(ib); + ib++; + } + } + + void PopIndexes(int ib, int cb) { + ib += cb - 1; + while (cb-- != 0) { + byte b = m_abSrc[ib]; + ArrayList alsT = m_mpbalsIndexes[b]; + Debug.Assert(ib == (int)alsT[alsT.Count - 1]); + alsT.RemoveAt(alsT.Count - 1); + ib--; + } + } + + Code[] FindMatchCodes(int ibSrc) { + ArrayList alsCode = new ArrayList(); + ArrayList alsIndexes = m_mpbalsIndexes[m_abSrc[ibSrc]]; + if (alsIndexes != null) { + // Find the longest match. + + int ibLongest = -1; + int cbLongest = 0; + + for (int iIndex = alsIndexes.Count - 1; iIndex >= 0; iIndex--) { + // The largest index is 16383. + + int ibStart = (int)alsIndexes[iIndex]; + if (ibSrc - ibStart > 16383) + break; + + // Check length of match. + + Debug.Assert(m_abSrc[ibSrc] == m_abSrc[ibStart]); + int c = 1; + int cbT = (ibSrc - ibStart) < (m_abSrc.Length - ibSrc) ? (ibSrc - ibStart) : (m_abSrc.Length - ibSrc); + for (int ibT = ibStart + 1; ibT < ibStart + cbT; ibT++) { + if (m_abSrc[ibT] != m_abSrc[ibSrc + (ibT - ibStart)]) + break; + c++; + + // Max length allowed is 129 + + if (c == 129) + break; + } + + // The match has to be >= 2 to be useful + + if (c < 2) + continue; + + // Remember this match if it is longer than what we know about + // already + + if (c > cbLongest) { + cbLongest = c; + ibLongest = ibStart; + } + } + + // Add best known matches + + if (ibLongest != -1) { + Code code; + int ib = ibSrc - ibLongest; + int cb = cbLongest; + + if (cb > 8 || ib >= 8192) { + // 3 bits old count (must be 0 since this is escape code) + // 7 bits count (0-127 translates to 2-129 count) + // 14 bits index (1-16383, 0 means end if count is also 0) + + Debug.Assert(ib <= 16384); + Debug.Assert(cb <= 129); + code.ab = new byte[3]; + code.ab[0] = (byte)(ib >> 9); + code.ab[1] = (byte)((ib >> 1) & 0xff); + code.ab[2] = (byte)((ib << 7) | (cb - 2)); + } else { + // 3 bits count (1-7 translates to 2-8, 0 reserved for escape code) + // 13 bits index (1-8191, 0 means end if count is also 0) + + Debug.Assert(ib < 8192); + Debug.Assert(cb <= 8); + code.ab = new byte[2]; + code.ab[0] = (byte)(((cb - 1) << 5) | (ib >> 8)); + code.ab[1] = (byte)ib; + } + code.cotype = CodeType.Match; + code.ibSrc = ibSrc; + code.cbSrc = cb; + code.cBitsSavings = (code.cbSrc - code.ab.Length) * 8; + alsCode.Add(code); + } + } + + // Add literal to round out the possibilities + + Code codeLiteral; + codeLiteral.ab = new byte[1]; + codeLiteral.ab[0] = m_abSrc[ibSrc]; + codeLiteral.cBitsSavings = 0; + codeLiteral.cbSrc = 1; + codeLiteral.ibSrc = ibSrc; + codeLiteral.cotype = CodeType.Literal; + alsCode.Add(codeLiteral); + + // Return them all + + return (Code[])alsCode.ToArray(typeof(Code)); + } + + Code FindBestCode(int ibSrc, int cLookAhead) { + // First find all possible Code at this point, independent of savings + + ArrayList alsCode = new ArrayList(); + alsCode.AddRange(FindMatchCodes(ibSrc)); + + // See which approach yields the smallest size over the next cLookAhead + // encodings. It is easy to get different results here. The larger + // cLookAhead is the better (but slower) + + int icodeBest = 0; + int cBitsSavingsBest = Int32.MinValue; + for (int i = 0; i < alsCode.Count; i++) { + Code code = (Code)alsCode[i]; + int cBitsSavingsT = code.cBitsSavings; + + // Look ahead if asked to + + if (cLookAhead != 0 && ibSrc + code.cbSrc < m_abSrc.Length) { + int ibSrcT = ibSrc + code.cbSrc; + PushIndexes(ibSrc, code.cbSrc); + for (int iT = 0; iT < cLookAhead; iT++) { + Code codeT = FindBestCode(ibSrcT, 0); + PushIndexes(ibSrcT, codeT.cbSrc); + ibSrcT += codeT.cbSrc; + cBitsSavingsT += codeT.cBitsSavings; + if (ibSrcT >= m_abSrc.Length) + break; + } + PopIndexes(ibSrc, ibSrcT - ibSrc); + } + + // Check against best so far + + if (cBitsSavingsT > cBitsSavingsBest) { + cBitsSavingsBest = cBitsSavingsT; + icodeBest = i; + } + } + + // Get and return the best + + return (Code)alsCode[icodeBest]; + } + + private Code[] Codify(int ibSrc) { + // Get the codes for this stream + + ArrayList alsCodes = new ArrayList(); + while (ibSrc < m_abSrc.Length) { + Code code = FindBestCode(ibSrc, 0); // 16); + PushIndexes(ibSrc, code.cbSrc); + alsCodes.Add(code); + ibSrc += code.cbSrc; + } + + // Add the End code and done + + Code codeEnd; + codeEnd.ab = new byte[2]; + codeEnd.ab[0] = (byte)0; + codeEnd.ab[1] = (byte)0; + codeEnd.cBitsSavings = 0; + codeEnd.cbSrc = 0; + codeEnd.ibSrc = 0; + codeEnd.cotype = CodeType.End; + alsCodes.Add(codeEnd); + return (Code[])alsCodes.ToArray(typeof(Code)); + } + + public byte[] Compress(byte[] abSrc) { + // Create compression codes + + m_abSrc = abSrc; + Code[] acode = Codify(0); + + // Turn into a byte stream + + ArrayList alsDst = new ArrayList(); + ArrayList alsDstT = new ArrayList(); + int ibitFlags = 7; + byte bFlags = 0; + for (int icode = 0; icode < acode.Length; icode++) { + Code code = acode[icode]; + if (code.cotype == CodeType.Literal) + bFlags |= (byte)(1 << ibitFlags); + alsDstT.AddRange(code.ab); + ibitFlags--; + if (ibitFlags < 0 || icode == acode.Length - 1) { + ibitFlags = 7; + alsDst.Add(bFlags); + alsDst.AddRange((byte[])alsDstT.ToArray(typeof(byte))); + bFlags = 0; + alsDstT.Clear(); + } + } + + // Done + + return (byte[])alsDst.ToArray(typeof(byte)); + } + + public static byte[] DecompressChunk(byte[] abChunk) { + ArrayList alsDst = new ArrayList(); + int ib = 0; + bool fDone = false; + while (!fDone) { + // Get next flags + + byte bFlags = abChunk[ib]; + ib++; + + for (int ibitFlags = 7; ibitFlags >= 0; ibitFlags--) { + // Literal or code? + + if ((bFlags & (1 << ibitFlags)) != 0) { + alsDst.Add(abChunk[ib]); + ib++; + continue; + } + + // Get a code + + ushort code = (ushort)(abChunk[ib] << 8); + ib++; + code |= (ushort)abChunk[ib]; + ib++; + + // Extended code? Check for count == 0. + + int ibBackwards = (int)(code & 0x1fff); + int cb; + if ((code & 0xe000) != 0) { + // Count != 0, so not an extended code + + cb = (byte)((code & 0xe000) >> 13) + 1; + } else { + // Extended code. End? + + if (ibBackwards == 0) { + fDone = true; + break; + } + + // Extended match + + ibBackwards = (int)(code << 1) | (int)(abChunk[ib] >> 7); + cb = (int)(abChunk[ib] & 0x7f) + 2; + ib++; + } + + // Copy this chunk into the output + + byte[] abAdd = new byte[cb]; + alsDst.CopyTo(alsDst.Count - ibBackwards, abAdd, 0, cb); + alsDst.AddRange(abAdd); + } + } + + return (byte[])alsDst.ToArray(typeof(byte)); + } + } +} + + + diff --git a/SpiffLib/doublerect.cs b/SpiffLib/doublerect.cs new file mode 100644 index 0000000..770ba0e --- /dev/null +++ b/SpiffLib/doublerect.cs @@ -0,0 +1,46 @@ +using System; + +namespace SpiffLib { + + public struct DoubleRect { + public DoubleRect(double leftT, double topT, double rightT, double bottomT) { + left = leftT; + top = topT; + right = rightT; + bottom = bottomT; + } + + public double Width { + get { + return right - left; + } + } + + public double Height { + get { + return bottom - top; + } + } + + public void Intersect(DoubleRect drc) { + left = Math.Max(left, drc.left); + right = Math.Min(right, drc.right); + if (left < right) { + top = Math.Max(top, drc.top); + bottom = Math.Min(bottom, drc.bottom); + if (top < bottom) + return; + } + left = 0.0; + right = 0.0; + top = 0.0; + bottom = 0.0; + } + + public double left; + public double top; + public double right; + public double bottom; + } +} + diff --git a/SpiffLib/ini.cs b/SpiffLib/ini.cs new file mode 100644 index 0000000..bbea26e --- /dev/null +++ b/SpiffLib/ini.cs @@ -0,0 +1,380 @@ +using System; +using System.Collections; +using System.IO; +using System.Text.RegularExpressions; +using System.Text; + +namespace SpiffLib +{ + public class Ini { + private ArrayList m_alsSections = new ArrayList(); + + public Ini() { + } + + public Ini(string strFile) { + Load(strFile); + } + + public Ini(Stream stm) { + Load(stm); + } + + public Ini(ArrayList alsSections) { + m_alsSections = alsSections; + } + + public void Load(string strFile) { + FileStream stm = new FileStream(strFile, FileMode.Open); + Load(stm); + stm.Close(); + } + + public void Load(Stream stm) { + TextReader tr = new StreamReader(stm); + Regex rexSec = new Regex(@"^\s*\[\s*(?
.+)\s*\]\s*$"); + Regex rexProperty = new Regex(@"^\s*(?.+)\s*=\s*(?.*)$"); + ArrayList alsComments = new ArrayList(); + Section secCurrent = null; + + m_alsSections.Clear(); + while (true) { + string strLine = tr.ReadLine(); + if (strLine == null) + break; + + // Search for a section header + Match matSec = rexSec.Match(strLine); + if (matSec.Groups["section"].Value.Length != 0) { + secCurrent = new Section(matSec.Groups["section"].Value, (string[])alsComments.ToArray(typeof(string))); + Add(secCurrent); + alsComments.Clear(); + continue; + } + + // See if it is a property. + if (secCurrent != null) { + Match matProperty = rexProperty.Match(strLine); + if (matProperty.Groups["name"].Value.Length != 0) { + secCurrent.Add(new Property(matProperty.Groups["name"].Value, matProperty.Groups["value"].Value, (string[])alsComments.ToArray(typeof(string)))); + alsComments.Clear(); + continue; + } + } + + // No clue what it is, retain it as a comment + alsComments.Add(strLine); + } + tr.Close(); + } + +#if false + // Ini binary format + + struct SecChunk { // sck + word offSecNext; // 0 if no next + short cprop; + // char szSecName[]; + }; + + struct PropChunk { // pck + // char szProp[]; // zero terminated + // char szPropValue[]; // zero terminated + }; +#endif + + static string ReadString(BinaryReader brdr) { + ArrayList alsBytes = new ArrayList(); + while (true) { + byte b = brdr.ReadByte(); + if (b == 0) { + break; + } + alsBytes.Add(b); + } + byte[] ab = (byte[])alsBytes.ToArray(typeof(byte)); + return Encoding.ASCII.GetString(ab); + } + + public static Ini LoadBinary(Stream stm) { + BinaryReader brdr = new BinaryReader(stm); + ArrayList alsSections = new ArrayList(); + while (true) { + int offNextSection = Misc.SwapUShort(brdr.ReadUInt16()); + int cProps = Misc.SwapUShort(brdr.ReadUInt16()); + string strNameSection = ReadString(brdr); + Section sec = new Section(strNameSection); + for (int iProp = 0; iProp < cProps; iProp++) { + string strKey = ReadString(brdr); + string strValue = ReadString(brdr); + sec.Add(new Property(strKey, strValue)); + } + alsSections.Add(sec); + if ((brdr.BaseStream.Position & 1) != 0) { + brdr.ReadByte(); + } + if (offNextSection == 0) { + break; + } + } + + return new Ini(alsSections); + } + + public void SaveBinary(Stream stm) { + BinaryWriter bwtr = new BinaryWriter(stm); + for (int n = 0; n < Count; n++) { + Section sec = (Section)m_alsSections[n]; + long posSection = bwtr.BaseStream.Position; + bwtr.Write((ushort)0); + bwtr.Write(Misc.SwapUShort((ushort)sec.Count)); + bwtr.Write(Misc.GetByteArrayFromString(sec.Name)); + foreach (Property prop in sec) { + bwtr.Write(Misc.GetByteArrayFromString(prop.Name)); + bwtr.Write(Misc.GetByteArrayFromString(prop.Value)); + } + if ((bwtr.BaseStream.Position & 1) != 0) + bwtr.Write((byte)0); + long posCurrent = bwtr.BaseStream.Position; + bwtr.Seek((int)posSection, SeekOrigin.Begin); + if (n == Count - 1) { + bwtr.Write((ushort)0); + } else { + bwtr.Write(Misc.SwapUShort((ushort)(posCurrent - posSection))); + } + bwtr.Seek((int)posCurrent, SeekOrigin.Begin); + } + bwtr.Close(); + } + + public void Save(Stream stm) { + TextWriter tw = new StreamWriter(stm); + foreach (Section sec in this) { + if (sec.Comments != null) { + foreach (string str in sec.Comments) + tw.WriteLine(str); + } + tw.WriteLine("[" + sec.Name + "]"); + foreach (Property prop in sec) { + if (prop.Comments != null) { + foreach (string str in prop.Comments) + tw.WriteLine(str); + } + tw.WriteLine(prop.Name + "=" + prop.Value); + } + } + tw.Flush(); + } + + public void Save(string strFile) { + FileStream stm = new FileStream(strFile, FileMode.Create); + Save(stm); + stm.Close(); + } + + public string GetProperty(string strSection, string strProp) { + Section sec = this[strSection]; + if (sec == null) + return null; + Property prop = sec[strProp]; + if (prop == null) + return null; + return prop.Value; + } + + public void SetProperty(string strSection, string strProp, string strValue) { + Section sec = this[strSection]; + if (sec == null) { + sec = new Section(strSection); + Add(sec); + } + Property prop = sec[strProp]; + if (prop == null) { + prop = new Property(strProp, strValue); + sec.Add(prop); + } else { + prop.Value = strValue; + } + } + + public Section Add(Section sec) { + if (!m_alsSections.Contains(sec)) + m_alsSections.Add(sec); + return sec; + } + + public void Remove(Section sec) { + m_alsSections.Remove(sec); + } + + public void Insert(int n, Section sec) { + m_alsSections.Insert(n, sec); + } + + public Section this[int index] { + get { + return (Section)m_alsSections[index]; + } + } + + public Section this[string strSection] { + get { + foreach (Section sec in m_alsSections) { + if (strSection.ToUpper() == sec.Name.ToUpper()) + return sec; + } + return null; + } + } + + public Section[] Sections { + get { + return (Section[])m_alsSections.ToArray(typeof(Section)); + } + } + + public int Count { + get { + return m_alsSections.Count; + } + } + + public SectionEnumerator GetEnumerator() { + return new SectionEnumerator(this); + } + + public class SectionEnumerator { + private Ini m_ini; + private int m_pos = -1; + + public SectionEnumerator(Ini ini) { + m_ini = ini; + } + + public bool MoveNext() { + if (m_pos < m_ini.m_alsSections.Count - 1) { + m_pos++; + return true; + } + return false; + } + + public void Reset() { + m_pos = -1; + } + + public Section Current { + get { + return (Section)m_ini.m_alsSections[m_pos]; + } + } + } + + public class Section { + private ArrayList m_alsProperties = new ArrayList(); + public string Name; + public string[] Comments; + + public Section(string strName, string[] astrComments) { + Name = strName; + Comments = astrComments; + } + + public Section(string strName) { + Name = strName; + Comments = new string[] { "" }; + } + + public Property Add(Property prop) { + if (!m_alsProperties.Contains(prop)) + m_alsProperties.Add(prop); + return prop; + } + + public void Remove(Property prop) { + m_alsProperties.Remove(prop); + } + + public void Insert(int n, Property prop) { + m_alsProperties.Insert(n, prop); + } + + public Property this[int index] { + get { + return (Property)m_alsProperties[index]; + } + } + + public Property this[string strProperty] { + get { + foreach (Property prop in m_alsProperties) { + if (prop.Name.ToUpper() == strProperty.ToUpper()) + return prop; + } + return null; + } + } + + public Property[] Properties { + get { + return (Property[])m_alsProperties.ToArray(typeof(Property)); + } + } + + public int Count { + get { + return m_alsProperties.Count; + } + } + + public PropertyEnumerator GetEnumerator() { + return new PropertyEnumerator(this); + } + + public class PropertyEnumerator { + private Section m_sec; + private int m_pos = -1; + + public PropertyEnumerator(Section sec) { + m_sec = sec; + } + + public bool MoveNext() { + if (m_pos < m_sec.m_alsProperties.Count - 1) { + m_pos++; + return true; + } + return false; + } + + public void Reset() { + m_pos = -1; + } + + public Property Current { + get { + return (Property)m_sec.m_alsProperties[m_pos]; + } + } + } + } + + public class Property { + public string Name; + public string Value; + public string[] Comments; + + public Property(string strName, string strValue, string[] astrComments) { + Name = strName; + Value = strValue; + Comments = astrComments; + } + + public Property(string strName, string strValue) { + Name = strName; + Value = strValue; + Comments = null; + } + } + } +} diff --git a/SpiffLib/misc.cs b/SpiffLib/misc.cs new file mode 100644 index 0000000..3a7cffb --- /dev/null +++ b/SpiffLib/misc.cs @@ -0,0 +1,1307 @@ +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; +using System.Collections; + +namespace SpiffLib +{ + /// + /// The Misc class encapsulates various static helper methods that lack a more + /// appropriate home. + /// + public class Misc + { + class Preprocessor { + Hashtable m_htbl = new Hashtable(); + + public Preprocessor(Stream stmInclude) { + // Parse stmInclude and make a hash table + + Regex rexI = new Regex(@"^\#define\s*(?\w+)\s*(?[\-0-9]+).*$"); + + stmInclude.Seek(0, SeekOrigin.Begin); + TextReader tr = new StreamReader(stmInclude); + while (true) { + string strT = tr.ReadLine(); + if (strT == null) + break; + + // Remove white space + + strT.Trim(); + if (strT.Length == 0) + continue; + + // Comment? + + if (strT.StartsWith("//")) + continue; + + // Match against regex + + Match mat = rexI.Match(strT); + string strName = mat.Groups["name"].Value; + int nValue = Int32.Parse(mat.Groups["value"].Value); + if (strName.Length != 0) + m_htbl.Add(strName, nValue); + } + } + + public MemoryStream Preprocess(Stream stmContent) { + // Go through stmContent and replace matches with values + + stmContent.Seek(0, SeekOrigin.Begin); + TextReader tr = new StreamReader(stmContent); + MemoryStream stm = new MemoryStream(); + TextWriter tw = new StreamWriter(stm); + + while (true) { + string strT = tr.ReadLine(); + if (strT == null) + break; + + // Parse for alpha numeric sequences + + string strResult = Regex.Replace(strT, @"\w+", new MatchEvaluator(ReplaceText)); + tw.WriteLine(strResult); + } + + tw.Flush(); + return stm; + } + + string ReplaceText(Match mat) { + string str = mat.ToString(); + if (!m_htbl.ContainsKey(str)) + return str; + return ((int)m_htbl[str]).ToString(); + } + } + + public static MemoryStream PreprocessStream(Stream stmContent, Stream stmInclude) { + Preprocessor pp = new Preprocessor(stmInclude); + return pp.Preprocess(stmContent); + } + + public static byte[] GetByteArrayFromString(string str) { + byte[] ab = new byte[str.Length + 1]; + for (int n = 0; n < str.Length; n++) + ab[n] = (byte)str[n]; + ab[str.Length] = 0; + return ab; + } + + /// + /// Takes RGB values as floats in the range from 0 to 1 and returns + /// HSL values also in the range from 0 to 1. + /// + /// Thank you Ken Fishkin of Pixar Inc and Graphics Gems + /// + /// + /// + /// + /// + /// + /// + public static unsafe void RgbToHsl(float r, float g, float b, float *h, float *s, float *l) { + float v; + float m; + float vm; + float r2, g2, b2; + + v = Math.Max(r,g); + v = Math.Max(v,b); + m = Math.Min(r,g); + m = Math.Min(m,b); + + *h = 0; + *s = 0; + if ((*l = (m + v) / 2.0f) <= 0.0f) + return; + if ((*s = vm = v - m) > 0.0f) { + *s /= (*l <= 0.5f) ? (v + m ) : (2.0f - v - m) ; + } else + return; + + r2 = (v - r) / vm; + g2 = (v - g) / vm; + b2 = (v - b) / vm; + + if (r == v) + *h = (g == m ? 5.0f + b2 : 1.0f - g2); + else if (g == v) + *h = (b == m ? 1.0f + r2 : 3.0f - b2); + else + *h = (r == m ? 3.0f + g2 : 5.0f - r2); + + *h /= 6; + } + + /// + /// Takes HSL values as floats in the range from 0 to 1 and returns + /// RGB values also in the range from 0 to 1. + /// + /// Thank you Ken Fishkin of Pixar Inc and Graphics Gems + /// + /// + /// + /// + /// + /// + /// + public static unsafe void HslToRgb(float h, float s, float l, float *r, float *g, float *b) { + float v; + + v = (l <= 0.5f) ? (l * (1.0f + s)) : (l + s - l * s); + if (v <= 0) { + *r = *g = *b = 0.0f; + } else { + float m; + float sv; + int sextant; + float fract, vsf, mid1, mid2; + + m = l + l - v; + sv = (v - m ) / v; + h *= 6.0f; + sextant = (int)h; + fract = h - sextant; + vsf = v * sv * fract; + mid1 = m + vsf; + mid2 = v - vsf; + switch (sextant) { + case 0: *r = v; *g = mid1; *b = m; break; + case 1: *r = mid2; *g = v; *b = m; break; + case 2: *r = m; *g = v; *b = mid1; break; + case 3: *r = m; *g = mid2; *b = v; break; + case 4: *r = mid1; *g = m; *b = v; break; + case 5: *r = v; *g = m; *b = mid2; break; + } + } + } + + /// + /// All this routine does is create a duplicate of the passed-in bitmap. Its utility + /// comes from the fact that it doesn't duplicate the properties (e.g., horizontal + /// and vertical resolution) that are attached to bitmaps after they've been read + /// from files. These properties can interfere with the expected operation of various + /// Bitmap/Graphics methods (e.g., DragImage will inexplicably scale the bitmap when + /// drawn from based on its intrinsic resolution properties). We would prefer to just + /// clear out these properties or force them to be ignored but all such attempts to + /// date have been rebuffed. + /// + /// The Bitmap to be 'normalized' + /// A property-free Bitmap + public static unsafe Bitmap NormalizeBitmap(Bitmap bmSrc) { + Bitmap bmDst = new Bitmap(bmSrc.Width, bmSrc.Height, bmSrc.PixelFormat); + + Rectangle rc = new Rectangle(0, 0, bmSrc.Width, bmSrc.Height); + BitmapData bmdSrc = bmSrc.LockBits(rc, ImageLockMode.ReadOnly, bmSrc.PixelFormat); + BitmapData bmdDst = bmDst.LockBits(rc, ImageLockMode.WriteOnly, bmSrc.PixelFormat); + + int cul = bmdSrc.Height * bmdSrc.Stride / sizeof(ulong); + ulong *pulSrc = (ulong *)bmdSrc.Scan0; + ulong *pulDst = (ulong *)bmdDst.Scan0; + while (cul-- > 0) { + *pulDst++ = *pulSrc++; + } + + bmSrc.UnlockBits(bmdSrc); + bmDst.UnlockBits(bmdDst); + + return bmDst; + } + + static Color s_clrShadow = Color.FromArgb(156, 212, 248); + static Color s_clrTransparent = Color.FromArgb(255, 0, 255); + + /// + /// Scan from the bottom up. First scanline containing non-shadow, non-transparent color + /// is the baseline. + /// + /// + /// A scanline offset from the top of the image + public static unsafe int FindBaseline(Bitmap bm) { + Rectangle rc = new Rectangle(0, 0, bm.Width, bm.Height); + BitmapData bmd = bm.LockBits(rc, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); + + byte *pbBase = (byte *)bmd.Scan0; + for (int y = bm.Height - 1; y >= 0; y--) { + byte *pb = pbBase + y * bmd.Stride; + for (int x = 0; x < bm.Width; x++, pb += 3) { + Color clr = Color.FromArgb(pb[2], pb[1], pb[0]); + if (clr != s_clrTransparent && clr != s_clrShadow) { + bm.UnlockBits(bmd); + return y; + } + } + } + + bm.UnlockBits(bmd); + return 0; + } + + /// + /// The SwapUShort method swaps the high and low bytes of the passed ushort + /// value. It's good for converting between big and little-endian ushort values. + /// + /// The ushort value whose bytes to swap + /// The byte-swapped ushort + public static ushort SwapUShort(ushort us) { + int n = (int)us; + return (ushort)(((n >> 8) & 0x00ff) | ((n << 8) & 0xff00)); + } + + /// + /// The SwapUInt method swaps the endian order of a 4 byte type. + /// + /// The uint value whose bytes to swap + /// The byte-swapped uint + public static uint SwapUInt(uint ui) { + return ((((ui)&0xFF)<<24) | (((ui)&0xFF00)<<8) | (((ui)&0xFF0000)>>8) | (((ui)&0xFF000000)>>24)); + } + + /// + /// This cool method returns a new bitmap which has a copy of the passed-in bitmap + /// surrounded by a colored outline. + /// + /// Bitmap to outline + /// width/height of outline in pixels + /// color of the outline + /// Bitmap with traced edges + public static Bitmap TraceEdges(Bitmap bm, int cp, Color clrEdge) { + // Create a mask of sorts + Color clrTransparent = Color.FromArgb(255, 0, 255); + Bitmap bmEdge = new Bitmap(bm.Width, bm.Height); + Graphics gEdge = Graphics.FromImage(bmEdge); + gEdge.Clear(clrTransparent); + gEdge.DrawImage(bm, 0, 0); + for (int x = 0; x < bmEdge.Width; x++) { + for (int y = 0; y < bmEdge.Height; y++) { + Color clrT = bmEdge.GetPixel(x, y); + if (clrT != clrTransparent) { + bmEdge.SetPixel(x, y, clrEdge); + } + } + } + bmEdge.MakeTransparent(clrTransparent); + + // Draw the mask aligned to create edges + Bitmap bmT = new Bitmap(bm.Width + cp * 2, bm.Height + cp * 2); + Graphics gT = Graphics.FromImage(bmT); + gT.Clear(clrTransparent); + gT.DrawImage(bmEdge, 0, 0); + gT.DrawImage(bmEdge, cp, 0); + gT.DrawImage(bmEdge, cp * 2, 0); + gT.DrawImage(bmEdge, cp * 2, cp); + gT.DrawImage(bmEdge, cp * 2, cp * 2); + gT.DrawImage(bmEdge, cp, cp * 2); + gT.DrawImage(bmEdge, 0, cp * 2); + gT.DrawImage(bmEdge, 0, cp); + + // Draw the actual image in the middle; we're done + gT.DrawImage(bm, cp, cp); + bmT.MakeTransparent(clrTransparent); + + // Force cleanup + gEdge.Dispose(); + gT.Dispose(); + return bmT; + } + + /// + /// The GetPropertyItemValue method takes a instance (usually + /// acquired via Image.PropertyItems[]) and returns its value as a (boxed) + /// CLR type. + /// + /// + /// + /// string strCopyright = "no copyright specified"; + /// Bitmap bm = new Bitmap("pict.jpg"); + /// foreach (PropertyItem prpi in bm.PropertyItems) { + /// if ((PropertyTag)prpi.Id == PropertyTag.Copyright) + /// strCopyright = Misc.GetPropertyItemValue(prpi); + /// } + /// bm.Dispose(); + /// + /// + /// The containing the desired Value + /// The value, usually a boxed primitive type + /// If the of the item is unknown the original + /// Value byte array is returned. + /// + /// + /// + static public object GetPropertyItemValue(PropertyItem prpi) { + switch ((PropertyTagType)prpi.Type) { + case PropertyTagType.ASCII: { + // UNDONE: a cleaner way to do this? + StreamReader stmr = new StreamReader(new MemoryStream(prpi.Value), Encoding.ASCII); + return stmr.ReadToEnd(); + } + + case PropertyTagType.Byte: + return prpi.Value[0]; + + case PropertyTagType.Long: + return BitConverter.ToUInt32(prpi.Value, 0); + + case PropertyTagType.Rational: { + UInt32 n1 = BitConverter.ToUInt32(prpi.Value, 0); + UInt32 n2 = BitConverter.ToUInt32(prpi.Value, 4); + return (float)n1 / (float)n2; + } + + case PropertyTagType.Short: + return BitConverter.ToUInt16(prpi.Value, 0); + + case PropertyTagType.SLONG: + return BitConverter.ToInt32(prpi.Value, 0); + + case PropertyTagType.SRational: { + Int32 n1 = BitConverter.ToInt32(prpi.Value, 0); + Int32 n2 = BitConverter.ToInt32(prpi.Value, 4); + return (float)n1 / (float)n2; + } + } + + return prpi.Value; + } + } + + /// + /// This enumeration should be supplied as part of the FCL and used by the PropertyInfo + /// class but it isn't. I dug its values from a GDI+ headerfile, GdiPlusImaging.h + /// + [Serializable] + public enum PropertyTagType : short { + /// + /// + /// + Byte = 1, + /// + /// + /// + ASCII = 2, + /// + /// + /// + Short = 3, + /// + /// + /// + Long = 4, + /// + /// + /// + Rational = 5, + /// + /// + /// + Undefined = 7, + /// + /// + /// + SLONG = 9, + /// + /// + /// + SRational = 10 + } + + /// + /// This enumeration should be supplied as part of the FCL and used by the PropertyInfo + /// class but it isn't. I dug its values from a GDI+ headerfile, GdiPlusImaging.h + /// + [Serializable] + public enum PropertyTag : int { + /// + /// + /// + ExifIFD = 0x8769, + /// + /// + /// + GpsIFD = 0x8825, + + /// + /// + /// + NewSubfileType = 0x00FE, + /// + /// + /// + SubfileType = 0x00FF, + /// + /// + /// + ImageWidth = 0x0100, + /// + /// + /// + ImageHeight = 0x0101, + /// + /// + /// + BitsPerSample = 0x0102, + /// + /// + /// + Compression = 0x0103, + /// + /// + /// + PhotometricInterp = 0x0106, + /// + /// + /// + ThreshHolding = 0x0107, + /// + /// + /// + CellWidth = 0x0108, + /// + /// + /// + CellHeight = 0x0109, + /// + /// + /// + FillOrder = 0x010A, + /// + /// + /// + DocumentName = 0x010D, + /// + /// + /// + ImageDescription = 0x010E, + /// + /// + /// + EquipMake = 0x010F, + /// + /// + /// + EquipModel = 0x0110, + /// + /// + /// + StripOffsets = 0x0111, + /// + /// + /// + Orientation = 0x0112, + /// + /// + /// + SamplesPerPixel = 0x0115, + /// + /// + /// + RowsPerStrip = 0x0116, + /// + /// + /// + StripBytesCount = 0x0117, + /// + /// + /// + MinSampleValue = 0x0118, + /// + /// + /// + MaxSampleValue = 0x0119, + /// + /// Image resolution in width direction + /// + XResolution = 0x011A, + /// + /// Image resolution in height direction + /// + YResolution = 0x011B, + /// + /// Image data arrangement + /// + PlanarConfig = 0x011C, + /// + /// + /// + PageName = 0x011D, + /// + /// + /// + XPosition = 0x011E, + /// + /// + /// + YPosition = 0x011F, + /// + /// + /// + FreeOffset = 0x0120, + /// + /// + /// + FreeByteCounts = 0x0121, + /// + /// + /// + GrayResponseUnit = 0x0122, + /// + /// + /// + GrayResponseCurve = 0x0123, + /// + /// + /// + T4Option = 0x0124, + /// + /// + /// + T6Option = 0x0125, + /// + /// Unit of X and Y resolution + /// + ResolutionUnit = 0x0128, + /// + /// + /// + PageNumber = 0x0129, + /// + /// + /// + TransferFuncition = 0x012D, + /// + /// + /// + SoftwareUsed = 0x0131, + /// + /// + /// + DateTime = 0x0132, + /// + /// + /// + Artist = 0x013B, + /// + /// + /// + HostComputer = 0x013C, + /// + /// + /// + Predictor = 0x013D, + /// + /// + /// + WhitePoint = 0x013E, + /// + /// + /// + PrimaryChromaticities = 0x013F, + /// + /// + /// + ColorMap = 0x0140, + /// + /// + /// + HalftoneHints = 0x0141, + /// + /// + /// + TileWidth = 0x0142, + /// + /// + /// + TileLength = 0x0143, + /// + /// + /// + TileOffset = 0x0144, + /// + /// + /// + TileByteCounts = 0x0145, + /// + /// + /// + InkSet = 0x014C, + /// + /// + /// + InkNames = 0x014D, + /// + /// + /// + NumberOfInks = 0x014E, + /// + /// + /// + DotRange = 0x0150, + /// + /// + /// + TargetPrinter = 0x0151, + /// + /// + /// + ExtraSamples = 0x0152, + /// + /// + /// + SampleFormat = 0x0153, + /// + /// + /// + SMinSampleValue = 0x0154, + /// + /// + /// + SMaxSampleValue = 0x0155, + /// + /// + /// + TransferRange = 0x0156, + + /// + /// + /// + JPEGProc = 0x0200, + /// + /// + /// + JPEGInterFormat = 0x0201, + /// + /// + /// + JPEGInterLength = 0x0202, + /// + /// + /// + JPEGRestartInterval = 0x0203, + /// + /// + /// + JPEGLosslessPredictors = 0x0205, + /// + /// + /// + JPEGPointTransforms = 0x0206, + /// + /// + /// + JPEGQTables = 0x0207, + /// + /// + /// + JPEGDCTables = 0x0208, + /// + /// + /// + JPEGACTables = 0x0209, + + /// + /// + /// + YCbCrCoefficients = 0x0211, + /// + /// + /// + YCbCrSubsampling = 0x0212, + /// + /// + /// + YCbCrPositioning = 0x0213, + /// + /// + /// + REFBlackWhite = 0x0214, + + /// + /// This TAG is defined by ICC for embedded ICC in TIFF + /// + ICCProfile = 0x8773, + /// + /// This TAG is defined by ICC for embedded ICC in TIFF + /// + Gamma = 0x0301, + /// + /// This TAG is defined by ICC for embedded ICC in TIFF + /// + ICCProfileDescriptor = 0x0302, + /// + /// This TAG is defined by ICC for embedded ICC in TIFF + /// + SRGBRenderingIntent = 0x0303, + + /// + /// + /// + ImageTitle = 0x0320, + /// + /// + /// + Copyright = 0x8298, + + // Extra TAGs (Like Adobe Image Information tags etc.) + + /// + /// + /// + ResolutionXUnit = 0x5001, + /// + /// + /// + ResolutionYUnit = 0x5002, + /// + /// + /// + ResolutionXLengthUnit = 0x5003, + /// + /// + /// + ResolutionYLengthUnit = 0x5004, + /// + /// + /// + PrintFlags = 0x5005, + /// + /// + /// + PrintFlagsVersion = 0x5006, + /// + /// + /// + PrintFlagsCrop = 0x5007, + /// + /// + /// + PrintFlagsBleedWidth = 0x5008, + /// + /// + /// + PrintFlagsBleedWidthScale = 0x5009, + /// + /// + /// + HalftoneLPI = 0x500A, + /// + /// + /// + HalftoneLPIUnit = 0x500B, + /// + /// + /// + HalftoneDegree = 0x500C, + /// + /// + /// + HalftoneShape = 0x500D, + /// + /// + /// + HalftoneMisc = 0x500E, + /// + /// + /// + HalftoneScreen = 0x500F, + /// + /// + /// + JPEGQuality = 0x5010, + /// + /// + /// + GridSize = 0x5011, + /// + /// 1 = JPEG, 0 = RAW RGB + /// + ThumbnailFormat = 0x5012, + /// + /// + /// + ThumbnailWidth = 0x5013, + /// + /// + /// + ThumbnailHeight = 0x5014, + /// + /// + /// + ThumbnailColorDepth = 0x5015, + /// + /// + /// + ThumbnailPlanes = 0x5016, + /// + /// + /// + ThumbnailRawBytes = 0x5017, + /// + /// + /// + ThumbnailSize = 0x5018, + /// + /// + /// + ThumbnailCompressedSize = 0x5019, + /// + /// + /// + ColorTransferFunction = 0x501A, + /// + /// RAW thumbnail bits in JPEG format or RGB format depends on PropertyTagThumbnailFormat + /// + ThumbnailData = 0x501B, + + // Thumbnail related TAGs + + /// + /// Thumbnail width + /// + ThumbnailImageWidth = 0x5020, + /// + /// Thumbnail height + /// + ThumbnailImageHeight = 0x5021, + /// + /// Number of bits per component + /// + ThumbnailBitsPerSample = 0x5022, + /// + /// Compression Scheme + /// + ThumbnailCompression = 0x5023, + /// + /// Pixel composition + /// + ThumbnailPhotometricInterp = 0x5024, + /// + /// Image Tile + /// + ThumbnailImageDescription = 0x5025, + /// + /// Manufacturer of Image + /// + ThumbnailEquipMake = 0x5026, + + // Input equipment + + /// + /// Model of Image input equipment + /// + ThumbnailEquipModel = 0x5027, + /// + /// Image data location + /// + ThumbnailStripOffsets = 0x5028, + /// + /// Orientation of image + /// + ThumbnailOrientation = 0x5029, + /// + /// Number of components + /// + ThumbnailSamplesPerPixel = 0x502A, + /// + /// Number of rows per strip + /// + ThumbnailRowsPerStrip = 0x502B, + /// + /// Bytes per compressed strip + /// + ThumbnailStripBytesCount = 0x502C, + /// + /// Resolution in width direction + /// + ThumbnailResolutionX = 0x502D, + /// + /// Resolution in height direction + /// + ThumbnailResolutionY = 0x502E, + /// + /// Image data arrangement + /// + ThumbnailPlanarConfig = 0x502F, + /// + /// Unit of X and Y resolution + /// + ThumbnailResolutionUnit = 0x5030, + /// + /// Transfer function + /// + ThumbnailTransferFunction = 0x5031, + /// + /// Software used + /// + ThumbnailSoftwareUsed = 0x5032, + /// + /// File change date and time + /// + ThumbnailDateTime = 0x5033, + /// + /// Person who created the image + /// + ThumbnailArtist = 0x5034, + /// + /// White point chromaticity + /// + ThumbnailWhitePoint = 0x5035, + /// + /// Chromaticities of primaries + /// + ThumbnailPrimaryChromaticities = 0x5036, + /// + /// Color space transformaion coefficients + /// + ThumbnailYCbCrCoefficients = 0x5037, + /// + /// Subsampling ratio of Y to C + /// + ThumbnailYCbCrSubsampling = 0x5038, + /// + /// Y and C position + /// + ThumbnailYCbCrPositioning = 0x5039, + /// + /// Pair of black and white reference values + /// + ThumbnailRefBlackWhite = 0x503A, + /// + /// Copyright holder + /// + ThumbnailCopyRight = 0x503B, + + /// + /// + /// + LuminanceTable = 0x5090, + /// + /// + /// + ChrominanceTable = 0x5091, + + /// + /// + /// + FrameDelay = 0x5100, + /// + /// + /// + LoopCount = 0x5101, + + /// + /// Unit specifier for pixel/unit + /// + PixelUnit = 0x5110, + /// + /// Pixels per unit in X + /// + PixelPerUnitX = 0x5111, + /// + /// Pixels per unit in Y + /// + PixelPerUnitY = 0x5112, + /// + /// Palette histogram + /// + PaletteHistogram = 0x5113, + + // EXIF specific tag + + /// + /// + /// + ExifExposureTime = 0x829A, + /// + /// + /// + ExifFNumber = 0x829D, + + /// + /// + /// + ExifExposureProg = 0x8822, + /// + /// + /// + ExifSpectralSense = 0x8824, + /// + /// + /// + ExifISOSpeed = 0x8827, + /// + /// + /// + ExifOECF = 0x8828, + + /// + /// + /// + ExifVer = 0x9000, + /// + /// Date and time of original + /// + ExifDTOrig = 0x9003, + /// + /// Date and time of digital data generation + /// + ExifDTDigitized = 0x9004, + + /// + /// + /// + ExifCompConfig = 0x9101, + /// + /// + /// + ExifCompBPP = 0x9102, + + /// + /// + /// + ExifShutterSpeed = 0x9201, + /// + /// + /// + ExifAperture = 0x9202, + /// + /// + /// + ExifBrightness = 0x9203, + /// + /// + /// + ExifExposureBias = 0x9204, + /// + /// + /// + ExifMaxAperture = 0x9205, + /// + /// + /// + ExifSubjectDist = 0x9206, + /// + /// + /// + ExifMeteringMode = 0x9207, + /// + /// + /// + ExifLightSource = 0x9208, + /// + /// + /// + ExifFlash = 0x9209, + /// + /// + /// + ExifFocalLength = 0x920A, + /// + /// + /// + ExifMakerNote = 0x927C, + /// + /// + /// + ExifUserComment = 0x9286, + /// + /// Date and Time subseconds + /// + ExifDTSubsec = 0x9290, + /// + /// Date and Time original subseconds + /// + ExifDTOrigSS = 0x9291, + /// + /// Date and TIme digitized subseconds + /// + ExifDTDigSS = 0x9292, + + /// + /// + /// + ExifFPXVer = 0xA000, + /// + /// + /// + ExifColorSpace = 0xA001, + /// + /// + /// + ExifPixXDim = 0xA002, + /// + /// + /// + ExifPixYDim = 0xA003, + /// + /// related sound file + /// + ExifRelatedWav = 0xA004, + /// + /// + /// + ExifInterop = 0xA005, + /// + /// + /// + ExifFlashEnergy = 0xA20B, + /// + /// Spatial Frequency Response + /// + ExifSpatialFR = 0xA20C, + /// + /// Focal Plane X Resolution + /// + ExifFocalXRes = 0xA20E, + /// + /// Focal Plane Y Resolution + /// + ExifFocalYRes = 0xA20F, + /// + /// Focal Plane Resolution Unit + /// + ExifFocalResUnit = 0xA210, + /// + /// + /// + ExifSubjectLoc = 0xA214, + /// + /// + /// + ExifExposureIndex = 0xA215, + /// + /// + /// + ExifSensingMethod = 0xA217, + /// + /// + /// + ExifFileSource = 0xA300, + /// + /// + /// + ExifSceneType = 0xA301, + /// + /// + /// + ExifCfaPattern = 0xA302, + + /// + /// + /// + GpsVer = 0x0000, + /// + /// + /// + GpsLatitudeRef = 0x0001, + /// + /// + /// + /// + /// + /// + GpsLatitude = 0x0002, + /// + /// + /// + GpsLongitudeRef = 0x0003, + /// + /// + /// + GpsLongitude = 0x0004, + /// + /// + /// + GpsAltitudeRef = 0x0005, + /// + /// + /// + GpsAltitude = 0x0006, + /// + /// + /// + GpsGpsTime = 0x0007, + /// + /// + /// + GpsGpsSatellites = 0x0008, + /// + /// + /// + GpsGpsStatus = 0x0009, + /// + /// + /// + GpsGpsMeasureMode = 0x000A, + /// + /// Measurement precision + /// + GpsGpsDop = 0x000B, + /// + /// + /// + GpsSpeedRef = 0x000C, + /// + /// + /// + GpsSpeed = 0x000D, + /// + /// + /// + GpsTrackRef = 0x000E, + /// + /// + /// + GpsTrack = 0x000F, + /// + /// + /// + GpsImgDirRef = 0x0010, + /// + /// + /// + GpsImgDir = 0x0011, + /// + /// + /// + GpsMapDatum = 0x0012, + /// + /// + /// + GpsDestLatRef = 0x0013, + /// + /// + /// + GpsDestLat = 0x0014, + /// + /// + /// + GpsDestLongRef = 0x0015, + /// + /// + /// + GpsDestLong = 0x0016, + /// + /// + /// + GpsDestBearRef = 0x0017, + /// + /// + /// + GpsDestBear = 0x0018, + /// + /// + /// + GpsDestDistRef = 0x0019, + /// + /// + /// + GpsDestDist = 0x001A, + } +} diff --git a/SpiffLib/palette.cs b/SpiffLib/palette.cs new file mode 100644 index 0000000..b503909 --- /dev/null +++ b/SpiffLib/palette.cs @@ -0,0 +1,304 @@ +using System; +using System.IO; +using System.Collections; +using System.Drawing; +using System.Windows.Forms; +using System.Runtime.Serialization; + +namespace SpiffLib +{ + /// + /// + /// + [Serializable] + public class Palette : ISerializable { + private Color[] m_aclr; + private Hashtable m_htbl = new Hashtable(); + private bool m_fClearHash = false; + /// + /// + /// + public string FileName; + + /// + /// + /// + /// + public Palette(string strFile) + { + if (!LoadJasc(strFile)) { + if (!LoadPhotoshopAct(strFile)) { + throw new Exception(strFile + " is not a Jasc or Photoshop .ACT format palette!"); + } + } + FileName = strFile; + } + + /// + /// + /// + /// + public Palette(Color[] aclr) { + m_aclr = (Color[])aclr.Clone(); + } + + /// + /// + /// + public Palette(int cColors) { + m_aclr = new Color[cColors]; + } + + /// + /// + /// + public Palette() { + m_aclr = new Color[0]; + } + + public Palette(SerializationInfo info, StreamingContext ctx) { + m_aclr = (Color[])info.GetValue("Colors", typeof(Color[])); + } + + public void GetObjectData(SerializationInfo info, StreamingContext context) { + info.AddValue("Colors", m_aclr); + } + + /// + /// + /// + /// + /// + public void Pad(int cEntriesUpTo, Color clrPad) { + Color[] aclrNew = new Color[cEntriesUpTo]; + for (int iclr = 0; iclr < aclrNew.Length; iclr++) { + if (iclr < m_aclr.Length) { + aclrNew[iclr] = m_aclr[iclr]; + } else { + aclrNew[iclr] = clrPad; + } + } + m_aclr = aclrNew; + m_fClearHash = true; + } + + /// + /// + /// + /// + /// + public static Palette OpenDialog(string strFile) { + OpenFileDialog frmOpen = new OpenFileDialog(); + frmOpen.Filter = "Jasc Palette File (*.pal)|*.pal|Photoshop Act File (*.act)|*.act"; + frmOpen.Title = "Palette File"; + frmOpen.FileName = strFile; + if (frmOpen.ShowDialog() == DialogResult.Cancel) + return null; + try { + return new Palette(frmOpen.FileName); + } catch { + return null; + } + } + + /// + /// + /// + /// + /// + public bool SaveDialog() { + SaveFileDialog frmSave = new SaveFileDialog(); + frmSave.Filter = "Jasc Palette File (*.pal)|*.pal|Photoshop Act File (*.act)|*.act"; + frmSave.Title = "Palette File"; + if (frmSave.ShowDialog() == DialogResult.Cancel) + return false; + + try { + SaveJasc(frmSave.FileName); + return true; + } catch { + return false; + } + } + + /// + /// + /// + /// + public void SaveBin(string strFile) { + // Write binary palette + Stream stm = new FileStream(strFile, FileMode.Create, FileAccess.Write, FileShare.None); + BinaryWriter bwtr = new BinaryWriter(stm); + bwtr.Write(Misc.SwapUShort((ushort)m_aclr.Length)); + foreach (Color clr in m_aclr) { + bwtr.Write(clr.R); + bwtr.Write(clr.G); + bwtr.Write(clr.B); + } + bwtr.Close(); + } + + /// + /// + /// + /// + /// + public bool LoadJasc(string strFile) { + m_htbl.Clear(); // in case of Palette reuse + + StreamReader stmr = new StreamReader(strFile); + if (stmr.ReadLine() != "JASC-PAL" || stmr.ReadLine() != "0100") { + stmr.Close(); + return false; + } + m_aclr = new Color[int.Parse(stmr.ReadLine())]; + for (int i = 0; i < m_aclr.Length; i++) { + string[] astr = stmr.ReadLine().Split(' '); + m_aclr[i] = Color.FromArgb(int.Parse(astr[0]), int.Parse(astr[1]), int.Parse(astr[2])); + } + stmr.Close(); + return true; + } + + /// + /// + /// + /// + public void SaveJasc(string strFile) { + // Write Jasc palette + Stream stm = new FileStream(strFile, FileMode.Create, FileAccess.Write, FileShare.None); + TextWriter twtr = new StreamWriter(stm); + twtr.WriteLine("JASC-PAL"); + twtr.WriteLine("0100"); + twtr.WriteLine(m_aclr.Length); + foreach(Color clr in m_aclr) + twtr.WriteLine(clr.R.ToString() + " " + clr.G.ToString() + " " + clr.B.ToString()); + twtr.Close(); + } + + /// + /// + /// + /// + /// + public bool LoadPhotoshopAct(string strFile) { + m_htbl.Clear(); // in case of Palette reuse + + FileStream stm = new FileStream(strFile, FileMode.Open, FileAccess.Read); + if (stm.Length != 768) { + stm.Close(); + return false; + } + + m_aclr = new Color[256]; + for (int i = 0; i < 256; i++) { + byte bRed = (byte)stm.ReadByte(); + byte bGreen = (byte)stm.ReadByte(); + byte bBlue = (byte)stm.ReadByte(); + m_aclr[i] = Color.FromArgb(bRed, bGreen, bBlue); + } + stm.Close(); + return true; + } + + /// + /// Save palette in Photoshop Color Table (.ACT) format. This format is extremely + /// simple. 256 entries, 3 bytes each (r, g, b). No count, no header. The output + /// file is always exactly 768 bytes long. + /// + /// + public void SavePhotoshopAct(string strFile) { + // Write binary Photoshop Color Table (.ACT) + Stream stm = new FileStream(strFile, FileMode.Create, FileAccess.Write, FileShare.None); + BinaryWriter bwtr = new BinaryWriter(stm); + foreach (Color clr in m_aclr) { + bwtr.Write(clr.R); + bwtr.Write(clr.G); + bwtr.Write(clr.B); + } + + // Pad out to 256 entries + + for (int i = m_aclr.Length; i < 256; i++) { + bwtr.Write(0); + bwtr.Write(0); + bwtr.Write(0); + } + + bwtr.Close(); + } + + /// + /// + /// + /// + /// + public int FindClosestEntry(Color clr) { + // If not sure of consistency with palette, clear the hash table + if (m_fClearHash) + m_htbl.Clear(); + + // Is this mapping available already? + int key = clr.GetHashCode(); + if (m_htbl.ContainsKey(key)) + return (int)m_htbl[key]; + + // Find the entry, the long way + int nLowest = 256 * 256 * 3; + int iLowest = 0; + int nR = clr.R; + int nG = clr.G; + int nB = clr.B; + for (int iclr = 0; iclr < m_aclr.Length; iclr++) { + Color clrPal = m_aclr[iclr]; + int dR = clrPal.R - nR; + int dG = clrPal.G - nG; + int dB = clrPal.B - nB; + int nD = dR * dR + dG * dG + dB * dB; + if (nD < nLowest) { + nLowest = nD; + iLowest = iclr; + } + } + + // Add it to the hash table and return it + m_htbl.Add(key, iLowest); + return iLowest; + } + + /// + /// clr = pal[iclr]; + /// + public Color this[int iclr] { + get { + return m_aclr[iclr]; + } + set { + m_fClearHash = true; + m_aclr[iclr] = value; + } + } + + /// + /// + /// + public Color[] Colors { + get { + return (Color[])m_aclr.Clone(); + } + set { + m_fClearHash = true; + m_aclr = (Color[])value.Clone(); + } + } + + /// + /// + /// + public int Length { + get { + return m_aclr.Length; + } + } + } +} diff --git a/SpiffLib/palmdatabase.cs b/SpiffLib/palmdatabase.cs new file mode 100644 index 0000000..736ff31 --- /dev/null +++ b/SpiffLib/palmdatabase.cs @@ -0,0 +1,418 @@ +using System; +using System.IO; +using System.Collections; +using System.Diagnostics; + +namespace SpiffLib { +#if false + // These structs straight from Palm includes with minor editting + + struct RecordEntryType { + uint localChunkID; // local chunkID of a record + byte attributes; // record attributes; + byte uniqueID; // unique ID of record; should not be 0 for a legal record. + byte uniqueID_byte1; + byte uniqueID_byte2; + } + + struct RsrcEntryType { + uint type; + ushort id; + uint localChunkID; + } + + struct RecordListType { + uint nextRecordListID; // local chunkID of next list + ushort numRecords; // number of records in this list + ushort firstEntry; // array of Record/Rsrc entries + // starts here + } + + struct DatabaseHdrType { + char[] name; // name of database, 32 bytes + ushort attributes; // database attributes + ushort version; // version of database + uint creationDate; // creation date of database + uint modificationDate; // latest modification date + uint lastBackupDate; // latest backup date + uint modificationNumber; // modification number of database + uint appInfoID; // application specific info + uint sortInfoID; // app specific sorting info + uint type; // database type + uint creator; // database creator + uint uniqueIDSeed; // used to generate unique IDs. + // Note that only the low order + // 3 bytes of this is used (in + // RecordEntryType.uniqueID). + // We are keeping 4 bytes for + // alignment purposes. + RecordListType recordList; // first record list + } + + // DatabaseHdrType - sizeof(ushort) + // followed by RecordEntryType + // followed by record data bytes +#endif + + public class PdbRecord { + public PdbRecord() { + } + + + public byte[] Data { + get { + return m_ab; + } + set { + m_ab = value; + } + } + + public uint Type { + get + { + return m_uiType; + } + set + { + m_uiType = value; + } + } + + public ushort ID { + get { + return m_usID; + } + set { + m_usID = value; + } + } + + public uint IbRec { + get { + return m_ibRec; + } + set { + m_ibRec = value; + } + } + + byte[] m_ab; + uint m_uiType; + ushort m_usID; + uint m_ibRec; + } + + public class PalmDatabase + { + ArrayList m_alsPdbRecords = new ArrayList(); + string m_strName = ""; + + const ushort dmHdrAttrBundle = 0x0800; + const ushort dmHdrAttrBackup = 0x0008; + const ushort dmHdrAttrResDB = 0x0001; + + ushort m_usAttributes = dmHdrAttrBundle | dmHdrAttrBackup; + ushort m_usVersion = 1; + uint m_uiCreationDate = GetCurrentTimePalmUnits(); + uint m_uiType; + uint m_uiCreator; + + static int s_cbDbName = 32; + + public PalmDatabase() + { + } + + public void SetRecordData(ArrayList alsRecordData) + { + m_alsPdbRecords.Clear(); + foreach (byte[] abData in alsRecordData) { + PdbRecord pdbr = new PdbRecord(); + pdbr.Data = (byte[])abData.Clone(); + m_alsPdbRecords.Add(pdbr); + } + } + + public ArrayList GetRecordData() + { + ArrayList alsRecordData = new ArrayList(); + foreach (PdbRecord pdbr in m_alsPdbRecords) + alsRecordData.Add((byte[])pdbr.Data.Clone()); + return alsRecordData; + } + + public void Add(byte [] abData) + { + Add(abData, 0, 0); + } + + public void Add(byte [] abData, ushort usID, uint uiType) + { + PdbRecord pdbr = new PdbRecord(); + pdbr.Data = (byte[])abData.Clone(); + pdbr.ID = usID; + pdbr.Type = uiType; + m_alsPdbRecords.Add(pdbr); + } + + public PdbRecord this [int index] + { + set { + m_alsPdbRecords[index] = value; + } + get { + return (PdbRecord)m_alsPdbRecords[index]; + } + } + + public int Count + { + get { + return m_alsPdbRecords.Count; + } + } + + static uint GetCurrentTimePalmUnits() + { + DateTime dt1904 = new DateTime(1904, 1, 1); + TimeSpan ts = DateTime.Now - dt1904; + return (uint)ts.TotalSeconds; + } + + public void Load(string strFileName) + { + Stream stm = new FileStream(strFileName, FileMode.Open, FileAccess.Read, FileShare.None); + BinaryReader brdr = new BinaryReader(stm); + + // Name + + m_strName = ""; + for (int i = 0; i < s_cbDbName; i++) + { + char ch = brdr.ReadChar(); + if (ch == 0) + break; + m_strName += ch; + } + brdr.BaseStream.Position = (long)s_cbDbName; + + // attributes + m_usAttributes = Misc.SwapUShort(brdr.ReadUInt16()); + + // version + m_usVersion = Misc.SwapUShort(brdr.ReadUInt16()); + + // creationDate + m_uiCreationDate = Misc.SwapUInt(brdr.ReadUInt32()); + + // modificationDate + brdr.ReadUInt32(); + + // lastBackupDate + brdr.ReadUInt32(); + + // modificationNumber + brdr.ReadUInt32(); + + // appInfoID + uint uiAppInfoID = Misc.SwapUInt(brdr.ReadUInt32()); + Debug.Assert(uiAppInfoID == 0); + + // sortInfoID + uint uiSortInfoID = Misc.SwapUInt(brdr.ReadUInt32()); + Debug.Assert(uiSortInfoID == 0); + + // type + m_uiType = Misc.SwapUInt(brdr.ReadUInt32()); + + // creator + m_uiCreator = Misc.SwapUInt(brdr.ReadUInt32()); + + // uniqueIDSeed + brdr.ReadUInt32(); + + // recordList.nextRecordListID + brdr.ReadUInt32(); + + // recordList.numRecords + ushort crecs = Misc.SwapUShort((ushort)brdr.ReadUInt16()); + + // Read in records + + m_alsPdbRecords = new ArrayList(); + + for (int irec = 0; irec < crecs; irec++) { + PdbRecord pdbr = new PdbRecord(); + m_alsPdbRecords.Add(pdbr); + + if ((m_usAttributes & dmHdrAttrResDB) != 0) { + pdbr.Type = Misc.SwapUInt(brdr.ReadUInt32()); + pdbr.ID = Misc.SwapUShort(brdr.ReadUInt16()); + pdbr.IbRec = Misc.SwapUInt(brdr.ReadUInt32()); + } + else { + // localChunkId (actually an offset to the bytes for this record) + pdbr.IbRec = Misc.SwapUInt((uint)brdr.ReadUInt32()); + + // attributes, unique id + + brdr.ReadByte(); + brdr.ReadByte(); + brdr.ReadByte(); + brdr.ReadByte(); + } + } + + for (int irec = 0; irec < crecs; irec++) { + uint ibrecNext; + + if (irec == crecs - 1) + ibrecNext = (uint)brdr.BaseStream.Length; + else + ibrecNext = ((PdbRecord)m_alsPdbRecords[irec+1]).IbRec; + + PdbRecord pdbr = (PdbRecord)m_alsPdbRecords[irec]; + brdr.BaseStream.Position = pdbr.IbRec; + pdbr.Data = brdr.ReadBytes((int)(ibrecNext - pdbr.IbRec)); + } + + // All done + + brdr.Close(); + } + + public bool Save(string strFileName) { + Stream stm = new FileStream(strFileName, FileMode.Create, FileAccess.Write, FileShare.None); + BinaryWriter bwtr = new BinaryWriter(stm); + + // dbName + for (int i = 0; i < s_cbDbName; i++) { + if (i < m_strName.Length) { + bwtr.Write((byte)m_strName[i]); + continue; + } + bwtr.Write((byte)0); + } + + // attributes + bwtr.Write(Misc.SwapUShort(m_usAttributes)); + + // version + bwtr.Write(Misc.SwapUShort(m_usVersion)); + + // creationDate + bwtr.Write(Misc.SwapUInt(m_uiCreationDate)); + + // modificationDate + uint uiDate = Misc.SwapUInt(GetCurrentTimePalmUnits()); + bwtr.Write(uiDate); + + // lastBackupDate + bwtr.Write(uiDate); + + // modificationNumber + bwtr.Write((uint)Misc.SwapUInt(1)); + + // appInfoID + bwtr.Write(Misc.SwapUInt(0)); + + // sortInfoID + bwtr.Write(Misc.SwapUInt(0)); + + // type + bwtr.Write(Misc.SwapUInt(m_uiType)); + + // creator + bwtr.Write(Misc.SwapUInt(m_uiCreator)); + + // uniqueIDSeed + bwtr.Write(Misc.SwapUInt((uint)(m_alsPdbRecords.Count + 1))); + + // recordList.nextRecordListID + bwtr.Write((uint)0); + + // recordList.numRecords + bwtr.Write(Misc.SwapUShort((ushort)m_alsPdbRecords.Count)); + + // Bonus padding because the Tapwave signing tool fails unless it is there + // It is also what buildprc inserts. Devices/simulators/emulators don't need it tho. + + int cbBonusPadding = 2; + + // Calc where the records begin, which is after the record headers + int cbHdr = (int)bwtr.BaseStream.Position; + int ibrecNext = cbHdr + m_alsPdbRecords.Count * ((m_usAttributes & dmHdrAttrResDB) != 0 ? 10 : 8) + cbBonusPadding; + + // Write out the record headers + uint id = 1; + foreach (PdbRecord pdbr in m_alsPdbRecords) { + + if ((m_usAttributes & dmHdrAttrResDB) != 0) { + bwtr.Write(Misc.SwapUInt(pdbr.Type)); + bwtr.Write(Misc.SwapUShort(pdbr.ID)); + bwtr.Write(Misc.SwapUInt((uint)ibrecNext)); + } + else { + // localChunkId (actually an offset to the bytes for this record) + bwtr.Write(Misc.SwapUInt((uint)ibrecNext)); + + // attributes + bwtr.Write((byte)0); + + // uniqueID + bwtr.Write((byte)(id & 0xff)); + bwtr.Write((byte)((id >> 8) & 0xff)); + bwtr.Write((byte)((id >> 16) & 0xff)); + } + + ibrecNext += pdbr.Data.Length; + id++; + } + + // Write bonus padding + + while (cbBonusPadding-- != 0) + bwtr.Write((byte)0); + + // Write out the record data + foreach (PdbRecord pdbr in m_alsPdbRecords) + bwtr.Write(pdbr.Data); + + // Close & stats. + Console.WriteLine(Path.GetFileName(strFileName) + " written. " + bwtr.BaseStream.Length + " bytes."); + bwtr.Close(); + return true; + } + + public string Name { + get { + return m_strName; + } + set { + string strT = value; + if (strT.Length + 1 >= s_cbDbName) + strT = strT.Substring(0, s_cbDbName - 1); + m_strName = strT; + } + } + + public uint CreatorId { + get { + return m_uiCreator; + } + set { + m_uiCreator = value; + } + } + + public uint TypeId { + get { + return m_uiType; + } + set { + m_uiType = value; + } + } + } +} diff --git a/SpiffLib/tbitmap.cs b/SpiffLib/tbitmap.cs new file mode 100644 index 0000000..60e9794 --- /dev/null +++ b/SpiffLib/tbitmap.cs @@ -0,0 +1,838 @@ +using System; +using System.IO; +using System.Collections; +using System.Collections.Specialized; +using System.Drawing; +using System.Drawing.Imaging; +using System.Text.RegularExpressions; +using System.Diagnostics; +using System.Windows.Forms; +using System.Drawing.Drawing2D; + +namespace SpiffLib { + class TBitmap { + enum Align { None, Even, Odd, Both }; + enum ColorType { Unknown, Shadow, Side, Transparent, Data }; + enum Op { + EvenData1, EvenData2, EvenData3, EvenData4, EvenData5, EvenData6, EvenData7, EvenData8, EvenData9, + EvenData10, EvenData11, EvenData12, EvenData13, EvenData14, EvenData15, EvenData16, EvenData17, + EvenData18, EvenData19, EvenData20, EvenData21, EvenData22, EvenData23, EvenData24, EvenData25, + EvenData26, EvenData27, EvenData28, EvenData29, EvenData30, EvenData31, EvenData32, EvenData33, + EvenData34, EvenData35, EvenData36, EvenData37, EvenData38, EvenData39, EvenData40, EvenData41, + EvenData42, EvenData43, EvenData44, EvenData45, EvenData46, EvenData47, EvenData48, + + OddData1, OddData2, OddData3, OddData4, OddData5, OddData6, OddData7, OddData8, OddData9, + OddData10, OddData11, OddData12, OddData13, OddData14, OddData15, OddData16, OddData17, + OddData18, OddData19, OddData20, OddData21, OddData22, OddData23, OddData24, OddData25, + OddData26, OddData27, OddData28, OddData29, OddData30, OddData31, OddData32, OddData33, + OddData34, OddData35, OddData36, OddData37, OddData38, OddData39, OddData40, OddData41, + OddData42, OddData43, OddData44, OddData45, OddData46, OddData47, OddData48, + + EvenSide1, EvenSide2, EvenSide3, EvenSide4, EvenSide5, EvenSide6, EvenSide7, EvenSide8, EvenSide9, + EvenSide10, EvenSide11, EvenSide12, EvenSide13, EvenSide14, EvenSide15, EvenSide16, + + OddSide1, OddSide2, OddSide3, OddSide4, OddSide5, OddSide6, OddSide7, OddSide8, OddSide9, + OddSide10, OddSide11, OddSide12, OddSide13, OddSide14, OddSide15, OddSide16, + + Shadow1, Shadow2, Shadow3, Shadow4, Shadow5, Shadow6, Shadow7, Shadow8, Shadow9, + Shadow10, Shadow11, Shadow12, Shadow13, Shadow14, Shadow15, Shadow16, Shadow17, + Shadow18, Shadow19, Shadow20, Shadow21, Shadow22, Shadow23, Shadow24, + + Transparent1, Transparent2, Transparent3, Transparent4, Transparent5, Transparent6, Transparent7, Transparent8, Transparent9, + Transparent10, Transparent11, Transparent12, Transparent13, Transparent14, Transparent15, Transparent16, Transparent17, + Transparent18, Transparent19, Transparent20, Transparent21, Transparent22, Transparent23, Transparent24, Transparent25, + Transparent26, Transparent27, Transparent28, Transparent29, Transparent30, Transparent31, Transparent32, + + NextScan0, NextScan1, NextScan2, NextScan3, NextScan4, NextScan5, NextScan6, NextScan7, NextScan8, NextScan9, + NextScan10, NextScan11, NextScan12, NextScan13, NextScan14, NextScan15, NextScan16, NextScan17, NextScan18, + NextScan19, NextScan20, NextScan21, NextScan22, NextScan23, NextScan24, NextScan25, NextScan26, NextScan27, + NextScan28, NextScan29, NextScan30, NextScan31, NextScan32, NextScan33, NextScan34, NextScan35, NextScan36, + NextScan37, NextScan38, NextScan39, NextScan40, NextScan41, NextScan42, NextScan43, NextScan44, NextScan45, + NextScan46, NextScan47, NextScan48, + + Align, + End + }; + int m_cx; + int m_cy; + int m_yBaseline; + ScanData[] m_asdEven; + Palette m_pal; + ArrayList m_alsFrames = new ArrayList(); + static Color s_clrShadow = Color.FromArgb(156, 212, 248); + static Color s_clrTransparent = Color.FromArgb(255, 0, 255); + static Color s_clrSideIndex0 = Color.FromArgb(0, 116, 232); + static Color s_clrSideIndex1 = Color.FromArgb(0, 96, 196); + static Color s_clrSideIndex2 = Color.FromArgb(0, 64, 120); + static Color s_clrSideIndex3 = Color.FromArgb(0, 48, 92); + static Color s_clrSideIndex4 = Color.FromArgb(0, 32, 64); + static int s_iclrShadow = -1; + static int s_iclrTransparent = -2; + static int s_iclrNotAssigned = -3; + static int s_iclrSideFirst = -8; + static int s_iclrSideLast = -4; + static int s_cpDataMax = 48; + static int s_cpSideMax = 16; + static int s_cpShadowMax = 24; + static int s_cpTransparentMax = 32; + static int s_cpNextScanMax = 48; + + public TBitmap(string strFile, Palette pal) { + m_pal = pal; + Init(new Bitmap(strFile)); + } + + public TBitmap(Bitmap bm, Palette pal) { + m_pal = pal; + Init(bm); + } + + public TBitmap(Palette pal) { + m_pal = pal; + } + + void Init(Bitmap bm) { + m_cx = bm.Width; + m_cy = bm.Height; + m_yBaseline = Misc.FindBaseline(bm); + int[][] aaiclrScans = GetScans(bm); + m_asdEven = CompileScanData(Align.Even, aaiclrScans); + } + + ScanData[] CompileScanData(Align alignMaster, int[][] aaiclrScans) { + ArrayList alsSd = new ArrayList(); + Align alignStart = Align.Even; + for (int y = 0; y < aaiclrScans.GetLength(0); y++) { + if (alsSd.Count != 0) + alignStart = ((ScanData)alsSd[alsSd.Count - 1]).GetNextDataAlignment(alignStart); + alsSd.Add(new ScanData(alignMaster, alignStart, aaiclrScans[y])); + } + return (ScanData[])alsSd.ToArray(typeof(ScanData)); + } + + unsafe int[][] GetScans(Bitmap bm) { + // Special colors + int[][] aaiclrScans = new int[m_cy][]; + + // Lock down bits for speed + Rectangle rc = new Rectangle(0, 0, m_cx, m_cy); + BitmapData bmd = bm.LockBits(rc, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); + byte *pbBase = (byte *)bmd.Scan0.ToPointer(); + for (int y = 0; y < m_cy; y++) { + int[] aiclrScan = new int[m_cx]; + for (int x = 0; x < m_cx; x++) { + // Get color + byte *pb = pbBase + y * bmd.Stride + x * 3; + Color clr = Color.FromArgb(pb[2], pb[1], pb[0]); + + if (clr == s_clrTransparent) { + aiclrScan[x] = s_iclrTransparent; + continue; + } + + if (clr == s_clrShadow) { + aiclrScan[x] = s_iclrShadow; + continue; + } + + if (clr == s_clrSideIndex0) { + aiclrScan[x] = s_iclrSideFirst + 0; + continue; + } + + if (clr == s_clrSideIndex1) { + aiclrScan[x] = s_iclrSideFirst + 1; + continue; + } + + if (clr == s_clrSideIndex2) { + aiclrScan[x] = s_iclrSideFirst + 2; + continue; + } + + if (clr == s_clrSideIndex3) { + aiclrScan[x] = s_iclrSideFirst + 3; + continue; + } + + if (clr == s_clrSideIndex4) { + aiclrScan[x] = s_iclrSideFirst + 4; + continue; + } + + aiclrScan[x] = m_pal.FindClosestEntry(clr); + } + aaiclrScans[y] = aiclrScan; + } + bm.UnlockBits(bmd); + return aaiclrScans; + } + + class ScanData { + ArrayList m_alsIclr = new ArrayList(); + ArrayList m_alsSideCodes = new ArrayList(); + ArrayList m_alsOps = new ArrayList(); + + struct Run { + public int cp; + public ColorType ct; + public Align align; + public int[] aiclr; + } + + public ScanData(Align alignMaster, Align alignData, int[] aiclrScan) { + CompileRuns(alignData, GetRuns(alignMaster, aiclrScan)); + } + + public Align GetNextDataAlignment(Align alignStart) { + if (alignStart == Align.Even) { + return (m_alsIclr.Count & 1) != 0 ? Align.Odd : Align.Even; + } else { + return (m_alsIclr.Count & 1) == 0 ? Align.Odd : Align.Even; + } + } + + public int[] GetColorData() { + return (int[])m_alsIclr.ToArray(typeof(int)); + } + + public int[] GetSideCodes() { + return (int[])m_alsSideCodes.ToArray(typeof(int)); + } + + public Op[] GetOps() { + return (Op[])m_alsOps.ToArray(typeof(Op)); + } + + void AddSideCodes(Align align, int[] aiclr) { + m_alsSideCodes.AddRange(EncodeSideColors(align, aiclr)); + } + + ColorType GetColorType(int iclr) { + if (iclr == s_iclrTransparent) + return ColorType.Transparent; + + if (iclr == s_iclrShadow) + return ColorType.Shadow; + + if (iclr >= s_iclrSideFirst && iclr <= s_iclrSideLast) + return ColorType.Side; + + return ColorType.Data; + } + + Run[] GetRuns(Align alignScan, int[] aiclrScan) { + // First calc and remember the runs + ArrayList alsCtRuns = new ArrayList(); + ArrayList alsCounts = new ArrayList(); + ColorType ctRun = ColorType.Unknown; + int iiclrFirst = 0; + for (int iiclr = 0; iiclr < aiclrScan.Length; iiclr++) { + // Classify color + ColorType ctCurrent = GetColorType(aiclrScan[iiclr]); + + // Continue if we're in a run + if (ctRun == ctCurrent) + continue; + + // Add the run type and the count of pixels this is + if (ctRun != ColorType.Unknown) { + alsCtRuns.Add(ctRun); + alsCounts.Add(iiclr - iiclrFirst); + } + + // Start a run with this color type + iiclrFirst = iiclr; + ctRun = ctCurrent; + } + // Add the last run + alsCtRuns.Add(ctRun); + alsCounts.Add(aiclrScan.Length - iiclrFirst); + + // Now all the scan is classified in terms of runs of color types. Now create runs. + ArrayList alsRun = new ArrayList(); + int iiclrRun = 0; + for (int i = 0; i < alsCtRuns.Count; i++) { + AddRun(alsRun, alignScan, iiclrRun, aiclrScan, (int)alsCounts[i], (ColorType)alsCtRuns[i]); + iiclrRun += (int)alsCounts[i]; + } + + // All done + return (Run[])alsRun.ToArray(typeof(Run)); + } + + void AddRun(ArrayList alsRun, Align alignScan, int iiclrRun, int[] aiclrScan, int cpRun, ColorType ctRun) { + // Figure out the alignment of this run at the dst if drawn + Align alignDst; + if (alignScan == Align.Even) { + alignDst = (iiclrRun & 1) != 0 ? Align.Odd : Align.Even; + } else { + alignDst = (iiclrRun & 1) == 0 ? Align.Odd : Align.Even; + } + + // Copy run data, figure required alignment + int[] aiclrRun = null; + Align alignRun = Align.None; + switch (ctRun) { + case ColorType.Data: + case ColorType.Side: + alignRun = alignDst; + aiclrRun = new int[cpRun]; + for (int iiclr = iiclrRun; iiclr < iiclrRun + cpRun; iiclr++) + aiclrRun[iiclr - iiclrRun] = aiclrScan[iiclr]; + break; + + case ColorType.Shadow: + break; + + case ColorType.Transparent: + break; + + default: + Debug.Assert(false); + break; + } + + // Add this run + Run run; + run.ct = ctRun; + run.cp = cpRun; + run.align = alignRun; + run.aiclr = aiclrRun; + alsRun.Add(run); + } + + int[] EncodeSideColors(Align align, int[] aiclr) { + int[] asc = new int[(aiclr.Length + 1) / 2]; + for (int isc = 0; isc < asc.Length; isc++) { + int iclr1 = aiclr[isc * 2]; + int iclr2 = s_iclrSideFirst; + if (isc * 2 + 1 < aiclr.Length) + iclr2 = aiclr[isc * 2 + 1]; + asc[isc] = (iclr1 - s_iclrSideFirst) * 2 * 16 + (iclr2 - s_iclrSideFirst) * 2; + } + return asc; + } + + Run[] ChopRuns(Run[] arun) { + // Chop the runs into compatible pieces. Most runs won't get chopped, only runs whose + // length exceeds the op handlers' abilities. Chop wisely, especially runs with + // alignment and / or optimal size issues. + + ArrayList alsRuns = new ArrayList(); + foreach (Run run in arun) { + int cp = run.cp; + Align align = run.align; + Align alignNext = align; + int[] aiclr; + switch (run.ct) { + case ColorType.Data: + aiclr = (int[])run.aiclr.Clone(); + while (aiclr.Length != 0) { + // Figure out the run length to use if this run is being chopped. + // Best bet is to ensure dword moves across the chop + int cpT = aiclr.Length; + if (cpT > s_cpDataMax) { + if (align == Align.Even) { + cpT = s_cpDataMax; + } else { + cpT = ((s_cpDataMax - 1) & ~3) + 1; + alignNext = Align.Even; + } + } + + // Add the run + Run runT = run; + runT.cp = cpT; + runT.align = align; + runT.aiclr = new int[cpT]; + for (int iiclr = 0; iiclr < cpT; iiclr++) + runT.aiclr[iiclr] = aiclr[iiclr]; + alsRuns.Add(runT); + + // Adjust the colors left + int ciclrLeft = aiclr.Length - cpT; + int[] aiclrT = new int[ciclrLeft]; + for (int iiclr = 0; iiclr < ciclrLeft; iiclr++) + aiclrT[iiclr] = aiclr[iiclr + cpT]; + aiclr = aiclrT; + align = alignNext; + } + break; + + case ColorType.Side: + aiclr = (int[])run.aiclr.Clone(); + while (aiclr.Length != 0) { + // Figure out the run length to use if this run is being chopped. + // Best bet is to ensure dword moves across the chop + int cpT = aiclr.Length; + if (cpT > s_cpSideMax) { + if (align == Align.Even) { + cpT = s_cpSideMax; + } else { + cpT = ((s_cpSideMax - 1) & ~3) + 1; + alignNext = Align.Even; + } + } + + // Add the run + Run runT = run; + runT.cp = cpT; + runT.align = align; + runT.aiclr = new int[cpT]; + for (int iiclr = 0; iiclr < cpT; iiclr++) + runT.aiclr[iiclr] = aiclr[iiclr]; + alsRuns.Add(runT); + + // Adjust the colors left + int ciclrLeft = aiclr.Length - cpT; + int[] aiclrT = new int[ciclrLeft]; + for (int iiclr = 0; iiclr < ciclrLeft; iiclr++) + aiclrT[iiclr] = aiclr[iiclr + cpT]; + aiclr = aiclrT; + align = alignNext; + } + break; + + case ColorType.Shadow: + while (cp != 0) { + int cpT = cp > s_cpShadowMax ? s_cpShadowMax : cp; + Run runT = run; + runT.cp = cpT; + alsRuns.Add(runT); + cp -= cpT; + } + break; + + case ColorType.Transparent: + while (cp != 0) { + int cpT = cp > s_cpTransparentMax ? s_cpTransparentMax : cp; + Run runT = run; + runT.cp = cpT; + alsRuns.Add(runT); + cp -= cpT; + } + break; + } + } + return (Run[])alsRuns.ToArray(typeof(Run)); + } + + void CompileRuns(Align alignStart, Run[] arun) { + // First chop the run into compatible pieces. + arun = ChopRuns(arun); + + // Now go through all runs in the scan and encode into ops and data + foreach (Run run in arun) { + switch (run.ct) { + case ColorType.Data: + Align align = GetNextDataAlignment(alignStart); + if (run.align != align && run.cp != 1) { + m_alsOps.Add(Op.Align); + m_alsIclr.Add(s_iclrNotAssigned); + } + if (run.align == Align.Even) { + m_alsOps.Add((Op)(run.cp + (int)Op.EvenData1 - 1)); + } else { + m_alsOps.Add((Op)(run.cp + (int)Op.OddData1 - 1)); + } + m_alsIclr.AddRange(run.aiclr); + break; + + case ColorType.Side: + if (run.align == Align.Even) { + m_alsOps.Add((Op)(run.cp + (int)Op.EvenSide1 - 1)); + } else { + m_alsOps.Add((Op)(run.cp + (int)Op.OddSide1 - 1)); + } + AddSideCodes(run.align, run.aiclr); + break; + + case ColorType.Shadow: + m_alsOps.Add((Op)(run.cp + (int)Op.Shadow1 - 1)); + break; + + case ColorType.Transparent: + m_alsOps.Add((Op)(run.cp + (int)Op.Transparent1 - 1)); + break; + + default: + Debug.Assert(false); + break; + } + } + } + } + + ArrayList SerializeOps(ScanData[] asd) { + // Serialize ops. Remove trailing and beginning transparency between scans + // and replace with NextScan ops. + ArrayList alsOps = new ArrayList(); + ArrayList alsT = new ArrayList(); + int cpSkip = -1; + foreach (ScanData sd in asd) { + alsT.Clear(); + alsT.AddRange(sd.GetOps()); + + // If cpSkip != -1 then we've removed some trailing transparency + // from the last scan. Search the start of this new scan. + if (cpSkip != -1) { + int iop = 0; + for (; iop < alsT.Count; iop++) { + // Add up the transparency + Op op = (Op)alsT[iop]; + if (op >= Op.Transparent1 && op <= Op.Transparent32) { + cpSkip += op - Op.Transparent1 + 1; + continue; + } + break; + } + + // Hit a non-transparent op or end of list. Remove found transparent ops + alsT.RemoveRange(0, iop); + + // Max is... + int cpMax = m_cx > s_cpNextScanMax ? s_cpNextScanMax : m_cx; + + // If there is too much transparency to endcode in one + // NextScan op, cut into pieces. + int cpExtra = cpSkip - cpMax; + if (cpExtra > 0) { + cpSkip -= cpExtra; + while (cpExtra != 0) { + int cpT = cpExtra <= s_cpTransparentMax ? cpExtra : s_cpTransparentMax; + alsT.Insert(0, Op.Transparent1 + cpT - 1); + cpExtra -= cpT; + } + } + + // Insert NextScan op + alsT.Insert(0, Op.NextScan0 + cpSkip); + } + + // Now remove trailing transparency if there is any. + cpSkip = 0; + int iopTrailing = -1; + for (int iop = 0; iop < alsT.Count; iop++) { + Op op = (Op)alsT[iop]; + if (op >= Op.Transparent1 && op <= Op.Transparent32) { + if (iopTrailing == -1) + iopTrailing = iop; + cpSkip += op - Op.Transparent1 + 1; + continue; + } else { + iopTrailing = -1; + cpSkip = 0; + } + } + + // Remove the trailing transparency + if (iopTrailing != -1) { + // Remove this transparency + alsT.RemoveRange(iopTrailing, alsT.Count - iopTrailing); + + // If we've skipped more than the largest EndScan, insert some + // transparency. + int cpExtra = cpSkip - s_cpNextScanMax; + if (cpExtra > 0) { + cpSkip -= cpExtra; + while (cpExtra != 0) { + int cpT = cpExtra <= s_cpTransparentMax ? cpExtra : s_cpTransparentMax; + alsT.Add(Op.Transparent1 + cpT - 1); + cpExtra -= cpT; + } + } + } + + // alsT is ready to add to the ops list + alsOps.AddRange(alsT); + } + + // Add End op + alsOps.Add(Op.End); + return alsOps; + } + + byte[] SerializeScanData() + { + // Combine all ScanData data + ScanData[] asd = m_asdEven; + ArrayList alsSideCodes = new ArrayList(); + ArrayList alsIclr = new ArrayList(); + foreach (ScanData sd in asd) { + alsSideCodes.AddRange(sd.GetSideCodes()); + alsIclr.AddRange(sd.GetColorData()); + } + + // Make ops. Add side codes to op stream to unify stream. + ArrayList alsOpsT = SerializeOps(asd); + ArrayList alsOps = new ArrayList(); + foreach (Op op in alsOpsT) { + alsOps.Add(op); + if (op >= Op.EvenSide1 && op <= Op.EvenSide16) { + int csc = (((op - Op.EvenSide1) + 1) + 1) / 2; + for (int isc = 0; isc < csc; isc++) + alsOps.Add(alsSideCodes[isc]); + alsSideCodes.RemoveRange(0, csc); + } + if (op >= Op.OddSide1 && op <= Op.OddSide16) { + int csc = (((op - Op.OddSide1) + 1) + 1) / 2; + for (int isc = 0; isc < csc; isc++) + alsOps.Add(alsSideCodes[isc]); + alsSideCodes.RemoveRange(0, csc); + } + } + + // Remember count of bytes needed for aligned data. Add one for both alignements. + // This is needed during compiling. Make it an even count. + int cbaiclrUnpacked = ((alsIclr.Count + 1) + 1) & ~1; + + // Create the packed, unaligned color data + ArrayList alsIclrPacked = new ArrayList(); + foreach (int iclr in alsIclr) { + if (iclr != s_iclrNotAssigned) + alsIclrPacked.Add(iclr); + } + + // Serialize: + // public ushort ibaiclr; + // public ushort cbaiclrUnpacked; + // public byte[] aop; + // public byte[] aiclr; + + // Write placeholders + BinaryWriter bwtr = new BinaryWriter(new MemoryStream()); + bwtr.Write((ushort)0); + bwtr.Write((ushort)0); + + // Data + foreach (Op op in alsOps) + bwtr.Write((byte)op); + int ibaiclr = (ushort)bwtr.BaseStream.Position; + foreach (int iclr in alsIclrPacked) + bwtr.Write((byte)iclr); + + // Fix up pointers + int cb = (int)bwtr.BaseStream.Length; + bwtr.BaseStream.Seek(0, SeekOrigin.Begin); + bwtr.Write((ushort)Misc.SwapUShort((ushort)ibaiclr)); + bwtr.Write((ushort)Misc.SwapUShort((ushort)cbaiclrUnpacked)); + + // Return buffer + byte[] ab = new byte[cb]; + bwtr.BaseStream.Seek(0, SeekOrigin.Begin); + bwtr.BaseStream.Read(ab, 0, cb); + bwtr.Close(); + return ab; + } + + static byte[] Serialize(TBitmap[] atbm) { + //struct TBitmapHeader: + // ctbm + // TBitmapEntry[] atbme; + // word cx; + // word cy; + // word yBaseline; + // word ibsd; + // word cbsd; + // ScanData[] asd; + + // Write header info + BinaryWriter bwtr = new BinaryWriter(new MemoryStream()); + bwtr.Write(Misc.SwapUShort((ushort)atbm.Length)); + + // Serialize TBitmapEntry's + ArrayList alsSdBytes = new ArrayList(); + int ibCurrent = 2 + 10 * atbm.Length; + for (int itbm = 0; itbm < atbm.Length; itbm++) { + bwtr.Write(Misc.SwapUShort((ushort)atbm[itbm].m_cx)); + bwtr.Write(Misc.SwapUShort((ushort)atbm[itbm].m_cy)); + bwtr.Write(Misc.SwapUShort((ushort)atbm[itbm].m_yBaseline)); + bwtr.Write(Misc.SwapUShort((ushort)ibCurrent)); + byte[] ab = atbm[itbm].SerializeScanData(); + alsSdBytes.AddRange(ab); + bwtr.Write(Misc.SwapUShort((ushort)ab.Length)); + ibCurrent += ab.Length; + Debug.Assert(ibCurrent < ushort.MaxValue); + } + + // Write sd bytes + bwtr.Write((byte[])alsSdBytes.ToArray(typeof(byte))); + + // Done + byte[] abT = new Byte[bwtr.BaseStream.Length]; + bwtr.BaseStream.Seek(0, SeekOrigin.Begin); + bwtr.BaseStream.Read(abT, 0, abT.Length); + bwtr.Close(); + return abT; + } + + static public void Save(Bitmap[] abm, Palette pal, string strFile) { + // Open file for writing + BinaryWriter bwtr = new BinaryWriter(new FileStream(strFile, FileMode.Create, FileAccess.Write)); + + // Convert into tbm's + TBitmap[] atbm = new TBitmap[abm.Length]; + for (int ibm = 0; ibm < abm.Length; ibm++) + atbm[ibm] = new TBitmap(abm[ibm], pal); + + // Serialize the whole thing + bwtr.Write(Serialize(atbm)); + + // All done + bwtr.Close(); + } + + static public void SaveFont(string strFileBitmap, Palette pal, string strFileAscii, string strFileSave) { + // Get the character order + TextReader tr = new StreamReader(strFileAscii); + string strAscii = tr.ReadLine(); + tr.Close(); + + // Get the character count + int cch = strAscii.Length; + + // Load the image, lose scaling factor + Bitmap bmFile = new Bitmap(strFileBitmap); + Bitmap bm = Misc.NormalizeBitmap(bmFile); + bmFile.Dispose(); + +// Turn this on to see the character -> glyph mapping as it happens (help for +// finding 'font bugs'). Set a breakpoint below on frm.Dispose(). + +#if SHOWFONT + Form frm = new Form(); + frm.Height = 1000; + frm.Show(); + Graphics gT = frm.CreateGraphics(); + gT.InterpolationMode = InterpolationMode.NearestNeighbor; + int yDst = 0; + int xDst = 0; +#endif + // Scan the bitmap for widths + int xLast = 0; + int ich = 0; + byte[] acxChar = new byte[256]; + for (int x = 0; x < bm.Width; x++) { + if (bm.GetPixel(x, 0) != Color.FromArgb(255, 0, 255)) { + Debug.Assert(ich < cch); + acxChar[strAscii[ich]] = (byte)(x - xLast); +#if SHOWFONT + gT.DrawString(strAscii[ich].ToString(), frm.Font, new SolidBrush(frm.ForeColor), new PointF(xDst, yDst)); + Rectangle rcDst = new Rectangle(xDst + 20, yDst + 2, (x - xLast), bm.Height); + Rectangle rcSrc = new Rectangle(xLast, 1, x - xLast, bm.Height); + gT.DrawImage(bm, rcDst, rcSrc, GraphicsUnit.Pixel); + yDst += Math.Max(bm.Height, frm.Font.Height); + if (yDst > frm.ClientRectangle.Height) { + xDst += 50; + yDst = 0; + } + + gT.Flush(); + Application.DoEvents(); +#endif + ich++; + xLast = x; + } + } +#if SHOWFONT + gT.Dispose(); + frm.Dispose(); +#endif + + if (ich != cch) { + MessageBox.Show(String.Format("Expecting {0} characters but found {2}{1}.", + cch, ich, ich < cch ? "only " : ""), "bcr2 - Font Compilation Error"); + Debug.Assert(ich == cch - 1); + } + int cy = bm.Height - 1; + + // Save serialization + ArrayList alsSdEven = new ArrayList(); + int xT = 0; + int ichDefault = -1; + for (ich = 0; ich < cch; ich++) { + // ? is the default "no glyph" character + if (strAscii[ich] == '?') + ichDefault = ich; + + // Get subimage + int cx = acxChar[strAscii[ich]]; + Rectangle rcT = new Rectangle(xT, 1, cx, cy); + xT += cx; + Bitmap bmT = new Bitmap(cx, cy, PixelFormat.Format24bppRgb); + Graphics g = Graphics.FromImage(bmT); + g.DrawImage(bm, 0, 0, rcT, GraphicsUnit.Pixel); + g.Dispose(); + + // Compile scan data + TBitmap tbm = new TBitmap(bmT, pal); + bmT.Dispose(); + + // Save scan data serialization + alsSdEven.Add(tbm.SerializeScanData()); + } + + //FontHeader { + // word cy; + // byte acxChar[256]; + // word mpchibsdEven[256]; + // ScanData asd[1]; + //}; + + // First serialize scan data + + ArrayList alsIbsdEven = new ArrayList(); + ArrayList alsSd = new ArrayList(); + foreach (byte[] absd in alsSdEven) { + if ((alsSd.Count & 1) != 0) + alsSd.Add((byte)0); + alsIbsdEven.Add(alsSd.Count); + alsSd.AddRange(absd); + } + + // Write out to file + BinaryWriter bwtr = new BinaryWriter(new FileStream(strFileSave, FileMode.Create, FileAccess.Write)); + + // Height + bwtr.Write(Misc.SwapUShort((ushort)cy)); + + // Ascii ordered char widths in bytes. First init 0's to width of '?' + + if (ichDefault != -1) { + for (ich = 0; ich < acxChar.Length; ich++) { + if (acxChar[ich] == 0) { + acxChar[ich] = acxChar[strAscii[ichDefault]]; + } + } + } + bwtr.Write(acxChar); + + // Ascii ordered offsets to even scan data (even) + // Fill unused entries to entry for '?' + int[] aibsdEven = new int[256]; + for (int ibsd = 0; ibsd < aibsdEven.Length; ibsd++) + aibsdEven[ibsd] = -1; + for (int i = 0; i < cch; i++) + aibsdEven[strAscii[i]] = (int)alsIbsdEven[i]; + if (ichDefault != -1) { + for (int ibsd = 0; ibsd < aibsdEven.Length; ibsd++) { + if (aibsdEven[ibsd] == -1) { + aibsdEven[ibsd] = (int)alsIbsdEven[ichDefault]; + } + } + } + + // Write it out + int cbHeader = 2 + 256 + 512; + for (int i = 0; i < 256; i++) + bwtr.Write(Misc.SwapUShort((ushort)(cbHeader + aibsdEven[i]))); + + // Now save scan data + bwtr.Write((byte[])alsSd.ToArray(typeof(byte))); + + // Done + bwtr.Close(); + } + } +} diff --git a/SpiffLib/tbitmapkey.cs b/SpiffLib/tbitmapkey.cs new file mode 100644 index 0000000..6684335 --- /dev/null +++ b/SpiffLib/tbitmapkey.cs @@ -0,0 +1,110 @@ +using System; +using System.IO; +using System.Collections; +using System.Drawing; +using System.Drawing.Imaging; + +namespace SpiffLib { + /// + /// + /// + public class TBitmap { + private Bitmap m_bm; + private Palette m_pal; + + /// + /// + /// + /// + /// + public TBitmap(string strFile, Palette pal) { + m_bm = new Bitmap(strFile); + m_pal = pal; + } + + /// + /// + /// + /// + /// + public TBitmap(Bitmap bm, Palette pal) { + m_bm = new Bitmap(bm); + m_pal = pal; + } + + /// + /// + /// + public void Dispose() { + m_bm.Dispose(); + } + + /// + /// + /// + /// + /// + public unsafe bool CheckColorsMatch(out Color clrMismatch) { + // Lock down bits for speed + Rectangle rc = new Rectangle(0, 0, m_bm.Width, m_bm.Height); + BitmapData bmd = m_bm.LockBits(rc, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); + byte *pbBase = (byte *)bmd.Scan0.ToPointer(); + + // Make sure each pixel is an exact match + clrMismatch = Color.FromArgb(0, 0, 0); + for (int y = 0; y < m_bm.Height; y++) { + for (int x = 0; x < m_bm.Width; x++) { + byte *pb = pbBase + y * bmd.Stride + x * 3; + Color clr = Color.FromArgb(pb[2], pb[1], pb[0]); + if (m_pal[m_pal.FindClosestEntry(clr)] != clr) { + m_bm.UnlockBits(bmd); + clrMismatch = clr; + return false; + } + } + } + m_bm.UnlockBits(bmd); + return true; + } + + /// + /// + /// + /// + public unsafe void Save(string strFile) { + // Find the palette index for the transparent color + Color clrTransparent = Color.FromArgb(0xff, 0, 0xff); + int iclrTransparent = m_pal.FindClosestEntry(clrTransparent); + + // Lock down bits for speed + Rectangle rc = new Rectangle(0, 0, m_bm.Width, m_bm.Height); + BitmapData bmd = m_bm.LockBits(rc, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); + byte *pbBase = (byte *)bmd.Scan0.ToPointer(); + + // Map all the pixels in the bitmap to 'nearest' color palette indices + byte[] ab = new byte[(m_bm.Width + 1 & ~1) * m_bm.Height]; + int i = 0; + for (int y = 0; y < m_bm.Height; y++) { + for (int x = 0; x < m_bm.Width; x++) { + byte *pb = pbBase + y * bmd.Stride + x * 3; + Color clr = Color.FromArgb(pb[2], pb[1], pb[0]); + if (clr == Color.FromArgb(156, 212, 248)) + clr = Color.FromArgb(0, 0, 0); + ab[i++] = (byte)m_pal.FindClosestEntry(clr); + } + if ((m_bm.Width & 1) == 1) + ab[i++] = (byte)iclrTransparent; + } + m_bm.UnlockBits(bmd); + + // Write bitmap header, bits + BinaryWriter bwtr = new BinaryWriter(new FileStream(strFile, FileMode.Create, FileAccess.Write)); + bwtr.Write(Misc.SwapUShort((ushort)TbmType.ColorKey)); + bwtr.Write(Misc.SwapUShort((ushort)m_bm.Width)); + bwtr.Write(Misc.SwapUShort((ushort)m_bm.Height)); + bwtr.Write((ushort)(((byte)iclrTransparent << 8) | (byte)iclrTransparent)); + bwtr.Write(ab); + bwtr.Close(); + } + } +} diff --git a/SpiffLib/tbitmapsr.cs b/SpiffLib/tbitmapsr.cs new file mode 100644 index 0000000..9a73ca7 --- /dev/null +++ b/SpiffLib/tbitmapsr.cs @@ -0,0 +1,1887 @@ +using System; +using System.IO; +using System.Collections; +using System.Collections.Specialized; +using System.Drawing; +using System.Drawing.Imaging; +using System.Diagnostics; + +namespace SpiffLib { + class TBitmapSR { + enum Op { + EvenData1, // 0 + EvenData1_Inc, // 1 + EvenData2, // 2 + EvenData2_Inc, // 3 + EvenData3, // 4 + EvenData3_Inc, // 5 + EvenData4, // 6 + EvenData4_Inc, // 7 + EvenData5, // 8 + EvenData5_Inc, // 9 + EvenData6, // 10 + EvenData6_Inc, // 11 + EvenData7, // 12 + EvenData7_Inc, // 13 + EvenData8, // 14 + EvenData8_Inc, // 15 + EvenData9, // 16 + EvenData9_Inc, // 17 + EvenData10, // 18 + EvenData10_Inc, // 19 + EvenData11, // 20 + EvenData11_Inc, // 21 + EvenData12, // 22 + EvenData12_Inc, // 23 + EvenData13, // 24 + EvenData13_Inc, // 25 + EvenData14, // 26 + EvenData14_Inc, // 27 + EvenData15, // 28 + EvenData15_Inc, // 29 + EvenData16, // 30 + EvenData16_Inc, // 31 + EvenData17, // 32 + EvenData17_Inc, // 33 + EvenData18, // 34 + EvenData18_Inc, // 35 + EvenData19, // 36 + EvenData19_Inc, // 37 + EvenData20, // 38 + EvenData20_Inc, // 39 + EvenData21, // 40 + EvenData21_Inc, // 41 + EvenData22, // 42 + EvenData22_Inc, // 43 + EvenData23, // 44 + EvenData23_Inc, // 45 + EvenData24, // 46 + EvenData24_Inc, // 47 + EvenData25, // 48 + EvenData25_Inc, // 49 + EvenData26, // 50 + EvenData26_Inc, // 51 + EvenData27, // 52 + EvenData27_Inc, // 53 + EvenData28, // 54 + EvenData28_Inc, // 55 + EvenData29, // 56 + EvenData29_Inc, // 57 + EvenData30, // 58 + EvenData30_Inc, // 59 + EvenData31, // 60 + EvenData31_Inc, // 61 + EvenData32, // 62 + EvenData32_Inc, // 63 + OddData1, // 64 + OddData1_Inc, // 65 + OddData2, // 66 + OddData2_Inc, // 67 + OddData3, // 68 + OddData3_Inc, // 69 + OddData4, // 70 + OddData4_Inc, // 71 + OddData5, // 72 + OddData5_Inc, // 73 + OddData6, // 74 + OddData6_Inc, // 75 + OddData7, // 76 + OddData7_Inc, // 77 + OddData8, // 78 + OddData8_Inc, // 79 + OddData9, // 80 + OddData9_Inc, // 81 + OddData10, // 82 + OddData10_Inc, // 83 + OddData11, // 84 + OddData11_Inc, // 85 + OddData12, // 86 + OddData12_Inc, // 87 + OddData13, // 88 + OddData13_Inc, // 89 + OddData14, // 90 + OddData14_Inc, // 91 + OddData15, // 92 + OddData15_Inc, // 93 + OddData16, // 94 + OddData16_Inc, // 95 + OddData17, // 96 + OddData17_Inc, // 97 + OddData18, // 98 + OddData18_Inc, // 99 + OddData19, // 100 + OddData19_Inc, // 101 + OddData20, // 102 + OddData20_Inc, // 103 + OddData21, // 104 + OddData21_Inc, // 105 + OddData22, // 106 + OddData22_Inc, // 107 + OddData23, // 108 + OddData23_Inc, // 109 + OddData24, // 110 + OddData24_Inc, // 111 + OddData25, // 112 + OddData25_Inc, // 113 + OddData26, // 114 + OddData26_Inc, // 115 + OddData27, // 116 + OddData27_Inc, // 117 + OddData28, // 118 + OddData28_Inc, // 119 + OddData29, // 120 + OddData29_Inc, // 121 + OddData30, // 122 + OddData30_Inc, // 123 + OddData31, // 124 + OddData31_Inc, // 125 + OddData32, // 126 + OddData32_Inc, // 127 + Side1, // 128 + Side2, // 129 + Side3, // 130 + Side4, // 131 + Side5, // 132 + Side6, // 133 + Side7, // 134 + Side8, // 135 + Side9, // 136 + Side10, // 137 + Side11, // 138 + Side12, // 139 + Side13, // 140 + Side14, // 141 + Side15, // 142 + Side16, // 143 + Side17, // 144 + Side18, // 145 + Side19, // 146 + Side20, // 147 + Side21, // 148 + Side22, // 149 + Side23, // 150 + Side24, // 151 + Side25, // 152 + Side26, // 153 + Side27, // 154 + Side28, // 155 + Side29, // 156 + Side30, // 157 + Side31, // 158 + Side32, // 159 + Shadow1, // 160 + Shadow2, // 161 + Shadow3, // 162 + Shadow4, // 163 + Shadow5, // 164 + Shadow6, // 165 + Shadow7, // 166 + Shadow8, // 167 + Shadow9, // 168 + Shadow10, // 169 + Shadow11, // 170 + Shadow12, // 171 + Shadow13, // 172 + Shadow14, // 173 + Shadow15, // 174 + Shadow16, // 175 + Shadow17, // 176 + Shadow18, // 177 + Shadow19, // 178 + Shadow20, // 179 + Shadow21, // 180 + Shadow22, // 181 + Shadow23, // 182 + Shadow24, // 183 + Shadow25, // 184 + Shadow26, // 185 + Shadow27, // 186 + Shadow28, // 187 + Shadow29, // 188 + Shadow30, // 189 + Shadow31, // 190 + Shadow32, // 191 + EvenDataLB, // 192 + EvenDataLB_Inc, // 193 + EvenDataLW, // 194 + EvenDataLW_Inc, // 195 + EvenDataLWB, // 196 + EvenDataLWB_Inc, // 197 + EvenDataL, // 198 + EvenDataL_Inc, // 199 + OddDataLB, // 200 + OddDataLB_Inc, // 201 + OddDataLW, // 202 + OddDataLW_Inc, // 203 + OddDataLWB, // 204 + OddDataLWB_Inc, // 205 + OddDataL, // 206 + OddDataL_Inc, // 207 + SideN, // 208 + ShadowN, // 209 + TransparentN, // 210 + EndScan, // 211 + Error, // 212 + EvenDataStart = 0, + EvenDataEnd = 63, + OddDataStart = 64, + OddDataEnd = 127, + SideStart = 128, + SideEnd = 159, + ShadowStart = 160, + ShadowEnd = 191, + EvenDataNStart = 192, + EvenDataNEnd = 199, + OddDataNStart = 200, + OddDataNEnd = 207 + }; + + enum OpType { None, Data, Side, Shadow, Transparent }; + + struct Frame { + public ArrayList alsArunsEven; + public ArrayList alsArunsOdd; + } + + struct Run { + public int cp; + public ColorType ct; + public Align align; + public int[] aiclr; + public bool fNeedSrcAlign; + } + + struct RunArgs { + public Op op; + public int cpSrc; + public int cpArgs; + public int cpDst; + } + + class CompileResults { + public ArrayList alsIclr; + public ArrayList alsRa; + public int[,] aaiiclrEven; + public int[,] aairaEven; + public int[,] aaiiclrOdd; + public int[,] aairaOdd; + } + + int m_cx; + int m_cy; + Palette m_pal; + ArrayList m_alsFrames = new ArrayList(); + CompileResults m_crLast; + static Color m_clrShadow = Color.FromArgb(156, 212, 248); + static Color m_clrTransparent = Color.FromArgb(255, 0, 255); + static int s_iclrShadow = -1; + static int s_iclrTransparent = -2; + static int s_iclrNotAssigned = -3; + static int s_iclrSideFirst = 16; + static int s_iclrSideLast = 20; + static int s_cpSideOpFixedMax = 32; + static int s_cpShadowOpFixedMax = 32; + static int s_cpDataOpFixedMax = 32; + static Op[] s_mpcpTrailingOpDataEven = { Op.EvenDataL, Op.EvenDataLB, Op.EvenDataLW, Op.EvenDataLWB }; + static Op[] s_mpcpTrailingOpDataOdd = { Op.OddDataL, Op.OddDataLB, Op.OddDataLW, Op.OddDataLWB }; + + public TBitmapSR(Palette pal) { + m_cx = -1; + m_cy = -1; + m_pal = pal; + } + + public void Dispose() { + } + + public void AddFrame(Bitmap bm) { + if (m_cx == -1) { + m_cx = bm.Width; + m_cy = bm.Height; + } + if (m_cx != bm.Width || m_cy != bm.Height) + throw new Exception("Bitmap different width and height!"); + + // Get color converted scanlines for this bitmap + int[][] aaiclrScans = GetScans(bm); + + // Add ScanRecords for each scan + Frame frame = new Frame(); + frame.alsArunsEven = new ArrayList(); + frame.alsArunsOdd = new ArrayList(); + for (int y = 0; y < m_cy; y++) { + frame.alsArunsEven.Add(GetRuns(Align.Even, aaiclrScans[y])); + frame.alsArunsOdd.Add(GetRuns(Align.Odd, aaiclrScans[y])); + } + + // Add this frame + m_alsFrames.Add(frame); + } + + unsafe int[][] GetScans(Bitmap bm) { + // Special colors + int[][] aaiclrScans = new int[m_cy][]; + + // Lock down bits for speed + Rectangle rc = new Rectangle(0, 0, m_cx, m_cy); + BitmapData bmd = bm.LockBits(rc, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); + byte *pbBase = (byte *)bmd.Scan0.ToPointer(); + for (int y = 0; y < m_cy; y++) { + int[] aiclrScan = new int[m_cx]; + for (int x = 0; x < m_cx; x++) { + // Get color + byte *pb = pbBase + y * bmd.Stride + x * 3; + Color clr = Color.FromArgb(pb[2], pb[1], pb[0]); + + // Transparent and shadow don't exist in the output palette + // so they have special values. + if (clr == m_clrTransparent) { + aiclrScan[x] = s_iclrTransparent; + continue; + } + if (clr == m_clrShadow) { + aiclrScan[x] = s_iclrShadow; + continue; + } + + // Find closest entry in this palette + aiclrScan[x] = m_pal.FindClosestEntry(clr); + } + aaiclrScans[y] = aiclrScan; + } + bm.UnlockBits(bmd); + return aaiclrScans; + } + + ColorType GetColorType(int iclr) { + if (iclr == s_iclrTransparent) + return ColorType.Transparent; + + if (iclr == s_iclrShadow) + return ColorType.Shadow; + + if (iclr >= s_iclrSideFirst && iclr <= s_iclrSideLast) + return ColorType.Side; + + return ColorType.Data; + } + + Run[] GetRuns(Align alignScan, int[] aiclrScan) { + // First calc and remember the runs + ArrayList alsCtRuns = new ArrayList(); + ArrayList alsCounts = new ArrayList(); + ColorType ctRun = ColorType.Unknown; + int iiclrFirst = 0; + for (int iiclr = 0; iiclr < aiclrScan.Length; iiclr++) { + // Classify color + ColorType ctCurrent = GetColorType(aiclrScan[iiclr]); + + // Continue if we're in a run + if (ctRun == ctCurrent) + continue; + + // We've encountered a color of a different type. + // If we're leaving a side run or if we're entering a data run, then check if side colors are + // close ahead. If they are then enter a side run or stay in the existing side run for + // efficiency's sake. + if (ctCurrent == ColorType.Data) { + for (int iiclrT = iiclr + 1; iiclrT < iiclr + 6; iiclrT++) { + // If we've run out of pixels, continue the side run if that's what we're in + if (iiclrT >= aiclrScan.Length) { + if (ctRun == ColorType.Side) + ctCurrent = ColorType.Side; + break; + } + + // If we encountered a side color, don't switch + ColorType ctT = GetColorType(aiclrScan[iiclrT]); + if (ctT == ColorType.Side) { + ctCurrent = ColorType.Side; + break; + } + + // If we've hit something other than data, it's cheaper to stay with the side run than + // switch to a small data run. + if (ctT != ColorType.Data) { + if (ctRun == ColorType.Side) + ctCurrent = ColorType.Side; + break; + } + } + if (ctRun == ctCurrent) + continue; + } + + // Add the run type and the count of pixels this is + if (ctRun != ColorType.Unknown) { + alsCtRuns.Add(ctRun); + alsCounts.Add(iiclr - iiclrFirst); + Debug.Assert((int)alsCounts[alsCounts.Count - 1] <= 255); + } + + // Start a run with this color type + iiclrFirst = iiclr; + ctRun = ctCurrent; + } + // Add the last run + alsCtRuns.Add(ctRun); + alsCounts.Add(aiclrScan.Length - iiclrFirst); + Debug.Assert((int)alsCounts[alsCounts.Count - 1] <= 255); + + // Now all the scan is classified in terms of runs of color types. Now create runs. + ArrayList alsRun = new ArrayList(); + int iiclrRun = 0; + for (int i = 0; i < alsCtRuns.Count; i++) { + if (i == alsCtRuns.Count - 1 && (ColorType)alsCtRuns[i] == ColorType.Transparent) + break; + AddRun(alsRun, alignScan, iiclrRun, aiclrScan, (int)alsCounts[i], (ColorType)alsCtRuns[i]); + iiclrRun += (int)alsCounts[i]; + } + + // Add terminator + Run run; + run.ct = ColorType.EndScan; + run.cp = 0; + run.align = Align.None; + run.aiclr = null; + run.fNeedSrcAlign = false; + alsRun.Add(run); + + // All done + return (Run[])alsRun.ToArray(typeof(Run)); + } + + void AddRun(ArrayList alsRun, Align alignScan, int iiclrRun, int[] aiclrScan, int cpRun, ColorType ctRun) { + // Figure out the alignment of this run at the dst if drawn + Align alignDst; + if (alignScan == Align.Even) { + alignDst = (iiclrRun & 1) != 0 ? Align.Odd : Align.Even; + } else { + alignDst = (iiclrRun & 1) == 0 ? Align.Odd : Align.Even; + } + + // Copy run data, figure required alignment + int[] aiclrRun = null; + Align alignRun = Align.None; + switch (ctRun) { + case ColorType.Data: + // Always aligned; needed to ensure word / dword copies + alignRun = alignDst; + goto CopyRun; + + case ColorType.Side: +CopyRun: + aiclrRun = new int[cpRun]; + for (int iiclr = iiclrRun; iiclr < iiclrRun + cpRun; iiclr++) + aiclrRun[iiclr - iiclrRun] = aiclrScan[iiclr]; + break; + + case ColorType.Shadow: + break; + + case ColorType.Transparent: + break; + + default: + Debug.Assert(false); + break; + } + + // Add this run + Run run; + run.ct = ctRun; + run.cp = cpRun; + run.align = alignRun; + run.aiclr = aiclrRun; + run.fNeedSrcAlign = false; + alsRun.Add(run); + } + + struct RunDataInstance { + public Run[] arun; + public int[] aiclr; + public Align align; + } + + RunDataInstance[] PermuteRunData(Run[] arun, int cMax) { + ArrayList alsRdInst = new ArrayList(); + Permute2(Align.Even, 0, arun, new int[0], alsRdInst, cMax); + Permute2(Align.Odd, 0, arun, new int[0], alsRdInst, cMax); + return (RunDataInstance[])alsRdInst.ToArray(typeof(RunDataInstance)); + } + + void Permute2(Align align, int irun, Run[] arun, int[] aiclr, ArrayList alsRdInst, int cMax) { + if (irun >= arun.Length) { + RunDataInstance rdinst; + rdinst.arun = (Run[])arun.Clone(); + rdinst.aiclr = (int[])aiclr.Clone(); + rdinst.align = align; + alsRdInst.Add(rdinst); + return; + } + + // Determine arguments + RunArgs ra = GetRunArg(arun[irun]); + int nArg = GetCountArg(ra); + if (arun[irun].align != Align.None) { + // Forced alignment + ArrayList alsIclr = new ArrayList(); + alsIclr.AddRange(aiclr); + Align alignDst; + if (align == Align.Even) { + alignDst = (aiclr.Length & 1) != 0 ? Align.Odd : Align.Even; + } else { + alignDst = (aiclr.Length & 1) == 0 ? Align.Odd : Align.Even; + } + arun[irun].fNeedSrcAlign = false; + if (nArg != -1) { + if (alignDst == arun[irun].align) { + arun[irun].fNeedSrcAlign = true; + alsIclr.Add(s_iclrNotAssigned); + } + alsIclr.Add(nArg); + } else { + if (alignDst != arun[irun].align) { + arun[irun].fNeedSrcAlign = true; + alsIclr.Add(s_iclrNotAssigned); + } + } + alsIclr.AddRange(arun[irun].aiclr); + Permute2(align, irun + 1, arun, (int[])alsIclr.ToArray(typeof(int)), alsRdInst, cMax); + return; + } else { + // No adjust + ArrayList alsIclr = new ArrayList(); + alsIclr.AddRange(aiclr); + if (nArg != -1) + alsIclr.Add(nArg); + if (arun[irun].aiclr != null) + alsIclr.AddRange(arun[irun].aiclr); + arun[irun].fNeedSrcAlign = false; + Permute2(align, irun + 1, arun, (int[])alsIclr.ToArray(typeof(int)), alsRdInst, cMax); + if (cMax != -1 && alsRdInst.Count >= cMax) + return; + +#if false +//This saves a modest amount of space however currently we're not supporting _Incs of all ops. + // With adjust + alsIclr = new ArrayList(); + alsIclr.AddRange(aiclr); + alsIclr.Add(s_iclrNotAssigned); + if (fCountArg) + alsIclr.Add(arun[irun].cp - 1); + arun[irun].fNeedSrcAlign = true; + if (arun[irun].aiclr != null) + alsIclr.AddRange(arun[irun].aiclr); + Permute2(align, irun + 1, arun, (int[])alsIclr.ToArray(typeof(int)), alsRdInst, cMax); +#endif + } + } + + int CompileImageData(ArrayList alsIclr, Run[] arun, bool fSmallest) { + // Get all permutations possible of run data + RunDataInstance[] ardinst = PermuteRunData(arun, fSmallest ? -1 : 1); + + // Match the best permutation with existing data + int iiclrStart; + int ird = AddBestRunDataInstance(alsIclr, ardinst, out iiclrStart); + RunDataInstance rdinst = ardinst[ird]; + + // Patch the passed arun with the matched run + for (int irun = 0; irun < arun.Length; irun++) + arun[irun] = rdinst.arun[irun]; + + // Return where the data starts +//temp +// Debug.Assert(iiclrStart == MatchImageData(rdinst.align, rdinst.aiclr, alsIclr)); + return iiclrStart; + } + + int AddBestRunDataInstance(ArrayList alsIclr, RunDataInstance[] ardinst, out int iiclrStart) { + // Match against all permutations + int irdLowest = 0; + int iiclrLowest = alsIclr.Count; + for (int ird = 0; ird < ardinst.Length; ird++) { + RunDataInstance rdinst = ardinst[ird]; + int iiclrMatch = MatchImageData(rdinst.align, rdinst.aiclr, alsIclr); + if (iiclrMatch < iiclrLowest) { + iiclrLowest = iiclrMatch; + irdLowest = ird; + } + } + + // Contained or partial match + if (iiclrLowest < alsIclr.Count) { + // Patch s_iclrNotAssigned in dst since we can reuse them + RunDataInstance rdinst = ardinst[irdLowest]; + int iiclr = 0; + for (; iiclr < rdinst.aiclr.Length && iiclr + iiclrLowest < alsIclr.Count; iiclr++) { + if ((int)alsIclr[iiclrLowest + iiclr] == s_iclrNotAssigned) + alsIclr[iiclrLowest + iiclr] = rdinst.aiclr[iiclr]; + Debug.Assert(rdinst.aiclr[iiclr] == s_iclrNotAssigned || rdinst.aiclr[iiclr] == (int)alsIclr[iiclrLowest + iiclr]); + } + + // Add the rest if needed + for (; iiclr < rdinst.aiclr.Length; iiclr++) { + Debug.Assert(iiclrLowest + iiclr == alsIclr.Count); + alsIclr.Add(rdinst.aiclr[iiclr]); + } + iiclrStart = iiclrLowest; + return irdLowest; + } + + // No best match; add the smallest + int irdSmallest = 0; + int cbSmallest = 0x7fff; + Align alignDst = (alsIclr.Count & 1) != 0 ? Align.Odd : Align.Even; + for (int ird = 0; ird < ardinst.Length; ird++) { + RunDataInstance rdinst = ardinst[ird]; + int cbT = rdinst.aiclr.Length; + +//temp +#if true + if (alignDst != rdinst.align) + continue; +#endif + + if (alignDst != rdinst.align) + cbT++; + if (cbT < cbSmallest) { + irdSmallest = ird; + cbSmallest = cbT; + } + } + + // Add the data + if (alignDst != ardinst[irdSmallest].align) + alsIclr.Add(s_iclrNotAssigned); + int iiclrDst = alsIclr.Count; + alsIclr.AddRange(ardinst[irdSmallest].aiclr); + iiclrStart = iiclrDst; + return irdSmallest; + } + + int MatchImageData(Align align, int[] aiclr, ArrayList alsIclr) { +//temp +#if false + // Find a full or partial (off the end) match + bool fMatch = false; + for (int iiclrDst = 0; iiclrDst < alsIclr.Count; iiclrDst++) { + // Proper alignment? + Align alignDst = (iiclrDst & 1) != 0 ? Align.Odd : Align.Even; + if (align != alignDst) + continue; + + // Match + fMatch = true; + for (int iiclrSrc = 0; iiclrSrc < aiclr.Length; iiclrSrc++) { + // Partial match? + int iiclrDstT = iiclrDst + iiclrSrc; + if (iiclrDstT >= alsIclr.Count) + return iiclrDst; + + // Continue matching + if (aiclr[iiclrSrc] == s_iclrNotAssigned) + continue; + if ((int)alsIclr[iiclrDstT] == s_iclrNotAssigned) + continue; + if (aiclr[iiclrSrc] == (int)alsIclr[iiclrDstT]) + continue; + fMatch = false; + break; + } + if (fMatch) + return iiclrDst; + } + +#endif + // No match + return alsIclr.Count; + } + + int GetCountArg(RunArgs ra) { + switch (ra.op) { + case Op.EvenDataLB: + case Op.EvenDataLB_Inc: + Debug.Assert(((ra.cpDst - 1) & 3) == 0); + return (63 - (ra.cpDst - 1) / 4) * 2; + + case Op.EvenDataLW: + case Op.EvenDataLW_Inc: + Debug.Assert(((ra.cpDst - 2) & 3) == 0); + return (63 - (ra.cpDst - 2) / 4) * 2; + + case Op.EvenDataLWB: + case Op.EvenDataLWB_Inc: + Debug.Assert(((ra.cpDst - 3) & 3) == 0); + return (63 - (ra.cpDst - 3) / 4) * 2; + + case Op.EvenDataL: + case Op.EvenDataL_Inc: + Debug.Assert(((ra.cpDst - 0) & 3) == 0); + return (64 - (ra.cpDst - 0) / 4) * 2; + + case Op.OddDataLB: + case Op.OddDataLB_Inc: + Debug.Assert(((-1 + ra.cpDst - 1) & 3) == 0); + return (63 - (-1 + ra.cpDst - 1) / 4) * 2; + + case Op.OddDataLW: + case Op.OddDataLW_Inc: + Debug.Assert(((-1 + ra.cpDst - 2) & 3) == 0); + return (63 - (-1 + ra.cpDst - 2) / 4) * 2; + + case Op.OddDataLWB: + case Op.OddDataLWB_Inc: + Debug.Assert(((-1 + ra.cpDst - 3) & 3) == 0); + return (63 - (-1 + ra.cpDst - 3) / 4) * 2; + + case Op.OddDataL: + case Op.OddDataL_Inc: + Debug.Assert(((-1 + ra.cpDst - 0) & 3) == 0); + return (63 - (-1 + ra.cpDst - 0) / 4) * 2; + + case Op.SideN: + case Op.ShadowN: + Debug.Assert(ra.cpDst < 64); + return (64 - ra.cpDst) * 6 / 2; + + case Op.TransparentN: + return ra.cpDst; + } + return -1; + } + + RunArgs GetRunArg(Run run) { + // Figure out the appropriate op for this run + Op op = Op.Error; + switch (run.ct) { + case ColorType.Transparent: + op = Op.TransparentN; + Debug.Assert(run.aiclr == null); + break; + + case ColorType.Side: + if (run.cp <= s_cpSideOpFixedMax) { + op = Op.SideStart + run.cp - 1; + } else { + op = Op.SideN; + } + Debug.Assert(run.aiclr != null); + Debug.Assert(run.aiclr.Length == run.cp); + break; + + case ColorType.Shadow: + if (run.cp <= s_cpShadowOpFixedMax) { + op = Op.ShadowStart + run.cp - 1; + } else { + op = Op.ShadowN; + } + Debug.Assert(run.aiclr == null); + break; + + case ColorType.Data: + if (run.cp <= s_cpDataOpFixedMax) { + if (run.align == Align.Even) { + op = (Op)((int)Op.EvenDataStart + (run.cp - 1) * 2 + (run.fNeedSrcAlign ? 1 : 0)); + } else if (run.align == Align.Odd) { + op = (Op)((int)Op.OddDataStart + (run.cp - 1) * 2 + (run.fNeedSrcAlign ? 1 : 0)); + } else { + Debug.Assert(false); + } + } else { + if (run.align == Align.Even) { + op = s_mpcpTrailingOpDataEven[run.aiclr.Length % 4]; + } else { + op = s_mpcpTrailingOpDataOdd[(run.aiclr.Length - 1)% 4]; + } + op += (run.fNeedSrcAlign ? 1 : 0); + } + Debug.Assert(run.aiclr != null); + Debug.Assert(run.aiclr.Length == run.cp); + break; + + case ColorType.EndScan: + op = Op.EndScan; + Debug.Assert(run.aiclr == null); + break; + + default: + op = Op.Error; + Debug.Assert(false); + break; + } + + // Make RunArgs + RunArgs ra; + ra.op = op; + ra.cpDst = run.cp; + ra.cpSrc = 0; + ra.cpArgs = 0; + if (run.aiclr != null) + ra.cpSrc += run.aiclr.Length; + if (run.fNeedSrcAlign) { + ra.cpArgs++; + ra.cpSrc++; + } + if (GetCountArg(ra) != -1) { + ra.cpSrc++; + ra.cpArgs++; + } + return ra; + } + + RunArgs[] MakeRunArgs(Run[] arun) { + ArrayList alsRa = new ArrayList(); + foreach (Run run in arun) + alsRa.Add(GetRunArg(run)); + return (RunArgs[])alsRa.ToArray(typeof(RunArgs)); + } + + int CompileRunArgs(ArrayList alsRa, Run[] arun) { + RunArgs[] ara = MakeRunArgs(arun); + +//temp +#if false + // Incorporate the run args into the list. Match against existing args if possible + bool fMatch = false; + int iraDst = 0; + for (; iraDst < alsRa.Count; iraDst++) { + fMatch = true; + for (int iraSrc = 0; iraSrc < ara.Length; iraSrc++) { + int iraDstT = iraSrc + iraDst; + if (iraDstT >= alsRa.Count) { + for (; iraSrc < ara.Length; iraSrc++) + alsRa.Add(ara[iraSrc]); + fMatch = true; + break; + } + RunArgs raSrc = ara[iraSrc]; + RunArgs raDst = (RunArgs)alsRa[iraDstT]; + if (raSrc.cpDst != raDst.cpDst || raSrc.op != raDst.op) { + fMatch = false; + break; + } + } + if (fMatch) + break; + } + if (!fMatch) + alsRa.AddRange(ara); + + return iraDst; +#else + int iraDst = alsRa.Count; + alsRa.AddRange(ara); + return iraDst; +#endif + } + + public void Compile(bool fSmallest) { + CompileResults cr = new CompileResults(); + cr.alsIclr = new ArrayList(); + cr.alsRa = new ArrayList(); + cr.aaiiclrEven = new int[m_alsFrames.Count, m_cy]; + cr.aairaEven = new int[m_alsFrames.Count, m_cy]; + cr.aaiiclrOdd = new int[m_alsFrames.Count, m_cy]; + cr.aairaOdd = new int[m_alsFrames.Count, m_cy]; + for (int iFrame = 0; iFrame < m_alsFrames.Count; iFrame++) { + Frame frame = (Frame)m_alsFrames[iFrame]; + for (int y = 0; y < m_cy; y++) { + cr.aaiiclrEven[iFrame, y] = CompileImageData(cr.alsIclr, (Run[])frame.alsArunsEven[y], fSmallest); + cr.aairaEven[iFrame, y] = CompileRunArgs(cr.alsRa, (Run[])frame.alsArunsEven[y]); + } + for (int y = 0; y < m_cy; y++) { + cr.aaiiclrOdd[iFrame, y] = CompileImageData(cr.alsIclr, (Run[])frame.alsArunsOdd[y], fSmallest); + cr.aairaOdd[iFrame, y] = CompileRunArgs(cr.alsRa, (Run[])frame.alsArunsOdd[y]); + } + } + m_crLast = cr; + } + + byte[] Serialize() { + + // word type; + //struct TBitmapSRHeader { + // word cx; + // word cy; + // word cra; + // word ibra; + // word cFrames; + // Frame aframe[1]; + //}; + + if (m_crLast == null) + throw new Exception("Compile image first!"); + CompileResults cr = m_crLast; + int ibSrStart = 2 + 2 + 2 + 2 + 2 + 4 * m_alsFrames.Count; + int cbSr = m_alsFrames.Count * (m_cy * 4 + m_cy * 4); + int ibRaStart = ibSrStart + cbSr; + int cbRa = cr.alsRa.Count * 4; + int ibDataStart = ibRaStart + cbRa; + + // Write header info + BinaryWriter bwtr = new BinaryWriter(new MemoryStream()); + bwtr.Write(Misc.SwapUShort((ushort)TbmType.SkipRun)); // not counted in offsets + bwtr.Write(Misc.SwapUShort((ushort)m_cx)); + bwtr.Write(Misc.SwapUShort((ushort)m_cy)); + bwtr.Write(Misc.SwapUShort((ushort)cr.alsRa.Count)); + bwtr.Write(Misc.SwapUShort((ushort)ibRaStart)); + bwtr.Write(Misc.SwapUShort((ushort)m_alsFrames.Count)); + + // Write isrEven, isrOdd for each frame + int isr = 0; + foreach (Frame frame in m_alsFrames) { + bwtr.Write(Misc.SwapUShort((ushort)(isr + ibSrStart))); + isr += frame.alsArunsEven.Count * 4; + bwtr.Write(Misc.SwapUShort((ushort)(isr + ibSrStart))); + isr += frame.alsArunsOdd.Count * 4; + } + + // Write sr's (ScanRecords) for each frame + for (int iFrame = 0; iFrame < m_alsFrames.Count; iFrame++) { + Frame frame = (Frame)m_alsFrames[iFrame]; + for (int y = 0; y < m_cy; y++) { + long pos = bwtr.BaseStream.Position - 2; + bwtr.Write(Misc.SwapUShort((ushort)(cr.aaiiclrEven[iFrame, y] + ibDataStart - pos))); + bwtr.Write(Misc.SwapUShort((ushort)(cr.aairaEven[iFrame, y] * 4))); + } + for (int y = 0; y < m_cy; y++) { + long pos = bwtr.BaseStream.Position - 2; + bwtr.Write(Misc.SwapUShort((ushort)(cr.aaiiclrOdd[iFrame, y] + ibDataStart - pos))); + bwtr.Write(Misc.SwapUShort((ushort)(cr.aairaOdd[iFrame, y] * 4))); + } + } + + // Write Ras + foreach (RunArgs ra in cr.alsRa) { + bwtr.Write((byte)ra.op); + bwtr.Write((byte)ra.cpSrc); + bwtr.Write((byte)ra.cpArgs); + bwtr.Write((byte)ra.cpDst); + } + + // Write iclrData + foreach (int iclr in cr.alsIclr) + bwtr.Write((byte)iclr); + + // Done + byte[] ab = new Byte[bwtr.BaseStream.Length]; + bwtr.BaseStream.Seek(0, SeekOrigin.Begin); + bwtr.BaseStream.Read(ab, 0, ab.Length); + bwtr.Close(); + return ab; + } + +#if false + // From runs version + public Bitmap CreateBitmap(int iFrame, bool fEven, bool fAsRuns) { + Bitmap bm = new Bitmap(m_cx, m_cy); + Graphics gMem = Graphics.FromImage(bm); + gMem.Clear(Color.FromArgb(0, 255, 255)); + + Frame frame = (Frame)m_alsFrames[iFrame]; + ArrayList alsArun = fEven ? frame.alsArunsEven : frame.alsArunsOdd; + + int y = 0; + int nColor = 0; + foreach(Run[] arun in alsArun) { + int x = 0; + foreach (Run run in arun) { + // Next run + if (run.ct == ColorType.EndScan) { + y++; + break; + } + + // Transparency + if (run.ct == ColorType.Transparent) { + for (int xT = x; xT < x + run.cp; xT++) + bm.SetPixel(xT, y, Color.FromArgb(0, 255, 255)); + x += run.cp; + continue; + } + + // Colorize + if (fAsRuns) { + nColor ^= 1; + Color clr = nColor != 0 ? Color.FromArgb(255, 0, 0) : Color.FromArgb(0, 0, 255); + for (int xT = x; xT < x + run.cp; xT++) + bm.SetPixel(xT, y, clr); + x += run.cp; + } else { + switch (run.ct) { + case ColorType.Data: + case ColorType.Side: + for (int xT = x; xT < x + run.cp; xT++) + bm.SetPixel(xT, y, m_pal[run.aiclr[xT - x]]); + x += run.cp; + break; + + case ColorType.Shadow: + for (int xT = x; xT < x + run.cp; xT++) + bm.SetPixel(xT, y, Color.FromArgb(255, 255, 0)); // Color.FromArgb(156, 212, 248)); + x += run.cp; + break; + } + } + } + } + + gMem.Dispose(); + return bm; + } +#else + // From compiled version + public Bitmap CreateBitmap(int iFrame, bool fEven, bool fAsRuns) { + if (m_crLast == null) + throw new Exception("Compile image first!"); + CompileResults cr = m_crLast; + + Bitmap bm = new Bitmap(m_cx, m_cy); + Graphics gMem = Graphics.FromImage(bm); + gMem.Clear(Color.FromArgb(0, 255, 255)); + + Frame frame = (Frame)m_alsFrames[iFrame]; + int[,] aaiiclr = fEven ? cr.aaiiclrEven : cr.aaiiclrOdd; + int[,] aaira = fEven ? cr.aairaEven : cr.aairaOdd; + + int xOrigin = fEven ? 0 : 1; + int nColor = 0; + for (int y = 0; y < m_cy; y++) { + int x = 0; + int iiclr = aaiiclr[iFrame, y]; + int irun = aaira[iFrame, y]; + while (true) { + RunArgs ra = (RunArgs)cr.alsRa[irun++]; + if (ra.op == Op.EndScan) + break; + + // Colorize + if (fAsRuns) { + nColor ^= 1; + Color clr = nColor != 0 ? Color.FromArgb(255, 0, 0) : Color.FromArgb(0, 0, 255); + for (int xT = x; xT < x + ra.cpDst; xT++) + bm.SetPixel(xT, y, clr); + x += ra.cpDst; + } else { + // Transparency + if (ra.op == Op.TransparentN) { + Debug.Assert((int)cr.alsIclr[iiclr++] == ra.cpDst); + for (int xT = x; xT < x + ra.cpDst; xT++) + bm.SetPixel(xT, y, Color.FromArgb(0, 255, 255)); + x += ra.cpDst; + continue; + } + if (ra.op >= Op.EvenDataStart && ra.op <= Op.EvenDataEnd) { + Debug.Assert(((int)ra.op - (int)Op.EvenDataStart) / 2 + 1 == ra.cpDst); + if (((int)ra.op & 1) != 0) + iiclr++; + Debug.Assert(((x + xOrigin) & 1) == 0 && (iiclr & 1) == 0); + for (int xT = x; xT < x + ra.cpDst; xT++) + bm.SetPixel(xT, y, m_pal[(int)cr.alsIclr[iiclr++]]); + x += ra.cpDst; + continue; + } + if (ra.op >= Op.OddDataStart && ra.op <= Op.OddDataEnd) { + Debug.Assert(((int)ra.op - (int)Op.OddDataStart) / 2 + 1 == ra.cpDst); + if (((int)ra.op & 1) != 0) + iiclr++; + Debug.Assert(((x + xOrigin) & 1) != 0 && (iiclr & 1) != 0); + for (int xT = x; xT < x + ra.cpDst; xT++) + bm.SetPixel(xT, y, m_pal[(int)cr.alsIclr[iiclr++]]); + x += ra.cpDst; + continue; + } + if (ra.op >= Op.EvenDataNStart && ra.op <= Op.OddDataNEnd) { + if (((int)ra.op & 1) != 0) + iiclr++; + iiclr++; + Debug.Assert(((x + xOrigin) & 1) == (iiclr & 1)); + for (int xT = x; xT < x + ra.cpDst; xT++) + bm.SetPixel(xT, y, m_pal[(int)cr.alsIclr[iiclr++]]); + x += ra.cpDst; + continue; + } + if (ra.op >= Op.SideStart && ra.op <= Op.SideEnd) { + Debug.Assert((int)ra.op - (int)Op.Side1 + 1 == ra.cpDst); + for (int xT = x; xT < x + ra.cpDst; xT++) + bm.SetPixel(xT, y, m_pal[(int)cr.alsIclr[iiclr++]]); + x += ra.cpDst; + continue; + } + if (ra.op == Op.SideN) { + Debug.Assert((int)cr.alsIclr[iiclr++] == ra.cpDst); + for (int xT = x; xT < x + ra.cpDst; xT++) + bm.SetPixel(xT, y, m_pal[(int)cr.alsIclr[iiclr++]]); + x += ra.cpDst; + continue; + } + if (ra.op >= Op.ShadowStart && ra.op <= Op.ShadowEnd) { + Debug.Assert((int)ra.op - (int)Op.Shadow1 + 1 == ra.cpDst); + for (int xT = x; xT < x + ra.cpDst; xT++) + bm.SetPixel(xT, y, Color.FromArgb(255, 255, 0)); + x += ra.cpDst; + continue; + } + if (ra.op == Op.ShadowN) { + for (int xT = x; xT < x + ra.cpDst; xT++) + bm.SetPixel(xT, y, Color.FromArgb(255, 255, 0)); + x += ra.cpDst; + continue; + } + Debug.Assert(false); + } + } + } + + gMem.Dispose(); + return bm; + } +#endif + + public void PrintStats() { + if (m_crLast == null) + throw new Exception("Compile image first!"); + CompileResults cr = m_crLast; + + int cRuns = 0; + int cSkips = 0; + int cRunsInScan = 0; + int cScans = 0; + foreach (Frame frame in m_alsFrames) { + foreach (Run[] arun in frame.alsArunsEven) { + cScans++; + foreach (Run run in arun) { + cRunsInScan++; + cRuns++; + if (run.ct == ColorType.Transparent) + cSkips++; + } + } + foreach (Run[] arun in frame.alsArunsOdd) { + cScans++; + foreach (Run run in arun) { + cRunsInScan++; + cRuns++; + if (run.ct == ColorType.Transparent) + cSkips++; + } + } + } + + Console.WriteLine("Runs: " + cRuns); + Console.WriteLine("Skips: " + cSkips); + Console.WriteLine("Avg runs per image: " + (float)cRuns / 2.0 / (float)m_alsFrames.Count); + Console.WriteLine("Avg runs per scan: " + (float)cRunsInScan / (float)cScans); + + Console.WriteLine("RunArgs size: " + cr.alsRa.Count * 4); + Console.WriteLine("RunData size: " + cr.alsIclr.Count); + Console.WriteLine("SR size: " + m_cy * 4 * m_alsFrames.Count * 2); + } + + public void Save(string strFile) { + if (m_crLast == null) + throw new Exception("Compile image first!"); + BinaryWriter bwtr = new BinaryWriter(new FileStream(strFile, FileMode.Create, FileAccess.Write)); + bwtr.Write(Serialize()); + bwtr.Close(); + } + + public static void EmitOpEnum() { + TextWriter tw = Console.Out; + tw.WriteLine("enum Op {"); + int iop = 0; + int iopEvenDataStart = iop; + for (int cp = 1; cp <= s_cpDataOpFixedMax; cp++) { + tw.WriteLine("\tEvenData" + cp + ", // " + iop++); + tw.WriteLine("\tEvenData" + cp + "_Inc, // " + iop++); + } + int iopEvenDataEnd = iop - 1; + int iopOddDataStart = iop; + for (int cp = 1; cp <= s_cpDataOpFixedMax; cp++) { + tw.WriteLine("\tOddData" + cp + ", // " + iop++); + tw.WriteLine("\tOddData" + cp + "_Inc, // " + iop++); + } + int iopOddDataEnd = iop - 1; + int iopSideStart = iop; + for (int cp = 1; cp <= s_cpSideOpFixedMax; cp++) { + tw.WriteLine("\tSide" + cp + ", // " + iop++); + //tw.WriteLine("\tSide" + cp + "_Inc, // " + iop++); + } + int iopSideEnd = iop - 1; + int iopShadowStart = iop; + for (int cp = 1; cp <= s_cpShadowOpFixedMax; cp++) { + tw.WriteLine("\tShadow" + cp + ", // " + iop++); + //tw.WriteLine("\tShadow" + cp + "_Inc, // " + iop++); + } + int iopShadowEnd = iop - 1; + + tw.WriteLine("\tEvenDataLB, // " + iop++); + tw.WriteLine("\tEvenDataLB_Inc, // " + iop++); + tw.WriteLine("\tEvenDataLW, // " + iop++); + tw.WriteLine("\tEvenDataLW_Inc, // " + iop++); + tw.WriteLine("\tEvenDataLWB, // " + iop++); + tw.WriteLine("\tEvenDataLWB_Inc, // " + iop++); + tw.WriteLine("\tEvenDataL, // " + iop++); + tw.WriteLine("\tEvenDataL_Inc, // " + iop++); + tw.WriteLine("\tOddDataLB, // " + iop++); + tw.WriteLine("\tOddDataLB_Inc, // " + iop++); + tw.WriteLine("\tOddDataLW, // " + iop++); + tw.WriteLine("\tOddDataLW_Inc, // " + iop++); + tw.WriteLine("\tOddDataLWB, // " + iop++); + tw.WriteLine("\tOddDataLWB_Inc, // " + iop++); + tw.WriteLine("\tOddDataL, // " + iop++); + tw.WriteLine("\tOddDataL_Inc, // " + iop++); + tw.WriteLine("\tSideN, // " + iop++); + //tw.WriteLine("\tSideN_Inc, // " + iop++); + tw.WriteLine("\tShadowN, // " + iop++); + //tw.WriteLine("\tShadowN_Inc, // " + iop++); + tw.WriteLine("\tTransparentN, // " + iop++); + //tw.WriteLine("\tTransparentN_Inc, // " + iop++); + tw.WriteLine("\tEndScan, // " + iop++); + tw.WriteLine("\tError, // " + iop++); + + tw.WriteLine("\tEvenDataStart = " + iopEvenDataStart + ","); + tw.WriteLine("\tEvenDataEnd = " + iopEvenDataEnd + ","); + tw.WriteLine("\tOddDataStart = " + iopOddDataStart + ","); + tw.WriteLine("\tOddDataEnd = " + iopOddDataEnd + ","); + tw.WriteLine("\tSideStart = " + iopSideStart + ","); + tw.WriteLine("\tSideEnd = " + iopSideEnd + ","); + tw.WriteLine("\tShadowStart = " + iopShadowStart + ","); + tw.WriteLine("\tShadowEnd = " + iopShadowEnd); + + tw.WriteLine("};"); + tw.Close(); + } + + public static void EmitHandlers() { + //TextWriter tw = new StreamWriter(new MemoryStream()); + TextWriter tw = Console.Out; + StringCollection strc = new StringCollection(); + + //tw.WriteLine(".text"); + tw.WriteLine(""); + + // Fixed data handlers + for (int cp = 1; cp <= s_cpDataOpFixedMax; cp++) + EmitDataHandler(tw, cp, Align.Even, false, strc); + for (int cp = 1; cp <= s_cpDataOpFixedMax; cp++) + EmitDataHandler(tw, cp, Align.Odd, false, strc); + + // Clipped fixed data handlers + for (int cp = 1; cp <= s_cpDataOpFixedMax; cp++) + EmitDataHandler(tw, cp, Align.Even, true, strc); + for (int cp = 1; cp <= s_cpDataOpFixedMax; cp++) + EmitDataHandler(tw, cp, Align.Odd, true, strc); + + // Fixed side handlers + for (int cp = 1; cp <= s_cpSideOpFixedMax; cp++) + EmitSideHandler(tw, cp, strc); + // Fixed shadow handlers + for (int cp = 1; cp <= s_cpShadowOpFixedMax; cp++) + EmitShadowHandler(tw, cp, strc); + + // Non-fixed data handlers + EmitDataNHandler(tw, "EvenDataLB", Align.Even, 63 * 4 + 1, strc); + EmitDataNHandler(tw, "EvenDataLW", Align.Even, 63 * 4 + 2, strc); + EmitDataNHandler(tw, "EvenDataLWB", Align.Even, 63 * 4 + 2 + 1, strc); + EmitDataNHandler(tw, "EvenDataL", Align.Even, 64 * 4, strc); + EmitDataNHandler(tw, "OddDataLB", Align.Odd, 1 + 63 * 4 + 1, strc); + EmitDataNHandler(tw, "OddDataLW", Align.Odd, 1 + 63 * 4 + 2, strc); + EmitDataNHandler(tw, "OddDataLWB", Align.Odd, 1 + 63 * 4 + 2 + 1, strc); + EmitDataNHandler(tw, "OddDataL", Align.Odd, 1 + 63 * 4, strc); + + // Table + strc.Add("SideN"); + strc.Add("ShadowN"); + strc.Add("TransparentN"); + strc.Add("EndScan"); + tw.WriteLine(".data"); + tw.WriteLine(".even"); + tw.WriteLine(".globl gapfnRunOps"); + tw.WriteLine("gapfnRunOps:"); + for (int i = 0; i < strc.Count; i++) { + string str = ".long " + strc[i]; + str = str.PadRight(40) + "| " + i + " - " + ((Op)i).ToString(); + tw.WriteLine(str); + } + tw.WriteLine(""); + + // Clip tables + EmitLeftClipTables(tw); + EmitRightClipTables(tw); + + tw.Close(); + } + + static OpType GetOpType(Op op) { + if (op >= Op.EvenDataStart && op <= Op.EvenDataEnd) + return OpType.Data; + if (op >= Op.OddDataStart && op <= Op.OddDataEnd) + return OpType.Data; + if (op >= Op.EvenDataNStart && op <= Op.EvenDataNEnd) + return OpType.Data; + if (op >= Op.OddDataNStart && op <= Op.OddDataNEnd) + return OpType.Data; + if (op >= Op.SideStart && op <= Op.SideEnd) + return OpType.Side; + if (op == Op.SideN) + return OpType.Side; + if (op >= Op.ShadowStart && op <= Op.ShadowEnd) + return OpType.Shadow; + if (op == Op.ShadowN) + return OpType.Shadow; + if (op == Op.TransparentN) + return OpType.Transparent; + Debug.Assert(false); + return OpType.None; + } + + static Align GetOpRightAlignment(Op op) { + // If it's not data, we don't know the alignment + if (GetOpType(op) != OpType.Data) + return Align.None; + + if (op >= Op.EvenDataStart && op <= Op.EvenDataEnd) { + int cp = (int)(op - (int)Op.EvenDataStart) / 2 + 1; + return (cp & 1) != 0 ? Align.Odd : Align.Even; + } + + if (op >= Op.OddDataStart && op <= Op.OddDataEnd) { + int cp = (int)(op - (int)Op.OddDataStart) / 2 + 1; + return (cp & 1) != 0 ? Align.Even : Align.Odd; + } + + switch (op) { + case Op.EvenDataLB: + case Op.EvenDataLB_Inc: + return Align.Odd; + + case Op.EvenDataLW: + case Op.EvenDataLW_Inc: + return Align.Even; + + case Op.EvenDataLWB: + case Op.EvenDataLWB_Inc: + return Align.Odd; + + case Op.EvenDataL: + case Op.EvenDataL_Inc: + return Align.Even; + + case Op.OddDataLB: + case Op.OddDataLB_Inc: + return Align.Odd; + + case Op.OddDataLW: + case Op.OddDataLW_Inc: + return Align.Even; + + case Op.OddDataLWB: + case Op.OddDataLWB_Inc: + return Align.Odd; + + case Op.OddDataL: + case Op.OddDataL_Inc: + return Align.Even; + } + + Debug.Assert(false); + return Align.None; + } + + static Align GetOpLeftAlignment(Op op) { + // If it's not data, we don't know the alignment + if (GetOpType(op) != OpType.Data) + return Align.None; + + if (op >= Op.EvenDataStart && op <= Op.EvenDataEnd) + return Align.Even; + + if (op >= Op.OddDataStart && op <= Op.OddDataEnd) + return Align.Odd; + + switch (op) { + case Op.EvenDataLB: + case Op.EvenDataLB_Inc: + return Align.Even; + + case Op.EvenDataLW: + case Op.EvenDataLW_Inc: + return Align.Even; + + case Op.EvenDataLWB: + case Op.EvenDataLWB_Inc: + return Align.Even; + + case Op.EvenDataL: + case Op.EvenDataL_Inc: + return Align.Even; + + case Op.OddDataLB: + case Op.OddDataLB_Inc: + return Align.Odd; + + case Op.OddDataLW: + case Op.OddDataLW_Inc: + return Align.Odd; + + case Op.OddDataLWB: + case Op.OddDataLWB_Inc: + return Align.Odd; + + case Op.OddDataL: + case Op.OddDataL_Inc: + return Align.Odd; + } + + Debug.Assert(false); + return Align.None; + } + + static void EmitLeftClipTables(TextWriter tw) { + // Emit gmpopapfnLeftClip + tw.WriteLine(".globl gmpopapfnLeftClip"); + tw.WriteLine("gmpopapfnLeftClip:"); + for (Op op = Op.EvenData1; op < Op.EndScan; op++) { + string strT = null; + switch (GetOpType(op)) { + case OpType.Data: + if (GetOpRightAlignment(op) == Align.Even) { + strT = ".long gmpcpRightpfnREEData"; + } else { + strT = ".long gmpcpRightpfnREOData"; + } + break; + + case OpType.Side: + strT = ".long gmpcppfnSide"; + break; + + case OpType.Shadow: + strT = ".long gmpcppfnShadow"; + break; + + case OpType.Transparent: + strT = ".long gmpcppfnTransparent"; + break; + + default: + Debug.Assert(false); + break; + } + if (strT != null) + tw.WriteLine(strT.PadRight(40) + "| " + (int)op + " - " + op.ToString()); + } + tw.WriteLine(""); + + // gmpcpRightpfnREEData (REE == Right Edge Even) + tw.WriteLine("gmpcpRightpfnREEData:"); + for (int cpRight = 0; cpRight < 256; cpRight++) { + if (cpRight == 0) { + tw.WriteLine(".long 0"); + continue; + } + Align alignLeftEdge = (cpRight & 1) != 0 ? Align.Odd : Align.Even; + switch (alignLeftEdge) { + case Align.Even: + if (cpRight <= s_cpDataOpFixedMax) { + tw.WriteLine(".long EvenData" + cpRight + "_Clip"); + } else { + tw.WriteLine(".long EvenDataN_Clip"); + } + break; + + case Align.Odd: + if (cpRight <= s_cpDataOpFixedMax) { + tw.WriteLine(".long OddData" + cpRight + "_Clip"); + } else { + tw.WriteLine(".long OddDataN_Clip"); + } + break; + } + } + tw.WriteLine(""); + + // gmpcpRightpfnREOData (REO == Right Edge Odd) + tw.WriteLine("gmpcpRightpfnREOData:"); + for (int cpRight = 0; cpRight < 256; cpRight++) { + if (cpRight == 0) { + tw.WriteLine(".long 0"); + continue; + } + Align alignLeftEdge = (cpRight & 1) != 0 ? Align.Even : Align.Odd; + switch (alignLeftEdge) { + case Align.Even: + if (cpRight <= s_cpDataOpFixedMax) { + tw.WriteLine(".long EvenData" + cpRight + "_Clip"); + } else { + tw.WriteLine(".long EvenDataN_Clip"); + } + break; + + case Align.Odd: + if (cpRight <= s_cpDataOpFixedMax) { + tw.WriteLine(".long OddData" + cpRight + "_Clip"); + } else { + tw.WriteLine(".long OddDataN_Clip"); + } + break; + } + } + tw.WriteLine(""); + + // gmpcppfnSide + tw.WriteLine("gmpcppfnSide:"); + for (int cpRight = 0; cpRight < 256; cpRight++) { + if (cpRight == 0) { + tw.WriteLine(".long 0"); + continue; + } + if (cpRight <= s_cpSideOpFixedMax) { + tw.WriteLine(".long Side" + cpRight + "_Clip"); + } else { + tw.WriteLine(".long SideN_Clip"); + } + } + tw.WriteLine(""); + + // gmpcppfnShadow + tw.WriteLine("gmpcppfnShadow:"); + for (int cpRight = 0; cpRight < 256; cpRight++) { + if (cpRight == 0) { + tw.WriteLine(".long 0"); + continue; + } + if (cpRight <= s_cpShadowOpFixedMax) { + tw.WriteLine(".long Shadow" + cpRight); + } else { + tw.WriteLine(".long ShadowN_Clip"); + } + } + tw.WriteLine(""); + + //gmpcppfnTransparent + tw.WriteLine("gmpcppfnTransparent:"); + for (int cpRight = 0; cpRight < 256; cpRight++) { + if (cpRight == 0) { + tw.WriteLine(".long 0"); + continue; + } + tw.WriteLine(".long TransparentN_Clip"); + } + tw.WriteLine(""); + } + + static void EmitRightClipTables(TextWriter tw) { + // Emit gmpopapfnRightClip + tw.WriteLine(".globl gmpopapfnRightClip"); + tw.WriteLine("gmpopapfnRightClip:"); + for (Op op = Op.EvenData1; op < Op.EndScan; op++) { + string strT = null; + switch (GetOpType(op)) { + case OpType.Data: + if (GetOpLeftAlignment(op) == Align.Even) { + strT = ".long gmpcpLeftpfnLEEData"; + } else { + strT = ".long gmpcpLeftpfnLEOData"; + } + break; + + case OpType.Side: + strT = ".long gmpcppfnSide"; + break; + + case OpType.Shadow: + strT = ".long gmpcppfnShadow"; + break; + + case OpType.Transparent: + strT = ".long gmpcppfnTransparent"; + break; + + default: + Debug.Assert(false); + break; + } + if (strT != null) + tw.WriteLine(strT.PadRight(40) + "| " + (int)op + " - " + op.ToString()); + } + tw.WriteLine(""); + + // gmpcpLeftpfnLEEData (LEE == Left Edge Even) + tw.WriteLine("gmpcpLeftpfnLEEData:"); + for (int cpLeft = 0; cpLeft < 256; cpLeft++) { + if (cpLeft == 0) { + tw.WriteLine(".long 0"); + continue; + } + if (cpLeft <= s_cpDataOpFixedMax) { + tw.WriteLine(".long EvenData" + cpLeft + "_Clip"); + } else { + tw.WriteLine(".long EvenDataN_Clip"); + } + } + tw.WriteLine(""); + + // gmpcpLeftpfnLEOData (LEO == Left Edge Odd) + tw.WriteLine("gmpcpLeftpfnLEOData:"); + for (int cpLeft = 0; cpLeft < 256; cpLeft++) { + if (cpLeft == 0) { + tw.WriteLine(".long 0"); + continue; + } + if (cpLeft <= s_cpDataOpFixedMax) { + tw.WriteLine(".long OddData" + cpLeft + "_Clip"); + } else { + tw.WriteLine(".long OddDataN_Clip"); + } + } + tw.WriteLine(""); + } + + static void EmitDataNHandler(TextWriter tw, string strLabel, Align align, int cp, StringCollection strc) { + strc.Add(strLabel); + strc.Add(strLabel + "_Inc"); + tw.WriteLine(strLabel + "_Inc:"); + tw.WriteLine("\taddq.w #1,%a0"); + tw.WriteLine(strLabel + ":"); + tw.WriteLine("\tmove.b (%a0)+,%d0"); + if (align == Align.Odd) { + tw.WriteLine("\tmove.b (%a0)+,(%a1)+"); + cp--; + align = Align.Even; + } + tw.WriteLine("\tjmp 2(%pc,%d0.w)"); + EmitAlignedDataCopy(tw, cp, align); + tw.WriteLine("\tmove.l (%a2)+,%a3"); + tw.WriteLine("\tjmp (%a3)"); + tw.WriteLine(""); + } + + static void EmitShadowHandler(TextWriter tw, int cp, StringCollection strc) { + string strLabel = "Shadow" + cp; + strc.Add(strLabel); + tw.WriteLine(strLabel + ":"); + while (cp-- != 0) { + tw.WriteLine("\tmove.b (%a1),%d0"); + tw.WriteLine("\tmove.b (%a5,%d0.w),(%a1)+"); + } + tw.WriteLine("\tmove.l (%a2)+,%a3"); + tw.WriteLine("\tjmp (%a3)"); + tw.WriteLine(""); + } + + static void EmitSideHandler(TextWriter tw, int cp, StringCollection strc) { + string strLabel = "Side" + cp; + strc.Add(strLabel); + + tw.WriteLine(strLabel + "_Clip:"); + tw.WriteLine("\tadd.w %d2,%a0"); + tw.WriteLine(strLabel + ":"); + while (cp-- != 0) { + tw.WriteLine("\tmove.b (%a0)+,%d0"); + tw.WriteLine("\tmove.b (%a4,%d0.w),(%a1)+"); + } + tw.WriteLine("\tmove.l (%a2)+,%a3"); + tw.WriteLine("\tjmp (%a3)"); + tw.WriteLine(""); + } + + static void EmitDataHandler(TextWriter tw, int cp, Align align, bool fClip, StringCollection strc) { + string strLabel = (align == Align.Even ? "EvenData" : "OddData") + cp; + if (!fClip) { + string strLabelInc = strLabel + "_Inc"; + strc.Add(strLabel); + strc.Add(strLabelInc); + tw.WriteLine(strLabelInc + ":"); + tw.WriteLine("\taddq.w #1,%a0"); + tw.WriteLine(strLabel + ":"); + } else { + tw.WriteLine(strLabel + "_Clip:"); + tw.WriteLine("\tadd.w %d2,%a0"); + } + EmitAlignedDataCopy(tw, cp, align); + tw.WriteLine("\tmove.l (%a2)+,%a3"); + tw.WriteLine("\tjmp (%a3)"); + tw.WriteLine(""); + } + + static void EmitAlignedDataCopy(TextWriter tw, int cp, Align align) { + Align alignWrite = align; + while (cp != 0) { + if (alignWrite == Align.Odd) { + tw.WriteLine("\tmove.b (%a0)+,(%a1)+"); + cp--; + alignWrite = Align.Even; + continue; + } + switch (cp) { + case 1: + tw.WriteLine("\tmove.b (%a0)+,(%a1)+"); + alignWrite = Align.Odd; + cp -= 1; + Debug.Assert(cp == 0); + break; + + case 2: + tw.WriteLine("\tmove.w (%a0)+,(%a1)+"); + cp -= 2; + break; + + case 3: + tw.WriteLine("\tmove.w (%a0)+,(%a1)+"); + tw.WriteLine("\tmove.b (%a0)+,(%a1)+"); + alignWrite = Align.Odd; + cp -= 3; + Debug.Assert(cp == 0); + break; + + case 4: + tw.WriteLine("\tmove.l (%a0)+,(%a1)+"); + cp -= 4; + break; + + default: + tw.WriteLine("\tmove.l (%a0)+,(%a1)+"); + cp -= 4; + break; + } + } + } + + // Emit code to draw the image. This is for data collection purposes at the moment + + public void EmitCode(string str, Align align) { + TextWriter tw = Console.Out; + tw.WriteLine(".section code4,\"x\""); + tw.WriteLine(".even"); + tw.WriteLine(".globl " + str); + tw.WriteLine(str + ":"); + tw.WriteLine("\tlea " + str + "_data(%pc),%a0"); + tw.WriteLine("\tmove.w #" + (m_cy - 1) + ",%d1"); + + Frame frame = (Frame)m_alsFrames[0]; + ArrayList aalsRuns; + if (align == Align.Even) { + aalsRuns = frame.alsArunsEven; + } else { + aalsRuns = frame.alsArunsOdd; + } + + int xEmitLast = 0; + int yEmitLast = 0; + ArrayList alsIclr = new ArrayList(); + for (int y = 0; y < m_cy; y++) { + int x = 0; + Run[] arun = (Run[])aalsRuns[y]; + foreach (Run run in arun) { + if (run.ct == ColorType.EndScan) + break; + Align alignDst; + if (align == Align.Even) { + alignDst = (x & 1) != 0 ? Align.Odd : Align.Even; + } else { + alignDst = (x & 1) == 0 ? Align.Odd : Align.Even; + } + Align alignSrc = (alsIclr.Count & 1) != 0 ? Align.Odd : Align.Even; + int cp; + + // First destination stepping + switch (run.ct) { + case ColorType.Data: + case ColorType.Shadow: + case ColorType.Side: + if (y - yEmitLast > 1) { + int cyStep = y - yEmitLast; + if (cyStep <= 8) { + tw.WriteLine("\tsubq.w #" + cyStep + ",%d1"); + } else { + tw.WriteLine("\tsub.w #" + cyStep + ",%d1"); + } + tw.WriteLine("\tbpl.b 1f"); + tw.WriteLine("\trts"); + tw.WriteLine("1:"); + int cbStep = ((y - yEmitLast) - 1) * 160 + (160 - xEmitLast) + x; + tw.WriteLine("\tadd.w #" + cbStep + ",%a1"); + } else if (y - yEmitLast == 1) { + tw.WriteLine("\tdbra %d1,1f"); + tw.WriteLine("\trts"); + tw.WriteLine("1:"); + int cbStep = ((y - yEmitLast) - 1) * 160 + (160 - xEmitLast) + x; + tw.WriteLine("\tadd.w #" + cbStep + ",%a1"); + } else if (y == yEmitLast) { + int cbStep = x - xEmitLast; + if (cbStep <= 8) { + if (cbStep > 0) + tw.WriteLine("\taddq.w #" + cbStep + ",%a1"); + } else { + tw.WriteLine("\tadd.w #" + cbStep + ",%a1"); + } + } + break; + } + + // Now handle run handling code + switch (run.ct) { + case ColorType.Data: + if (alignSrc != alignDst) { + alsIclr.Add(s_iclrNotAssigned); + tw.WriteLine("\taddq.w #1,%a0"); + alignSrc = alignDst; + } + alsIclr.AddRange(run.aiclr); + EmitAlignedDataCopy(tw, run.cp, alignDst); + x += run.cp; + xEmitLast = x; + yEmitLast = y; + break; + + case ColorType.Shadow: + cp = run.cp; + while (cp-- != 0) { + tw.WriteLine("\tmove.b (%a1),%d0"); + tw.WriteLine("\tmove.b 0.b(%a5,%d0.w),(%a1)+"); + } + x += run.cp; + xEmitLast = x; + yEmitLast = y; + break; + + case ColorType.Side: + alsIclr.AddRange(run.aiclr); + cp = run.cp; + while (cp-- != 0) { + tw.WriteLine("\tmove.b (%a0)+,%d0"); + tw.WriteLine("\tmove.b 0.b(%a4,%d0.w),(%a1)+"); + } + x += run.cp; + xEmitLast = x; + yEmitLast = y; + break; + + case ColorType.Transparent: + x += run.cp; + break; + } + } + } + tw.WriteLine("\trts"); + tw.WriteLine(""); + + // Emit data: + tw.WriteLine(".even"); + tw.WriteLine(str + "_data:"); + foreach (int iclr in alsIclr) + tw.WriteLine(".byte " + iclr); + tw.Close(); + } + + public int FrameCount { + get { + return m_alsFrames.Count; + } + } + } +} diff --git a/SpiffLib/tbitmaptools.cs b/SpiffLib/tbitmaptools.cs new file mode 100644 index 0000000..c7f86e7 --- /dev/null +++ b/SpiffLib/tbitmaptools.cs @@ -0,0 +1,208 @@ +using System; +using System.IO; +using System.Collections; +using System.Collections.Specialized; +using System.Drawing; +using System.Drawing.Imaging; +using System.Text.RegularExpressions; +using System.Diagnostics; +using System.Windows.Forms; +using System.Drawing.Drawing2D; + +namespace SpiffLib { + class TBitmapTools { + public static Bitmap ScaleBitmap(Bitmap bm, double nScale, Palette palFixed) { + Color[] aclrSpecial = new Color[] { + // side colors + Color.FromArgb(0, 116, 232), + Color.FromArgb(0, 96, 196), + Color.FromArgb(0, 64, 120), + Color.FromArgb(0, 48, 92), + Color.FromArgb(0, 32, 64), + // transparent + Color.FromArgb(255, 0, 255), + // shadow + Color.FromArgb(156, 212, 248), + }; + + Bitmap bmNew = new Bitmap((int)Math.Floor(bm.Width * nScale + 0.5), (int)Math.Floor(bm.Height * nScale + 0.5)); + for (int y = 0; y < bmNew.Height; y++) { + for (int x = 0; x < bmNew.Width; x++) { + double nWidthRatio = (double)bm.Width / (double)bmNew.Width; + double xLeft = (double)x * nWidthRatio; + double nHeightRatio = (double)bm.Height / (double)bmNew.Height; + double yTop = (double)y * nHeightRatio; + double xRight = xLeft + 1.0 * nWidthRatio; + if (xRight > bm.Width) + xRight = bm.Width; + double yBottom = yTop + 1.0 * nHeightRatio; + if (yBottom > bm.Height) + yBottom = bm.Height; + DoubleRect drc = new DoubleRect(xLeft, yTop, xRight, yBottom); + Color clrSample = SampleGobBitmap(bm, drc, aclrSpecial, 4); + if (palFixed != null) { + bool fSpecial = false; + foreach (Color clrSpecial in aclrSpecial) { + if (clrSample == clrSpecial) { + fSpecial = true; + break; + } + } + if (!fSpecial) + clrSample = palFixed[palFixed.FindClosestEntry(clrSample)]; + } + + bmNew.SetPixel(x, y, clrSample); + } + } + + return bmNew; + } + + static Color SampleGobBitmap(Bitmap bm, DoubleRect drc, Color[] aclrSpecial, int iclrSideLast) { + int r = 0; + int g = 0; + int b = 0; + + // First figure out amount of special color + + double nAreaSide = 0.0; + double nAreaSpecial = 0.0; + double nAreaTotal = drc.Width * drc.Height; + double[] anAreaColorSpecial = new double[aclrSpecial.Length]; + for (int y = (int)Math.Floor(drc.top); y < (int)Math.Ceiling(drc.bottom); y++) { + for (int x = (int)Math.Floor(drc.left); x < (int)Math.Ceiling(drc.right); x++) { + // Calc the area taken by this pixel fragment + + DoubleRect drcPixel = new DoubleRect(x, y, x + 1.0, y + 1.0); + drcPixel.Intersect(drc); + double nAreaPixel = drcPixel.Width * drcPixel.Height; + + // Is this is a special color? Remember which and area taken + + Color clr = bm.GetPixel(x, y); + for (int iclr = 0; iclr < aclrSpecial.Length; iclr++) { + if (clr == aclrSpecial[iclr]) { + anAreaColorSpecial[iclr] += nAreaPixel; + nAreaSpecial += nAreaPixel; + if (iclr <= iclrSideLast) + nAreaSide += nAreaPixel; + break; + } + } + } + } + + // If percent of special color area is over a given threshold, return a special color + + if (nAreaSpecial / nAreaTotal >= 0.5) { + // Which was most popular? + + double nAreaMax = -1.0; + int iclrMax = -1; + for (int iclr = 0; iclr < aclrSpecial.Length; iclr++) { + if (anAreaColorSpecial[iclr] > nAreaMax) { + nAreaMax = anAreaColorSpecial[iclr]; + iclrMax = iclr; + } + } + + // If not a side color, return it + + if (iclrMax > iclrSideLast && nAreaMax > nAreaSide) + return aclrSpecial[iclrMax]; + + // Otherwise blend and color match side colors. + + nAreaTotal = nAreaSide; + for (int y = (int)Math.Floor(drc.top); y < (int)Math.Ceiling(drc.bottom); y++) { + for (int x = (int)Math.Floor(drc.left); x < (int)Math.Ceiling(drc.right); x++) { + // Is this is a special color? Remember which and area taken + + bool fSideColor = false; + Color clr = bm.GetPixel(x, y); + for (int iclr = 0; iclr < aclrSpecial.Length; iclr++) { + if (clr == aclrSpecial[iclr]) { + if (iclr <= iclrSideLast) + fSideColor = true; + break; + } + } + if (!fSideColor) + continue; + + // Calc the % of whole taken by this pixel fragment + + DoubleRect drcPixel = new DoubleRect(x, y, x + 1.0, y + 1.0); + drcPixel.Intersect(drc); + double nAreaPixel = drcPixel.Width * drcPixel.Height; + double nPercentPixel = nAreaPixel / nAreaTotal; + + // Add in the color components + + r += (int)(clr.R * nPercentPixel); + g += (int)(clr.G * nPercentPixel); + b += (int)(clr.B * nPercentPixel); + } + } + + // Now color match to the closest side color + + int dMax = int.MaxValue; + int iclrClosest = -1; + for (int iclr = 0; iclr <= iclrSideLast; iclr++) { + Color clrSide = aclrSpecial[iclr]; + int dr = r - clrSide.R; + int dg = g - clrSide.G; + int db = b - clrSide.B; + int d = dr * dr + dg * dg + db * db; + if (d < dMax) { + dMax = d; + iclrClosest = iclr; + } + } + + // Have our color... + + return aclrSpecial[iclrClosest]; + } + + // Otherwise add in the color components of each non-special color pixel fragment. + // Subtract the special color area from the total area since we won't be + // including it. + + nAreaTotal -= nAreaSpecial; + for (int y = (int)Math.Floor(drc.top); y < (int)Math.Ceiling(drc.bottom); y++) { + for (int x = (int)Math.Floor(drc.left); x < (int)Math.Ceiling(drc.right); x++) { + // Is this is a special color? If so ignore it + + bool fSpecial = false; + Color clr = bm.GetPixel(x, y); + for (int iclr = 0; iclr < aclrSpecial.Length; iclr++) { + if (clr == aclrSpecial[iclr]) { + fSpecial = true; + break; + } + } + if (fSpecial) + continue; + + // Calc the % of whole taken by this pixel fragment + + DoubleRect drcPixel = new DoubleRect(x, y, x + 1.0, y + 1.0); + drcPixel.Intersect(drc); + double nAreaPixel = drcPixel.Width * drcPixel.Height; + double nPercentPixel = nAreaPixel / nAreaTotal; + + // Add in the color components + + r += (int)(clr.R * nPercentPixel); + g += (int)(clr.G * nPercentPixel); + b += (int)(clr.B * nPercentPixel); + } + } + + return Color.FromArgb(r, g, b); + } + } +} diff --git a/StringTable/App.ico b/StringTable/App.ico new file mode 100644 index 0000000..3a5525f Binary files /dev/null and b/StringTable/App.ico differ diff --git a/StringTable/AssemblyInfo.cs b/StringTable/AssemblyInfo.cs new file mode 100644 index 0000000..9f89a32 --- /dev/null +++ b/StringTable/AssemblyInfo.cs @@ -0,0 +1,58 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.*")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] diff --git a/StringTable/Class1.cs b/StringTable/Class1.cs new file mode 100644 index 0000000..d93fbbe --- /dev/null +++ b/StringTable/Class1.cs @@ -0,0 +1,115 @@ +using System; +using System.IO; +using System.Collections; +using System.Text.RegularExpressions; + +namespace StringTable +{ + /// + /// Summary description for Class1. + /// + class Class1 + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main(string[] args) + { + // + // TODO: Add code to start application here + // + + string sFileNameIn = null; + string sFileNameOut = null; + + if (args.Length != 2) { + Console.WriteLine("StringTable "); + } else { + sFileNameIn = args[0]; + sFileNameOut = args[1]; + } + Save(sFileNameIn, sFileNameOut); + } + + public static bool Save(string strFileIn, string strFileOut) + { + // Read it + + TextReader tr; + try + { + tr = new StreamReader(strFileIn); + } + catch + { + return false; + } + + // Find the line that starts with "enum" + + while (true) { + string strT = tr.ReadLine(); + if (strT == null) + { + tr.Close(); + return false; + } + if (strT.StartsWith("enum")) + break; + } + + // Read in all the strings + + ArrayList alsStrings = new ArrayList(); + alsStrings.Clear(); + while (true) { + string strT = tr.ReadLine(); + if (strT == null) + break; + if (strT == "};") + break; + Regex rex = new Regex(@"^\s*(?[a-zA-Z_0-9]+)\,\s*\/\/\s*(?.*)\s*$"); + Match mat = rex.Match(strT); + string strValue = mat.Groups["string"].Value; + if (strValue.Length != 0) + alsStrings.Add(strValue); + } + tr.Close(); + + // Write directory + + Stream stm = new FileStream(strFileOut, FileMode.Create, FileAccess.Write, FileShare.None); + BinaryWriter bwtr = new BinaryWriter(stm); + int offStart = alsStrings.Count * 2; + foreach (string strT in alsStrings) { + // Write the current string's offset + + bwtr.Write(SwapUShort((ushort)offStart)); + + // Calc next index + + offStart += strT.Length + 1; + } + + // Write strings, length preceeded + + foreach (string strT in alsStrings) { + bwtr.Write((byte)strT.Length); + foreach (char ch in strT) + bwtr.Write((byte)ch); + } + + // Done + + bwtr.Close(); + return true; + } + + + public static ushort SwapUShort(ushort us) { + int n = (int)us; + return (ushort)(((n >> 8) & 0x00ff) | ((n << 8) & 0xff00)); + } + } +} diff --git a/StringTable/StringTable.csproj b/StringTable/StringTable.csproj new file mode 100644 index 0000000..0701ef3 --- /dev/null +++ b/StringTable/StringTable.csproj @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/StringTable/StringTable.sln b/StringTable/StringTable.sln new file mode 100644 index 0000000..c32f8d9 --- /dev/null +++ b/StringTable/StringTable.sln @@ -0,0 +1,21 @@ +Microsoft Visual Studio Solution File, Format Version 7.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StringTable", "StringTable.csproj", "{66C5CD0C-A547-4B7A-BD2C-53E148C0DED9}" +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + ConfigName.0 = Debug + ConfigName.1 = Release + EndGlobalSection + GlobalSection(ProjectDependencies) = postSolution + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {66C5CD0C-A547-4B7A-BD2C-53E148C0DED9}.Debug.ActiveCfg = Debug|.NET + {66C5CD0C-A547-4B7A-BD2C-53E148C0DED9}.Debug.Build.0 = Debug|.NET + {66C5CD0C-A547-4B7A-BD2C-53E148C0DED9}.Release.ActiveCfg = Release|.NET + {66C5CD0C-A547-4B7A-BD2C-53E148C0DED9}.Release.Build.0 = Release|.NET + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/TemplateExtractor/.cvsignore b/TemplateExtractor/.cvsignore new file mode 100644 index 0000000..ed332d6 --- /dev/null +++ b/TemplateExtractor/.cvsignore @@ -0,0 +1,3 @@ +bin +test + diff --git a/TemplateExtractor/AssemblyInfo.cs b/TemplateExtractor/AssemblyInfo.cs new file mode 100644 index 0000000..9f89a32 --- /dev/null +++ b/TemplateExtractor/AssemblyInfo.cs @@ -0,0 +1,58 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.*")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] diff --git a/TemplateExtractor/TemplateExtractor.cs b/TemplateExtractor/TemplateExtractor.cs new file mode 100644 index 0000000..3f4b994 --- /dev/null +++ b/TemplateExtractor/TemplateExtractor.cs @@ -0,0 +1,329 @@ +using System; +using System.IO; +using System.Collections; +using System.Collections.Specialized; +using System.Drawing; +using System.Drawing.Imaging; +using SpiffLib; + +namespace TemplateExtractor +{ + /// + /// Summary description for App. + /// + class App + { + static string gstrTemplateBitmap; + static string gstrTerrainBitmap; + static string gstrColorsBitmap; + static string gstrTemplateNames; + static string gstrOutputPrefix; + static string gstrTileCollection; + static int gcxTile = 24, gcyTile = 24; + static BitmapData gbmd; + static Color gclrTransparent; + static int[,] gaCells; + + /// + /// The main entry point for the application. + /// + [STAThread] + static unsafe int Main(string[] astrArgs) + { +#if false + while (true) { + float h, s, l; + float r, g, b; + string str = Console.ReadLine(); + r = float.Parse(str); + str = Console.ReadLine(); + g = float.Parse(str); + str = Console.ReadLine(); + b = float.Parse(str); + + // r = .5f; g = .5f; b = 1.0f; + Misc.RgbToHsl(r, g, b, &h, &s, &l); + Console.WriteLine("r: {3}, g: {4}, b: {5} -> h: {0}, s: {1}, l: {2}", h, s, l, r, g, b); + + Misc.HslToRgb(h, s, l, &r, &g, &b); + Console.WriteLine("r: {3}, g: {4}, b: {5} <- h: {0}, s: {1}, l: {2}", h, s, l, r, g, b); + } + + return 0; +#endif + // Command-line argument processing + + if (astrArgs.Length == 0) { + PrintHelp(); + return 0; + } + + for (int i = 0; i < astrArgs.Length; i++) { + switch (astrArgs[i]) { + case "-?": + PrintHelp(); + return 0; + + case "-n": + gstrTemplateNames = astrArgs[++i]; + break; + + case "-tc": + gstrTileCollection = astrArgs[++i]; + break; + + case "-art": + gstrTemplateBitmap = astrArgs[++i]; + break; + + case "-ter": + gstrTerrainBitmap = astrArgs[++i]; + break; + + case "-colors": + gstrColorsBitmap = astrArgs[++i]; + break; + + case "-ts": + gcxTile = int.Parse(astrArgs[++i]); + gcyTile = gcxTile; + break; + + default: + Console.WriteLine("Error: invalid argument '{0}'", astrArgs[i]); + return 1; + } + } + + Color clrBlocked = Color.FromArgb(255, 0, 0); + Bitmap bmTerrain = null; + if (gstrTerrainBitmap != null) + bmTerrain = new Bitmap(gstrTerrainBitmap); + + Bitmap bmColors = null; + if (gstrColorsBitmap != null) + bmColors = new Bitmap(gstrColorsBitmap); + + if (gstrTemplateBitmap == null) { + Console.WriteLine("Error: A valid source bitmap must be specified"); + return -1; + } + + if (gstrOutputPrefix == null) + gstrOutputPrefix = Path.GetFileNameWithoutExtension(gstrTemplateBitmap); + + Console.WriteLine("Reading {0}", gstrTemplateBitmap); + + Bitmap bmFile = null; + try { + bmFile = new Bitmap(gstrTemplateBitmap); + } catch { + Console.WriteLine("Error: {0} is not a recognized bitmap file", gstrTemplateBitmap); + return -1; + } + + // Using DrawImage to copy subrectangles of a loaded bitmap doesn't work reliably + // due to GDI+'s desire to scale based on Bitmap.Horizontal/VerticalResolution so + // we must create a neutral resolution bitmap before doing any DrawImage'ing from it. + + Console.WriteLine("Creating neutral resolution source bitmap", gstrTemplateBitmap); + + Bitmap bm = SpiffLib.Misc.NormalizeBitmap(bmFile); + bmFile.Dispose(); + + // The upper-left-most pixel defines the transparent color + + gclrTransparent = bm.GetPixel(0, 0); + + // + + int ctx = bm.Width / gcxTile; + int cty = bm.Height / gcyTile; + + gaCells = new int[ctx, cty]; + for (int j = 0; j < cty; j++) { + for (int i = 0; i < ctx; i++) { + gaCells[i, j] = -1; // -1 = empty + } + } + ArrayList alTemplates = new ArrayList(); + + Console.WriteLine("Scanning for templates", alTemplates.Count); + + // Lock down bits for speed + + Rectangle rc = new Rectangle(0, 0, bm.Width, bm.Height); + gbmd = bm.LockBits(rc, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); + byte *pbBase = (byte *)gbmd.Scan0.ToPointer(); + + for (int y = gcyTile, ty = 1; y < bm.Height - gcyTile; y += gcyTile, ty++) { + for (int x = gcxTile, tx = 1; x < bm.Width - gcxTile; x += gcxTile, tx++) { + byte *pb = pbBase + y * gbmd.Stride + x * 3; + Color clr = Color.FromArgb(pb[2], pb[1], pb[0]); + if (clr != gclrTransparent && gaCells[tx, ty] == -1) { + ArrayList alTiles = new ArrayList(); + FloodFill(pbBase, alTiles, tx, ty); + alTemplates.Add(alTiles); + } + } + } + + bm.UnlockBits(gbmd); + + StreamReader stmrTemplateNames = null; + if (gstrTemplateNames != null) + stmrTemplateNames = new StreamReader(gstrTemplateNames); + + Console.WriteLine("Extracting {0} templates", alTemplates.Count); + + m.DocManager.AddTemplate(new m.TemplateDocTemplate()); + m.TemplateDoc tmpd = (m.TemplateDoc)m.DocManager.NewDocument(typeof(m.TemplateDoc), new Object[] { new Size(gcxTile, gcyTile) }); + m.Template[] atmpl = new m.Template[alTemplates.Count]; + + // Create a new bitmap for each template. Empty cells of the template are + // filled with the transparent color. + + int cTiles = 0; + int nTemplate = 0; + foreach (ArrayList alTiles in alTemplates) { + cTiles += alTiles.Count; + + // Found boundary of template + + int txMax = -1, tyMax = -1; + int txMin = 99999, tyMin = 99999; + + foreach (Point pt in alTiles) { + if (pt.X < txMin) + txMin = pt.X; + if (pt.X > txMax) + txMax = pt.X; + if (pt.Y < tyMin) + tyMin = pt.Y; + if (pt.Y > tyMax) + tyMax = pt.Y; + } + + // + + ctx = txMax + 1 - txMin; + cty = tyMax + 1 - tyMin; + int cx = ctx * gcxTile; + int cy = cty * gcyTile; + + Bitmap bmTemplate = new Bitmap(cx, cy, PixelFormat.Format24bppRgb); + Graphics gTemplate = Graphics.FromImage(bmTemplate); + + Rectangle rcSrc = new Rectangle(txMin * gcxTile, tyMin * gcyTile, cx, cy); + gTemplate.DrawImage(bm, 0, 0, rcSrc, GraphicsUnit.Pixel); + + string strT; + if (gstrTemplateNames != null) { + strT = stmrTemplateNames.ReadLine(); + if (strT == null) // end of template names reached + strT = String.Format("template{0:0#}", nTemplate); + } else { + strT = String.Format("{0}{1:0#}", gstrOutputPrefix, nTemplate); + } + + if (gstrTileCollection == null) { + bmTemplate.Save(strT + ".png", ImageFormat.Png); + bmTemplate.Dispose(); + } else { + m.Template tmpl = new m.Template(tmpd, bmTemplate, strT); + if (bmTerrain != null) { + tmpl.TerrainMap = new m.TerrainTypes[cty, ctx]; + for (int j = 0; j < cty; j++) { + for (int i = 0; i < ctx; i++) { + if (bmTerrain.GetPixel((txMin + i) * gcxTile, (tyMin + j) * gcyTile) == clrBlocked) + tmpl.TerrainMap[j, i] = m.TerrainTypes.Blocked; + else + tmpl.TerrainMap[j, i] = m.TerrainTypes.Open; + } + } + } + if (bmColors != null) { + Color[] aclr = new Color[4] { + Color.FromArgb(114, 167, 48), // grass + Color.FromArgb(81, 142, 118), // cliff + Color.FromArgb(1, 172, 254), // water + Color.FromArgb(174, 168, 99), // road + }; + tmpl.TerrainColors = new m.TerrainColors[cty * 2, ctx * 2]; + for (int yT = 0; yT < cty * 2; yT++) { + for (int xT = 0; xT < ctx * 2; xT++) { + Color clr = bmColors.GetPixel(txMin * gcxTile + xT * gcxTile / 2, tyMin * gcyTile + yT * gcyTile / 2); + m.TerrainColors tclr = m.TerrainColors.Grass; + for (int i = 0; i < aclr.Length; i++) { + if (clr == aclr[i]) { + tclr = (m.TerrainColors)i; + break; + } + } + tmpl.TerrainColors[yT, xT] = tclr; + } + } + } + + if (strT.ToLower() == "background") + tmpd.SetBackgroundTemplate(tmpl); + + atmpl[nTemplate] = tmpl; + } + + gTemplate.Dispose(); + + nTemplate++; + } + + if (stmrTemplateNames != null) + stmrTemplateNames.Close(); + + if (gstrTileCollection != null) { + Console.WriteLine("Writing templates to {0}", gstrTileCollection); + + tmpd.AddTemplates(atmpl); + tmpd.SaveAs(gstrTileCollection); + } + + Console.WriteLine("{0} tiles total in {1} templates ({2:###,###,###} bytes)", cTiles, alTemplates.Count, cTiles * gcxTile * gcyTile); + + return 0; + } + + static unsafe private void FloodFill(byte *pbBase, ArrayList alTiles, int tx, int ty) { + // Has this cell already been filled? + + if (gaCells[tx, ty] != -1) + return; + + byte *pb = pbBase + (ty * gcyTile) * gbmd.Stride + (tx * gcxTile) * 3; + Color clr = Color.FromArgb(pb[2], pb[1], pb[0]); + if (clr == gclrTransparent) + return; + + gaCells[tx, ty] = 1; + alTiles.Add(new Point(tx, ty)); + + FloodFill(pbBase, alTiles, tx - 1, ty - 1); + FloodFill(pbBase, alTiles, tx, ty - 1); + FloodFill(pbBase, alTiles, tx + 1, ty - 1); + FloodFill(pbBase, alTiles, tx - 1, ty); + FloodFill(pbBase, alTiles, tx + 1, ty); + FloodFill(pbBase, alTiles, tx - 1, ty + 1); + FloodFill(pbBase, alTiles, tx, ty + 1); + FloodFill(pbBase, alTiles, tx + 1, ty + 1); + } + + // + + static void PrintHelp() { + Console.WriteLine( + "Usage: TemplateExtractor -art [-n names file] [-tc template collection] [-ter terrain bitmap]\n" + + "-art source bitmap: bitmap file containing templates to be processed.\n" + + "-n names file: file containing template names, one per line\n" + + "-tc template collection: name of template collection to output (instead of .pngs)\n" + + "-ter terrain bitmap: bitmap file containing terrain info to be processed."); + } + } +} diff --git a/TemplateExtractor/TemplateExtractor.csproj b/TemplateExtractor/TemplateExtractor.csproj new file mode 100644 index 0000000..f22b7ac --- /dev/null +++ b/TemplateExtractor/TemplateExtractor.csproj @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TemplateExtractor/TemplateExtractor.sln b/TemplateExtractor/TemplateExtractor.sln new file mode 100644 index 0000000..d931b8d --- /dev/null +++ b/TemplateExtractor/TemplateExtractor.sln @@ -0,0 +1,21 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TemplateExtractor", "TemplateExtractor.csproj", "{C2D0A473-D426-4F01-B8E8-FB4F6BD41A41}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Release = Release + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {C2D0A473-D426-4F01-B8E8-FB4F6BD41A41}.Debug.ActiveCfg = Debug|.NET + {C2D0A473-D426-4F01-B8E8-FB4F6BD41A41}.Debug.Build.0 = Debug|.NET + {C2D0A473-D426-4F01-B8E8-FB4F6BD41A41}.Release.ActiveCfg = Release|.NET + {C2D0A473-D426-4F01-B8E8-FB4F6BD41A41}.Release.Build.0 = Release|.NET + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/TemplateExtractor/app.ico b/TemplateExtractor/app.ico new file mode 100644 index 0000000..3a5525f Binary files /dev/null and b/TemplateExtractor/app.ico differ diff --git a/TemplateExtractor/deploy.bat b/TemplateExtractor/deploy.bat new file mode 100644 index 0000000..fe38283 --- /dev/null +++ b/TemplateExtractor/deploy.bat @@ -0,0 +1 @@ +copy bin\debug\TemplateExtractor.exe ..\bin diff --git a/acrunch/.cvsignore b/acrunch/.cvsignore new file mode 100644 index 0000000..132530c --- /dev/null +++ b/acrunch/.cvsignore @@ -0,0 +1,2 @@ +bin +out diff --git a/acrunch/App.ico b/acrunch/App.ico new file mode 100644 index 0000000..3a5525f Binary files /dev/null and b/acrunch/App.ico differ diff --git a/acrunch/AssemblyInfo.cs b/acrunch/AssemblyInfo.cs new file mode 100644 index 0000000..9f89a32 --- /dev/null +++ b/acrunch/AssemblyInfo.cs @@ -0,0 +1,58 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.*")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] diff --git a/acrunch/acrunch.cs b/acrunch/acrunch.cs new file mode 100644 index 0000000..466a17f --- /dev/null +++ b/acrunch/acrunch.cs @@ -0,0 +1,324 @@ +using System; +using System.IO; +using System.Drawing; +using System.Drawing.Imaging; +using SpiffCode; +using SpiffLib; + +namespace acrunch +{ + /// + /// Summary description for Class1. + /// + class App + { + /// + /// The main entry point for the application. + /// + [STAThread] + static int Main(string[] astrArgs) { + + if (astrArgs.Length < 3 || astrArgs.Length > 10) { + Console.WriteLine("Usage:\nacrunch [-scale N] [-save] [-noscaleicon] [-noshrinkwrap] [-highlight ] [-stats] \n"); + return -1; + } + + double nScale = 1.0; + bool fSave = false; + bool fNoShrinkWrap = false; + bool fDumpStats = false; + bool fScaleIcon = true; + bool fMakeHighlight = true; + int cxTile = 0; + + int iarg = 0; + while (astrArgs[iarg][0] == '-') { + switch (astrArgs[iarg]) { + case "-scale": + nScale = double.Parse(astrArgs[++iarg]); + break; + + case "-noscaleicon": + fScaleIcon = false; + break; + + case "-save": + fSave = true; + break; + + case "-noshrinkwrap": + fNoShrinkWrap = true; + break; + + case "-stats": + fDumpStats = true; + break; + + case "-highlight": + fMakeHighlight = true; + cxTile = Int32.Parse(astrArgs[++iarg]); + break; + } + + iarg++; + } + + // Read in the palette + + string strFilePal = astrArgs[iarg++]; + Palette pal = new Palette(strFilePal); + if (pal == null) { + Console.WriteLine("Error: unable to read the palette file {0}\n", strFilePal); + return -1; + } + + // Read in the animation file (.amx) + + string strFileAmx = astrArgs[iarg++]; + AnimDoc doc = AnimDoc.Load(strFileAmx); + if (doc == null) { + Console.WriteLine("Error: unable to read animation file {0}\n", strFileAmx); + return -1; + } + + // Get directory + + string strDir = astrArgs[iarg++]; + + // Reduce the Bitmaps down to the tightest rectangular boundary around the + // non-transparent pixels. + + if (!fNoShrinkWrap) + ShrinkWrap(doc); + + // Make highlight if asked. This uses existing images, so do this + // before scaling. + + Strip stpHighlight = null; + if (fMakeHighlight) { + stpHighlight = MakeHighlightStrip(doc, cxTile, pal); + } + + // Scale if asked + + if (nScale != 1.0) { + Scale(doc, nScale, pal, fScaleIcon); + } + + // Add in highlight strip + + if (stpHighlight != null) { + doc.StripSet.Add(stpHighlight); + foreach (Frame fr in stpHighlight) { + foreach (BitmapPlacer plc in fr.BitmapPlacers) { + doc.XBitmapSet.Add(plc.XBitmap); + } + } + } + + // If dump stats, we're *only* dumping stats + + if (fDumpStats) { + DumpStats(doc); + return 0; + } + + // Write the runtime animation file (.anir) and crunched bitmaps + + if (fSave) + doc.Save(strDir + Path.DirectorySeparatorChar + Path.GetFileName(strFileAmx)); + else + doc.WriteAnir(pal, strDir, Path.GetFileNameWithoutExtension(strFileAmx)); + return 0; + } + + static void DumpStats(AnimDoc doc) { + Console.WriteLine("Filename: " + doc.FileName.ToLower()); + Console.WriteLine("FrameRate: " + doc.FrameRate); + Console.WriteLine("Strip Count: " + doc.StripSet.Count); + foreach (Strip stp in doc.StripSet) { + Console.WriteLine("Strip Name: " + stp.Name); + Console.WriteLine("Frame Count: " + stp.Count); + } + } + + static Strip MakeHighlightStrip(AnimDoc doc, int cxTile, + Palette palFixed) { + Strip stpHelp = doc.StripSet["help"]; + if (stpHelp == null) { + return null; + } + + // This does a deep copy + Strip stpHighlight = (Strip)stpHelp.Clone(); + stpHighlight.Name = "highlight"; + + // Figure out the scaling. It would be better to pass this in as + // a parameter. + + Frame frT = stpHighlight[0]; + Rectangle rcUnion = new Rectangle(); + foreach (BitmapPlacer plc in frT.BitmapPlacers) { + Rectangle rc = new Rectangle(); + rc.X = -plc.X; + rc.Y = -plc.Y; + rc.Width = plc.XBitmap.Width; + rc.Height = plc.XBitmap.Height; + if (rcUnion.IsEmpty) { + rcUnion = rc; + } else { + rcUnion = Rectangle.Union(rcUnion, rc); + } + } + + // Needs to be 4 tiles high. Keep aspect ratio + + int cy = rcUnion.Height; + if (cy < cxTile * 4) { + cy = cxTile * 4; + } + double nScale = 1.0; + if (cy > rcUnion.Height) { + nScale = (double)cy / (double)rcUnion.Height; + } + + // Scale + + if (nScale != 1.0) { + foreach (Frame fr in stpHighlight) { + foreach (BitmapPlacer plc in fr.BitmapPlacers) { + plc.XBitmap.Bitmap = TBitmapTools.ScaleBitmap( + plc.XBitmap.Bitmap, nScale, palFixed); + plc.X = (int)Math.Round(plc.X * nScale); + plc.Y = (int)Math.Round(plc.Y * nScale); + } + + Point pt = new Point(); + pt.X = (int)Math.Round(fr.SpecialPoint.X * nScale); + pt.Y = (int)Math.Round(fr.SpecialPoint.Y * nScale); + fr.SpecialPoint = pt; + } + } + + return stpHighlight; + } + + static void Scale(AnimDoc doc, double nScale, Palette palFixed, bool fScaleIcon) { + if (nScale >= 1.5) + doc.Hires = true; + + // If not scaling icon, make a clone of it so it doesn't get scaled, + // then after scaling set this clone back in. + + Strip stpIcon = doc.StripSet["icon"]; + Strip stpIconClone = null; + if (!fScaleIcon && stpIcon != null) { + stpIconClone = (Strip)stpIcon.Clone(); + } + + // Scale all the bitmaps + + foreach (XBitmap xbm in doc.XBitmapSet) { + // Scale + + xbm.Bitmap = TBitmapTools.ScaleBitmap(xbm.Bitmap, nScale, palFixed); + + // Scale the points in the frames that use this bitmap + + foreach (Strip stp in doc.StripSet) { + foreach (Frame fr in stp) { + foreach (BitmapPlacer plc in fr.BitmapPlacers) { + if (plc.XBitmap == xbm) { + plc.X = (int)Math.Round(plc.X * nScale); + plc.Y = (int)Math.Round(plc.Y * nScale); + } + } + } + } + } + + // Scale all special points too + + foreach (Strip stp in doc.StripSet) { + foreach (Frame fr in stp) { + Point pt = new Point(); + pt.X = (int)Math.Round(fr.SpecialPoint.X * nScale); + pt.Y = (int)Math.Round(fr.SpecialPoint.Y * nScale); + fr.SpecialPoint = pt; + } + } + + // Put the strip icon back in if it shouldn't be scaled + + if (!fScaleIcon && stpIconClone != null) { + // Patch in the clone and add the images to the XBitmapSet + // (they are not added auto-magically). + + doc.StripSet[doc.StripSet.IndexOf(stpIcon)] = stpIconClone; + foreach (Frame fr in stpIconClone) { + foreach (BitmapPlacer plc in fr.BitmapPlacers) { + doc.XBitmapSet.Add(plc.XBitmap); + } + } + } + } + + static void ShrinkWrap(AnimDoc doc) { + Color clrTransparent = Color.FromArgb(0xff, 0, 0xff); + SolidBrush brTransparent = new SolidBrush(clrTransparent); + + foreach (XBitmap xbm in doc.XBitmapSet) { + + Bitmap bm = xbm.Bitmap; + + // OPT: this could be made faster by doing four independent edge scans + + int xL = bm.Width; + int xR = 0; + int yT = bm.Height; + int yB = 0; + for (int y = 0; y < bm.Height; y++) { + for (int x = 0; x < bm.Width; x++) { + Color clr = bm.GetPixel(x, y); + if (clr != clrTransparent) { + xL = Math.Min(xL, x); + xR = Math.Max(xR, x); + yT = Math.Min(yT, y); + yB = Math.Max(yB, y); + } + } + } + int cx = xR - xL + 1; + int cy = yB - yT + 1; + + Bitmap bmT = new Bitmap(cx, cy, PixelFormat.Format24bppRgb); + using (Graphics g = Graphics.FromImage(bmT)) { + Rectangle rcT = new Rectangle(xL, yT, cx, cy); + g.DrawImage(bm, 0, 0, rcT, GraphicsUnit.Pixel); + } + + xbm.Bitmap = bmT; + + // Don't need this anymore + + bm.Dispose(); + + // If the upper-left corner of the bitmap has been adjusted + // we must adjust the origins of all Frames referencing it. + + if (xL != 0 || yT != 0) { + foreach (Strip stp in doc.StripSet) { + foreach (Frame fr in stp) { + foreach (BitmapPlacer plc in fr.BitmapPlacers) { + if (plc.XBitmap == xbm) { + plc.X -= xL; + plc.Y -= yT; + } + } + } + } + } + } + } + } +} diff --git a/acrunch/acrunch.csproj b/acrunch/acrunch.csproj new file mode 100644 index 0000000..8f1e7ca --- /dev/null +++ b/acrunch/acrunch.csproj @@ -0,0 +1,93 @@ + + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {E38095A5-464E-4BF3-9E98-36BABC43EC32} + Exe + acrunch + acrunch + + + true + full + false + bin\Debug\ + prompt + 4 + false + -unsafe + + + none + false + bin\Release\ + prompt + 4 + false + -unsafe + + + + + + doublerect.cs + + + AnimDoc.cs + + + Frame.cs + + + Globals.cs + + + Strip.cs + + + StripSet.cs + + + XBitmap.cs + + + XBitmapSet.cs + + + misc.cs + + + palette.cs + + + tbitmap.cs + + + tbitmaptools.cs + + + + + + + + + + + + + + False + ..\bin\SharpZipLib.dll + + + + + + + + + \ No newline at end of file diff --git a/acrunch/acrunch.sln b/acrunch/acrunch.sln new file mode 100644 index 0000000..e812d04 --- /dev/null +++ b/acrunch/acrunch.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "acrunch", "acrunch.csproj", "{E38095A5-464E-4BF3-9E98-36BABC43EC32}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E38095A5-464E-4BF3-9E98-36BABC43EC32}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E38095A5-464E-4BF3-9E98-36BABC43EC32}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E38095A5-464E-4BF3-9E98-36BABC43EC32}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E38095A5-464E-4BF3-9E98-36BABC43EC32}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(MonoDevelopProperties) = preSolution + StartupItem = acrunch.csproj + EndGlobalSection +EndGlobal diff --git a/acrunch/deploy.bat b/acrunch/deploy.bat new file mode 100644 index 0000000..e23429c --- /dev/null +++ b/acrunch/deploy.bat @@ -0,0 +1 @@ +copy bin\debug\acrunch.exe ..\bin diff --git a/acrunch/deploy.sh b/acrunch/deploy.sh new file mode 100755 index 0000000..f04cda3 --- /dev/null +++ b/acrunch/deploy.sh @@ -0,0 +1 @@ +cp bin/Debug/acrunch.exe ../bin \ No newline at end of file diff --git a/aed/.cvsignore b/aed/.cvsignore new file mode 100644 index 0000000..0ca40fa --- /dev/null +++ b/aed/.cvsignore @@ -0,0 +1,2 @@ +bin +SpiffLib diff --git a/aed/AED.cs b/aed/AED.cs new file mode 100644 index 0000000..b66d63d --- /dev/null +++ b/aed/AED.cs @@ -0,0 +1,477 @@ +// UNDONE: batch mode (batch from within script?) +// UNDONE: pull function names from compiled script to build menu, function call from menu +// UNDONE: events to script, e.g., anim import/load +// UNDONE: try creating new CodeItems instead of reinstancing the engine + +using System; +using System.IO; +using System.Drawing; +using System.Drawing.Imaging; +using System.Xml; +using System.Collections; +using System.Diagnostics; +using System.Threading; +using System.Net; +using System.Windows.Forms; +using System.Text; // For ASCIIEncoding +using SpiffLib; + +namespace AED { + /// + /// Summary description for AED. + /// + class AED { + static bool gfVerbose = false; + static bool gfSuperVerbose = false; + + public static int Main(string[] astrArgs) + { + bool fGui, fLoad = false, fImport = false, fSaveAs = false, fExport = false; + bool fCrunch = false, fPalette = false, fValidateColors = false; + string strLoadFileName = null, strImportFileSpec = null, strSaveAsFileName = null, strExportPath = null; + string strPaletteFileName = null; + + fGui = astrArgs.Length == 0; + + // Parse command line arguments. Commands can be in any order. + + for (int i = 0; i < astrArgs.Length; i++) { + switch (astrArgs[i]) { + case "-g": + fGui = true; + break; + + case "-p": + fPalette = true; + if (i + 1 >= astrArgs.Length) { + Console.WriteLine("Error: -p command requires a filename argument"); + return -1; + } + strPaletteFileName = astrArgs[++i]; + break; + + case "-v": + gfVerbose = true; + break; + + case "-v2": + gfVerbose = true; + gfSuperVerbose = true; + break; + + case "-validatecolors": + fValidateColors = true; + break; + + case "-c": + fCrunch = true; + break; + + case "-l": + fLoad = true; + if (i + 1 >= astrArgs.Length) { + Console.WriteLine("Error: -f command requires a filename argument"); + return -1; + } + strLoadFileName = astrArgs[++i]; + break; + + case "-i": + fImport = true; + if (i + 1 >= astrArgs.Length) { + Console.WriteLine("Error: -i command requires a filespec argument"); + return -1; + } + strImportFileSpec = astrArgs[++i]; + break; + + case "-s": + fSaveAs = true; + if (i + 1 < astrArgs.Length && !astrArgs[i + 1].StartsWith("-")) + strSaveAsFileName = astrArgs[++i]; + break; + + case "-x": + fExport = true; + if (i + 1 < astrArgs.Length && !astrArgs[i + 1].StartsWith("-")) + strExportPath = astrArgs[++i]; + break; + + case "-?": + case "-help": + case "/?": + case "/help": + Console.WriteLine( + "Usage:\nAED [-v] [-c] [-p palette.pal] <-l | -i > [-s [filename]] [-x [dir]]\n" + + "-v: verbose\n" + + "-v2: super verbose!\n" + + "-c: crunch (compile to runtime format) when exporting\n" + + "-p palette.pal: specify the palette to be matched to when crunching to 8-bpp\n" + + "-l filename: load a .ani animation\n" + + "-s filename: save a .ani animation\n" + + "-i filespec: import a set of bitmaps\n" + + "-x dir: export a set of bitmaps to the specified directory\n" + + "-validatecolors: during crunch, validate that all pixel colors are in\n" + + " the specified palette\n"); + return 0; + + default: + Console.WriteLine("Error: Unknown command line argument \"{0}\"", astrArgs[i]); + return -1; + } + } + + // Additional command validation + + if (fLoad && fImport) { + Console.WriteLine("Error: Can't use -l and -i commands together, they're ambiguous"); + return -1; + } + + if (!fGui && !fLoad && !fImport) { + Console.WriteLine("Error: Must specify either -l or -i command so AED has something to work with"); + return -1; + } + + // Execute the specified commands + + AnimSet anis = new AnimSet(); + + if (fLoad) { + if (gfVerbose) + Console.WriteLine("Loading {0}...", strLoadFileName); + if (!Load(anis, strLoadFileName)) + return -1; + } + + if (fImport) { + if (gfVerbose) + Console.WriteLine("Importing {0}...", strImportFileSpec); + if (!Import(anis, strImportFileSpec)) + return -1; + } + + if (fGui) { + // This is modal and won't return until the MainForm is closed + + Application.Run(new Gui(anis.Items.Count != 0 ? anis : null)); + } + + if (fSaveAs) { + if (gfVerbose) + Console.WriteLine("Saving {0}...", strSaveAsFileName); + if (!SaveAs(anis, strSaveAsFileName)) + return -1; + } + + if (fExport) { + if (gfVerbose) + Console.WriteLine("Exporting {1} to {0}...", strExportPath, anis.Name); + Palette pal = null; + if (fPalette) + pal = new Palette(strPaletteFileName); + if (!Export(anis, strExportPath, pal, fCrunch, fValidateColors)) + return -1; + } + + return 0; + } + + // Expand the filespec into a list of filenames and pass them to the real Import() + + static public bool Import(AnimSet anis, string strFileSpec) { + string strDir = Path.GetDirectoryName(strFileSpec); + if (strDir == "") + strDir = "."; + string[] astrFiles = Directory.GetFiles(strDir, Path.GetFileName(strFileSpec)); + + return Import(anis, astrFiles); + } + + static public bool Import(AnimSet anis, string[] astrFileNames) { + + // By sorting the filenames we introduce a useful bit of determinism. + + Array.Sort(astrFileNames); + + // Enumerate all the filenames and for each one: + + Color clrTransparent = Color.FromArgb(0xff, 0, 0xff); + SolidBrush brTransparent = new SolidBrush(clrTransparent); + + foreach (string strFile in astrFileNames) { + + // 1. Read the bitmap from it. + + Bitmap bm; + try { + bm = new Bitmap(strFile); + } catch { + Console.WriteLine("Error: Can't load \"{0}\"", strFile); + return false; + } + + // 2. 'Normalize' the bitmap. Normalized bitmaps are 24-bit, 'tight', + // have the proper transparent color, and an origin. + + // All pixels the same color as the upper-left pixel get mapped to the + // transparent color + + bm.MakeTransparent(bm.GetPixel(0, 0)); + + Bitmap bmT = new Bitmap(bm.Width, bm.Height, PixelFormat.Format24bppRgb); + using (Graphics g = Graphics.FromImage(bmT)) { + + // Prep the new image by filling with the transparent color + + g.FillRectangle(brTransparent, 0, 0, bm.Width, bm.Height); + + // Convert the Bitmap to 24-bpp while leaving transparent pixels behind + + g.DrawImageUnscaled(bm, 0, 0); + } + + bm = bmT; + + // UNDONE: any color mapping + + // Find the tight boundary of the image and create a new Bitmap with just + // that portion of the Bitmap + // OPT: this could be made faster by doing four independent edge scans + + int xL = bm.Width; + int xR = 0; + int yT = bm.Height; + int yB = 0; + for (int y = 0; y < bm.Height; y++) { + for (int x = 0; x < bm.Width; x++) { + Color clr = bm.GetPixel(x, y); + if (clr != clrTransparent) { + xL = Math.Min(xL, x); + xR = Math.Max(xR, x); + yT = Math.Min(yT, y); + yB = Math.Max(yB, y); + } + } + } + int cx = xR - xL + 1; + int cy = yB - yT + 1; + int xOrigin = bm.Width / 2 - xL; + int yOrigin = bm.Height / 2 - yT; + + bmT = new Bitmap(cx, cy, PixelFormat.Format24bppRgb); + using (Graphics g = Graphics.FromImage(bmT)) { + Rectangle rcT = new Rectangle(xL, yT, cx, cy); + g.DrawImage(bm, 0, 0, rcT, GraphicsUnit.Pixel); + } + + bm = bmT; + + // 3. Create a Frame object which references the normalized bitmap. + + Frame frm = new Frame(bm, xOrigin, yOrigin); + + // 4. Assign it to the proper Anim and FrameSet by using info parsed + // from the bitmap's original filename. If the proper Anim/FrameSet + // does not yet exist, create it. + + string[] astr = strFile.Substring(strFile.LastIndexOf('\\') + 1).Split('_', '.'); + if (astr.Length != 5) { + Console.WriteLine("Warning: file {0} does not match the requisite naming pattern", strFile); + continue; + } + string strAnimSet = astr[0]; + string strAnim = astr[1]; + string strFrameSet = astr[2]; + string strFrame = astr[3]; + + anis.Name = strAnimSet; + anis.AddFrame(strAnim, strFrameSet, strFrame, frm); + } + + return true; + } + + static bool Load(AnimSet anis, string strFileName) { + Console.WriteLine("Loading not implemented yet."); + return true; + } + + // Write the .ani file and create a _ani subdirectory populated with one + // .png for each frame + + public static bool SaveAs(AnimSet anis, string strFileName) { + if (strFileName == null) + strFileName = anis.Name + ".ani"; + XmlTextWriter xwtr = new XmlTextWriter(strFileName, null); + xwtr.Formatting = Formatting.Indented; + anis.Serialize(xwtr); + xwtr.Close(); + + // Delete/create _ani subdirectory + + string strDir = strFileName.Replace(".ani", "_ani"); + if (Directory.Exists(strDir)) + Directory.Delete(strDir, true); + Directory.CreateDirectory(strDir); + + // Write all the frames as .png files into the _ani subdir + + foreach (DictionaryEntry deAnimSet in anis.Items) { + Anim ani = (Anim)deAnimSet.Value; + foreach (DictionaryEntry deAnim in ani.Items) { + FrameSet frms = (FrameSet)deAnim.Value; + foreach (Frame frm in frms) { + frm.Bitmap.Save(strDir + @"\" + anis.Name + "_" + ani.Name + "_" + frms.Name + "_" + frm.Index + ".png", + ImageFormat.Png); + } + } + } + + return true; + } + + // Write the .anir file and .bmps or .tbms, depending on fCrunch + + public static bool Export(AnimSet anis, string strExportPath, Palette pal, bool fCrunch, bool fValidateColors) { + Color clrTransparent = Color.FromArgb(0xff, 0, 0xff); + SolidBrush brTransparent = new SolidBrush(clrTransparent); + + if (strExportPath == null) + strExportPath = "."; + + ASCIIEncoding enc = new ASCIIEncoding(); + + FileStream stm = new FileStream(strExportPath + @"\" + anis.Name + ".anir", FileMode.Create, FileAccess.Write); + BinaryWriter stmw = new BinaryWriter(stm); + + // Count the number of FrameSets (aka Strips) + + ushort cstpd = 0; + foreach (DictionaryEntry deAnimSet in anis.Items) { + Anim ani = (Anim)deAnimSet.Value; + cstpd += (ushort)ani.Items.Count; + } + + // Write AnimationFileHeader.cstpd + + stmw.Write(Misc.SwapUShort(cstpd)); + + // Write array of offsets to StripDatas (AnimationFileHeader.aoffStpd) + + ushort offStpd = (ushort)(2 + (2 * cstpd)); + byte ibm = 0; + ArrayList albm = new ArrayList(); + + foreach (DictionaryEntry deAnimSet in anis.Items) { + Anim ani = (Anim)deAnimSet.Value; + foreach (DictionaryEntry deAnim in ani.Items) { + FrameSet frms = (FrameSet)deAnim.Value; + stmw.Write(Misc.SwapUShort(offStpd)); + + // Advance offset to where the next StripData will be + + offStpd += (ushort)((26+1+1+2) /* sizeof(StripData) - sizeof(FrameData) */ + + ((1+1+1+1+1+1) /* sizeof(FrameData) */ * frms.Count)); + } + } + + // Write array of StripDatas + + foreach (DictionaryEntry deAnimSet in anis.Items) { + Anim ani = (Anim)deAnimSet.Value; + foreach (DictionaryEntry deAnim in ani.Items) { + FrameSet frms = (FrameSet)deAnim.Value; + + // Write StripData.Name + + string strName = ani.Name + " " + frms.Name; + byte[] abT = new byte[26]; + enc.GetBytes(strName, 0, Math.Min(strName.Length, 25), abT, 0); + abT[25] = 0; + stmw.Write(abT); + + // Write StripData.cDelay + + stmw.Write((byte)0); + + // Write StripData.bfFlags + + stmw.Write((byte)0); + + // Write StripData.cfrmd + + ushort cfrmd = (ushort)frms.Count; + stmw.Write(Misc.SwapUShort(cfrmd)); + + // Write array of FrameDatas + + foreach (Frame frm in frms) { + + // Write FrameData.ibm (the index of the Bitmap as it will be in the Bitmap array) + + stmw.Write((byte)ibm); + ibm++; + + // Add the Frame's Bitmap for output + + Bitmap bm = frm.Bitmap; + if (fCrunch) { + albm.Add(bm); + } else { + // If not crunching then we need to go through some special work to preserve + // the origin point. Since the origin point is determined by finding the center + // of the imported bitmap we preserve it at export by creating a new bitmap + // sized so that he origin will be in its center. + + + // The '+2' at the end gives us a pixel of transparent space padding the left + // and right sides of the bitmap. We don't require this for any particular + // reason but it comes in handy. + + int cxNew = (Math.Max(frm.OriginX, bm.Width - frm.OriginX) * 2) + 2; + + // The '+2' at the end is to ensure a single blank scanline at the top + // which Import relies on to determine the transparent color. + + int cyNew = (Math.Max(frm.OriginY, bm.Height - frm.OriginY) * 2) + 2; + + using (Bitmap bmNew = new Bitmap(cxNew, cyNew, bm.PixelFormat)) { + using (Graphics g = Graphics.FromImage(bmNew)) { + g.FillRectangle(brTransparent, 0, 0, cxNew, cyNew); + g.DrawImage(bm, cxNew / 2 - frm.OriginX, cyNew / 2 - frm.OriginY); + } + strName = anis.Name + "_" + ani.Name + "_" + frms.Name + "_" + frm.Index.ToString(); + bmNew.Save(strExportPath + @"\" + strName + ".png", ImageFormat.Png); + } + } + + // Write FrameData.xOrigin, FrameData.yOrigin + + stmw.Write((byte)frm.OriginX); + stmw.Write((byte)frm.OriginY); + + // Write FrameData.bCustomData1, FrameData.bCustomData2, FrameData.bCustomData3 + + stmw.Write((byte)0); + stmw.Write((byte)0); + stmw.Write((byte)0); + } + } + } + + stmw.Close(); + + // Write out .tbm + + if (albm.Count != 0) { + string strFileName = strExportPath + @"\" + anis.Name + ".tbm"; + if (gfSuperVerbose) + Console.WriteLine("Crunching and writing " + strFileName); + TBitmap.Save((Bitmap[])albm.ToArray(typeof(Bitmap)), pal, strFileName); + } + + return true; + } + } +} diff --git a/aed/AED.csproj b/aed/AED.csproj new file mode 100644 index 0000000..e2ff728 --- /dev/null +++ b/aed/AED.csproj @@ -0,0 +1,204 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/aed/AED.sln b/aed/AED.sln new file mode 100644 index 0000000..6054c58 --- /dev/null +++ b/aed/AED.sln @@ -0,0 +1,21 @@ +Microsoft Visual Studio Solution File, Format Version 7.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AED", "AED.csproj", "{2EA04455-EA2C-40C7-AD73-0A834353467A}" +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + ConfigName.0 = Debug + ConfigName.1 = Release + EndGlobalSection + GlobalSection(ProjectDependencies) = postSolution + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {2EA04455-EA2C-40C7-AD73-0A834353467A}.Debug.ActiveCfg = Debug|.NET + {2EA04455-EA2C-40C7-AD73-0A834353467A}.Debug.Build.0 = Debug|.NET + {2EA04455-EA2C-40C7-AD73-0A834353467A}.Release.ActiveCfg = Release|.NET + {2EA04455-EA2C-40C7-AD73-0A834353467A}.Release.Build.0 = Release|.NET + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/aed/AnimSet.cs b/aed/AnimSet.cs new file mode 100644 index 0000000..ca09125 --- /dev/null +++ b/aed/AnimSet.cs @@ -0,0 +1,411 @@ +using System; +using System.Drawing; +using System.Xml; +using System.Collections; +using System.Collections.Specialized; +using System.ComponentModel; + +namespace AED +{ + /// + /// Summary description for AnimSet. + /// + + public class AnimSet { // anis + /// + /// + /// + public string Name; + /// + /// + /// + public ListDictionary Items = new ListDictionary(); + + /// + /// + /// + public AnimSet() {} + + /// + /// + /// + /// + public AnimSet(string strName) { + Name = strName; + } + + /// + /// + /// + /// + /// + public bool AddAnim(Anim ani) { + Items.Add(ani.Name, ani); + return true; + } + + /// + /// + /// + /// + /// + /// + /// + /// + public bool AddFrame(string strAnim, string strFrameSet, string strFrame, Frame frm) { + Anim ani; + if (Items.Contains(strAnim)) { + ani = (Anim)Items[strAnim]; + } else { + ani = new Anim(strAnim); + Items.Add(strAnim, ani); + } + ani.AddFrame(strFrameSet, strFrame, frm); + return true; + } + + /// + /// + /// + public Anim this[string strAnimName] { + get { + return (Anim)Items[strAnimName]; + } + } + + // OPT: not terribly efficient... + /// + /// + /// + public Anim this[int i] { + get { + Anim[] aani = new Anim[Items.Count]; + Items.Values.CopyTo(aani, 0); + return aani[i]; + } + } + + /// + /// + /// + /// + public void Serialize(XmlWriter xwtr) { + xwtr.WriteStartElement("AnimSet"); + + xwtr.WriteAttributeString("Name", Name); + foreach (DictionaryEntry de in Items) + ((Anim)de.Value).Serialize(xwtr); + + xwtr.WriteEndElement(); + } + } + + /// + /// + /// + public class Anim { // ani + /// + /// + /// + public string Name; + + /// + /// + /// + public ListDictionary Items = new ListDictionary(); + + /// + /// + /// + public Anim() {} + + /// + /// + /// + /// + public Anim(string strName) { + Name = strName; + } + + /// + /// + /// + /// + /// + /// + /// + public bool AddFrame(string strFrameSet, string strFrame, Frame frm) { + FrameSet frms; + if (Items.Contains(strFrameSet)) { + frms = (FrameSet)Items[strFrameSet]; + } else { + frms = new FrameSet(strFrameSet); + Items.Add(strFrameSet, frms); + } + frms.Add(frm); + return true; + } + + /// + /// + /// + public FrameSet this[string strFrameSetName] { + get { + return (FrameSet)Items[strFrameSetName]; + } + } + + // OPT: not terribly efficient... + /// + /// + /// + public FrameSet this[int i] { + get { + FrameSet[] afrms = new FrameSet[Items.Count]; + Items.Values.CopyTo(afrms, 0); + return afrms[i]; + } + } + + /// + /// + /// + /// + public void Serialize(XmlWriter xwtr) { + xwtr.WriteStartElement("Anim"); + xwtr.WriteAttributeString("Name", Name); + + foreach (DictionaryEntry de in Items) + ((FrameSet)de.Value).Serialize(xwtr); + + xwtr.WriteEndElement(); + } + } + + /// + /// + /// + public class FrameSet : CollectionBase { // frms + /// + /// + /// + public string Name; + + /// + /// + /// + /// + public FrameSet(string strName) { + Name = strName; + } + + /// + /// + /// + public Frame this[int i] { + get { + return (Frame)InnerList[i]; + } + set { + InnerList[i] = value; + } + } + + // UNDONE: BUGBUG: This shouldn't be necessary. FrameSet inherits + // from CollectionBase which implements IList so IList's methods + // (e.g., Add, IndexOf) should automatically be exposed by FrameSet. + + /// + /// + /// + /// + /// + public int Add(Frame frm) { + return ((IList)this).Add(frm); + } + + /// + /// + /// + /// + /// + public int IndexOf(Frame frm) { + return ((IList)this).IndexOf(frm); + } + + /// + /// + /// + /// + public void Serialize(XmlWriter xwtr) { + xwtr.WriteStartElement("FrameSet"); + xwtr.WriteAttributeString("Name", Name); + + foreach (object ob in InnerList) + ((Frame)ob).Serialize(xwtr); + + xwtr.WriteEndElement(); + } + + // CollectionBase overrides + + /// + /// + /// + /// + /// + protected override void OnInsertComplete(int i, object ob) { + ((Frame)ob).Parent = this; + } + + } + + /// + /// + /// + public class Frame { // frm + private Bitmap m_bm; + private FrameSet m_frmsParent; + private int m_xOrigin, m_yOrigin; + + /// + /// + /// + [Category("FYI")] + public Bitmap Bitmap { + get { + return m_bm; + } + } + + /// + /// + /// + public FrameSet Parent { + get { + return m_frmsParent; + } + set { + m_frmsParent = value; + } + } + + /// + /// + /// + [Category("Layout")] + public int OriginX { + get { + return m_xOrigin; + } + set { + m_xOrigin = value; + } + } + + /// + /// + /// + [Category("Layout")] + public int OriginY { + get { + return m_yOrigin; + } + set { + m_yOrigin = value; + } + } + + /// + /// + /// + /// + /// + /// + public Frame(Bitmap bm, int xOrigin, int yOrigin) { + m_bm = bm; +// m_bm.MakeTransparent(Color.FromArgb(255, 0, 255)); +#if false + // Map shadow color into black with light alpha + Color clrShadow = Color.FromArgb(156, 212, 248); + Color clrAlphaShadow = Color.FromArgb(101, 0, 0, 0); + for (int y = 0; y < m_bm.Height; y++) { + for (int x = 0; x < m_bm.Width; x++) { + Color clr = m_bm.GetPixel(x, y); + if (clr == clrShadow) + m_bm.SetPixel(x, y, clrAlphaShadow); + } + } +#endif + m_xOrigin = xOrigin; + m_yOrigin = yOrigin; + } + + /// + /// + /// + [Category("FYI")] + public int Index { + get { + return ((IList)Parent).IndexOf(this); + } + } + + /// + /// + /// + /// + public void Serialize(XmlWriter xwtr) { + xwtr.WriteStartElement("Frame"); + xwtr.WriteAttributeString("Index", Index.ToString()); + xwtr.WriteAttributeString("xOrigin", m_xOrigin.ToString()); + xwtr.WriteAttributeString("yOrigin", m_yOrigin.ToString()); + xwtr.WriteEndElement(); + } + + /// + /// + /// + /// + /// + /// + /// + public bool ReplaceColor(Color clrOld, Color clrNew, bool f6bit) { + bool fFound = false; + Bitmap bm = Bitmap; + + if (f6bit) { + clrOld = Color.FromArgb(clrOld.R & 0xfc, clrOld.G & 0xfc, clrOld.B & 0xfc); + clrNew = Color.FromArgb(clrNew.R & 0xfc, clrNew.G & 0xfc, clrNew.B & 0xfc); + } + + for (int y = 0; y < bm.Height; y++) { + for (int x = 0; x < bm.Width; x++) { + Color clr = bm.GetPixel(x, y); + if (f6bit) + clr = Color.FromArgb(clr.R & 0xfc, clr.G & 0xfc, clr.B & 0xfc); + + if (clr == clrOld) { + fFound = true; + bm.SetPixel(x, y, clrNew); + } + } + } + + return fFound; + } + + /// + /// + /// + /// + /// + /// + /// + public bool ReplaceColor(int[] aiOld, int[] aiNew, bool f6bit) { + Color clrOld = Color.FromArgb(aiOld[0], aiOld[1], aiOld[2]); + Color clrNew = Color.FromArgb(aiNew[0], aiNew[1], aiNew[2]); + return ReplaceColor(clrOld, clrNew, f6bit); + } + } +} diff --git a/aed/AssemblyInfo.cs b/aed/AssemblyInfo.cs new file mode 100644 index 0000000..9f89a32 --- /dev/null +++ b/aed/AssemblyInfo.cs @@ -0,0 +1,58 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.*")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] diff --git a/aed/Gui.cs b/aed/Gui.cs new file mode 100644 index 0000000..5354f55 --- /dev/null +++ b/aed/Gui.cs @@ -0,0 +1,1666 @@ +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; +using System.IO; +using SpiffLib; +using System.Xml.Serialization; +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace AED { + /// + /// Summary description for Gui. + /// + public class Gui : System.Windows.Forms.Form { + private System.Windows.Forms.MainMenu mainMenu1; + private System.Windows.Forms.MenuItem menuItem7; + private System.Windows.Forms.MenuItem menuItem9; + private System.Windows.Forms.OpenFileDialog importFileDialog; + private System.Windows.Forms.Splitter splt; + private System.Windows.Forms.Panel panel1; + private System.Windows.Forms.TreeView trvAnimSet; + private System.Windows.Forms.ImageList imageList1; + private System.Windows.Forms.HScrollBar sbh; + private System.Windows.Forms.NumericUpDown spnFrameNumber; + private System.Windows.Forms.Button btnStop; + private System.Windows.Forms.Button btnPlay; + private System.Windows.Forms.Button btnLoopForward; + private System.Windows.Forms.Panel pnlSbPbcGroup; + private System.Windows.Forms.Panel pnlPlaybackControls; + private System.Windows.Forms.Timer tmrAnim; + private System.Windows.Forms.MenuItem mniShowOrigin; + private System.Windows.Forms.MenuItem mniImport; + private System.Windows.Forms.MenuItem mniExit; + private System.Windows.Forms.MenuItem mniSetBackgroundColor; + private System.Windows.Forms.TrackBar trackBar1; + private System.Windows.Forms.SaveFileDialog saveFileDialog; + private System.Windows.Forms.MenuItem mniSaveAs; + private System.Windows.Forms.MenuItem mniOpen; + private System.Windows.Forms.MenuItem mniSave; + private System.Windows.Forms.MenuItem mniExport; + private System.Windows.Forms.SaveFileDialog exportFileDialog; + private System.Windows.Forms.OpenFileDialog openPaletteFileDialog; + private System.Windows.Forms.MenuItem mniFile; + private System.Windows.Forms.Button btnShiftUp; + private System.Windows.Forms.Button btnShiftDown; + private System.Windows.Forms.Button btnShiftLeft; + private System.Windows.Forms.Button btnShiftRight; + private System.Windows.Forms.ToolTip ttip; + private System.Windows.Forms.Label lblFrameInfo; + private System.Windows.Forms.MenuItem menuItem1; + private System.Windows.Forms.MenuItem mniEditScript; + private System.Windows.Forms.MenuItem mniRunScript; + private System.Windows.Forms.ContextMenu mnuTreeView; + private System.Windows.Forms.MenuItem mniTreeViewProperties; + private System.Windows.Forms.MenuItem mniOptions; + private System.Windows.Forms.MenuItem mniShowGrid; + private System.Windows.Forms.RadioButton rbtnA; + private System.Windows.Forms.RadioButton rbtnB; + private System.Windows.Forms.MenuItem mniShowBOverA; + private System.Windows.Forms.Label lblTest; + private System.Windows.Forms.Panel panel2; + private System.Windows.Forms.MenuItem mniMapSideColors; + private System.Windows.Forms.Label lblFPS; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.NumericUpDown nudFrameRate; + private System.Windows.Forms.MenuItem mniSetBackgroundBitmap; + private System.Windows.Forms.OpenFileDialog openBitmapDialog; + private System.ComponentModel.IContainer components; + + /// + /// + /// + public Gui(AnimSet anis) { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + // Non-Designer initialization + + m_strAedDir = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName); + try { + // UNDONE: problems with this approach to settings: + // 1. proper location of .config file? Documents and Settings? + // 2. default values + // 3. maintenance of Settings class + // 4. what happens if member isn't present? + // 5. order-dependent? + // 6. XML file less readable than .ini + // 7. more code/maintenance than ini.write(m_nScale), etc w/ overloads + // 8. doesn't handle Color and other types + TextReader trdr = new StreamReader(m_strAedDir + @"\AED.config"); + XmlSerializer xser = new XmlSerializer(typeof(Settings)); + Settings settings = (Settings)xser.Deserialize(trdr); + + m_nScale = settings.nPreviewSize; + m_fShowOrigin = settings.fShowOrigin; + m_fShowGrid = settings.fShowGrid; + m_fShowBOverA = settings.fShowBOverA; + m_clrBackground = Color.FromArgb(settings.nArgbBackground); + m_fMapSideColors = settings.fMapSideColors; + // UNDONE: default frame rate + trdr.Close(); + } catch { + } + + // Initialize frame rate dependent components + + SetFrameRate(80); + + // UNDONE: doesn't work because panel1 can't accept Focus + panel1.MouseWheel += new System.Windows.Forms.MouseEventHandler(this.panel1_MouseWheel); + trackBar1.Value = m_nScale; + SetPreviewPanelBackColor(m_clrBackground); + + if (anis != null) { + m_anis = anis; + ResetTreeView(); + ShowFirstFrame(); + } + + // Script stuff + + m_se = new ScriptEngine(); + m_se.ScriptDone += new EventHandler(OnScriptDone); + m_se.AddGlobal("AED", this); + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) { + if( disposing ) { + if(components != null) { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this.components = new System.ComponentModel.Container(); + System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(Gui)); + this.mnuTreeView = new System.Windows.Forms.ContextMenu(); + this.mniTreeViewProperties = new System.Windows.Forms.MenuItem(); + this.menuItem9 = new System.Windows.Forms.MenuItem(); + this.mniSetBackgroundColor = new System.Windows.Forms.MenuItem(); + this.spnFrameNumber = new System.Windows.Forms.NumericUpDown(); + this.menuItem7 = new System.Windows.Forms.MenuItem(); + this.btnStop = new System.Windows.Forms.Button(); + this.imageList1 = new System.Windows.Forms.ImageList(this.components); + this.menuItem1 = new System.Windows.Forms.MenuItem(); + this.mniEditScript = new System.Windows.Forms.MenuItem(); + this.mniRunScript = new System.Windows.Forms.MenuItem(); + this.exportFileDialog = new System.Windows.Forms.SaveFileDialog(); + this.sbh = new System.Windows.Forms.HScrollBar(); + this.mniOpen = new System.Windows.Forms.MenuItem(); + this.mniSave = new System.Windows.Forms.MenuItem(); + this.importFileDialog = new System.Windows.Forms.OpenFileDialog(); + this.mniFile = new System.Windows.Forms.MenuItem(); + this.mniSaveAs = new System.Windows.Forms.MenuItem(); + this.mniImport = new System.Windows.Forms.MenuItem(); + this.mniExport = new System.Windows.Forms.MenuItem(); + this.mniExit = new System.Windows.Forms.MenuItem(); + this.trackBar1 = new System.Windows.Forms.TrackBar(); + this.ttip = new System.Windows.Forms.ToolTip(this.components); + this.btnShiftUp = new System.Windows.Forms.Button(); + this.btnShiftDown = new System.Windows.Forms.Button(); + this.btnPlay = new System.Windows.Forms.Button(); + this.btnLoopForward = new System.Windows.Forms.Button(); + this.btnShiftRight = new System.Windows.Forms.Button(); + this.btnShiftLeft = new System.Windows.Forms.Button(); + this.trvAnimSet = new System.Windows.Forms.TreeView(); + this.rbtnB = new System.Windows.Forms.RadioButton(); + this.rbtnA = new System.Windows.Forms.RadioButton(); + this.nudFrameRate = new System.Windows.Forms.NumericUpDown(); + this.mniOptions = new System.Windows.Forms.MenuItem(); + this.mniShowOrigin = new System.Windows.Forms.MenuItem(); + this.mniShowGrid = new System.Windows.Forms.MenuItem(); + this.mniMapSideColors = new System.Windows.Forms.MenuItem(); + this.mniShowBOverA = new System.Windows.Forms.MenuItem(); + this.mniSetBackgroundBitmap = new System.Windows.Forms.MenuItem(); + this.pnlSbPbcGroup = new System.Windows.Forms.Panel(); + this.pnlPlaybackControls = new System.Windows.Forms.Panel(); + this.mainMenu1 = new System.Windows.Forms.MainMenu(); + this.openPaletteFileDialog = new System.Windows.Forms.OpenFileDialog(); + this.splt = new System.Windows.Forms.Splitter(); + this.saveFileDialog = new System.Windows.Forms.SaveFileDialog(); + this.tmrAnim = new System.Windows.Forms.Timer(this.components); + this.panel1 = new System.Windows.Forms.Panel(); + this.lblTest = new System.Windows.Forms.Label(); + this.lblFrameInfo = new System.Windows.Forms.Label(); + this.panel2 = new System.Windows.Forms.Panel(); + this.label1 = new System.Windows.Forms.Label(); + this.lblFPS = new System.Windows.Forms.Label(); + this.openBitmapDialog = new System.Windows.Forms.OpenFileDialog(); + ((System.ComponentModel.ISupportInitialize)(this.spnFrameNumber)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.trackBar1)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.nudFrameRate)).BeginInit(); + this.pnlSbPbcGroup.SuspendLayout(); + this.pnlPlaybackControls.SuspendLayout(); + this.panel1.SuspendLayout(); + this.panel2.SuspendLayout(); + this.SuspendLayout(); + // + // mnuTreeView + // + this.mnuTreeView.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.mniTreeViewProperties}); + // + // mniTreeViewProperties + // + this.mniTreeViewProperties.Index = 0; + this.mniTreeViewProperties.Text = "Properties..."; + this.mniTreeViewProperties.Click += new System.EventHandler(this.mniTreeViewProperties_Click); + // + // menuItem9 + // + this.menuItem9.Index = 6; + this.menuItem9.Text = "-"; + // + // mniSetBackgroundColor + // + this.mniSetBackgroundColor.Index = 4; + this.mniSetBackgroundColor.Text = "Set &Background Color..."; + this.mniSetBackgroundColor.Click += new System.EventHandler(this.mniSetBackgroundColor_Click); + // + // spnFrameNumber + // + this.spnFrameNumber.Location = new System.Drawing.Point(74, 3); + this.spnFrameNumber.Name = "spnFrameNumber"; + this.spnFrameNumber.Size = new System.Drawing.Size(50, 20); + this.spnFrameNumber.TabIndex = 2; + this.spnFrameNumber.TabStop = false; + this.ttip.SetToolTip(this.spnFrameNumber, "Frame number"); + this.spnFrameNumber.ValueChanged += new System.EventHandler(this.spnFrameNumber_ValueChanged); + // + // menuItem7 + // + this.menuItem7.Index = 3; + this.menuItem7.Text = "-"; + // + // btnStop + // + this.btnStop.Image = ((System.Drawing.Bitmap)(resources.GetObject("btnStop.Image"))); + this.btnStop.ImageIndex = 0; + this.btnStop.ImageList = this.imageList1; + this.btnStop.Name = "btnStop"; + this.btnStop.Size = new System.Drawing.Size(24, 24); + this.btnStop.TabIndex = 1; + this.btnStop.TabStop = false; + this.ttip.SetToolTip(this.btnStop, "Stop playing"); + this.btnStop.Click += new System.EventHandler(this.btnStop_Click); + // + // imageList1 + // + this.imageList1.ColorDepth = System.Windows.Forms.ColorDepth.Depth8Bit; + this.imageList1.ImageSize = new System.Drawing.Size(16, 16); + this.imageList1.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("imageList1.ImageStream"))); + this.imageList1.TransparentColor = System.Drawing.Color.White; + // + // menuItem1 + // + this.menuItem1.Index = 2; + this.menuItem1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.mniEditScript, + this.mniRunScript}); + this.menuItem1.Text = "Script"; + // + // mniEditScript + // + this.mniEditScript.Index = 0; + this.mniEditScript.Text = "&Edit Script..."; + this.mniEditScript.Click += new System.EventHandler(this.mniEditScript_Click); + // + // mniRunScript + // + this.mniRunScript.Index = 1; + this.mniRunScript.Text = "&Run Script"; + // + // exportFileDialog + // + this.exportFileDialog.AddExtension = false; + this.exportFileDialog.FileName = "no file name needed, click Save"; + this.exportFileDialog.Filter = "Directories|*.nomatchesplease"; + this.exportFileDialog.Title = "Export"; + this.exportFileDialog.ValidateNames = false; + // + // sbh + // + this.sbh.Dock = System.Windows.Forms.DockStyle.Fill; + this.sbh.LargeChange = 1; + this.sbh.Name = "sbh"; + this.sbh.Size = new System.Drawing.Size(301, 24); + this.sbh.TabIndex = 2; + this.ttip.SetToolTip(this.sbh, "Frame selector"); + this.sbh.Scroll += new System.Windows.Forms.ScrollEventHandler(this.sbh_Scroll); + // + // mniOpen + // + this.mniOpen.Enabled = false; + this.mniOpen.Index = 0; + this.mniOpen.Text = "&Open..."; + // + // mniSave + // + this.mniSave.Index = 1; + this.mniSave.Text = "&Save"; + this.mniSave.Click += new System.EventHandler(this.mniSave_Click); + // + // importFileDialog + // + this.importFileDialog.Filter = "Bitmap files (*.bmp,*.png,*.gif,*.jpg,*.exif,*.tif)|*.bmp;*.png;*.gif;*.exif;*.jp" + + "g;*.tif|All files (*.*)|*.*"; + this.importFileDialog.Multiselect = true; + this.importFileDialog.Title = "Import"; + // + // mniFile + // + this.mniFile.Index = 0; + this.mniFile.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.mniOpen, + this.mniSave, + this.mniSaveAs, + this.menuItem7, + this.mniImport, + this.mniExport, + this.menuItem9, + this.mniExit}); + this.mniFile.Text = "&File"; + this.mniFile.Popup += new System.EventHandler(this.mniFile_Popup); + // + // mniSaveAs + // + this.mniSaveAs.Index = 2; + this.mniSaveAs.Text = "Save &As..."; + this.mniSaveAs.Click += new System.EventHandler(this.mniSaveAs_Click); + // + // mniImport + // + this.mniImport.Index = 4; + this.mniImport.Text = "&Import..."; + this.mniImport.Click += new System.EventHandler(this.mniImport_Click); + // + // mniExport + // + this.mniExport.Index = 5; + this.mniExport.Text = "&Export..."; + this.mniExport.Click += new System.EventHandler(this.mniExport_Click); + // + // mniExit + // + this.mniExit.Index = 7; + this.mniExit.Text = "E&xit"; + this.mniExit.Click += new System.EventHandler(this.mniExit_Click); + // + // trackBar1 + // + this.trackBar1.Location = new System.Drawing.Point(0, 8); + this.trackBar1.Maximum = 20; + this.trackBar1.Minimum = 1; + this.trackBar1.Name = "trackBar1"; + this.trackBar1.Size = new System.Drawing.Size(96, 45); + this.trackBar1.TabIndex = 5; + this.trackBar1.TickFrequency = 2; + this.trackBar1.TickStyle = System.Windows.Forms.TickStyle.TopLeft; + this.ttip.SetToolTip(this.trackBar1, "Zoom level"); + this.trackBar1.Value = 2; + this.trackBar1.Scroll += new System.EventHandler(this.trackBar1_Scroll); + // + // btnShiftUp + // + this.btnShiftUp.FlatStyle = System.Windows.Forms.FlatStyle.System; + this.btnShiftUp.Location = new System.Drawing.Point(35, 56); + this.btnShiftUp.Name = "btnShiftUp"; + this.btnShiftUp.Size = new System.Drawing.Size(24, 23); + this.btnShiftUp.TabIndex = 6; + this.btnShiftUp.TabStop = false; + this.btnShiftUp.Text = "â–²"; + this.ttip.SetToolTip(this.btnShiftUp, "Shift frame up relative to origin point"); + this.btnShiftUp.Click += new System.EventHandler(this.btnShiftUp_Click); + // + // btnShiftDown + // + this.btnShiftDown.FlatStyle = System.Windows.Forms.FlatStyle.System; + this.btnShiftDown.Location = new System.Drawing.Point(36, 104); + this.btnShiftDown.Name = "btnShiftDown"; + this.btnShiftDown.Size = new System.Drawing.Size(24, 23); + this.btnShiftDown.TabIndex = 6; + this.btnShiftDown.TabStop = false; + this.btnShiftDown.Text = "â–¼"; + this.ttip.SetToolTip(this.btnShiftDown, "Shift frame down relative to origin point"); + this.btnShiftDown.Click += new System.EventHandler(this.btnShiftDown_Click); + // + // btnPlay + // + this.btnPlay.BackColor = System.Drawing.SystemColors.Control; + this.btnPlay.Image = ((System.Drawing.Bitmap)(resources.GetObject("btnPlay.Image"))); + this.btnPlay.ImageIndex = 1; + this.btnPlay.ImageList = this.imageList1; + this.btnPlay.Location = new System.Drawing.Point(24, 0); + this.btnPlay.Name = "btnPlay"; + this.btnPlay.Size = new System.Drawing.Size(24, 24); + this.btnPlay.TabIndex = 1; + this.btnPlay.TabStop = false; + this.ttip.SetToolTip(this.btnPlay, "Play once"); + this.btnPlay.Click += new System.EventHandler(this.btnPlay_Click); + // + // btnLoopForward + // + this.btnLoopForward.Image = ((System.Drawing.Bitmap)(resources.GetObject("btnLoopForward.Image"))); + this.btnLoopForward.ImageIndex = 2; + this.btnLoopForward.ImageList = this.imageList1; + this.btnLoopForward.Location = new System.Drawing.Point(48, 0); + this.btnLoopForward.Name = "btnLoopForward"; + this.btnLoopForward.Size = new System.Drawing.Size(24, 24); + this.btnLoopForward.TabIndex = 1; + this.btnLoopForward.TabStop = false; + this.ttip.SetToolTip(this.btnLoopForward, "Play looped"); + this.btnLoopForward.Click += new System.EventHandler(this.btnLoopForward_Click); + // + // btnShiftRight + // + this.btnShiftRight.FlatStyle = System.Windows.Forms.FlatStyle.System; + this.btnShiftRight.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0))); + this.btnShiftRight.Location = new System.Drawing.Point(59, 80); + this.btnShiftRight.Name = "btnShiftRight"; + this.btnShiftRight.Size = new System.Drawing.Size(24, 23); + this.btnShiftRight.TabIndex = 6; + this.btnShiftRight.TabStop = false; + this.btnShiftRight.Text = "â–º"; + this.ttip.SetToolTip(this.btnShiftRight, "Shift frame right relative to origin point"); + this.btnShiftRight.Click += new System.EventHandler(this.btnShiftRight_Click); + // + // btnShiftLeft + // + this.btnShiftLeft.FlatStyle = System.Windows.Forms.FlatStyle.System; + this.btnShiftLeft.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0))); + this.btnShiftLeft.Location = new System.Drawing.Point(11, 80); + this.btnShiftLeft.Name = "btnShiftLeft"; + this.btnShiftLeft.Size = new System.Drawing.Size(24, 23); + this.btnShiftLeft.TabIndex = 6; + this.btnShiftLeft.TabStop = false; + this.btnShiftLeft.Text = "â—„"; + this.ttip.SetToolTip(this.btnShiftLeft, "Shift frame left relative to origin point"); + this.btnShiftLeft.Click += new System.EventHandler(this.btnShiftLeft_Click); + // + // trvAnimSet + // + this.trvAnimSet.Dock = System.Windows.Forms.DockStyle.Left; + this.trvAnimSet.FullRowSelect = true; + this.trvAnimSet.HideSelection = false; + this.trvAnimSet.ImageIndex = -1; + this.trvAnimSet.Indent = 15; + this.trvAnimSet.Name = "trvAnimSet"; + this.trvAnimSet.SelectedImageIndex = -1; + this.trvAnimSet.ShowLines = false; + this.trvAnimSet.Size = new System.Drawing.Size(80, 461); + this.trvAnimSet.TabIndex = 1; + this.ttip.SetToolTip(this.trvAnimSet, "FrameSet selector"); + this.trvAnimSet.MouseDown += new System.Windows.Forms.MouseEventHandler(this.trvAnimSet_MouseDown); + this.trvAnimSet.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.trvAnimSet_AfterSelect); + // + // rbtnB + // + this.rbtnB.Appearance = System.Windows.Forms.Appearance.Button; + this.rbtnB.FlatStyle = System.Windows.Forms.FlatStyle.System; + this.rbtnB.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0))); + this.rbtnB.Location = new System.Drawing.Point(56, 136); + this.rbtnB.Name = "rbtnB"; + this.rbtnB.Size = new System.Drawing.Size(24, 24); + this.rbtnB.TabIndex = 10; + this.rbtnB.Text = "B"; + this.rbtnB.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + this.ttip.SetToolTip(this.rbtnB, "Switch to AnimSet B"); + this.rbtnB.CheckedChanged += new System.EventHandler(this.rbtnB_CheckedChanged); + // + // rbtnA + // + this.rbtnA.Appearance = System.Windows.Forms.Appearance.Button; + this.rbtnA.Checked = true; + this.rbtnA.FlatStyle = System.Windows.Forms.FlatStyle.System; + this.rbtnA.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0))); + this.rbtnA.Location = new System.Drawing.Point(16, 136); + this.rbtnA.Name = "rbtnA"; + this.rbtnA.Size = new System.Drawing.Size(24, 24); + this.rbtnA.TabIndex = 9; + this.rbtnA.TabStop = true; + this.rbtnA.Text = "A"; + this.rbtnA.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + this.ttip.SetToolTip(this.rbtnA, "Switch to AnimSet A"); + this.rbtnA.CheckedChanged += new System.EventHandler(this.rbtnA_CheckedChanged); + // + // nudFrameRate + // + this.nudFrameRate.CausesValidation = false; + this.nudFrameRate.Increment = new System.Decimal(new int[] { + 10, + 0, + 0, + 0}); + this.nudFrameRate.Location = new System.Drawing.Point(0, 352); + this.nudFrameRate.Maximum = new System.Decimal(new int[] { + 1000, + 0, + 0, + 0}); + this.nudFrameRate.Minimum = new System.Decimal(new int[] { + 16, + 0, + 0, + 0}); + this.nudFrameRate.Name = "nudFrameRate"; + this.nudFrameRate.Size = new System.Drawing.Size(48, 20); + this.nudFrameRate.TabIndex = 15; + this.ttip.SetToolTip(this.nudFrameRate, "Playback Rate (in milliseconds per frame)"); + this.nudFrameRate.Value = new System.Decimal(new int[] { + 80, + 0, + 0, + 0}); + this.nudFrameRate.ValueChanged += new System.EventHandler(this.nudFrameRate_ValueChanged); + // + // mniOptions + // + this.mniOptions.Index = 1; + this.mniOptions.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.mniShowOrigin, + this.mniShowGrid, + this.mniMapSideColors, + this.mniShowBOverA, + this.mniSetBackgroundColor, + this.mniSetBackgroundBitmap}); + this.mniOptions.Text = "&Options"; + this.mniOptions.Popup += new System.EventHandler(this.mniOptions_Popup); + // + // mniShowOrigin + // + this.mniShowOrigin.Index = 0; + this.mniShowOrigin.Text = "Show &Origin Point"; + this.mniShowOrigin.Click += new System.EventHandler(this.mniShowOrigin_Click); + // + // mniShowGrid + // + this.mniShowGrid.Index = 1; + this.mniShowGrid.Text = "Show 16x16 &Grid"; + this.mniShowGrid.Click += new System.EventHandler(this.mniShowGrid_Click); + // + // mniMapSideColors + // + this.mniMapSideColors.Index = 2; + this.mniMapSideColors.Text = "Map &Side Colors"; + this.mniMapSideColors.Click += new System.EventHandler(this.mniMapSideColors_Click); + // + // mniShowBOverA + // + this.mniShowBOverA.Index = 3; + this.mniShowBOverA.Text = "Show B over A"; + this.mniShowBOverA.Click += new System.EventHandler(this.mniShowBOverA_Click); + // + // mniSetBackgroundBitmap + // + this.mniSetBackgroundBitmap.Index = 5; + this.mniSetBackgroundBitmap.Text = "Set Background Bitmap..."; + this.mniSetBackgroundBitmap.Click += new System.EventHandler(this.mniSetBackgroundBitmap_Click); + // + // pnlSbPbcGroup + // + this.pnlSbPbcGroup.Controls.AddRange(new System.Windows.Forms.Control[] { + this.sbh, + this.pnlPlaybackControls}); + this.pnlSbPbcGroup.Dock = System.Windows.Forms.DockStyle.Bottom; + this.pnlSbPbcGroup.Location = new System.Drawing.Point(0, 437); + this.pnlSbPbcGroup.Name = "pnlSbPbcGroup"; + this.pnlSbPbcGroup.Size = new System.Drawing.Size(429, 24); + this.pnlSbPbcGroup.TabIndex = 4; + // + // pnlPlaybackControls + // + this.pnlPlaybackControls.Controls.AddRange(new System.Windows.Forms.Control[] { + this.spnFrameNumber, + this.btnStop, + this.btnPlay, + this.btnLoopForward}); + this.pnlPlaybackControls.Dock = System.Windows.Forms.DockStyle.Right; + this.pnlPlaybackControls.Location = new System.Drawing.Point(301, 0); + this.pnlPlaybackControls.Name = "pnlPlaybackControls"; + this.pnlPlaybackControls.Size = new System.Drawing.Size(128, 24); + this.pnlPlaybackControls.TabIndex = 3; + // + // mainMenu1 + // + this.mainMenu1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.mniFile, + this.mniOptions, + this.menuItem1}); + // + // openPaletteFileDialog + // + this.openPaletteFileDialog.DefaultExt = "pal"; + this.openPaletteFileDialog.Filter = "Jasc Palette (*.pal)|*.pal"; + this.openPaletteFileDialog.RestoreDirectory = true; + this.openPaletteFileDialog.Title = "Specify a Palette to map to"; + // + // splt + // + this.splt.BackColor = System.Drawing.SystemColors.ActiveBorder; + this.splt.Location = new System.Drawing.Point(80, 0); + this.splt.Name = "splt"; + this.splt.Size = new System.Drawing.Size(3, 461); + this.splt.TabIndex = 3; + this.splt.TabStop = false; + // + // saveFileDialog + // + this.saveFileDialog.DefaultExt = "ani"; + this.saveFileDialog.FileName = "untitled.ani"; + this.saveFileDialog.Filter = "AED files (*.ani)|*.ani|All files (*.*)|*.*"; + this.saveFileDialog.Title = "Save File As"; + // + // tmrAnim + // + this.tmrAnim.Interval = 80; + this.tmrAnim.Tick += new System.EventHandler(this.tmrAnim_Tick); + // + // panel1 + // + this.panel1.Controls.AddRange(new System.Windows.Forms.Control[] { + this.pnlSbPbcGroup}); + this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel1.Location = new System.Drawing.Point(83, 0); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(429, 461); + this.panel1.TabIndex = 4; + this.panel1.MouseUp += new System.Windows.Forms.MouseEventHandler(this.panel1_MouseUp); + this.panel1.Paint += new System.Windows.Forms.PaintEventHandler(this.panel1_Paint); + this.panel1.MouseMove += new System.Windows.Forms.MouseEventHandler(this.panel1_MouseMove); + this.panel1.MouseDown += new System.Windows.Forms.MouseEventHandler(this.panel1_MouseDown); + // + // lblTest + // + this.lblTest.Location = new System.Drawing.Point(0, 264); + this.lblTest.Name = "lblTest"; + this.lblTest.Size = new System.Drawing.Size(104, 40); + this.lblTest.TabIndex = 11; + this.lblTest.UseMnemonic = false; + // + // lblFrameInfo + // + this.lblFrameInfo.BackColor = System.Drawing.SystemColors.Control; + this.lblFrameInfo.Font = new System.Drawing.Font("Comic Sans MS", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0))); + this.lblFrameInfo.Location = new System.Drawing.Point(0, 176); + this.lblFrameInfo.Name = "lblFrameInfo"; + this.lblFrameInfo.Size = new System.Drawing.Size(96, 80); + this.lblFrameInfo.TabIndex = 7; + this.lblFrameInfo.UseMnemonic = false; + // + // panel2 + // + this.panel2.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.panel2.Controls.AddRange(new System.Windows.Forms.Control[] { + this.nudFrameRate, + this.label1, + this.lblFPS, + this.btnShiftRight, + this.btnShiftLeft, + this.btnShiftUp, + this.btnShiftDown, + this.trackBar1, + this.rbtnB, + this.rbtnA, + this.lblFrameInfo, + this.lblTest}); + this.panel2.Dock = System.Windows.Forms.DockStyle.Right; + this.panel2.Location = new System.Drawing.Point(512, 0); + this.panel2.Name = "panel2"; + this.panel2.Size = new System.Drawing.Size(96, 461); + this.panel2.TabIndex = 5; + // + // label1 + // + this.label1.Location = new System.Drawing.Point(0, 328); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(100, 24); + this.label1.TabIndex = 14; + this.label1.Text = "Playback Rate (msec/frame):"; + this.label1.TextAlign = System.Drawing.ContentAlignment.BottomLeft; + // + // lblFPS + // + this.lblFPS.Font = new System.Drawing.Font("Microsoft Sans Serif", 6.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0))); + this.lblFPS.Location = new System.Drawing.Point(46, 355); + this.lblFPS.Name = "lblFPS"; + this.lblFPS.Size = new System.Drawing.Size(96, 16); + this.lblFPS.TabIndex = 13; + this.lblFPS.Text = "(12.5 FPS)"; + // + // openBitmapDialog + // + this.openBitmapDialog.Filter = "Bitmap files (*.bmp,*.png,*.gif,*.jpg,*.exif,*.tif)|*.bmp;*.png;*.gif;*.exif;*.jp" + + "g;*.tif|All files (*.*)|*.*"; + this.openBitmapDialog.Multiselect = true; + this.openBitmapDialog.Title = "Import"; + // + // Gui + // + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.ClientSize = new System.Drawing.Size(608, 461); + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.panel1, + this.panel2, + this.splt, + this.trvAnimSet}); + this.KeyPreview = true; + this.Menu = this.mainMenu1; + this.Name = "Gui"; + this.Text = "AED"; + this.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.Gui_KeyPress); + this.Closed += new System.EventHandler(this.Gui_Closed); + ((System.ComponentModel.ISupportInitialize)(this.spnFrameNumber)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.trackBar1)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.nudFrameRate)).EndInit(); + this.pnlSbPbcGroup.ResumeLayout(false); + this.pnlPlaybackControls.ResumeLayout(false); + this.panel1.ResumeLayout(false); + this.panel2.ResumeLayout(false); + this.ResumeLayout(false); + + } + #endregion + + // + // Non-Windows.Forms state + // + + ToggleableState m_ts = new ToggleableState(); + private AnimSet m_anis; + private int m_nScale = 2; + private bool m_fLoop = false; + private bool m_fShowOrigin = false; + private bool m_fShowGrid = false; + private bool m_fShowBOverA = false; + private bool m_fMapSideColors = true; + private Color m_clrBackground = Color.FromKnownColor(KnownColor.Beige); + private int[] m_aiCustomColors = null; +#if false + private string m_strPaletteFileName = null; +#endif + private Palette m_pal = null; + private ScriptEngine m_se; + private string m_strScript = null; + private ArrayList m_alstFrames = new ArrayList(); + private TreeNode m_trnMenued = null; + private string m_strAedDir = null; + private string m_strSaveFileName; + private int m_cmsFrameRate; + private Bitmap m_bmBackground = null; + private Point m_ptBackgroundOffset; + private Point m_ptDragStart, m_ptPreDragBackgroundOffset; + private bool m_fDraggingBackground = false; + + // + // Public Properties + // + + /// + /// + /// + public ArrayList Frames { + get { + return m_alstFrames; + } + } + + /// + /// + /// + public Frame ActiveFrame { + get { + return m_ts.ActiveFrameSet[m_ts.ActiveFrameIndex]; + } + } + + // + // Public Methods + // + + /// + /// + /// + /// + public void ShowProperties(object ob) { + PropertyInspector prpi = new PropertyInspector(ob); + prpi.PropertyValueChanged += new PropertyValueChangedEventHandler(OnPropertyValueChanged); + prpi.Show(); + } + + // + // UI event handlers + // + + private void trvAnimSet_AfterSelect(object sender, System.Windows.Forms.TreeViewEventArgs e) { + TreeNode trn = e.Node; + if (trn.Parent == null) { + trn = trn.FirstNode; + trvAnimSet.SelectedNode = trn; + } + SetActiveFrameSet(trn.Parent.Text, trn.Text); + } + + private void btnPlay_Click(object sender, System.EventArgs e) { + m_ts.ActiveFrameIndex = 0; + m_fLoop = false; + tmrAnim.Start(); + } + + private void btnStop_Click(object sender, System.EventArgs e) { + tmrAnim.Stop(); + m_fLoop = false; + } + + private void btnLoopForward_Click(object sender, System.EventArgs e) { + m_fLoop = !m_fLoop; + if (m_fLoop) + tmrAnim.Start(); + else + tmrAnim.Stop(); + } + + private void tmrAnim_Tick(object sender, System.EventArgs e) { + if (m_ts.ActiveFrameSet == null) + return; + + int ifrm = m_ts.ActiveFrameIndex + 1; + if (ifrm >= m_ts.ActiveFrameSet.Count) { + ifrm = 0; + if (!m_fLoop) + tmrAnim.Stop(); + } + + SetActiveFrame(ifrm); + } + + private void Gui_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e) { + e.Handled = true; + + switch (e.KeyChar) { + case '[': + if (m_ts.ActiveFrameSet != null) { + int ifrm = m_ts.ActiveFrameIndex - 1; + if (ifrm < 0) + ifrm = m_ts.ActiveFrameSet.Count - 1; + SetActiveFrame(ifrm); + } + break; + + case ']': + if (m_ts.ActiveFrameSet != null) { + int ifrm = m_ts.ActiveFrameIndex + 1; + if (ifrm >= m_ts.ActiveFrameSet.Count) + ifrm = 0; + SetActiveFrame(ifrm); + } + break; + + default: + e.Handled = false; + break; + } + } + + // + // Menu event handlers + // + + private void mniShowGrid_Click(object sender, System.EventArgs e) { + m_fShowGrid = !m_fShowGrid; + PaintFrame(); + } + + private void mniFile_Popup(object sender, System.EventArgs e) { + bool fHaveAnimSet = m_anis != null; + mniSaveAs.Enabled = fHaveAnimSet; + mniSave.Enabled = fHaveAnimSet; + mniExport.Enabled = fHaveAnimSet; + } + + private void mniImport_Click(object sender, System.EventArgs e) { + if (importFileDialog.ShowDialog() != DialogResult.OK) + return; + + string[] astrFileNames = importFileDialog.FileNames; + if (astrFileNames == null) + return; + + m_ts = new ToggleableState(); + + m_anis = new AnimSet(); + if (!AED.Import(m_anis, astrFileNames)) + return; + + rbtnA.Checked = true; + // rbtnB.Checked = false; + + ResetTreeView(); + UpdateFrameList(); + ShowFirstFrame(); + } + + void ResetTreeView() { + trvAnimSet.Nodes.Clear(); + + foreach (DictionaryEntry deAnimSet in m_anis.Items) { + Anim ani = (Anim)deAnimSet.Value; + TreeNode trn = new TreeNode(ani.Name); + trvAnimSet.Nodes.Add(trn); + + // Sort the framesets using a numeric sort + + FrameSet[] afrms = new FrameSet[ani.Items.Count]; + + int i = 0; + foreach (DictionaryEntry deAnim in ani.Items) + afrms[i++] = (FrameSet)deAnim.Value; + + Array.Sort(afrms, new FrameSetComparer()); + + // Add the framesets to the tree + + foreach (FrameSet frms in afrms) { + trn.Nodes.Add(new TreeNode(frms.Name)); + } + trn.Expand(); + } + } + + void ShowFirstFrame() { + if (m_anis.Items.Count == 0) + return; + + Anim aniT = m_anis[0]; + SetActiveFrameSet(aniT.Name, aniT[0].Name); + trvAnimSet.SelectedNode = trvAnimSet.Nodes[0].FirstNode; + } + + private void mniExit_Click(object sender, System.EventArgs e) { + Close(); + } + + private void mniShowOrigin_Click(object sender, System.EventArgs e) { + m_fShowOrigin = !m_fShowOrigin; + PaintFrame(); + } + + private void mniSetBackgroundColor_Click(object sender, System.EventArgs e) { + ColorDialog dlgColor = new ColorDialog(); + dlgColor.CustomColors = m_aiCustomColors; + dlgColor.Color = m_clrBackground; + dlgColor.ShowDialog(); + SetPreviewPanelBackColor(dlgColor.Color); + m_aiCustomColors = dlgColor.CustomColors; + PaintFrame(); + } + + private void mniSaveAs_Click(object sender, System.EventArgs e) { + if (saveFileDialog.ShowDialog() != DialogResult.OK) + return; + if (!AED.SaveAs(m_anis, saveFileDialog.FileName)) { + MessageBox.Show("AED.SaveAs failed"); + return; + } + + m_strSaveFileName = saveFileDialog.FileName; + } + + private void mniSave_Click(object sender, System.EventArgs e) { + if (m_strSaveFileName == null) { + mniSaveAs_Click(sender, e); + return; + } + + if (!AED.SaveAs(m_anis, m_strSaveFileName)) + MessageBox.Show("AED.SaveAs failed"); + } + + private void mniExport_Click(object sender, System.EventArgs e) { + if (exportFileDialog.ShowDialog() != DialogResult.OK) + return; + string strExportDir = Path.GetDirectoryName(exportFileDialog.FileName); +#if false + if (m_strPaletteFileName == null) { + if (openPaletteFileDialog.ShowDialog() != DialogResult.OK) + return; + m_strPaletteFileName = openPaletteFileDialog.FileName; + m_pal = new Palette(m_strPaletteFileName); + } +#endif + + AED.Export(m_anis, strExportDir, m_pal, false, false); + } + + private void sbh_Scroll(object sender, System.Windows.Forms.ScrollEventArgs e) { + SetActiveFrame(e.NewValue, false); + } + + private void panel1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { + PaintFrame(); + } + + private void panel1_MouseWheel(object sender, System.Windows.Forms.MouseEventArgs e) { + int nSize = m_nScale + e.Delta; + if (nSize < 1) + nSize = 1; + if (nSize != m_nScale) { + m_nScale = nSize; + PaintFrame(); + } + } + + private void spnFrameNumber_ValueChanged(object sender, System.EventArgs e) { + SetActiveFrame(System.Convert.ToInt32(spnFrameNumber.Value)); + } + + private void trackBar1_Scroll(object sender, System.EventArgs e) { + m_nScale = trackBar1.Value; + PaintFrame(); + } + + // Origin shifting buttons + + private void btnShiftUp_Click(object sender, System.EventArgs e) { + if (m_ts.ActiveFrameSet == null) + return; + + if ((ModifierKeys & Keys.Shift) != 0) { + foreach (Frame frm in m_ts.ActiveFrameSet) + frm.OriginY++; + } else { + m_ts.ActiveFrameSet[m_ts.ActiveFrameIndex].OriginY++; + } + PaintFrame(); + } + + private void btnShiftDown_Click(object sender, System.EventArgs e) { + if (m_ts.ActiveFrameSet == null) + return; + + if ((ModifierKeys & Keys.Shift) != 0) { + foreach (Frame frm in m_ts.ActiveFrameSet) + frm.OriginY--; + } else { + m_ts.ActiveFrameSet[m_ts.ActiveFrameIndex].OriginY--; + } + PaintFrame(); + } + + private void btnShiftLeft_Click(object sender, System.EventArgs e) { + if (m_ts.ActiveFrameSet == null) + return; + + if ((ModifierKeys & Keys.Shift) != 0) { + foreach (Frame frm in m_ts.ActiveFrameSet) + frm.OriginX++; + } else { + m_ts.ActiveFrameSet[m_ts.ActiveFrameIndex].OriginX++; + } + PaintFrame(); + } + + private void btnShiftRight_Click(object sender, System.EventArgs e) { + if (m_ts.ActiveFrameSet == null) + return; + + if ((ModifierKeys & Keys.Shift) != 0) { + foreach (Frame frm in m_ts.ActiveFrameSet) + frm.OriginX--; + } else { + m_ts.ActiveFrameSet[m_ts.ActiveFrameIndex].OriginX--; + } + PaintFrame(); + } + + // + // Script event handlers + // + + private void OnScriptDone(object obSender, EventArgs e) { + PaintFrame(); + } + + private void OnScriptEditorClosing(object obSender, EventArgs e) { + m_strScript = ((ScriptEditor)obSender).Script; + } + + // + // Everything else + // + + private Point WxyFromFxy(Point ptFrame) { + Rectangle rcClient = GetAnimViewRect(); + int xCenter = rcClient.Width / 2; + int yCenter = rcClient.Height / 2; + + Bitmap bmSrc = m_ts.ActiveFrameSet[m_ts.ActiveFrameIndex].Bitmap; + + return new Point(xCenter - ((bmSrc.Width / 2) * m_nScale) + (ptFrame.X * m_nScale), + yCenter - ((bmSrc.Height / 2) * m_nScale) + (ptFrame.Y * m_nScale)); + } + + private Point FxyFromWxy(Point ptWindow) { + Rectangle rcClient = GetAnimViewRect(); + int xCenter = rcClient.Width / 2; + int yCenter = rcClient.Height / 2; + + int dx = ptWindow.X - xCenter; + if (dx < 0) + dx -= m_nScale; + int dy = ptWindow.Y - yCenter; + if (dy < 0) + dy -= m_nScale; + return new Point(dx / m_nScale, dy / m_nScale); + } + + private void PaintFrame() { + Rectangle rcClient = GetAnimViewRect(); + int xCenter = rcClient.Width / 2; + int yCenter = rcClient.Height / 2; + + int cxT = ((rcClient.Width + m_nScale - 1) / m_nScale) + 2; + int cyT = ((rcClient.Height + m_nScale - 1) / m_nScale) + 2; + int xCenterT = cxT / 2; + int yCenterT = cyT / 2; + + // NOTE: these 'using' statements (a 'shortcut' for calling .Dispose()) are + // absolutely necessary or we chew up all virtual memory while animating + + using (Graphics g = panel1.CreateGraphics()) { + + // Create a temporary bitmap for compositing the grid, frames, origin indicator, etc into + + using (Bitmap bmT = new Bitmap(cxT, cyT)) { + + using (Graphics gT = Graphics.FromImage(bmT)) { + gT.Clear(m_clrBackground); + + // Draw background bitmap, if any + + if (m_bmBackground != null) + gT.DrawImage(m_bmBackground, xCenterT - (m_bmBackground.Width / 2) + m_ptBackgroundOffset.X, + yCenterT - (m_bmBackground.Height / 2) + m_ptBackgroundOffset.Y, + m_bmBackground.Width, m_bmBackground.Height); + + // Draw grid (if enabled) + // UNDONE: use alpha to draw grid (e.g., brighten or darken) + + if (m_fShowGrid) { + Brush br = new SolidBrush(Color.FromKnownColor(KnownColor.LightGray)); + for (int x = xCenterT % 16; x < cxT; x += 16) + gT.FillRectangle(br, x, 0, 1, cyT); + + for (int y = yCenterT % 16; y < cyT; y += 16) + gT.FillRectangle(br, 0, y, cxT, 1); + } + + BitmapData bmdDst = bmT.LockBits(new Rectangle(0, 0, bmT.Width, bmT.Height), + ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); + + if (m_ts.FrameSetArray[0] != null && (m_ts.Active == 0 || m_fShowBOverA)) { + Frame frm = m_ts.FrameSetArray[0][m_ts.FrameIndexArray[0]]; + Bitmap bmSrc = frm.Bitmap; + + BitmapData bmdSrc = bmSrc.LockBits(new Rectangle(0, 0, bmSrc.Width, bmSrc.Height), + ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); + + SuperBlt(bmdSrc, 0, 0, bmdDst, xCenterT - frm.OriginX, yCenterT - frm.OriginY, bmSrc.Width, bmSrc.Height, m_fMapSideColors); + + bmSrc.UnlockBits(bmdSrc); + } + + if (m_ts.FrameSetArray[1] != null && (m_ts.Active == 1 || m_fShowBOverA)) { + Frame frm = m_ts.FrameSetArray[1][m_ts.FrameIndexArray[1]]; + Bitmap bmSrc = frm.Bitmap; + + BitmapData bmdSrc = bmSrc.LockBits(new Rectangle(0, 0, bmSrc.Width, bmSrc.Height), + ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); + + SuperBlt(bmdSrc, 0, 0, bmdDst, xCenterT - frm.OriginX, yCenterT - frm.OriginY, bmSrc.Width, bmSrc.Height, m_fMapSideColors); + + bmSrc.UnlockBits(bmdSrc); + } + + bmT.UnlockBits(bmdDst); + + // Draw origin point (if enabled) + + if (m_fShowOrigin) + bmT.SetPixel(xCenterT, yCenterT, Color.FromKnownColor(KnownColor.Orange)); + + // Force a nice simple, fast old-school stretchblt + + g.InterpolationMode = InterpolationMode.NearestNeighbor; + + // NOTE: _without_ this the first row and column are only scaled by half! + + g.PixelOffsetMode = PixelOffsetMode.Half; + + // StretchBlt the temporary composite to the screen + + g.DrawImage(bmT, rcClient.Left - ((xCenterT * m_nScale) - xCenter), + rcClient.Top - ((yCenterT * m_nScale) - yCenter), + cxT * m_nScale, cyT * m_nScale); + } + } + } + } + + struct RgbData { + public RgbData(byte bRed, byte bGreen, byte bBlue) { + this.bRed = bRed; + this.bGreen = bGreen; + this.bBlue = bBlue; + } + + public byte bBlue; + public byte bGreen; + public byte bRed; + } + + static RgbData[] argbSide = { + new RgbData(232, 32, 0), + new RgbData(196, 28, 0), + new RgbData(128, 8, 0), + new RgbData(92, 8, 0), + new RgbData(64, 8, 0) + }; + + // Skips dst where src has transparent color. + // Darkens dst where src has shadow color. + // Translates side colors. + // NOTE: Performs dst but not src clipping!!! + // NOTE: Assumes src and dst BitmapData are PixelFormat.Format24bppRgb + + private unsafe void SuperBlt(BitmapData bmdSrc, int xSrc, int ySrc, BitmapData bmdDst, int xDst, int yDst, int cx, int cy, bool fMapSideColors) { + + // If completely off dst bounds, just return. + + if ((xDst >= bmdDst.Width || xDst + cx < 0) || (yDst >= bmdDst.Height) || (yDst + cy < 0)) + return; + + // Dst clip + + if (xDst + cx > bmdDst.Width) + cx = bmdDst.Width - xDst; + if (yDst + cy > bmdDst.Height) + cy = bmdDst.Height - yDst; + + if (xDst < 0) { + cx += xDst; + xSrc -= xDst; + xDst = 0; + } + + if (yDst < 0) { + cy += yDst; + ySrc -= yDst; + yDst = 0; + } + + RgbData* prgbSrc = (RgbData*)((byte*)bmdSrc.Scan0 + (ySrc * bmdSrc.Stride) + (xSrc * sizeof(RgbData))); + RgbData* prgbDst = (RgbData*)((byte*)bmdDst.Scan0 + (yDst * bmdDst.Stride) + (xDst * sizeof(RgbData))); + + while (cy-- > 0) { + RgbData* prgbDstT = prgbDst; + RgbData* prgbSrcT = prgbSrc; + + for (int x = 0; x < cx; x++) { + RgbData rgbSrc = *prgbSrcT++; + + // Handle shadow color + + if (rgbSrc.bRed == 156 && rgbSrc.bGreen == 212 & rgbSrc.bBlue == 248) { + prgbDstT->bRed = (byte)((prgbDstT->bRed * 60) / 100); + prgbDstT->bGreen = (byte)((prgbDstT->bGreen * 60) / 100); + prgbDstT->bBlue = (byte)((prgbDstT->bBlue * 60) / 100); + prgbDstT++; + + // Handle transparent color + + } else if (rgbSrc.bRed == 255 && rgbSrc.bGreen == 0 && rgbSrc.bBlue == 255) { + prgbDstT++; + + // Handle side colors + + } else if (fMapSideColors) { + if (rgbSrc.bRed == 0 && rgbSrc.bGreen == 116 && rgbSrc.bBlue == 232) { + *prgbDstT++ = argbSide[0]; + } else if (rgbSrc.bRed == 0 && rgbSrc.bGreen == 96 && rgbSrc.bBlue == 196) { + *prgbDstT++ = argbSide[1]; + } else if (rgbSrc.bRed == 0 && rgbSrc.bGreen == 64 && rgbSrc.bBlue == 120) { + *prgbDstT++ = argbSide[2]; + } else if (rgbSrc.bRed == 0 && rgbSrc.bGreen == 48 && rgbSrc.bBlue == 92) { + *prgbDstT++ = argbSide[3]; + } else if (rgbSrc.bRed == 0 && rgbSrc.bGreen == 32 && rgbSrc.bBlue == 64) { + *prgbDstT++ = argbSide[4]; + } else { + *prgbDstT++ = rgbSrc; + } + + // Just copy everything else unaltered + + } else { + *prgbDstT++ = rgbSrc; + } + } + + // Advance to next scan line + + prgbDst = (RgbData*)(((byte*)prgbDst) + bmdDst.Stride); + prgbSrc = (RgbData*)(((byte*)prgbSrc) + bmdSrc.Stride); + } + } + + private void SetFrameRate(int cms) { + m_cmsFrameRate = cms; + tmrAnim.Interval = m_cmsFrameRate; + nudFrameRate.Value = m_cmsFrameRate; + lblFPS.Text = string.Format("({0:###.#} FPS)", 1000.0f / m_cmsFrameRate); + } + + private void SetActiveFrameSet(string strAniName, string strFrameSetName) { + m_ts.ActiveAnim = m_anis[strAniName]; + m_ts.ActiveFrameSet = m_ts.ActiveAnim[strFrameSetName]; + OnActiveFrameSetChanged(); + } + + private void SetActiveFrame(int ifrm) { + SetActiveFrame(ifrm, true); + } + + private void SetActiveFrame(int ifrm, bool fSetScrollbar) { + m_ts.ActiveFrameIndex = ifrm; + OnActiveFrameChanged(fSetScrollbar); + } + + private void OnActiveFrameSetChanged() { + int nMax; + if (m_ts.ActiveFrameSet == null) + nMax = 0; + else + nMax = m_ts.ActiveFrameSet.Count - 1; + sbh.Minimum = 0; + sbh.Maximum = nMax; + spnFrameNumber.Maximum = nMax; + SetActiveFrame(m_ts.ActiveFrameIndex); + } + + private void OnActiveFrameChanged(bool fSetScrollbar) { + if (fSetScrollbar) + sbh.Value = m_ts.ActiveFrameIndex; + spnFrameNumber.Value = m_ts.ActiveFrameIndex; + PaintFrame(); + if (m_ts.ActiveFrameSet != null) { + Bitmap bm = m_ts.ActiveFrameSet[m_ts.ActiveFrameIndex].Bitmap; + lblFrameInfo.Text = string.Format("Anim: {0}\nSet: {1}\nFrame: {2} of {3}\n({4}x{5})", + m_ts.ActiveAnim.Name, m_ts.ActiveFrameSet.Name, m_ts.ActiveFrameIndex + 1, m_ts.ActiveFrameSet.Count, bm.Width, bm.Height); + } else { + lblFrameInfo.Text = ""; + } + } + + private Rectangle GetAnimViewRect() { + return new Rectangle(0, 0, panel1.Width, panel1.Height - sbh.Height); + } + + private void mniEditScript_Click(object sender, System.EventArgs e) { + ScriptEditor se = new ScriptEditor(m_se, m_strScript); + se.ScriptEditorClosing += new EventHandler(this.OnScriptEditorClosing); + se.Show(); + } + + private void UpdateFrameList() { + m_alstFrames.Clear(); + + foreach (DictionaryEntry deAnimSet in m_anis.Items) { + Anim ani = (Anim)deAnimSet.Value; + foreach (DictionaryEntry deAnim in ani.Items) { + FrameSet frms = (FrameSet)deAnim.Value; + foreach (Frame frm in frms) { + m_alstFrames.Add(frm); + } + } + } + } + + private void trvAnimSet_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e) + { + if (e.Button != MouseButtons.Right) + return; + m_trnMenued = trvAnimSet.GetNodeAt(e.X, e.Y); + trvAnimSet.SelectedNode = m_trnMenued; + mnuTreeView.Show(trvAnimSet, new Point(e.X, e.Y)); + } + + // PropertyInspector stuff + + private void mniTreeViewProperties_Click(object sender, System.EventArgs e) { + ShowProperties(ActiveFrame); + } + + private void OnPropertyValueChanged(object obSender, PropertyValueChangedEventArgs ea) { + PaintFrame(); + } + + // + + private void mniOptions_Popup(object sender, System.EventArgs e) { + mniShowOrigin.Checked = m_fShowOrigin; + mniShowGrid.Checked = m_fShowGrid; + mniShowBOverA.Checked = m_fShowBOverA; + mniMapSideColors.Checked = m_fMapSideColors; + } + + private void Gui_Closed(object sender, System.EventArgs e) { + TextWriter twtr = new StreamWriter(m_strAedDir + @"\AED.config"); + XmlSerializer xser = new XmlSerializer(typeof(Settings)); + Settings settings = new Settings(); + settings.nPreviewSize = m_nScale; + settings.fShowOrigin = m_fShowOrigin; + settings.fShowGrid = m_fShowGrid; + settings.nArgbBackground = m_clrBackground.ToArgb(); + settings.fShowBOverA = m_fShowBOverA; + settings.fMapSideColors = m_fMapSideColors; + xser.Serialize(twtr, settings); + twtr.Close(); + } + + private void SetPreviewPanelBackColor(Color clr) { + m_clrBackground = clr; + } + + private void mniShowBOverA_Click(object sender, System.EventArgs e) { + m_fShowBOverA = !m_fShowBOverA; + PaintFrame(); + } + + private void rbtnA_CheckedChanged(object sender, System.EventArgs e) { + if (rbtnA.Checked) { + m_ts.Activate(0); + OnActiveFrameSetChanged(); + } + } + + private void rbtnB_CheckedChanged(object sender, System.EventArgs e) { + if (rbtnB.Checked) { + m_ts.Activate(1); + OnActiveFrameSetChanged(); + } + } + + private void panel1_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e) { + if ((ModifierKeys & Keys.Shift) != 0) { + m_fDraggingBackground = true; + m_ptPreDragBackgroundOffset = m_ptBackgroundOffset; + m_ptDragStart = FxyFromWxy(new Point(e.X, e.Y)); + } + } + + private void panel1_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e) { + if (m_fDraggingBackground) + m_fDraggingBackground = false; + } + + private void panel1_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e) { + Point ptFrame = FxyFromWxy(new Point(e.X, e.Y)); + if (m_fDraggingBackground) { + m_ptBackgroundOffset.X = m_ptPreDragBackgroundOffset.X + ptFrame.X - m_ptDragStart.X; + m_ptBackgroundOffset.Y = m_ptPreDragBackgroundOffset.Y + ptFrame.Y - m_ptDragStart.Y; + PaintFrame(); + } + + if (m_ts.ActiveFrameSet == null) + return; + + FrameSet frms = m_ts.ActiveFrameSet; + Frame frm = frms[m_ts.ActiveFrameIndex]; + Point ptBitmap = new Point(ptFrame.X + frm.OriginX, ptFrame.Y + frm.OriginY); + Bitmap bm = frm.Bitmap; + Color clr; + if (ptBitmap.X < 0 || ptBitmap.X >= bm.Width || ptBitmap.Y < 0 || ptBitmap.Y >= bm.Height) + clr = Color.Black; + else + clr = bm.GetPixel(ptBitmap.X, ptBitmap.Y); + lblTest.Text = string.Format("x: {0}, y: {1}\nrgb: {2}, {3}, {4}", ptFrame.X, ptFrame.Y, clr.R, clr.G, clr.B); + + } + + private void mniMapSideColors_Click(object sender, System.EventArgs e) { + m_fMapSideColors = !m_fMapSideColors; + PaintFrame(); + } + + private void nudFrameRate_ValueChanged(object sender, System.EventArgs e) { + SetFrameRate((int)nudFrameRate.Value); + } + + private void nudFrameRate_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e) { + SetFrameRate((int)nudFrameRate.Value); + } + + private void mniSetBackgroundBitmap_Click(object sender, System.EventArgs e) { + if (openBitmapDialog.ShowDialog() != DialogResult.OK) + return; + + if (openBitmapDialog.FileName == null) + return; + + m_ts = new ToggleableState(); + + m_bmBackground = new Bitmap(openBitmapDialog.FileName); + PaintFrame(); + } + + class ToggleableState { // ts + public void Activate(int i) { + m_iActive = i; + + if (m_aani[i] == null) + m_aani[i] = m_aani[i ^ 1]; + if (m_afrms[i] == null) { + m_afrms[i] = m_afrms[i ^ 1]; + m_aifrm[i] = m_aifrm[i ^ 1]; + } + } + + public int Active { + get { + return m_iActive; + } + } + + public Anim ActiveAnim { + get { + return m_aani[m_iActive]; + } + set { + m_aani[m_iActive] = value; + } + } + + public FrameSet ActiveFrameSet { + get { + return m_afrms[m_iActive]; + } + set { + m_afrms[m_iActive] = value; + } + } + + public int ActiveFrameIndex { + get { + return m_aifrm[m_iActive]; + } + set { + m_aifrm[m_iActive] = value; + } + } + + public Anim[] AnimArray { + get { + return m_aani; + } + } + + public FrameSet[] FrameSetArray { + get { + return m_afrms; + } + } + + public int[] FrameIndexArray { + get { + return m_aifrm; + } + } + + private Anim[] m_aani = new Anim[2]; + private FrameSet[] m_afrms = new FrameSet[2]; + private int[] m_aifrm = new int[2]; + private int m_iActive = 0; + } + } + + /// + /// + /// + public class FrameSetComparer : IComparer { + + /// + /// + /// + /// + /// + /// + public int Compare(object obA, object obB) { + FrameSet frmsA = (FrameSet)obA; + FrameSet frmsB = (FrameSet)obB; + + try { + int nA = Int32.Parse(frmsA.Name); + int nB = Int32.Parse(frmsB.Name); + return nA - nB; + } catch (FormatException) { + return frmsA.Name.CompareTo(frmsB.Name); + } + } + } + + /// + /// + /// + public class Settings { + /// + /// + /// + public int nVersion; + + /// + /// + /// + public int nPreviewSize; + + /// + /// + /// + public bool fShowOrigin; + + /// + /// + /// + public int nArgbBackground; + + /// + /// + /// + public bool fShowGrid; + + /// + /// + /// + public bool fShowBOverA; + + /// + /// + /// + public bool fMapSideColors; + } +} diff --git a/aed/Gui.resx b/aed/Gui.resx new file mode 100644 index 0000000..979ee2b --- /dev/null +++ b/aed/Gui.resx @@ -0,0 +1,195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAACdJREFUOE9jYBh2 + 4D/QR/gwQQ+PGoA/AEHhQxAMfCASdOKoAswQAABZvj/BKbRZVQAAAABJRU5ErkJggg== + + + + 137, 17 + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFpTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj0xLjAuMzMw + MC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZT + eXN0ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMA + AACYBwAAAk1TRnQBSQFMAgEBAwEAAQUBAAEEAQABEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQC + AAEoAwABQAMAASADAAEBAQABCAYAAQgYAAGAAgABgAMAAoABAAGAAwABgAEAAYABAAKAAgADwAEAAcAB + 3AHAAQAB8AHKAaYBAAEzBQABMwEAATMBAAEzAQACMwIAAxYBAAMcAQADIgEAAykBAANVAQADTQEAA0IB + AAM5AQABgAF8Af8BAAJQAf8BAAGTAQAB1gEAAf8B7AHMAQABxgHWAe8BAAHWAucBAAGQAakBrQIAAf8B + MwMAAWYDAAGZAwABzAIAATMDAAIzAgABMwFmAgABMwGZAgABMwHMAgABMwH/AgABZgMAAWYBMwIAAmYC + AAFmAZkCAAFmAcwCAAFmAf8CAAGZAwABmQEzAgABmQFmAgACmQIAAZkBzAIAAZkB/wIAAcwDAAHMATMC + AAHMAWYCAAHMAZkCAALMAgABzAH/AgAB/wFmAgAB/wGZAgAB/wHMAQABMwH/AgAB/wEAATMBAAEzAQAB + ZgEAATMBAAGZAQABMwEAAcwBAAEzAQAB/wEAAf8BMwIAAzMBAAIzAWYBAAIzAZkBAAIzAcwBAAIzAf8B + AAEzAWYCAAEzAWYBMwEAATMCZgEAATMBZgGZAQABMwFmAcwBAAEzAWYB/wEAATMBmQIAATMBmQEzAQAB + MwGZAWYBAAEzApkBAAEzAZkBzAEAATMBmQH/AQABMwHMAgABMwHMATMBAAEzAcwBZgEAATMBzAGZAQAB + MwLMAQABMwHMAf8BAAEzAf8BMwEAATMB/wFmAQABMwH/AZkBAAEzAf8BzAEAATMC/wEAAWYDAAFmAQAB + MwEAAWYBAAFmAQABZgEAAZkBAAFmAQABzAEAAWYBAAH/AQABZgEzAgABZgIzAQABZgEzAWYBAAFmATMB + mQEAAWYBMwHMAQABZgEzAf8BAAJmAgACZgEzAQADZgEAAmYBmQEAAmYBzAEAAWYBmQIAAWYBmQEzAQAB + ZgGZAWYBAAFmApkBAAFmAZkBzAEAAWYBmQH/AQABZgHMAgABZgHMATMBAAFmAcwBmQEAAWYCzAEAAWYB + zAH/AQABZgH/AgABZgH/ATMBAAFmAf8BmQEAAWYB/wHMAQABzAEAAf8BAAH/AQABzAEAApkCAAGZATMB + mQEAAZkBAAGZAQABmQEAAcwBAAGZAwABmQIzAQABmQEAAWYBAAGZATMBzAEAAZkBAAH/AQABmQFmAgAB + mQFmATMBAAGZATMBZgEAAZkBZgGZAQABmQFmAcwBAAGZATMB/wEAApkBMwEAApkBZgEAA5kBAAKZAcwB + AAKZAf8BAAGZAcwCAAGZAcwBMwEAAWYBzAFmAQABmQHMAZkBAAGZAswBAAGZAcwB/wEAAZkB/wIAAZkB + /wEzAQABmQHMAWYBAAGZAf8BmQEAAZkB/wHMAQABmQL/AQABzAMAAZkBAAEzAQABzAEAAWYBAAHMAQAB + mQEAAcwBAAHMAQABmQEzAgABzAIzAQABzAEzAWYBAAHMATMBmQEAAcwBMwHMAQABzAEzAf8BAAHMAWYC + AAHMAWYBMwEAAZkCZgEAAcwBZgGZAQABzAFmAcwBAAGZAWYB/wEAAcwBmQIAAcwBmQEzAQABzAGZAWYB + AAHMApkBAAHMAZkBzAEAAcwBmQH/AQACzAIAAswBMwEAAswBZgEAAswBmQEAA8wBAALMAf8BAAHMAf8C + AAHMAf8BMwEAAZkB/wFmAQABzAH/AZkBAAHMAf8BzAEAAcwC/wEAAcwBAAEzAQAB/wEAAWYBAAH/AQAB + mQEAAcwBMwIAAf8CMwEAAf8BMwFmAQAB/wEzAZkBAAH/ATMBzAEAAf8BMwH/AQAB/wFmAgAB/wFmATMB + AAHMAmYBAAH/AWYBmQEAAf8BZgHMAQABzAFmAf8BAAH/AZkCAAH/AZkBMwEAAf8BmQFmAQAB/wKZAQAB + /wGZAcwBAAH/AZkB/wEAAf8BzAIAAf8BzAEzAQAB/wHMAWYBAAH/AcwBmQEAAf8CzAEAAf8BzAH/AQAC + /wEzAQABzAH/AWYBAAL/AZkBAAL/AcwBAAJmAf8BAAFmAf8BZgEAAWYC/wEAAf8CZgEAAf8BZgH/AQAC + /wFmAQABIQEAAaUBAANfAQADdwEAA4YBAAOWAQADywEAA7IBAAPXAQAD3QEAA+MBAAPqAQAD8QEAA/gB + AAHwAfsB/wEAAaQCoAEAA4ADAAH/AgAB/wMAAv8BAAH/AwAB/wEAAf8BAAL/AgAD//8A/wD/AP8A/wD/ + AP8A/wAJAAFCAU0BPgcAAT4DAAEoAwABQAMAASADAAEBAQABAQYAAQEWAAP/gQAG/wIABv8CAAb/AgAG + /wIAAv8B8wH/AYABBwIAAeABHwHxAf8BAAEDAgAB4AEfAfAB/wE/AfMCAAHgAR8B8AF/ATsB8wIAAeAB + HwHwAT8BOQHzAgAB4AEfAfABPwE4AfMCAAHgAR8B8AF/AQABQwIAAeABHwHwAf8BgAFHAgAB4AEfAfEB + /wH4Af8CAAL/AfMB/wH5Af8CAAT/AfsB/wIABv8CAAs= + + + + 242, 17 + + + 373, 17 + + + 505, 17 + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAADtJREFUOE9jYBiM + 4D/QUSBMNoAZQLYhyAaQZQi6ASQbgs0AkgyhiQEkxQhVA5Ekm2GKKU5IZNk6hDUBAPEhJ9lpJIA8AAAA + AElFTkSuQmCC + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAFFJREFUOE9jYKAR + +E+puSADKDIEZgDRhiBrwMYG+QinoYQ0w1yBrg4eTNj8i88LGOrxGYAtMog2AFdMEmUAvmSA0wBiApOi + mCA5bVCamoeKfgBzvU2zp/fg5wAAAABJRU5ErkJggg== + + + + 573, 17 + + + 682, 17 + + + 17, 54 + + + 139, 54 + + + 233, 54 + + + Gui + + \ No newline at end of file diff --git a/aed/PropertyInspector.cs b/aed/PropertyInspector.cs new file mode 100644 index 0000000..9e19c6c --- /dev/null +++ b/aed/PropertyInspector.cs @@ -0,0 +1,92 @@ +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; + +namespace AED +{ + /// + /// Summary description for PropertyInspector. + /// + public class PropertyInspector : System.Windows.Forms.Form { + private System.Windows.Forms.PropertyGrid prpg; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + /// + /// + /// + /// + public PropertyInspector(object obInspectee) { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + // + // TODO: Add any constructor code after InitializeComponent call + // + + prpg.SelectedObject = obInspectee; + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) { + if( disposing ) { + if(components != null) { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this.prpg = new System.Windows.Forms.PropertyGrid(); + this.SuspendLayout(); + // + // prpg + // + this.prpg.CommandsVisibleIfAvailable = true; + this.prpg.Dock = System.Windows.Forms.DockStyle.Fill; + this.prpg.LargeButtons = false; + this.prpg.LineColor = System.Drawing.SystemColors.ScrollBar; + this.prpg.Name = "prpg"; + this.prpg.Size = new System.Drawing.Size(280, 429); + this.prpg.TabIndex = 0; + this.prpg.Text = "propertyGrid1"; + this.prpg.ViewBackColor = System.Drawing.SystemColors.Window; + this.prpg.ViewForeColor = System.Drawing.SystemColors.WindowText; + // + // PropertyInspector + // + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.ClientSize = new System.Drawing.Size(280, 429); + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.prpg}); + this.Name = "PropertyInspector"; + this.Text = "Property Inspector"; + this.ResumeLayout(false); + + } + #endregion + + public event PropertyValueChangedEventHandler PropertyValueChanged { + add { + prpg.PropertyValueChanged += value; + } + remove { + prpg.PropertyValueChanged -= value; + } + } + } +} diff --git a/aed/PropertyInspector.resx b/aed/PropertyInspector.resx new file mode 100644 index 0000000..495a4aa --- /dev/null +++ b/aed/PropertyInspector.resx @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PropertyInspector + + + text/microsoft-resx + + + 1.0.0.0 + + + System.Resources.ResXResourceReader + + + System.Resources.ResXResourceWriter + + \ No newline at end of file diff --git a/aed/Script.cs b/aed/Script.cs new file mode 100644 index 0000000..849754c --- /dev/null +++ b/aed/Script.cs @@ -0,0 +1,224 @@ +// NOTES: +// on attempting to call a script-defined JScript function -- +// - can't find an instance to call the function methods via +// - function methods expect a 'this' (type unknown), an engine instance, and a parameter array +// - the containing class type is called "JScript 1" and the number increments with each instatiation of the script engine +// - JScript 1 contains another method called "Global Code" which appears to be the, uh, global script code +// - the .ctor for JScript 1 requires an arg, Global Scope +// - easiest thing to do seems to be to create a scaffolding class around user script code that +// puts the global code in a constructor or other well-known method and results in user-defined +// functions being members of this class. Then it can be instanced and its members easily +// enumerated. But what of user-defined classes, etc? P.S. I've verified that members of such +// a wrapped class have the necessary context when called to do JScript-y things like +// calling "print". +// - JScript Engine appears to have an option to keep it from generating the _Startup class +// - JScript has other interesting options -- find some documentation +// - the above approach is jscript-specific. Each plugged-in script language would need its +// own wrapper-generator. Another approach would be to establish a well known name (e.g., Macros) +// and leave it to script writers to provide the class definition. +// UNDONE: rework Compile/Run so Run() leaves the engine running and returns an instance +// of the script.Macros class. + +/* Sample scripts: + +// 1. Offset all frames + +for (frm in AED.Frames) { + frm.OriginX -= 16; + frm.OriginY -= 15; +} + +// 2. Change a color in all frames + +for (frm in AED.Frames) { + var clrOld = new Array(156, 208, 248) + var clrNew = new Array(156, 212, 248) + frm.ReplaceColor(clrOld, clrNew, true) +} + +// 3. Change a bunch of colors in all frames + +for (frm in AED.Frames) { + var clrOld = new Array(0,114,232) + var clrNew = new Array(0,116,232) + frm.ReplaceColor(clrOld, clrNew, true) + + clrOld = new Array(0,112,232) + clrNew = new Array(0,116,232) + frm.ReplaceColor(clrOld, clrNew, true) + + clrOld = new Array(0,96,192) + clrNew = new Array(0,96,196) + frm.ReplaceColor(clrOld, clrNew, true) + + clrOld = new Array(0,48,88) + clrNew = new Array(0,48,92) + frm.ReplaceColor(clrOld, clrNew, true) +} + +*/ + + +using System; +using System.Collections; +using Microsoft.Vsa; +using Microsoft.JScript; +using System.Windows.Forms; +using System.Diagnostics; +using System.Reflection; + +namespace AED +{ + /// + /// Summary description for Script. + /// + + public delegate void CompileErrorEventHandler(object obSender, IVsaError vsaerr); + + /// + /// + /// + public class ScriptEngine : IVsaSite { + Microsoft.JScript.Vsa.VsaEngine m_vsae = null; + IVsaCodeItem m_vsaciScript; + Hashtable m_htEventSources = new Hashtable(); + Hashtable m_htGlobals = new Hashtable(); + + public ScriptEngine() { + } + + public void Init() { + // UNDONE: this is crap. The whole engine shouldn't have to be recreated and + // reinitialized for each compile. Unfortunately it's the only way I've found + // to work around the "Variable 'AED' has not been declared" problem. + // A better solution needs to be found as this one seems to consume roughly + // 240K of RAM each compile. + + m_vsae = new Microsoft.JScript.Vsa.VsaEngine(); + m_vsae.RootMoniker = "com.spiffcode://script"; + m_vsae.Site = this; + m_vsae.InitNew(); + + m_vsae.SetOption("print", true); // Enable the 'print' function for scripters + m_vsae.SetOption("fast", false); // Enable legacy mode + m_vsae.RootNamespace = "script"; + m_vsaciScript = (IVsaCodeItem)m_vsae.Items.CreateItem("scriptspace", + VsaItemType.Code, VsaItemFlag.None); + + m_vsae.Items.CreateItem("mscorlib.dll", VsaItemType.Reference, VsaItemFlag.None); + m_vsae.Items.CreateItem("System.Windows.Forms.dll", VsaItemType.Reference, VsaItemFlag.None); +// m_vsae.Items.CreateItem("System.dll", VsaItemType.Reference, VsaItemFlag.None); + + string[] astrNames = new String[m_htGlobals.Count]; + m_htGlobals.Keys.CopyTo(astrNames, 0); + + for (int i = 0; i < m_htGlobals.Count; i++) { + IVsaGlobalItem vsagi = (IVsaGlobalItem)m_vsae.Items.CreateItem(astrNames[i], + VsaItemType.AppGlobal, VsaItemFlag.None); + // UNDONE: this doesn't seem to be working + vsagi.ExposeMembers = true; +// object obInstance = m_htGlobals[astrNames[i]]; +// vsagi.TypeString = obInstance.GetType().FullName; + } + } + + public bool Compile(string strScript) { + if (m_vsae != null) { + m_vsae.Close(); + m_vsae = null; + } + + Init(); + + m_vsaciScript.SourceText = WrapUserScript(strScript); + return m_vsae.Compile(); + } + + public bool Run() { + Debug.Assert(m_vsae != null); + + try { + m_vsae.Run(); + +#if false + Assembly mbly = m_vsae.Assembly; + object ob = mbly.CreateInstance("script.Foo"); + Type[] atyp = mbly.GetTypes(); + Type typMain = atyp[0]; + MemberInfo[] amthi = typMain.GetMembers(); + foreach (MemberInfo mthi in amthi) { + MessageBox.Show(mthi.ToString()); + } + object obT = mbly.CreateInstance(atyp[0].Name); + MessageBox.Show(obT.ToString()); + obT = null; + atyp = null; + mbly = null; +#endif + +// } catch (VsaException exVsa) { +// return false; + } finally { + m_vsae.Close(); + m_vsae = null; + OnScriptDone(EventArgs.Empty); + } + return true; + } + + private string WrapUserScript(string strUserScript) { + return strUserScript; +// return "class Macros {\n" + strUserScript + "\n}"; + } + + public void AddGlobal(string strName, object obInstance) { + m_htGlobals.Add(strName, obInstance); + } + + public event EventHandler ScriptDone; + + protected virtual void OnScriptDone(EventArgs evta) { + if (ScriptDone != null) + ScriptDone(this, evta); + } + + public event CompileErrorEventHandler CompileError; + + protected virtual void OnCompileError(IVsaError vsaerr) { + if (CompileError != null) + CompileError(this, vsaerr); + else + MessageBox.Show("line " + vsaerr.Line + ": " + vsaerr.Description, "Compile Error"); + } + + // + // IVsaSite implementation + // + + public void AddEventSource(string strName, object obEventSource) { + m_htEventSources.Add(strName, obEventSource); + } + + public void GetCompiledState(out byte[] pe, out byte[] debugInfo) { + pe = null; + debugInfo = null; + } + + public object GetEventSourceInstance(string itemName, string strEventSourceName) { + return m_htEventSources[strEventSourceName]; + } + + public object GetGlobalInstance(string strName) { + return m_htGlobals[strName]; + } + + public void Notify(string notify, object info) { + } + + public bool OnCompilerError(IVsaError vsaerr) { + OnCompileError(vsaerr); +// throw new Exception("line " + vsaerr.Line + ": " + vsaerr.Description + "\n" + vsaerr.LineText); + return true; + } + } +} diff --git a/aed/ScriptEditor.cs b/aed/ScriptEditor.cs new file mode 100644 index 0000000..984ea89 --- /dev/null +++ b/aed/ScriptEditor.cs @@ -0,0 +1,490 @@ +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; +using Microsoft.Vsa; +using System.IO; + +namespace AED +{ + /// + /// Summary description for ScriptEditor. + /// + public class ScriptEditor : System.Windows.Forms.Form + { + private System.Windows.Forms.MainMenu mainMenu1; + private System.Windows.Forms.RichTextBox rtbEdit; + private System.Windows.Forms.RichTextBox rtbOutput; + private System.Windows.Forms.MenuItem menuItem1; + private System.Windows.Forms.MenuItem mniCompile; + private System.Windows.Forms.MenuItem mniRun; + private System.Windows.Forms.Splitter splitter1; + private System.Windows.Forms.MenuItem menuItem2; + private System.Windows.Forms.MenuItem menuItem4; + private System.Windows.Forms.MenuItem menuItem5; + private System.Windows.Forms.MenuItem mniOpen; + private System.Windows.Forms.OpenFileDialog openFileDialog; + private System.Windows.Forms.StatusBar stb; + private System.Windows.Forms.StatusBarPanel stbpText; + private System.Windows.Forms.StatusBarPanel stbpLine; + private System.Windows.Forms.StatusBarPanel stbpColumn; + private System.Windows.Forms.MenuItem menuItem3; + private System.Windows.Forms.MenuItem mniLibraryColorMapper; + private System.Windows.Forms.MenuItem mniLibraryOriginOffsetter; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + /// + /// + /// + /// + /// + public ScriptEditor(ScriptEngine se, string strScript) + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + // Non-Designer initialization + + m_se = se; + m_se.CompileError += new CompileErrorEventHandler(OnCompileError); + Script = strScript; + + m_tbwtr = new TextBoxWriter(rtbOutput); +// m_tbwtr.WriteLine("s: {0}, l: {1}", rtbEdit.SelectionStart, rtbEdit.SelectionLength); + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.openFileDialog = new System.Windows.Forms.OpenFileDialog(); + this.mniOpen = new System.Windows.Forms.MenuItem(); + this.stbpColumn = new System.Windows.Forms.StatusBarPanel(); + this.splitter1 = new System.Windows.Forms.Splitter(); + this.mainMenu1 = new System.Windows.Forms.MainMenu(); + this.menuItem2 = new System.Windows.Forms.MenuItem(); + this.menuItem4 = new System.Windows.Forms.MenuItem(); + this.menuItem5 = new System.Windows.Forms.MenuItem(); + this.menuItem1 = new System.Windows.Forms.MenuItem(); + this.mniCompile = new System.Windows.Forms.MenuItem(); + this.mniRun = new System.Windows.Forms.MenuItem(); + this.menuItem3 = new System.Windows.Forms.MenuItem(); + this.mniLibraryColorMapper = new System.Windows.Forms.MenuItem(); + this.mniLibraryOriginOffsetter = new System.Windows.Forms.MenuItem(); + this.rtbEdit = new System.Windows.Forms.RichTextBox(); + this.stbpLine = new System.Windows.Forms.StatusBarPanel(); + this.stb = new System.Windows.Forms.StatusBar(); + this.stbpText = new System.Windows.Forms.StatusBarPanel(); + this.rtbOutput = new System.Windows.Forms.RichTextBox(); + ((System.ComponentModel.ISupportInitialize)(this.stbpColumn)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.stbpLine)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.stbpText)).BeginInit(); + this.SuspendLayout(); + // + // openFileDialog + // + this.openFileDialog.DefaultExt = "js"; + this.openFileDialog.Filter = "EcmaScript/JavaScript (*.js)|*.js|All files (*.*)|*.*"; + this.openFileDialog.Title = "Open Script"; + // + // mniOpen + // + this.mniOpen.Index = 0; + this.mniOpen.Text = "&Open..."; + this.mniOpen.Click += new System.EventHandler(this.mniOpen_Click); + // + // stbpColumn + // + this.stbpColumn.MinWidth = 40; + this.stbpColumn.Width = 50; + // + // splitter1 + // + this.splitter1.Dock = System.Windows.Forms.DockStyle.Bottom; + this.splitter1.Location = new System.Drawing.Point(0, 284); + this.splitter1.Name = "splitter1"; + this.splitter1.Size = new System.Drawing.Size(520, 3); + this.splitter1.TabIndex = 2; + this.splitter1.TabStop = false; + // + // mainMenu1 + // + this.mainMenu1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.menuItem2, + this.menuItem1, + this.menuItem3}); + // + // menuItem2 + // + this.menuItem2.Index = 0; + this.menuItem2.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.mniOpen, + this.menuItem4, + this.menuItem5}); + this.menuItem2.Text = "&File"; + // + // menuItem4 + // + this.menuItem4.Enabled = false; + this.menuItem4.Index = 1; + this.menuItem4.Text = "&Save"; + // + // menuItem5 + // + this.menuItem5.Enabled = false; + this.menuItem5.Index = 2; + this.menuItem5.Text = "Save &As..."; + // + // menuItem1 + // + this.menuItem1.Index = 1; + this.menuItem1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.mniCompile, + this.mniRun}); + this.menuItem1.Text = "Script"; + // + // mniCompile + // + this.mniCompile.Index = 0; + this.mniCompile.Shortcut = System.Windows.Forms.Shortcut.F7; + this.mniCompile.Text = "&Compile"; + this.mniCompile.Click += new System.EventHandler(this.mniCompile_Click); + // + // mniRun + // + this.mniRun.Index = 1; + this.mniRun.Shortcut = System.Windows.Forms.Shortcut.F5; + this.mniRun.Text = "&Run"; + this.mniRun.Click += new System.EventHandler(this.mniRun_Click); + // + // menuItem3 + // + this.menuItem3.Index = 2; + this.menuItem3.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.mniLibraryColorMapper, + this.mniLibraryOriginOffsetter}); + this.menuItem3.Text = "&Library"; + // + // mniLibraryColorMapper + // + this.mniLibraryColorMapper.Index = 0; + this.mniLibraryColorMapper.Text = "&Color Mapper"; + this.mniLibraryColorMapper.Click += new System.EventHandler(this.mniLibraryColorMapper_Click); + // + // mniLibraryOriginOffsetter + // + this.mniLibraryOriginOffsetter.Index = 1; + this.mniLibraryOriginOffsetter.Text = "&Origin Offsetter"; + this.mniLibraryOriginOffsetter.Click += new System.EventHandler(this.mniLibraryOriginOffsetter_Click); + // + // rtbEdit + // + this.rtbEdit.Dock = System.Windows.Forms.DockStyle.Fill; + this.rtbEdit.Name = "rtbEdit"; + this.rtbEdit.Size = new System.Drawing.Size(520, 383); + this.rtbEdit.TabIndex = 0; + this.rtbEdit.Text = ""; + this.rtbEdit.WordWrap = false; + this.rtbEdit.SelectionChanged += new System.EventHandler(this.rtbEdit_SelectionChanged); + // + // stbpLine + // + this.stbpLine.MinWidth = 40; + this.stbpLine.Width = 50; + // + // stb + // + this.stb.Location = new System.Drawing.Point(0, 383); + this.stb.Name = "stb"; + this.stb.Panels.AddRange(new System.Windows.Forms.StatusBarPanel[] { + this.stbpText, + this.stbpLine, + this.stbpColumn}); + this.stb.ShowPanels = true; + this.stb.Size = new System.Drawing.Size(520, 20); + this.stb.TabIndex = 3; + // + // stbpText + // + this.stbpText.AutoSize = System.Windows.Forms.StatusBarPanelAutoSize.Spring; + this.stbpText.Width = 404; + // + // rtbOutput + // + this.rtbOutput.Dock = System.Windows.Forms.DockStyle.Bottom; + this.rtbOutput.Location = new System.Drawing.Point(0, 287); + this.rtbOutput.Name = "rtbOutput"; + this.rtbOutput.Size = new System.Drawing.Size(520, 96); + this.rtbOutput.TabIndex = 1; + this.rtbOutput.Text = ""; + this.rtbOutput.WordWrap = false; + // + // ScriptEditor + // + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.ClientSize = new System.Drawing.Size(520, 403); + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.splitter1, + this.rtbOutput, + this.rtbEdit, + this.stb}); + this.Menu = this.mainMenu1; + this.Name = "ScriptEditor"; + this.Text = "Script Editor"; + this.Closing += new System.ComponentModel.CancelEventHandler(this.ScriptEditor_Closing); + ((System.ComponentModel.ISupportInitialize)(this.stbpColumn)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.stbpLine)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.stbpText)).EndInit(); + this.ResumeLayout(false); + + } + #endregion + + // + // Non-Windows.Forms state + // + + private ScriptEngine m_se; + private TextBoxWriter m_tbwtr; + + // + // Public Properties + // + + public string Script { + get { + return rtbEdit.Text; + } + + set { + rtbEdit.Text = value; + UpdateLineColumnIndicator(); + + // For some reason this can't be set until after it has been read (inside + // of UpdateLineColumnIndicator in this case). Otherwise a "SelectionStart + // argument (-1) invalid" exception is raised. + + rtbEdit.SelectionLength = 0; + } + } + + public event EventHandler ScriptEditorClosing; + + // + // UI event handlers + // + + private void mniCompile_Click(object sender, System.EventArgs e) { + ClearErrors(); + ClearOutput(); + // UNDONE: clear colored (error) text + bool fSuccess = false; +// try { + fSuccess = m_se.Compile(rtbEdit.Text); +// } catch (Exception ex) { +// WriteLine("Errors found during compile:\n{0}", ex.Message); +// return; +// } + + if (fSuccess) + WriteLine("No errors"); + } + + private void mniRun_Click(object sender, System.EventArgs e) { + ClearErrors(); + ClearOutput(); + TextWriter twtrConsoleOut = Console.Out; + Console.SetOut(m_tbwtr); + + // UNDONE: clear colored (error) text + // try { + m_se.Compile(rtbEdit.Text); +// } catch { +// return; +// } + m_se.Run(); + + Console.SetOut(twtrConsoleOut); + } + + private void mniOpen_Click(object sender, System.EventArgs e) { + if (openFileDialog.ShowDialog() != DialogResult.OK) + return; + rtbEdit.LoadFile(openFileDialog.FileName); + } + + // UNDONE: somehow determine where the insertion point really is and use it, not the + // selection start. + private void rtbEdit_SelectionChanged(object sender, System.EventArgs e) { + UpdateLineColumnIndicator(); + } + + // + // Everything else + // + + protected virtual void OnScriptEditorClosing(EventArgs evta) { + if (ScriptEditorClosing != null) + ScriptEditorClosing(this, evta); + m_se.CompileError -= new CompileErrorEventHandler(OnCompileError); + } + + private void OnCompileError(object obSender, IVsaError vsaerr) { + WriteLine("({0},{1}): sev {4}, {5} {2:x}: {3}", vsaerr.Line, vsaerr.StartColumn, + vsaerr.Number, vsaerr.Description, vsaerr.Severity, vsaerr.Severity > 0 ? "warning" : "error"); + int ichSelStart = rtbEdit.SelectionStart; + int ichSelLength = rtbEdit.SelectionLength; + int ichT = GetCharIndexFromLine(rtbEdit.Text, vsaerr.Line - 1); + rtbEdit.SelectionStart = ichT + vsaerr.StartColumn - 1; // columns are numbered starting at 1 + rtbEdit.SelectionLength = vsaerr.EndColumn - vsaerr.StartColumn; + rtbEdit.SelectionColor = Color.FromArgb(255, 0, 128); + rtbEdit.SelectionStart = ichSelStart; + rtbEdit.SelectionLength = ichSelLength; + } + + private int GetCharIndexFromLine(string strText, int iLine) { + if (iLine == 0) + return 0; + int cLines = 0; + for (int i = 0; i < strText.Length; i++) { + if (strText[i] == '\n') { + cLines++; + if (cLines == iLine) + return i + 1; + } + } + return 0; + } + + private void ClearOutput() { + rtbOutput.Clear(); + } + + private void ClearErrors() { + string strT = rtbEdit.Text; + int ichSelStart = rtbEdit.SelectionStart; + int ichSelLength = rtbEdit.SelectionLength; + rtbEdit.Text = null; + rtbEdit.Text = strT; + rtbEdit.SelectionStart = ichSelStart; + rtbEdit.SelectionLength = ichSelLength; + } + + private void WriteLine(string strFormat, params object[] aobParams) { + Write(strFormat, aobParams); + Write("\n"); + } + + private void Write(string strFormat, params object[] aobParams) { + string strT = string.Format(strFormat, aobParams); + rtbOutput.Text += strT; + } + + private void ScriptEditor_Closing(object sender, System.ComponentModel.CancelEventArgs e) { + OnScriptEditorClosing(EventArgs.Empty); + } + + + private void UpdateLineColumnIndicator() { + int ichInsertionPoint = rtbEdit.SelectionStart; + int iLine = rtbEdit.GetLineFromCharIndex(ichInsertionPoint) + 1; + stbpLine.Text = "Ln " + iLine; + int ich = ichInsertionPoint; + while (--ich >= 0 && rtbEdit.Text[ich] != '\n'); + stbpColumn.Text = "Col " + (ichInsertionPoint - ich); + } + + private void mniLibraryColorMapper_Click(object sender, System.EventArgs e) { + rtbEdit.AppendText(@"// Change a bunch of colors in all frames + +for (frm in AED.Frames) { + var clrOld = new Array(0,114,232) + var clrNew = new Array(0,116,232) + frm.ReplaceColor(clrOld, clrNew, true) + + clrOld = new Array(0,112,232) + clrNew = new Array(0,116,232) + frm.ReplaceColor(clrOld, clrNew, true) + + clrOld = new Array(0,96,192) + clrNew = new Array(0,96,196) + frm.ReplaceColor(clrOld, clrNew, true) + + clrOld = new Array(0,48,88) + clrNew = new Array(0,48,92) + frm.ReplaceColor(clrOld, clrNew, true) +} +"); + } + + private void mniLibraryOriginOffsetter_Click(object sender, System.EventArgs e) { + rtbEdit.AppendText(@"// Offset all frames + +for (frm in AED.Frames) { + frm.OriginX -= 16; + frm.OriginY -= 15; +} +"); + } + } + + // + // Wrap a TextBox control in a TextWriter stream so StdOut, etc output can be + // routed to the TextBox. + // + + class TextBoxWriter : TextWriter { + private TextBoxBase m_tb = null; + + public TextBoxWriter(TextBoxBase tb) { + m_tb = tb; + } + + override public void WriteLine(string str) { + m_tb.Text += str + "\n"; + } + +#if false + override public void WriteLine(string strFormat, params object[] aobParams) { + Write(strFormat, aobParams); + Write("\n"); + } + + override public void Write(string strFormat, params object[] aobParams) { + string strT = string.Format(strFormat, aobParams); + m_tb.Text += strT; + } +#endif + + // Standard property we have to implement (...grumble...) + override public System.Text.Encoding Encoding { + get { + return System.Text.Encoding.ASCII; + } + } + } +} diff --git a/aed/ScriptEditor.resx b/aed/ScriptEditor.resx new file mode 100644 index 0000000..9f5aab2 --- /dev/null +++ b/aed/ScriptEditor.resx @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 126, 17 + + + 17, 17 + + + ScriptEditor + + \ No newline at end of file diff --git a/aed/deploy.bat b/aed/deploy.bat new file mode 100644 index 0000000..7ff58c1 --- /dev/null +++ b/aed/deploy.bat @@ -0,0 +1 @@ +copy bin\debug\aed.exe ..\bin diff --git a/aed/licenses.licx b/aed/licenses.licx new file mode 100644 index 0000000..17ed459 --- /dev/null +++ b/aed/licenses.licx @@ -0,0 +1,23 @@ +System.Windows.Forms.TrackBar, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.PictureBox, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.ToolTip, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.Label, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.DomainUpDown, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.MainMenu, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.Timer, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.ContextMenu, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.StatusBar, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.NumericUpDown, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.Panel, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.Form, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.SaveFileDialog, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.StatusBarPanel, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.OpenFileDialog, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.Splitter, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.Button, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.HScrollBar, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.MenuItem, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.ImageList, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.RichTextBox, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.TreeView, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.PropertyGrid, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 diff --git a/aed/loopforward.png b/aed/loopforward.png new file mode 100644 index 0000000..22ac90d Binary files /dev/null and b/aed/loopforward.png differ diff --git a/aed/play.png b/aed/play.png new file mode 100644 index 0000000..023d4a0 Binary files /dev/null and b/aed/play.png differ diff --git a/aed/playbackcontrols.bmp b/aed/playbackcontrols.bmp new file mode 100644 index 0000000..9624e81 Binary files /dev/null and b/aed/playbackcontrols.bmp differ diff --git a/aed/stop.png b/aed/stop.png new file mode 100644 index 0000000..91549a2 Binary files /dev/null and b/aed/stop.png differ diff --git a/amx2zamx/.cvsignore b/amx2zamx/.cvsignore new file mode 100644 index 0000000..ba077a4 --- /dev/null +++ b/amx2zamx/.cvsignore @@ -0,0 +1 @@ +bin diff --git a/amx2zamx/App.ico b/amx2zamx/App.ico new file mode 100644 index 0000000..3a5525f Binary files /dev/null and b/amx2zamx/App.ico differ diff --git a/amx2zamx/AssemblyInfo.cs b/amx2zamx/AssemblyInfo.cs new file mode 100644 index 0000000..9f89a32 --- /dev/null +++ b/amx2zamx/AssemblyInfo.cs @@ -0,0 +1,58 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.*")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] diff --git a/amx2zamx/amx2zamx.cs b/amx2zamx/amx2zamx.cs new file mode 100644 index 0000000..4e955b6 --- /dev/null +++ b/amx2zamx/amx2zamx.cs @@ -0,0 +1,77 @@ +using System; +using System.IO; +using System.Collections; +using SpiffCode; + +namespace amx2zamx +{ + /// + /// Summary description for App. + /// + class App + { + /// + /// The main entry point for the application. + /// + [STAThread] + static int Main(string[] astrArgs) + { + string strOutputDir = null; + ArrayList alFiles = new ArrayList(); + bool fForce = false; + + if (astrArgs.Length < 1) { + Console.WriteLine("amx2zamx usage:\namx2zamx [-o output dir]"); + return -1; + } + + for (int i = 0; i < astrArgs.Length; i++) { + string str = astrArgs[i]; + + if (str == "-o") { + strOutputDir = astrArgs[++i]; + continue; + } else if (str == "-f") { + fForce = true; + continue; + } + + string strRoot = Path.GetPathRoot(str); + if (strRoot == "") + strRoot = "."; + string strFile = Path.GetFileName(str); + string[] astrFiles = Directory.GetFiles(strRoot, strFile); + if (astrFiles.Length == 0) { + Console.WriteLine("Warning: nothing matches {0}", str); + continue; + } + + alFiles.AddRange(astrFiles); + } + + foreach (string strFile in alFiles) { + if (Path.GetExtension(strFile).ToLower() != ".amx") { + Console.WriteLine("Warning: ignoring {0}", strFile); + continue; + } + + AnimDoc doc = AnimDoc.Load(strFile); + + string strOutFile = Path.GetFileName(strFile); + strOutFile = Path.ChangeExtension(strOutFile, ".zamx"); + if (strOutputDir != null) + strOutFile = Path.Combine(strOutputDir, strOutFile); + + if (File.Exists(strOutFile) && !fForce) { + Console.WriteLine("Warning: {0} already exists, skipping", strOutFile); + continue; + } + + Console.WriteLine("writing {0}", strOutFile); + doc.Save(strOutFile); + } + + return 0; + } + } +} diff --git a/amx2zamx/amx2zamx.csproj b/amx2zamx/amx2zamx.csproj new file mode 100644 index 0000000..71e5bd2 --- /dev/null +++ b/amx2zamx/amx2zamx.csproj @@ -0,0 +1,177 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/amx2zamx/amx2zamx.sln b/amx2zamx/amx2zamx.sln new file mode 100644 index 0000000..772fb1f --- /dev/null +++ b/amx2zamx/amx2zamx.sln @@ -0,0 +1,21 @@ +Microsoft Visual Studio Solution File, Format Version 7.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "amx2zamx", "amx2zamx.csproj", "{4D9B810E-EB6A-44A9-94D4-3EEE1BC90501}" +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + ConfigName.0 = Debug + ConfigName.1 = Release + EndGlobalSection + GlobalSection(ProjectDependencies) = postSolution + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {4D9B810E-EB6A-44A9-94D4-3EEE1BC90501}.Debug.ActiveCfg = Debug|.NET + {4D9B810E-EB6A-44A9-94D4-3EEE1BC90501}.Debug.Build.0 = Debug|.NET + {4D9B810E-EB6A-44A9-94D4-3EEE1BC90501}.Release.ActiveCfg = Release|.NET + {4D9B810E-EB6A-44A9-94D4-3EEE1BC90501}.Release.Build.0 = Release|.NET + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/amx2zamx/deploy.bat b/amx2zamx/deploy.bat new file mode 100644 index 0000000..56fa4b8 --- /dev/null +++ b/amx2zamx/deploy.bat @@ -0,0 +1 @@ +copy bin\debug\amx2zamx.exe ..\bin diff --git a/ani2amx/.cvsignore b/ani2amx/.cvsignore new file mode 100644 index 0000000..ba077a4 --- /dev/null +++ b/ani2amx/.cvsignore @@ -0,0 +1 @@ +bin diff --git a/ani2amx/App.ico b/ani2amx/App.ico new file mode 100644 index 0000000..3a5525f Binary files /dev/null and b/ani2amx/App.ico differ diff --git a/ani2amx/AssemblyInfo.cs b/ani2amx/AssemblyInfo.cs new file mode 100644 index 0000000..9f89a32 --- /dev/null +++ b/ani2amx/AssemblyInfo.cs @@ -0,0 +1,58 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.*")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] diff --git a/ani2amx/ani2amx.cs b/ani2amx/ani2amx.cs new file mode 100644 index 0000000..fb6f2de --- /dev/null +++ b/ani2amx/ani2amx.cs @@ -0,0 +1,33 @@ +using System; +using System.IO; +using SpiffCode; + +namespace acrunch +{ + /// + /// Summary description for Class1. + /// + class App + { + /// + /// The main entry point for the application. + /// + [STAThread] + static int Main(string[] astrArgs) { + foreach (string strFileName in Directory.GetFiles(".", "*.ani")) { + string strImportDir = Path.GetFileNameWithoutExtension(strFileName); + Console.WriteLine("Importing files from {0}", strImportDir); + + AnimDoc doc = new AnimDoc(); + if (!doc.Import(Directory.GetFiles(strImportDir, "*.png"))) { + Console.WriteLine("Error: couldn't import files from dir {0}", strImportDir); + return -1; + } + + Console.WriteLine("Writing {0}.amx", strImportDir); + doc.Save(strImportDir + ".amx"); + } + return 0; + } + } +} diff --git a/ani2amx/ani2amx.csproj b/ani2amx/ani2amx.csproj new file mode 100644 index 0000000..bcd8c24 --- /dev/null +++ b/ani2amx/ani2amx.csproj @@ -0,0 +1,177 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ani2amx/ani2amx.sln b/ani2amx/ani2amx.sln new file mode 100644 index 0000000..2e58273 --- /dev/null +++ b/ani2amx/ani2amx.sln @@ -0,0 +1,21 @@ +Microsoft Visual Studio Solution File, Format Version 7.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ani2amx", "ani2amx.csproj", "{9BA0F30F-802F-4E9F-914C-FCCBBB787699}" +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + ConfigName.0 = Debug + ConfigName.1 = Release + EndGlobalSection + GlobalSection(ProjectDependencies) = postSolution + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {9BA0F30F-802F-4E9F-914C-FCCBBB787699}.Debug.ActiveCfg = Debug|.NET + {9BA0F30F-802F-4E9F-914C-FCCBBB787699}.Debug.Build.0 = Debug|.NET + {9BA0F30F-802F-4E9F-914C-FCCBBB787699}.Release.ActiveCfg = Release|.NET + {9BA0F30F-802F-4E9F-914C-FCCBBB787699}.Release.Build.0 = Release|.NET + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/archive/2bpp.pal b/archive/2bpp.pal new file mode 100644 index 0000000..419ae87 --- /dev/null +++ b/archive/2bpp.pal @@ -0,0 +1,7 @@ +JASC-PAL +0100 +4 +255 255 255 +192 192 192 +128 128 128 +0 0 0 diff --git a/archive/4bpp.pal b/archive/4bpp.pal new file mode 100644 index 0000000..57afaea --- /dev/null +++ b/archive/4bpp.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +255 255 255 +238 238 238 +221 221 221 +204 204 204 +187 187 187 +170 170 170 +153 153 153 +136 136 136 +119 119 119 +102 102 102 +85 85 85 +68 68 68 +51 51 51 +34 34 34 +17 17 17 +0 0 0 diff --git a/archive/8bpp.pal b/archive/8bpp.pal new file mode 100644 index 0000000..36294e3 --- /dev/null +++ b/archive/8bpp.pal @@ -0,0 +1,259 @@ +JASC-PAL +0100 +256 +255 255 255 +255 204 255 +255 153 255 +255 102 255 +255 51 255 +255 0 255 +255 255 204 +255 204 204 +255 153 204 +255 102 204 +255 51 204 +255 0 204 +255 255 153 +255 204 153 +255 153 153 +255 102 153 +255 51 153 +255 0 153 +204 255 255 +204 204 255 +204 153 255 +204 102 255 +204 51 255 +204 0 255 +204 255 204 +204 204 204 +204 153 204 +204 102 204 +204 51 204 +204 0 204 +204 255 153 +204 204 153 +204 153 153 +204 102 153 +204 51 153 +204 0 153 +153 255 255 +153 204 255 +153 153 255 +153 102 255 +153 51 255 +153 0 255 +153 255 204 +153 204 204 +153 153 204 +153 102 204 +153 51 204 +153 0 204 +153 255 153 +153 204 153 +153 153 153 +153 102 153 +153 51 153 +153 0 153 +102 255 255 +102 204 255 +102 153 255 +102 102 255 +102 51 255 +102 0 255 +102 255 204 +102 204 204 +102 153 204 +102 102 204 +102 51 204 +102 0 204 +102 255 153 +102 204 153 +102 153 153 +102 102 153 +102 51 153 +102 0 153 +51 255 255 +51 204 255 +51 153 255 +51 102 255 +51 51 255 +51 0 255 +51 255 204 +51 204 204 +51 153 204 +51 102 204 +51 51 204 +51 0 204 +51 255 153 +51 204 153 +51 153 153 +51 102 153 +51 51 153 +51 0 153 +0 255 255 +0 204 255 +0 153 255 +0 102 255 +0 51 255 +0 0 255 +0 255 204 +0 204 204 +0 153 204 +0 102 204 +0 51 204 +0 0 204 +0 255 153 +0 204 153 +0 153 153 +0 102 153 +0 51 153 +0 0 153 +255 255 102 +255 204 102 +255 153 102 +255 102 102 +255 51 102 +255 0 102 +255 255 51 +255 204 51 +255 153 51 +255 102 51 +255 51 51 +255 0 51 +255 255 0 +255 204 0 +255 153 0 +255 102 0 +255 51 0 +255 0 0 +204 255 102 +204 204 102 +204 153 102 +204 102 102 +204 51 102 +204 0 102 +204 255 51 +204 204 51 +204 153 51 +204 102 51 +204 51 51 +204 0 51 +204 255 0 +204 204 0 +204 153 0 +204 102 0 +204 51 0 +204 0 0 +153 255 102 +153 204 102 +153 153 102 +153 102 102 +153 51 102 +153 0 102 +153 255 51 +153 204 51 +153 153 51 +153 102 51 +153 51 51 +153 0 51 +153 255 0 +153 204 0 +153 153 0 +153 102 0 +153 51 0 +153 0 0 +102 255 102 +102 204 102 +102 153 102 +102 102 102 +102 51 102 +102 0 102 +102 255 51 +102 204 51 +102 153 51 +102 102 51 +102 51 51 +102 0 51 +102 255 0 +102 204 0 +102 153 0 +102 102 0 +102 51 0 +102 0 0 +51 255 102 +51 204 102 +51 153 102 +51 102 102 +51 51 102 +51 0 102 +51 255 51 +51 204 51 +51 153 51 +51 102 51 +51 51 51 +51 0 51 +51 255 0 +51 204 0 +51 153 0 +51 102 0 +51 51 0 +51 0 0 +0 255 102 +0 204 102 +0 153 102 +0 102 102 +0 51 102 +0 0 102 +0 255 51 +0 204 51 +0 153 51 +0 102 51 +0 51 51 +0 0 51 +0 255 0 +0 204 0 +0 153 0 +0 102 0 +0 51 0 +17 17 17 +34 34 34 +68 68 68 +85 85 85 +119 119 119 +136 136 136 +170 170 170 +187 187 187 +221 221 221 +238 238 238 +192 192 192 +128 0 0 +128 0 128 +0 128 0 +0 128 128 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 diff --git a/archive/Old Art/first grass_dirt_cliff tiles.zip b/archive/Old Art/first grass_dirt_cliff tiles.zip new file mode 100644 index 0000000..fd65240 Binary files /dev/null and b/archive/Old Art/first grass_dirt_cliff tiles.zip differ diff --git a/archive/Old Art/grassyTemplates01082003.zip b/archive/Old Art/grassyTemplates01082003.zip new file mode 100644 index 0000000..1e63bbc Binary files /dev/null and b/archive/Old Art/grassyTemplates01082003.zip differ diff --git a/archive/Old Art/sandyTemplates.png b/archive/Old Art/sandyTemplates.png new file mode 100644 index 0000000..fae8856 Binary files /dev/null and b/archive/Old Art/sandyTemplates.png differ diff --git a/archive/Old Art/sandyTerrain.png b/archive/Old Art/sandyTerrain.png new file mode 100644 index 0000000..184b711 Binary files /dev/null and b/archive/Old Art/sandyTerrain.png differ diff --git a/assets/3d_assets.zip b/assets/3d_assets.zip new file mode 100644 index 0000000..bbff6c5 Binary files /dev/null and b/assets/3d_assets.zip differ diff --git a/assets/Hillterraintemplate.vue b/assets/Hillterraintemplate.vue new file mode 100644 index 0000000..ef27cbb Binary files /dev/null and b/assets/Hillterraintemplate.vue differ diff --git a/assets/Hostile Takeover level design concepts from Rob.doc b/assets/Hostile Takeover level design concepts from Rob.doc new file mode 100644 index 0000000..7491556 Binary files /dev/null and b/assets/Hostile Takeover level design concepts from Rob.doc differ diff --git a/assets/finalgreentemplatemountains.vue b/assets/finalgreentemplatemountains.vue new file mode 100644 index 0000000..424cb6e Binary files /dev/null and b/assets/finalgreentemplatemountains.vue differ diff --git a/assets/iphone_icon.psd b/assets/iphone_icon.psd new file mode 100644 index 0000000..e73d5f0 Binary files /dev/null and b/assets/iphone_icon.psd differ diff --git a/assets/test renders.zip b/assets/test renders.zip new file mode 100644 index 0000000..a20b6ae Binary files /dev/null and b/assets/test renders.zip differ diff --git a/assets/warfare_tank.png b/assets/warfare_tank.png new file mode 100644 index 0000000..b113224 Binary files /dev/null and b/assets/warfare_tank.png differ diff --git a/base/base64.cpp b/base/base64.cpp new file mode 100644 index 0000000..46f9809 --- /dev/null +++ b/base/base64.cpp @@ -0,0 +1,144 @@ +/* + * FILE: base64.c + * AUTHOR: Colin Perkins + * + * MIME base64 encoder/decoder described in rfc1521. This code is derived + * from version 2.7 of the Bellcore metamail package. + * + * Copyright (c) 1998-2000 University College London + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, is permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the Computer Science + * Department at University College London + * 4. Neither the name of the University nor of the Department may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Copyright (c) 1991 Bell Communications Research, Inc. (Bellcore) + * + * Permission to use, copy, modify, and distribute this material + * for any purpose and without fee is hereby granted, provided + * that the above copyright notice and this permission notice + * appear in all copies, and that the name of Bellcore not be + * used in advertising or publicity pertaining to this + * material without the specific, prior written permission + * of an authorized representative of Bellcore. BELLCORE + * MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY + * OF THIS MATERIAL FOR ANY PURPOSE. IT IS PROVIDED "AS IS", + * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. + * + */ + +#include "base/base64.h" + +namespace base { + +static unsigned char basis_64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +int base64encode(const unsigned char *input, int input_length, + unsigned char *output, int output_length) +{ + int i = 0, j = 0; + int pad; + + if (output_length < (input_length * 4 / 3)) { + return -1; + } + + while (i < input_length) { + pad = 3 - (input_length - i); + if (pad == 2) { + output[j ] = basis_64[input[i]>>2]; + output[j+1] = basis_64[(input[i] & 0x03) << 4]; + output[j+2] = '='; + output[j+3] = '='; + } else if (pad == 1) { + output[j ] = basis_64[input[i]>>2]; + output[j+1] = basis_64[((input[i] & 0x03) << 4) | + ((input[i+1] & 0xf0) >> 4)]; + output[j+2] = basis_64[(input[i+1] & 0x0f) << 2]; + output[j+3] = '='; + } else{ + output[j ] = basis_64[input[i]>>2]; + output[j+1] = basis_64[((input[i] & 0x03) << 4) | + ((input[i+1] & 0xf0) >> 4)]; + output[j+2] = basis_64[((input[i+1] & 0x0f) << 2) | + ((input[i+2] & 0xc0) >> 6)]; + output[j+3] = basis_64[input[i+2] & 0x3f]; + } + i += 3; + j += 4; + } + return j; +} + +/* This assumes that an unsigned char is exactly 8 bits. Not portable code! :-) */ +static unsigned char index_64[128] = { +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x3e,0xff,0xff,0xff,0x3f, +0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e, +0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0xff,0xff,0xff,0xff,0xff, +0xff,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28, +0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x30,0x31,0x32,0x33,0xff,0xff,0xff,0xff,0xff +}; + +#define char64(c) ((c > 127) ? 0xff : index_64[(c)]) + +int base64decode(const unsigned char *input, int input_length, unsigned char *output, int output_length) +{ + int i = 0, j = 0, pad; + unsigned char c[4]; + + if (output_length < (input_length * 3 / 4)) { + return -1; + } + if ((input_length % 4) != 0) { + return -1; + } + while ((i + 3) < input_length) { + pad = 0; + c[0] = char64(input[i ]); pad += (c[0] == 0xff); + c[1] = char64(input[i+1]); pad += (c[1] == 0xff); + c[2] = char64(input[i+2]); pad += (c[2] == 0xff); + c[3] = char64(input[i+3]); pad += (c[3] == 0xff); + if (pad == 2) { + output[j++] = (c[0] << 2) | ((c[1] & 0x30) >> 4); + output[j] = (c[1] & 0x0f) << 4; + } else if (pad == 1) { + output[j++] = (c[0] << 2) | ((c[1] & 0x30) >> 4); + output[j++] = ((c[1] & 0x0f) << 4) | ((c[2] & 0x3c) >> 2); + output[j] = (c[2] & 0x03) << 6; + } else { + output[j++] = (c[0] << 2) | ((c[1] & 0x30) >> 4); + output[j++] = ((c[1] & 0x0f) << 4) | ((c[2] & 0x3c) >> 2); + output[j++] = ((c[2] & 0x03) << 6) | (c[3] & 0x3f); + } + i += 4; + } + return j; +} + +} // namespace base diff --git a/base/base64.h b/base/base64.h new file mode 100644 index 0000000..1d3a192 --- /dev/null +++ b/base/base64.h @@ -0,0 +1,48 @@ +/* + * FILE: base64.h + * AUTHORS: Colin Perkins + * + * Copyright (c) 1998-2000 University College London + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, is permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the Computer Science + * Department at University College London + * 4. Neither the name of the University nor of the Department may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef __BASE64_H__ +#define __BASE64_H__ + +namespace base { + +int base64encode(const unsigned char *input, int input_length, + unsigned char *output, int output_length); +int base64decode(const unsigned char *input, int input_length, + unsigned char *output, int output_length); + +} // namespace base + +#endif // __BASE64_H__ diff --git a/base/bytebuffer.cpp b/base/bytebuffer.cpp new file mode 100644 index 0000000..ca5d2e7 --- /dev/null +++ b/base/bytebuffer.cpp @@ -0,0 +1,175 @@ +#include "bytebuffer.h" +#include +#include +#include + +namespace base { + +ByteBuffer::ByteBuffer(dword cb) { + start_ = 0; + end_ = 0; + size_ = cb; + bytes_ = (byte *)malloc(size_); + pbbNext_ = NULL; +} + +ByteBuffer::~ByteBuffer() { + if (bytes_ != NULL) { + free(bytes_); + } +} + +bool ByteBuffer::ReadByte(byte *pb) { + return ReadBytes(pb, sizeof(byte)); +} + +bool ByteBuffer::ReadWord(word *pw) { + word w; + if (!ReadBytes((byte *)&w, sizeof(word))) { + return false; + } else { + *pw = NetToHostWord(w); + return true; + } +} + +bool ByteBuffer::ReadDword(dword *pdw) { + dword dw; + if (!ReadBytes((byte *)&dw, 4)) { + return false; + } else { + *pdw = NetToHostDword(dw); + return true; + } +} + +bool ByteBuffer::ReadString(char *psz, int cb) { + byte *pbStart = bytes_ + start_; + byte *pbEnd = bytes_ + MIN(cb, end_ - start_); + byte *pb = pbStart; + for (; pb < pbEnd; pb++) { + if (*pb == 0) { + break; + } + } + + // Include the zero termination if found + if (*pb == 0) { + if (!ReadBytes((byte *)psz, pb - pbStart)) { + return false; + } + } else { + // Ensure there is room for a forced zero, and + // only read the # of bytes that fit. + if (cb > 0) { + if (pb == bytes_ + cb) { + pb--; + } + if (!ReadBytes((byte *)psz, pb - pbStart)) { + return false; + } + psz[cb - 1] = 0; + } + } + return true; +} + +bool ByteBuffer::ReadBytes(byte *pb, int cb) { + if (cb > Length()) { + return false; + } else { + memcpy(pb, bytes_ + start_, cb); + start_ += cb; + return true; + } +} + +void ByteBuffer::WriteByte(byte b) { + WriteBytes(&b, sizeof(byte)); +} + +void ByteBuffer::WriteWord(word w) { + w = HostToNetWord(w); + WriteBytes((byte *)&w, sizeof(word)); +} + +void ByteBuffer::WriteDword(dword dw) { + dw = HostToNetDword(dw); + WriteBytes((byte *)&dw, sizeof(dword)); +} + +void ByteBuffer::WriteString(const char *psz, bool zero) { + int cch = strlen(psz); + int cbWrite = cch + (zero ? 1 : 0); + WriteBytes((const byte *)psz, cbWrite); +} + +void ByteBuffer::WriteBytes(const byte *pb, int cb) { + Shift(0); + if (Length() + cb > Capacity()) { + Resize(Length() + cb); + } + memcpy(bytes_ + end_, pb, cb); + end_ += cb; +} + +void ByteBuffer::Resize(int size) { + if (size > size_) + size = MAX(size, 3 * size_ / 2); + + int len = MIN(end_ - start_, size); + byte* new_bytes = (byte *)malloc(size); + memcpy(new_bytes, bytes_ + start_, len); + free(bytes_); + + start_ = 0; + end_ = len; + size_ = size; + bytes_ = new_bytes; +} + +void ByteBuffer::Shift(int size) { + // Move start_ to the beginning. Either bytes + // have to be copied or Length() is zero and they don't need to be copied. + + dword len = Length(); + if (len == 0) { + start_ = 0; + end_ = 0; + } + if (size == 0 && start_ == 0) { + return; + } + + // Does two things: eats bytes from the current buffer window, + // and moves the window to the beginning + + if (size > len) { + return; + } + end_ = Length() - size; + memmove(bytes_, bytes_ + start_ + size, end_); + start_ = 0; +} + +ByteBuffer *ByteBuffer::Clone() const { + ByteBuffer *bbT = new ByteBuffer(Length()); + bbT->WriteBytes(Data(), Length()); + return bbT; +} + +void *ByteBuffer::Strip(int *pcb) { + Shift(0); + void *pv = bytes_; + if (pcb != NULL) { + *pcb = Length(); + } + + start_ = 0; + end_ = 0; + size_ = DEFAULT_SIZE; + bytes_ = (byte *)malloc(size_); + return pv; +} + +} // namespace base diff --git a/base/bytebuffer.h b/base/bytebuffer.h new file mode 100644 index 0000000..a808dd1 --- /dev/null +++ b/base/bytebuffer.h @@ -0,0 +1,62 @@ +#ifndef __BYTEBUFFER_H__ +#define __BYTEBUFFER_H__ + +#include "inc/basictypes.h" +#include + +namespace base { + +static const int DEFAULT_SIZE = 128; + +class ByteBuffer { +public: + ByteBuffer(dword cb = DEFAULT_SIZE); + ~ByteBuffer(); + + const byte *Data() const { return bytes_ + start_; } + int Length() const { return end_ - start_; } + int Capacity() const { return size_ - start_; } + void Rewind() { start_ = 0; } + + bool ReadByte(byte *pb); + bool ReadWord(word *pw); + bool ReadDword(dword *pdw); + bool ReadString(char *psz, int cb); + bool ReadBytes(byte *pb, int cb); + + void WriteByte(byte b); + void WriteWord(word w); + void WriteDword(dword dw); + void WriteString(const char *psz, bool zero = true); + void WriteBytes(const byte *pb, int cb); + + void *Strip(int *pcb = NULL); + void Resize(int size); + void Shift(int size); + ByteBuffer *Clone() const; + + static word HostToNetWord(word w) { + return htons(w); + } + static word NetToHostWord(word w) { + return ntohs(w); + } + static dword HostToNetDword(dword dw) { + return htonl(dw); + } + static dword NetToHostDword(dword dw) { + return ntohl(dw); + } + + ByteBuffer *pbbNext_; + +private: + byte *bytes_; + int size_; + int start_; + int end_; +}; + +} // namespace base + +#endif // __BYTEBUFFER_H__ diff --git a/base/criticalsection.h b/base/criticalsection.h new file mode 100644 index 0000000..2408adb --- /dev/null +++ b/base/criticalsection.h @@ -0,0 +1,47 @@ +#ifndef __CRITICALSECTION_H__ +#define __CRITICALSECTION_H__ + +#include "inc/basictypes.h" +#include + +namespace base { + +class CriticalSection { +public: + CriticalSection() { + pthread_mutexattr_t mutex_attribute; + pthread_mutexattr_init(&mutex_attribute); + pthread_mutexattr_settype(&mutex_attribute, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&mutex_, &mutex_attribute); + } + ~CriticalSection() { + pthread_mutex_destroy(&mutex_); + } + void Enter() { + pthread_mutex_lock(&mutex_); + } + void Leave() { + pthread_mutex_unlock(&mutex_); + } +private: + pthread_mutex_t mutex_; +}; + +// CritScope, for serializing exection through a scope + +class CritScope { +public: + CritScope(CriticalSection *pcrit) { + pcrit_ = pcrit; + pcrit_->Enter(); + } + ~CritScope() { + pcrit_->Leave(); + } +private: + CriticalSection *pcrit_; +}; + +} // namespace base + +#endif // __CRITICALSECTION_H__ diff --git a/base/deletetracker.cpp b/base/deletetracker.cpp new file mode 100644 index 0000000..ab59b3b --- /dev/null +++ b/base/deletetracker.cpp @@ -0,0 +1,48 @@ +#include "base/deletetracker.h" + +namespace base { + +// DeleteRecord methods + +DeleteRecord::DeleteRecord(DeleteTracker *tracker) { + tracker_ = tracker; + pdrNext_ = NULL; + deleted_ = false; + tracker_->Register(this); +} + +DeleteRecord::~DeleteRecord() { + if (!deleted_) { + tracker_->Unregister(this); + } +} + +// DeleteTracker methods + +DeleteTracker::DeleteTracker() { + pdrFirst_ = NULL; +} + +DeleteTracker::~DeleteTracker() { + for (DeleteRecord *pdr = pdrFirst_; pdr != NULL; pdr = pdr->pdrNext_) { + pdr->deleted_ = true; + } +} + +void DeleteTracker::Register(DeleteRecord *pdr) { + pdr->pdrNext_ = pdrFirst_; + pdrFirst_ = pdr; +} + +void DeleteTracker::Unregister(DeleteRecord *pdr) { +#ifdef LOGGING + if (pdr != pdrFirst_) { + LOG() << "Error!"; + } +#endif + if (pdr == pdrFirst_) { + pdrFirst_ = pdr->pdrNext_; + } +} + +} // namespace base diff --git a/base/deletetracker.h b/base/deletetracker.h new file mode 100644 index 0000000..50c7db3 --- /dev/null +++ b/base/deletetracker.h @@ -0,0 +1,38 @@ +#ifndef __DELETETRACKER_H__ +#define __DELETETRACKER_H__ + +#include "inc/basictypes.h" +#include "base/log.h" + +namespace base { + +class DeleteTracker; + +class DeleteRecord { +public: + DeleteRecord(DeleteTracker *tracker); + ~DeleteRecord(); + bool deleted() { return deleted_; } + +private: + bool deleted_; + DeleteRecord *pdrNext_; + DeleteTracker *tracker_; + + friend class DeleteTracker; +}; + +class DeleteTracker { +public: + DeleteTracker(); + virtual ~DeleteTracker(); + void Register(DeleteRecord *pdr); + void Unregister(DeleteRecord *pdr); + +private: + DeleteRecord *pdrFirst_; +}; + +} // namespace base + +#endif // __DELETETRACKER_H__ diff --git a/base/dispatcher.h b/base/dispatcher.h new file mode 100644 index 0000000..ce07797 --- /dev/null +++ b/base/dispatcher.h @@ -0,0 +1,55 @@ +#ifndef __DISPATCHER_H__ +#define __DISPATCHER_H__ + +#include "inc/basictypes.h" +#include "base/deletetracker.h" +#include "base/socketserver.h" + +namespace base { + +class Dispatchee; + +class Dispatcher : public DeleteTracker { +public: + Dispatcher(SocketServer *ss) : ss_(ss), dispatchee_(NULL), descriptor_(-1), + events_(0) {} + virtual ~Dispatcher() {} + + virtual void SetEvents(dword events) { events_ |= events; } + virtual void ClearEvents(dword events = (dword)-1) { events_ &= ~events; } + virtual dword GetEvents() { return events_; } + virtual void OnEvent(dword ff); + virtual void SetDispatchee(Dispatchee *dispatchee, int descriptor = -1) {} + + int descriptor() { return descriptor_; } + SocketServer *ss() { return ss_; } + + // TODO: Some of these event flags have Socket semantics in them, that should be + // owned by socket, not here. + enum EventFlags { + kfRead = 0x1, kfWrite = 0x2, kfConnect = 0x4, kfClose = 0x8, + kfRemoteClose = 0x10 + }; + +protected: + dword events_; + int descriptor_; + SocketServer *ss_; + Dispatchee *dispatchee_; +}; + +class Dispatchee { +public: + Dispatchee(SocketServer *ss) : dispatcher_(ss->CreateDispatcher()) {} + virtual ~Dispatchee() { delete dispatcher_; } + + virtual void OnEvent(dword ff) = 0; + +protected: + Dispatcher *dispatcher_; +}; + + +} // namespace base + +#endif // __DISPATCHER_H__ diff --git a/base/epollserver.cpp b/base/epollserver.cpp new file mode 100644 index 0000000..1af42a4 --- /dev/null +++ b/base/epollserver.cpp @@ -0,0 +1,205 @@ +#include "base/epollserver.h" + +#include "base/log.h" +#include "base/tick.h" +#include "base/socket.h" +#include +#include +#include +#include +#include + +namespace base { + +EpollServer::EpollServer() : notifying_(0), wait_(false), key_(0), + epoll_events_(NULL), epoll_events_count_(0) { + // The reserved socket size dynamically grows, so just use size 1 + ed_ = ::epoll_create(1); + eventer_ = new Eventer(this, &wait_); +} + +EpollServer::~EpollServer() { + delete eventer_; + ::close(ed_); +#ifdef DEBUG + if (waitmap_.size() > 0) { + LOG() << "EpollServer deleting, with waiting dispatchers!"; + } +#endif + delete epoll_events_; +} + +void EpollServer::WakeUp() { + eventer_->Signal(); +} + +int EpollServer::GetEpollEventsList(epoll_event **epoll_event_list) { + // If the current list is big enough, use it + if (waitmap_.size() > epoll_events_count_) { + // Free it and make a bigger one + delete epoll_events_; + epoll_events_ = NULL; + epoll_events_count_ = 0; + + // Should probably check for error here but this is unlikely to be + // a problem + int new_count = waitmap_.size() + waitmap_.size() / 2; + epoll_events_ = new epoll_event[new_count]; + epoll_events_count_ = new_count; + } + + *epoll_event_list = epoll_events_; + return epoll_events_count_; +} + +bool EpollServer::Wait(long64 ctWait, bool fProcessIO) { + if (notifying_ != 0) { + LOG() << "Error - re-entering!"; + return false; + } + + // Calculate timing information + long64 cmsNext = -1; + long64 msStop; + if (ctWait != kctForever) { + cmsNext = ctWait * 10; + msStop = base::GetMillisecondCount() + cmsNext; + } + + // Wait for and dispatch events + wait_ = true; + while (wait_) { + epoll_event *epoll_events; + int count = GetEpollEventsList(&epoll_events); + count = epoll_wait(ed_, epoll_events, count, (int)cmsNext); + + // Check for error + if (count < 0) { + LOG() << "epoll_wait returns < 0, errno: " << errno << ", " + << Socket::GetErrorString(errno); + return false; + } + + // If timeout, success + if (count == 0) { + return true; + } + + // Enumerate and dispatch events + notifying_++; + for (int i = 0; i < count; i++) { + // Get the dispatcher. Check to see if the dispatcher has been + // removed. This lookup must be fast. + epoll_event& e = epoll_events[i]; + WaitMap::iterator it = waitmap_.find(e.data.fd); + if (it == waitmap_.end()) { + continue; + } + EpollDispatcher *dispatcher = it->second; + + // Notify of the event(s) + dword ff = 0; + if (e.events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR)) { + ff |= Dispatcher::kfClose; + } + if (e.events & EPOLLIN) { + if (dispatcher->GetEvents() & Dispatcher::kfRemoteClose) { + ff |= Dispatcher::kfClose; + } else { + ff |= Dispatcher::kfRead; + } + } + if (e.events & EPOLLOUT) { + if (dispatcher->GetEvents() & Dispatcher::kfConnect) { + ff |= Dispatcher::kfConnect; + } else { + ff |= Dispatcher::kfWrite; + } + } + if (ff != 0) { + dispatcher->OnEvent(ff); + } + } + notifying_--; + + // Recalc the time remaining. + if (ctWait != kctForever) { + long64 msCurrent = base::GetMillisecondCount(); + if (msCurrent >= msStop) { + return true; + } + cmsNext = msStop - msCurrent; + } + } + return true; +} + +Dispatcher *EpollServer::CreateDispatcher() { + return new EpollDispatcher(this, key_++); +} + +void EpollServer::Add(int key, int descriptor, EpollDispatcher *disp, + uint32_t epoll_events) { +#ifdef DEBUG + WaitMap::iterator it = waitmap_.find(key); + if (it != waitmap_.end()) { + LOG() << key << " ALREADY ADDED!"; + } +#endif + waitmap_.insert(WaitMap::value_type(key, disp)); + epoll_event e; + memset(&e, 0, sizeof(e)); + e.events = epoll_events | EPOLLET | EPOLLRDHUP; + e.data.fd = key; + epoll_ctl(ed_, EPOLL_CTL_ADD, descriptor, &e); +} + +void EpollServer::Remove(int key, int descriptor) { + WaitMap::iterator it = waitmap_.find(key); + if (it == waitmap_.end()) { + LOG() << key << " NOT FOUND!"; + return; + } + waitmap_.erase(it); + epoll_event e; // ignored but must be passed on kernels < 2.6.9 + memset(&e, 0, sizeof(e)); + epoll_ctl(ed_, EPOLL_CTL_DEL, descriptor, &e); +} + +void EpollServer::Update(int key, int descriptor, uint32_t epoll_events) { + epoll_event e; + memset(&e, 0, sizeof(e)); + e.events = epoll_events | EPOLLET | EPOLLRDHUP; + e.data.fd = key; + epoll_ctl(ed_, EPOLL_CTL_MOD, descriptor, &e); +} + +// Static factory method for EpollServer +SocketServer *SocketServer::Create() { + return new EpollServer; +} + +// EpollDispatcher methods + +void EpollDispatcher::SetDispatchee(Dispatchee *dispatchee, int descriptor) { + // If no dispatchee currently, and now setting one + if (dispatchee_ == NULL && dispatchee != NULL) { + ((EpollServer *)ss_)->Add(key_, descriptor, this, EPOLLIN | EPOLLOUT); + } + + // If have dispatchee, and now clearing it + if (dispatchee_ != NULL && dispatchee == NULL) { + ((EpollServer *)ss_)->Remove(key_, descriptor_); + } + + // Switching dispatchees likely won't happen, but cover the cases... + if (dispatchee_ != NULL && dispatchee != NULL) { + ((EpollServer *)ss_)->Remove(key_, descriptor_); + ((EpollServer *)ss_)->Add(key_, descriptor, this, EPOLLIN | EPOLLOUT); + } + + descriptor_ = descriptor; + dispatchee_ = dispatchee; +} + +} // namespace base diff --git a/base/epollserver.h b/base/epollserver.h new file mode 100644 index 0000000..acad8e3 --- /dev/null +++ b/base/epollserver.h @@ -0,0 +1,61 @@ +#ifndef __EPOLLSERVER_H__ +#define __EPOLLSERVER_H__ + +#include "inc/basictypes.h" +#include "base/dispatcher.h" +#include "base/eventer.h" +#include "base/socketserver.h" +#include +#include // use std::unordered_map once standardized and stable + +namespace base { + +class EpollDispatcher; +class EpollDispatcher; +typedef std::map WaitMap; + +// SocketServer that uses epoll() as the base primitive + +class EpollServer : public SocketServer { +public: + EpollServer(); + ~EpollServer(); + + virtual bool Wait(long64 ctWait, bool fProcessIO = true); + virtual void WakeUp(); + virtual Dispatcher *CreateDispatcher(); + + void Add(int key, int descriptor, EpollDispatcher *dispatcher, + uint32_t epoll_events); + void Remove(int key, int descriptor); + void Update(int key, int descriptor, uint32_t epoll_events); + +protected: + int GetEpollEventsList(epoll_event **epoll_event_list); + + int key_; + WaitMap waitmap_; + int ed_; + bool wait_; + int notifying_; + Eventer *eventer_; + epoll_event *epoll_events_; + int epoll_events_count_; +}; + +// Dispatcher that is compatible with EpollServer + +class EpollDispatcher : public Dispatcher { +public: + EpollDispatcher(EpollServer *ss, int key) : Dispatcher(ss), key_(key) {} + virtual ~EpollDispatcher() { SetDispatchee(NULL); } + + virtual void SetDispatchee(Dispatchee *dispatchee, int descriptor = -1); + +protected: + int key_; +}; + +} // namespace base + +#endif // __EPOLLSERVER_H__ diff --git a/base/eventer.cpp b/base/eventer.cpp new file mode 100644 index 0000000..61028e1 --- /dev/null +++ b/base/eventer.cpp @@ -0,0 +1,44 @@ +#include "eventer.h" +#include +#include + +namespace base { + +Eventer::Eventer(SocketServer *ss, bool *wait) : Dispatchee(ss), wait_(wait), + signaled_(false) { + pipe(afd_); + signal(SIGPIPE, SIG_IGN); + dispatcher_->SetDispatchee(this, afd_[0]); + dispatcher_->SetEvents(Dispatcher::kfRead); +} + +Eventer::~Eventer() +{ + close(afd_[0]); + close(afd_[1]); +} + +void Eventer::Signal() +{ + CritScope cs(&crit_); + if (!signaled_) { + byte b = 0; + write(afd_[1], &b, sizeof(b)); + signaled_ = true; + } +} + +void Eventer::OnEvent(dword ff) +{ + CritScope cs(&crit_); + if (signaled_) { + byte b; + read(afd_[0], &b, sizeof(b)); + signaled_ = false; + } + if (wait_ != NULL) { + *wait_ = false; + } +} + +} // namespace base diff --git a/base/eventer.h b/base/eventer.h new file mode 100644 index 0000000..f29714a --- /dev/null +++ b/base/eventer.h @@ -0,0 +1,33 @@ +#ifndef __EVENTER_H__ +#define __EVENTER_H__ + +#include "inc/basictypes.h" +#include "base/dispatcher.h" +#include "base/socketserver.h" +#include "base/criticalsection.h" + +namespace base { + +class SocketServer; + +class Eventer : Dispatchee { +public: + Eventer(SocketServer *ss, bool *wait); + virtual ~Eventer(); + + virtual void Signal(); + Dispatcher *dispatcher() { return dispatcher_; } + +private: + // Dispatchee interface + virtual void OnEvent(dword ff); + + bool *wait_; + int afd_[2]; + bool signaled_; + CriticalSection crit_; +}; + +} // namespace base + +#endif // __EVENTER_H__ diff --git a/base/format.cpp b/base/format.cpp new file mode 100644 index 0000000..2614a9f --- /dev/null +++ b/base/format.cpp @@ -0,0 +1,122 @@ +#include "base/format.h" +#include +#include +#include +#include +#include + +namespace base { + +static std::string s_str; + +const char *Format::ToString(const char *fmt, ...) { + va_list va; + va_start(va, fmt); + char sz[512]; + vsnprintf(sz, sizeof(sz), fmt, va); + va_end(va); + s_str = sz; + const char *psz = s_str.c_str(); + return psz; +} + +const char *Format::ToHex(const byte *pb, int cb) { + const char *map = { "0123456789abcdef" }; + char sz[512]; + if (cb >= (sizeof(sz) - 1) / 2) { + return "0"; + } + char *pch = sz; + while (cb-- != 0) { + *pch++ = map[(((*pb) >> 4) & 0xf)]; + *pch++ = map[((*pb) & 0xf)]; + pb++; + } + *pch = 0; + s_str = sz; + return s_str.c_str(); +} + +bool Format::GetHexDigit(char ch, byte *pb) { + if (ch >= '0' && ch <= '9') { + *pb = ch - '0'; + return true; + } else if (ch >= 'A' && ch <= 'F') { + *pb = ch - 'A' + 10; + return true; + } else if (ch >= 'a' && ch <= 'f') { + *pb = ch - 'a' + 10; + return true; + } + return false; +} + +bool Format::FromHex(const char *psz, byte *pb, int cb) { + const char *pch = psz; + while (*pch != 0 && cb != 0) { + byte b; + if (!GetHexDigit(*pch++, &b)) { + return false; + } + *pb = b << 4; + if (!GetHexDigit(*pch++, &b)) { + return false; + } + *pb++ |= b; + cb--; + } + return true; +} + +bool Format::ToDword(const char *psz, int base, dword *pdw) { + if (!ValidateString(psz, base)) { + return false; + } + + *pdw = 0; + long long ll = strtoll(psz, (char **)NULL, base); + *pdw = (dword)ll; + return true; +} + +bool Format::ToInteger(const char *psz, int base, int *pint) { + if (!ValidateString(psz, base)) { + return false; + } + + *pint = 0; + long l = strtol(psz, (char **)NULL, base); + *pint = (int)l; + return true; +} + +bool Format::ValidateString(const char *psz, int base) { + // Checking errno after calling strtol or strtoll is an unreliable way to + // detect error! That is why this method exists. + + if (base == 10) { + for (const char *pch = psz; *pch != 0; pch++ ) { + if (*pch < '0' || *pch > '9') { + if (pch != psz || *pch != '-') { + return false; + } + } + } + return true; + } + + if (base == 16) { + for (const char *pch = psz; *pch != 0; pch++ ) { + if (!((*pch >= '0' && *pch <= '9') || + (*pch >= 'a' && *pch <= 'f') || + (*pch >= 'A' && *pch <= 'F'))) { + return false; + } + } + return true; + } + + return false; +} + +} // namespace base diff --git a/base/format.h b/base/format.h new file mode 100644 index 0000000..84fba1c --- /dev/null +++ b/base/format.h @@ -0,0 +1,23 @@ +#ifndef __FORMAT_H__ +#define __FORMAT_H__ + +#include "inc/basictypes.h" + +namespace base { + +class Format { +public: + static const char *ToString(const char *fmt, ...); + static const char *ToHex(const byte *pb, int cb); + static bool FromHex(const char *psz, byte *pb, int cb); + static bool ToDword(const char *psz, int base, dword *pdw); + static bool ToInteger(const char *psz, int base, int *pint); + +private: + static bool GetHexDigit(char ch, byte *pb); + static bool ValidateString(const char *psz, int base); +}; + +} // namespace base + +#endif // __FORMAT_H__ diff --git a/base/log.h b/base/log.h new file mode 100644 index 0000000..68db50c --- /dev/null +++ b/base/log.h @@ -0,0 +1,182 @@ +#ifndef __LOG_H__ +#define __LOG_H__ + +// Compile-in logging facilities. If not turned on at compile time, the LOG +// statements will not produce any code. +// +// Two major flags: +// DEBUG_LOGGING: turns on debug logging (LOG statements). If not turned on, +// all debug LOG statements will not get compiled in. +// RELEASE_LOGGING: turns on release logging (RLOG statements). If not turned +// on, all RLOG statements will not get compled in. + +// legacy: turn on LOGGING if DEBUG_LOGGING is on and vice versa. LOGGING is +// used both to turn on logging, and as a compile time conditional in existing +// code. + +#if defined(LOGGING) +#if !defined(DEBUG_LOGGING) +#define DEBUG_LOGGING +#endif +#else +#if defined(DEBUG_LOGGING) +#define LOGGING +#endif +#endif + +// If DEBUG_LOGGING, RELEASE_LOGGING is turned on too +#if defined(DEBUG_LOGGING) +#define RELEASE_LOGGING +#endif + +// If either of these types of logging are turned on, we need CONSOLE_LOGGING +#if defined(DEBUG_LOGGING) || defined(RELEASE_LOGGING) +#define CONSOLE_LOGGING +#endif + +#if !defined(DEBUG_LOGGING) || !defined(RELEASE_LOGGING) +#define EMPTY_LOGGING +#endif + +#if defined(DEBUG_LOGGING) +#define LOG() base::ConsoleLog(__PRETTY_FUNCTION__, NULL, __LINE__).stream() +#define LOGF() base::ConsoleLog(__PRETTY_FUNCTION__, __FILE__, __LINE__).stream() +#define LOGS() base::ConsoleLog().stream() +#define LOGX() base::ConsoleLog(NULL, NULL, -1).stream() +#else +#define LOG() while(false) base::EmptyLog() +#define LOGF() while(false) base::EmptyLog() +#define LOGS() while(false) base::EmptyLog() +#define LOGX() while(false) base::EmptyLog() +#endif + +#if defined(DEBUG_LOGGING) || defined(RELEASE_LOGGING) +#define RLOG() base::ConsoleLog(__PRETTY_FUNCTION__, NULL, __LINE__).stream() +#define RLOGF() base::ConsoleLog(__PRETTY_FUNCTION__, __FILE__, __LINE__).stream() +#define RLOGS() base::ConsoleLog().stream() +#define RLOGX() base::ConsoleLog(NULL, NULL, -1).stream() +#else +#define RLOG() while(false) base::EmptyLog() +#define RLOGF() while(false) base::EmptyLog() +#define RLOGS() while(false) base::EmptyLog() +#define RLOGX() while(false) base::EmptyLog() +#endif + +// Log string formatting class (for legacy reasons called Log) + +#if defined(CONSOLE_LOGGING) + +#include +#include + +namespace base { +class Log { +public: + static std::string Format(const char *format, ...) { + va_list va; + va_start(va, format); + std::string str = vFormat(format, va); + va_end(va); + return str; + } +private: + static std::string vFormat(const char *format, va_list va) { + char sz[512]; + vsnprintf(sz, sizeof(sz), format, va); + return std::string(sz); + } +}; +} // namespace base + +#else + +namespace base { +class Log { +public: + static int Format(const char *format, ...) { return 0; } +}; +} // namespace base + +#endif // CONSOLE_LOGGING + +// Empty Logging + +#if defined(EMPTY_LOGGING) +namespace base { +class EmptyLog { +public: + EmptyLog() {} + EmptyLog &operator<<(const char *s) { return *this; } + EmptyLog &operator<<(int n) { return *this; } +#ifdef CONSOLE_LOGGING + EmptyLog &operator<<(const std::string& s) { return *this; } +#endif +}; +} // namespace base +#endif // EMPTY_LOGGING + +// Console Logging + +#if defined(CONSOLE_LOGGING) +#include "inc/basictypes.h" +#include +#include +#include +#include +namespace base { +class ConsoleLog { +public: + ConsoleLog(const char *function, const char *file, int line) { + time_t t = time(NULL); + struct tm *tm = localtime(&t); + struct timeval tv; + gettimeofday(&tv, 0); + stream_ << Log::Format("%02d%02d%02d%02d.%02d%03d|", + tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, + tm->tm_sec, tv.tv_usec / 1000); + + if (function != NULL) { + std::string func(function); + size_t n2 = func.find('('); + if (n2 != std::string::npos) { + size_t n1 = func.rfind(' ', n2); + if (n1 != std::string::npos) { + func = std::string(func, n1 + 1, n2 - n1 - 1); + } + } + if (line == -1 && file == NULL) { + stream_ << func << "(): "; + } else { + stream_ << func << "() "; + } + } + + if (line != -1) { + if (file != NULL) { + stream_ << "line " << line << " "; + } else { + stream_ << "line " << line << ": "; + } + } + + if (file != NULL) { + stream_ << "file " << file << ": "; + } + } + + ConsoleLog() { + } + + ~ConsoleLog() { + std::cout << stream_.str() << std::endl; + } + + std::ostringstream& stream() { return stream_; } + +private: + std::ostringstream stream_; +}; +} // namespace base +#endif // CONSOLE_LOGGING + +#endif // __LOG_H__ diff --git a/base/md5.h b/base/md5.h new file mode 100755 index 0000000..447d3ba --- /dev/null +++ b/base/md5.h @@ -0,0 +1,45 @@ +/* + * This is the header file for the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + * + */ + +#ifndef __MD5_H__ +#define __MD5_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef long unsigned int uint32; +typedef struct MD5Context MD5_CTX; + +#define md5byte unsigned char + +struct MD5Context { + uint32 buf[4]; + uint32 bits[2]; + uint32 in[16]; +}; + +void MD5Init(struct MD5Context *context); +void MD5Update(struct MD5Context *context, md5byte const *buf, unsigned len); +void MD5Final(unsigned char digest[16], struct MD5Context *context); +void MD5Transform(uint32 buf[4], uint32 const in[16]); + +#ifdef __cplusplus +}; +#endif + +#endif // __MD5_H__ diff --git a/base/md5c.cpp b/base/md5c.cpp new file mode 100755 index 0000000..ef9f862 --- /dev/null +++ b/base/md5c.cpp @@ -0,0 +1,256 @@ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ +#include /* for memcpy() */ +#include "md5.h" + +#ifndef HIGHFIRST +#define byteReverse(buf, len) /* Nothing */ +#else +void byteReverse(unsigned char *buf, unsigned longs); + +#ifndef ASM_MD5 +/* + * Note: this code is harmless on little-endian machines. + */ +void byteReverse(unsigned char *buf, unsigned longs) +{ + uint32 t; + do { + t = (uint32)((unsigned)buf[3]<<8 | buf[2]) << 16 | + ((unsigned)buf[1]<<8 | buf[0]); + *(uint32 *)buf = t; + buf += 4; + } while (--longs); +} +#endif +#endif + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void +MD5Init(struct MD5Context *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void +MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len) +{ + uint32 t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((uint32)len << 3)) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if ( t ) { + unsigned char *p = (unsigned char *)ctx->in + t; + + t = 64-t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32 *)ctx->in); + buf += t; + len -= t; + } + + /* Process data in 64-byte chunks */ + + while (len >= 64) { + memcpy(ctx->in, buf, 64); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32 *)ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void +MD5Final(unsigned char digest[16], struct MD5Context *ctx) +{ + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = (unsigned char*)(ctx->in) + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32 *)ctx->in); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count-8); + } + byteReverse(ctx->in, 14); + + /* Append length in bits and transform */ + ((uint32 *)ctx->in)[ 14 ] = ctx->bits[0]; + ((uint32 *)ctx->in)[ 15 ] = ctx->bits[1]; + + MD5Transform(ctx->buf, (uint32 *)ctx->in); + byteReverse((unsigned char *)ctx->buf, 4); + memcpy(digest, ctx->buf, 16); + memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ +} + +#ifndef ASM_MD5 + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +void +MD5Transform(uint32 buf[4], uint32 const in[16]) +{ + register uint32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12]+0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10]+0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} +#endif diff --git a/base/messagehandler.cpp b/base/messagehandler.cpp new file mode 100644 index 0000000..146bed1 --- /dev/null +++ b/base/messagehandler.cpp @@ -0,0 +1,32 @@ +#include "base/log.h" +#include "base/messagehandler.h" +#include "base/thread.h" + +namespace base { + +MessageHandler::MessageHandler(Thread& thread) : thread_(thread), + disposed_(false) { +} + +MessageHandler::MessageHandler() : thread_(Thread::current()), + disposed_(false) { +} + +MessageHandler::~MessageHandler() { + disposed_ = true; + thread_.Clear(this); + thread_.ClearDispose(this); +} + +void MessageHandler::Dispose() { + if (!disposed_) { + disposed_ = true; + thread_.Dispose(this); + LOG() << base::Log::Format("0x%08lx ", this) << "disposing"; + } else { + LOG() << base::Log::Format("0x%08lx ", this) + << "tried to dispose but already disposed"; + } +} + +} // namespace base diff --git a/base/messagehandler.h b/base/messagehandler.h new file mode 100644 index 0000000..3512c1a --- /dev/null +++ b/base/messagehandler.h @@ -0,0 +1,27 @@ +#ifndef __MESSAGEHANDLER_H__ +#define __MESSAGEHANDLER_H__ + +#include "inc/basictypes.h" + +namespace base { + +class Message; +class Thread; + +class MessageHandler { +public: + MessageHandler(); + MessageHandler(Thread& thread); + virtual ~MessageHandler(); + virtual void OnMessage(Message *pmsg) {} + Thread& thread() { return thread_; } + void Dispose(); + +protected: + Thread& thread_; + bool disposed_; +}; + +} // namespace base + +#endif // __MESSAGEHANDLER_H__ diff --git a/base/messagequeue.cpp b/base/messagequeue.cpp new file mode 100644 index 0000000..7116c9d --- /dev/null +++ b/base/messagequeue.cpp @@ -0,0 +1,330 @@ +#include "base/messagequeue.h" +#include "base/tick.h" + +namespace base { + +MessageQueue::MessageQueue(SocketServer *ss) : ss_(ss), stop_(false), + pmsg_(NULL), pmsgDelayed_(NULL) +{ + if (ss_ == NULL) { + ss_ = SocketServer::Create(); + } +} + +MessageQueue::~MessageQueue() +{ + Clear(); + ClearDispose(); + delete ss_; +} + +void MessageQueue::Stop() +{ + stop_ = true; + ss_->WakeUp(); +} + +bool MessageQueue::IsStopping() +{ + return stop_; +} + +bool MessageQueue::Get(Message *pmsg, long64 ctWait) +{ + long64 ctTotal = ctWait; + long64 ctElapsed = 0; + long64 ctDelayNext = kctForever; + long64 tStart = GetTickCount(); + long64 tCurrent = tStart; + while (true) { + { + CritScope cs(&crit_); + + // Check for timed messages (delayed messages, and timers) + if (pmsgDelayed_ != NULL) { + ctDelayNext = pmsgDelayed_->tTrigger - tCurrent; + if (ctDelayNext <= 0) { + // If it's a delayed message, move it onto the message + // queue, and we're good to go. + if (!(pmsgDelayed_->ff & kfMsgTimer)) { + Message *pmsgT = pmsgDelayed_; + pmsgDelayed_ = pmsgDelayed_->pmsgNext; + pmsgT->pmsgNext = pmsg_; + pmsg_ = pmsgT; + } else { + // It is a timer, which means it goes off on time + // at a constant rate. Remove it from the timer list, + // since it will need to be re-inserted because of the + // fixed sort. + Message *pmsgT = pmsgDelayed_; + pmsgDelayed_ = pmsgDelayed_->pmsgNext; + + // Make a copy and put it at the top of the regular + // message queue, so it gets processed. + Message *pmsgNew = new Message; + if (pmsgNew != NULL) { + *pmsgNew = *pmsgT; + pmsgNew->pmsgNext = pmsg_; + pmsg_ = pmsgNew; + } + + // Adjust the trigger to track a constant rate, so + // that processing time doesn't add to latency. + pmsgT->tTrigger += pmsgT->ctRate; + + // Add latency if the trigger is already behind, + // and insert it back into the delayed message list. + if ((tCurrent - pmsgT->tTrigger) >= pmsgT->ctRate) { + pmsgT->tTrigger = tCurrent; + } + InsertDelayedMessage(pmsgT); + } + } + } + + // Check for regular messages + while (pmsg_ != NULL) { + Message *pmsgT = pmsg_; + pmsg_ = pmsg_->pmsgNext; + if (pmsgT->id == kidmDispose && pmsgT->handler == NULL) { + delete pmsgT->data; + delete pmsgT; + continue; + } + *pmsg = *pmsgT; + delete pmsgT; + return true; + } + } + + // If asked, stop before waiting + + if (stop_) { + break; + } + + // Calculate wait remaining + + long64 ctNext; + if (ctWait == kctForever) { + ctNext = ctDelayNext; + } else { + ctNext = ctTotal - ctElapsed; + if (ctNext < 0) { + ctNext = 0; + } + if (ctDelayNext != kctForever && ctDelayNext < ctNext) { + ctNext = ctDelayNext; + } + } + + // Wait and multiplex + + ss_->Wait(ctNext, true); + + // If the requested time has expired, return + + tCurrent = GetTickCount(); + ctElapsed = tCurrent - tStart; + if (ctWait != kctForever) { + if (ctElapsed >= ctWait) { + return false; + } + } + } + return false; +} + +void MessageQueue::Post(Message *pmsg, int idCoalesce) +{ + if (stop_) + return; + + // Copy message before putting in queue + Message *pmsgNew = new Message; + if (pmsgNew == NULL) { + return; + } + *pmsgNew = *pmsg; + + // Search for end or message to coalesce + CritScope cs(&crit_); + Message **ppmsgT = &pmsg_; + Message *pmsgCoalesce = NULL; + while (*ppmsgT != NULL) { + // Find the last posted message that matches pmsg->id. Allow + // idCoalesce after, but no others. + + if (idCoalesce != -1) { + if ((*ppmsgT)->id == pmsg->id) { + pmsgCoalesce = *ppmsgT; + } else { + if ((*ppmsgT)->id != idCoalesce) { + pmsgCoalesce = NULL; + } + } + } + + ppmsgT = &((*ppmsgT)->pmsgNext); + } + + // If coalesce message found, mark it as a message to be coalesced. The + // game will decide what to do with it. + + if (pmsgCoalesce != NULL) { + pmsgCoalesce->ff |= kfMsgCoalesce; + } + + // Put new message at the end + pmsgNew->pmsgNext = NULL; + *ppmsgT = pmsgNew; + + ss_->WakeUp(); +} + +void MessageQueue::PostDelayed(Message *pmsg, long ct, long ctBoost) { + if (stop_) { + return; + } + + // Copy message and set timestamp before putting in queue + Message *pmsgNew = new Message; + if (pmsgNew == NULL) { + return; + } + *pmsgNew = *pmsg; + pmsgNew->ctRate = ct; + pmsgNew->tTrigger = GetTickCount() + ct + ctBoost; + InsertDelayedMessage(pmsgNew); + + ss_->WakeUp(); +} + +void MessageQueue::BoostTimer(int id, MessageHandler *handler, long ctBoost) { + // Find the timer, remove it, adjust trigger, then re-insert it + CritScope cs(&crit_); + Message **ppmsg = &pmsgDelayed_; + while (*ppmsg != NULL) { + Message *pmsgT = *ppmsg; + if (pmsgT->id == id && pmsgT->handler == handler) { + *ppmsg = pmsgT->pmsgNext; + pmsgT->tTrigger += ctBoost; + InsertDelayedMessage(pmsgT); + return; + } + ppmsg = &pmsgT->pmsgNext; + } +} + +void MessageQueue::InsertDelayedMessage(Message *pmsg) { + // Perform insertion sort on timestamp + CritScope cs(&crit_); + Message **ppmsg = &pmsgDelayed_; + for (; (*ppmsg) != NULL; ppmsg = &(*ppmsg)->pmsgNext) { + if (pmsg->tTrigger < (*ppmsg)->tTrigger) { + pmsg->pmsgNext = *ppmsg; + *ppmsg = pmsg; + break; + } + } + if ((*ppmsg) == NULL) { + *ppmsg = pmsg; + pmsg->pmsgNext = NULL; + } +} + +void MessageQueue::Clear(MessageHandler *handler, int id) { + CritScope cs(&crit_); + ClearChain(&pmsg_, handler, id); + ClearChain(&pmsgDelayed_, handler, id); +} + +void MessageQueue::ClearDispose(MessageHandler *handler) { + // Restart loop if a data is deleted, since that can change the list + bool restart = false; + do { + Message **ppmsg = &pmsg_; + for (; (*ppmsg) != NULL; ppmsg = &(*ppmsg)->pmsgNext) { + Message *pmsg = *ppmsg; + if (pmsg->id != kidmDispose || pmsg->handler != NULL) { + continue; + } + DisposeData *dispose = (DisposeData *)pmsg->data; + if (handler == NULL || dispose->data_ == (void *)handler) { + *ppmsg = pmsg->pmsgNext; + delete pmsg->data; + delete pmsg; + restart = true; + break; + } + } + } while (restart); +} + +void MessageQueue::ClearChain(Message **ppmsg, MessageHandler *handler, + int id) { + Message **ppmsgStart = ppmsg; + while (true) { + ppmsg = ppmsgStart; + bool restart = false; + while (!restart && (*ppmsg) != NULL) { + Message *pmsg = *ppmsg; + bool remove = false; + if (handler == NULL) { + if (id == kidmNone) { + remove = true; + } else if (pmsg->id == id) { + remove = true; + } + } else if (pmsg->handler == handler) { + if (id == -1) { + remove = true; + } else if (pmsg->id == id) { + remove = true; + } + } + if (remove) { + // Unlink the message first, and restart the scan if deleting + // data, since that can modify the message list. + *ppmsg = pmsg->pmsgNext; + if (pmsg->data != NULL) { + delete pmsg->data; + restart = true; + } + delete pmsg; + continue; + } + ppmsg = &(*ppmsg)->pmsgNext; + } + if (!restart) { + break; + } + } +} + +void MessageQueue::Dispatch(Message *pmsg) { + if (pmsg->handler != NULL) { + pmsg->handler->OnMessage(pmsg); + } else { +#ifdef LOGGING + if (pmsg->data != NULL) { + LOG() << "message " << pmsg->id << " NULL handler non-NULL data"; + } +#endif + } +} + +bool MessageQueue::FindDispose(void *pv) { + for (Message *pmsg = pmsg_; pmsg != NULL; pmsg = pmsg->pmsgNext) { + if (pmsg->id != kidmDispose) { + continue; + } + DisposeData *dispose = (DisposeData *)pmsg->data; + if (dispose->data_ == pv) { + return true; + } + } + return false; +} + +} // namespace base diff --git a/base/messagequeue.h b/base/messagequeue.h new file mode 100644 index 0000000..2fe9f34 --- /dev/null +++ b/base/messagequeue.h @@ -0,0 +1,118 @@ +#ifndef __MESSAGEQUEUE_H__ +#define __MESSAGEQUEUE_H__ + +#include "base/criticalsection.h" +#include "base/socketserver.h" +#include "base/messagehandler.h" +#include "base/log.h" +#include "inc/basictypes.h" +#include + +namespace base { + +class MessageData { +public: + MessageData() {} + virtual ~MessageData() {} +}; + +template +class DisposeData : public MessageData { +public: + DisposeData(T *data) : data_(data) { } + virtual ~DisposeData() { delete data_; } + T *data_; +}; + +struct Message; + +struct Message { + Message() { memset(this, 0, sizeof(*this)); } + int id; + int x; + int y; + long ms; + dword ff; + long64 ctRate; + long64 tTrigger; + MessageData *data; + MessageHandler *handler; + Message *pmsgNext; +}; +#define kfMsgCoalesce 0x1 +#define kfMsgCancelMode 0x2 +#define kfMsgTimer 0x4 +const int kidmDispose = 0x7f00; + +// New base messages are negative to avoid colliding with game idm's defined in input.h. +const int kidmNone = -1; +const int kidmNullEvent = -2; +const int kidmTransportEvent = -3; + +class MessageQueue { +public: + MessageQueue(SocketServer *ss = NULL); + virtual ~MessageQueue(); + + SocketServer& ss() { return *ss_; } + void set_ss(SocketServer *ss) { delete ss_; ss_ = ss; } + + virtual void Stop(); + virtual bool IsStopping(); + virtual bool Get(Message *pmsg, long64 ctWait = kctForever); + virtual void Post(Message *pmsg, int idCoalesce = -1); + virtual void Post(int id, MessageHandler *handler, + MessageData *data = NULL) { + Message msg; + msg.id = id; + msg.handler = handler; + msg.data = data; + Post(&msg); + } + virtual void PostDelayed(Message *pmsg, long ct, long ctBoost = 0); + virtual void PostDelayed(int id, MessageHandler *handler, long ct, + long ctBoost = 0) { + Message msg; + msg.id = id; + msg.handler = handler; + PostDelayed(&msg, ct, ctBoost); + } + virtual void PostTimer(int id, MessageHandler *handler, long ct, + long ctBoost = 0) { + Message msg; + msg.id = id; + msg.handler = handler; + msg.ff = kfMsgTimer; + PostDelayed(&msg, ct, ctBoost); + } + virtual void BoostTimer(int id, MessageHandler *handler, long ctBoost); + virtual void Clear(MessageHandler *handler = NULL, int id = kidmNone); + virtual void ClearDispose(MessageHandler *handler = NULL); + virtual void Dispatch(Message *pmsg); + + // Nice helper to async-delete objects + template void Dispose(T *obj) { + if (FindDispose(obj)) { + LOG() << "error - disposing twice!"; + return; + } + if (obj != NULL) { + Post(kidmDispose, NULL, new DisposeData(obj)); + } + } + +private: + bool FindDispose(void *pv); + void ClearChain(Message **ppmsg, MessageHandler *handler, int id); + void InsertDelayedMessage(Message *pmsg); + + SocketServer *ss_; + bool stop_; + Message *pmsg_; + Message *pmsgDelayed_; + CriticalSection crit_; +}; + +} // namespace base + +#endif // __MESSAGEQUEUE_H__ diff --git a/base/misc.cpp b/base/misc.cpp new file mode 100644 index 0000000..5f8fcff --- /dev/null +++ b/base/misc.cpp @@ -0,0 +1,60 @@ +#include "base/misc.h" +#include +#include + +namespace base { + +const char *StringEncoder::Replace(const char *s, const char *findstr, + const char *replacewith) { + int index = 0; + std::string out(s); + while (true) { + size_t found = out.find(findstr, index); + if (found == std::string::npos) { + break; + } + out.replace(found, strlen(findstr), replacewith); + index += found + strlen(replacewith); + } + return base::Format::ToString("%s", out.c_str()); +} + +// Algorithmically identical to python cgi.escape +const char *StringEncoder::Escape(const char *s, bool quote) { + // html content escaping + std::string out = Replace(s, "&", "&"); + out = Replace(out.c_str(), "<", "<"); + out = Replace(out.c_str(), ">", ">"); + if (quote) { + out = Replace(out.c_str(), "\"", """); + } + return base::Format::ToString("%s", out.c_str()); +} + +// For encoding query keys and values +const char *StringEncoder::QueryEncode(const char *s) { + char *buf = new char[strlen(s) * 3 + 1]; + char *out = buf; + const char *in = s; + while (*in) { + if (isalnum(*in) || *in == '-' || *in == '_' || *in == '.' || + *in == '~') { + *out++ = *in++; + } else if (*in == ' ') { + *out++ = '+'; + in++; + } else { + char *hex = "0123456789abcdef"; + *out++ = '%'; + *out++ = hex[(*in >> 4) & 0xf]; + *out++ = hex[(*in) & 0xf]; + in++; + } + } + *out = 0; + const char *result = base::Format::ToString("%s", buf); + delete buf; + return result; +} + +} // namespace base diff --git a/base/misc.h b/base/misc.h new file mode 100644 index 0000000..29cd448 --- /dev/null +++ b/base/misc.h @@ -0,0 +1,56 @@ +#ifndef __BASE_MISC_H__ +#define __BASE_MISC_H__ + +#include "inc/basictypes.h" +#include "base/log.h" +#include "base/format.h" + +#if defined(LOGGING) && !defined(LABELS) +#define LABELS +#endif + +namespace base { + +struct Label { + int index; + const char *label; +}; + +class Labels { +public: + Labels(const Label *alabel) { + alabel_ = alabel; + } + const char *Find(int index) const { + for (const Label *label = alabel_; label->label != NULL; label++) { + if (label->index == index) { + return label->label; + } + } + return Format::ToString("UNKNOWN: %d", index); + } +private: + const Label *alabel_; +}; + +#ifdef LABELS +#define STARTLABEL(name) const base::Label name ## _labels[] = { +#define LABEL(x) { x, #x }, +#define ENDLABEL(name) { 0, 0 } }; const base::Labels name(name ## _labels); +#else // !LABELS +#define STARTLABEL(name) const base::Label name ## _labels[] = { +#define LABEL(x) +#define ENDLABEL(name) { 0, 0 } }; const base::Labels name(name ## _labels); +#endif + +class StringEncoder { +public: + static const char *Escape(const char *s, bool quote = false); + static const char *Replace(const char *s, const char *findstr, + const char *replacewith); + static const char *QueryEncode(const char *s); +}; + +} // namespace base + +#endif // __BASE_MISC_H__ diff --git a/base/selectserver.cpp b/base/selectserver.cpp new file mode 100644 index 0000000..236e698 --- /dev/null +++ b/base/selectserver.cpp @@ -0,0 +1,232 @@ +#include "base/selectserver.h" + +#include "base/log.h" +#include "base/socket.h" +#include +#include +#include +#include +#include + +namespace base { + +SelectServer::SelectServer() { + notifying_ = 0; + wait_ = false; + pdispFirst_ = NULL; + pdispNotifyNext_ = NULL; + eventer_ = new Eventer(this, &wait_); +} + +SelectServer::~SelectServer() { + delete eventer_; +} + +void SelectServer::Add(SelectDispatcher *pdisp) { + CritScope cs(&crit_); + pdisp->pdispNext_ = pdispFirst_; + pdispFirst_ = pdisp; +} + +void SelectServer::Remove(SelectDispatcher *pdisp) { + CritScope cs(&crit_); + SelectDispatcher **ppdispT = &pdispFirst_; + while ((*ppdispT) != NULL) { + if (*ppdispT == pdisp) { + *ppdispT = pdisp->pdispNext_; + if (pdispNotifyNext_ == pdisp) { + pdispNotifyNext_ = pdisp->pdispNext_; + } + break; + } + ppdispT = &(*ppdispT)->pdispNext_; + } +} + +void SelectServer::WakeUp() { + eventer_->Signal(); +} + +bool SelectServer::Wait(long64 ctWaitBig, bool fProcessIO) { + // Allow recursion when processing input. + if (notifying_ != 0) { + LOG() << "Error - re-entering!"; + fProcessIO = false; + } + + // Calculate timing information + + int ctWait = (int)ctWaitBig; + struct timeval *ptvWait = NULL; + struct timeval tvWait; + struct timeval tvStop; + if (ctWait != kctForever) { + // Calculate wait timeval + + tvWait.tv_sec = ctWait / 100; + tvWait.tv_usec = (ctWait % 100) * 10000; + ptvWait = &tvWait; + + // Calculate when to return in a timeval + + gettimeofday(&tvStop, NULL); + tvStop.tv_sec += tvWait.tv_sec; + tvStop.tv_usec += tvWait.tv_usec; + if (tvStop.tv_usec >= 1000000) { + tvStop.tv_usec -= 1000000; + tvStop.tv_sec += 1; + } + } + + // Zero all fd_sets. Don't need to do this inside the loop since + // select() zeros the descriptors not signaled + + fd_set fdsRead; + FD_ZERO(&fdsRead); + fd_set fdsWrite; + FD_ZERO(&fdsWrite); + + wait_ = true; + + while (wait_) { + int fdMax = -1; + { + CritScope cr(&crit_); + + SelectDispatcher *pdispT = pdispFirst_; + for (; pdispT != NULL; pdispT = pdispT->pdispNext_) { + // Query dispatchers for read and write wait state + + if (!fProcessIO && (pdispT != eventer_->dispatcher())) { + continue; + } + + int fd = pdispT->descriptor(); + if (fd > fdMax) { + fdMax = fd; + } + + dword ff = pdispT->GetEvents(); + if (ff & (Dispatcher::kfRead | Dispatcher::kfRemoteClose)) { + FD_SET(fd, &fdsRead); + } + if (ff & (Dispatcher::kfWrite | Dispatcher::kfConnect)) { + FD_SET(fd, &fdsWrite); + } + } + } + + // Wait then call handlers as appropriate + // < 0 means error + // 0 means timeout + // > 0 means count of descriptors ready + + int n = select(fdMax + 1, &fdsRead, &fdsWrite, NULL, ptvWait); + + // If error, return error + + if (n < 0) { + LOG() << "select returns < 0, errno: " << errno << ", " + << Socket::GetErrorString(errno); + return false; + } + + // If timeout, return success + + if (n == 0) { + return true; + } + + // Find a dispatcher to notify, then notify outside of the loop + // and outside of the critical section. + + notifying_++; + SelectDispatcher *pdispNotify = pdispFirst_; + do { + dword ff = 0; + { + CritScope cs(&crit_); + while (pdispNotify != NULL) { + if (!fProcessIO && (pdispNotify != eventer_->dispatcher())) { + pdispNotify = pdispNotify->pdispNext_; + continue; + } + int fd = pdispNotify->descriptor(); + if (FD_ISSET(fd, &fdsRead)) { + FD_CLR(fd, &fdsRead); + if (pdispNotify->GetEvents() & Dispatcher::kfRemoteClose) { + ff |= Dispatcher::kfClose; + } else { + ff |= Dispatcher::kfRead; + } + } + if (FD_ISSET(fd, &fdsWrite)) { + FD_CLR(fd, &fdsWrite); + if (pdispNotify->GetEvents() & Dispatcher::kfConnect) { + ff |= Dispatcher::kfConnect; + } else { + ff |= Dispatcher::kfWrite; + } + } + if (ff != 0) { + break; + } + pdispNotify = pdispNotify->pdispNext_; + } + } + + // Notify outside of the critical section and the loop. + + if (pdispNotify != NULL) { + // Remember pdispNext so that it can be updated in case + // of deletion during callback + if (fProcessIO) { + pdispNotifyNext_ = pdispNotify->pdispNext_; + } + pdispNotify->OnEvent(ff); + pdispNotify = pdispNotifyNext_; + if (fProcessIO) { + pdispNotifyNext_ = NULL; + } + } + } while (pdispNotify != NULL); + notifying_--; + + // Recalc the time remaining to wait. Doing it here means it doesn't + // get calced twice the first time through the loop + + if (ctWait != kctForever) { + ptvWait->tv_sec = 0; + ptvWait->tv_usec = 0; + struct timeval tvT; + gettimeofday(&tvT, NULL); + if (tvStop.tv_sec < tvT.tv_sec) { + continue; + } + if (tvStop.tv_sec == tvT.tv_sec) { + if (tvStop.tv_usec > tvT.tv_usec) { + ptvWait->tv_usec = tvStop.tv_usec - tvT.tv_usec; + } + continue; + } + ptvWait->tv_sec = tvStop.tv_sec - tvT.tv_sec; + ptvWait->tv_usec = tvStop.tv_usec - tvT.tv_usec; + if (ptvWait->tv_usec < 0) { + ptvWait->tv_usec += 1000000; + ptvWait->tv_sec -= 1; + } + } + } + + return true; +} + +Dispatcher *SelectServer::CreateDispatcher() { + return new SelectDispatcher(this); +} + +SocketServer *SocketServer::Create() { + return new SelectServer; +} + +} // namespace base diff --git a/base/selectserver.h b/base/selectserver.h new file mode 100644 index 0000000..00aa619 --- /dev/null +++ b/base/selectserver.h @@ -0,0 +1,63 @@ +#ifndef __SELECTSERVER_H__ +#define __SELECTSERVER_H__ + +#include "inc/basictypes.h" +#include "base/criticalsection.h" +#include "base/dispatcher.h" +#include "base/eventer.h" +#include "base/socketserver.h" + +namespace base { + +class SelectDispatcher; + +// SocketServer that uses select() as the base primitive + +class SelectServer : public SocketServer { +public: + SelectServer(); + ~SelectServer(); + + virtual bool Wait(long64 ctWait, bool fProcessIO = true); + virtual void WakeUp(); + virtual Dispatcher *CreateDispatcher(); + + void Add(SelectDispatcher *pdisp); + void Remove(SelectDispatcher *pdisp); + +protected: + bool wait_; + int notifying_; + SelectDispatcher *pdispFirst_; + Eventer *eventer_; + CriticalSection crit_; + SelectDispatcher *pdispNotifyNext_; +}; + +// Dispatcher that is compatible with SelectServer + +class SelectDispatcher : public Dispatcher { +public: + SelectDispatcher(SelectServer *ss) : Dispatcher(ss), pdispNext_(NULL) {} + ~SelectDispatcher() { SetDispatchee(NULL); } + + virtual void SetDispatchee(Dispatchee *dispatchee, int descriptor = -1) { + if (dispatchee_ != NULL) { + ((SelectServer *)ss_)->Remove(this); + descriptor_ = -1; + } + dispatchee_ = dispatchee; + if (dispatchee_ != NULL) { + descriptor_ = descriptor; + ((SelectServer *)ss_)->Add(this); + } + } + +protected: + SelectDispatcher *pdispNext_; + friend class SelectServer; +}; + +} // namespace base + +#endif // __SELECTSERVER_H__ diff --git a/base/sigslot.h b/base/sigslot.h new file mode 100755 index 0000000..74781a7 --- /dev/null +++ b/base/sigslot.h @@ -0,0 +1,2703 @@ +// sigslot.h: Signal/Slot classes +// +// Written by Sarah Thompson (sarah@telergy.com) 2002. +// +// License: Public domain. You are free to use this code however you like, with the proviso that +// the author takes on no responsibility or liability for any use. +// +// QUICK DOCUMENTATION +// +// (see also the full documentation at http://sigslot.sourceforge.net/) +// +// #define switches +// SIGSLOT_PURE_ISO - Define this to force ISO C++ compliance. This also disables +// all of the thread safety support on platforms where it is +// available. +// +// SIGSLOT_USE_POSIX_THREADS - Force use of Posix threads when using a C++ compiler other than +// gcc on a platform that supports Posix threads. (When using gcc, +// this is the default - use SIGSLOT_PURE_ISO to disable this if +// necessary) +// +// SIGSLOT_DEFAULT_MT_POLICY - Where thread support is enabled, this defaults to multi_threaded_global. +// Otherwise, the default is single_threaded. #define this yourself to +// override the default. In pure ISO mode, anything other than +// single_threaded will cause a compiler error. +// +// PLATFORM NOTES +// +// Win32 - On Win32, the WIN32 symbol must be #defined. Most mainstream +// compilers do this by default, but you may need to define it +// yourself if your build environment is less standard. This causes +// the Win32 thread support to be compiled in and used automatically. +// +// Unix/Linux/BSD, etc. - If you're using gcc, it is assumed that you have Posix threads +// available, so they are used automatically. You can override this +// (as under Windows) with the SIGSLOT_PURE_ISO switch. If you're using +// something other than gcc but still want to use Posix threads, you +// need to #define SIGSLOT_USE_POSIX_THREADS. +// +// ISO C++ - If none of the supported platforms are detected, or if +// SIGSLOT_PURE_ISO is defined, all multithreading support is turned off, +// along with any code that might cause a pure ISO C++ environment to +// complain. Before you ask, gcc -ansi -pedantic won't compile this +// library, but gcc -ansi is fine. Pedantic mode seems to throw a lot of +// errors that aren't really there. If you feel like investigating this, +// please contact the author. +// +// +// THREADING MODES +// +// single_threaded - Your program is assumed to be single threaded from the point of view +// of signal/slot usage (i.e. all objects using signals and slots are +// created and destroyed from a single thread). Behaviour if objects are +// destroyed concurrently is undefined (i.e. you'll get the occasional +// segmentation fault/memory exception). +// +// multi_threaded_global - Your program is assumed to be multi threaded. Objects using signals and +// slots can be safely created and destroyed from any thread, even when +// connections exist. In multi_threaded_global mode, this is achieved by a +// single global mutex (actually a critical section on Windows because they +// are faster). This option uses less OS resources, but results in more +// opportunities for contention, possibly resulting in more context switches +// than are strictly necessary. +// +// multi_threaded_local - Behaviour in this mode is essentially the same as multi_threaded_global, +// except that each signal, and each object that inherits has_slots, all +// have their own mutex/critical section. In practice, this means that +// mutex collisions (and hence context switches) only happen if they are +// absolutely essential. However, on some platforms, creating a lot of +// mutexes can slow down the whole OS, so use this option with care. +// +// USING THE LIBRARY +// +// See the full documentation at http://sigslot.sourceforge.net/ +// +// + +#ifndef __SIGSLOT_H__ +#define __SIGSLOT_H__ + +#include +#include + +// On our copy of sigslot.h, we force single threading +#define SIGSLOT_PURE_ISO + +#if defined(SIGSLOT_PURE_ISO) || (!defined(WIN32) && !defined(__GNUG__) && !defined(SIGSLOT_USE_POSIX_THREADS)) +# define _SIGSLOT_SINGLE_THREADED +#elif defined(WIN32) +# define _SIGSLOT_HAS_WIN32_THREADS +# include +#elif defined(__GNUG__) || defined(SIGSLOT_USE_POSIX_THREADS) +# define _SIGSLOT_HAS_POSIX_THREADS +# include +#else +# define _SIGSLOT_SINGLE_THREADED +#endif + +#ifndef SIGSLOT_DEFAULT_MT_POLICY +# ifdef _SIGSLOT_SINGLE_THREADED +# define SIGSLOT_DEFAULT_MT_POLICY single_threaded +# else +# define SIGSLOT_DEFAULT_MT_POLICY multi_threaded_local +# endif +#endif + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdelete-non-virtual-dtor" + +namespace base { + + class single_threaded + { + public: + single_threaded() + { + ; + } + + virtual ~single_threaded() + { + ; + } + + virtual void lock() + { + ; + } + + virtual void unlock() + { + ; + } + }; + +#ifdef _SIGSLOT_HAS_WIN32_THREADS + // The multi threading policies only get compiled in if they are enabled. + class multi_threaded_global + { + public: + multi_threaded_global() + { + static bool isinitialised = false; + + if(!isinitialised) + { + InitializeCriticalSection(get_critsec()); + isinitialised = true; + } + } + + multi_threaded_global(const multi_threaded_global&) + { + ; + } + + virtual ~multi_threaded_global() + { + ; + } + + virtual void lock() + { + EnterCriticalSection(get_critsec()); + } + + virtual void unlock() + { + LeaveCriticalSection(get_critsec()); + } + + private: + CRITICAL_SECTION* get_critsec() + { + static CRITICAL_SECTION g_critsec; + return &g_critsec; + } + }; + + class multi_threaded_local + { + public: + multi_threaded_local() + { + InitializeCriticalSection(&m_critsec); + } + + multi_threaded_local(const multi_threaded_local&) + { + InitializeCriticalSection(&m_critsec); + } + + virtual ~multi_threaded_local() + { + DeleteCriticalSection(&m_critsec); + } + + virtual void lock() + { + EnterCriticalSection(&m_critsec); + } + + virtual void unlock() + { + LeaveCriticalSection(&m_critsec); + } + + private: + CRITICAL_SECTION m_critsec; + }; +#endif // _SIGSLOT_HAS_WIN32_THREADS + +#ifdef _SIGSLOT_HAS_POSIX_THREADS + // The multi threading policies only get compiled in if they are enabled. + class multi_threaded_global + { + public: + multi_threaded_global() + { + pthread_mutex_init(get_mutex(), NULL); + } + + multi_threaded_global(const multi_threaded_global&) + { + ; + } + + virtual ~multi_threaded_global() + { + ; + } + + virtual void lock() + { + pthread_mutex_lock(get_mutex()); + } + + virtual void unlock() + { + pthread_mutex_unlock(get_mutex()); + } + + private: + pthread_mutex_t* get_mutex() + { + static pthread_mutex_t g_mutex; + return &g_mutex; + } + }; + + class multi_threaded_local + { + public: + multi_threaded_local() + { + pthread_mutex_init(&m_mutex, NULL); + } + + multi_threaded_local(const multi_threaded_local&) + { + pthread_mutex_init(&m_mutex, NULL); + } + + virtual ~multi_threaded_local() + { + pthread_mutex_destroy(&m_mutex); + } + + virtual void lock() + { + pthread_mutex_lock(&m_mutex); + } + + virtual void unlock() + { + pthread_mutex_unlock(&m_mutex); + } + + private: + pthread_mutex_t m_mutex; + }; +#endif // _SIGSLOT_HAS_POSIX_THREADS + + template + class lock_block + { + public: + mt_policy *m_mutex; + + lock_block(mt_policy *mtx) + : m_mutex(mtx) + { + m_mutex->lock(); + } + + ~lock_block() + { + m_mutex->unlock(); + } + }; + + template + class has_slots; + + template + class _connection_base0 + { + public: + virtual has_slots* getdest() const = 0; + virtual void emit() = 0; + virtual _connection_base0* clone() = 0; + virtual _connection_base0* duplicate(has_slots* pnewdest) = 0; + }; + + template + class _connection_base1 + { + public: + virtual has_slots* getdest() const = 0; + virtual void emit(arg1_type) = 0; + virtual _connection_base1* clone() = 0; + virtual _connection_base1* duplicate(has_slots* pnewdest) = 0; + }; + + template + class _connection_base2 + { + public: + virtual has_slots* getdest() const = 0; + virtual void emit(arg1_type, arg2_type) = 0; + virtual _connection_base2* clone() = 0; + virtual _connection_base2* duplicate(has_slots* pnewdest) = 0; + }; + + template + class _connection_base3 + { + public: + virtual has_slots* getdest() const = 0; + virtual void emit(arg1_type, arg2_type, arg3_type) = 0; + virtual _connection_base3* clone() = 0; + virtual _connection_base3* duplicate(has_slots* pnewdest) = 0; + }; + + template + class _connection_base4 + { + public: + virtual has_slots* getdest() const = 0; + virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type) = 0; + virtual _connection_base4* clone() = 0; + virtual _connection_base4* duplicate(has_slots* pnewdest) = 0; + }; + + template + class _connection_base5 + { + public: + virtual has_slots* getdest() const = 0; + virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type) = 0; + virtual _connection_base5* clone() = 0; + virtual _connection_base5* duplicate(has_slots* pnewdest) = 0; + }; + + template + class _connection_base6 + { + public: + virtual has_slots* getdest() const = 0; + virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type, arg5_type, + arg6_type) = 0; + virtual _connection_base6* clone() = 0; + virtual _connection_base6* duplicate(has_slots* pnewdest) = 0; + }; + + template + class _connection_base7 + { + public: + virtual has_slots* getdest() const = 0; + virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type, arg5_type, + arg6_type, arg7_type) = 0; + virtual _connection_base7* clone() = 0; + virtual _connection_base7* duplicate(has_slots* pnewdest) = 0; + }; + + template + class _connection_base8 + { + public: + virtual has_slots* getdest() const = 0; + virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type, arg5_type, + arg6_type, arg7_type, arg8_type) = 0; + virtual _connection_base8* clone() = 0; + virtual _connection_base8* duplicate(has_slots* pnewdest) = 0; + }; + + template + class _signal_base : public mt_policy + { + public: + virtual void slot_disconnect(has_slots* pslot) = 0; + virtual void slot_duplicate(const has_slots* poldslot, has_slots* pnewslot) = 0; + }; + + template + class has_slots : public mt_policy + { + private: + typedef typename std::set<_signal_base *> sender_set; + typedef typename sender_set::const_iterator const_iterator; + + public: + has_slots() + { + ; + } + + has_slots(const has_slots& hs) + : mt_policy(hs) + { + lock_block lock(this); + const_iterator it = hs.m_senders.begin(); + const_iterator itEnd = hs.m_senders.end(); + + while(it != itEnd) + { + (*it)->slot_duplicate(&hs, this); + m_senders.insert(*it); + ++it; + } + } + + void signal_connect(_signal_base* sender) + { + lock_block lock(this); + m_senders.insert(sender); + } + + void signal_disconnect(_signal_base* sender) + { + lock_block lock(this); + m_senders.erase(sender); + } + + virtual ~has_slots() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block lock(this); + const_iterator it = m_senders.begin(); + const_iterator itEnd = m_senders.end(); + + while(it != itEnd) + { + (*it)->slot_disconnect(this); + ++it; + } + + m_senders.erase(m_senders.begin(), m_senders.end()); + } + + private: + sender_set m_senders; + }; + + template + class _signal_base0 : public _signal_base + { + public: + typedef std::list<_connection_base0 *> connections_list; + + _signal_base0() + { + ; + } + + _signal_base0(const _signal_base0& s) + : _signal_base(s) + { + lock_block lock(this); + typename connections_list::const_iterator it = s.m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + ~_signal_base0() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + +#ifdef _DEBUG + bool connected(has_slots* pclass) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + while(it != itEnd) + { + itNext = it; + ++itNext; + if ((*it)->getdest() == pclass) + return true; + it = itNext; + } + return false; + } +#endif + + void disconnect(has_slots* pclass) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots* pslot) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + typename connections_list::iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + void slot_duplicate(const has_slots* oldtarget, has_slots* newtarget) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + protected: + connections_list m_connected_slots; + }; + + template + class _signal_base1 : public _signal_base + { + public: + typedef std::list<_connection_base1 *> connections_list; + + _signal_base1() + { + ; + } + + _signal_base1(const _signal_base1& s) + : _signal_base(s) + { + lock_block lock(this); + typename connections_list::const_iterator it = s.m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots* oldtarget, has_slots* newtarget) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base1() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + +#ifdef _DEBUG + bool connected(has_slots* pclass) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + while(it != itEnd) + { + itNext = it; + ++itNext; + if ((*it)->getdest() == pclass) + return true; + it = itNext; + } + return false; + } +#endif + + void disconnect(has_slots* pclass) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots* pslot) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + typename connections_list::iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + + protected: + connections_list m_connected_slots; + }; + + template + class _signal_base2 : public _signal_base + { + public: + typedef std::list<_connection_base2 *> + connections_list; + + _signal_base2() + { + ; + } + + _signal_base2(const _signal_base2& s) + : _signal_base(s) + { + lock_block lock(this); + typename connections_list::const_iterator it = s.m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots* oldtarget, has_slots* newtarget) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base2() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + +#ifdef _DEBUG + bool connected(has_slots* pclass) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + while(it != itEnd) + { + itNext = it; + ++itNext; + if ((*it)->getdest() == pclass) + return true; + it = itNext; + } + return false; + } +#endif + + void disconnect(has_slots* pclass) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots* pslot) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + typename connections_list::iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + template + class _signal_base3 : public _signal_base + { + public: + typedef std::list<_connection_base3 *> + connections_list; + + _signal_base3() + { + ; + } + + _signal_base3(const _signal_base3& s) + : _signal_base(s) + { + lock_block lock(this); + typename connections_list::const_iterator it = s.m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots* oldtarget, has_slots* newtarget) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base3() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + +#ifdef _DEBUG + bool connected(has_slots* pclass) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + while(it != itEnd) + { + itNext = it; + ++itNext; + if ((*it)->getdest() == pclass) + return true; + it = itNext; + } + return false; + } +#endif + + void disconnect(has_slots* pclass) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots* pslot) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + typename connections_list::iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + template + class _signal_base4 : public _signal_base + { + public: + typedef std::list<_connection_base4 *> connections_list; + + _signal_base4() + { + ; + } + + _signal_base4(const _signal_base4& s) + : _signal_base(s) + { + lock_block lock(this); + typename connections_list::const_iterator it = s.m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots* oldtarget, has_slots* newtarget) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base4() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + +#ifdef _DEBUG + bool connected(has_slots* pclass) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + while(it != itEnd) + { + itNext = it; + ++itNext; + if ((*it)->getdest() == pclass) + return true; + it = itNext; + } + return false; + } +#endif + + void disconnect(has_slots* pclass) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots* pslot) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + typename connections_list::iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + template + class _signal_base5 : public _signal_base + { + public: + typedef std::list<_connection_base5 *> connections_list; + + _signal_base5() + { + ; + } + + _signal_base5(const _signal_base5& s) + : _signal_base(s) + { + lock_block lock(this); + typename connections_list::const_iterator it = s.m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots* oldtarget, has_slots* newtarget) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base5() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + +#ifdef _DEBUG + bool connected(has_slots* pclass) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + while(it != itEnd) + { + itNext = it; + ++itNext; + if ((*it)->getdest() == pclass) + return true; + it = itNext; + } + return false; + } +#endif + + void disconnect(has_slots* pclass) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots* pslot) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + typename connections_list::iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + template + class _signal_base6 : public _signal_base + { + public: + typedef std::list<_connection_base6 *> connections_list; + + _signal_base6() + { + ; + } + + _signal_base6(const _signal_base6& s) + : _signal_base(s) + { + lock_block lock(this); + typename connections_list::const_iterator it = s.m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots* oldtarget, has_slots* newtarget) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base6() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + +#ifdef _DEBUG + bool connected(has_slots* pclass) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + while(it != itEnd) + { + itNext = it; + ++itNext; + if ((*it)->getdest() == pclass) + return true; + it = itNext; + } + return false; + } +#endif + + void disconnect(has_slots* pclass) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots* pslot) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + typename connections_list::iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + template + class _signal_base7 : public _signal_base + { + public: + typedef std::list<_connection_base7 *> connections_list; + + _signal_base7() + { + ; + } + + _signal_base7(const _signal_base7& s) + : _signal_base(s) + { + lock_block lock(this); + typename connections_list::const_iterator it = s.m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots* oldtarget, has_slots* newtarget) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base7() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + +#ifdef _DEBUG + bool connected(has_slots* pclass) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + while(it != itEnd) + { + itNext = it; + ++itNext; + if ((*it)->getdest() == pclass) + return true; + it = itNext; + } + return false; + } +#endif + + void disconnect(has_slots* pclass) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots* pslot) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + typename connections_list::iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + template + class _signal_base8 : public _signal_base + { + public: + typedef std::list<_connection_base8 *> + connections_list; + + _signal_base8() + { + ; + } + + _signal_base8(const _signal_base8& s) + : _signal_base(s) + { + lock_block lock(this); + typename connections_list::const_iterator it = s.m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots* oldtarget, has_slots* newtarget) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base8() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + +#ifdef _DEBUG + bool connected(has_slots* pclass) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + while(it != itEnd) + { + itNext = it; + ++itNext; + if ((*it)->getdest() == pclass) + return true; + it = itNext; + } + return false; + } +#endif + + void disconnect(has_slots* pclass) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots* pslot) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + typename connections_list::iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + + template + class _connection0 : public _connection_base0 + { + public: + _connection0() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection0(dest_type* pobject, void (dest_type::*pmemfun)()) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual _connection_base0* clone() + { + return new _connection0(*this); + } + + virtual _connection_base0* duplicate(has_slots* pnewdest) + { + return new _connection0((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit() + { + (m_pobject->*m_pmemfun)(); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(); + }; + + template + class _connection1 : public _connection_base1 + { + public: + _connection1() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection1(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual _connection_base1* clone() + { + return new _connection1(*this); + } + + virtual _connection_base1* duplicate(has_slots* pnewdest) + { + return new _connection1((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1) + { + (m_pobject->*m_pmemfun)(a1); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type); + }; + + template + class _connection2 : public _connection_base2 + { + public: + _connection2() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection2(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual _connection_base2* clone() + { + return new _connection2(*this); + } + + virtual _connection_base2* duplicate(has_slots* pnewdest) + { + return new _connection2((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2) + { + (m_pobject->*m_pmemfun)(a1, a2); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type); + }; + + template + class _connection3 : public _connection_base3 + { + public: + _connection3() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection3(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual _connection_base3* clone() + { + return new _connection3(*this); + } + + virtual _connection_base3* duplicate(has_slots* pnewdest) + { + return new _connection3((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3) + { + (m_pobject->*m_pmemfun)(a1, a2, a3); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type); + }; + + template + class _connection4 : public _connection_base4 + { + public: + _connection4() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection4(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual _connection_base4* clone() + { + return new _connection4(*this); + } + + virtual _connection_base4* duplicate(has_slots* pnewdest) + { + return new _connection4((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, + arg4_type a4) + { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, + arg4_type); + }; + + template + class _connection5 : public _connection_base5 + { + public: + _connection5() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection5(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual _connection_base5* clone() + { + return new _connection5(*this); + } + + virtual _connection_base5* duplicate(has_slots* pnewdest) + { + return new _connection5((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5) + { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type); + }; + + template + class _connection6 : public _connection_base6 + { + public: + _connection6() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection6(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual _connection_base6* clone() + { + return new _connection6(*this); + } + + virtual _connection_base6* duplicate(has_slots* pnewdest) + { + return new _connection6((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6) + { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5, a6); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type); + }; + + template + class _connection7 : public _connection_base7 + { + public: + _connection7() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection7(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, arg7_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual _connection_base7* clone() + { + return new _connection7(*this); + } + + virtual _connection_base7* duplicate(has_slots* pnewdest) + { + return new _connection7((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7) + { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5, a6, a7); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type); + }; + + template + class _connection8 : public _connection_base8 + { + public: + _connection8() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection8(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, + arg7_type, arg8_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual _connection_base8* clone() + { + return new _connection8(*this); + } + + virtual _connection_base8* duplicate(has_slots* pnewdest) + { + return new _connection8((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7, arg8_type a8) + { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5, a6, a7, a8); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, arg8_type); + }; + + template + class signal0 : public _signal_base0 + { + public: + typedef _signal_base0 base; + typedef typename base::connections_list connections_list; + using base::m_connected_slots; + + signal0() + { + ; + } + + signal0(const signal0& s) + : _signal_base0(s) + { + ; + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)()) + { + lock_block lock(this); + _connection0* conn = + new _connection0(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit() + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(); + + it = itNext; + } + } + + void operator()() + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(); + + it = itNext; + } + } + }; + + template + class signal1 : public _signal_base1 + { + public: + typedef _signal_base1 base; + typedef typename base::connections_list connections_list; + using base::m_connected_slots; + + signal1() + { + ; + } + + signal1(const signal1& s) + : _signal_base1(s) + { + ; + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type)) + { + lock_block lock(this); + _connection1* conn = + new _connection1(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1); + + it = itNext; + } + } + + void operator()(arg1_type a1) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1); + + it = itNext; + } + } + }; + + template + class signal2 : public _signal_base2 + { + public: + typedef _signal_base2 base; + typedef typename base::connections_list connections_list; + using base::m_connected_slots; + + signal2() + { + ; + } + + signal2(const signal2& s) + : _signal_base2(s) + { + ; + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type)) + { + lock_block lock(this); + _connection2* conn = new + _connection2(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2); + + it = itNext; + } + } + }; + + template + class signal3 : public _signal_base3 + { + public: + typedef _signal_base3 base; + typedef typename base::connections_list connections_list; + using base::m_connected_slots; + + signal3() + { + ; + } + + signal3(const signal3& s) + : _signal_base3(s) + { + ; + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type)) + { + lock_block lock(this); + _connection3* conn = + new _connection3(pclass, + pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2, arg3_type a3) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3); + + it = itNext; + } + } + }; + + template + class signal4 : public _signal_base4 + { + public: + typedef _signal_base4 base; + typedef typename base::connections_list connections_list; + using base::m_connected_slots; + + signal4() + { + ; + } + + signal4(const signal4& s) + : _signal_base4(s) + { + ; + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type)) + { + lock_block lock(this); + _connection4* + conn = new _connection4(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4); + + it = itNext; + } + } + }; + + template + class signal5 : public _signal_base5 + { + public: + typedef _signal_base5 base; + typedef typename base::connections_list connections_list; + using base::m_connected_slots; + + signal5() + { + ; + } + + signal5(const signal5& s) + : _signal_base5(s) + { + ; + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type)) + { + lock_block lock(this); + _connection5* conn = new _connection5(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5); + + it = itNext; + } + } + }; + + + template + class signal6 : public _signal_base6 + { + public: + typedef _signal_base6 base; + typedef typename base::connections_list connections_list; + using base::m_connected_slots; + + signal6() + { + ; + } + + signal6(const signal6& s) + : _signal_base6(s) + { + ; + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type)) + { + lock_block lock(this); + _connection6* conn = + new _connection6(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5, a6); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5, a6); + + it = itNext; + } + } + }; + + template + class signal7 : public _signal_base7 + { + public: + typedef _signal_base7 base; + typedef typename base::connections_list connections_list; + using base::m_connected_slots; + + signal7() + { + ; + } + + signal7(const signal7& s) + : _signal_base7(s) + { + ; + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, + arg7_type)) + { + lock_block lock(this); + _connection7* conn = + new _connection7(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5, a6, a7); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5, a6, a7); + + it = itNext; + } + } + }; + + template + class signal8 : public _signal_base8 + { + public: + typedef _signal_base8 base; + typedef typename base::connections_list connections_list; + using base::m_connected_slots; + + signal8() + { + ; + } + + signal8(const signal8& s) + : _signal_base8(s) + { + ; + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, + arg7_type, arg8_type)) + { + lock_block lock(this); + _connection8* conn = + new _connection8(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7, arg8_type a8) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5, a6, a7, a8); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7, arg8_type a8) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5, a6, a7, a8); + + it = itNext; + } + } + }; + +}; // namespace base + +#pragma clang diagnostic pop + +#endif // __SIGSLOT_H__ diff --git a/base/socket.cpp b/base/socket.cpp new file mode 100644 index 0000000..8b28114 --- /dev/null +++ b/base/socket.cpp @@ -0,0 +1,332 @@ +#include "base/socket.h" +#include "base/socketserver.h" +#include "base/socketaddress.h" +#include "base/misc.h" +#include +#include +#include +#include + +namespace base { + +Socket::Socket(SocketServer *ss) : Dispatchee(ss), s_(-1), notify_(NULL), + error_(0), state_(CS_CLOSED), type_(0) { +} + +Socket::~Socket() { + Close(); +} + +bool Socket::Attach(int s, SocketNotify *notify) { + Close(); + s_ = s; + if (s_ == -1) { + return false; + } + state_ = CS_CONNECTED; + SetNotify(notify); + return true; +} + +bool Socket::Create(int type, SocketNotify *notify) { + Close(); + s_ = ::socket(AF_INET, type, 0); + UpdateLastError(); + SetNotify(notify); + + // Turn off nagle + int value = 1; + setsockopt(s_, IPPROTO_TCP, TCP_NODELAY, &value, sizeof(value)); + type_ = type; + + // Return EPIPE instead of raising SIGPIPE +#ifdef SO_NOSIGPIPE + value = 1; + setsockopt(s_, SOL_SOCKET, SO_NOSIGPIPE, (void *)&value, sizeof(value)); +#endif + + return s_ != -1; +} + +SocketAddress Socket::GetLocalAddress() const { + sockaddr_in addr; + socklen_t addrlen = sizeof(addr); + int result = ::getsockname(s_, (sockaddr*)&addr, &addrlen); + SocketAddress address; + if (result >= 0) { + address.FromSockAddr(addr); + } + return address; +} + +SocketAddress Socket::GetRemoteAddress() const { + sockaddr_in addr; + socklen_t addrlen = sizeof(addr); + int result = ::getpeername(s_, (sockaddr*)&addr, &addrlen); + SocketAddress address; + if (result >= 0) { + address.FromSockAddr(addr); + } + return address; +} + +int Socket::Bind(const SocketAddress& addr, bool reuse) { + if (reuse) { + int n = 1; + int err = setsockopt(s_, SOL_SOCKET, SO_REUSEADDR, (void *)&n, + sizeof(n)); + if (err < 0) { + UpdateLastError(); + return err; + } + } + + sockaddr_in saddr; + addr.ToSockAddr(&saddr); + int err = ::bind(s_, (sockaddr*)&saddr, sizeof(saddr)); + UpdateLastError(); + return err; +} + +int Socket::Connect(const SocketAddress& addr) { + if (s_ == -1 && !Create(SOCK_STREAM)) { + return -1; + } + SocketAddress addr2(addr); + if (addr2.IsUnresolved()) { + addr2.Resolve(); + } + sockaddr_in saddr; + addr2.ToSockAddr(&saddr); + int err = ::connect(s_, (sockaddr*)&saddr, sizeof(saddr)); + UpdateLastError(); + if (err == 0) { + state_ = CS_CONNECTED; + } else if (IsBlocking()) { + state_ = CS_CONNECTING; + dispatcher_->SetEvents(Dispatcher::kfConnect); + } + dispatcher_->SetEvents(Dispatcher::kfRead | Dispatcher::kfWrite); + return err; +} + +int Socket::GetError() const { + return error_; +} + +void Socket::SetError(int error) { + error_ = error; +} + +Socket::ConnState Socket::GetState() const { + return state_; +} + +int Socket::Send(const void *pv, size_t cb) { + int sent = ::send(s_, reinterpret_cast(pv), (int)cb, 0); + UpdateLastError(); + if ((sent < 0) && IsBlocking()) { + dispatcher_->SetEvents(Dispatcher::kfWrite); + } + return sent; +} + +int Socket::SendTo(const void *pv, size_t cb, const SocketAddress& addr) { + sockaddr_in saddr; + addr.ToSockAddr(&saddr); + int sent = ::sendto( + s_, (const char *)pv, (int)cb, 0, (sockaddr*)&saddr, + sizeof(saddr)); + UpdateLastError(); + if ((sent < 0) && IsBlocking()) { + dispatcher_->SetEvents(Dispatcher::kfWrite); + } + return sent; +} + +int Socket::Recv(void *pv, size_t cb) { + int received = ::recv(s_, (char *)pv, (int)cb, 0); + if ((received == 0) && (cb != 0)) { + dispatcher_->SetEvents(Dispatcher::kfRead | Dispatcher::kfRemoteClose); + error_ = EWOULDBLOCK; + return -1; + } + UpdateLastError(); + if ((received >= 0) || IsBlocking()) { + dispatcher_->SetEvents(Dispatcher::kfRead); + } + return received; +} + +int Socket::RecvFrom(void *pv, size_t cb, SocketAddress *paddr) { + sockaddr_in saddr; + socklen_t cbAddr = sizeof(saddr); + int received = ::recvfrom(s_, (char *)pv, (int)cb, 0, (sockaddr*)&saddr, + &cbAddr); + UpdateLastError(); + if (received >= 0 && paddr != NULL) { + paddr->FromSockAddr(saddr); + } + if (received >= 0 || IsBlocking()) { + dispatcher_->SetEvents(Dispatcher::kfRead); + } + return received; +} + +int Socket::Listen(int backlog) { + int err = ::listen(s_, backlog); + UpdateLastError(); + if (err == 0) + state_ = CS_CONNECTING; + dispatcher_->SetEvents(Dispatcher::kfRead); + return err; +} + +Socket *Socket::Accept(SocketAddress *paddr, SocketNotify *notify) { + sockaddr_in saddr; + socklen_t cbAddr = sizeof(saddr); + int s = ::accept(s_, (sockaddr*)&saddr, &cbAddr); + UpdateLastError(); + if (s == -1) { + return NULL; + } + if (paddr != NULL) { + paddr->FromSockAddr(saddr); + } + dispatcher_->SetEvents(Dispatcher::kfRead | Dispatcher::kfWrite); + return dispatcher_->ss()->WrapSocket(s, notify); +} + +int Socket::Close() { + if (s_ == -1) { + return 0; + } + int err = close(s_); + UpdateLastError(); + s_ = -1; + state_ = CS_CLOSED; + SetNotify(NULL); + return err; +} + +void Socket::UpdateLastError() { + error_ = errno; +} + +bool Socket::IsBlocking() const { + return error_ == EWOULDBLOCK || error_ == EAGAIN || error_ == EINPROGRESS; +} + +void Socket::OnEvent(dword ff) { + if (ff & Dispatcher::kfConnect) { + state_ = CS_CONNECTED; + } + + if (ff & Dispatcher::kfRead) { + dispatcher_->ClearEvents(Dispatcher::kfRead); + if (notify_ != NULL) { + DeleteRecord dr(dispatcher_); + notify_->OnReadEvent(this); + if (dr.deleted()) { + return; + } + } + } + if (ff & Dispatcher::kfWrite) { + dispatcher_->ClearEvents(Dispatcher::kfWrite); + if (notify_ != NULL) { + DeleteRecord dr(dispatcher_); + notify_->OnWriteEvent(this); + if (dr.deleted()) { + return; + } + } + } + if (ff & Dispatcher::kfConnect) { + dispatcher_->ClearEvents(Dispatcher::kfConnect); + if (notify_ != NULL) { + DeleteRecord dr(dispatcher_); + notify_->OnConnectEvent(this); + if (dr.deleted()) { + return; + } + } + } + if (ff & Dispatcher::kfClose) { + state_ = CS_CLOSED; + if (notify_ != NULL) { + DeleteRecord dr(dispatcher_); + notify_->OnCloseEvent(this); + if (dr.deleted()) { + return; + } + } + } +} + +void Socket::SetNotify(SocketNotify *notify) { + if (notify_ != NULL) { + dispatcher_->SetDispatchee(NULL); + dispatcher_->ClearEvents(); + fcntl(s_, F_SETFL, fcntl(s_, F_GETFL, 0) & ~O_NONBLOCK); + } + notify_ = notify; + if (notify_ != NULL) { + fcntl(s_, F_SETFL, fcntl(s_, F_GETFL, 0) | O_NONBLOCK); + dispatcher_->SetDispatchee(this, s_); + if (state_ == CS_CONNECTED || type_ != SOCK_STREAM) { + dispatcher_->SetEvents(Dispatcher::kfRead | Dispatcher::kfWrite); + } + } +} + +STARTLABEL(SocErrors) + LABEL(EINTR) + LABEL(EBADF) + LABEL(EACCES) + LABEL(EFAULT) + LABEL(EINVAL) + LABEL(EMFILE) + LABEL(EWOULDBLOCK) + LABEL(EINPROGRESS) + LABEL(EALREADY) + LABEL(ENOTSOCK) + LABEL(EDESTADDRREQ) + LABEL(EMSGSIZE) + LABEL(EPROTOTYPE) + LABEL(ENOPROTOOPT) + LABEL(EPROTONOSUPPORT) + LABEL(ESOCKTNOSUPPORT) + LABEL(EOPNOTSUPP) + LABEL(EPFNOSUPPORT) + LABEL(EAFNOSUPPORT) + LABEL(EADDRINUSE) + LABEL(EADDRNOTAVAIL) + LABEL(ENETDOWN) + LABEL(ENETUNREACH) + LABEL(ENETRESET) + LABEL(ECONNABORTED) + LABEL(ECONNRESET) + LABEL(ENOBUFS) + LABEL(EISCONN) + LABEL(ENOTCONN) + LABEL(ESHUTDOWN) + LABEL(ETOOMANYREFS) + LABEL(ETIMEDOUT) + LABEL(ECONNREFUSED) + LABEL(ELOOP) + LABEL(ENAMETOOLONG) + LABEL(EHOSTDOWN) + LABEL(EHOSTUNREACH) + LABEL(ENOTEMPTY) + LABEL(EUSERS) + LABEL(EDQUOT) + LABEL(ESTALE) + LABEL(EREMOTE) +ENDLABEL(SocErrors) + +const char *Socket::GetErrorString(int error) { + return SocErrors.Find(error); +} + +} // namespace base diff --git a/base/socket.h b/base/socket.h new file mode 100644 index 0000000..0bdcee4 --- /dev/null +++ b/base/socket.h @@ -0,0 +1,73 @@ +#ifndef __SOCKET_H__ +#define __SOCKET_H__ + +#include +#include +#include +#include +#include +#include "base/dispatcher.h" +#include "inc/basictypes.h" + +namespace base { + +class SocketServer; +class SocketNotify; +class SocketAddress; + +class Socket : Dispatchee { +public: + Socket(SocketServer *ss); + virtual ~Socket(); + + virtual bool Attach(int s, SocketNotify *notify = NULL); + virtual bool Create(int type, SocketNotify *notify = NULL); + virtual int Bind(const SocketAddress& addr, bool reuse = true); + virtual int Connect(const SocketAddress& addr); + virtual int Send(const void *pv, size_t cb); + virtual int SendTo(const void *pv, size_t cb, const SocketAddress& addr); + virtual int Recv(void *pv, size_t cb); + virtual int RecvFrom(void *pv, size_t cb, SocketAddress *paddr); + virtual int Listen(int backlog = SOMAXCONN); + virtual Socket *Accept(SocketAddress *paddr, SocketNotify *notify = NULL); + virtual int Close(); + virtual int GetError() const; + virtual void SetError(int error); + virtual bool IsBlocking() const; + virtual SocketAddress GetLocalAddress() const; + virtual SocketAddress GetRemoteAddress() const; + virtual void SetNotify(SocketNotify *notify); + + enum ConnState { + CS_CLOSED, + CS_CONNECTING, + CS_CONNECTED + }; + virtual ConnState GetState() const; + static const char *GetErrorString(int error); + +protected: + void UpdateLastError(); + + // Dispatchee interface + virtual void OnEvent(dword ff); + + int type_; + int s_; + int error_; + ConnState state_; + SocketNotify *notify_; +}; + +// Socket notification interface for async sockets. +class SocketNotify { +public: + virtual void OnConnectEvent(Socket *socket) = 0; + virtual void OnReadEvent(Socket *socket) = 0; + virtual void OnWriteEvent(Socket *socket) = 0; + virtual void OnCloseEvent(Socket *socket) = 0; +}; + +} // namespace base + +#endif // __SOCKET_H__ diff --git a/base/socketaddress.cpp b/base/socketaddress.cpp new file mode 100644 index 0000000..adc822b --- /dev/null +++ b/base/socketaddress.cpp @@ -0,0 +1,326 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "base/socketaddress.h" + +namespace wi { +extern void strncpyz(char *pszDst, const char *pszSrc, int cb); +} + +namespace base { + +SocketAddress::SocketAddress() { + hostname_ = NULL; + Clear(); +} + +SocketAddress::~SocketAddress() { + Clear(); +} + +SocketAddress::SocketAddress(const char *hostname, int port, bool use_dns) { + hostname_ = NULL; + Clear(); + SetIP(hostname, use_dns); + SetPort(port); +} + +SocketAddress::SocketAddress(const char *hostport, bool use_dns) { + hostname_ = NULL; + Clear(); + + // Parse hostname:port format + const char *colon = NULL; + for (const char *s = hostport; *s != 0; s++) { + if (*s == ':') { + colon = s; + } + } + + if (colon == NULL) { + // Just hostname + SetIP(hostport, use_dns); + } else { + // Hostname:port + char hostnameT[256]; + wi::strncpyz(hostnameT, hostport, colon - hostport + 1); + long port = strtol(colon + 1, NULL, 10); + SetIP(hostnameT, use_dns); + SetPort(port); + } +} + +SocketAddress::SocketAddress(dword ip, int port) { + hostname_ = NULL; + Clear(); + SetIP(ip); + SetPort(port); +} + +SocketAddress::SocketAddress(const SocketAddress& addr) { + hostname_ = NULL; + Clear(); + this->operator=(addr); +} + +void SocketAddress::Clear() { + delete[] hostname_; + hostname_ = NULL; + ip_ = 0; + port_ = 0; +} + +SocketAddress& SocketAddress::operator=(const SocketAddress& addr) { + SetHostname(addr.hostname_); + ip_ = addr.ip_; + port_ = addr.port_; + return *this; +} + +void SocketAddress::SetHostname(const char *hostname) { + if (hostname == hostname_) + return; + delete[] hostname_; + hostname_ = NULL; + if (hostname != NULL) { + int cb = strlen(hostname) + 1; + hostname_ = new char[cb]; + wi::strncpyz(hostname_, hostname, cb); + } +} + +bool SocketAddress::IsHostnameEmpty() const { + return (hostname_ == NULL || hostname_[0] == 0); +} + +void SocketAddress::SetIP(dword ip) { + SetHostname(NULL); + ip_ = ip; +} + +bool SocketAddress::SetIP(const char *hostname, bool use_dns) { + SetHostname(hostname); + ip_ = 0; + return Resolve(true, use_dns); +} + +void SocketAddress::SetResolvedIP(dword ip) { + ip_ = ip; +} + +void SocketAddress::SetPort(int port) { + port_ = port; +} + +dword SocketAddress::ip() const { + return ip_; +} + +word SocketAddress::port() const { + return port_; +} + +void SocketAddress::IPAsString(char *psz, int cb) const { + if (!IsHostnameEmpty()) { + wi::strncpyz(psz, hostname_, cb); + return; + } + IPToString(ip_, psz, cb); +} + +void SocketAddress::PortAsString(char *psz, int cb) const { + char szT[32]; + sprintf(szT, "%d", port_); + wi::strncpyz(psz, szT, cb); +} + +void SocketAddress::ToString(char *psz, int cb) const { + IPAsString(psz, cb); + int cbT = strlen(psz); + cb -= cbT + 1; + psz = &psz[cbT]; + if (cb <= 0) + return; + *psz++ = ':'; + cb--; + PortAsString(psz, cb); +} + +const char *SocketAddress::ToString() const { + static char s_szT[128]; + ToString(s_szT, sizeof(s_szT)); + return s_szT; +} + +bool SocketAddress::IsAny() const { + return (ip_ == 0); +} + +bool SocketAddress::IsLocalIP() const { + return (ip_ >> 24) == 127; +} + +bool SocketAddress::IsPrivateIP() const { + return ((ip_ >> 24) == 127) || + ((ip_ >> 24) == 10) || + ((ip_ >> 20) == ((172 << 4) | 1)) || + ((ip_ >> 16) == ((192 << 8) | 168)); +} + +bool SocketAddress::IsUnresolved() const { + return IsAny() && !IsHostnameEmpty(); +} + +bool SocketAddress::Resolve(bool force, bool use_dns) { + if (IsHostnameEmpty()) { + // nothing to resolve + } else if (!force && !IsAny()) { + // already resolved + } else if (dword ip = StringToIP(hostname_, use_dns)) { + ip_ = ip; + } else { + return false; + } + return true; +} + +bool SocketAddress::operator ==(const SocketAddress& addr) const { + return EqualIPs(addr) && EqualPorts(addr); +} + +bool SocketAddress::EqualIPs(const SocketAddress& addr) const { + + if (ip_ != addr.ip_) { + return false; + } + if (ip_ != 0) { + return true; + } + if (IsHostnameEmpty() != addr.IsHostnameEmpty()) { + return false; + } + if (hostname_ == NULL) { + return true; + } + return strcmp(hostname_, addr.hostname_) == 0; +} + +bool SocketAddress::EqualPorts(const SocketAddress& addr) const { + return (port_ == addr.port_); +} + +dword SocketAddress::Hash() const { + dword h = 0; + h ^= ip_; + h ^= port_ | (port_ << 16); + return h; +} + +void SocketAddress::ToSockAddr(sockaddr_in* saddr) const { + memset(saddr, 0, sizeof(*saddr)); + saddr->sin_family = AF_INET; + saddr->sin_port = htons(port_); + if (0 == ip_) { + saddr->sin_addr.s_addr = INADDR_ANY; + } else { + saddr->sin_addr.s_addr = htonl(ip_); + } +} + +void SocketAddress::FromSockAddr(const sockaddr_in& saddr) { + SetIP(ntohl(saddr.sin_addr.s_addr)); + SetPort(ntohs(saddr.sin_port)); +} + +void SocketAddress::IPToString(dword ip, char *psz, int cb) const { + char szT[64]; + sprintf(szT, "%d.%d.%d.%d", (int)((ip >> 24) & 0xff), + (int)((ip >> 16) & 0xff), (int)((ip >> 8) & 0xff), + (int)((ip >> 0) & 0xff)); + wi::strncpyz(psz, szT, cb); +} + +dword SocketAddress::StringToIP(const char *hostname, bool use_dns) { + in_addr addr; + if (inet_aton(hostname, &addr) != 0) { + return ntohl(addr.s_addr); + } else if (use_dns) { + hostent *phost = gethostbyname(hostname); + if (phost == NULL) { + return 0; + } + return ntohl(*reinterpret_cast(phost->h_addr_list[0])); + } + return 0; +} + +void SocketAddress::GetHostname(char *psz, int cb) { + char hostname[256]; + if (gethostname(hostname, sizeof(hostname)) == 0) { + wi::strncpyz(psz, hostname, cb); + } else { + if (cb > 0) { + *psz = 0; + } + } +} + +bool SocketAddress::GetNetworkInfo(int n, char *pszName, int cb, dword *pip) { + int fd; + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + return false; + } + + struct ifconf ifc; + ifc.ifc_len = 64 * sizeof(struct ifreq); + ifc.ifc_buf = new char[ifc.ifc_len]; + + if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) { + delete [] ifc.ifc_buf; + close(fd); + return false; + } + + int nT = 0; + struct ifreq* ptr = reinterpret_cast(ifc.ifc_buf); + struct ifreq* end = reinterpret_cast(ifc.ifc_buf + ifc.ifc_len); + while (ptr < end) { + struct sockaddr_in* inaddr = reinterpret_cast(&ptr->ifr_ifru.ifru_addr); + if (inaddr->sin_family == AF_INET) { + if (nT == n) { + wi::strncpyz(pszName, ptr->ifr_name, cb); + *pip = ntohl(inaddr->sin_addr.s_addr); + delete [] ifc.ifc_buf; + close(fd); + return true; + } + nT++; + } + +#ifdef _SIZEOF_ADDR_IFREQ + ptr = reinterpret_cast( + reinterpret_cast(ptr) + _SIZEOF_ADDR_IFREQ(*ptr)); +#else + ptr++; +#endif + } + + delete [] ifc.ifc_buf; + close(fd); + + return false; +} + +} // namespace base diff --git a/base/socketaddress.h b/base/socketaddress.h new file mode 100644 index 0000000..4f2e91d --- /dev/null +++ b/base/socketaddress.h @@ -0,0 +1,141 @@ +#ifndef __SOCKETADDRESS_H__ +#define __SOCKETADDRESS_H__ + +#include +#include "inc/basictypes.h" + +#undef SetPort + +namespace base { + +// Records an IP address and port, which are 32 and 16 bit integers, +// respectively, both in host byte-order. +class SocketAddress { +public: + // Creates a missing / unknown address. + SocketAddress(); + ~SocketAddress(); + + // Creates the address with the given host and port. If use_dns is true, + // the hostname will be immediately resolved to an IP (which may block for + // several seconds if DNS is not available). Alternately, set use_dns to + // false, and then call Resolve() to complete resolution later, or use + // SetResolvedIP to set the IP explictly. + SocketAddress(const char *hostname, int port, bool use_dns = true); + + // Creates address with host:port format string + SocketAddress(const char *hostport, bool use_dns = true); + + // Creates the address with the given IP and port. + SocketAddress(dword ip, int port); + + // Creates a copy of the given address. + SocketAddress(const SocketAddress& addr); + + // Resets to missing / unknown address. + void Clear(); + + // Replaces our address with the given one. + SocketAddress& operator =(const SocketAddress& addr); + + // Changes the IP of this address to the given one, and clears the hostname. + void SetIP(dword ip); + + // Changes the hostname of this address to the given one. + // Calls Resolve and returns the result. + bool SetIP(const char *hostname, bool use_dns = true); + + // Sets the IP address while retaining the hostname. Useful for bypassing + // DNS for a pre-resolved IP. + void SetResolvedIP(dword ip); + + // Changes the port of this address to the given one. + void SetPort(int port); + + // Returns the IP address. + dword ip() const; + + // Returns the port part of this address. + word port() const; + + // Returns the hostname + void hostname(char *psz, int cb); + + // Returns the IP address in dotted form. + void IPAsString(char *psz, int cb) const; + + // Returns the port as a string + void PortAsString(char *psz, int cb) const; + + // Returns a display version of the IP/port. + void ToString(char *psz, int cb) const; + const char *ToString() const; + + // Determines whether this represents a missing / any address. + bool IsAny() const; + + // Synomym for missing / any. + bool IsNil() const { return IsAny(); } + + // Determines whether the IP address refers to the local host, i.e. within + // the range 127.0.0.0/8. + bool IsLocalIP() const; + + // Determines whether the IP address is in one of the private ranges: + // 127.0.0.0/8 10.0.0.0/8 192.168.0.0/16 172.16.0.0/12. + bool IsPrivateIP() const; + + // Determines whether the hostname has been resolved to an IP + bool IsUnresolved() const; + + // Attempt to resolve a hostname to IP address. + // Returns false if resolution is required but failed. + // 'force' will cause re-resolution of hostname. + // + bool Resolve(bool force = false, bool use_dns = true); + + // Determines whether this address is identical to the given one. + bool operator ==(const SocketAddress& addr) const; + + inline bool operator !=(const SocketAddress& addr) const { + return !this->operator ==(addr); + } + + // Determines whether this address has the same IP as the one given. + bool EqualIPs(const SocketAddress& addr) const; + + // Deteremines whether this address has the same port as the one given. + bool EqualPorts(const SocketAddress& addr) const; + + // Hashes this address into a small number. + dword Hash() const; + + // Convert to and from sockaddr_in + void ToSockAddr(sockaddr_in* saddr) const; + void FromSockAddr(const sockaddr_in& saddr); + + // Converts the IP address given in compact form into dotted form. + void IPToString(dword ip, char *psz, int cb) const; + + // Converts the IP address given in dotted form into compact form. + // Without 'use_dns', only dotted names (A.B.C.D) are resolved. + static dword StringToIP(const char *psz, bool use_dns = true); + + // Get local machine's hostname + static void GetHostname(char *psz, int cb); + + // Get information about the ip networks this machine is connected to + static bool GetNetworkInfo(int n, char *pszName, int cb, dword *pip); + +private: + void SetHostname(const char *psz); + bool IsHostnameEmpty() const; + + char *hostname_; + dword ip_; + word port_; +}; + +} // namespace base + +#endif // __SOCKET_H__ diff --git a/base/socketserver.cpp b/base/socketserver.cpp new file mode 100644 index 0000000..27c7990 --- /dev/null +++ b/base/socketserver.cpp @@ -0,0 +1,35 @@ +#include "base/socketserver.h" +#include "base/socket.h" + +namespace base { + +// Convenient helpers (not necessary, but nice to have) + +Socket *SocketServer::CreateSocket(int type, SocketNotify *notify) { + Socket *socket = new Socket(this); + if (socket->Create(type, notify)) { + return socket; + } + delete socket; + return NULL; +} + +Socket *SocketServer::WrapSocket(int s, SocketNotify *notify) { + Socket *socket = new Socket(this); + if (socket->Attach(s, notify)) { + return socket; + } + delete socket; + return NULL; +} + +// Can't put this in the include file because of class definition ordering issues + +void Dispatcher::OnEvent(dword ff) { + if (dispatchee_ != NULL) { + dispatchee_->OnEvent(ff); + } +} + + +} // namespace base diff --git a/base/socketserver.h b/base/socketserver.h new file mode 100644 index 0000000..6a66f97 --- /dev/null +++ b/base/socketserver.h @@ -0,0 +1,31 @@ +#ifndef __SOCKETSERVER_H__ +#define __SOCKETSERVER_H__ + +#include "inc/basictypes.h" + +namespace base { + +const long64 kctForever = -1; + +class Dispatcher; +class Socket; +class SocketNotify; + +class SocketServer { +public: + virtual ~SocketServer() {} + virtual bool Wait(long64 ctWait, bool fProcessIO = true) = 0; + virtual void WakeUp() = 0; + virtual Dispatcher *CreateDispatcher() = 0; + + // Convenient helpers (not necessary, but nice to have) + Socket *CreateSocket(int type, SocketNotify *notify); + Socket *WrapSocket(int s, SocketNotify *notify); + + // Factory method + static SocketServer *Create(); +}; + +} // namespace base + +#endif // __SOCKETSERVER_H__ diff --git a/base/thread.cpp b/base/thread.cpp new file mode 100644 index 0000000..98177d3 --- /dev/null +++ b/base/thread.cpp @@ -0,0 +1,78 @@ +#include "base/thread.h" +#include "base/tick.h" + +namespace base { + +Thread g_main_thread; +pthread_key_t Thread::s_key_; + +Thread::Thread(SocketServer *ss) : start_routine_(NULL), start_pv_(NULL), + MessageQueue(ss) { + if (&g_main_thread == this) { + pthread_key_create(&s_key_, NULL); + pthread_setspecific(s_key_, this); + } +} + +Thread::~Thread() { + Stop(); +} + +void Thread::Start(void (start_routine)(void *), void *start_pv) { + start_routine_ = start_routine; + start_pv_ = start_pv; + pthread_create(&thread_, NULL, PreRun, this); +} + +void *Thread::PreRun(void *pv) { + Thread *thread = (Thread *)pv; + pthread_setspecific(s_key_, thread); + if (thread->start_routine_ != NULL) { + thread->start_routine_(thread->start_pv_); + } else { + thread->RunLoop(); + } + return NULL; +} + +void Thread::CallerStart(void *pv) { + ICaller *caller = (ICaller *)pv; + caller->Call(); + delete caller; +} + +void Thread::Stop(bool wait) { + MessageQueue::Stop(); + if (wait && ¤t() != this) { + void *pv; + pthread_join(thread_, &pv); + } +} + +void Thread::RunLoop(long64 ct) { + Thread& thread = current(); + + long64 tEnd; + if (ct != kctForever) { + tEnd = base::GetTickCount() + ct; + } + long64 ctNext = ct; + + while (true) { + Message msg; + if (!thread.Get(&msg, ctNext)) { + return; + } + thread.Dispatch(&msg); + + if (ct != -1) { + long64 tCur = base::GetTickCount(); + if (tCur >= tEnd) { + return; + } + ctNext = tEnd - tCur; + } + } +} + +} // namespace base diff --git a/base/thread.h b/base/thread.h new file mode 100644 index 0000000..38f8807 --- /dev/null +++ b/base/thread.h @@ -0,0 +1,58 @@ +#ifndef __THREAD_H__ +#define __THREAD_H__ + +#include +#include "inc/basictypes.h" +#include "base/messagequeue.h" + +namespace base { + +class ICaller { +public: + virtual ~ICaller() {} + virtual void Call() = 0; +}; + +template +class Caller : public ICaller { +public: + Caller(T *obj, void (T::*method)(void *), void *pv) : obj_(obj), + method_(method), pv_(pv) {} + void Call() { (obj_->*method_)(pv_); } +private: + T *obj_; + void (T::*method_)(void *); + void *pv_; +}; + +class Thread : public MessageQueue { +public: + Thread(SocketServer *ss = NULL); + virtual ~Thread(); + + static Thread& current() { return *(Thread *)pthread_getspecific(s_key_); } + bool is_current() const { return ¤t() == this; } + + template + void Start(T *obj, void (T::*method)(void *), void *pv = NULL) { + Start(CallerStart, new Caller(obj, method, pv)); + } + virtual void Start(void (*start_routine)(void *) = NULL, + void *start_pv = NULL); + virtual void Stop(bool wait = true); + static void RunLoop(long64 ct = -1); + +private: + static void *PreRun(void *pv); + static void CallerStart(void *pv); + + static pthread_key_t s_key_; + pthread_t thread_; + void (*start_routine_)(void *); + void *start_pv_; +}; + +} // namespace base + +#endif // __THREAD_H__ + diff --git a/base/tick.cpp b/base/tick.cpp new file mode 100644 index 0000000..15c1105 --- /dev/null +++ b/base/tick.cpp @@ -0,0 +1,40 @@ +#include "inc/basictypes.h" +#include "base/tick.h" +#include +#include + +namespace base { + +void GetTimeOfDay(int *sec, int *usec) { + struct timeval tv; + gettimeofday(&tv, 0); + + static bool s_init; + static dword s_sec; + if (!s_init) { + s_init = true; + s_sec = tv.tv_sec - 1; // so it's never zero + } + + *sec = tv.tv_sec - s_sec; + *usec = tv.tv_usec; +} + +long64 GetTickCount() { + int sec, usec; + GetTimeOfDay(&sec, &usec); + return ((long64)sec) * 100 + usec / 10000; +} + +long64 GetMillisecondCount() { + int sec, usec; + GetTimeOfDay(&sec, &usec); + return ((long64)sec) * 1000 + usec / 1000; +} + +dword GetSecondsUnixEpocUTC() { + time_t t = time(NULL); + return (dword)t; +} + +} // namespace base diff --git a/base/tick.h b/base/tick.h new file mode 100644 index 0000000..60d20d3 --- /dev/null +++ b/base/tick.h @@ -0,0 +1,14 @@ +#ifndef __TICK_H__ +#define __TICK_H__ + +#include "inc/basictypes.h" + +namespace base { + +extern long64 GetTickCount(); +extern long64 GetMillisecondCount(); +extern dword GetSecondsUnixEpocUTC(); + +} // namespace base + +#endif // __TICK_H__ diff --git a/bcr2/.cvsignore b/bcr2/.cvsignore new file mode 100644 index 0000000..0ca40fa --- /dev/null +++ b/bcr2/.cvsignore @@ -0,0 +1,2 @@ +bin +SpiffLib diff --git a/bcr2/AssemblyInfo.cs b/bcr2/AssemblyInfo.cs new file mode 100644 index 0000000..9f89a32 --- /dev/null +++ b/bcr2/AssemblyInfo.cs @@ -0,0 +1,58 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.*")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] diff --git a/bcr2/Class1.cs b/bcr2/Class1.cs new file mode 100644 index 0000000..14f7ac6 --- /dev/null +++ b/bcr2/Class1.cs @@ -0,0 +1,100 @@ +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.Collections; +using System.Resources; +using System.IO; +using SpiffLib; + +namespace bcr2 { + class Class1 { + static void Usage() { + Console.WriteLine("bcr2 [-scale N] [-font] [-code] [-colorkey] [-savescaled] palette outputdir imagefiles"); } + + [STAThread] + static void Main(string[] astr) { + // Special cases + if (astr.Length == 0) { + Usage(); + return; + } + + bool fRaw = false; + bool fFont = false; + double nScale = 1.0; + bool fSaveScaled = false; + + int istr = 0; + for (; istr < astr.Length; istr++) { + if (astr[istr][0] != '-') + break; + switch (astr[istr]) { + case "-raw": + fRaw = true; + break; + + case "-font": + fFont = true; + break; + + case "-scale": + istr++; + nScale = double.Parse(astr[istr]); + break; + + case "-savescaled": + fSaveScaled = true; + break; + } + } + + // Params: palette outputdir filespecs + // Get palette + Palette pal = new Palette(astr[istr++]); + + // Get directory + string strDir = astr[istr++]; + + // Expand filespecs + ArrayList alsFiles = new ArrayList(); + for (; istr < astr.Length; istr++) { + string strFileT = Path.GetFileName(astr[istr]); + string strDirT = Path.GetDirectoryName(astr[istr]); + if (strDirT == "") + strDirT = "."; + string[] astrFiles = Directory.GetFiles(strDirT, strFileT); + alsFiles.AddRange(astrFiles); + } + + // Output + if (fFont) { + foreach (string strFile in alsFiles) { + string strFileFnt = strDir + Path.DirectorySeparatorChar + Path.GetFileName(strFile.Substring(0, strFile.Length - 4) + ".fnt"); + string strFileAscii = Path.GetDirectoryName(strFile) + Path.DirectorySeparatorChar + Path.GetFileName(strFile.Substring(0, strFile.Length - 4) + ".txt"); + Console.WriteLine(strFile + " -> " + strFileFnt); + TBitmap.SaveFont(strFile, pal, strFileAscii, strFileFnt); + } + } else { + if (fRaw) { + foreach (string strFile in alsFiles) { + string strFileRaw = strDir + Path.DirectorySeparatorChar + Path.GetFileName(strFile.Substring(0, strFile.Length - 4) + ".rbm"); + BitmapRaw.Save(strFile, pal, strFileRaw); + } + } else { + foreach (string strFile in alsFiles) { + string strFileTbm = strDir + Path.DirectorySeparatorChar + Path.GetFileName(strFile.Substring(0, strFile.Length - 4) + ".tbm"); + Console.WriteLine(strFile + " -> " + strFileTbm); + Bitmap[] abm = new Bitmap[1]; + abm[0] = new Bitmap(strFile); + if (nScale != 1.0) + abm[0] = TBitmapTools.ScaleBitmap(abm[0], nScale, pal); + if (fSaveScaled) + abm[0].Save(Path.Combine(strDir, Path.GetFileName(strFile)), ImageFormat.Bmp); + else + TBitmap.Save(abm, pal, strFileTbm); + } + } + } + } + } +} diff --git a/bcr2/ShowForm.resources b/bcr2/ShowForm.resources new file mode 100644 index 0000000..b1759fe Binary files /dev/null and b/bcr2/ShowForm.resources differ diff --git a/bcr2/app.ico b/bcr2/app.ico new file mode 100644 index 0000000..3a5525f Binary files /dev/null and b/bcr2/app.ico differ diff --git a/bcr2/bcr2.csproj b/bcr2/bcr2.csproj new file mode 100644 index 0000000..2c0158e --- /dev/null +++ b/bcr2/bcr2.csproj @@ -0,0 +1,76 @@ + + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {AA5B1574-27B8-4644-A766-EF1B1C5D748E} + Exe + bcr2 + bcr2 + + + true + full + false + bin\Debug\ + prompt + 4 + false + -unsafe + + + none + false + bin\Release\ + prompt + 4 + false + -unsafe + + + + + + + + + + bitmapraw.cs + + + doublerect.cs + + + misc.cs + + + palette.cs + + + tbitmap.cs + + + tbitmaptools.cs + + + + + ShowForm.cs + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/bcr2/bcr2.sln b/bcr2/bcr2.sln new file mode 100644 index 0000000..90724b6 --- /dev/null +++ b/bcr2/bcr2.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "bcr2", "bcr2.csproj", "{AA5B1574-27B8-4644-A766-EF1B1C5D748E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {AA5B1574-27B8-4644-A766-EF1B1C5D748E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AA5B1574-27B8-4644-A766-EF1B1C5D748E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AA5B1574-27B8-4644-A766-EF1B1C5D748E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AA5B1574-27B8-4644-A766-EF1B1C5D748E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(MonoDevelopProperties) = preSolution + StartupItem = bcr2.csproj + EndGlobalSection +EndGlobal diff --git a/bcr2/deploy.bat b/bcr2/deploy.bat new file mode 100644 index 0000000..4e06f88 --- /dev/null +++ b/bcr2/deploy.bat @@ -0,0 +1 @@ +copy bin\debug\bcr2.exe ..\bin diff --git a/bcr2/showform.cs b/bcr2/showform.cs new file mode 100644 index 0000000..6f0959b --- /dev/null +++ b/bcr2/showform.cs @@ -0,0 +1,103 @@ +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; + +namespace bcr2 +{ + /// + /// Summary description for Form1. + /// + public class ShowForm : System.Windows.Forms.Form + { + private System.Windows.Forms.PictureBox pictureBox1; + private System.Windows.Forms.PictureBox pictureBox2; + private System.Windows.Forms.PictureBox pictureBox3; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + public ShowForm(Bitmap bm1, Bitmap bm2, Bitmap bm3) + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + // + // TODO: Add any constructor code after InitializeComponent call + // + pictureBox1.Image = bm1; + pictureBox2.Image = bm2; + pictureBox3.Image = bm3; + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.pictureBox2 = new System.Windows.Forms.PictureBox(); + this.pictureBox3 = new System.Windows.Forms.PictureBox(); + this.SuspendLayout(); + // + // pictureBox1 + // + this.pictureBox1.Location = new System.Drawing.Point(72, 120); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(144, 144); + this.pictureBox1.TabIndex = 0; + this.pictureBox1.TabStop = false; + // + // pictureBox2 + // + this.pictureBox2.Location = new System.Drawing.Point(312, 216); + this.pictureBox2.Name = "pictureBox2"; + this.pictureBox2.Size = new System.Drawing.Size(152, 144); + this.pictureBox2.TabIndex = 1; + this.pictureBox2.TabStop = false; + // + // pictureBox3 + // + this.pictureBox3.Location = new System.Drawing.Point(312, 24); + this.pictureBox3.Name = "pictureBox3"; + this.pictureBox3.Size = new System.Drawing.Size(144, 136); + this.pictureBox3.TabIndex = 3; + this.pictureBox3.TabStop = false; + // + // ShowForm + // + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.ClientSize = new System.Drawing.Size(520, 398); + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.pictureBox3, + this.pictureBox2, + this.pictureBox1}); + this.Name = "ShowForm"; + this.Text = "Show Form"; + this.ResumeLayout(false); + + } + #endregion + } +} diff --git a/bcr2/showform.resx b/bcr2/showform.resx new file mode 100644 index 0000000..3aa3549 --- /dev/null +++ b/bcr2/showform.resx @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ShowForm + + \ No newline at end of file diff --git a/bcrunch/.cvsignore b/bcrunch/.cvsignore new file mode 100644 index 0000000..f295332 --- /dev/null +++ b/bcrunch/.cvsignore @@ -0,0 +1,3 @@ +bcrunch.dsw +Debug +Release diff --git a/bcrunch/bcrunch.dsp b/bcrunch/bcrunch.dsp new file mode 100644 index 0000000..4fba9e5 --- /dev/null +++ b/bcrunch/bcrunch.dsp @@ -0,0 +1,92 @@ +# Microsoft Developer Studio Project File - Name="bcrunch" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=bcrunch - 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 "bcrunch.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 "bcrunch.mak" CFG="bcrunch - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "bcrunch - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "bcrunch - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "bcrunch" +# PROP Scc_LocalPath "." +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "bcrunch - 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 Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# 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 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:console /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 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:console /machine:I386 + +!ELSEIF "$(CFG)" == "bcrunch - 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 Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# 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 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:console /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 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:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "bcrunch - Win32 Release" +# Name "bcrunch - Win32 Debug" +# Begin Source File + +SOURCE=.\main.cpp +# End Source File +# Begin Source File + +SOURCE=.\win32_rle.txt +# End Source File +# End Target +# End Project diff --git a/bcrunch/main.cpp b/bcrunch/main.cpp new file mode 100644 index 0000000..ff1778a --- /dev/null +++ b/bcrunch/main.cpp @@ -0,0 +1,749 @@ +// Bitmap Cruncher. Reads in windows bitmap, outputs ht bitmap +// +// Inputs: +// - Bitmap in .bmp format +// - Palette in Jasc format +// - Transparent information +// - HT output depth +// +// Outputs: +// - HT bitmap in appropriate depth + +#include +#include +#include +#include + +typedef unsigned char byte; +typedef unsigned short word; +typedef unsigned long dword; + +#define BigWord(x) ((((x)&0xFF)<<8) | (((x)&0xFF00)>>8)) + +byte gargbPal[256][3]; +int gcPalEntries; +int BMP_RGBToColorIndex(int r, int g, int b, byte palette[][3], int paletteSize); + +void Usage() +{ + printf("\n"); + printf("Usage: bcrunch <-s> <-tcorner | -tvalue value> [-p palette] [-d depth] [-o output] input\n"); + printf("-s: show image in top left corner of screen\n"); + printf("-tcorner: use top, left corner of image for transparent color.\n"); + printf("-tvalue: specify transparent pixel value.\n"); + printf("-trgb: specify transparent rgb value.\n"); + printf("-p: specify paintshop pro compatible palette to match output against.\n"); + printf("-d: specify output bit depth\n"); + printf("-o: specify ouput filename\n"); + printf("\n"); + printf("Note if no transparency option specified, -trgb 255 0 255 assumed.\n"); + printf("\n"); + exit(1); +} + +// Read in jasc format palette file (paintshop pro) + +bool ReadPalette(char *pszFn) +{ + FILE *pf = fopen(pszFn, "r"); + + if (pf == NULL) + return false; + + // Scan for "JASC-PAL" + + char szT[128]; + int c; + c = fscanf(pf, "%s\n", szT); + if (c != 1) + return false; + if (strcmp(szT, "JASC-PAL") != 0) + return false; + + // Scan for version info (we support "0100") + + c = fscanf(pf, "%s\n", szT); + if (c != 1) + return false; + if (strcmp(szT, "0100") != 0) + return false; + + // Scan for size of palette + + c = fscanf(pf, "%d\n", &gcPalEntries); + if (c != 1) + return false; + switch (gcPalEntries) { + case 2: + case 4: + case 16: + case 256: + break; + + default: + return false; + } + + // Read in the palette info + + for (int n = 0; n < gcPalEntries; n++) { + int r; + int g; + int b; + c = fscanf(pf, "%d %d %d\n", &r, &g, &b); + if (c != 3) + return false; + gargbPal[n][0] = (byte)r; + gargbPal[n][1] = (byte)g; + gargbPal[n][2] = (byte)b; + } + fclose(pf); + return true; +} + +dword GetPixelValue(BITMAPINFO *pbmi, byte *pb, int x, int y) +{ + int cxImage = pbmi->bmiHeader.biWidth; + int cyImage = abs(pbmi->bmiHeader.biHeight); + + if (pbmi->bmiHeader.biHeight >= 0) + y = (pbmi->bmiHeader.biHeight - 1) - y; + + byte *pbT; + int cbRow; + byte n; + + switch (pbmi->bmiHeader.biBitCount) { + case 1: + cbRow = (cxImage / 8 + 3) & ~3; + pbT = pb + y * cbRow + x / 8; + n = (*pbT >> (7 - (x & 7))) & 1; + return (dword)n; + + case 2: + cbRow = (cxImage / 4 + 3) & ~3; + pbT = pb + y * cbRow + x / 4; + n = (*pbT >> (7 - (x & 3) * 2)) & 3; + return (dword)n; + + case 4: + cbRow = (cxImage / 2 + 3) & ~3; + pbT = pb + y * cbRow + x / 2; + n = (*pbT >> (7 - (x & 1) * 4)) & 0xf; + return (dword)n; + + case 8: + cbRow = (cxImage + 3) & ~3; + pbT = pb + y * cbRow + x; + n = *pbT; + return (dword)n; + + case 16: + cbRow = (cxImage * 2 + 3) & ~3; + pbT = pb + y * cbRow + x * 2; + return (dword)(*(word *)pbT); + + case 24: + cbRow = (cxImage * 3 + 3) & ~3; + pbT = pb + y * cbRow + x * 3; + return (dword)((pbT[2] << 16) | (pbT[1] << 8) | (pbT[0])); + + default: + return 0; + } +} + +void ConvertPixel2RGB(BITMAPINFO *pbmi, dword dw, byte rgb[3]) +{ + RGBQUAD *prgbq = pbmi->bmiColors; + rgb[0] = 0; + rgb[1] = 0; + rgb[2] = 0; + + switch (pbmi->bmiHeader.biBitCount) { + case 1: + case 2: + case 4: + case 8: + rgb[0] = prgbq[dw].rgbRed; + rgb[1] = prgbq[dw].rgbGreen; + rgb[2] = prgbq[dw].rgbBlue; + break; + + case 16: + { + // Must be BI_RGB or BI_BITFIELDS + + if (!(pbmi->bmiHeader.biCompression & (BI_RGB | BI_BITFIELDS))) { + rgb[0] = 0; + rgb[1] = 0; + rgb[2] = 0; + break; + } + + // BI_RGB which is 5,5,5 in this order + + word aw[3]; + aw[0] = 0x7c00; + aw[1] = 0x3e0; + aw[2] = 0x1f; + + // Most 16bpp images are BI_BITFIELDS + + if (pbmi->bmiHeader.biCompression & BI_BITFIELDS) { + aw[0] = *(word *)&prgbq[0]; + aw[1] = *(word *)&prgbq[1]; + aw[2] = *(word *)&prgbq[2]; + } + + for (int n = 0; n < 3; n++) { + word w = aw[n]; + int c = 0; + while ((w & 1) == 0) { + w >>= 1; + c++; + } + word wT = (((word)dw & aw[n]) >> c) & w; + rgb[n] = (byte)((float)wT / (float)w * 255.0f + 0.5f); + } + } + break; + + case 24: + { + byte *pbT = (byte *)&dw; + rgb[0] = pbT[2]; + rgb[1] = pbT[1]; + rgb[2] = pbT[0]; + } + break; + } +} + +dword ConvertRGB2Pixel(BITMAPINFO *pbmi, byte rgb[3]) +{ + switch (pbmi->bmiHeader.biBitCount) { + case 1: + case 2: + case 4: + case 8: + { + byte rgbPal[256][3]; + for (int n = 0; n < (int)pbmi->bmiHeader.biClrUsed; n++) { + rgbPal[n][0] = pbmi->bmiColors[n].rgbRed; + rgbPal[n][1] = pbmi->bmiColors[n].rgbGreen; + rgbPal[n][2] = pbmi->bmiColors[n].rgbBlue; + } + return (dword)BMP_RGBToColorIndex(rgb[0], rgb[1], rgb[2], rgbPal, pbmi->bmiHeader.biClrUsed); + } + + case 16: + { + // r5g6b5 + + word w = 0; + w |= ((word)(((float)rgb[0] / 255.0f * 32.0f) + 0.5f) & 0x1f) << 11; + w |= ((word)(((float)rgb[1] / 255.0f * 64.0f) + 0.5f) & 0x3f) << 5; + w |= ((word)(((float)rgb[2] / 255.0f * 32.0f) + 0.5f) & 0x1f) << 0; + return (dword)w; + } + break; + + case 24: + { + dword dw = 0; + byte *pbT = (byte *)&dw; + pbT[2] = rgb[0]; + pbT[1] = rgb[1]; + pbT[0] = rgb[2]; + return dw; + } + } + return 0; +} + +BITMAPINFO *ReadBitmap(char *pszFilename, byte **ppb) +{ + // Open the alleged bitmap file. + + FILE *pfil = fopen(pszFilename, "rb"); + + // NOTE: Contrary to what the documentation says, fopen() returns NULL + // when it can't find the file, not -1. + + if (pfil == (FILE *)-1 || pfil == NULL) { + printf("Couldn't open the bitmap file!\n"); + return NULL; + } + + // Get the bitmap header. + + BITMAPFILEHEADER bfh; + if (fread(&bfh, sizeof(bfh), 1, pfil) != 1) { + printf("Couldn't read the bitmap file header!\n"); + fclose(pfil); + return NULL; + } + + // Is it really a bitmap? + + if (bfh.bfType != ('B' | ((word)'M' << 8))) { + printf("Not a valid Windows bitmap file!\n"); + fclose(pfil); + return NULL; + } + + // Read the whole header, color table, and bitmap bits into memory. + + long lPosBmi = ftell(pfil); + fseek(pfil, 0, SEEK_END); + long lPosEnd = ftell(pfil); + fseek(pfil, lPosBmi, SEEK_SET); + + BITMAPINFO *pbmiSrc = (BITMAPINFO *)new char[lPosEnd - lPosBmi]; + if (pbmiSrc == NULL) { + printf("Couldn't allocate bitmap read buffer!\n"); + fclose(pfil); + return NULL; + } + + if (fread(pbmiSrc, lPosEnd - lPosBmi, 1, pfil) != 1) { + printf("Couldn't read bitmap header, colors, bits!\n"); + delete pbmiSrc; + fclose(pfil); + return NULL; + } + + fclose(pfil); + + *ppb = ((byte *)pbmiSrc) + bfh.bfOffBits - sizeof(BITMAPFILEHEADER); + return pbmiSrc; +} + +// From pilrc. Easy to write from scratch, but I knew this already existed + +int BMP_RGBToColorIndex(int r, + int g, + int b, + byte palette[][3], + int paletteSize) +{ + int index, lowValue, i, *diffArray; + + // generate the color "differences" for all colors in the palette + diffArray = (int *)malloc(paletteSize * sizeof(int)); + for (i=0; i < paletteSize; i++) { + diffArray[i] = ((palette[i][0]-r)*(palette[i][0]-r)) + + ((palette[i][1]-g)*(palette[i][1]-g)) + + ((palette[i][2]-b)*(palette[i][2]-b)); + } + + // find the palette index that has the smallest color "difference" + index = 0; + lowValue = diffArray[0]; + for (i=1; ibmiHeader.biWidth; + int cyImage = abs(pbmi->bmiHeader.biHeight); + int acUsed[256]; + memset(acUsed, 0, sizeof(acUsed)); + for (x = 0; x < cxImage; x++) { + for (y = 0; y < cyImage; y++) { + dword dw = GetPixelValue(pbmi, pbBits, x, y); + if (dw == dwTransparent) + continue; + byte rgb[3]; + ConvertPixel2RGB(pbmi, dw, rgb); + byte b = BMP_RGBToColorIndex(rgb[0], rgb[1], rgb[2], gargbPal, gcPalEntries); + acUsed[b]++; + } + } + + // First see if the transparent color we asked for mapped to an index that isn't used. + // Otherwise look for an unused color index + + byte rgbT[3]; + ConvertPixel2RGB(pbmi, dwTransparent, rgbT); + int nT = BMP_RGBToColorIndex(rgbT[0], rgbT[1], rgbT[2], gargbPal, gcPalEntries); + int nUnused = -1; + if (acUsed[nT] == 0) { + nUnused = nT; + } else { + for (int n = 0; n < 256; n++) { + if (acUsed[n] == 0) { + nUnused = n; + break; + } + } + } + + // If we couldn't find an unused color index we have a mapping error + + if (nUnused == -1) { + printf("All colors in the output palette are in used by this image therefore\n"); + printf("no transparent color can be specified.\n"); + exit(1); + } + + // Init our new image format + + byte bTrans = (byte)nUnused; + word cbRow = (cxImage + 1) & ~1; + word cbImage = cbRow * cyImage; + *pcb = sizeof(Bitmap) + cbImage; + byte *pb = new byte[*pcb]; + memset(pb, bTrans, *pcb); + Bitmap *pbm = (Bitmap *)pb; + pbm->cx = BigWord(cxImage); + pbm->cy = BigWord(cyImage); + pbm->wTrans = bTrans | (bTrans << 8); + + // Palette match and stuff new image + + byte *pbT = (byte *)(pbm + 1); + for (x = 0; x < cxImage; x++) { + for (y = 0; y < cyImage; y++) { + dword dw = GetPixelValue(pbmi, pbBits, x, y); + byte b; + if (dw == dwTransparent) { + b = bTrans; + } else { + byte rgb[3]; + ConvertPixel2RGB(pbmi, dw, rgb); + b = BMP_RGBToColorIndex(rgb[0], rgb[1], rgb[2], gargbPal, gcPalEntries); + } + pbT[y * cbRow + x] = b; + } + } + + return pb; +} + +byte *PrepareImage8bppGrayscale(BITMAPINFO *pbmi, byte *pbBits, dword dwTransparent, dword *pcb) +{ + int cxImage = pbmi->bmiHeader.biWidth; + int cyImage = abs(pbmi->bmiHeader.biHeight); + word cbRow = (cxImage + 1) & ~1; + word cbImage = cbRow * cyImage; + + *pcb = sizeof(Bitmap) + cbImage; + byte *pb = new byte[*pcb]; + memset(pb, 0, *pcb); + Bitmap *pbm = (Bitmap *)pb; + pbm->cx = BigWord(cxImage); + pbm->cy = BigWord(cyImage); + pbm->wTrans = 0xffff; + + // Palette match and stuff new image + + byte *pbT = (byte *)(pbm + 1); + for (int x = 0; x < cxImage; x++) { + for (int y = 0; y < cyImage; y++) { + dword dw = GetPixelValue(pbmi, pbBits, x, y); + byte b = 0xff; + if (dw != dwTransparent) { + byte rgb[3]; + ConvertPixel2RGB(pbmi, dw, rgb); + b = BMP_RGBToColorIndex(rgb[0], rgb[1], rgb[2], gargbPal, gcPalEntries); + } + pbT[y * cbRow + x] = b; + } + } + + return pb; +} + +int main(int cArgs, char **ppszArgs) +{ + if (cArgs == 1) + Usage(); + + // Skip the filename + + cArgs--; + ppszArgs++; + + // Process parameters + + bool fShowImage = false; + bool fCornerTransparent = false; + bool fTransparentRGB = false; + byte rgbTrans[3]; + dword dwTransparent = 0xff000000; + byte *pbBits; + BITMAPINFO *pbmi; + int cBppOutput = 0; + char szFnOutput[MAX_PATH]; + char szFnInput[MAX_PATH]; + + szFnOutput[0] = 0; + while (cArgs > 0) { + // Grab the next parameter + + char *psz = *ppszArgs++; + cArgs--; + + // -p %s (palette filename) + + if (strcmp(psz, "-p") == 0) { + if (cArgs < 1) { + printf("Error parsing -p.\n"); + Usage(); + } + char *pszFn = *ppszArgs++; + cArgs--; + if (!ReadPalette(pszFn)) { + printf("Error reading palette %s.\n", pszFn); + Usage(); + } + continue; + } + + // -? + + if (strcmp(psz, "-?") == 0) + Usage(); + + // -tcorner (transparent pixel at 0,0) + + if (strcmp(psz, "-tcorner") == 0) { + fCornerTransparent = true; + continue; + } + + // -tvalue %x (transparent pixel value in hex before conversion) + + if (strcmp(psz, "-tvalue") == 0) { + if (cArgs < 1) { + printf("Error parsing -tvalue.\n"); + Usage(); + } + psz = *ppszArgs++; + cArgs--; + int c = sscanf(psz, "%x", &dwTransparent); + if (c != 1) { + printf("Error parsing -tvalue.\n"); + Usage(); + } + continue; + } + + // -trgb %d %d %d (transparent rgb) + + if (strcmp(psz, "-trgb") == 0) { + if (cArgs < 3) { + printf("Error parsing -trgb.\n"); + Usage(); + } + rgbTrans[0] = (byte)atoi(*ppszArgs++); + cArgs--; + rgbTrans[1] = (byte)atoi(*ppszArgs++); + cArgs--; + rgbTrans[2] = (byte)atoi(*ppszArgs++); + cArgs--; + fTransparentRGB = true; + continue; + } + + // -d %d (output depth, 2, 4, 8, 16 supported) + + if (strcmp(psz, "-d") == 0) { + if (cArgs < 1) { + printf("Error parsing -d.\n"); + Usage(); + } + psz = *ppszArgs++; + cArgs--; + int c = sscanf(psz, "%d", &cBppOutput); + if (c != 1) { + printf("Error parsing -d.\n"); + Usage(); + } + switch (cBppOutput) { + case 2: + case 4: + case 8: + case 16: + break; + + default: + printf("Legal depths are 2, 4, 8 or 16.\n"); + Usage(); + } + continue; + } + + // -s (show image in top left) + + if (strcmp(psz, "-s") == 0) { + fShowImage = true; + continue; + } + + // -o %s (output filename) + + if (strcmp(psz, "-o") == 0) { + if (cArgs < 1) { + printf("Error parsing -o.\n"); + Usage(); + } + psz = *ppszArgs++; + cArgs--; + int c = sscanf(psz, "%s", szFnOutput); + if (c != 1) { + printf("Error parsing -o.\n"); + Usage(); + } + continue; + } + + // None of the above, it must be the input filename + + int c = sscanf(psz, "%s", szFnInput); + if (c != 1) { + printf("Error opening input filename %s.\n", psz); + Usage(); + } + + // Load in and convert to rgb + + pbmi = ReadBitmap(szFnInput, &pbBits); + if (pbmi == NULL) { + printf("Error opening input filename %s.\n", psz); + Usage(); + } + + if (fCornerTransparent) { + // -tcorner specified. Get the transparent pixel value from the top left corner. + + dwTransparent = GetPixelValue(pbmi, pbBits, 0, 0); + ConvertPixel2RGB(pbmi, dwTransparent, rgbTrans); + } else if (fTransparentRGB) { + // -trgb specified. Get the transparent color from this rgb + + dwTransparent = ConvertRGB2Pixel(pbmi, rgbTrans); + } else { + // Perhaps no transparent color was specified. If so, + // assume 255 0 255 + + if (dwTransparent == 0xff000000) { + rgbTrans[0] = 255; + rgbTrans[1] = 0; + rgbTrans[2] = 255; + dwTransparent = ConvertRGB2Pixel(pbmi, rgbTrans); + } else { + // -tvalue specified. Get the rgb for this transparent pixel value + + ConvertPixel2RGB(pbmi, dwTransparent, rgbTrans); + } + } + + // If the input image is paletted, ensure the incoming palette has + // the transparent color in it rather than it being a fuzzy match + // which can lead to mapping errors. + + switch (pbmi->bmiHeader.biBitCount) { + case 1: + case 2: + case 4: + case 8: + { + RGBQUAD *prgbq = &pbmi->bmiColors[dwTransparent]; + if (prgbq->rgbRed != rgbTrans[0] || prgbq->rgbGreen != rgbTrans[1] || prgbq->rgbBlue != rgbTrans[2]) { + printf("%s warning: transparent color specified is not in the\n", szFnInput); + printf("image's palette. This could lead to transparent color mapping problems.\n"); + } + } + } + } + + // Show the image if asked + + int cxImage = pbmi->bmiHeader.biWidth; + int cyImage = abs(pbmi->bmiHeader.biHeight); + if (fShowImage) { + HDC hdc = GetDC(NULL); + for (int x = 0; x < cxImage; x++) { + for (int y = 0; y < cyImage; y++) { + dword dw = GetPixelValue(pbmi, pbBits, x, y); + byte rgb[3]; + ConvertPixel2RGB(pbmi, dw, rgb); + if (dw != dwTransparent) { + SetPixel(hdc, x, y, RGB(rgb[0], rgb[1], rgb[2])); + } + } + } + ReleaseDC(NULL, hdc); + } + + dword cb; + byte *pbNew = NULL; + switch (cBppOutput) { + case 1: + case 2: + case 4: + case 16: + default: + printf("Output depth %d not supported.\n", cBppOutput); + return 1; + + case 8: + if (gcPalEntries <= 16) { + pbNew = PrepareImage8bppGrayscale(pbmi, pbBits, dwTransparent, &cb); + } else { + pbNew = PrepareImage8bpp(pbmi, pbBits, dwTransparent, &cb); + } + break; + } + if (pbNew == NULL) { + printf("Error processing bitmap file.\n"); + Usage(); + } + + // Save it out + + FILE *pf = fopen(szFnOutput, "wb"); + if (pf == NULL) { + printf("Could not create output file %s.\n"); + return 1; + } + if (fwrite(pbNew, cb, 1, pf) != 1) { + fclose(pf); + DeleteFile(szFnOutput); + printf("Could not create output file %s.\n"); + Usage(); + } + fclose(pf); + delete pbNew; + + byte rgb[3]; + ConvertPixel2RGB(pbmi, dwTransparent, rgb); + printf("%s(%d) -> %s(%d), %dx%d, trans:%d,%d,%d, %d bytes\n", szFnInput, pbmi->bmiHeader.biBitCount, szFnOutput, cBppOutput, cxImage, cyImage, rgb[0], rgb[1], rgb[2], cb); + delete pbmi; + return 0; +} \ No newline at end of file diff --git a/bcrunch/tank16.bmp b/bcrunch/tank16.bmp new file mode 100644 index 0000000..fb65ad9 Binary files /dev/null and b/bcrunch/tank16.bmp differ diff --git a/bcrunch/tank24.bmp b/bcrunch/tank24.bmp new file mode 100644 index 0000000..82f818f Binary files /dev/null and b/bcrunch/tank24.bmp differ diff --git a/bcrunch/tank8.bmp b/bcrunch/tank8.bmp new file mode 100644 index 0000000..5819256 Binary files /dev/null and b/bcrunch/tank8.bmp differ diff --git a/bcrunch/win32_rle.txt b/bcrunch/win32_rle.txt new file mode 100644 index 0000000..190111b --- /dev/null +++ b/bcrunch/win32_rle.txt @@ -0,0 +1,96 @@ +Windows 8bit RLE ++++ + +When the biCompression member of the BITMAPINFOHEADER structure is set to +BI_RLE8, the DIB is compressed using a run-length encoded format for a +256-color bitmap. This format uses two modes: encoded mode and absolute mode. +Both modes can occur anywhere throughout a single bitmap. + +Encoded Mode + +A unit of information in encoded mode consists of two bytes. The first byte +specifies the number of consecutive pixels to be drawn using the color index +contained in the second byte. The first byte of the pair can be set to zero +to indicate an escape that denotes the end of a line, the end of the bitmap, +or a delta. The interpretation of the escape depends on the value of the +second byte of the pair, which must be in the range 0x00 through 0x02. +Following are the meanings of the escape values that can be used in the +second byte: + +Second byte Meaning + +0 End of line. +1 End of bitmap. +2 Delta. The two bytes following the escape contain unsigned values +indicating the horizontal and vertical offsets of the next pixel from the +current position. + +Absolute Mode + +Absolute mode is signaled by the first byte in the pair being set to zero and +the second byte to a value between 0x03 and 0xFF. The second byte represents +the number of bytes that follow, each of which contains the color index of a +single pixel. Each run must be aligned on a word boundary. Following is an +example of an 8-bit RLE bitmap (the two-digit hexadecimal values in the +second column represent a color index for a single pixel): + +Compressed data Expanded data + +03 04 04 04 04 +05 06 06 06 06 06 06 +00 03 45 56 67 00 45 56 67 +02 78 78 78 +00 02 05 01 Move 5 right and 1 down +02 78 78 78 +00 00 End of line +09 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E +00 01 End of RLE bitmap + +Compression of 4-Bits-per-Pixel Bitmaps + +When the biCompression member of the BITMAPINFOHEADER structure is set to +BI_RLE4, the DIB is compressed using a run-length encoded format for a +16-color bitmap. This format uses two modes: encoded mode and absolute mode. + +Encoded Mode + +A unit of information in encoded mode consists of two bytes. The first byte +of the pair contains the number of pixels to be drawn using the color indexes +in the second byte. + +The second byte contains two color indexes, one in its high-order nibble +(that is, its low-order 4 bits) and one in its low-order nibble. + +The first pixel is drawn using the color specified by the high-order nibble, +the second is drawn using the color in the low-order nibble, the third is +drawn with the color in the high-order nibble, and so on, until all the +pixels specified by the first byte have been drawn. + +The first byte of the pair can be set to zero to indicate an escape that +denotes the end of a line, the end of the bitmap, or a delta. The +interpretation of the escape depends on the value of the second byte of the +pair. In encoded mode, the second byte has a value in the range 0x00 through +0x02. The meaning of these values is the same as for a DIB with 8 bits per +pixel. + +Absolute Mode + +In absolute mode, the first byte contains zero, the second byte contains the +number of color indexes that follow, and subsequent bytes contain color +indexes in their high- and low-order nibbles, one color index for each pixel. +Each run must be aligned on a word boundary. + +Following is an example of a 4-bit RLE bitmap (the one-digit hexadecimal +values in the second column represent a color index for a single pixel): + +Compressed data Expanded data + +03 04 0 4 0 +05 06 0 6 0 6 0 +00 06 45 56 67 00 4 5 5 6 6 7 +04 78 7 8 7 8 +00 02 05 01 Move 5 right and 1 down +04 78 7 8 7 8 +00 00 End of line +09 1E 1 E 1 E 1 E 1 E 1 +00 01 End of RLE bitmap \ No newline at end of file diff --git a/bin/.cvsignore b/bin/.cvsignore new file mode 100644 index 0000000..6f0b10a --- /dev/null +++ b/bin/.cvsignore @@ -0,0 +1,3 @@ +m.ini +AED.config + diff --git a/bin/AniMax.exe.manifest b/bin/AniMax.exe.manifest new file mode 100644 index 0000000..af3f84b --- /dev/null +++ b/bin/AniMax.exe.manifest @@ -0,0 +1,22 @@ + + + +Animation Editor + + + + + + diff --git a/bin/FormEdit.exe.manifest b/bin/FormEdit.exe.manifest new file mode 100644 index 0000000..db3fa45 --- /dev/null +++ b/bin/FormEdit.exe.manifest @@ -0,0 +1,22 @@ + + + +Animation Editor + + + + + + diff --git a/bin/M.exe.manifest b/bin/M.exe.manifest new file mode 100644 index 0000000..70fead8 --- /dev/null +++ b/bin/M.exe.manifest @@ -0,0 +1,22 @@ + + + +Map/Level Editor + + + + + + diff --git a/bin/ProfView.exe.manifest b/bin/ProfView.exe.manifest new file mode 100644 index 0000000..ff45ccd --- /dev/null +++ b/bin/ProfView.exe.manifest @@ -0,0 +1,22 @@ + + + +Profile Results Viewer + + + + + + diff --git a/bin/inicrunch b/bin/inicrunch new file mode 100755 index 0000000..bf21af3 Binary files /dev/null and b/bin/inicrunch differ diff --git a/bin/markversion b/bin/markversion new file mode 100755 index 0000000..6726fc3 --- /dev/null +++ b/bin/markversion @@ -0,0 +1,2 @@ +[[ $# -eq 0 ]] && echo "Usage: $0 " && echo "NOTE: Must be run from the project root directory (e.g. wi)." && exit +mono bin/MarkVersion.exe 1 game/htdata832.pdb +++VERSION+++ $1 diff --git a/bscale/AssemblyInfo.cs b/bscale/AssemblyInfo.cs new file mode 100644 index 0000000..9f89a32 --- /dev/null +++ b/bscale/AssemblyInfo.cs @@ -0,0 +1,58 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.*")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] diff --git a/bscale/app.ico b/bscale/app.ico new file mode 100644 index 0000000..3a5525f Binary files /dev/null and b/bscale/app.ico differ diff --git a/bscale/bscale.cs b/bscale/bscale.cs new file mode 100644 index 0000000..4b734f8 --- /dev/null +++ b/bscale/bscale.cs @@ -0,0 +1,78 @@ +using System; +using System.IO; +using System.Drawing; +using System.Drawing.Imaging; +using System.Collections; +//using SpiffCode; +using SpiffLib; + +namespace bscale +{ + /// + /// Summary description for Class1. + /// + class Class1 + { + /// + /// The main entry point for the application. + /// + [STAThread] + static int Main(string[] astrArgs) + { + if (astrArgs.Length < 3) { + Console.WriteLine("Usage:\nbscale -scale -pal -out \n"); + return -1; + } + + double nScale = 1.0; + string strOutDir = null, strPalette = null; + ArrayList alFileSpecs = new ArrayList(); + + for (int iarg = 0; iarg < astrArgs.Length; iarg++) { + if (astrArgs[iarg][0] == '-') { + switch (astrArgs[iarg]) { + case "-scale": + nScale = double.Parse(astrArgs[++iarg]); + break; + + case "-out": + strOutDir = astrArgs[++iarg]; + break; + + case "-pal": + strPalette = astrArgs[++iarg]; + break; + } + } else { + alFileSpecs.Add(astrArgs[iarg]); + } + } + + // Read in the palette + + Palette pal = new Palette(strPalette); + if (pal == null) { + Console.WriteLine("Error: unable to read the palette file {0}\n", strPalette); + return -1; + } + + foreach (string strFileSpec in alFileSpecs) { + Console.WriteLine("dir = " + Path.GetDirectoryName(strFileSpec) + ", file = " + Path.GetFileName(strFileSpec)); + string[] astrFiles = Directory.GetFiles(Path.GetDirectoryName(strFileSpec), Path.GetFileName(strFileSpec)); + + foreach (string strFile in astrFiles) { + Console.WriteLine(strFile); + Bitmap bm = new Bitmap(strFile); + Bitmap bmScaled = TBitmapTools.ScaleBitmap(bm, nScale, pal); + if (!Directory.Exists(strOutDir)) + Directory.CreateDirectory(strOutDir); + + bmScaled.Save(strOutDir + Path.DirectorySeparatorChar + + Path.GetFileName(strFile), bm.RawFormat); + } + } + + return 0; + } + } +} diff --git a/bscale/bscale.csproj b/bscale/bscale.csproj new file mode 100644 index 0000000..e883ca8 --- /dev/null +++ b/bscale/bscale.csproj @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bscale/bscale.sln b/bscale/bscale.sln new file mode 100644 index 0000000..853b866 --- /dev/null +++ b/bscale/bscale.sln @@ -0,0 +1,21 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "bscale", "bscale.csproj", "{E82529E9-4171-4798-9594-443D78E7F544}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Release = Release + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {E82529E9-4171-4798-9594-443D78E7F544}.Debug.ActiveCfg = Debug|.NET + {E82529E9-4171-4798-9594-443D78E7F544}.Debug.Build.0 = Debug|.NET + {E82529E9-4171-4798-9594-443D78E7F544}.Release.ActiveCfg = Release|.NET + {E82529E9-4171-4798-9594-443D78E7F544}.Release.Build.0 = Release|.NET + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/bscale/deploy.bat b/bscale/deploy.bat new file mode 100644 index 0000000..fc8c4b6 --- /dev/null +++ b/bscale/deploy.bat @@ -0,0 +1 @@ +copy bin\debug\bscale.exe ..\bin diff --git a/data/.cvsignore b/data/.cvsignore new file mode 100644 index 0000000..20559cc --- /dev/null +++ b/data/.cvsignore @@ -0,0 +1,10 @@ +416 +424 +816 +820 +824 +art820 +art416 +art424 +prepre +trg diff --git a/data/.gitignore b/data/.gitignore new file mode 100644 index 0000000..6fe0b24 --- /dev/null +++ b/data/.gitignore @@ -0,0 +1,9 @@ +ini.tmp +files.tmp +832/ +824/ +trg/ +art824/desert_8bpp.pal +art824/grassy_8bpp.pal +art824/fixed_8bpp.pal +art824/shell_8bpp.pal diff --git a/data/C_01.ld b/data/C_01.ld new file mode 100644 index 0000000..dc26321 Binary files /dev/null and b/data/C_01.ld differ diff --git a/data/C_02.ld b/data/C_02.ld new file mode 100644 index 0000000..2e51cb1 Binary files /dev/null and b/data/C_02.ld differ diff --git a/data/C_03.ld b/data/C_03.ld new file mode 100644 index 0000000..e6c28e0 Binary files /dev/null and b/data/C_03.ld differ diff --git a/data/C_04.ld b/data/C_04.ld new file mode 100644 index 0000000..b902d60 Binary files /dev/null and b/data/C_04.ld differ diff --git a/data/C_05.ld b/data/C_05.ld new file mode 100644 index 0000000..9022646 Binary files /dev/null and b/data/C_05.ld differ diff --git a/data/D_03-sample.ld b/data/D_03-sample.ld new file mode 100644 index 0000000..9167041 Binary files /dev/null and b/data/D_03-sample.ld differ diff --git a/data/D_03.ld b/data/D_03.ld new file mode 100644 index 0000000..34071a3 Binary files /dev/null and b/data/D_03.ld differ diff --git a/data/GobTemplates.ini.pp b/data/GobTemplates.ini.pp new file mode 100644 index 0000000..5429455 --- /dev/null +++ b/data/GobTemplates.ini.pp @@ -0,0 +1,463 @@ +#include + +// MoveRate (in wc/upd) - WCoord moved per update. MUST BE LESS THAN 64! +// FiringRate (in msecs) - delay between firings +// TimeToBuild (in secs) - amount of time required to build this structure/unit. MUST BE LESS THAN 1000! +// ArmorStrength (in hitpoints) - when this falls to 0 the structure/unit dies +// Infantry/Vehicle/StructureDamage (in hitpoints) - amount of hitpoints subtracted from target class when hit +// SightRange (in tiles) - distance the structure/unit can 'see' +// FiringRange (in tiles) - distance the structure/unit can fire +// Cost - # of credits consumed by creating this unit + +// Infantry Units + +[kgtShortRangeInfantry] +Name=Guard +LongName=Security Guard +Cost=150 +CostMP=100 +MoveRate=18 +ArmorStrength=39 +InfantryDamage=9 +InfantryDamageMP=12 +VehicleDamage=3 +StructureDamage=3 +FiringRate=750 +FiringRange=2 +TimeToBuild=16 +TimeToBuildMP=10 +UIBoundsLeft=-6 +UIBoundsTop=-7 +UIBoundsRight=7 +UIBoundsBottom=7 +SortOffset=5 +Animation=sri.anir +Description=Cheap and cheerful. Limited firepower and defensive capability. + +[kgtLongRangeInfantry] +Name=Trooper +LongName=Rocket Trooper +Cost=300 +MoveRate=11 +ArmorStrength=50 +InfantryDamage=6 +VehicleDamage=17 +StructureDamage=6 +FiringRate=1600 +FiringRange=3 +TimeToBuild=16 +UIBoundsLeft=-6 +UIBoundsTop=-7 +UIBoundsRight=7 +UIBoundsBottom=7 +SortOffset=5 +Animation=lri.anir +Description=For those hard to reach places. Rockets are particularly effective against armored vehicles. + +// Corporate Raider's StructureDamage and FiringRate are set to match kfxDamagePerSecMax +// so it will show 8 structure icons for its damage in the build form + +[kgtTakeoverSpecialist] +Name=Raider +LongName=Corporate Raider +Cost=550 +MoveRate=10 +MoveRateMP=16 +ArmorStrength=50 +InfantryDamage=0 +VehicleDamage=0 +StructureDamage=16 +FiringRate=1000 +FiringRange=1 +TimeToBuild=16 +UIBoundsLeft=-6 +UIBoundsTop=-7 +UIBoundsRight=7 +UIBoundsBottom=7 +SortOffset=5 +Animation=spi.anir +Description=Send the Raider to take over ememy buildings. Keep in mind he has no conventional weapons and little armor! + +[kgtAndy] +Name=Andy +LongName=Andy +Cost=550 +MoveRate=22 +ArmorStrength=100 +InfantryDamage=56 +VehicleDamage=24 +StructureDamage=8 +FiringRate=2300 +FiringRange=4 +TimeToBuild=16 +UIBoundsLeft=-6 +UIBoundsTop=-7 +UIBoundsRight=7 +UIBoundsBottom=7 +SortOffset=5 +Animation=andy.anir +Description=Long range scout and sniper + +[kgtFox] +Name=Fox +LongName=Fox +Cost=550 +MoveRate=18 +ArmorStrength=80 +InfantryDamage=56 +VehicleDamage=24 +StructureDamage=8 +FiringRate=2300 +FiringRange=4 +TimeToBuild=16 +UIBoundsLeft=-6 +UIBoundsTop=-7 +UIBoundsRight=7 +UIBoundsBottom=7 +SortOffset=5 +Animation=andy.anir +Description=Bad guy + +// Mechanical Units + +[kgtMachineGunVehicle] +Name=Eagle +LongName=SR-98 Eagle +Cost=300 +MoveRate=24 +ArmorStrength=75 +InfantryDamage=8 +VehicleDamage=4 +StructureDamage=4 +FiringRate=1000 +FiringRange=3 +TimeToBuild=16 +UIBoundsLeft=-10 +UIBoundsTop=-10 +UIBoundsRight=10 +UIBoundsBottom=11 +SortOffset=7 +Animation=tmac.anir +Description=Fast but not particularly powerful or well armored. Makes a nice scout. + +[kgtLightTank] +Name=Broadsword +LongName=T-29 Broadsword +Cost=450 +MoveRate=16 +ArmorStrength=125 +InfantryDamage=6 +VehicleDamage=9 +StructureDamage=15 +FiringRate=1500 +FiringRange=3 +TimeToBuild=16 +UIBoundsLeft=-10 +UIBoundsTop=-10 +UIBoundsRight=10 +UIBoundsBottom=11 +SortOffset=7 +Animation=ltank.anir +Description=Equipped with a fine mortar cannon. Increased damage against buildings. + +[kgtRocketVehicle] +Name=Hydra +LongName=M-18 Hydra +Cost=450 +CostMP=550 +MoveRate=14 +ArmorStrength=100 +ArmorStrengthMP=85 +InfantryDamage=5 +VehicleDamage=20 +StructureDamage=8 +FiringRate=1250 +FiringRange=3 +TimeToBuild=16 +UIBoundsLeft=-10 +UIBoundsTop=-10 +UIBoundsRight=10 +UIBoundsBottom=11 +SortOffset=7 +Animation=troc.anir +Description=Well armored and equipped with high-explosive rockets that really pack a punch against vehicles. + +[kgtMediumTank] +Name=Liberator +LongName=T-33 Liberator +Cost=600 +MoveRate=15 +ArmorStrength=150 +InfantryDamage=16 +VehicleDamage=6 +StructureDamage=4 +FiringRate=1000 +FiringRange=3 +TimeToBuild=16 +UIBoundsLeft=-10 +UIBoundsTop=-10 +UIBoundsRight=10 +UIBoundsBottom=11 +SortOffset=7 +Animation=mtank.anir +Description=This tank's dual cannons fire fragmentation grenades that do serious damage to enemy infantry. + +[kgtArtillery] +Name=Cyclops +LongName=A-3 Cyclops +Cost=500 +MoveRate=10 +ArmorStrength=50 +InfantryDamage=7 +VehicleDamage=24 +StructureDamage=48 +FiringRate=3000 +FiringRange=4 +TimeToBuild=16 +UIBoundsLeft=-12 +UIBoundsTop=-12 +UIBoundsRight=12 +UIBoundsBottom=13 +SortOffset=7 +Animation=artillery.anir +Description=Long range of fire. Slow moving. Packs a punch but is not accurate against smaller units. + +[kgtGalaxMiner] +Name=Bullpup +LongName=G-4 Bullpup +Cost=600 +MoveRate=14 +ArmorStrength=200 +FiringRate=0 +FiringRange=0 +TimeToBuild=16 +UIBoundsLeft=-10 +UIBoundsTop=-10 +UIBoundsRight=10 +UIBoundsBottom=11 +SortOffset=7 +Animation=miner.anir +Menu=kidfMinerMenu +Description=Increase your mining rate with additional Bullpups. It has no weapons but can take a lot of punishment. + +[kgtMobileHeadquarters] +Name=Dominion +LongName=H-7 Dominion +Cost=1200 +MoveRate=12 +ArmorStrength=200 +FiringRate=0 +FiringRange=0 +TimeToBuild=16 +UIBoundsLeft=-10 +UIBoundsTop=-10 +UIBoundsRight=10 +UIBoundsBottom=11 +SortOffset=7 +Animation=mobilehq.anir +Menu=kidfMobileHqMenu +Description=Not happy where you are? Start a new base at a remote location with this Mobile Headquarters. + +// Structures + +[kgtHumanResourceCenter] +Name=H.R.C. +LongName=Human Resource Center (HRC) +TimeToBuild=15 +Cost=1000 +PowerDemand=10 +ArmorStrength=250 +UIBoundsLeft=-1 +UIBoundsTop=-1 +UIBoundsRight=49 +UIBoundsBottom=33 +SortOffset=29 +Animation=HRC.anir +TileDimensions=3,2 +ReserveDimensions=3,3 +Menu=kidfHrcMenu +BuildRate=2 +UpgradeCost=750 +Description=Use this building to recruit new personnel. Upgrade the HRC to gain access to more advanced units. + +[kgtProcessor] +Name=Processor +LongName=Galaxite Processor +TimeToBuild=15 +Cost=1500 +PowerDemand=10 +ArmorStrength=350 +UIBoundsLeft=0 +UIBoundsTop=-1 +UIBoundsRight=48 +UIBoundsBottom=32 +SortOffset=32 +Animation=proc.anir +TileDimensions=3,2 +ReserveDimensions=3,3 +Description=Earth HQ provides credit for processed Galaxite. This building comes complete with one mining vehicle (Bullpup)! A Processor can store 3000 credits worth of Galaxite. + +[kgtReactor] +Name=Generator +LongName=Power Generator +TimeToBuild=10 +Cost=750 +PowerSupply=40 +ArmorStrength=200 +UIBoundsLeft=1 +UIBoundsTop=3 +UIBoundsRight=33 +UIBoundsBottom=31 +SortOffset=30 +Animation=Reactor.anir +TileDimensions=2,2 +Description=Most buildings will not operate without sufficient power. Keep an eye on your power supply and demand at all times. + +[kgtHeadquarters] +Name=H.Q. +LongName=Headquarters (HQ) +TimeToBuild=15 +Cost=750 +ArmorStrength=500 +UIBoundsLeft=0 +UIBoundsTop=0 +UIBoundsRight=48 +UIBoundsBottom=32 +SortOffset=30 +Animation=HQ.anir +TileDimensions=3,2 +Menu=kidfHqMenu +BuildRate=100 + +[kgtRadar] +Name=S.C. +LongName=Surveillance Center (SC) +TimeToBuild=15 +Cost=750 +PowerDemand=15 +ArmorStrength=250 +UIBoundsLeft=-1 +UIBoundsTop=-1 +UIBoundsRight=33 +UIBoundsBottom=33 +SortOffset=30 +Animation=Radar.anir +TileDimensions=2,2 +Description=A Surveillance Center provides guidance control for Gun and Rocket Towers and is required to build them. + +[kgtResearchCenter] +Name=R&D Center +LongName=Research & Development Center +TimeToBuild=15 +Cost=750 +PowerDemand=15 +ArmorStrength=250 +UIBoundsLeft=-1 +UIBoundsTop=-1 +UIBoundsRight=33 +UIBoundsBottom=33 +SortOffset=30 +Animation=Research.anir +TileDimensions=2,2 +Menu=kidfResearchMenu +Description=Invest in an R&D Center and its scientists will work hard to produce technological advances to aid you in your operations. + +[kgtVehicleTransportStation] +Name=V.T.S. +LongName=Vehicle Transport Station (VTS) +TimeToBuild=15 +Cost=1250 +CostMP=2000 +PowerDemand=10 +ArmorStrength=300 +UIBoundsLeft=-1 +UIBoundsTop=-1 +UIBoundsRight=49 +UIBoundsBottom=33 +SortOffset=30 +Animation=VTS.anir +TileDimensions=3,2 +ReserveDimensions=3,3 +Menu=kidfVtsMenu +BuildRate=10 +UpgradeCost=1000 +Description=Use this building to order new vehicles. Upgrade the VTS to gain access to more advanced units. + +[kgtWarehouse] +Name=Warehouse +LongName=Galaxite Storage Warehouse +TimeToBuild=15 +Cost=750 +PowerDemand=7 +ArmorStrength=200 +UIBoundsLeft=0 +UIBoundsTop=0 +UIBoundsRight=33 +UIBoundsBottom=33 +SortOffset=30 +Animation=Warehouse.anir +TileDimensions=2,2 +Description=Add Warehouses to increase processed Galaxite inventory capacity. Each Warehouse can store up to 5000 credits worth of Galaxite. + +[kgtReplicator] +Name=Replicator +LongName=Replicator +TimeToBuild=15 +Cost=1000 +PowerDemand=0 +ArmorStrength=300 +UIBoundsLeft=3 +UIBoundsTop=0 +UIBoundsRight=77 +UIBoundsBottom=56 +SortOffset=84 +Animation=replicator.anir +TileDimensions=5,4 +ReserveDimensions=5,4 +BuildRate=2 +UpgradeCost=400 +Description=You're not supposed to be able to build this! + +// Towers + +[kgtMachineGunTower] +Name=Gatling +LongName=Gatling Tower +TimeToBuild=10 +Cost=750 +PowerDemand=10 +ArmorStrength=150 +InfantryDamage=16 +VehicleDamage=5 +StructureDamage=6 +FiringRate=1333 +FiringRange=3 +UIBoundsLeft=0 +UIBoundsTop=0 +UIBoundsRight=16 +UIBoundsBottom=16 +SortOffset=30 +Animation=mtower.anir +TileDimensions=1,1 +Description=Defend your base from unwanted visits by the competition. Gatling Towers are particularly effective against troops. NOTE: a Surveillance Center is required to guide their automatic targeting systems. + +[kgtRocketTower] +Name=Rocket Twr +LongName=Rocket Tower +TimeToBuild=10 +Cost=850 +PowerDemand=10 +ArmorStrength=150 +InfantryDamage=6 +VehicleDamage=18 +StructureDamage=6 +FiringRate=1500 +FiringRange=3 +UIBoundsLeft=0 +UIBoundsTop=0 +UIBoundsRight=16 +UIBoundsBottom=16 +SortOffset=30 +Animation=rtower.anir +TileDimensions=1,1 +Description=Rocket Towers are particularly effective against armored vehicles. NOTE: a Surveillance Center is required to guide their automatic targeting systems. diff --git a/data/M_01.ld b/data/M_01.ld new file mode 100644 index 0000000..0894a32 Binary files /dev/null and b/data/M_01.ld differ diff --git a/data/M_02.ld b/data/M_02.ld new file mode 100644 index 0000000..0ae6103 Binary files /dev/null and b/data/M_02.ld differ diff --git a/data/M_03.ld b/data/M_03.ld new file mode 100644 index 0000000..d682e3c Binary files /dev/null and b/data/M_03.ld differ diff --git a/data/M_04.ld b/data/M_04.ld new file mode 100644 index 0000000..b37bdb5 Binary files /dev/null and b/data/M_04.ld differ diff --git a/data/M_05.ld b/data/M_05.ld new file mode 100644 index 0000000..3c100a7 Binary files /dev/null and b/data/M_05.ld differ diff --git a/data/M_06.ld b/data/M_06.ld new file mode 100644 index 0000000..f354e4e Binary files /dev/null and b/data/M_06.ld differ diff --git a/data/M_07.ld b/data/M_07.ld new file mode 100644 index 0000000..cb98ae8 Binary files /dev/null and b/data/M_07.ld differ diff --git a/data/M_08.ld b/data/M_08.ld new file mode 100644 index 0000000..7398e4d Binary files /dev/null and b/data/M_08.ld differ diff --git a/data/M_09.ld b/data/M_09.ld new file mode 100644 index 0000000..f20a3af Binary files /dev/null and b/data/M_09.ld differ diff --git a/data/M_10.ld b/data/M_10.ld new file mode 100644 index 0000000..ad49388 Binary files /dev/null and b/data/M_10.ld differ diff --git a/data/M_11.ld b/data/M_11.ld new file mode 100644 index 0000000..70acce0 Binary files /dev/null and b/data/M_11.ld differ diff --git a/data/M_12.ld b/data/M_12.ld new file mode 100644 index 0000000..fea3a13 Binary files /dev/null and b/data/M_12.ld differ diff --git a/data/M_13.ld b/data/M_13.ld new file mode 100644 index 0000000..f00213e Binary files /dev/null and b/data/M_13.ld differ diff --git a/data/M_14.ld b/data/M_14.ld new file mode 100644 index 0000000..ef61bb9 Binary files /dev/null and b/data/M_14.ld differ diff --git a/data/M_15.ld b/data/M_15.ld new file mode 100644 index 0000000..ddb8225 Binary files /dev/null and b/data/M_15.ld differ diff --git a/data/M_16.ld b/data/M_16.ld new file mode 100644 index 0000000..4c2c609 Binary files /dev/null and b/data/M_16.ld differ diff --git a/data/M_17.ld b/data/M_17.ld new file mode 100644 index 0000000..75a055f Binary files /dev/null and b/data/M_17.ld differ diff --git a/data/M_18.ld b/data/M_18.ld new file mode 100644 index 0000000..04425e1 Binary files /dev/null and b/data/M_18.ld differ diff --git a/data/M_19.ld b/data/M_19.ld new file mode 100644 index 0000000..824d813 Binary files /dev/null and b/data/M_19.ld differ diff --git a/data/M_20.ld b/data/M_20.ld new file mode 100644 index 0000000..55bf7f9 Binary files /dev/null and b/data/M_20.ld differ diff --git a/data/M_21.ld b/data/M_21.ld new file mode 100644 index 0000000..49caffe Binary files /dev/null and b/data/M_21.ld differ diff --git a/data/M_22.ld b/data/M_22.ld new file mode 100644 index 0000000..a5b3308 Binary files /dev/null and b/data/M_22.ld differ diff --git a/data/S_00.ld b/data/S_00.ld new file mode 100644 index 0000000..4dbc715 Binary files /dev/null and b/data/S_00.ld differ diff --git a/data/S_01-sample.ld b/data/S_01-sample.ld new file mode 100644 index 0000000..9fc4cd3 Binary files /dev/null and b/data/S_01-sample.ld differ diff --git a/data/S_01.ld b/data/S_01.ld new file mode 100644 index 0000000..b3cf8b5 Binary files /dev/null and b/data/S_01.ld differ diff --git a/data/S_02-sample.ld b/data/S_02-sample.ld new file mode 100644 index 0000000..17c37a2 Binary files /dev/null and b/data/S_02-sample.ld differ diff --git a/data/S_02.ld b/data/S_02.ld new file mode 100644 index 0000000..8ce8084 Binary files /dev/null and b/data/S_02.ld differ diff --git a/data/S_03.ld b/data/S_03.ld new file mode 100644 index 0000000..8ea0127 Binary files /dev/null and b/data/S_03.ld differ diff --git a/data/S_04.ld b/data/S_04.ld new file mode 100644 index 0000000..a447a0b Binary files /dev/null and b/data/S_04.ld differ diff --git a/data/S_05.ld b/data/S_05.ld new file mode 100644 index 0000000..23c0d14 Binary files /dev/null and b/data/S_05.ld differ diff --git a/data/S_06.ld b/data/S_06.ld new file mode 100644 index 0000000..a9baabe Binary files /dev/null and b/data/S_06.ld differ diff --git a/data/S_07.ld b/data/S_07.ld new file mode 100644 index 0000000..b081045 Binary files /dev/null and b/data/S_07.ld differ diff --git a/data/S_08.ld b/data/S_08.ld new file mode 100644 index 0000000..2eff44c Binary files /dev/null and b/data/S_08.ld differ diff --git a/data/S_09.ld b/data/S_09.ld new file mode 100644 index 0000000..da35f4a Binary files /dev/null and b/data/S_09.ld differ diff --git a/data/S_10.ld b/data/S_10.ld new file mode 100644 index 0000000..036134f Binary files /dev/null and b/data/S_10.ld differ diff --git a/data/S_11.ld b/data/S_11.ld new file mode 100644 index 0000000..d967548 Binary files /dev/null and b/data/S_11.ld differ diff --git a/data/S_12.ld b/data/S_12.ld new file mode 100644 index 0000000..e4a4317 Binary files /dev/null and b/data/S_12.ld differ diff --git a/data/S_13.ld b/data/S_13.ld new file mode 100644 index 0000000..178b4ab Binary files /dev/null and b/data/S_13.ld differ diff --git a/data/S_14.ld b/data/S_14.ld new file mode 100644 index 0000000..aa398fc Binary files /dev/null and b/data/S_14.ld differ diff --git a/data/SourceArt/Eurostyle Extended.TTF b/data/SourceArt/Eurostyle Extended.TTF new file mode 100644 index 0000000..fac75bd Binary files /dev/null and b/data/SourceArt/Eurostyle Extended.TTF differ diff --git a/data/SourceArt/Quadtrangle.TTF b/data/SourceArt/Quadtrangle.TTF new file mode 100644 index 0000000..51b100f Binary files /dev/null and b/data/SourceArt/Quadtrangle.TTF differ diff --git a/data/SourceArt/SteelWolf.ttf b/data/SourceArt/SteelWolf.ttf new file mode 100644 index 0000000..e0b9039 Binary files /dev/null and b/data/SourceArt/SteelWolf.ttf differ diff --git a/data/SourceArt/titlescreen_hi.psd b/data/SourceArt/titlescreen_hi.psd new file mode 100644 index 0000000..98bd20c Binary files /dev/null and b/data/SourceArt/titlescreen_hi.psd differ diff --git a/data/SourceArt/titlescreen_iphone.psd b/data/SourceArt/titlescreen_iphone.psd new file mode 100644 index 0000000..141c0c7 Binary files /dev/null and b/data/SourceArt/titlescreen_iphone.psd differ diff --git a/data/SourceArt/titlescreen_lo.psd b/data/SourceArt/titlescreen_lo.psd new file mode 100644 index 0000000..0320f44 Binary files /dev/null and b/data/SourceArt/titlescreen_lo.psd differ diff --git a/data/SourceArt/titlescreen_med.psd b/data/SourceArt/titlescreen_med.psd new file mode 100644 index 0000000..5a48e1c Binary files /dev/null and b/data/SourceArt/titlescreen_med.psd differ diff --git a/data/SourceArt/titlescreenbkgd.bmp b/data/SourceArt/titlescreenbkgd.bmp new file mode 100644 index 0000000..4706a3d Binary files /dev/null and b/data/SourceArt/titlescreenbkgd.bmp differ diff --git a/data/SourceArt/wide logo.psd b/data/SourceArt/wide logo.psd new file mode 100644 index 0000000..e2eca55 Binary files /dev/null and b/data/SourceArt/wide logo.psd differ diff --git a/data/TestBmpColors.bat b/data/TestBmpColors.bat new file mode 100644 index 0000000..81a6563 --- /dev/null +++ b/data/TestBmpColors.bat @@ -0,0 +1 @@ +..\bin\bmpproof -p fixed_8bpp.pal *.ani diff --git a/data/TestLevel.ld b/data/TestLevel.ld new file mode 100644 index 0000000..e35b608 Binary files /dev/null and b/data/TestLevel.ld differ diff --git a/data/all.sfx b/data/all.sfx new file mode 100644 index 0000000..63b29b5 --- /dev/null +++ b/data/all.sfx @@ -0,0 +1,1104 @@ +checked +ksfxNothing + +0 +Unknown + +checked +ksfxGalaxiteProcessorAbortRepair +AbortRepair.wav +0 +UI Response + +checked +ksfxGalaxiteProcessorDamaged +crashmix1_2.wav +0 +High Priority + +checked +ksfxGalaxiteProcessorDestroyed +StrutureDestroyed2.wav +0 +Explosion + +checked +ksfxGalaxiteProcessorDoorOpening +dooropen560_v3.wav +0 +Background + +checked +ksfxGalaxiteProcessorDoorClosing +doorclose560_v3.wav + +Background + +checked +ksfxGalaxiteProcessorRepair +Repair.wav +0 +UI Response + +checked +ksfxGalaxiteProcessorSelect +splitent.wav +0 +UI Response + +checked +ksfxGalaxiteWarehouseAbortRepair +AbortRepair.wav +0 +UI Response + +checked +ksfxGalaxiteWarehouseDamaged +crashmix1_2.wav +0 +High Priority + +checked +ksfxGalaxiteWarehouseDestroyed +StrutureDestroyed2.wav +0 +Explosion + +checked +ksfxGalaxiteWarehouseRepair +Repair.wav +0 +UI Response + +checked +ksfxGalaxiteWarehouseSelect +splitent.wav +0 +UI Response + +checked +ksfxGalaxiteWarehouseTooFull +C10d.wav +0 +Voice Status + +checked +ksfxGalaxMinerMine +major01_15.wav +0 +Voice Reponse + +checked +ksfxGalaxMinerUnderAttack +C08b.wav +0 +Voice Status + +checked +ksfxGalaxMinerDeliver +major01_09.wav +0 +Voice Reponse + +checked +ksfxGameBaseUnderAttack +C09b.wav +0 +Voice Status + +checked +ksfxGameCreditsDecreasing +moneya3.wav +0 +Background + +checked +ksfxGameCreditsIncreasing +moneya.wav +0 +Background + +checked +ksfxGameLevelResults + +0 +Unknown + +checked +ksfxGameLoseLevel +C17b.wav +0 +Voice Status + +checked +ksfxGameNewVehicleOptions +C14b.wav +0 +Voice Status + +checked +ksfxGameNewRecruitOptions +C13b.wav +0 +Voice Status + +checked +ksfxGameNewStructureOptions +C12b.wav +0 +Voice Status + +checked +ksfxGameWinLevel +C16d.wav +0 +Voice Status + +checked +ksfxGuiBuildMenuHide + +0 +Unknown + +checked +ksfxGuiBuildMenuSelectItem + +0 +Unknown + +checked +ksfxGuiBuildMenuShow + +0 +Unknown + +checked +ksfxGuiButtonTap +softclick.wav +0 +UI Response + +checked +ksfxGuiCheckBoxTap +checkbox_down.wav +0 +UI Response + +checked +ksfxGuiEditBoxCharacterEntered + +0 +Unknown + +checked +ksfxGuiFormHide + +0 +UI Response + +checked +ksfxGuiFormShow +BRIEFING.WAV +0 +UI Response + +checked +ksfxGuiMissionTextCharOutput + +0 +Unknown + +checked +ksfxGuiScrollingListSelectItem +listselect.wav +0 +UI Response + +checked +ksfxHeadquartersAbortConstruction +C01o.wav +0 +Voice Reponse + +checked +ksfxHeadquartersAbortRepair +AbortRepair.wav +0 +UI Response + +checked +ksfxHeadquartersConstruct +C06b.wav +0 +Voice Status + +checked +ksfxHeadquartersDamaged +crashmix1_2.wav +0 +High Priority + +checked +ksfxHeadquartersDestroyed +StrutureDestroyed2.wav +0 +Explosion + +checked +ksfxHeadquartersRepair +Repair.wav +0 +UI Response + +checked +ksfxHeadquartersSelect +splitent.wav +0 +UI Response + +checked +ksfxHeadquartersStructureReady +C07a.wav +0 +Voice Status + +checked +ksfxHumanResourceCenterAbortRecruiting +C01o.wav +0 +Voice Status + +checked +ksfxHumanResourceCenterAbortRepair +AbortRepair.wav +0 +UI Response + +checked +ksfxHumanResourceCenterDamaged +crashmix1_2.wav +0 +High Priority + +checked +ksfxHumanResourceCenterDestroyed +StrutureDestroyed2.wav +0 +Explosion + +checked +ksfxHumanResourceCenterRecruit +C02g.wav +0 +Voice Status + +checked +ksfxHumanResourceCenterRepair +Repair.wav +0 +UI Response + +checked +ksfxHumanResourceCenterSelect +splitent.wav +0 +UI Response + +checked +ksfxHumanResourceCenterUnitReady +C03q.wav +0 +Voice Status + +checked +ksfxLightTankFire +TankGun_v2.wav +0 +Tank Shot + +checked +ksfxLightTankImpact +Grenade1.wav +0 +Tank Impact + +checked +ksfxMachineGunInfantryFire +sri_machgun_fire4.wav +0 +Machine Gun + +checked +ksfxMachineGunTowerAbortRepair +AbortRepair.wav +0 +UI Response + +checked +ksfxMachineGunTowerAttack + +0 +Unknown + +checked +ksfxMachineGunTowerDamaged +crashmix1_2.wav +0 +High Priority + +checked +ksfxMachineGunTowerDestroyed +StrutureDestroyed2.wav +0 +Explosion + +checked +ksfxMachineGunTowerFire +tower_machgun_fire.wav +0 +Machine Gun + +checked +ksfxMachineGunTowerRepair +Repair.wav +0 +UI Response + +checked +ksfxMachineGunTowerSelect +splitent.wav +0 +UI Response + +checked +ksfxMachineGunVehicleFire +vehicle_machgun_fire2.wav +0 +Machine Gun + +checked +ksfxMediumTankFire +TankGun1v5.wav +0 +Tank Shot + +checked +ksfxMediumTankImpact +xplo5med_v3.wav +0 +Tank Impact + +checked +ksfxMobileHeadquartersDeploy +HQDeploy1_1P3.wav +0 +UI Response + +checked +ksfxRadarAbortRepair +AbortRepair.wav +0 +UI Response + +checked +ksfxRadarDamaged +crashmix1_2.wav +0 +High Priority + +checked +ksfxRadarDestroyed +StrutureDestroyed2.wav +0 +Explosion + +checked +ksfxRadarRepair +Repair.wav +0 +UI Response + +checked +ksfxRadarSelect +splitent.wav +0 +UI Response + +checked +ksfxReactorAbortRepair +AbortRepair.wav +0 +UI Response + +checked +ksfxReactorDamaged +crashmix1_2.wav +0 +High Priority + +checked +ksfxReactorDestroyed +StrutureDestroyed2.wav +0 +Explosion + +checked +ksfxReactorPowerTooLow +C11f.wav +0 +Voice Status + +checked +ksfxReactorRepair +Repair.wav +0 +UI Response + +checked +ksfxReactorSelect +splitent.wav +0 +UI Response + +checked +ksfxResearchCenterAbortRepair +AbortRepair.wav +0 +UI Response + +checked +ksfxResearchCenterDamaged +crashmix1_2.wav +0 +High Priority + +checked +ksfxResearchCenterDestroyed +StrutureDestroyed2.wav +0 +Explosion + +checked +ksfxResearchCenterRepair +Repair.wav +0 +UI Response + +checked +ksfxResearchCenterSelect +splitent.wav +0 +UI Response + +checked +ksfxRocketInfantryFire +rocket_fire.wav +0 +Missle Shot + +checked +ksfxRocketInfantryImpact +rocket_impact.wav +0 +Missle Impact + +checked +ksfxRocketTowerAbortRepair +AbortRepair.wav +0 +UI Response + +checked +ksfxRocketTowerAttack + +0 +Unknown + +checked +ksfxRocketTowerDamaged +crashmix1_2.wav +0 +High Priority + +checked +ksfxRocketTowerDestroyed +StrutureDestroyed2.wav +0 +Explosion + +checked +ksfxRocketTowerFire +rocket_fire.wav +0 +Missle Shot + +checked +ksfxRocketTowerImpact +rocket_impact.wav +0 +Missle Impact + +checked +ksfxRocketTowerRepair +Repair.wav +0 +UI Response + +checked +ksfxRocketTowerSelect +splitent.wav +0 +UI Response + +checked +ksfxRocketVehicleFire +rocket_fire.wav +0 +Missle Shot + +checked +ksfxRocketVehicleImpact +rocket_impact.wav +0 +Missle Impact + +checked +ksfxTakeoverSpecialistStructureCaptured +C15d.wav +0 +Voice Status + +checked +ksfxVehicleTransportStationAbortManufacture +C01o.wav +0 +Voice Status + +checked +ksfxVehicleTransportStationAbortRepair +AbortRepair.wav +0 +UI Response + +checked +ksfxVehicleTransportStationDamaged +crashmix1_2.wav +0 +High Priority + +checked +ksfxVehicleTransportStationDestroyed +StrutureDestroyed2.wav +0 +Explosion + +checked +ksfxVehicleTransportStationManufacture +C04b.wav +0 +Voice Status + +checked +ksfxVehicleTransportStationRepair +Repair.wav +0 +UI Response + +checked +ksfxVehicleTransportStationSelect +splitent.wav +0 +UI Response + +checked +ksfxVehicleTransportStationVehicleReady +C05j.wav +0 +Voice Status + +checked +ksfxVehicleDestroyed +xplo12med.wav +0 +Explosion + +checked +ksfxInfantryDestroyed0 +DeathScream_03.wav +0 +Voice Reponse + +checked +ksfxInfantryDestroyed1 +DeathScream_09.wav +0 +Voice Reponse + +checked +ksfxInfantryDestroyed2 +DeathScream_13.wav +0 +Voice Reponse + +checked +ksfxInfantryDestroyed3 +DeathScream_15.wav +0 +Voice Reponse + +checked +ksfxInfantryDestroyed4 +DeathScream_27.wav +0 +Voice Reponse + +checked +ksfxMale01Select0 +01_04.wav +0 +Voice Reponse + +checked +ksfxMale01Select1 +01_02.wav +0 +Voice Reponse + +checked +ksfxMale01Select2 +01_03.wav +0 +Voice Reponse + +checked +ksfxMale01Select3 +01_04.wav +0 +Voice Reponse + +checked +ksfxMale01Move0 +01_01.wav +0 +Voice Reponse + +checked +ksfxMale01Move1 +01_06.wav +0 +Voice Reponse + +checked +ksfxMale01Move2 +01_08.wav +0 +Voice Reponse + +checked +ksfxMale01Move3 +01_10.wav +0 +Voice Reponse + +checked +ksfxMale01Attack0 +01_12.wav +0 +Voice Reponse + +checked +ksfxMale01Attack1 +01_14.wav +0 +Voice Reponse + +checked +ksfxMale01Attack2 +01_06.wav +0 +Voice Reponse + +checked +ksfxMale01Attack3 +01_10.wav +0 +Voice Reponse + +checked +ksfxMale03Select0 +03_04.wav +0 +Voice Reponse + +checked +ksfxMale03Select1 +03_02.wav +0 +Voice Reponse + +checked +ksfxMale03Select2 +03_03.wav +0 +Voice Reponse + +checked +ksfxMale03Select3 +03_09.wav +0 +Voice Reponse + +checked +ksfxMale03Move0 +03_01.wav +0 +Voice Reponse + +checked +ksfxMale03Move1 +03_06.wav +0 +Voice Reponse + +checked +ksfxMale03Move2 +03_10.wav +0 +Voice Reponse + +checked +ksfxMale03Move3 +03_04.wav +0 +Voice Reponse + +checked +ksfxMale03Attack0 +03_04.wav +0 +Voice Reponse + +checked +ksfxMale03Attack1 +03_06.wav +0 +Voice Reponse + +checked +ksfxMale03Attack2 +03_12.wav +0 +Voice Reponse + +checked +ksfxMale03Attack3 +03_14.wav +0 +Voice Reponse + +checked +ksfxMale06Select0 +06_02.wav +0 +Voice Reponse + +checked +ksfxMale06Select1 +06_03.wav +0 +Voice Reponse + +checked +ksfxMale06Select2 +06_04.wav +0 +Voice Reponse + +checked +ksfxMale06Select3 +06_03.wav +0 +Voice Reponse + +checked +ksfxMale06Move0 +06_01.wav +0 +Voice Reponse + +checked +ksfxMale06Move1 +06_06.wav +0 +Voice Reponse + +checked +ksfxMale06Move2 +06_08.wav +0 +Voice Reponse + +checked +ksfxMale06Move3 +06_10.wav +0 +Voice Reponse + +checked +ksfxMale06Attack0 +06_10.wav +0 +Voice Reponse + +checked +ksfxMale06Attack1 +06_08.wav +0 +Voice Reponse + +checked +ksfxMale06Attack2 +06_12.wav +0 +Voice Reponse + +checked +ksfxMale06Attack3 +06_13.wav +0 +Voice Reponse + +checked +ksfxMajor01Select0 +major01_05.wav +0 +Voice Reponse + +checked +ksfxMajor01Select1 +major01_02.wav +0 +Voice Reponse + +checked +ksfxMajor01Select2 +major01_07.wav +0 +Voice Reponse + +checked +ksfxMajor01Select3 +major01_06.wav +0 +Voice Reponse + +checked +ksfxMajor01Move0 +major01_01.wav +0 +Voice Reponse + +checked +ksfxMajor01Move1 +major01_04.wav +0 +Voice Reponse + +checked +ksfxMajor01Move2 +major01_15.wav +0 +Voice Reponse + +checked +ksfxMajor01Move3 +major01_09.wav +0 +Voice Reponse + +checked +ksfxMajor01Attack0 +major01_17.wav +0 +Voice Reponse + +checked +ksfxMajor01Attack1 +major01_15.wav +0 +Voice Reponse + +checked +ksfxMajor01Attack2 +major01_18.wav +0 +Voice Reponse + +checked +ksfxMajor01Attack3 +major01_12.wav +0 +Voice Reponse + +checked +ksfxMajor02Select0 +Major02_02.wav +0 +Voice Reponse + +checked +ksfxMajor02Select1 +Major02_05.wav +0 +Voice Reponse + +checked +ksfxMajor02Select2 +Major02_06.wav +0 +Voice Reponse + +checked +ksfxMajor02Select3 +Major02_03.wav +0 +Voice Reponse + +checked +ksfxMajor02Move0 +Major02_01.wav +0 +Voice Reponse + +checked +ksfxMajor02Move1 +Major02_04.wav +0 +Voice Reponse + +checked +ksfxMajor02Move2 +Major02_07.wav +0 +Voice Reponse + +checked +ksfxMajor02Move3 +Major02_09.wav +0 +Voice Reponse + +checked +ksfxMajor02Attack0 +Major02_15.wav +0 +Voice Reponse + +checked +ksfxMajor02Attack1 +Major02_16.wav +0 +Voice Reponse + +checked +ksfxMajor02Attack2 +Major02_07.wav +0 +Voice Reponse + +checked +ksfxMajor02Attack3 +Major02_01.wav +0 +Voice Reponse + +checked +ksfxAndySelect +Andy_Select.wav +0 +Voice Reponse + +checked +ksfxAndyMove +Andy_Move.wav +0 +Voice Reponse + +checked +ksfxAndyAttack +Andy_Attack.wav +0 +Voice Reponse + +checked +ksfxAndyFire +Andy_Fire.wav +0 +Missle Impact + +checked +ksfxAndyDestroyed +Andy_Destroyed.wav +0 +Voice Reponse + +checked +ksfxReplicatorOn +ReplicatorOn.wav +0 +High Priority + +checked +ksfxReplicatorOff +ReplicatorOff.wav +0 +High Priority + +checked +ksfxReplicatorBuild +ReplicatorBuild.wav +0 +High Priority + +checked +ksfxActivatorOn +ActivatorOn.wav +0 +High Priority + +checked +ksfxActivatorOff +ActivatorOff.wav +0 +High Priority + +checked +ksfxArtilleryFire +BigTank.wav +0 +Explosion + +checked +ksfxArtilleryImpact +BigTankImpact.wav +0 +Explosion + +checked +ksfxHappyEnding +HappyEndinglng.wav +0 +High Priority + +checked +ksfxReplicatorDestroyed +BigExplosion4lng.wav +0 +High Priority + +checked +ksfxFoxDestroyed +DeathScream99b.wav +0 +Voice Reponse + diff --git a/data/art824/.cvsignore b/data/art824/.cvsignore new file mode 100644 index 0000000..96811e1 --- /dev/null +++ b/data/art824/.cvsignore @@ -0,0 +1 @@ +*.pal diff --git a/data/art824/HQ.amx b/data/art824/HQ.amx new file mode 100644 index 0000000..81572f1 --- /dev/null +++ b/data/art824/HQ.amx @@ -0,0 +1,453 @@ + + + + + +80 +true + + +<_x0030_ href="#ref-5"/> +<_x0031_ href="#ref-6"/> +<_x0032_ href="#ref-7"/> +<_x0033_ href="#ref-8"/> +<_x0034_ href="#ref-9"/> +<_x0035_ href="#ref-10"/> +<_x0036_ href="#ref-11"/> +<_x0037_ href="#ref-12"/> +<_x0038_ href="#ref-13"/> +<_x0039_ href="#ref-14"/> +<_x0031_0 href="#ref-15"/> +<_x0031_1 href="#ref-16"/> + + +<_x0030_ href="#ref-17"/> +<_x0031_ href="#ref-18"/> +<_x0032_ href="#ref-19"/> +<_x0033_ href="#ref-20"/> +<_x0034_ href="#ref-21"/> + + +HQ\hq_a_0_0.png + + +HQ\hq_a_0_1.png + + +HQ\hq_a_0_2.png + + +HQ\hq_a_0_3.png + + +HQ\Hq_a_1_0.png + + +HQ\hq_a_1_1.png + + +HQ\hq_a_1_2.png + + +HQ\hq_a_1_3.png + + +HQ\Hq_a_2_0.png + + +HQ\hq_a_0_4.png + + +HQ\hq_a_1_4.png + + +HQ\icon.png + + +a 0 +0 +8 +<_x0030_ href="#ref-35"/> +<_x0031_ href="#ref-36"/> +<_x0032_ href="#ref-37"/> +<_x0033_ href="#ref-38"/> +<_x0034_ href="#ref-39"/> +<_x0035_ href="#ref-40"/> +<_x0036_ href="#ref-41"/> +<_x0037_ href="#ref-42"/> + + +a 1 +0 +8 +<_x0030_ href="#ref-44"/> +<_x0031_ href="#ref-45"/> +<_x0032_ href="#ref-46"/> +<_x0033_ href="#ref-47"/> +<_x0034_ href="#ref-48"/> +<_x0035_ href="#ref-49"/> +<_x0036_ href="#ref-50"/> +<_x0037_ href="#ref-51"/> + + +a 2 +0 +1 +<_x0030_ href="#ref-53"/> + + +icon +0 +1 +<_x0030_ href="#ref-55"/> + + +help +0 +1 +<_x0030_ href="#ref-57"/> + + + +38 + +0 +0 + + + + +1 + +0 +0 + + + + +1 + +0 +0 + + + + +1 + +0 +0 + + + + +10 + +0 +0 + + + + +1 + +0 +0 + + + + +1 + +0 +0 + + + + +1 + +0 +0 + + + + +38 + +0 +0 + + + + +1 + +0 +0 + + + + +1 + +0 +0 + + + + +1 + +0 +0 + + + + +10 + +0 +0 + + + + +1 + +0 +0 + + + + +1 + +0 +0 + + + + +1 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + +<_x0030_ href="#ref-78"/> + + +<_x0030_ href="#ref-79"/> + + +<_x0030_ href="#ref-80"/> + + +<_x0030_ href="#ref-81"/> + + +<_x0030_ href="#ref-82"/> + + +<_x0030_ href="#ref-83"/> + + +<_x0030_ href="#ref-84"/> + + +<_x0030_ href="#ref-85"/> + + +<_x0030_ href="#ref-86"/> + + +<_x0030_ href="#ref-87"/> + + +<_x0030_ href="#ref-88"/> + + +<_x0030_ href="#ref-89"/> + + +<_x0030_ href="#ref-90"/> + + +<_x0030_ href="#ref-91"/> + + +<_x0030_ href="#ref-92"/> + + +<_x0030_ href="#ref-93"/> + + +<_x0030_ href="#ref-94"/> + + +<_x0030_ href="#ref-95"/> + + +<_x0030_ href="#ref-96"/> + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +36 +27 + + + + + diff --git a/data/art824/HQ/Hq_a_1_0.png b/data/art824/HQ/Hq_a_1_0.png new file mode 100644 index 0000000..4cfee53 Binary files /dev/null and b/data/art824/HQ/Hq_a_1_0.png differ diff --git a/data/art824/HQ/Hq_a_2_0.png b/data/art824/HQ/Hq_a_2_0.png new file mode 100644 index 0000000..e803abe Binary files /dev/null and b/data/art824/HQ/Hq_a_2_0.png differ diff --git a/data/art824/HQ/hq_a_0_0.png b/data/art824/HQ/hq_a_0_0.png new file mode 100644 index 0000000..6533291 Binary files /dev/null and b/data/art824/HQ/hq_a_0_0.png differ diff --git a/data/art824/HQ/hq_a_0_1.png b/data/art824/HQ/hq_a_0_1.png new file mode 100644 index 0000000..74c22ae Binary files /dev/null and b/data/art824/HQ/hq_a_0_1.png differ diff --git a/data/art824/HQ/hq_a_0_2.png b/data/art824/HQ/hq_a_0_2.png new file mode 100644 index 0000000..bb2c401 Binary files /dev/null and b/data/art824/HQ/hq_a_0_2.png differ diff --git a/data/art824/HQ/hq_a_0_3.png b/data/art824/HQ/hq_a_0_3.png new file mode 100644 index 0000000..3b16dd5 Binary files /dev/null and b/data/art824/HQ/hq_a_0_3.png differ diff --git a/data/art824/HQ/hq_a_0_4.png b/data/art824/HQ/hq_a_0_4.png new file mode 100644 index 0000000..8e3b8a6 Binary files /dev/null and b/data/art824/HQ/hq_a_0_4.png differ diff --git a/data/art824/HQ/hq_a_1_1.png b/data/art824/HQ/hq_a_1_1.png new file mode 100644 index 0000000..528b59d Binary files /dev/null and b/data/art824/HQ/hq_a_1_1.png differ diff --git a/data/art824/HQ/hq_a_1_2.png b/data/art824/HQ/hq_a_1_2.png new file mode 100644 index 0000000..125c216 Binary files /dev/null and b/data/art824/HQ/hq_a_1_2.png differ diff --git a/data/art824/HQ/hq_a_1_3.png b/data/art824/HQ/hq_a_1_3.png new file mode 100644 index 0000000..99eab1a Binary files /dev/null and b/data/art824/HQ/hq_a_1_3.png differ diff --git a/data/art824/HQ/hq_a_1_4.png b/data/art824/HQ/hq_a_1_4.png new file mode 100644 index 0000000..07303e6 Binary files /dev/null and b/data/art824/HQ/hq_a_1_4.png differ diff --git a/data/art824/HQ/icon.png b/data/art824/HQ/icon.png new file mode 100644 index 0000000..5006e7a Binary files /dev/null and b/data/art824/HQ/icon.png differ diff --git a/data/art824/Radar.amx b/data/art824/Radar.amx new file mode 100644 index 0000000..a3a7634 --- /dev/null +++ b/data/art824/Radar.amx @@ -0,0 +1,293 @@ + + + + + +80 +true + + +<_x0030_ href="#ref-5"/> +<_x0031_ href="#ref-6"/> +<_x0032_ href="#ref-7"/> +<_x0033_ href="#ref-8"/> +<_x0034_ href="#ref-9"/> +<_x0035_ href="#ref-10"/> +<_x0036_ href="#ref-11"/> +<_x0037_ href="#ref-12"/> +<_x0038_ href="#ref-13"/> +<_x0039_ href="#ref-14"/> + + +<_x0030_ href="#ref-15"/> +<_x0031_ href="#ref-16"/> +<_x0032_ href="#ref-17"/> +<_x0033_ href="#ref-18"/> +<_x0034_ href="#ref-19"/> + + +Radar\radar_a_0_3.png + + +Radar\radar_a_0_1.png + + +Radar\radar_a_0_2.png + + +Radar\radar_a_0_0.png + + +Radar\radar_a_1_3.png + + +Radar\radar_a_1_1.png + + +Radar\radar_a_1_2.png + + +Radar\radar_a_1_0.png + + +Radar\radar_a_2_0.png + + +Radar\icon.png + + +a 0 +2 +4 +<_x0030_ href="#ref-31"/> +<_x0031_ href="#ref-32"/> +<_x0032_ href="#ref-33"/> +<_x0033_ href="#ref-34"/> + + +a 1 +2 +4 +<_x0030_ href="#ref-36"/> +<_x0031_ href="#ref-37"/> +<_x0032_ href="#ref-38"/> +<_x0033_ href="#ref-39"/> + + +a 2 +0 +1 +<_x0030_ href="#ref-41"/> + + +icon +0 +1 +<_x0030_ href="#ref-43"/> + + +help +0 +1 +<_x0030_ href="#ref-45"/> + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + +<_x0030_ href="#ref-58"/> + + +<_x0030_ href="#ref-59"/> + + +<_x0030_ href="#ref-60"/> + + +<_x0030_ href="#ref-61"/> + + +<_x0030_ href="#ref-62"/> + + +<_x0030_ href="#ref-63"/> + + +<_x0030_ href="#ref-64"/> + + +<_x0030_ href="#ref-65"/> + + +<_x0030_ href="#ref-66"/> + + +<_x0030_ href="#ref-67"/> + + +<_x0030_ href="#ref-68"/> + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +24 +24 + + + + + diff --git a/data/art824/Research.amx b/data/art824/Research.amx new file mode 100644 index 0000000..9f41637 --- /dev/null +++ b/data/art824/Research.amx @@ -0,0 +1,272 @@ + + + + + +80 +true + + +<_x0030_ href="#ref-5"/> +<_x0031_ href="#ref-6"/> +<_x0032_ href="#ref-7"/> +<_x0033_ href="#ref-8"/> +<_x0034_ href="#ref-9"/> +<_x0035_ href="#ref-10"/> +<_x0036_ href="#ref-11"/> + + +<_x0030_ href="#ref-12"/> +<_x0031_ href="#ref-13"/> +<_x0032_ href="#ref-14"/> +<_x0033_ href="#ref-15"/> +<_x0034_ href="#ref-16"/> + + +Research\research_a_0_0.png + + +Research\research_dish_0_2.png + + +Research\research_dish_0_1.png + + +Research\research_dish_0_0.png + + +Research\research_a_2_0.png + + +Research\research_a_1_0.png + + +Research\icon.png + + +a 0 +0 +4 +<_x0030_ href="#ref-25"/> +<_x0031_ href="#ref-26"/> +<_x0032_ href="#ref-27"/> +<_x0033_ href="#ref-28"/> + + +a 1 +0 +1 +<_x0030_ href="#ref-30"/> + + +a 2 +0 +1 +<_x0030_ href="#ref-32"/> + + +icon +0 +1 +<_x0030_ href="#ref-34"/> + + +help +0 +1 +<_x0030_ href="#ref-36"/> + + + +6 + +0 +0 + + + + +5 + +0 +0 + + + + +6 + +0 +0 + + + + +5 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + +<_x0030_ href="#ref-46"/> +<_x0031_ href="#ref-47"/> + + +<_x0030_ href="#ref-48"/> +<_x0031_ href="#ref-49"/> + + +<_x0030_ href="#ref-50"/> +<_x0031_ href="#ref-51"/> + + +<_x0030_ href="#ref-52"/> +<_x0031_ href="#ref-53"/> + + +<_x0030_ href="#ref-54"/> +<_x0031_ href="#ref-55"/> + + +<_x0030_ href="#ref-56"/> + + +<_x0030_ href="#ref-57"/> + + +<_x0030_ href="#ref-58"/> +<_x0031_ href="#ref-59"/> + + + +-12 +-3 + + + + + +3 +-3 + + + + + +-13 +-3 + + + + + +3 +-3 + + + + + +-14 +-3 + + + + + +3 +-3 + + + + + +-13 +-3 + + + + + +3 +-3 + + + + + +-12 +-6 + + + + + +3 +-3 + + + + + +3 +-3 + + + + + +1 +-2 + + + + + +14 +23 + + + + + +26 +21 + + + + + diff --git a/data/art824/VTS.amx b/data/art824/VTS.amx new file mode 100644 index 0000000..eb8fc68 --- /dev/null +++ b/data/art824/VTS.amx @@ -0,0 +1,242 @@ + + + + + +80 +true + + +<_x0030_ href="#ref-5"/> +<_x0031_ href="#ref-6"/> +<_x0032_ href="#ref-7"/> +<_x0033_ href="#ref-8"/> +<_x0034_ href="#ref-9"/> +<_x0035_ href="#ref-10"/> +<_x0036_ href="#ref-11"/> + + +<_x0030_ href="#ref-12"/> +<_x0031_ href="#ref-13"/> +<_x0032_ href="#ref-14"/> +<_x0033_ href="#ref-15"/> +<_x0034_ href="#ref-16"/> +<_x0035_ href="#ref-17"/> +<_x0036_ href="#ref-18"/> +<_x0037_ href="#ref-19"/> + + +VTS\vts_b_0_0.png + + +VTS\vts_b_1_0.png + + +VTS\vts_b_2_0.png + + +VTS\vts_a_2_0.png + + +VTS\vts_a_1_0.png + + +VTS\vts_a_0_0.png + + +VTS\icon.png + + +a 0 +0 +1 +<_x0030_ href="#ref-28"/> + + +a 1 +0 +1 +<_x0030_ href="#ref-30"/> + + +a 2 +0 +1 +<_x0030_ href="#ref-32"/> + + +b 0 +0 +1 +<_x0030_ href="#ref-34"/> + + +b 1 +0 +1 +<_x0030_ href="#ref-36"/> + + +b 2 +0 +1 +<_x0030_ href="#ref-38"/> + + +icon +0 +1 +<_x0030_ href="#ref-40"/> + + +help +0 +1 +<_x0030_ href="#ref-42"/> + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + +<_x0030_ href="#ref-52"/> + + +<_x0030_ href="#ref-53"/> + + +<_x0030_ href="#ref-54"/> + + +<_x0030_ href="#ref-55"/> + + +<_x0030_ href="#ref-56"/> + + +<_x0030_ href="#ref-57"/> + + +<_x0030_ href="#ref-58"/> + + +<_x0030_ href="#ref-59"/> + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +36 +24 + + + + + diff --git a/data/art824/activator.amx b/data/art824/activator.amx new file mode 100644 index 0000000..d588687 --- /dev/null +++ b/data/art824/activator.amx @@ -0,0 +1,91 @@ + + + + + +80 +true + + +<_x0030_ href="#ref-5"/> +<_x0031_ href="#ref-6"/> + + +<_x0030_ href="#ref-7"/> +<_x0031_ href="#ref-8"/> + + +activator\activator_off.png + + +activator\activator_on.png + + +off +0 +1 +<_x0030_ href="#ref-12"/> + + +on +0 +2 +<_x0030_ href="#ref-14"/> +<_x0031_ href="#ref-15"/> + + + +0 + +0 +0 + + + + +12 + +0 +0 + + + + +12 + +0 +0 + + + +<_x0030_ href="#ref-20"/> + + +<_x0030_ href="#ref-21"/> + + +<_x0030_ href="#ref-22"/> + + + +22 +19 + + + + + +22 +19 + + + + + +22 +19 + + + + + diff --git a/data/art824/activator/activator_off.png b/data/art824/activator/activator_off.png new file mode 100644 index 0000000..df87883 Binary files /dev/null and b/data/art824/activator/activator_off.png differ diff --git a/data/art824/activator/activator_on.png b/data/art824/activator/activator_on.png new file mode 100644 index 0000000..b35815a Binary files /dev/null and b/data/art824/activator/activator_on.png differ diff --git a/data/art824/andy.amx b/data/art824/andy.amx new file mode 100644 index 0000000..9ee0e42 --- /dev/null +++ b/data/art824/andy.amx @@ -0,0 +1,2770 @@ + + + + + +80 +true + + +<_x0030_ href="#ref-5"/> +<_x0031_ href="#ref-6"/> +<_x0032_ href="#ref-7"/> +<_x0033_ href="#ref-8"/> +<_x0034_ href="#ref-9"/> +<_x0035_ href="#ref-10"/> +<_x0036_ href="#ref-11"/> +<_x0037_ href="#ref-12"/> +<_x0038_ href="#ref-13"/> +<_x0039_ href="#ref-14"/> +<_x0031_0 href="#ref-15"/> +<_x0031_1 href="#ref-16"/> +<_x0031_2 href="#ref-17"/> +<_x0031_3 href="#ref-18"/> +<_x0031_4 href="#ref-19"/> +<_x0031_5 href="#ref-20"/> +<_x0031_6 href="#ref-21"/> +<_x0031_7 href="#ref-22"/> +<_x0031_8 href="#ref-23"/> +<_x0031_9 href="#ref-24"/> +<_x0032_0 href="#ref-25"/> +<_x0032_1 href="#ref-26"/> +<_x0032_2 href="#ref-27"/> +<_x0032_3 href="#ref-28"/> +<_x0032_4 href="#ref-29"/> +<_x0032_5 href="#ref-30"/> +<_x0032_6 href="#ref-31"/> +<_x0032_7 href="#ref-32"/> +<_x0032_8 href="#ref-33"/> +<_x0032_9 href="#ref-34"/> +<_x0033_0 href="#ref-35"/> +<_x0033_1 href="#ref-36"/> +<_x0033_2 href="#ref-37"/> +<_x0033_3 href="#ref-38"/> +<_x0033_4 href="#ref-39"/> +<_x0033_5 href="#ref-40"/> +<_x0033_6 href="#ref-41"/> +<_x0033_7 href="#ref-42"/> +<_x0033_8 href="#ref-43"/> +<_x0033_9 href="#ref-44"/> +<_x0034_0 href="#ref-45"/> +<_x0034_1 href="#ref-46"/> +<_x0034_2 href="#ref-47"/> +<_x0034_3 href="#ref-48"/> +<_x0034_4 href="#ref-49"/> +<_x0034_5 href="#ref-50"/> +<_x0034_6 href="#ref-51"/> +<_x0034_7 href="#ref-52"/> +<_x0034_8 href="#ref-53"/> +<_x0034_9 href="#ref-54"/> +<_x0035_0 href="#ref-55"/> +<_x0035_1 href="#ref-56"/> +<_x0035_2 href="#ref-57"/> +<_x0035_3 href="#ref-58"/> +<_x0035_4 href="#ref-59"/> +<_x0035_5 href="#ref-60"/> +<_x0035_6 href="#ref-61"/> +<_x0035_7 href="#ref-62"/> +<_x0035_8 href="#ref-63"/> +<_x0035_9 href="#ref-64"/> +<_x0036_0 href="#ref-65"/> +<_x0036_1 href="#ref-66"/> +<_x0036_2 href="#ref-67"/> +<_x0036_3 href="#ref-68"/> +<_x0036_4 href="#ref-69"/> +<_x0036_5 href="#ref-70"/> +<_x0036_6 href="#ref-71"/> +<_x0036_7 href="#ref-72"/> +<_x0036_8 href="#ref-73"/> +<_x0036_9 href="#ref-74"/> +<_x0037_0 href="#ref-75"/> +<_x0037_1 href="#ref-76"/> +<_x0037_2 href="#ref-77"/> +<_x0037_3 href="#ref-78"/> +<_x0037_4 href="#ref-79"/> +<_x0037_5 href="#ref-80"/> +<_x0037_6 href="#ref-81"/> +<_x0037_7 href="#ref-82"/> +<_x0037_8 href="#ref-83"/> +<_x0037_9 href="#ref-84"/> +<_x0038_0 href="#ref-85"/> +<_x0038_1 href="#ref-86"/> +<_x0038_2 href="#ref-87"/> +<_x0038_3 href="#ref-88"/> +<_x0038_4 href="#ref-89"/> +<_x0038_5 href="#ref-90"/> +<_x0038_6 href="#ref-91"/> +<_x0038_7 href="#ref-92"/> +<_x0038_8 href="#ref-93"/> +<_x0038_9 href="#ref-94"/> +<_x0039_0 href="#ref-95"/> +<_x0039_1 href="#ref-96"/> +<_x0039_2 href="#ref-97"/> +<_x0039_3 href="#ref-98"/> +<_x0039_4 href="#ref-99"/> +<_x0039_5 href="#ref-100"/> +<_x0039_6 href="#ref-101"/> +<_x0039_7 href="#ref-102"/> +<_x0039_8 href="#ref-103"/> +<_x0039_9 href="#ref-104"/> +<_x0031_00 href="#ref-105"/> +<_x0031_01 href="#ref-106"/> +<_x0031_02 href="#ref-107"/> +<_x0031_03 href="#ref-108"/> +<_x0031_04 href="#ref-109"/> +<_x0031_05 href="#ref-110"/> +<_x0031_06 href="#ref-111"/> + + +<_x0030_ href="#ref-112"/> +<_x0031_ href="#ref-113"/> +<_x0032_ href="#ref-114"/> +<_x0033_ href="#ref-115"/> +<_x0034_ href="#ref-116"/> +<_x0035_ href="#ref-117"/> +<_x0036_ href="#ref-118"/> +<_x0037_ href="#ref-119"/> +<_x0038_ href="#ref-120"/> +<_x0039_ href="#ref-121"/> +<_x0031_0 href="#ref-122"/> +<_x0031_1 href="#ref-123"/> +<_x0031_2 href="#ref-124"/> +<_x0031_3 href="#ref-125"/> +<_x0031_4 href="#ref-126"/> +<_x0031_5 href="#ref-127"/> +<_x0031_6 href="#ref-128"/> +<_x0031_7 href="#ref-129"/> +<_x0031_8 href="#ref-130"/> +<_x0031_9 href="#ref-131"/> +<_x0032_0 href="#ref-132"/> +<_x0032_1 href="#ref-133"/> +<_x0032_2 href="#ref-134"/> +<_x0032_3 href="#ref-135"/> +<_x0032_4 href="#ref-136"/> +<_x0032_5 href="#ref-137"/> +<_x0032_6 href="#ref-138"/> + + +andy\andy_jog_7_5.png + + +andy\andy_jog_0_0.png + + +andy\andy_jog_0_1.png + + +andy\andy_jog_0_2.png + + +andy\andy_jog_0_3.png + + +andy\andy_jog_0_4.png + + +andy\andy_jog_0_5.png + + +andy\andy_jog_1_0.png + + +andy\andy_jog_1_1.png + + +andy\andy_jog_1_2.png + + +andy\andy_jog_1_3.png + + +andy\andy_jog_1_4.png + + +andy\andy_jog_1_5.png + + +andy\andy_jog_2_0.png + + +andy\andy_jog_2_1.png + + +andy\andy_jog_2_2.png + + +andy\andy_jog_2_3.png + + +andy\andy_jog_2_4.png + + +andy\andy_jog_2_5.png + + +andy\andy_jog_3_0.png + + +andy\andy_jog_3_1.png + + +andy\andy_jog_3_2.png + + +andy\andy_jog_3_3.png + + +andy\andy_jog_3_4.png + + +andy\andy_jog_3_5.png + + +andy\andy_jog_4_0.png + + +andy\andy_jog_4_1.png + + +andy\andy_jog_4_2.png + + +andy\andy_jog_4_3.png + + +andy\andy_jog_4_4.png + + +andy\andy_jog_4_5.png + + +andy\andy_jog_5_0.png + + +andy\andy_jog_5_1.png + + +andy\andy_jog_5_2.png + + +andy\andy_jog_5_3.png + + +andy\andy_jog_5_4.png + + +andy\andy_jog_5_5.png + + +andy\andy_jog_6_0.png + + +andy\andy_jog_6_1.png + + +andy\andy_jog_6_2.png + + +andy\andy_jog_6_3.png + + +andy\andy_jog_6_4.png + + +andy\andy_jog_6_5.png + + +andy\andy_jog_7_0.png + + +andy\andy_jog_7_1.png + + +andy\andy_jog_7_2.png + + +andy\andy_jog_7_3.png + + +andy\andy_jog_7_4.png + + +andy\andy_die_3_5.png + + +andy\andy_die_3_1.png + + +andy\andy_die_3_2.png + + +andy\andy_die_3_3.png + + +andy\andy_die_3_4.png + + +andy\andy_die_3_0.png + + +andy\andy_fire_7_2.png + + +andy\andy_fire_0_0.png + + +andy\andy_fire_0_1.png + + +andy\andy_fire_0_2.png + + +andy\andy_fire_1_0.png + + +andy\andy_fire_1_1.png + + +andy\andy_fire_1_2.png + + +andy\andy_fire_2_0.png + + +andy\andy_fire_2_2.png + + +andy\andy_fire_4_0.png + + +andy\andy_fire_4_1.png + + +andy\andy_fire_4_2.png + + +andy\andy_fire_5_0.png + + +andy\andy_fire_5_1.png + + +andy\andy_fire_5_2.png + + +andy\andy_fire_6_0.png + + +andy\andy_fire_6_1.png + + +andy\andy_fire_6_2.png + + +andy\andy_fire_7_0.png + + +andy\andy_fire_7_1.png + + +andy\andy_fire_2_1.png + + +andy\andy_fire_3_2.png + + +andy\andy_fire_3_0.png + + +andy\andy_fire_3_1.png + + +andy\andy_idle_7_5.png + + +andy\andy_idle_0_0.png + + +andy\andy_idle_0_1.png + + +andy\andy_idle_0_2.png + + +andy\andy_idle_0_3.png + + +andy\andy_idle_1_0.png + + +andy\andy_idle_1_1.png + + +andy\andy_idle_1_2.png + + +andy\andy_idle_2_0.png + + +andy\andy_idle_2_1.png + + +andy\andy_idle_2_2.png + + +andy\andy_idle_3_0.png + + +andy\andy_idle_3_1.png + + +andy\andy_idle_3_2.png + + +andy\andy_idle_4_0.png + + +andy\andy_idle_4_1.png + + +andy\andy_idle_4_2.png + + +andy\andy_idle_5_0.png + + +andy\andy_idle_5_1.png + + +andy\andy_idle_6_0.png + + +andy\andy_idle_6_1.png + + +andy\andy_idle_6_2.png + + +andy\andy_idle_6_3.png + + +andy\andy_idle_7_0.png + + +andy\andy_idle_7_1.png + + +andy\andy_idle_7_2.png + + +andy\andy_idle_7_3.png + + +andy\andy_idle_7_4.png + + +andy\andy_idle_5_2.png + + +die 3 +0 +6 +<_x0030_ href="#ref-247"/> +<_x0031_ href="#ref-248"/> +<_x0032_ href="#ref-249"/> +<_x0033_ href="#ref-250"/> +<_x0034_ href="#ref-251"/> +<_x0035_ href="#ref-252"/> + + +fire 0 +0 +3 +<_x0030_ href="#ref-254"/> +<_x0031_ href="#ref-255"/> +<_x0032_ href="#ref-256"/> + + +fire 1 +0 +3 +<_x0030_ href="#ref-258"/> +<_x0031_ href="#ref-259"/> +<_x0032_ href="#ref-260"/> + + +fire 2 +0 +3 +<_x0030_ href="#ref-262"/> +<_x0031_ href="#ref-263"/> +<_x0032_ href="#ref-264"/> + + +fire 3 +0 +3 +<_x0030_ href="#ref-266"/> +<_x0031_ href="#ref-267"/> +<_x0032_ href="#ref-268"/> + + +fire 4 +0 +3 +<_x0030_ href="#ref-270"/> +<_x0031_ href="#ref-271"/> +<_x0032_ href="#ref-272"/> + + +fire 5 +0 +3 +<_x0030_ href="#ref-274"/> +<_x0031_ href="#ref-275"/> +<_x0032_ href="#ref-276"/> + + +fire 6 +0 +3 +<_x0030_ href="#ref-278"/> +<_x0031_ href="#ref-279"/> +<_x0032_ href="#ref-280"/> + + +fire 7 +0 +3 +<_x0030_ href="#ref-282"/> +<_x0031_ href="#ref-283"/> +<_x0032_ href="#ref-284"/> + + +idle 0 +0 +4 +<_x0030_ href="#ref-286"/> +<_x0031_ href="#ref-287"/> +<_x0032_ href="#ref-288"/> +<_x0033_ href="#ref-289"/> + + +idle 1 +0 +4 +<_x0030_ href="#ref-291"/> +<_x0031_ href="#ref-292"/> +<_x0032_ href="#ref-293"/> +<_x0033_ href="#ref-294"/> + + +idle 2 +0 +4 +<_x0030_ href="#ref-296"/> +<_x0031_ href="#ref-297"/> +<_x0032_ href="#ref-298"/> +<_x0033_ href="#ref-299"/> + + +idle 3 +0 +4 +<_x0030_ href="#ref-301"/> +<_x0031_ href="#ref-302"/> +<_x0032_ href="#ref-303"/> +<_x0033_ href="#ref-304"/> + + +idle 4 +0 +4 +<_x0030_ href="#ref-306"/> +<_x0031_ href="#ref-307"/> +<_x0032_ href="#ref-308"/> +<_x0033_ href="#ref-309"/> + + +idle 5 +0 +4 +<_x0030_ href="#ref-311"/> +<_x0031_ href="#ref-312"/> +<_x0032_ href="#ref-313"/> +<_x0033_ href="#ref-314"/> + + +idle 6 +0 +4 +<_x0030_ href="#ref-316"/> +<_x0031_ href="#ref-317"/> +<_x0032_ href="#ref-318"/> +<_x0033_ href="#ref-319"/> + + +idle 7 +0 +6 +<_x0030_ href="#ref-321"/> +<_x0031_ href="#ref-322"/> +<_x0032_ href="#ref-323"/> +<_x0033_ href="#ref-324"/> +<_x0034_ href="#ref-325"/> +<_x0035_ href="#ref-326"/> + + +jog 0 +0 +6 +<_x0030_ href="#ref-328"/> +<_x0031_ href="#ref-329"/> +<_x0032_ href="#ref-330"/> +<_x0033_ href="#ref-331"/> +<_x0034_ href="#ref-332"/> +<_x0035_ href="#ref-333"/> + + +jog 1 +0 +6 +<_x0030_ href="#ref-335"/> +<_x0031_ href="#ref-336"/> +<_x0032_ href="#ref-337"/> +<_x0033_ href="#ref-338"/> +<_x0034_ href="#ref-339"/> +<_x0035_ href="#ref-340"/> + + +jog 2 +0 +6 +<_x0030_ href="#ref-342"/> +<_x0031_ href="#ref-343"/> +<_x0032_ href="#ref-344"/> +<_x0033_ href="#ref-345"/> +<_x0034_ href="#ref-346"/> +<_x0035_ href="#ref-347"/> + + +jog 3 +0 +6 +<_x0030_ href="#ref-349"/> +<_x0031_ href="#ref-350"/> +<_x0032_ href="#ref-351"/> +<_x0033_ href="#ref-352"/> +<_x0034_ href="#ref-353"/> +<_x0035_ href="#ref-354"/> + + +jog 4 +0 +6 +<_x0030_ href="#ref-356"/> +<_x0031_ href="#ref-357"/> +<_x0032_ href="#ref-358"/> +<_x0033_ href="#ref-359"/> +<_x0034_ href="#ref-360"/> +<_x0035_ href="#ref-361"/> + + +jog 5 +0 +6 +<_x0030_ href="#ref-363"/> +<_x0031_ href="#ref-364"/> +<_x0032_ href="#ref-365"/> +<_x0033_ href="#ref-366"/> +<_x0034_ href="#ref-367"/> +<_x0035_ href="#ref-368"/> + + +jog 6 +0 +6 +<_x0030_ href="#ref-370"/> +<_x0031_ href="#ref-371"/> +<_x0032_ href="#ref-372"/> +<_x0033_ href="#ref-373"/> +<_x0034_ href="#ref-374"/> +<_x0035_ href="#ref-375"/> + + +jog 7 +0 +6 +<_x0030_ href="#ref-377"/> +<_x0031_ href="#ref-378"/> +<_x0032_ href="#ref-379"/> +<_x0033_ href="#ref-380"/> +<_x0034_ href="#ref-381"/> +<_x0035_ href="#ref-382"/> + + +icon +0 +1 +<_x0030_ href="#ref-384"/> + + +help +0 +1 +<_x0030_ href="#ref-386"/> + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +2 +-14 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +11 +-13 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +14 +-6 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +11 +5 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-3 +2 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-9 +2 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-14 +-4 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-9 +-13 + + + + +0 + +0 +0 + + + + +5 + +0 +0 + + + + +1 + +0 +0 + + + + +2 + +0 +0 + + + + +10 + +0 +0 + + + + +10 + +0 +0 + + + + +0 + +0 +0 + + + + +20 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +20 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +20 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +1 + +0 +0 + + + + +20 + +0 +0 + + + + +1 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +14 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +1 + +0 +0 + + + + +15 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + +<_x0030_ href="#ref-502"/> + + +<_x0030_ href="#ref-503"/> + + +<_x0030_ href="#ref-504"/> + + +<_x0030_ href="#ref-505"/> + + +<_x0030_ href="#ref-506"/> + + +<_x0030_ href="#ref-507"/> + + +<_x0030_ href="#ref-508"/> + + +<_x0030_ href="#ref-509"/> + + +<_x0030_ href="#ref-510"/> + + +<_x0030_ href="#ref-511"/> + + +<_x0030_ href="#ref-512"/> + + +<_x0030_ href="#ref-513"/> + + +<_x0030_ href="#ref-514"/> + + +<_x0030_ href="#ref-515"/> + + +<_x0030_ href="#ref-516"/> + + +<_x0030_ href="#ref-517"/> + + +<_x0030_ href="#ref-518"/> + + +<_x0030_ href="#ref-519"/> + + +<_x0030_ href="#ref-520"/> + + +<_x0030_ href="#ref-521"/> + + +<_x0030_ href="#ref-522"/> + + +<_x0030_ href="#ref-523"/> + + +<_x0030_ href="#ref-524"/> + + +<_x0030_ href="#ref-525"/> + + +<_x0030_ href="#ref-526"/> + + +<_x0030_ href="#ref-527"/> + + +<_x0030_ href="#ref-528"/> + + +<_x0030_ href="#ref-529"/> + + +<_x0030_ href="#ref-530"/> + + +<_x0030_ href="#ref-531"/> + + +<_x0030_ href="#ref-532"/> + + +<_x0030_ href="#ref-533"/> + + +<_x0030_ href="#ref-534"/> + + +<_x0030_ href="#ref-535"/> + + +<_x0030_ href="#ref-536"/> + + +<_x0030_ href="#ref-537"/> + + +<_x0030_ href="#ref-538"/> + + +<_x0030_ href="#ref-539"/> + + +<_x0030_ href="#ref-540"/> + + +<_x0030_ href="#ref-541"/> + + +<_x0030_ href="#ref-542"/> + + +<_x0030_ href="#ref-543"/> + + +<_x0030_ href="#ref-544"/> + + +<_x0030_ href="#ref-545"/> + + +<_x0030_ href="#ref-546"/> + + +<_x0030_ href="#ref-547"/> + + +<_x0030_ href="#ref-548"/> + + +<_x0030_ href="#ref-549"/> + + +<_x0030_ href="#ref-550"/> + + +<_x0030_ href="#ref-551"/> + + +<_x0030_ href="#ref-552"/> + + +<_x0030_ href="#ref-553"/> + + +<_x0030_ href="#ref-554"/> + + +<_x0030_ href="#ref-555"/> + + +<_x0030_ href="#ref-556"/> + + +<_x0030_ href="#ref-557"/> + + +<_x0030_ href="#ref-558"/> + + +<_x0030_ href="#ref-559"/> + + +<_x0030_ href="#ref-560"/> + + +<_x0030_ href="#ref-561"/> + + +<_x0030_ href="#ref-562"/> + + +<_x0030_ href="#ref-563"/> + + +<_x0030_ href="#ref-564"/> + + +<_x0030_ href="#ref-565"/> + + +<_x0030_ href="#ref-566"/> + + +<_x0030_ href="#ref-567"/> + + +<_x0030_ href="#ref-568"/> + + +<_x0030_ href="#ref-569"/> + + +<_x0030_ href="#ref-570"/> + + +<_x0030_ href="#ref-571"/> + + +<_x0030_ href="#ref-572"/> + + +<_x0030_ href="#ref-573"/> + + +<_x0030_ href="#ref-574"/> + + +<_x0030_ href="#ref-575"/> + + +<_x0030_ href="#ref-576"/> + + +<_x0030_ href="#ref-577"/> + + +<_x0030_ href="#ref-578"/> + + +<_x0030_ href="#ref-579"/> + + +<_x0030_ href="#ref-580"/> + + +<_x0030_ href="#ref-581"/> + + +<_x0030_ href="#ref-582"/> + + +<_x0030_ href="#ref-583"/> + + +<_x0030_ href="#ref-584"/> + + +<_x0030_ href="#ref-585"/> + + +<_x0030_ href="#ref-586"/> + + +<_x0030_ href="#ref-587"/> + + +<_x0030_ href="#ref-588"/> + + +<_x0030_ href="#ref-589"/> + + +<_x0030_ href="#ref-590"/> + + +<_x0030_ href="#ref-591"/> + + +<_x0030_ href="#ref-592"/> + + +<_x0030_ href="#ref-593"/> + + +<_x0030_ href="#ref-594"/> + + +<_x0030_ href="#ref-595"/> + + +<_x0030_ href="#ref-596"/> + + +<_x0030_ href="#ref-597"/> + + +<_x0030_ href="#ref-598"/> + + +<_x0030_ href="#ref-599"/> + + +<_x0030_ href="#ref-600"/> + + +<_x0030_ href="#ref-601"/> + + +<_x0030_ href="#ref-602"/> + + +<_x0030_ href="#ref-603"/> + + +<_x0030_ href="#ref-604"/> + + +<_x0030_ href="#ref-605"/> + + +<_x0030_ href="#ref-606"/> + + +<_x0030_ href="#ref-607"/> + + +<_x0030_ href="#ref-608"/> + + +<_x0030_ href="#ref-609"/> + + +<_x0030_ href="#ref-610"/> + + +<_x0030_ href="#ref-611"/> + + +<_x0030_ href="#ref-612"/> + + +<_x0030_ href="#ref-613"/> + + +<_x0030_ href="#ref-614"/> + + +<_x0030_ href="#ref-615"/> + + + +7 +6 + + + + + +4 +9 + + + + + +2 +10 + + + + + +1 +10 + + + + + +1 +10 + + + + + +0 +10 + + + + + +13 +15 + + + + + +13 +15 + + + + + +13 +15 + + + + + +7 +16 + + + + + +7 +16 + + + + + +7 +16 + + + + + +6 +16 + + + + + +6 +16 + + + + + +6 +16 + + + + + +8 +8 + + + + + +8 +8 + + + + + +8 +8 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +18 +9 + + + + + +18 +9 + + + + + +18 +9 + + + + + +21 +10 + + + + + +21 +10 + + + + + +21 +10 + + + + + +20 +15 + + + + + +20 +15 + + + + + +20 +15 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +7 +8 + + + + + +9 +8 + + + + + +11 +8 + + + + + +9 +8 + + + + + +9 +9 + + + + + +9 +9 + + + + + +9 +9 + + + + + +9 +9 + + + + + +9 +8 + + + + + +9 +8 + + + + + +9 +8 + + + + + +9 +8 + + + + + +8 +8 + + + + + +8 +8 + + + + + +8 +8 + + + + + +8 +8 + + + + + +6 +8 + + + + + +6 +8 + + + + + +6 +8 + + + + + +6 +8 + + + + + +6 +9 + + + + + +6 +9 + + + + + +6 +9 + + + + + +6 +9 + + + + + +6 +9 + + + + + +6 +9 + + + + + +6 +9 + + + + + +6 +9 + + + + + +6 +9 + + + + + +6 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +10 + + + + + +8 +9 + + + + + +9 +8 + + + + + +9 +8 + + + + + +9 +8 + + + + + +9 +8 + + + + + +9 +8 + + + + + +9 +8 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +9 +8 + + + + + +9 +8 + + + + + +9 +8 + + + + + +9 +8 + + + + + +9 +8 + + + + + +9 +8 + + + + + +8 +8 + + + + + +8 +8 + + + + + +8 +8 + + + + + +8 +8 + + + + + +8 +8 + + + + + +8 +8 + + + + + +6 +8 + + + + + +6 +8 + + + + + +6 +8 + + + + + +6 +8 + + + + + +6 +8 + + + + + +6 +8 + + + + + +7 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +6 +8 + + + + + +6 +8 + + + + + +6 +8 + + + + + +6 +8 + + + + + +6 +8 + + + + + +6 +8 + + + + + +1 +0 + + + + + +7 +9 + + + + + diff --git a/data/art824/andy/andy_die_3_0.png b/data/art824/andy/andy_die_3_0.png new file mode 100644 index 0000000..da66bbb Binary files /dev/null and b/data/art824/andy/andy_die_3_0.png differ diff --git a/data/art824/andy/andy_die_3_1.png b/data/art824/andy/andy_die_3_1.png new file mode 100644 index 0000000..384cfeb Binary files /dev/null and b/data/art824/andy/andy_die_3_1.png differ diff --git a/data/art824/andy/andy_die_3_2.png b/data/art824/andy/andy_die_3_2.png new file mode 100644 index 0000000..0755846 Binary files /dev/null and b/data/art824/andy/andy_die_3_2.png differ diff --git a/data/art824/andy/andy_die_3_3.png b/data/art824/andy/andy_die_3_3.png new file mode 100644 index 0000000..6d6b2ac Binary files /dev/null and b/data/art824/andy/andy_die_3_3.png differ diff --git a/data/art824/andy/andy_die_3_4.png b/data/art824/andy/andy_die_3_4.png new file mode 100644 index 0000000..5cef5be Binary files /dev/null and b/data/art824/andy/andy_die_3_4.png differ diff --git a/data/art824/andy/andy_die_3_5.png b/data/art824/andy/andy_die_3_5.png new file mode 100644 index 0000000..9885766 Binary files /dev/null and b/data/art824/andy/andy_die_3_5.png differ diff --git a/data/art824/andy/andy_fire_0_0.png b/data/art824/andy/andy_fire_0_0.png new file mode 100644 index 0000000..e78fad3 Binary files /dev/null and b/data/art824/andy/andy_fire_0_0.png differ diff --git a/data/art824/andy/andy_fire_0_1.png b/data/art824/andy/andy_fire_0_1.png new file mode 100644 index 0000000..46803b8 Binary files /dev/null and b/data/art824/andy/andy_fire_0_1.png differ diff --git a/data/art824/andy/andy_fire_0_2.png b/data/art824/andy/andy_fire_0_2.png new file mode 100644 index 0000000..9ed1b20 Binary files /dev/null and b/data/art824/andy/andy_fire_0_2.png differ diff --git a/data/art824/andy/andy_fire_1_0.png b/data/art824/andy/andy_fire_1_0.png new file mode 100644 index 0000000..5c1f687 Binary files /dev/null and b/data/art824/andy/andy_fire_1_0.png differ diff --git a/data/art824/andy/andy_fire_1_1.png b/data/art824/andy/andy_fire_1_1.png new file mode 100644 index 0000000..11db3ee Binary files /dev/null and b/data/art824/andy/andy_fire_1_1.png differ diff --git a/data/art824/andy/andy_fire_1_2.png b/data/art824/andy/andy_fire_1_2.png new file mode 100644 index 0000000..38e27b2 Binary files /dev/null and b/data/art824/andy/andy_fire_1_2.png differ diff --git a/data/art824/andy/andy_fire_2_0.png b/data/art824/andy/andy_fire_2_0.png new file mode 100644 index 0000000..5dc72ea Binary files /dev/null and b/data/art824/andy/andy_fire_2_0.png differ diff --git a/data/art824/andy/andy_fire_2_1.png b/data/art824/andy/andy_fire_2_1.png new file mode 100644 index 0000000..76a334e Binary files /dev/null and b/data/art824/andy/andy_fire_2_1.png differ diff --git a/data/art824/andy/andy_fire_2_2.png b/data/art824/andy/andy_fire_2_2.png new file mode 100644 index 0000000..5bd11f0 Binary files /dev/null and b/data/art824/andy/andy_fire_2_2.png differ diff --git a/data/art824/andy/andy_fire_3_0.png b/data/art824/andy/andy_fire_3_0.png new file mode 100644 index 0000000..37b741a Binary files /dev/null and b/data/art824/andy/andy_fire_3_0.png differ diff --git a/data/art824/andy/andy_fire_3_1.png b/data/art824/andy/andy_fire_3_1.png new file mode 100644 index 0000000..a18b3c6 Binary files /dev/null and b/data/art824/andy/andy_fire_3_1.png differ diff --git a/data/art824/andy/andy_fire_3_2.png b/data/art824/andy/andy_fire_3_2.png new file mode 100644 index 0000000..89615c6 Binary files /dev/null and b/data/art824/andy/andy_fire_3_2.png differ diff --git a/data/art824/andy/andy_fire_4_0.png b/data/art824/andy/andy_fire_4_0.png new file mode 100644 index 0000000..0268708 Binary files /dev/null and b/data/art824/andy/andy_fire_4_0.png differ diff --git a/data/art824/andy/andy_fire_4_1.png b/data/art824/andy/andy_fire_4_1.png new file mode 100644 index 0000000..13588e2 Binary files /dev/null and b/data/art824/andy/andy_fire_4_1.png differ diff --git a/data/art824/andy/andy_fire_4_2.png b/data/art824/andy/andy_fire_4_2.png new file mode 100644 index 0000000..6d8997b Binary files /dev/null and b/data/art824/andy/andy_fire_4_2.png differ diff --git a/data/art824/andy/andy_fire_5_0.png b/data/art824/andy/andy_fire_5_0.png new file mode 100644 index 0000000..d0c5a5a Binary files /dev/null and b/data/art824/andy/andy_fire_5_0.png differ diff --git a/data/art824/andy/andy_fire_5_1.png b/data/art824/andy/andy_fire_5_1.png new file mode 100644 index 0000000..1a55d1c Binary files /dev/null and b/data/art824/andy/andy_fire_5_1.png differ diff --git a/data/art824/andy/andy_fire_5_2.png b/data/art824/andy/andy_fire_5_2.png new file mode 100644 index 0000000..2f830ae Binary files /dev/null and b/data/art824/andy/andy_fire_5_2.png differ diff --git a/data/art824/andy/andy_fire_6_0.png b/data/art824/andy/andy_fire_6_0.png new file mode 100644 index 0000000..f68e242 Binary files /dev/null and b/data/art824/andy/andy_fire_6_0.png differ diff --git a/data/art824/andy/andy_fire_6_1.png b/data/art824/andy/andy_fire_6_1.png new file mode 100644 index 0000000..c44d897 Binary files /dev/null and b/data/art824/andy/andy_fire_6_1.png differ diff --git a/data/art824/andy/andy_fire_6_2.png b/data/art824/andy/andy_fire_6_2.png new file mode 100644 index 0000000..4290cb6 Binary files /dev/null and b/data/art824/andy/andy_fire_6_2.png differ diff --git a/data/art824/andy/andy_fire_7_0.png b/data/art824/andy/andy_fire_7_0.png new file mode 100644 index 0000000..e84042a Binary files /dev/null and b/data/art824/andy/andy_fire_7_0.png differ diff --git a/data/art824/andy/andy_fire_7_1.png b/data/art824/andy/andy_fire_7_1.png new file mode 100644 index 0000000..314d2f2 Binary files /dev/null and b/data/art824/andy/andy_fire_7_1.png differ diff --git a/data/art824/andy/andy_fire_7_2.png b/data/art824/andy/andy_fire_7_2.png new file mode 100644 index 0000000..eff57f2 Binary files /dev/null and b/data/art824/andy/andy_fire_7_2.png differ diff --git a/data/art824/andy/andy_idle_0_0.png b/data/art824/andy/andy_idle_0_0.png new file mode 100644 index 0000000..da8c6d5 Binary files /dev/null and b/data/art824/andy/andy_idle_0_0.png differ diff --git a/data/art824/andy/andy_idle_0_1.png b/data/art824/andy/andy_idle_0_1.png new file mode 100644 index 0000000..7ce4584 Binary files /dev/null and b/data/art824/andy/andy_idle_0_1.png differ diff --git a/data/art824/andy/andy_idle_0_2.png b/data/art824/andy/andy_idle_0_2.png new file mode 100644 index 0000000..2e135b1 Binary files /dev/null and b/data/art824/andy/andy_idle_0_2.png differ diff --git a/data/art824/andy/andy_idle_0_3.png b/data/art824/andy/andy_idle_0_3.png new file mode 100644 index 0000000..ecf2775 Binary files /dev/null and b/data/art824/andy/andy_idle_0_3.png differ diff --git a/data/art824/andy/andy_idle_1_0.png b/data/art824/andy/andy_idle_1_0.png new file mode 100644 index 0000000..01f8fa6 Binary files /dev/null and b/data/art824/andy/andy_idle_1_0.png differ diff --git a/data/art824/andy/andy_idle_1_1.png b/data/art824/andy/andy_idle_1_1.png new file mode 100644 index 0000000..5241a83 Binary files /dev/null and b/data/art824/andy/andy_idle_1_1.png differ diff --git a/data/art824/andy/andy_idle_1_2.png b/data/art824/andy/andy_idle_1_2.png new file mode 100644 index 0000000..0323d36 Binary files /dev/null and b/data/art824/andy/andy_idle_1_2.png differ diff --git a/data/art824/andy/andy_idle_2_0.png b/data/art824/andy/andy_idle_2_0.png new file mode 100644 index 0000000..0690d72 Binary files /dev/null and b/data/art824/andy/andy_idle_2_0.png differ diff --git a/data/art824/andy/andy_idle_2_1.png b/data/art824/andy/andy_idle_2_1.png new file mode 100644 index 0000000..3a2818f Binary files /dev/null and b/data/art824/andy/andy_idle_2_1.png differ diff --git a/data/art824/andy/andy_idle_2_2.png b/data/art824/andy/andy_idle_2_2.png new file mode 100644 index 0000000..39524c5 Binary files /dev/null and b/data/art824/andy/andy_idle_2_2.png differ diff --git a/data/art824/andy/andy_idle_3_0.png b/data/art824/andy/andy_idle_3_0.png new file mode 100644 index 0000000..cd8e320 Binary files /dev/null and b/data/art824/andy/andy_idle_3_0.png differ diff --git a/data/art824/andy/andy_idle_3_1.png b/data/art824/andy/andy_idle_3_1.png new file mode 100644 index 0000000..e93161c Binary files /dev/null and b/data/art824/andy/andy_idle_3_1.png differ diff --git a/data/art824/andy/andy_idle_3_2.png b/data/art824/andy/andy_idle_3_2.png new file mode 100644 index 0000000..95485eb Binary files /dev/null and b/data/art824/andy/andy_idle_3_2.png differ diff --git a/data/art824/andy/andy_idle_4_0.png b/data/art824/andy/andy_idle_4_0.png new file mode 100644 index 0000000..0f41152 Binary files /dev/null and b/data/art824/andy/andy_idle_4_0.png differ diff --git a/data/art824/andy/andy_idle_4_1.png b/data/art824/andy/andy_idle_4_1.png new file mode 100644 index 0000000..9e14e9b Binary files /dev/null and b/data/art824/andy/andy_idle_4_1.png differ diff --git a/data/art824/andy/andy_idle_4_2.png b/data/art824/andy/andy_idle_4_2.png new file mode 100644 index 0000000..86eb8a7 Binary files /dev/null and b/data/art824/andy/andy_idle_4_2.png differ diff --git a/data/art824/andy/andy_idle_5_0.png b/data/art824/andy/andy_idle_5_0.png new file mode 100644 index 0000000..bd350e7 Binary files /dev/null and b/data/art824/andy/andy_idle_5_0.png differ diff --git a/data/art824/andy/andy_idle_5_1.png b/data/art824/andy/andy_idle_5_1.png new file mode 100644 index 0000000..7e5d3c1 Binary files /dev/null and b/data/art824/andy/andy_idle_5_1.png differ diff --git a/data/art824/andy/andy_idle_5_2.png b/data/art824/andy/andy_idle_5_2.png new file mode 100644 index 0000000..9192e08 Binary files /dev/null and b/data/art824/andy/andy_idle_5_2.png differ diff --git a/data/art824/andy/andy_idle_6_0.png b/data/art824/andy/andy_idle_6_0.png new file mode 100644 index 0000000..519472a Binary files /dev/null and b/data/art824/andy/andy_idle_6_0.png differ diff --git a/data/art824/andy/andy_idle_6_1.png b/data/art824/andy/andy_idle_6_1.png new file mode 100644 index 0000000..87d7d2e Binary files /dev/null and b/data/art824/andy/andy_idle_6_1.png differ diff --git a/data/art824/andy/andy_idle_6_2.png b/data/art824/andy/andy_idle_6_2.png new file mode 100644 index 0000000..74760ab Binary files /dev/null and b/data/art824/andy/andy_idle_6_2.png differ diff --git a/data/art824/andy/andy_idle_6_3.png b/data/art824/andy/andy_idle_6_3.png new file mode 100644 index 0000000..027955d Binary files /dev/null and b/data/art824/andy/andy_idle_6_3.png differ diff --git a/data/art824/andy/andy_idle_7_0.png b/data/art824/andy/andy_idle_7_0.png new file mode 100644 index 0000000..202bb5b Binary files /dev/null and b/data/art824/andy/andy_idle_7_0.png differ diff --git a/data/art824/andy/andy_idle_7_1.png b/data/art824/andy/andy_idle_7_1.png new file mode 100644 index 0000000..74bc974 Binary files /dev/null and b/data/art824/andy/andy_idle_7_1.png differ diff --git a/data/art824/andy/andy_idle_7_2.png b/data/art824/andy/andy_idle_7_2.png new file mode 100644 index 0000000..cf712be Binary files /dev/null and b/data/art824/andy/andy_idle_7_2.png differ diff --git a/data/art824/andy/andy_idle_7_3.png b/data/art824/andy/andy_idle_7_3.png new file mode 100644 index 0000000..1b040ed Binary files /dev/null and b/data/art824/andy/andy_idle_7_3.png differ diff --git a/data/art824/andy/andy_idle_7_4.png b/data/art824/andy/andy_idle_7_4.png new file mode 100644 index 0000000..b734291 Binary files /dev/null and b/data/art824/andy/andy_idle_7_4.png differ diff --git a/data/art824/andy/andy_idle_7_5.png b/data/art824/andy/andy_idle_7_5.png new file mode 100644 index 0000000..fefb856 Binary files /dev/null and b/data/art824/andy/andy_idle_7_5.png differ diff --git a/data/art824/andy/andy_jog_0_0.png b/data/art824/andy/andy_jog_0_0.png new file mode 100644 index 0000000..8410350 Binary files /dev/null and b/data/art824/andy/andy_jog_0_0.png differ diff --git a/data/art824/andy/andy_jog_0_1.png b/data/art824/andy/andy_jog_0_1.png new file mode 100644 index 0000000..4379116 Binary files /dev/null and b/data/art824/andy/andy_jog_0_1.png differ diff --git a/data/art824/andy/andy_jog_0_2.png b/data/art824/andy/andy_jog_0_2.png new file mode 100644 index 0000000..b0a0a2f Binary files /dev/null and b/data/art824/andy/andy_jog_0_2.png differ diff --git a/data/art824/andy/andy_jog_0_3.png b/data/art824/andy/andy_jog_0_3.png new file mode 100644 index 0000000..b9cb27c Binary files /dev/null and b/data/art824/andy/andy_jog_0_3.png differ diff --git a/data/art824/andy/andy_jog_0_4.png b/data/art824/andy/andy_jog_0_4.png new file mode 100644 index 0000000..a8fa1e8 Binary files /dev/null and b/data/art824/andy/andy_jog_0_4.png differ diff --git a/data/art824/andy/andy_jog_0_5.png b/data/art824/andy/andy_jog_0_5.png new file mode 100644 index 0000000..9f9f74f Binary files /dev/null and b/data/art824/andy/andy_jog_0_5.png differ diff --git a/data/art824/andy/andy_jog_1_0.png b/data/art824/andy/andy_jog_1_0.png new file mode 100644 index 0000000..85aaa79 Binary files /dev/null and b/data/art824/andy/andy_jog_1_0.png differ diff --git a/data/art824/andy/andy_jog_1_1.png b/data/art824/andy/andy_jog_1_1.png new file mode 100644 index 0000000..376619e Binary files /dev/null and b/data/art824/andy/andy_jog_1_1.png differ diff --git a/data/art824/andy/andy_jog_1_2.png b/data/art824/andy/andy_jog_1_2.png new file mode 100644 index 0000000..0233ccb Binary files /dev/null and b/data/art824/andy/andy_jog_1_2.png differ diff --git a/data/art824/andy/andy_jog_1_3.png b/data/art824/andy/andy_jog_1_3.png new file mode 100644 index 0000000..5048183 Binary files /dev/null and b/data/art824/andy/andy_jog_1_3.png differ diff --git a/data/art824/andy/andy_jog_1_4.png b/data/art824/andy/andy_jog_1_4.png new file mode 100644 index 0000000..1830f43 Binary files /dev/null and b/data/art824/andy/andy_jog_1_4.png differ diff --git a/data/art824/andy/andy_jog_1_5.png b/data/art824/andy/andy_jog_1_5.png new file mode 100644 index 0000000..edc743f Binary files /dev/null and b/data/art824/andy/andy_jog_1_5.png differ diff --git a/data/art824/andy/andy_jog_2_0.png b/data/art824/andy/andy_jog_2_0.png new file mode 100644 index 0000000..15bb79c Binary files /dev/null and b/data/art824/andy/andy_jog_2_0.png differ diff --git a/data/art824/andy/andy_jog_2_1.png b/data/art824/andy/andy_jog_2_1.png new file mode 100644 index 0000000..b1919e9 Binary files /dev/null and b/data/art824/andy/andy_jog_2_1.png differ diff --git a/data/art824/andy/andy_jog_2_2.png b/data/art824/andy/andy_jog_2_2.png new file mode 100644 index 0000000..8199a11 Binary files /dev/null and b/data/art824/andy/andy_jog_2_2.png differ diff --git a/data/art824/andy/andy_jog_2_3.png b/data/art824/andy/andy_jog_2_3.png new file mode 100644 index 0000000..fc728ef Binary files /dev/null and b/data/art824/andy/andy_jog_2_3.png differ diff --git a/data/art824/andy/andy_jog_2_4.png b/data/art824/andy/andy_jog_2_4.png new file mode 100644 index 0000000..c32fbc7 Binary files /dev/null and b/data/art824/andy/andy_jog_2_4.png differ diff --git a/data/art824/andy/andy_jog_2_5.png b/data/art824/andy/andy_jog_2_5.png new file mode 100644 index 0000000..cbf266e Binary files /dev/null and b/data/art824/andy/andy_jog_2_5.png differ diff --git a/data/art824/andy/andy_jog_3_0.png b/data/art824/andy/andy_jog_3_0.png new file mode 100644 index 0000000..aef4990 Binary files /dev/null and b/data/art824/andy/andy_jog_3_0.png differ diff --git a/data/art824/andy/andy_jog_3_1.png b/data/art824/andy/andy_jog_3_1.png new file mode 100644 index 0000000..790c98e Binary files /dev/null and b/data/art824/andy/andy_jog_3_1.png differ diff --git a/data/art824/andy/andy_jog_3_2.png b/data/art824/andy/andy_jog_3_2.png new file mode 100644 index 0000000..f2c0e9c Binary files /dev/null and b/data/art824/andy/andy_jog_3_2.png differ diff --git a/data/art824/andy/andy_jog_3_3.png b/data/art824/andy/andy_jog_3_3.png new file mode 100644 index 0000000..70ebe48 Binary files /dev/null and b/data/art824/andy/andy_jog_3_3.png differ diff --git a/data/art824/andy/andy_jog_3_4.png b/data/art824/andy/andy_jog_3_4.png new file mode 100644 index 0000000..a3f8acb Binary files /dev/null and b/data/art824/andy/andy_jog_3_4.png differ diff --git a/data/art824/andy/andy_jog_3_5.png b/data/art824/andy/andy_jog_3_5.png new file mode 100644 index 0000000..2aa08f9 Binary files /dev/null and b/data/art824/andy/andy_jog_3_5.png differ diff --git a/data/art824/andy/andy_jog_4_0.png b/data/art824/andy/andy_jog_4_0.png new file mode 100644 index 0000000..f8d276b Binary files /dev/null and b/data/art824/andy/andy_jog_4_0.png differ diff --git a/data/art824/andy/andy_jog_4_1.png b/data/art824/andy/andy_jog_4_1.png new file mode 100644 index 0000000..2028f41 Binary files /dev/null and b/data/art824/andy/andy_jog_4_1.png differ diff --git a/data/art824/andy/andy_jog_4_2.png b/data/art824/andy/andy_jog_4_2.png new file mode 100644 index 0000000..d4e81e2 Binary files /dev/null and b/data/art824/andy/andy_jog_4_2.png differ diff --git a/data/art824/andy/andy_jog_4_3.png b/data/art824/andy/andy_jog_4_3.png new file mode 100644 index 0000000..e4ac704 Binary files /dev/null and b/data/art824/andy/andy_jog_4_3.png differ diff --git a/data/art824/andy/andy_jog_4_4.png b/data/art824/andy/andy_jog_4_4.png new file mode 100644 index 0000000..7597c1f Binary files /dev/null and b/data/art824/andy/andy_jog_4_4.png differ diff --git a/data/art824/andy/andy_jog_4_5.png b/data/art824/andy/andy_jog_4_5.png new file mode 100644 index 0000000..d65ebad Binary files /dev/null and b/data/art824/andy/andy_jog_4_5.png differ diff --git a/data/art824/andy/andy_jog_5_0.png b/data/art824/andy/andy_jog_5_0.png new file mode 100644 index 0000000..6bf75ac Binary files /dev/null and b/data/art824/andy/andy_jog_5_0.png differ diff --git a/data/art824/andy/andy_jog_5_1.png b/data/art824/andy/andy_jog_5_1.png new file mode 100644 index 0000000..a6a09ad Binary files /dev/null and b/data/art824/andy/andy_jog_5_1.png differ diff --git a/data/art824/andy/andy_jog_5_2.png b/data/art824/andy/andy_jog_5_2.png new file mode 100644 index 0000000..40d4ab1 Binary files /dev/null and b/data/art824/andy/andy_jog_5_2.png differ diff --git a/data/art824/andy/andy_jog_5_3.png b/data/art824/andy/andy_jog_5_3.png new file mode 100644 index 0000000..ad35310 Binary files /dev/null and b/data/art824/andy/andy_jog_5_3.png differ diff --git a/data/art824/andy/andy_jog_5_4.png b/data/art824/andy/andy_jog_5_4.png new file mode 100644 index 0000000..530d467 Binary files /dev/null and b/data/art824/andy/andy_jog_5_4.png differ diff --git a/data/art824/andy/andy_jog_5_5.png b/data/art824/andy/andy_jog_5_5.png new file mode 100644 index 0000000..dcffaa0 Binary files /dev/null and b/data/art824/andy/andy_jog_5_5.png differ diff --git a/data/art824/andy/andy_jog_6_0.png b/data/art824/andy/andy_jog_6_0.png new file mode 100644 index 0000000..62fd65b Binary files /dev/null and b/data/art824/andy/andy_jog_6_0.png differ diff --git a/data/art824/andy/andy_jog_6_1.png b/data/art824/andy/andy_jog_6_1.png new file mode 100644 index 0000000..5c98426 Binary files /dev/null and b/data/art824/andy/andy_jog_6_1.png differ diff --git a/data/art824/andy/andy_jog_6_2.png b/data/art824/andy/andy_jog_6_2.png new file mode 100644 index 0000000..32888b1 Binary files /dev/null and b/data/art824/andy/andy_jog_6_2.png differ diff --git a/data/art824/andy/andy_jog_6_3.png b/data/art824/andy/andy_jog_6_3.png new file mode 100644 index 0000000..b095ea9 Binary files /dev/null and b/data/art824/andy/andy_jog_6_3.png differ diff --git a/data/art824/andy/andy_jog_6_4.png b/data/art824/andy/andy_jog_6_4.png new file mode 100644 index 0000000..a4c98e7 Binary files /dev/null and b/data/art824/andy/andy_jog_6_4.png differ diff --git a/data/art824/andy/andy_jog_6_5.png b/data/art824/andy/andy_jog_6_5.png new file mode 100644 index 0000000..4308f13 Binary files /dev/null and b/data/art824/andy/andy_jog_6_5.png differ diff --git a/data/art824/andy/andy_jog_7_0.png b/data/art824/andy/andy_jog_7_0.png new file mode 100644 index 0000000..7f2ecb4 Binary files /dev/null and b/data/art824/andy/andy_jog_7_0.png differ diff --git a/data/art824/andy/andy_jog_7_1.png b/data/art824/andy/andy_jog_7_1.png new file mode 100644 index 0000000..3301f28 Binary files /dev/null and b/data/art824/andy/andy_jog_7_1.png differ diff --git a/data/art824/andy/andy_jog_7_2.png b/data/art824/andy/andy_jog_7_2.png new file mode 100644 index 0000000..7da97fc Binary files /dev/null and b/data/art824/andy/andy_jog_7_2.png differ diff --git a/data/art824/andy/andy_jog_7_3.png b/data/art824/andy/andy_jog_7_3.png new file mode 100644 index 0000000..57cd266 Binary files /dev/null and b/data/art824/andy/andy_jog_7_3.png differ diff --git a/data/art824/andy/andy_jog_7_4.png b/data/art824/andy/andy_jog_7_4.png new file mode 100644 index 0000000..c275cc6 Binary files /dev/null and b/data/art824/andy/andy_jog_7_4.png differ diff --git a/data/art824/andy/andy_jog_7_5.png b/data/art824/andy/andy_jog_7_5.png new file mode 100644 index 0000000..647e455 Binary files /dev/null and b/data/art824/andy/andy_jog_7_5.png differ diff --git a/data/art824/andyshot.amx b/data/art824/andyshot.amx new file mode 100644 index 0000000..8f60303 --- /dev/null +++ b/data/art824/andyshot.amx @@ -0,0 +1,302 @@ + + + + + +80 +true + + +<_x0030_ href="#ref-5"/> +<_x0031_ href="#ref-6"/> +<_x0032_ href="#ref-7"/> +<_x0033_ href="#ref-8"/> +<_x0034_ href="#ref-9"/> +<_x0035_ href="#ref-10"/> +<_x0036_ href="#ref-11"/> +<_x0037_ href="#ref-12"/> +<_x0038_ href="#ref-13"/> +<_x0039_ href="#ref-14"/> +<_x0031_0 href="#ref-15"/> +<_x0031_1 href="#ref-16"/> + + +<_x0030_ href="#ref-17"/> +<_x0031_ href="#ref-18"/> + + +andyshot\shot_a_0_0.png + + +andyshot\shot_a_0_1.png + + +andyshot\shot_a_0_2.png + + +andyshot\shot_a_0_3.png + + +andyshot\shot_a_0_4.png + + +andyshot\shot_a_0_5.png + + +andyshot\shot_a_0_6.png + + +andyshot\shot_a_0_7.png + + +andyshot\shot_a_1_0.png + + +andyshot\shot_a_1_1.png + + +andyshot\shot_a_1_2.png + + +andyshot\shot_a_1_3.png + + +a 0 +0 +8 +<_x0030_ href="#ref-32"/> +<_x0031_ href="#ref-33"/> +<_x0032_ href="#ref-34"/> +<_x0033_ href="#ref-35"/> +<_x0034_ href="#ref-36"/> +<_x0035_ href="#ref-37"/> +<_x0036_ href="#ref-38"/> +<_x0037_ href="#ref-39"/> + + +a 1 +0 +4 +<_x0030_ href="#ref-41"/> +<_x0031_ href="#ref-42"/> +<_x0032_ href="#ref-43"/> +<_x0033_ href="#ref-44"/> + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + +<_x0030_ href="#ref-58"/> + + +<_x0030_ href="#ref-59"/> + + +<_x0030_ href="#ref-60"/> + + +<_x0030_ href="#ref-61"/> + + +<_x0030_ href="#ref-62"/> + + +<_x0030_ href="#ref-63"/> + + +<_x0030_ href="#ref-64"/> + + +<_x0030_ href="#ref-65"/> + + +<_x0030_ href="#ref-66"/> + + +<_x0030_ href="#ref-67"/> + + +<_x0030_ href="#ref-68"/> + + +<_x0030_ href="#ref-69"/> + + + +12 +5 + + + + + +13 +6 + + + + + +13 +7 + + + + + +12 +8 + + + + + +12 +9 + + + + + +11 +8 + + + + + +9 +6 + + + + + +10 +6 + + + + + +3 +3 + + + + + +4 +4 + + + + + +6 +6 + + + + + +6 +6 + + + + + diff --git a/data/art824/andyshot/shot_a_0_0.png b/data/art824/andyshot/shot_a_0_0.png new file mode 100644 index 0000000..6d2dc3d Binary files /dev/null and b/data/art824/andyshot/shot_a_0_0.png differ diff --git a/data/art824/andyshot/shot_a_0_1.png b/data/art824/andyshot/shot_a_0_1.png new file mode 100644 index 0000000..464a1dc Binary files /dev/null and b/data/art824/andyshot/shot_a_0_1.png differ diff --git a/data/art824/andyshot/shot_a_0_2.png b/data/art824/andyshot/shot_a_0_2.png new file mode 100644 index 0000000..25a4bb2 Binary files /dev/null and b/data/art824/andyshot/shot_a_0_2.png differ diff --git a/data/art824/andyshot/shot_a_0_3.png b/data/art824/andyshot/shot_a_0_3.png new file mode 100644 index 0000000..2c8d0f4 Binary files /dev/null and b/data/art824/andyshot/shot_a_0_3.png differ diff --git a/data/art824/andyshot/shot_a_0_4.png b/data/art824/andyshot/shot_a_0_4.png new file mode 100644 index 0000000..487c553 Binary files /dev/null and b/data/art824/andyshot/shot_a_0_4.png differ diff --git a/data/art824/andyshot/shot_a_0_5.png b/data/art824/andyshot/shot_a_0_5.png new file mode 100644 index 0000000..2251659 Binary files /dev/null and b/data/art824/andyshot/shot_a_0_5.png differ diff --git a/data/art824/andyshot/shot_a_0_6.png b/data/art824/andyshot/shot_a_0_6.png new file mode 100644 index 0000000..e778d20 Binary files /dev/null and b/data/art824/andyshot/shot_a_0_6.png differ diff --git a/data/art824/andyshot/shot_a_0_7.png b/data/art824/andyshot/shot_a_0_7.png new file mode 100644 index 0000000..a3a0da3 Binary files /dev/null and b/data/art824/andyshot/shot_a_0_7.png differ diff --git a/data/art824/andyshot/shot_a_1_0.png b/data/art824/andyshot/shot_a_1_0.png new file mode 100644 index 0000000..143e7db Binary files /dev/null and b/data/art824/andyshot/shot_a_1_0.png differ diff --git a/data/art824/andyshot/shot_a_1_1.png b/data/art824/andyshot/shot_a_1_1.png new file mode 100644 index 0000000..fcff62d Binary files /dev/null and b/data/art824/andyshot/shot_a_1_1.png differ diff --git a/data/art824/andyshot/shot_a_1_2.png b/data/art824/andyshot/shot_a_1_2.png new file mode 100644 index 0000000..750a345 Binary files /dev/null and b/data/art824/andyshot/shot_a_1_2.png differ diff --git a/data/art824/andyshot/shot_a_1_3.png b/data/art824/andyshot/shot_a_1_3.png new file mode 100644 index 0000000..559f126 Binary files /dev/null and b/data/art824/andyshot/shot_a_1_3.png differ diff --git a/data/art824/artillery.amx b/data/art824/artillery.amx new file mode 100644 index 0000000..0ab9363 --- /dev/null +++ b/data/art824/artillery.amx @@ -0,0 +1,864 @@ + + + + + +80 +true + + +<_x0030_ href="#ref-5"/> +<_x0031_ href="#ref-6"/> +<_x0032_ href="#ref-7"/> +<_x0033_ href="#ref-8"/> +<_x0034_ href="#ref-9"/> +<_x0035_ href="#ref-10"/> +<_x0036_ href="#ref-11"/> +<_x0037_ href="#ref-12"/> +<_x0038_ href="#ref-13"/> +<_x0039_ href="#ref-14"/> +<_x0031_0 href="#ref-15"/> +<_x0031_1 href="#ref-16"/> +<_x0031_2 href="#ref-17"/> +<_x0031_3 href="#ref-18"/> +<_x0031_4 href="#ref-19"/> +<_x0031_5 href="#ref-20"/> +<_x0031_6 href="#ref-21"/> +<_x0031_7 href="#ref-22"/> +<_x0031_8 href="#ref-23"/> +<_x0031_9 href="#ref-24"/> +<_x0032_0 href="#ref-25"/> +<_x0032_1 href="#ref-26"/> +<_x0032_2 href="#ref-27"/> +<_x0032_3 href="#ref-28"/> + + +<_x0030_ href="#ref-29"/> +<_x0031_ href="#ref-30"/> +<_x0032_ href="#ref-31"/> +<_x0033_ href="#ref-32"/> +<_x0034_ href="#ref-33"/> +<_x0035_ href="#ref-34"/> +<_x0036_ href="#ref-35"/> +<_x0037_ href="#ref-36"/> +<_x0038_ href="#ref-37"/> +<_x0039_ href="#ref-38"/> +<_x0031_0 href="#ref-39"/> +<_x0031_1 href="#ref-40"/> +<_x0031_2 href="#ref-41"/> +<_x0031_3 href="#ref-42"/> +<_x0031_4 href="#ref-43"/> +<_x0031_5 href="#ref-44"/> +<_x0031_6 href="#ref-45"/> +<_x0031_7 href="#ref-46"/> + + +artillery\art_7_0.png + + +artillery\art_0_0.png + + +artillery\art_1_0.png + + +artillery\art_2_0.png + + +artillery\art_3_0.png + + +artillery\art_4_0.png + + +artillery\art_5_0.png + + +artillery\art_6_0.png + + +artillery\art_7_2.png + + +artillery\art_0_1.png + + +artillery\art_0_2.png + + +artillery\art_1_1.png + + +artillery\art_1_2.png + + +artillery\art_2_1.png + + +artillery\art_2_2.png + + +artillery\art_3_1.png + + +artillery\art_3_2.png + + +artillery\art_4_1.png + + +artillery\art_4_2.png + + +artillery\art_5_1.png + + +artillery\art_5_2.png + + +artillery\art_6_1.png + + +artillery\art_7_1.png + + +artillery\art_6_2.png + + +fire 0 +0 +3 +<_x0030_ href="#ref-72"/> +<_x0031_ href="#ref-73"/> +<_x0032_ href="#ref-74"/> + + +fire 1 +0 +3 +<_x0030_ href="#ref-76"/> +<_x0031_ href="#ref-77"/> +<_x0032_ href="#ref-78"/> + + +fire 2 +0 +3 +<_x0030_ href="#ref-80"/> +<_x0031_ href="#ref-81"/> +<_x0032_ href="#ref-82"/> + + +fire 3 +0 +3 +<_x0030_ href="#ref-84"/> +<_x0031_ href="#ref-85"/> +<_x0032_ href="#ref-86"/> + + +fire 4 +0 +3 +<_x0030_ href="#ref-88"/> +<_x0031_ href="#ref-89"/> +<_x0032_ href="#ref-90"/> + + +fire 5 +0 +3 +<_x0030_ href="#ref-92"/> +<_x0031_ href="#ref-93"/> +<_x0032_ href="#ref-94"/> + + +fire 6 +0 +3 +<_x0030_ href="#ref-96"/> +<_x0031_ href="#ref-97"/> +<_x0032_ href="#ref-98"/> + + +fire 7 +0 +3 +<_x0030_ href="#ref-100"/> +<_x0031_ href="#ref-101"/> +<_x0032_ href="#ref-102"/> + + +move 0 +0 +1 +<_x0030_ href="#ref-104"/> + + +move 1 +0 +1 +<_x0030_ href="#ref-106"/> + + +move 2 +0 +1 +<_x0030_ href="#ref-108"/> + + +move 3 +0 +1 +<_x0030_ href="#ref-110"/> + + +move 4 +0 +1 +<_x0030_ href="#ref-112"/> + + +move 5 +0 +1 +<_x0030_ href="#ref-114"/> + + +move 6 +0 +1 +<_x0030_ href="#ref-116"/> + + +move 7 +0 +1 +<_x0030_ href="#ref-118"/> + + +icon +0 +1 +<_x0030_ href="#ref-120"/> + + +help +0 +1 +<_x0030_ href="#ref-122"/> + + + +0 + +0 +0 + + + + +0 + +0 +-29 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +13 +-21 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +29 +-2 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +23 +22 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +27 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-24 +18 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-27 +-4 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-21 +-21 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + +<_x0030_ href="#ref-158"/> + + +<_x0030_ href="#ref-159"/> + + +<_x0030_ href="#ref-160"/> + + +<_x0030_ href="#ref-161"/> + + +<_x0030_ href="#ref-162"/> + + +<_x0030_ href="#ref-163"/> + + +<_x0030_ href="#ref-164"/> + + +<_x0030_ href="#ref-165"/> + + +<_x0030_ href="#ref-166"/> + + +<_x0030_ href="#ref-167"/> + + +<_x0030_ href="#ref-168"/> + + +<_x0030_ href="#ref-169"/> + + +<_x0030_ href="#ref-170"/> + + +<_x0030_ href="#ref-171"/> + + +<_x0030_ href="#ref-172"/> + + +<_x0030_ href="#ref-173"/> + + +<_x0030_ href="#ref-174"/> + + +<_x0030_ href="#ref-175"/> + + +<_x0030_ href="#ref-176"/> + + +<_x0030_ href="#ref-177"/> + + +<_x0030_ href="#ref-178"/> + + +<_x0030_ href="#ref-179"/> + + +<_x0030_ href="#ref-180"/> + + +<_x0030_ href="#ref-181"/> + + +<_x0030_ href="#ref-182"/> + + +<_x0030_ href="#ref-183"/> + + +<_x0030_ href="#ref-184"/> + + +<_x0030_ href="#ref-185"/> + + +<_x0030_ href="#ref-186"/> + + +<_x0030_ href="#ref-187"/> + + +<_x0030_ href="#ref-188"/> + + +<_x0030_ href="#ref-189"/> + + +<_x0030_ href="#ref-190"/> + + +<_x0030_ href="#ref-191"/> + + + +17 +18 + + + + + +25 +30 + + + + + +25 +30 + + + + + +19 +19 + + + + + +19 +34 + + + + + +19 +34 + + + + + +16 +18 + + + + + +15 +24 + + + + + +15 +24 + + + + + +15 +16 + + + + + +11 +13 + + + + + +11 +13 + + + + + +17 +16 + + + + + +24 +16 + + + + + +24 +16 + + + + + +21 +14 + + + + + +35 +14 + + + + + +35 +14 + + + + + +19 +19 + + + + + +31 +19 + + + + + +31 +19 + + + + + +20 +17 + + + + + +34 +32 + + + + + +34 +32 + + + + + +17 +18 + + + + + +19 +19 + + + + + +16 +18 + + + + + +15 +16 + + + + + +17 +16 + + + + + +21 +14 + + + + + +19 +19 + + + + + +20 +17 + + + + + +3 +8 + + + + + +19 +19 + + + + + diff --git a/data/art824/artillery/art_0_0.png b/data/art824/artillery/art_0_0.png new file mode 100644 index 0000000..4519632 Binary files /dev/null and b/data/art824/artillery/art_0_0.png differ diff --git a/data/art824/artillery/art_0_1.png b/data/art824/artillery/art_0_1.png new file mode 100644 index 0000000..0e8f776 Binary files /dev/null and b/data/art824/artillery/art_0_1.png differ diff --git a/data/art824/artillery/art_0_2.png b/data/art824/artillery/art_0_2.png new file mode 100644 index 0000000..e3c03e6 Binary files /dev/null and b/data/art824/artillery/art_0_2.png differ diff --git a/data/art824/artillery/art_1_0.png b/data/art824/artillery/art_1_0.png new file mode 100644 index 0000000..1b68bbb Binary files /dev/null and b/data/art824/artillery/art_1_0.png differ diff --git a/data/art824/artillery/art_1_1.png b/data/art824/artillery/art_1_1.png new file mode 100644 index 0000000..d90d447 Binary files /dev/null and b/data/art824/artillery/art_1_1.png differ diff --git a/data/art824/artillery/art_1_2.png b/data/art824/artillery/art_1_2.png new file mode 100644 index 0000000..476d531 Binary files /dev/null and b/data/art824/artillery/art_1_2.png differ diff --git a/data/art824/artillery/art_2_0.png b/data/art824/artillery/art_2_0.png new file mode 100644 index 0000000..545f717 Binary files /dev/null and b/data/art824/artillery/art_2_0.png differ diff --git a/data/art824/artillery/art_2_1.png b/data/art824/artillery/art_2_1.png new file mode 100644 index 0000000..40cf4df Binary files /dev/null and b/data/art824/artillery/art_2_1.png differ diff --git a/data/art824/artillery/art_2_2.png b/data/art824/artillery/art_2_2.png new file mode 100644 index 0000000..f5174b3 Binary files /dev/null and b/data/art824/artillery/art_2_2.png differ diff --git a/data/art824/artillery/art_3_0.png b/data/art824/artillery/art_3_0.png new file mode 100644 index 0000000..9b4cc20 Binary files /dev/null and b/data/art824/artillery/art_3_0.png differ diff --git a/data/art824/artillery/art_3_1.png b/data/art824/artillery/art_3_1.png new file mode 100644 index 0000000..306161d Binary files /dev/null and b/data/art824/artillery/art_3_1.png differ diff --git a/data/art824/artillery/art_3_2.png b/data/art824/artillery/art_3_2.png new file mode 100644 index 0000000..43e975e Binary files /dev/null and b/data/art824/artillery/art_3_2.png differ diff --git a/data/art824/artillery/art_4_0.png b/data/art824/artillery/art_4_0.png new file mode 100644 index 0000000..28d17a7 Binary files /dev/null and b/data/art824/artillery/art_4_0.png differ diff --git a/data/art824/artillery/art_4_1.png b/data/art824/artillery/art_4_1.png new file mode 100644 index 0000000..37c8791 Binary files /dev/null and b/data/art824/artillery/art_4_1.png differ diff --git a/data/art824/artillery/art_4_2.png b/data/art824/artillery/art_4_2.png new file mode 100644 index 0000000..faed957 Binary files /dev/null and b/data/art824/artillery/art_4_2.png differ diff --git a/data/art824/artillery/art_5_0.png b/data/art824/artillery/art_5_0.png new file mode 100644 index 0000000..c7fd562 Binary files /dev/null and b/data/art824/artillery/art_5_0.png differ diff --git a/data/art824/artillery/art_5_1.png b/data/art824/artillery/art_5_1.png new file mode 100644 index 0000000..47837d1 Binary files /dev/null and b/data/art824/artillery/art_5_1.png differ diff --git a/data/art824/artillery/art_5_2.png b/data/art824/artillery/art_5_2.png new file mode 100644 index 0000000..86ecfdb Binary files /dev/null and b/data/art824/artillery/art_5_2.png differ diff --git a/data/art824/artillery/art_6_0.png b/data/art824/artillery/art_6_0.png new file mode 100644 index 0000000..e68730c Binary files /dev/null and b/data/art824/artillery/art_6_0.png differ diff --git a/data/art824/artillery/art_6_1.png b/data/art824/artillery/art_6_1.png new file mode 100644 index 0000000..dbc210a Binary files /dev/null and b/data/art824/artillery/art_6_1.png differ diff --git a/data/art824/artillery/art_6_2.png b/data/art824/artillery/art_6_2.png new file mode 100644 index 0000000..68e5dae Binary files /dev/null and b/data/art824/artillery/art_6_2.png differ diff --git a/data/art824/artillery/art_7_0.png b/data/art824/artillery/art_7_0.png new file mode 100644 index 0000000..02b190d Binary files /dev/null and b/data/art824/artillery/art_7_0.png differ diff --git a/data/art824/artillery/art_7_1.png b/data/art824/artillery/art_7_1.png new file mode 100644 index 0000000..5a501ea Binary files /dev/null and b/data/art824/artillery/art_7_1.png differ diff --git a/data/art824/artillery/art_7_2.png b/data/art824/artillery/art_7_2.png new file mode 100644 index 0000000..d91e873 Binary files /dev/null and b/data/art824/artillery/art_7_2.png differ diff --git a/data/art824/artilleryshot.amx b/data/art824/artilleryshot.amx new file mode 100644 index 0000000..d74deac --- /dev/null +++ b/data/art824/artilleryshot.amx @@ -0,0 +1,187 @@ + + + + + +80 +true + + +<_x0030_ href="#ref-5"/> +<_x0031_ href="#ref-6"/> +<_x0032_ href="#ref-7"/> +<_x0033_ href="#ref-8"/> +<_x0034_ href="#ref-9"/> +<_x0035_ href="#ref-10"/> +<_x0036_ href="#ref-11"/> + + +<_x0030_ href="#ref-12"/> +<_x0031_ href="#ref-13"/> + + +artilleryshot\artshot_a_1_3.png + + +artilleryshot\artshot_a_0_0.png + + +artilleryshot\artshot_a_0_1.png + + +artilleryshot\artshot_a_0_2.png + + +artilleryshot\artshot_a_1_0.png + + +artilleryshot\artshot_a_1_1.png + + +artilleryshot\artshot_a_1_2.png + + +a 0 +0 +3 +<_x0030_ href="#ref-22"/> +<_x0031_ href="#ref-23"/> +<_x0032_ href="#ref-24"/> + + +a 1 +0 +4 +<_x0030_ href="#ref-26"/> +<_x0031_ href="#ref-27"/> +<_x0032_ href="#ref-28"/> +<_x0033_ href="#ref-29"/> + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + +<_x0030_ href="#ref-38"/> + + +<_x0030_ href="#ref-39"/> + + +<_x0030_ href="#ref-40"/> + + +<_x0030_ href="#ref-41"/> + + +<_x0030_ href="#ref-42"/> + + +<_x0030_ href="#ref-43"/> + + +<_x0030_ href="#ref-44"/> + + + +4 +4 + + + + + +4 +4 + + + + + +4 +4 + + + + + +6 +6 + + + + + +6 +6 + + + + + +6 +6 + + + + + +6 +6 + + + + + diff --git a/data/art824/artilleryshot/artshot_a_0_0.png b/data/art824/artilleryshot/artshot_a_0_0.png new file mode 100644 index 0000000..527116a Binary files /dev/null and b/data/art824/artilleryshot/artshot_a_0_0.png differ diff --git a/data/art824/artilleryshot/artshot_a_0_1.png b/data/art824/artilleryshot/artshot_a_0_1.png new file mode 100644 index 0000000..645ce53 Binary files /dev/null and b/data/art824/artilleryshot/artshot_a_0_1.png differ diff --git a/data/art824/artilleryshot/artshot_a_0_2.png b/data/art824/artilleryshot/artshot_a_0_2.png new file mode 100644 index 0000000..1db624a Binary files /dev/null and b/data/art824/artilleryshot/artshot_a_0_2.png differ diff --git a/data/art824/artilleryshot/artshot_a_1_0.png b/data/art824/artilleryshot/artshot_a_1_0.png new file mode 100644 index 0000000..c20729e Binary files /dev/null and b/data/art824/artilleryshot/artshot_a_1_0.png differ diff --git a/data/art824/artilleryshot/artshot_a_1_1.png b/data/art824/artilleryshot/artshot_a_1_1.png new file mode 100644 index 0000000..b9802b1 Binary files /dev/null and b/data/art824/artilleryshot/artshot_a_1_1.png differ diff --git a/data/art824/artilleryshot/artshot_a_1_2.png b/data/art824/artilleryshot/artshot_a_1_2.png new file mode 100644 index 0000000..3cdf51a Binary files /dev/null and b/data/art824/artilleryshot/artshot_a_1_2.png differ diff --git a/data/art824/artilleryshot/artshot_a_1_3.png b/data/art824/artilleryshot/artshot_a_1_3.png new file mode 100644 index 0000000..6b17832 Binary files /dev/null and b/data/art824/artilleryshot/artshot_a_1_3.png differ diff --git a/data/art824/bitmaps/RocketArtifact.bmp b/data/art824/bitmaps/RocketArtifact.bmp new file mode 100644 index 0000000..c72165e Binary files /dev/null and b/data/art824/bitmaps/RocketArtifact.bmp differ diff --git a/data/art824/bitmaps/abort_build_btn_down.bmp b/data/art824/bitmaps/abort_build_btn_down.bmp new file mode 100644 index 0000000..66af5b6 Binary files /dev/null and b/data/art824/bitmaps/abort_build_btn_down.bmp differ diff --git a/data/art824/bitmaps/abort_build_btn_up.bmp b/data/art824/bitmaps/abort_build_btn_up.bmp new file mode 100644 index 0000000..f221d83 Binary files /dev/null and b/data/art824/bitmaps/abort_build_btn_up.bmp differ diff --git a/data/art824/bitmaps/abort_repair_btn_down.bmp b/data/art824/bitmaps/abort_repair_btn_down.bmp new file mode 100644 index 0000000..ba247dd Binary files /dev/null and b/data/art824/bitmaps/abort_repair_btn_down.bmp differ diff --git a/data/art824/bitmaps/abort_repair_btn_up.bmp b/data/art824/bitmaps/abort_repair_btn_up.bmp new file mode 100644 index 0000000..6e6a11e Binary files /dev/null and b/data/art824/bitmaps/abort_repair_btn_up.bmp differ diff --git a/data/art824/bitmaps/andyportrait.bmp b/data/art824/bitmaps/andyportrait.bmp new file mode 100644 index 0000000..3b38ea8 Binary files /dev/null and b/data/art824/bitmaps/andyportrait.bmp differ diff --git a/data/art824/bitmaps/arrow0.bmp b/data/art824/bitmaps/arrow0.bmp new file mode 100644 index 0000000..3bac542 Binary files /dev/null and b/data/art824/bitmaps/arrow0.bmp differ diff --git a/data/art824/bitmaps/arrow1.bmp b/data/art824/bitmaps/arrow1.bmp new file mode 100644 index 0000000..287ded5 Binary files /dev/null and b/data/art824/bitmaps/arrow1.bmp differ diff --git a/data/art824/bitmaps/arrow2.bmp b/data/art824/bitmaps/arrow2.bmp new file mode 100644 index 0000000..e19946a Binary files /dev/null and b/data/art824/bitmaps/arrow2.bmp differ diff --git a/data/art824/bitmaps/arrow3.bmp b/data/art824/bitmaps/arrow3.bmp new file mode 100644 index 0000000..bf96801 Binary files /dev/null and b/data/art824/bitmaps/arrow3.bmp differ diff --git a/data/art824/bitmaps/arrow4.bmp b/data/art824/bitmaps/arrow4.bmp new file mode 100644 index 0000000..02fafcb Binary files /dev/null and b/data/art824/bitmaps/arrow4.bmp differ diff --git a/data/art824/bitmaps/arrow5.bmp b/data/art824/bitmaps/arrow5.bmp new file mode 100644 index 0000000..e09b8db Binary files /dev/null and b/data/art824/bitmaps/arrow5.bmp differ diff --git a/data/art824/bitmaps/arrow6.bmp b/data/art824/bitmaps/arrow6.bmp new file mode 100644 index 0000000..6c6d7d8 Binary files /dev/null and b/data/art824/bitmaps/arrow6.bmp differ diff --git a/data/art824/bitmaps/arrow7.bmp b/data/art824/bitmaps/arrow7.bmp new file mode 100644 index 0000000..32cb8a5 Binary files /dev/null and b/data/art824/bitmaps/arrow7.bmp differ diff --git a/data/art824/bitmaps/arrowheaddown.bmp b/data/art824/bitmaps/arrowheaddown.bmp new file mode 100644 index 0000000..39ef0ff Binary files /dev/null and b/data/art824/bitmaps/arrowheaddown.bmp differ diff --git a/data/art824/bitmaps/arrowheadleft.bmp b/data/art824/bitmaps/arrowheadleft.bmp new file mode 100644 index 0000000..cf81e9a Binary files /dev/null and b/data/art824/bitmaps/arrowheadleft.bmp differ diff --git a/data/art824/bitmaps/arrowheadright.bmp b/data/art824/bitmaps/arrowheadright.bmp new file mode 100644 index 0000000..a7f681c Binary files /dev/null and b/data/art824/bitmaps/arrowheadright.bmp differ diff --git a/data/art824/bitmaps/arrowheadup.bmp b/data/art824/bitmaps/arrowheadup.bmp new file mode 100644 index 0000000..9cc0d45 Binary files /dev/null and b/data/art824/bitmaps/arrowheadup.bmp differ diff --git a/data/art824/bitmaps/backdown.bmp b/data/art824/bitmaps/backdown.bmp new file mode 100644 index 0000000..78dbb57 Binary files /dev/null and b/data/art824/bitmaps/backdown.bmp differ diff --git a/data/art824/bitmaps/backup.bmp b/data/art824/bitmaps/backup.bmp new file mode 100644 index 0000000..85ecac3 Binary files /dev/null and b/data/art824/bitmaps/backup.bmp differ diff --git a/data/art824/bitmaps/build_btn_disabled.bmp b/data/art824/bitmaps/build_btn_disabled.bmp new file mode 100644 index 0000000..6c67847 Binary files /dev/null and b/data/art824/bitmaps/build_btn_disabled.bmp differ diff --git a/data/art824/bitmaps/build_btn_down.bmp b/data/art824/bitmaps/build_btn_down.bmp new file mode 100644 index 0000000..98a509a Binary files /dev/null and b/data/art824/bitmaps/build_btn_down.bmp differ diff --git a/data/art824/bitmaps/build_btn_up.bmp b/data/art824/bitmaps/build_btn_up.bmp new file mode 100644 index 0000000..cb0537f Binary files /dev/null and b/data/art824/bitmaps/build_btn_up.bmp differ diff --git a/data/art824/bitmaps/buttonleftdown.bmp b/data/art824/bitmaps/buttonleftdown.bmp new file mode 100644 index 0000000..5a5d907 Binary files /dev/null and b/data/art824/bitmaps/buttonleftdown.bmp differ diff --git a/data/art824/bitmaps/buttonleftup.bmp b/data/art824/bitmaps/buttonleftup.bmp new file mode 100644 index 0000000..c02e2f6 Binary files /dev/null and b/data/art824/bitmaps/buttonleftup.bmp differ diff --git a/data/art824/bitmaps/buttonmiddown.bmp b/data/art824/bitmaps/buttonmiddown.bmp new file mode 100644 index 0000000..374716e Binary files /dev/null and b/data/art824/bitmaps/buttonmiddown.bmp differ diff --git a/data/art824/bitmaps/buttonmidup.bmp b/data/art824/bitmaps/buttonmidup.bmp new file mode 100644 index 0000000..0f39a73 Binary files /dev/null and b/data/art824/bitmaps/buttonmidup.bmp differ diff --git a/data/art824/bitmaps/buttonrightdown.bmp b/data/art824/bitmaps/buttonrightdown.bmp new file mode 100644 index 0000000..dabd770 Binary files /dev/null and b/data/art824/bitmaps/buttonrightdown.bmp differ diff --git a/data/art824/bitmaps/buttonrightup.bmp b/data/art824/bitmaps/buttonrightup.bmp new file mode 100644 index 0000000..c8a9fe0 Binary files /dev/null and b/data/art824/bitmaps/buttonrightup.bmp differ diff --git a/data/art824/bitmaps/canceldown.bmp b/data/art824/bitmaps/canceldown.bmp new file mode 100644 index 0000000..18175f1 Binary files /dev/null and b/data/art824/bitmaps/canceldown.bmp differ diff --git a/data/art824/bitmaps/cancelup.bmp b/data/art824/bitmaps/cancelup.bmp new file mode 100644 index 0000000..5b50105 Binary files /dev/null and b/data/art824/bitmaps/cancelup.bmp differ diff --git a/data/art824/bitmaps/checkboxoffdown.bmp b/data/art824/bitmaps/checkboxoffdown.bmp new file mode 100644 index 0000000..35c061a Binary files /dev/null and b/data/art824/bitmaps/checkboxoffdown.bmp differ diff --git a/data/art824/bitmaps/checkboxoffup.bmp b/data/art824/bitmaps/checkboxoffup.bmp new file mode 100644 index 0000000..35898e2 Binary files /dev/null and b/data/art824/bitmaps/checkboxoffup.bmp differ diff --git a/data/art824/bitmaps/checkboxondown.bmp b/data/art824/bitmaps/checkboxondown.bmp new file mode 100644 index 0000000..e40c6cf Binary files /dev/null and b/data/art824/bitmaps/checkboxondown.bmp differ diff --git a/data/art824/bitmaps/checkboxonup.bmp b/data/art824/bitmaps/checkboxonup.bmp new file mode 100644 index 0000000..ac0620e Binary files /dev/null and b/data/art824/bitmaps/checkboxonup.bmp differ diff --git a/data/art824/bitmaps/damage_infantry.bmp b/data/art824/bitmaps/damage_infantry.bmp new file mode 100644 index 0000000..df4d8d4 Binary files /dev/null and b/data/art824/bitmaps/damage_infantry.bmp differ diff --git a/data/art824/bitmaps/damage_structure.bmp b/data/art824/bitmaps/damage_structure.bmp new file mode 100644 index 0000000..48166d2 Binary files /dev/null and b/data/art824/bitmaps/damage_structure.bmp differ diff --git a/data/art824/bitmaps/damage_vehicle.bmp b/data/art824/bitmaps/damage_vehicle.bmp new file mode 100644 index 0000000..6cec7bb Binary files /dev/null and b/data/art824/bitmaps/damage_vehicle.bmp differ diff --git a/data/art824/bitmaps/fog0001.bmp b/data/art824/bitmaps/fog0001.bmp new file mode 100644 index 0000000..8904685 Binary files /dev/null and b/data/art824/bitmaps/fog0001.bmp differ diff --git a/data/art824/bitmaps/fog0010.bmp b/data/art824/bitmaps/fog0010.bmp new file mode 100644 index 0000000..62073ac Binary files /dev/null and b/data/art824/bitmaps/fog0010.bmp differ diff --git a/data/art824/bitmaps/fog0011.bmp b/data/art824/bitmaps/fog0011.bmp new file mode 100644 index 0000000..3e2640a Binary files /dev/null and b/data/art824/bitmaps/fog0011.bmp differ diff --git a/data/art824/bitmaps/fog0100.bmp b/data/art824/bitmaps/fog0100.bmp new file mode 100644 index 0000000..9eb8c4c Binary files /dev/null and b/data/art824/bitmaps/fog0100.bmp differ diff --git a/data/art824/bitmaps/fog0101.bmp b/data/art824/bitmaps/fog0101.bmp new file mode 100644 index 0000000..410b302 Binary files /dev/null and b/data/art824/bitmaps/fog0101.bmp differ diff --git a/data/art824/bitmaps/fog0111.bmp b/data/art824/bitmaps/fog0111.bmp new file mode 100644 index 0000000..94d2521 Binary files /dev/null and b/data/art824/bitmaps/fog0111.bmp differ diff --git a/data/art824/bitmaps/fog1000.bmp b/data/art824/bitmaps/fog1000.bmp new file mode 100644 index 0000000..e6635e9 Binary files /dev/null and b/data/art824/bitmaps/fog1000.bmp differ diff --git a/data/art824/bitmaps/fog1010.bmp b/data/art824/bitmaps/fog1010.bmp new file mode 100644 index 0000000..5e9f4e0 Binary files /dev/null and b/data/art824/bitmaps/fog1010.bmp differ diff --git a/data/art824/bitmaps/fog1011.bmp b/data/art824/bitmaps/fog1011.bmp new file mode 100644 index 0000000..f11e757 Binary files /dev/null and b/data/art824/bitmaps/fog1011.bmp differ diff --git a/data/art824/bitmaps/fog1100.bmp b/data/art824/bitmaps/fog1100.bmp new file mode 100644 index 0000000..e613b8e Binary files /dev/null and b/data/art824/bitmaps/fog1100.bmp differ diff --git a/data/art824/bitmaps/fog1101.bmp b/data/art824/bitmaps/fog1101.bmp new file mode 100644 index 0000000..bdaeae9 Binary files /dev/null and b/data/art824/bitmaps/fog1101.bmp differ diff --git a/data/art824/bitmaps/fog1110.bmp b/data/art824/bitmaps/fog1110.bmp new file mode 100644 index 0000000..d63136a Binary files /dev/null and b/data/art824/bitmaps/fog1110.bmp differ diff --git a/data/art824/bitmaps/fog1111.bmp b/data/art824/bitmaps/fog1111.bmp new file mode 100644 index 0000000..bfae5b0 Binary files /dev/null and b/data/art824/bitmaps/fog1111.bmp differ diff --git a/data/art824/bitmaps/fox.bmp b/data/art824/bitmaps/fox.bmp new file mode 100644 index 0000000..3fc01d4 Binary files /dev/null and b/data/art824/bitmaps/fox.bmp differ diff --git a/data/art824/bitmaps/galax1a.bmp b/data/art824/bitmaps/galax1a.bmp new file mode 100644 index 0000000..f422347 Binary files /dev/null and b/data/art824/bitmaps/galax1a.bmp differ diff --git a/data/art824/bitmaps/galax1b.bmp b/data/art824/bitmaps/galax1b.bmp new file mode 100644 index 0000000..78e1ecd Binary files /dev/null and b/data/art824/bitmaps/galax1b.bmp differ diff --git a/data/art824/bitmaps/galax1c.bmp b/data/art824/bitmaps/galax1c.bmp new file mode 100644 index 0000000..f777809 Binary files /dev/null and b/data/art824/bitmaps/galax1c.bmp differ diff --git a/data/art824/bitmaps/galax2a.bmp b/data/art824/bitmaps/galax2a.bmp new file mode 100644 index 0000000..83a50ed Binary files /dev/null and b/data/art824/bitmaps/galax2a.bmp differ diff --git a/data/art824/bitmaps/galax2b.bmp b/data/art824/bitmaps/galax2b.bmp new file mode 100644 index 0000000..ad1a057 Binary files /dev/null and b/data/art824/bitmaps/galax2b.bmp differ diff --git a/data/art824/bitmaps/galax2c.bmp b/data/art824/bitmaps/galax2c.bmp new file mode 100644 index 0000000..b4ac46e Binary files /dev/null and b/data/art824/bitmaps/galax2c.bmp differ diff --git a/data/art824/bitmaps/galax3a.bmp b/data/art824/bitmaps/galax3a.bmp new file mode 100644 index 0000000..84e26a9 Binary files /dev/null and b/data/art824/bitmaps/galax3a.bmp differ diff --git a/data/art824/bitmaps/galax3b.bmp b/data/art824/bitmaps/galax3b.bmp new file mode 100644 index 0000000..dffde9a Binary files /dev/null and b/data/art824/bitmaps/galax3b.bmp differ diff --git a/data/art824/bitmaps/galax3c.bmp b/data/art824/bitmaps/galax3c.bmp new file mode 100644 index 0000000..74da2a7 Binary files /dev/null and b/data/art824/bitmaps/galax3c.bmp differ diff --git a/data/art824/bitmaps/helpdown.bmp b/data/art824/bitmaps/helpdown.bmp new file mode 100644 index 0000000..c52b504 Binary files /dev/null and b/data/art824/bitmaps/helpdown.bmp differ diff --git a/data/art824/bitmaps/helpup.bmp b/data/art824/bitmaps/helpup.bmp new file mode 100644 index 0000000..ab316ac Binary files /dev/null and b/data/art824/bitmaps/helpup.bmp differ diff --git a/data/art824/bitmaps/jana.bmp b/data/art824/bitmaps/jana.bmp new file mode 100644 index 0000000..784b002 Binary files /dev/null and b/data/art824/bitmaps/jana.bmp differ diff --git a/data/art824/bitmaps/mapdown.bmp b/data/art824/bitmaps/mapdown.bmp new file mode 100644 index 0000000..8230ddf Binary files /dev/null and b/data/art824/bitmaps/mapdown.bmp differ diff --git a/data/art824/bitmaps/mapup.bmp b/data/art824/bitmaps/mapup.bmp new file mode 100644 index 0000000..ec6d618 Binary files /dev/null and b/data/art824/bitmaps/mapup.bmp differ diff --git a/data/art824/bitmaps/menudown.bmp b/data/art824/bitmaps/menudown.bmp new file mode 100644 index 0000000..7ed3c8b Binary files /dev/null and b/data/art824/bitmaps/menudown.bmp differ diff --git a/data/art824/bitmaps/menuup.bmp b/data/art824/bitmaps/menuup.bmp new file mode 100644 index 0000000..37c117b Binary files /dev/null and b/data/art824/bitmaps/menuup.bmp differ diff --git a/data/art824/bitmaps/needs_credits_symbol.bmp b/data/art824/bitmaps/needs_credits_symbol.bmp new file mode 100644 index 0000000..351906d Binary files /dev/null and b/data/art824/bitmaps/needs_credits_symbol.bmp differ diff --git a/data/art824/bitmaps/needs_power_symbol.bmp b/data/art824/bitmaps/needs_power_symbol.bmp new file mode 100644 index 0000000..18b2b0f Binary files /dev/null and b/data/art824/bitmaps/needs_power_symbol.bmp differ diff --git a/data/art824/bitmaps/nextdown.bmp b/data/art824/bitmaps/nextdown.bmp new file mode 100644 index 0000000..f17a0f3 Binary files /dev/null and b/data/art824/bitmaps/nextdown.bmp differ diff --git a/data/art824/bitmaps/nextup.bmp b/data/art824/bitmaps/nextup.bmp new file mode 100644 index 0000000..bde89d9 Binary files /dev/null and b/data/art824/bitmaps/nextup.bmp differ diff --git a/data/art824/bitmaps/nosymbol.bmp b/data/art824/bitmaps/nosymbol.bmp new file mode 100644 index 0000000..229054e Binary files /dev/null and b/data/art824/bitmaps/nosymbol.bmp differ diff --git a/data/art824/bitmaps/nosymboldown.bmp b/data/art824/bitmaps/nosymboldown.bmp new file mode 100644 index 0000000..fefbd4b Binary files /dev/null and b/data/art824/bitmaps/nosymboldown.bmp differ diff --git a/data/art824/bitmaps/okdown.bmp b/data/art824/bitmaps/okdown.bmp new file mode 100644 index 0000000..e4c4723 Binary files /dev/null and b/data/art824/bitmaps/okdown.bmp differ diff --git a/data/art824/bitmaps/okup.bmp b/data/art824/bitmaps/okup.bmp new file mode 100644 index 0000000..f289754 Binary files /dev/null and b/data/art824/bitmaps/okup.bmp differ diff --git a/data/art824/bitmaps/olstrom.bmp b/data/art824/bitmaps/olstrom.bmp new file mode 100644 index 0000000..8f01ea2 Binary files /dev/null and b/data/art824/bitmaps/olstrom.bmp differ diff --git a/data/art824/bitmaps/pip.bmp b/data/art824/bitmaps/pip.bmp new file mode 100644 index 0000000..66e15d5 Binary files /dev/null and b/data/art824/bitmaps/pip.bmp differ diff --git a/data/art824/bitmaps/placementBad.bmp b/data/art824/bitmaps/placementBad.bmp new file mode 100644 index 0000000..8e0eafe Binary files /dev/null and b/data/art824/bitmaps/placementBad.bmp differ diff --git a/data/art824/bitmaps/placementGood.bmp b/data/art824/bitmaps/placementGood.bmp new file mode 100644 index 0000000..3b82cba Binary files /dev/null and b/data/art824/bitmaps/placementGood.bmp differ diff --git a/data/art824/bitmaps/plant.bmp b/data/art824/bitmaps/plant.bmp new file mode 100644 index 0000000..ee3f906 Binary files /dev/null and b/data/art824/bitmaps/plant.bmp differ diff --git a/data/art824/bitmaps/plant1.bmp b/data/art824/bitmaps/plant1.bmp new file mode 100644 index 0000000..7fd3c87 Binary files /dev/null and b/data/art824/bitmaps/plant1.bmp differ diff --git a/data/art824/bitmaps/plant2.bmp b/data/art824/bitmaps/plant2.bmp new file mode 100644 index 0000000..f1f808a Binary files /dev/null and b/data/art824/bitmaps/plant2.bmp differ diff --git a/data/art824/bitmaps/plant3.bmp b/data/art824/bitmaps/plant3.bmp new file mode 100644 index 0000000..ccb1a47 Binary files /dev/null and b/data/art824/bitmaps/plant3.bmp differ diff --git a/data/art824/bitmaps/plant4.bmp b/data/art824/bitmaps/plant4.bmp new file mode 100644 index 0000000..17937c8 Binary files /dev/null and b/data/art824/bitmaps/plant4.bmp differ diff --git a/data/art824/bitmaps/plant5.bmp b/data/art824/bitmaps/plant5.bmp new file mode 100644 index 0000000..82a5965 Binary files /dev/null and b/data/art824/bitmaps/plant5.bmp differ diff --git a/data/art824/bitmaps/presetdown.bmp b/data/art824/bitmaps/presetdown.bmp new file mode 100644 index 0000000..a0d6046 Binary files /dev/null and b/data/art824/bitmaps/presetdown.bmp differ diff --git a/data/art824/bitmaps/presetup.bmp b/data/art824/bitmaps/presetup.bmp new file mode 100644 index 0000000..46e2597 Binary files /dev/null and b/data/art824/bitmaps/presetup.bmp differ diff --git a/data/art824/bitmaps/previousdown.bmp b/data/art824/bitmaps/previousdown.bmp new file mode 100644 index 0000000..eafae7e Binary files /dev/null and b/data/art824/bitmaps/previousdown.bmp differ diff --git a/data/art824/bitmaps/previousup.bmp b/data/art824/bitmaps/previousup.bmp new file mode 100644 index 0000000..3674ccb Binary files /dev/null and b/data/art824/bitmaps/previousup.bmp differ diff --git a/data/art824/bitmaps/repair_btn_disabled.bmp b/data/art824/bitmaps/repair_btn_disabled.bmp new file mode 100644 index 0000000..3497533 Binary files /dev/null and b/data/art824/bitmaps/repair_btn_disabled.bmp differ diff --git a/data/art824/bitmaps/repair_btn_down.bmp b/data/art824/bitmaps/repair_btn_down.bmp new file mode 100644 index 0000000..bf09367 Binary files /dev/null and b/data/art824/bitmaps/repair_btn_down.bmp differ diff --git a/data/art824/bitmaps/repair_btn_up.bmp b/data/art824/bitmaps/repair_btn_up.bmp new file mode 100644 index 0000000..7139ad1 Binary files /dev/null and b/data/art824/bitmaps/repair_btn_up.bmp differ diff --git a/data/art824/bitmaps/repairing_symbol.bmp b/data/art824/bitmaps/repairing_symbol.bmp new file mode 100644 index 0000000..25f9f64 Binary files /dev/null and b/data/art824/bitmaps/repairing_symbol.bmp differ diff --git a/data/art824/bitmaps/rocks.bmp b/data/art824/bitmaps/rocks.bmp new file mode 100644 index 0000000..77d9f73 Binary files /dev/null and b/data/art824/bitmaps/rocks.bmp differ diff --git a/data/art824/bitmaps/scorch_16x16.bmp b/data/art824/bitmaps/scorch_16x16.bmp new file mode 100644 index 0000000..4637fa8 Binary files /dev/null and b/data/art824/bitmaps/scorch_16x16.bmp differ diff --git a/data/art824/bitmaps/scorch_32x16.bmp b/data/art824/bitmaps/scorch_32x16.bmp new file mode 100644 index 0000000..9c84476 Binary files /dev/null and b/data/art824/bitmaps/scorch_32x16.bmp differ diff --git a/data/art824/bitmaps/scorch_48x48.bmp b/data/art824/bitmaps/scorch_48x48.bmp new file mode 100644 index 0000000..1fb9748 Binary files /dev/null and b/data/art824/bitmaps/scorch_48x48.bmp differ diff --git a/data/art824/bitmaps/scorch_8x8.bmp b/data/art824/bitmaps/scorch_8x8.bmp new file mode 100644 index 0000000..2c478c5 Binary files /dev/null and b/data/art824/bitmaps/scorch_8x8.bmp differ diff --git a/data/art824/bitmaps/scrolldowndown.bmp b/data/art824/bitmaps/scrolldowndown.bmp new file mode 100644 index 0000000..18c6503 Binary files /dev/null and b/data/art824/bitmaps/scrolldowndown.bmp differ diff --git a/data/art824/bitmaps/scrolldownup.bmp b/data/art824/bitmaps/scrolldownup.bmp new file mode 100644 index 0000000..de4b601 Binary files /dev/null and b/data/art824/bitmaps/scrolldownup.bmp differ diff --git a/data/art824/bitmaps/scrollupdown.bmp b/data/art824/bitmaps/scrollupdown.bmp new file mode 100644 index 0000000..5ed057c Binary files /dev/null and b/data/art824/bitmaps/scrollupdown.bmp differ diff --git a/data/art824/bitmaps/scrollupup.bmp b/data/art824/bitmaps/scrollupup.bmp new file mode 100644 index 0000000..ac9cabb Binary files /dev/null and b/data/art824/bitmaps/scrollupup.bmp differ diff --git a/data/art824/bitmaps/sell_btn_disabled.bmp b/data/art824/bitmaps/sell_btn_disabled.bmp new file mode 100644 index 0000000..3e03a24 Binary files /dev/null and b/data/art824/bitmaps/sell_btn_disabled.bmp differ diff --git a/data/art824/bitmaps/sell_btn_down.bmp b/data/art824/bitmaps/sell_btn_down.bmp new file mode 100644 index 0000000..c493487 Binary files /dev/null and b/data/art824/bitmaps/sell_btn_down.bmp differ diff --git a/data/art824/bitmaps/sell_btn_up.bmp b/data/art824/bitmaps/sell_btn_up.bmp new file mode 100644 index 0000000..67cd96c Binary files /dev/null and b/data/art824/bitmaps/sell_btn_up.bmp differ diff --git a/data/art824/bitmaps/tree.bmp b/data/art824/bitmaps/tree.bmp new file mode 100644 index 0000000..dafaa4e Binary files /dev/null and b/data/art824/bitmaps/tree.bmp differ diff --git a/data/art824/bitmaps/tree1.bmp b/data/art824/bitmaps/tree1.bmp new file mode 100644 index 0000000..ae8a319 Binary files /dev/null and b/data/art824/bitmaps/tree1.bmp differ diff --git a/data/art824/bitmaps/tree2.bmp b/data/art824/bitmaps/tree2.bmp new file mode 100644 index 0000000..a3fb8e0 Binary files /dev/null and b/data/art824/bitmaps/tree2.bmp differ diff --git a/data/art824/bitmaps/tree3.bmp b/data/art824/bitmaps/tree3.bmp new file mode 100644 index 0000000..b5381df Binary files /dev/null and b/data/art824/bitmaps/tree3.bmp differ diff --git a/data/art824/bitmaps/tree4.bmp b/data/art824/bitmaps/tree4.bmp new file mode 100644 index 0000000..64a5e5a Binary files /dev/null and b/data/art824/bitmaps/tree4.bmp differ diff --git a/data/art824/bitmaps/tree5.bmp b/data/art824/bitmaps/tree5.bmp new file mode 100644 index 0000000..5079399 Binary files /dev/null and b/data/art824/bitmaps/tree5.bmp differ diff --git a/data/art824/bitmaps/tree6.bmp b/data/art824/bitmaps/tree6.bmp new file mode 100644 index 0000000..147a0e2 Binary files /dev/null and b/data/art824/bitmaps/tree6.bmp differ diff --git a/data/art824/bitmaps/tree7.bmp b/data/art824/bitmaps/tree7.bmp new file mode 100644 index 0000000..2c1a538 Binary files /dev/null and b/data/art824/bitmaps/tree7.bmp differ diff --git a/data/art824/bitmaps/x.bmp b/data/art824/bitmaps/x.bmp new file mode 100644 index 0000000..b7437f3 Binary files /dev/null and b/data/art824/bitmaps/x.bmp differ diff --git a/data/art824/bullet.amx b/data/art824/bullet.amx new file mode 100644 index 0000000..ae151fe --- /dev/null +++ b/data/art824/bullet.amx @@ -0,0 +1,142 @@ + + + + + +80 +true + + +<_x0030_ href="#ref-5"/> +<_x0031_ href="#ref-6"/> +<_x0032_ href="#ref-7"/> +<_x0033_ href="#ref-8"/> +<_x0034_ href="#ref-9"/> +<_x0035_ href="#ref-10"/> +<_x0036_ href="#ref-11"/> +<_x0037_ href="#ref-12"/> +<_x0038_ href="#ref-13"/> +<_x0039_ href="#ref-14"/> + + +<_x0030_ href="#ref-15"/> +<_x0031_ href="#ref-16"/> + + +bullet\bullet_a_0_0.png + + +bullet\ricochet_a_3_2.png + + +bullet\ricochet_a_1_0.png + + +bullet\ricochet_a_1_1.png + + +bullet\ricochet_a_1_2.png + + +bullet\ricochet_a_2_0.png + + +bullet\ricochet_a_2_1.png + + +bullet\ricochet_a_2_2.png + + +bullet\ricochet_a_3_0.png + + +bullet\ricochet_a_3_1.png + + +a 0 +0 +1 +<_x0030_ href="#ref-28"/> + + +a 1 +0 +3 +<_x0030_ href="#ref-30"/> +<_x0031_ href="#ref-31"/> +<_x0032_ href="#ref-32"/> + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + +<_x0030_ href="#ref-38"/> + + +<_x0030_ href="#ref-39"/> + + +<_x0030_ href="#ref-40"/> + + +<_x0030_ href="#ref-41"/> + + + +3 +3 + + + + + +2 +2 + + + + + +3 +3 + + + + + +4 +4 + + + + + diff --git a/data/art824/bullet/bullet_a_0_0.png b/data/art824/bullet/bullet_a_0_0.png new file mode 100644 index 0000000..32205e7 Binary files /dev/null and b/data/art824/bullet/bullet_a_0_0.png differ diff --git a/data/art824/bullet/ricochet_a_1_0.png b/data/art824/bullet/ricochet_a_1_0.png new file mode 100644 index 0000000..d728c56 Binary files /dev/null and b/data/art824/bullet/ricochet_a_1_0.png differ diff --git a/data/art824/bullet/ricochet_a_1_1.png b/data/art824/bullet/ricochet_a_1_1.png new file mode 100644 index 0000000..cdfad77 Binary files /dev/null and b/data/art824/bullet/ricochet_a_1_1.png differ diff --git a/data/art824/bullet/ricochet_a_1_2.png b/data/art824/bullet/ricochet_a_1_2.png new file mode 100644 index 0000000..3c419ca Binary files /dev/null and b/data/art824/bullet/ricochet_a_1_2.png differ diff --git a/data/art824/bullet/ricochet_a_2_0.png b/data/art824/bullet/ricochet_a_2_0.png new file mode 100644 index 0000000..9c98ca0 Binary files /dev/null and b/data/art824/bullet/ricochet_a_2_0.png differ diff --git a/data/art824/bullet/ricochet_a_2_1.png b/data/art824/bullet/ricochet_a_2_1.png new file mode 100644 index 0000000..c0f0342 Binary files /dev/null and b/data/art824/bullet/ricochet_a_2_1.png differ diff --git a/data/art824/bullet/ricochet_a_2_2.png b/data/art824/bullet/ricochet_a_2_2.png new file mode 100644 index 0000000..3d063b3 Binary files /dev/null and b/data/art824/bullet/ricochet_a_2_2.png differ diff --git a/data/art824/bullet/ricochet_a_3_0.png b/data/art824/bullet/ricochet_a_3_0.png new file mode 100644 index 0000000..2ee3fd5 Binary files /dev/null and b/data/art824/bullet/ricochet_a_3_0.png differ diff --git a/data/art824/bullet/ricochet_a_3_1.png b/data/art824/bullet/ricochet_a_3_1.png new file mode 100644 index 0000000..6dab752 Binary files /dev/null and b/data/art824/bullet/ricochet_a_3_1.png differ diff --git a/data/art824/bullet/ricochet_a_3_2.png b/data/art824/bullet/ricochet_a_3_2.png new file mode 100644 index 0000000..4e3c275 Binary files /dev/null and b/data/art824/bullet/ricochet_a_3_2.png differ diff --git a/data/art824/buttonfont.bmp b/data/art824/buttonfont.bmp new file mode 100644 index 0000000..0614dea Binary files /dev/null and b/data/art824/buttonfont.bmp differ diff --git a/data/art824/buttonfont.txt b/data/art824/buttonfont.txt new file mode 100644 index 0000000..2edf19f --- /dev/null +++ b/data/art824/buttonfont.txt @@ -0,0 +1 @@ +!"#$%&'()*+,-./ ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz:;<=>?\@ \ No newline at end of file diff --git a/data/art824/common_8bpp.pal b/data/art824/common_8bpp.pal new file mode 100644 index 0000000..c2354fc --- /dev/null +++ b/data/art824/common_8bpp.pal @@ -0,0 +1,259 @@ +JASC-PAL +0100 +256 +0 0 0 +252 252 252 +252 0 0 +0 252 0 +252 252 0 +0 192 196 +184 248 248 +52 96 108 +28 96 112 +36 68 76 +16 40 44 +104 252 252 +84 160 172 +56 120 132 +40 80 88 +28 56 60 +0 116 232 +0 96 196 +0 64 120 +0 48 92 +0 32 64 +232 32 0 +196 28 0 +120 8 0 +92 8 0 +64 8 0 +232 228 0 +196 192 0 +120 116 0 +92 88 0 +64 60 0 +156 88 140 +216 216 216 +168 168 168 +144 144 144 +132 128 128 +120 120 120 +112 112 112 +100 100 100 +96 96 96 +92 92 92 +84 84 84 +76 76 76 +72 72 72 +68 68 68 +64 64 64 +60 60 60 +52 52 48 +48 48 44 +24 24 24 +20 20 16 +8 8 8 +252 0 252 +4 12 24 +12 24 44 +32 68 76 +116 116 116 +156 156 188 +36 32 28 +40 36 32 +44 40 36 +104 104 104 +128 128 172 +0 60 64 +208 200 180 +0 116 120 +92 40 76 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 +84 80 88 diff --git a/data/art824/cutscenebitmaps/andyjana.act b/data/art824/cutscenebitmaps/andyjana.act new file mode 100644 index 0000000..f159e46 Binary files /dev/null and b/data/art824/cutscenebitmaps/andyjana.act differ diff --git a/data/art824/cutscenebitmaps/andyjana.bmp b/data/art824/cutscenebitmaps/andyjana.bmp new file mode 100644 index 0000000..1370266 Binary files /dev/null and b/data/art824/cutscenebitmaps/andyjana.bmp differ diff --git a/data/art824/cutscenebitmaps/desertforbox.act b/data/art824/cutscenebitmaps/desertforbox.act new file mode 100644 index 0000000..38f255c Binary files /dev/null and b/data/art824/cutscenebitmaps/desertforbox.act differ diff --git a/data/art824/cutscenebitmaps/desertforbox.bmp b/data/art824/cutscenebitmaps/desertforbox.bmp new file mode 100644 index 0000000..38a450c Binary files /dev/null and b/data/art824/cutscenebitmaps/desertforbox.bmp differ diff --git a/data/art824/cutscenebitmaps/explosion.act b/data/art824/cutscenebitmaps/explosion.act new file mode 100644 index 0000000..ff97d55 Binary files /dev/null and b/data/art824/cutscenebitmaps/explosion.act differ diff --git a/data/art824/cutscenebitmaps/explosion.bmp b/data/art824/cutscenebitmaps/explosion.bmp new file mode 100644 index 0000000..5de1df1 Binary files /dev/null and b/data/art824/cutscenebitmaps/explosion.bmp differ diff --git a/data/art824/cutscenebitmaps/galaxite.act b/data/art824/cutscenebitmaps/galaxite.act new file mode 100644 index 0000000..31f409c Binary files /dev/null and b/data/art824/cutscenebitmaps/galaxite.act differ diff --git a/data/art824/cutscenebitmaps/galaxite.bmp b/data/art824/cutscenebitmaps/galaxite.bmp new file mode 100644 index 0000000..8df32bc Binary files /dev/null and b/data/art824/cutscenebitmaps/galaxite.bmp differ diff --git a/data/art824/cutscenebitmaps/monolith.act b/data/art824/cutscenebitmaps/monolith.act new file mode 100644 index 0000000..093736e Binary files /dev/null and b/data/art824/cutscenebitmaps/monolith.act differ diff --git a/data/art824/cutscenebitmaps/monolith.bmp b/data/art824/cutscenebitmaps/monolith.bmp new file mode 100644 index 0000000..767293e Binary files /dev/null and b/data/art824/cutscenebitmaps/monolith.bmp differ diff --git a/data/art824/cutscenebitmaps/orbitshot4.act b/data/art824/cutscenebitmaps/orbitshot4.act new file mode 100644 index 0000000..98143e5 Binary files /dev/null and b/data/art824/cutscenebitmaps/orbitshot4.act differ diff --git a/data/art824/cutscenebitmaps/orbitshot4.bmp b/data/art824/cutscenebitmaps/orbitshot4.bmp new file mode 100644 index 0000000..4802bbd Binary files /dev/null and b/data/art824/cutscenebitmaps/orbitshot4.bmp differ diff --git a/data/art824/cutscenebitmaps/replicator.act b/data/art824/cutscenebitmaps/replicator.act new file mode 100644 index 0000000..7c0fd38 Binary files /dev/null and b/data/art824/cutscenebitmaps/replicator.act differ diff --git a/data/art824/cutscenebitmaps/replicator.bmp b/data/art824/cutscenebitmaps/replicator.bmp new file mode 100644 index 0000000..40dbf47 Binary files /dev/null and b/data/art824/cutscenebitmaps/replicator.bmp differ diff --git a/data/art824/hrc.amx b/data/art824/hrc.amx new file mode 100644 index 0000000..5c94d5f --- /dev/null +++ b/data/art824/hrc.amx @@ -0,0 +1,242 @@ + + + + + +80 +true + + +<_x0030_ href="#ref-5"/> +<_x0031_ href="#ref-6"/> +<_x0032_ href="#ref-7"/> +<_x0033_ href="#ref-8"/> +<_x0034_ href="#ref-9"/> +<_x0035_ href="#ref-10"/> +<_x0036_ href="#ref-11"/> + + +<_x0030_ href="#ref-12"/> +<_x0031_ href="#ref-13"/> +<_x0032_ href="#ref-14"/> +<_x0033_ href="#ref-15"/> +<_x0034_ href="#ref-16"/> +<_x0035_ href="#ref-17"/> +<_x0036_ href="#ref-18"/> +<_x0037_ href="#ref-19"/> + + +hrc\hrc_base2_2_0.png + + +hrc\hrc_base_1_0.png + + +hrc\hrc_base_2_0.png + + +hrc\hrc_base2_0_0.png + + +hrc\hrc_base2_1_0.png + + +hrc\hrc_base_0_0.png + + +hrc\icon.png + + +base 0 +0 +1 +<_x0030_ href="#ref-28"/> + + +base 1 +0 +1 +<_x0030_ href="#ref-30"/> + + +base 2 +0 +1 +<_x0030_ href="#ref-32"/> + + +base2 0 +0 +1 +<_x0030_ href="#ref-34"/> + + +base2 1 +0 +1 +<_x0030_ href="#ref-36"/> + + +base2 2 +0 +1 +<_x0030_ href="#ref-38"/> + + +icon +0 +1 +<_x0030_ href="#ref-40"/> + + +help +0 +1 +<_x0030_ href="#ref-42"/> + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + +<_x0030_ href="#ref-52"/> + + +<_x0030_ href="#ref-53"/> + + +<_x0030_ href="#ref-54"/> + + +<_x0030_ href="#ref-55"/> + + +<_x0030_ href="#ref-56"/> + + +<_x0030_ href="#ref-57"/> + + +<_x0030_ href="#ref-58"/> + + +<_x0030_ href="#ref-59"/> + + + +2 +0 + + + + + +2 +0 + + + + + +2 +0 + + + + + +2 +0 + + + + + +2 +0 + + + + + +2 +0 + + + + + +1 +0 + + + + + +37 +26 + + + + + diff --git a/data/art824/hrc/hrc_base2_0_0.png b/data/art824/hrc/hrc_base2_0_0.png new file mode 100644 index 0000000..5fe1af4 Binary files /dev/null and b/data/art824/hrc/hrc_base2_0_0.png differ diff --git a/data/art824/hrc/hrc_base2_1_0.png b/data/art824/hrc/hrc_base2_1_0.png new file mode 100644 index 0000000..90a9e1d Binary files /dev/null and b/data/art824/hrc/hrc_base2_1_0.png differ diff --git a/data/art824/hrc/hrc_base2_2_0.png b/data/art824/hrc/hrc_base2_2_0.png new file mode 100644 index 0000000..05a210c Binary files /dev/null and b/data/art824/hrc/hrc_base2_2_0.png differ diff --git a/data/art824/hrc/hrc_base_0_0.png b/data/art824/hrc/hrc_base_0_0.png new file mode 100644 index 0000000..6a22d9a Binary files /dev/null and b/data/art824/hrc/hrc_base_0_0.png differ diff --git a/data/art824/hrc/hrc_base_1_0.png b/data/art824/hrc/hrc_base_1_0.png new file mode 100644 index 0000000..41700a2 Binary files /dev/null and b/data/art824/hrc/hrc_base_1_0.png differ diff --git a/data/art824/hrc/hrc_base_2_0.png b/data/art824/hrc/hrc_base_2_0.png new file mode 100644 index 0000000..06206c3 Binary files /dev/null and b/data/art824/hrc/hrc_base_2_0.png differ diff --git a/data/art824/hrc/icon.png b/data/art824/hrc/icon.png new file mode 100644 index 0000000..a4ce3f9 Binary files /dev/null and b/data/art824/hrc/icon.png differ diff --git a/data/art824/hudfont.bmp b/data/art824/hudfont.bmp new file mode 100644 index 0000000..a9c3049 Binary files /dev/null and b/data/art824/hudfont.bmp differ diff --git a/data/art824/hudfont.txt b/data/art824/hudfont.txt new file mode 100644 index 0000000..6e433e8 --- /dev/null +++ b/data/art824/hudfont.txt @@ -0,0 +1 @@ + ABCDEFGHIJ0123456789 \ No newline at end of file diff --git a/data/art824/lri.amx b/data/art824/lri.amx new file mode 100644 index 0000000..08a79a6 --- /dev/null +++ b/data/art824/lri.amx @@ -0,0 +1,3057 @@ + + + + + +80 +true + + +<_x0030_ href="#ref-5"/> +<_x0031_ href="#ref-6"/> +<_x0032_ href="#ref-7"/> +<_x0033_ href="#ref-8"/> +<_x0034_ href="#ref-9"/> +<_x0035_ href="#ref-10"/> +<_x0036_ href="#ref-11"/> +<_x0037_ href="#ref-12"/> +<_x0038_ href="#ref-13"/> +<_x0039_ href="#ref-14"/> +<_x0031_0 href="#ref-15"/> +<_x0031_1 href="#ref-16"/> +<_x0031_2 href="#ref-17"/> +<_x0031_3 href="#ref-18"/> +<_x0031_4 href="#ref-19"/> +<_x0031_5 href="#ref-20"/> +<_x0031_6 href="#ref-21"/> +<_x0031_7 href="#ref-22"/> +<_x0031_8 href="#ref-23"/> +<_x0031_9 href="#ref-24"/> +<_x0032_0 href="#ref-25"/> +<_x0032_1 href="#ref-26"/> +<_x0032_2 href="#ref-27"/> +<_x0032_3 href="#ref-28"/> +<_x0032_4 href="#ref-29"/> +<_x0032_5 href="#ref-30"/> +<_x0032_6 href="#ref-31"/> +<_x0032_7 href="#ref-32"/> +<_x0032_8 href="#ref-33"/> +<_x0032_9 href="#ref-34"/> +<_x0033_0 href="#ref-35"/> +<_x0033_1 href="#ref-36"/> +<_x0033_2 href="#ref-37"/> +<_x0033_3 href="#ref-38"/> +<_x0033_4 href="#ref-39"/> +<_x0033_5 href="#ref-40"/> +<_x0033_6 href="#ref-41"/> +<_x0033_7 href="#ref-42"/> +<_x0033_8 href="#ref-43"/> +<_x0033_9 href="#ref-44"/> +<_x0034_0 href="#ref-45"/> +<_x0034_1 href="#ref-46"/> +<_x0034_2 href="#ref-47"/> +<_x0034_3 href="#ref-48"/> +<_x0034_4 href="#ref-49"/> +<_x0034_5 href="#ref-50"/> +<_x0034_6 href="#ref-51"/> +<_x0034_7 href="#ref-52"/> +<_x0034_8 href="#ref-53"/> +<_x0034_9 href="#ref-54"/> +<_x0035_0 href="#ref-55"/> +<_x0035_1 href="#ref-56"/> +<_x0035_2 href="#ref-57"/> +<_x0035_3 href="#ref-58"/> +<_x0035_4 href="#ref-59"/> +<_x0035_5 href="#ref-60"/> +<_x0035_6 href="#ref-61"/> +<_x0035_7 href="#ref-62"/> +<_x0035_8 href="#ref-63"/> +<_x0035_9 href="#ref-64"/> +<_x0036_0 href="#ref-65"/> +<_x0036_1 href="#ref-66"/> +<_x0036_2 href="#ref-67"/> +<_x0036_3 href="#ref-68"/> +<_x0036_4 href="#ref-69"/> +<_x0036_5 href="#ref-70"/> +<_x0036_6 href="#ref-71"/> +<_x0036_7 href="#ref-72"/> +<_x0036_8 href="#ref-73"/> +<_x0036_9 href="#ref-74"/> +<_x0037_0 href="#ref-75"/> +<_x0037_1 href="#ref-76"/> +<_x0037_2 href="#ref-77"/> +<_x0037_3 href="#ref-78"/> +<_x0037_4 href="#ref-79"/> +<_x0037_5 href="#ref-80"/> +<_x0037_6 href="#ref-81"/> +<_x0037_7 href="#ref-82"/> +<_x0037_8 href="#ref-83"/> +<_x0037_9 href="#ref-84"/> +<_x0038_0 href="#ref-85"/> +<_x0038_1 href="#ref-86"/> +<_x0038_2 href="#ref-87"/> +<_x0038_3 href="#ref-88"/> +<_x0038_4 href="#ref-89"/> +<_x0038_5 href="#ref-90"/> +<_x0038_6 href="#ref-91"/> +<_x0038_7 href="#ref-92"/> +<_x0038_8 href="#ref-93"/> +<_x0038_9 href="#ref-94"/> +<_x0039_0 href="#ref-95"/> +<_x0039_1 href="#ref-96"/> +<_x0039_2 href="#ref-97"/> +<_x0039_3 href="#ref-98"/> +<_x0039_4 href="#ref-99"/> +<_x0039_5 href="#ref-100"/> +<_x0039_6 href="#ref-101"/> +<_x0039_7 href="#ref-102"/> +<_x0039_8 href="#ref-103"/> +<_x0039_9 href="#ref-104"/> +<_x0031_00 href="#ref-105"/> +<_x0031_01 href="#ref-106"/> +<_x0031_02 href="#ref-107"/> +<_x0031_03 href="#ref-108"/> +<_x0031_04 href="#ref-109"/> +<_x0031_05 href="#ref-110"/> +<_x0031_06 href="#ref-111"/> +<_x0031_07 href="#ref-112"/> +<_x0031_08 href="#ref-113"/> +<_x0031_09 href="#ref-114"/> +<_x0031_10 href="#ref-115"/> +<_x0031_11 href="#ref-116"/> +<_x0031_12 href="#ref-117"/> +<_x0031_13 href="#ref-118"/> +<_x0031_14 href="#ref-119"/> +<_x0031_15 href="#ref-120"/> +<_x0031_16 href="#ref-121"/> + + +<_x0030_ href="#ref-122"/> +<_x0031_ href="#ref-123"/> +<_x0032_ href="#ref-124"/> +<_x0033_ href="#ref-125"/> +<_x0034_ href="#ref-126"/> +<_x0035_ href="#ref-127"/> +<_x0036_ href="#ref-128"/> +<_x0037_ href="#ref-129"/> +<_x0038_ href="#ref-130"/> +<_x0039_ href="#ref-131"/> +<_x0031_0 href="#ref-132"/> +<_x0031_1 href="#ref-133"/> +<_x0031_2 href="#ref-134"/> +<_x0031_3 href="#ref-135"/> +<_x0031_4 href="#ref-136"/> +<_x0031_5 href="#ref-137"/> +<_x0031_6 href="#ref-138"/> +<_x0031_7 href="#ref-139"/> +<_x0031_8 href="#ref-140"/> +<_x0031_9 href="#ref-141"/> +<_x0032_0 href="#ref-142"/> +<_x0032_1 href="#ref-143"/> +<_x0032_2 href="#ref-144"/> +<_x0032_3 href="#ref-145"/> +<_x0032_4 href="#ref-146"/> +<_x0032_5 href="#ref-147"/> +<_x0032_6 href="#ref-148"/> + + +lri\lri_jog_1_0.png + + +lri\lri_jog_1_1.png + + +lri\lri_jog_1_2.png + + +lri\lri_jog_1_3.png + + +lri\lri_jog_7_5.png + + +lri\lri_jog_0_1.png + + +lri\lri_jog_0_2.png + + +lri\lri_jog_0_3.png + + +lri\lri_jog_0_5.png + + +lri\lri_jog_1_0.png + + +lri\lri_jog_1_1.png + + +lri\lri_jog_1_2.png + + +lri\lri_jog_1_3.png + + +lri\lri_jog_1_4.png + + +lri\lri_jog_1_5.png + + +lri\lri_jog_2_0.png + + +lri\lri_jog_2_1.png + + +lri\lri_jog_2_2.png + + +lri\lri_jog_2_3.png + + +lri\lri_jog_2_4.png + + +lri\lri_jog_2_5.png + + +lri\lri_jog_3_0.png + + +lri\lri_jog_3_1.png + + +lri\lri_jog_3_2.png + + +lri\lri_jog_3_3.png + + +lri\lri_jog_3_4.png + + +lri\lri_jog_3_5.png + + +lri\lri_jog_4_0.png + + +lri\lri_jog_4_1.png + + +lri\lri_jog_4_2.png + + +lri\lri_jog_4_3.png + + +lri\lri_jog_4_4.png + + +lri\lri_jog_4_5.png + + +lri\lri_jog_5_0.png + + +lri\lri_jog_5_1.png + + +lri\lri_jog_5_2.png + + +lri\lri_jog_5_3.png + + +lri\lri_jog_5_4.png + + +lri\lri_jog_5_5.png + + +lri\lri_jog_6_0.png + + +lri\lri_jog_6_1.png + + +lri\lri_jog_6_2.png + + +lri\lri_jog_6_3.png + + +lri\lri_jog_6_4.png + + +lri\lri_jog_6_5.png + + +lri\lri_jog_7_0.png + + +lri\lri_jog_7_1.png + + +lri\lri_jog_7_2.png + + +lri\lri_jog_7_3.png + + +lri\lri_jog_7_4.png + + +lri\lri_jog_0_0.png + + +lri\lri_jog_0_4.png + + +lri\lri_die_3_8.png + + +lri\lri_die_3_1.png + + +lri\lri_die_3_2.png + + +lri\lri_die_3_3.png + + +lri\lri_die_3_4.png + + +lri\lri_die_3_5.png + + +lri\lri_die_3_6.png + + +lri\lri_die_3_7.png + + +lri\lri_die_3_0.png + + +lri\lri_fire_7_2.png + + +lri\lri_fire_0_1.png + + +lri\lri_fire_0_2.png + + +lri\lri_fire_1_0.png + + +lri\lri_fire_1_1.png + + +lri\lri_fire_1_2.png + + +lri\lri_fire_2_0.png + + +lri\lri_fire_2_1.png + + +lri\lri_fire_2_2.png + + +lri\lri_fire_3_0.png + + +lri\lri_fire_3_1.png + + +lri\lri_fire_3_2.png + + +lri\lri_fire_4_0.png + + +lri\lri_fire_4_1.png + + +lri\lri_fire_4_2.png + + +lri\lri_fire_5_0.png + + +lri\lri_fire_5_1.png + + +lri\lri_fire_5_2.png + + +lri\lri_fire_6_0.png + + +lri\lri_fire_6_1.png + + +lri\lri_fire_6_2.png + + +lri\lri_fire_7_0.png + + +lri\lri_fire_7_1.png + + +lri\lri_fire_0_0.png + + +lri\lri_idle_7_5.png + + +lri\lri_idle_0_1.png + + +lri\lri_idle_0_2.png + + +lri\lri_idle_0_3.png + + +lri\lri_idle_0_4.png + + +lri\lri_idle_1_0.png + + +lri\lri_idle_1_1.png + + +lri\lri_idle_1_2.png + + +lri\lri_idle_2_0.png + + +lri\lri_idle_2_1.png + + +lri\lri_idle_2_2.png + + +lri\lri_idle_2_3.png + + +lri\lri_idle_3_0.png + + +lri\lri_idle_3_1.png + + +lri\lri_idle_3_2.png + + +lri\lri_idle_4_0.png + + +lri\lri_idle_4_1.png + + +lri\lri_idle_4_2.png + + +lri\lri_idle_5_0.png + + +lri\lri_idle_5_1.png + + +lri\lri_idle_5_2.png + + +lri\lri_idle_5_3.png + + +lri\lri_idle_6_0.png + + +lri\lri_idle_6_1.png + + +lri\lri_idle_6_2.png + + +lri\lri_idle_6_3.png + + +lri\lri_idle_7_0.png + + +lri\lri_idle_7_1.png + + +lri\lri_idle_7_2.png + + +lri\lri_idle_7_3.png + + +lri\lri_idle_7_4.png + + +lri\lri_idle_0_0.png + + +die 3 +0 +9 +<_x0030_ href="#ref-267"/> +<_x0031_ href="#ref-268"/> +<_x0032_ href="#ref-269"/> +<_x0033_ href="#ref-270"/> +<_x0034_ href="#ref-271"/> +<_x0035_ href="#ref-272"/> +<_x0036_ href="#ref-273"/> +<_x0037_ href="#ref-274"/> +<_x0038_ href="#ref-275"/> + + +fire 0 +0 +3 +<_x0030_ href="#ref-277"/> +<_x0031_ href="#ref-278"/> +<_x0032_ href="#ref-279"/> + + +fire 1 +0 +3 +<_x0030_ href="#ref-281"/> +<_x0031_ href="#ref-282"/> +<_x0032_ href="#ref-283"/> + + +fire 2 +0 +3 +<_x0030_ href="#ref-285"/> +<_x0031_ href="#ref-286"/> +<_x0032_ href="#ref-287"/> + + +fire 3 +0 +3 +<_x0030_ href="#ref-289"/> +<_x0031_ href="#ref-290"/> +<_x0032_ href="#ref-291"/> + + +fire 4 +0 +3 +<_x0030_ href="#ref-293"/> +<_x0031_ href="#ref-294"/> +<_x0032_ href="#ref-295"/> + + +fire 5 +0 +3 +<_x0030_ href="#ref-297"/> +<_x0031_ href="#ref-298"/> +<_x0032_ href="#ref-299"/> + + +fire 6 +0 +3 +<_x0030_ href="#ref-301"/> +<_x0031_ href="#ref-302"/> +<_x0032_ href="#ref-303"/> + + +fire 7 +0 +3 +<_x0030_ href="#ref-305"/> +<_x0031_ href="#ref-306"/> +<_x0032_ href="#ref-307"/> + + +idle 0 +0 +8 +<_x0030_ href="#ref-309"/> +<_x0031_ href="#ref-310"/> +<_x0032_ href="#ref-311"/> +<_x0033_ href="#ref-312"/> +<_x0034_ href="#ref-313"/> +<_x0035_ href="#ref-314"/> +<_x0036_ href="#ref-315"/> +<_x0037_ href="#ref-316"/> + + +idle 1 +0 +4 +<_x0030_ href="#ref-318"/> +<_x0031_ href="#ref-319"/> +<_x0032_ href="#ref-320"/> +<_x0033_ href="#ref-321"/> + + +idle 2 +0 +6 +<_x0030_ href="#ref-323"/> +<_x0031_ href="#ref-324"/> +<_x0032_ href="#ref-325"/> +<_x0033_ href="#ref-326"/> +<_x0034_ href="#ref-327"/> +<_x0035_ href="#ref-328"/> + + +idle 3 +0 +4 +<_x0030_ href="#ref-330"/> +<_x0031_ href="#ref-331"/> +<_x0032_ href="#ref-332"/> +<_x0033_ href="#ref-333"/> + + +idle 4 +0 +4 +<_x0030_ href="#ref-335"/> +<_x0031_ href="#ref-336"/> +<_x0032_ href="#ref-337"/> +<_x0033_ href="#ref-338"/> + + +idle 5 +0 +6 +<_x0030_ href="#ref-340"/> +<_x0031_ href="#ref-341"/> +<_x0032_ href="#ref-342"/> +<_x0033_ href="#ref-343"/> +<_x0034_ href="#ref-344"/> +<_x0035_ href="#ref-345"/> + + +idle 6 +0 +6 +<_x0030_ href="#ref-347"/> +<_x0031_ href="#ref-348"/> +<_x0032_ href="#ref-349"/> +<_x0033_ href="#ref-350"/> +<_x0034_ href="#ref-351"/> +<_x0035_ href="#ref-352"/> + + +idle 7 +0 +6 +<_x0030_ href="#ref-354"/> +<_x0031_ href="#ref-355"/> +<_x0032_ href="#ref-356"/> +<_x0033_ href="#ref-357"/> +<_x0034_ href="#ref-358"/> +<_x0035_ href="#ref-359"/> + + +jog 0 +0 +6 +<_x0030_ href="#ref-361"/> +<_x0031_ href="#ref-362"/> +<_x0032_ href="#ref-363"/> +<_x0033_ href="#ref-364"/> +<_x0034_ href="#ref-365"/> +<_x0035_ href="#ref-366"/> + + +jog 1 +0 +6 +<_x0030_ href="#ref-368"/> +<_x0031_ href="#ref-369"/> +<_x0032_ href="#ref-370"/> +<_x0033_ href="#ref-371"/> +<_x0034_ href="#ref-372"/> +<_x0035_ href="#ref-373"/> + + +jog 2 +0 +6 +<_x0030_ href="#ref-375"/> +<_x0031_ href="#ref-376"/> +<_x0032_ href="#ref-377"/> +<_x0033_ href="#ref-378"/> +<_x0034_ href="#ref-379"/> +<_x0035_ href="#ref-380"/> + + +jog 3 +0 +6 +<_x0030_ href="#ref-382"/> +<_x0031_ href="#ref-383"/> +<_x0032_ href="#ref-384"/> +<_x0033_ href="#ref-385"/> +<_x0034_ href="#ref-386"/> +<_x0035_ href="#ref-387"/> + + +jog 4 +0 +6 +<_x0030_ href="#ref-389"/> +<_x0031_ href="#ref-390"/> +<_x0032_ href="#ref-391"/> +<_x0033_ href="#ref-392"/> +<_x0034_ href="#ref-393"/> +<_x0035_ href="#ref-394"/> + + +jog 5 +0 +6 +<_x0030_ href="#ref-396"/> +<_x0031_ href="#ref-397"/> +<_x0032_ href="#ref-398"/> +<_x0033_ href="#ref-399"/> +<_x0034_ href="#ref-400"/> +<_x0035_ href="#ref-401"/> + + +jog 6 +0 +6 +<_x0030_ href="#ref-403"/> +<_x0031_ href="#ref-404"/> +<_x0032_ href="#ref-405"/> +<_x0033_ href="#ref-406"/> +<_x0034_ href="#ref-407"/> +<_x0035_ href="#ref-408"/> + + +jog 7 +0 +6 +<_x0030_ href="#ref-410"/> +<_x0031_ href="#ref-411"/> +<_x0032_ href="#ref-412"/> +<_x0033_ href="#ref-413"/> +<_x0034_ href="#ref-414"/> +<_x0035_ href="#ref-415"/> + + +icon +0 +1 +<_x0030_ href="#ref-417"/> + + +help +0 +1 +<_x0030_ href="#ref-419"/> + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +2 +-13 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +9 +-8 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +11 +-5 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +5 +3 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-3 +2 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-6 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-10 +-6 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-4 +-12 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +20 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +15 + +0 +0 + + + + +0 + +0 +0 + + + + +20 + +0 +0 + + + + +0 + +0 +0 + + + + +20 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +15 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +16 + +0 +0 + + + + +0 + +0 +0 + + + + +20 + +0 +0 + + + + +0 + +0 +0 + + + + +16 + +0 +0 + + + + +0 + +0 +0 + + + + +23 + +0 +0 + + + + +0 + +0 +0 + + + + +6 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +17 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +5 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +21 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +10 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + +<_x0030_ href="#ref-548"/> + + +<_x0030_ href="#ref-549"/> + + +<_x0030_ href="#ref-550"/> + + +<_x0030_ href="#ref-551"/> + + +<_x0030_ href="#ref-552"/> + + +<_x0030_ href="#ref-553"/> + + +<_x0030_ href="#ref-554"/> + + +<_x0030_ href="#ref-555"/> + + +<_x0030_ href="#ref-556"/> + + +<_x0030_ href="#ref-557"/> + + +<_x0030_ href="#ref-558"/> + + +<_x0030_ href="#ref-559"/> + + +<_x0030_ href="#ref-560"/> + + +<_x0030_ href="#ref-561"/> + + +<_x0030_ href="#ref-562"/> + + +<_x0030_ href="#ref-563"/> + + +<_x0030_ href="#ref-564"/> + + +<_x0030_ href="#ref-565"/> + + +<_x0030_ href="#ref-566"/> + + +<_x0030_ href="#ref-567"/> + + +<_x0030_ href="#ref-568"/> + + +<_x0030_ href="#ref-569"/> + + +<_x0030_ href="#ref-570"/> + + +<_x0030_ href="#ref-571"/> + + +<_x0030_ href="#ref-572"/> + + +<_x0030_ href="#ref-573"/> + + +<_x0030_ href="#ref-574"/> + + +<_x0030_ href="#ref-575"/> + + +<_x0030_ href="#ref-576"/> + + +<_x0030_ href="#ref-577"/> + + +<_x0030_ href="#ref-578"/> + + +<_x0030_ href="#ref-579"/> + + +<_x0030_ href="#ref-580"/> + + +<_x0030_ href="#ref-581"/> + + +<_x0030_ href="#ref-582"/> + + +<_x0030_ href="#ref-583"/> + + +<_x0030_ href="#ref-584"/> + + +<_x0030_ href="#ref-585"/> + + +<_x0030_ href="#ref-586"/> + + +<_x0030_ href="#ref-587"/> + + +<_x0030_ href="#ref-588"/> + + +<_x0030_ href="#ref-589"/> + + +<_x0030_ href="#ref-590"/> + + +<_x0030_ href="#ref-591"/> + + +<_x0030_ href="#ref-592"/> + + +<_x0030_ href="#ref-593"/> + + +<_x0030_ href="#ref-594"/> + + +<_x0030_ href="#ref-595"/> + + +<_x0030_ href="#ref-596"/> + + +<_x0030_ href="#ref-597"/> + + +<_x0030_ href="#ref-598"/> + + +<_x0030_ href="#ref-599"/> + + +<_x0030_ href="#ref-600"/> + + +<_x0030_ href="#ref-601"/> + + +<_x0030_ href="#ref-602"/> + + +<_x0030_ href="#ref-603"/> + + +<_x0030_ href="#ref-604"/> + + +<_x0030_ href="#ref-605"/> + + +<_x0030_ href="#ref-606"/> + + +<_x0030_ href="#ref-607"/> + + +<_x0030_ href="#ref-608"/> + + +<_x0030_ href="#ref-609"/> + + +<_x0030_ href="#ref-610"/> + + +<_x0030_ href="#ref-611"/> + + +<_x0030_ href="#ref-612"/> + + +<_x0030_ href="#ref-613"/> + + +<_x0030_ href="#ref-614"/> + + +<_x0030_ href="#ref-615"/> + + +<_x0030_ href="#ref-616"/> + + +<_x0030_ href="#ref-617"/> + + +<_x0030_ href="#ref-618"/> + + +<_x0030_ href="#ref-619"/> + + +<_x0030_ href="#ref-620"/> + + +<_x0030_ href="#ref-621"/> + + +<_x0030_ href="#ref-622"/> + + +<_x0030_ href="#ref-623"/> + + +<_x0030_ href="#ref-624"/> + + +<_x0030_ href="#ref-625"/> + + +<_x0030_ href="#ref-626"/> + + +<_x0030_ href="#ref-627"/> + + +<_x0030_ href="#ref-628"/> + + +<_x0030_ href="#ref-629"/> + + +<_x0030_ href="#ref-630"/> + + +<_x0030_ href="#ref-631"/> + + +<_x0030_ href="#ref-632"/> + + +<_x0030_ href="#ref-633"/> + + +<_x0030_ href="#ref-634"/> + + +<_x0030_ href="#ref-635"/> + + +<_x0030_ href="#ref-636"/> + + +<_x0030_ href="#ref-637"/> + + +<_x0030_ href="#ref-638"/> + + +<_x0030_ href="#ref-639"/> + + +<_x0030_ href="#ref-640"/> + + +<_x0030_ href="#ref-641"/> + + +<_x0030_ href="#ref-642"/> + + +<_x0030_ href="#ref-643"/> + + +<_x0030_ href="#ref-644"/> + + +<_x0030_ href="#ref-645"/> + + +<_x0030_ href="#ref-646"/> + + +<_x0030_ href="#ref-647"/> + + +<_x0030_ href="#ref-648"/> + + +<_x0030_ href="#ref-649"/> + + +<_x0030_ href="#ref-650"/> + + +<_x0030_ href="#ref-651"/> + + +<_x0030_ href="#ref-652"/> + + +<_x0030_ href="#ref-653"/> + + +<_x0030_ href="#ref-654"/> + + +<_x0030_ href="#ref-655"/> + + +<_x0030_ href="#ref-656"/> + + +<_x0030_ href="#ref-657"/> + + +<_x0030_ href="#ref-658"/> + + +<_x0030_ href="#ref-659"/> + + +<_x0030_ href="#ref-660"/> + + +<_x0030_ href="#ref-661"/> + + +<_x0030_ href="#ref-662"/> + + +<_x0030_ href="#ref-663"/> + + +<_x0030_ href="#ref-664"/> + + +<_x0030_ href="#ref-665"/> + + +<_x0030_ href="#ref-666"/> + + +<_x0030_ href="#ref-667"/> + + +<_x0030_ href="#ref-668"/> + + +<_x0030_ href="#ref-669"/> + + +<_x0030_ href="#ref-670"/> + + +<_x0030_ href="#ref-671"/> + + +<_x0030_ href="#ref-672"/> + + +<_x0030_ href="#ref-673"/> + + +<_x0030_ href="#ref-674"/> + + + +14 +8 + + + + + +16 +10 + + + + + +17 +9 + + + + + +19 +9 + + + + + +20 +11 + + + + + +21 +9 + + + + + +23 +8 + + + + + +23 +8 + + + + + +24 +8 + + + + + +13 +15 + + + + + +13 +15 + + + + + +13 +15 + + + + + +10 +13 + + + + + +10 +13 + + + + + +10 +13 + + + + + +9 +14 + + + + + +9 +14 + + + + + +9 +14 + + + + + +12 +12 + + + + + +12 +12 + + + + + +12 +12 + + + + + +12 +12 + + + + + +12 +12 + + + + + +12 +12 + + + + + +11 +12 + + + + + +11 +12 + + + + + +11 +12 + + + + + +13 +13 + + + + + +13 +13 + + + + + +13 +13 + + + + + +11 +13 + + + + + +11 +13 + + + + + +11 +13 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +7 +8 + + + + + +7 +8 + + + + + +7 +8 + + + + + +7 +8 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +9 +7 + + + + + +9 +7 + + + + + +9 +7 + + + + + +9 +7 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +6 +8 + + + + + +6 +8 + + + + + +6 +8 + + + + + +6 +8 + + + + + +6 +8 + + + + + +6 +8 + + + + + +6 +9 + + + + + +6 +9 + + + + + +6 +9 + + + + + +6 +9 + + + + + +6 +9 + + + + + +6 +9 + + + + + +6 +8 + + + + + +6 +8 + + + + + +6 +8 + + + + + +6 +8 + + + + + +6 +8 + + + + + +6 +8 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +9 +8 + + + + + +9 +8 + + + + + +9 +8 + + + + + +9 +8 + + + + + +9 +8 + + + + + +9 +8 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +9 +8 + + + + + +9 +8 + + + + + +9 +8 + + + + + +9 +8 + + + + + +9 +8 + + + + + +9 +8 + + + + + +8 +8 + + + + + +8 +8 + + + + + +8 +8 + + + + + +8 +8 + + + + + +8 +8 + + + + + +8 +8 + + + + + +6 +8 + + + + + +6 +8 + + + + + +6 +8 + + + + + +6 +8 + + + + + +6 +8 + + + + + +6 +8 + + + + + +7 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +6 +8 + + + + + +6 +8 + + + + + +6 +8 + + + + + +6 +8 + + + + + +6 +8 + + + + + +6 +8 + + + + + +4 +6 + + + + + +8 +9 + + + + + diff --git a/data/art824/lri/lri_die_3_0.png b/data/art824/lri/lri_die_3_0.png new file mode 100644 index 0000000..8f9caad Binary files /dev/null and b/data/art824/lri/lri_die_3_0.png differ diff --git a/data/art824/lri/lri_die_3_1.png b/data/art824/lri/lri_die_3_1.png new file mode 100644 index 0000000..0523f16 Binary files /dev/null and b/data/art824/lri/lri_die_3_1.png differ diff --git a/data/art824/lri/lri_die_3_2.png b/data/art824/lri/lri_die_3_2.png new file mode 100644 index 0000000..b029b84 Binary files /dev/null and b/data/art824/lri/lri_die_3_2.png differ diff --git a/data/art824/lri/lri_die_3_3.png b/data/art824/lri/lri_die_3_3.png new file mode 100644 index 0000000..0a92893 Binary files /dev/null and b/data/art824/lri/lri_die_3_3.png differ diff --git a/data/art824/lri/lri_die_3_4.png b/data/art824/lri/lri_die_3_4.png new file mode 100644 index 0000000..4cc7848 Binary files /dev/null and b/data/art824/lri/lri_die_3_4.png differ diff --git a/data/art824/lri/lri_die_3_5.png b/data/art824/lri/lri_die_3_5.png new file mode 100644 index 0000000..33069c0 Binary files /dev/null and b/data/art824/lri/lri_die_3_5.png differ diff --git a/data/art824/lri/lri_die_3_6.png b/data/art824/lri/lri_die_3_6.png new file mode 100644 index 0000000..90f2df3 Binary files /dev/null and b/data/art824/lri/lri_die_3_6.png differ diff --git a/data/art824/lri/lri_die_3_7.png b/data/art824/lri/lri_die_3_7.png new file mode 100644 index 0000000..21c0b8d Binary files /dev/null and b/data/art824/lri/lri_die_3_7.png differ diff --git a/data/art824/lri/lri_die_3_8.png b/data/art824/lri/lri_die_3_8.png new file mode 100644 index 0000000..fda9b75 Binary files /dev/null and b/data/art824/lri/lri_die_3_8.png differ diff --git a/data/art824/lri/lri_fire_0_0.png b/data/art824/lri/lri_fire_0_0.png new file mode 100644 index 0000000..cb116fe Binary files /dev/null and b/data/art824/lri/lri_fire_0_0.png differ diff --git a/data/art824/lri/lri_fire_0_1.png b/data/art824/lri/lri_fire_0_1.png new file mode 100644 index 0000000..54631ca Binary files /dev/null and b/data/art824/lri/lri_fire_0_1.png differ diff --git a/data/art824/lri/lri_fire_0_2.png b/data/art824/lri/lri_fire_0_2.png new file mode 100644 index 0000000..b83a686 Binary files /dev/null and b/data/art824/lri/lri_fire_0_2.png differ diff --git a/data/art824/lri/lri_fire_1_0.png b/data/art824/lri/lri_fire_1_0.png new file mode 100644 index 0000000..e08019a Binary files /dev/null and b/data/art824/lri/lri_fire_1_0.png differ diff --git a/data/art824/lri/lri_fire_1_1.png b/data/art824/lri/lri_fire_1_1.png new file mode 100644 index 0000000..539436c Binary files /dev/null and b/data/art824/lri/lri_fire_1_1.png differ diff --git a/data/art824/lri/lri_fire_1_2.png b/data/art824/lri/lri_fire_1_2.png new file mode 100644 index 0000000..4118057 Binary files /dev/null and b/data/art824/lri/lri_fire_1_2.png differ diff --git a/data/art824/lri/lri_fire_2_0.png b/data/art824/lri/lri_fire_2_0.png new file mode 100644 index 0000000..38763ae Binary files /dev/null and b/data/art824/lri/lri_fire_2_0.png differ diff --git a/data/art824/lri/lri_fire_2_1.png b/data/art824/lri/lri_fire_2_1.png new file mode 100644 index 0000000..9a811c6 Binary files /dev/null and b/data/art824/lri/lri_fire_2_1.png differ diff --git a/data/art824/lri/lri_fire_2_2.png b/data/art824/lri/lri_fire_2_2.png new file mode 100644 index 0000000..8212762 Binary files /dev/null and b/data/art824/lri/lri_fire_2_2.png differ diff --git a/data/art824/lri/lri_fire_3_0.png b/data/art824/lri/lri_fire_3_0.png new file mode 100644 index 0000000..1d9e171 Binary files /dev/null and b/data/art824/lri/lri_fire_3_0.png differ diff --git a/data/art824/lri/lri_fire_3_1.png b/data/art824/lri/lri_fire_3_1.png new file mode 100644 index 0000000..6bb2573 Binary files /dev/null and b/data/art824/lri/lri_fire_3_1.png differ diff --git a/data/art824/lri/lri_fire_3_2.png b/data/art824/lri/lri_fire_3_2.png new file mode 100644 index 0000000..ae84d9a Binary files /dev/null and b/data/art824/lri/lri_fire_3_2.png differ diff --git a/data/art824/lri/lri_fire_4_0.png b/data/art824/lri/lri_fire_4_0.png new file mode 100644 index 0000000..c0618a9 Binary files /dev/null and b/data/art824/lri/lri_fire_4_0.png differ diff --git a/data/art824/lri/lri_fire_4_1.png b/data/art824/lri/lri_fire_4_1.png new file mode 100644 index 0000000..a8b9875 Binary files /dev/null and b/data/art824/lri/lri_fire_4_1.png differ diff --git a/data/art824/lri/lri_fire_4_2.png b/data/art824/lri/lri_fire_4_2.png new file mode 100644 index 0000000..c191222 Binary files /dev/null and b/data/art824/lri/lri_fire_4_2.png differ diff --git a/data/art824/lri/lri_fire_5_0.png b/data/art824/lri/lri_fire_5_0.png new file mode 100644 index 0000000..663fd5e Binary files /dev/null and b/data/art824/lri/lri_fire_5_0.png differ diff --git a/data/art824/lri/lri_fire_5_1.png b/data/art824/lri/lri_fire_5_1.png new file mode 100644 index 0000000..38169bb Binary files /dev/null and b/data/art824/lri/lri_fire_5_1.png differ diff --git a/data/art824/lri/lri_fire_5_2.png b/data/art824/lri/lri_fire_5_2.png new file mode 100644 index 0000000..722a7b8 Binary files /dev/null and b/data/art824/lri/lri_fire_5_2.png differ diff --git a/data/art824/lri/lri_fire_6_0.png b/data/art824/lri/lri_fire_6_0.png new file mode 100644 index 0000000..a953ce9 Binary files /dev/null and b/data/art824/lri/lri_fire_6_0.png differ diff --git a/data/art824/lri/lri_fire_6_1.png b/data/art824/lri/lri_fire_6_1.png new file mode 100644 index 0000000..c9cf826 Binary files /dev/null and b/data/art824/lri/lri_fire_6_1.png differ diff --git a/data/art824/lri/lri_fire_6_2.png b/data/art824/lri/lri_fire_6_2.png new file mode 100644 index 0000000..3973d95 Binary files /dev/null and b/data/art824/lri/lri_fire_6_2.png differ diff --git a/data/art824/lri/lri_fire_7_0.png b/data/art824/lri/lri_fire_7_0.png new file mode 100644 index 0000000..a8d85af Binary files /dev/null and b/data/art824/lri/lri_fire_7_0.png differ diff --git a/data/art824/lri/lri_fire_7_1.png b/data/art824/lri/lri_fire_7_1.png new file mode 100644 index 0000000..e2fc205 Binary files /dev/null and b/data/art824/lri/lri_fire_7_1.png differ diff --git a/data/art824/lri/lri_fire_7_2.png b/data/art824/lri/lri_fire_7_2.png new file mode 100644 index 0000000..8f1880b Binary files /dev/null and b/data/art824/lri/lri_fire_7_2.png differ diff --git a/data/art824/lri/lri_idle_0_0.png b/data/art824/lri/lri_idle_0_0.png new file mode 100644 index 0000000..7edad56 Binary files /dev/null and b/data/art824/lri/lri_idle_0_0.png differ diff --git a/data/art824/lri/lri_idle_0_1.png b/data/art824/lri/lri_idle_0_1.png new file mode 100644 index 0000000..c57fb3b Binary files /dev/null and b/data/art824/lri/lri_idle_0_1.png differ diff --git a/data/art824/lri/lri_idle_0_2.png b/data/art824/lri/lri_idle_0_2.png new file mode 100644 index 0000000..702f7b2 Binary files /dev/null and b/data/art824/lri/lri_idle_0_2.png differ diff --git a/data/art824/lri/lri_idle_0_3.png b/data/art824/lri/lri_idle_0_3.png new file mode 100644 index 0000000..d7a00d6 Binary files /dev/null and b/data/art824/lri/lri_idle_0_3.png differ diff --git a/data/art824/lri/lri_idle_0_4.png b/data/art824/lri/lri_idle_0_4.png new file mode 100644 index 0000000..935283f Binary files /dev/null and b/data/art824/lri/lri_idle_0_4.png differ diff --git a/data/art824/lri/lri_idle_1_0.png b/data/art824/lri/lri_idle_1_0.png new file mode 100644 index 0000000..bbf696a Binary files /dev/null and b/data/art824/lri/lri_idle_1_0.png differ diff --git a/data/art824/lri/lri_idle_1_1.png b/data/art824/lri/lri_idle_1_1.png new file mode 100644 index 0000000..3dd9cb2 Binary files /dev/null and b/data/art824/lri/lri_idle_1_1.png differ diff --git a/data/art824/lri/lri_idle_1_2.png b/data/art824/lri/lri_idle_1_2.png new file mode 100644 index 0000000..077c2c6 Binary files /dev/null and b/data/art824/lri/lri_idle_1_2.png differ diff --git a/data/art824/lri/lri_idle_2_0.png b/data/art824/lri/lri_idle_2_0.png new file mode 100644 index 0000000..4b594aa Binary files /dev/null and b/data/art824/lri/lri_idle_2_0.png differ diff --git a/data/art824/lri/lri_idle_2_1.png b/data/art824/lri/lri_idle_2_1.png new file mode 100644 index 0000000..e889ba2 Binary files /dev/null and b/data/art824/lri/lri_idle_2_1.png differ diff --git a/data/art824/lri/lri_idle_2_2.png b/data/art824/lri/lri_idle_2_2.png new file mode 100644 index 0000000..35d610a Binary files /dev/null and b/data/art824/lri/lri_idle_2_2.png differ diff --git a/data/art824/lri/lri_idle_2_3.png b/data/art824/lri/lri_idle_2_3.png new file mode 100644 index 0000000..f8dc2a8 Binary files /dev/null and b/data/art824/lri/lri_idle_2_3.png differ diff --git a/data/art824/lri/lri_idle_3_0.png b/data/art824/lri/lri_idle_3_0.png new file mode 100644 index 0000000..cdb9fab Binary files /dev/null and b/data/art824/lri/lri_idle_3_0.png differ diff --git a/data/art824/lri/lri_idle_3_1.png b/data/art824/lri/lri_idle_3_1.png new file mode 100644 index 0000000..accd33e Binary files /dev/null and b/data/art824/lri/lri_idle_3_1.png differ diff --git a/data/art824/lri/lri_idle_3_2.png b/data/art824/lri/lri_idle_3_2.png new file mode 100644 index 0000000..1859459 Binary files /dev/null and b/data/art824/lri/lri_idle_3_2.png differ diff --git a/data/art824/lri/lri_idle_4_0.png b/data/art824/lri/lri_idle_4_0.png new file mode 100644 index 0000000..478d955 Binary files /dev/null and b/data/art824/lri/lri_idle_4_0.png differ diff --git a/data/art824/lri/lri_idle_4_1.png b/data/art824/lri/lri_idle_4_1.png new file mode 100644 index 0000000..0279ac5 Binary files /dev/null and b/data/art824/lri/lri_idle_4_1.png differ diff --git a/data/art824/lri/lri_idle_4_2.png b/data/art824/lri/lri_idle_4_2.png new file mode 100644 index 0000000..67f14a6 Binary files /dev/null and b/data/art824/lri/lri_idle_4_2.png differ diff --git a/data/art824/lri/lri_idle_5_0.png b/data/art824/lri/lri_idle_5_0.png new file mode 100644 index 0000000..df7aa66 Binary files /dev/null and b/data/art824/lri/lri_idle_5_0.png differ diff --git a/data/art824/lri/lri_idle_5_1.png b/data/art824/lri/lri_idle_5_1.png new file mode 100644 index 0000000..05af14c Binary files /dev/null and b/data/art824/lri/lri_idle_5_1.png differ diff --git a/data/art824/lri/lri_idle_5_2.png b/data/art824/lri/lri_idle_5_2.png new file mode 100644 index 0000000..5e8e05b Binary files /dev/null and b/data/art824/lri/lri_idle_5_2.png differ diff --git a/data/art824/lri/lri_idle_5_3.png b/data/art824/lri/lri_idle_5_3.png new file mode 100644 index 0000000..40001d2 Binary files /dev/null and b/data/art824/lri/lri_idle_5_3.png differ diff --git a/data/art824/lri/lri_idle_6_0.png b/data/art824/lri/lri_idle_6_0.png new file mode 100644 index 0000000..fe1e7f7 Binary files /dev/null and b/data/art824/lri/lri_idle_6_0.png differ diff --git a/data/art824/lri/lri_idle_6_1.png b/data/art824/lri/lri_idle_6_1.png new file mode 100644 index 0000000..c354072 Binary files /dev/null and b/data/art824/lri/lri_idle_6_1.png differ diff --git a/data/art824/lri/lri_idle_6_2.png b/data/art824/lri/lri_idle_6_2.png new file mode 100644 index 0000000..97a6b54 Binary files /dev/null and b/data/art824/lri/lri_idle_6_2.png differ diff --git a/data/art824/lri/lri_idle_6_3.png b/data/art824/lri/lri_idle_6_3.png new file mode 100644 index 0000000..950b4aa Binary files /dev/null and b/data/art824/lri/lri_idle_6_3.png differ diff --git a/data/art824/lri/lri_idle_7_0.png b/data/art824/lri/lri_idle_7_0.png new file mode 100644 index 0000000..87250e0 Binary files /dev/null and b/data/art824/lri/lri_idle_7_0.png differ diff --git a/data/art824/lri/lri_idle_7_1.png b/data/art824/lri/lri_idle_7_1.png new file mode 100644 index 0000000..3edd8de Binary files /dev/null and b/data/art824/lri/lri_idle_7_1.png differ diff --git a/data/art824/lri/lri_idle_7_2.png b/data/art824/lri/lri_idle_7_2.png new file mode 100644 index 0000000..0426e04 Binary files /dev/null and b/data/art824/lri/lri_idle_7_2.png differ diff --git a/data/art824/lri/lri_idle_7_3.png b/data/art824/lri/lri_idle_7_3.png new file mode 100644 index 0000000..230a65b Binary files /dev/null and b/data/art824/lri/lri_idle_7_3.png differ diff --git a/data/art824/lri/lri_idle_7_4.png b/data/art824/lri/lri_idle_7_4.png new file mode 100644 index 0000000..b94d2e9 Binary files /dev/null and b/data/art824/lri/lri_idle_7_4.png differ diff --git a/data/art824/lri/lri_idle_7_5.png b/data/art824/lri/lri_idle_7_5.png new file mode 100644 index 0000000..82c6b06 Binary files /dev/null and b/data/art824/lri/lri_idle_7_5.png differ diff --git a/data/art824/lri/lri_jog_0_0.png b/data/art824/lri/lri_jog_0_0.png new file mode 100644 index 0000000..4b07fd3 Binary files /dev/null and b/data/art824/lri/lri_jog_0_0.png differ diff --git a/data/art824/lri/lri_jog_0_1.png b/data/art824/lri/lri_jog_0_1.png new file mode 100644 index 0000000..7edad56 Binary files /dev/null and b/data/art824/lri/lri_jog_0_1.png differ diff --git a/data/art824/lri/lri_jog_0_2.png b/data/art824/lri/lri_jog_0_2.png new file mode 100644 index 0000000..f958dbe Binary files /dev/null and b/data/art824/lri/lri_jog_0_2.png differ diff --git a/data/art824/lri/lri_jog_0_3.png b/data/art824/lri/lri_jog_0_3.png new file mode 100644 index 0000000..50d46c2 Binary files /dev/null and b/data/art824/lri/lri_jog_0_3.png differ diff --git a/data/art824/lri/lri_jog_0_4.png b/data/art824/lri/lri_jog_0_4.png new file mode 100644 index 0000000..a44f919 Binary files /dev/null and b/data/art824/lri/lri_jog_0_4.png differ diff --git a/data/art824/lri/lri_jog_0_5.png b/data/art824/lri/lri_jog_0_5.png new file mode 100644 index 0000000..7b4c03c Binary files /dev/null and b/data/art824/lri/lri_jog_0_5.png differ diff --git a/data/art824/lri/lri_jog_1_0.png b/data/art824/lri/lri_jog_1_0.png new file mode 100644 index 0000000..4516d40 Binary files /dev/null and b/data/art824/lri/lri_jog_1_0.png differ diff --git a/data/art824/lri/lri_jog_1_1.png b/data/art824/lri/lri_jog_1_1.png new file mode 100644 index 0000000..5ff5b37 Binary files /dev/null and b/data/art824/lri/lri_jog_1_1.png differ diff --git a/data/art824/lri/lri_jog_1_2.png b/data/art824/lri/lri_jog_1_2.png new file mode 100644 index 0000000..4b7159a Binary files /dev/null and b/data/art824/lri/lri_jog_1_2.png differ diff --git a/data/art824/lri/lri_jog_1_3.png b/data/art824/lri/lri_jog_1_3.png new file mode 100644 index 0000000..06be712 Binary files /dev/null and b/data/art824/lri/lri_jog_1_3.png differ diff --git a/data/art824/lri/lri_jog_1_4.png b/data/art824/lri/lri_jog_1_4.png new file mode 100644 index 0000000..b92c476 Binary files /dev/null and b/data/art824/lri/lri_jog_1_4.png differ diff --git a/data/art824/lri/lri_jog_1_5.png b/data/art824/lri/lri_jog_1_5.png new file mode 100644 index 0000000..ad2c382 Binary files /dev/null and b/data/art824/lri/lri_jog_1_5.png differ diff --git a/data/art824/lri/lri_jog_2_0.png b/data/art824/lri/lri_jog_2_0.png new file mode 100644 index 0000000..b1db0fc Binary files /dev/null and b/data/art824/lri/lri_jog_2_0.png differ diff --git a/data/art824/lri/lri_jog_2_1.png b/data/art824/lri/lri_jog_2_1.png new file mode 100644 index 0000000..3d6d873 Binary files /dev/null and b/data/art824/lri/lri_jog_2_1.png differ diff --git a/data/art824/lri/lri_jog_2_2.png b/data/art824/lri/lri_jog_2_2.png new file mode 100644 index 0000000..e361653 Binary files /dev/null and b/data/art824/lri/lri_jog_2_2.png differ diff --git a/data/art824/lri/lri_jog_2_3.png b/data/art824/lri/lri_jog_2_3.png new file mode 100644 index 0000000..7975c67 Binary files /dev/null and b/data/art824/lri/lri_jog_2_3.png differ diff --git a/data/art824/lri/lri_jog_2_4.png b/data/art824/lri/lri_jog_2_4.png new file mode 100644 index 0000000..8c58b68 Binary files /dev/null and b/data/art824/lri/lri_jog_2_4.png differ diff --git a/data/art824/lri/lri_jog_2_5.png b/data/art824/lri/lri_jog_2_5.png new file mode 100644 index 0000000..153f6c7 Binary files /dev/null and b/data/art824/lri/lri_jog_2_5.png differ diff --git a/data/art824/lri/lri_jog_3_0.png b/data/art824/lri/lri_jog_3_0.png new file mode 100644 index 0000000..c387f08 Binary files /dev/null and b/data/art824/lri/lri_jog_3_0.png differ diff --git a/data/art824/lri/lri_jog_3_1.png b/data/art824/lri/lri_jog_3_1.png new file mode 100644 index 0000000..e9b0c4c Binary files /dev/null and b/data/art824/lri/lri_jog_3_1.png differ diff --git a/data/art824/lri/lri_jog_3_2.png b/data/art824/lri/lri_jog_3_2.png new file mode 100644 index 0000000..db98629 Binary files /dev/null and b/data/art824/lri/lri_jog_3_2.png differ diff --git a/data/art824/lri/lri_jog_3_3.png b/data/art824/lri/lri_jog_3_3.png new file mode 100644 index 0000000..6025b4d Binary files /dev/null and b/data/art824/lri/lri_jog_3_3.png differ diff --git a/data/art824/lri/lri_jog_3_4.png b/data/art824/lri/lri_jog_3_4.png new file mode 100644 index 0000000..1a62016 Binary files /dev/null and b/data/art824/lri/lri_jog_3_4.png differ diff --git a/data/art824/lri/lri_jog_3_5.png b/data/art824/lri/lri_jog_3_5.png new file mode 100644 index 0000000..614209d Binary files /dev/null and b/data/art824/lri/lri_jog_3_5.png differ diff --git a/data/art824/lri/lri_jog_4_0.png b/data/art824/lri/lri_jog_4_0.png new file mode 100644 index 0000000..b88fcff Binary files /dev/null and b/data/art824/lri/lri_jog_4_0.png differ diff --git a/data/art824/lri/lri_jog_4_1.png b/data/art824/lri/lri_jog_4_1.png new file mode 100644 index 0000000..bc0218c Binary files /dev/null and b/data/art824/lri/lri_jog_4_1.png differ diff --git a/data/art824/lri/lri_jog_4_2.png b/data/art824/lri/lri_jog_4_2.png new file mode 100644 index 0000000..eb59edf Binary files /dev/null and b/data/art824/lri/lri_jog_4_2.png differ diff --git a/data/art824/lri/lri_jog_4_3.png b/data/art824/lri/lri_jog_4_3.png new file mode 100644 index 0000000..b277bab Binary files /dev/null and b/data/art824/lri/lri_jog_4_3.png differ diff --git a/data/art824/lri/lri_jog_4_4.png b/data/art824/lri/lri_jog_4_4.png new file mode 100644 index 0000000..107ecc2 Binary files /dev/null and b/data/art824/lri/lri_jog_4_4.png differ diff --git a/data/art824/lri/lri_jog_4_5.png b/data/art824/lri/lri_jog_4_5.png new file mode 100644 index 0000000..7ad8fc4 Binary files /dev/null and b/data/art824/lri/lri_jog_4_5.png differ diff --git a/data/art824/lri/lri_jog_5_0.png b/data/art824/lri/lri_jog_5_0.png new file mode 100644 index 0000000..f44d137 Binary files /dev/null and b/data/art824/lri/lri_jog_5_0.png differ diff --git a/data/art824/lri/lri_jog_5_1.png b/data/art824/lri/lri_jog_5_1.png new file mode 100644 index 0000000..7ae5655 Binary files /dev/null and b/data/art824/lri/lri_jog_5_1.png differ diff --git a/data/art824/lri/lri_jog_5_2.png b/data/art824/lri/lri_jog_5_2.png new file mode 100644 index 0000000..05181c5 Binary files /dev/null and b/data/art824/lri/lri_jog_5_2.png differ diff --git a/data/art824/lri/lri_jog_5_3.png b/data/art824/lri/lri_jog_5_3.png new file mode 100644 index 0000000..8437939 Binary files /dev/null and b/data/art824/lri/lri_jog_5_3.png differ diff --git a/data/art824/lri/lri_jog_5_4.png b/data/art824/lri/lri_jog_5_4.png new file mode 100644 index 0000000..388b829 Binary files /dev/null and b/data/art824/lri/lri_jog_5_4.png differ diff --git a/data/art824/lri/lri_jog_5_5.png b/data/art824/lri/lri_jog_5_5.png new file mode 100644 index 0000000..4ec07ec Binary files /dev/null and b/data/art824/lri/lri_jog_5_5.png differ diff --git a/data/art824/lri/lri_jog_6_0.png b/data/art824/lri/lri_jog_6_0.png new file mode 100644 index 0000000..231143e Binary files /dev/null and b/data/art824/lri/lri_jog_6_0.png differ diff --git a/data/art824/lri/lri_jog_6_1.png b/data/art824/lri/lri_jog_6_1.png new file mode 100644 index 0000000..a67e670 Binary files /dev/null and b/data/art824/lri/lri_jog_6_1.png differ diff --git a/data/art824/lri/lri_jog_6_2.png b/data/art824/lri/lri_jog_6_2.png new file mode 100644 index 0000000..9b978a3 Binary files /dev/null and b/data/art824/lri/lri_jog_6_2.png differ diff --git a/data/art824/lri/lri_jog_6_3.png b/data/art824/lri/lri_jog_6_3.png new file mode 100644 index 0000000..d6b4039 Binary files /dev/null and b/data/art824/lri/lri_jog_6_3.png differ diff --git a/data/art824/lri/lri_jog_6_4.png b/data/art824/lri/lri_jog_6_4.png new file mode 100644 index 0000000..d4aae3a Binary files /dev/null and b/data/art824/lri/lri_jog_6_4.png differ diff --git a/data/art824/lri/lri_jog_6_5.png b/data/art824/lri/lri_jog_6_5.png new file mode 100644 index 0000000..785e005 Binary files /dev/null and b/data/art824/lri/lri_jog_6_5.png differ diff --git a/data/art824/lri/lri_jog_7_0.png b/data/art824/lri/lri_jog_7_0.png new file mode 100644 index 0000000..1a9bc29 Binary files /dev/null and b/data/art824/lri/lri_jog_7_0.png differ diff --git a/data/art824/lri/lri_jog_7_1.png b/data/art824/lri/lri_jog_7_1.png new file mode 100644 index 0000000..4f6c8ec Binary files /dev/null and b/data/art824/lri/lri_jog_7_1.png differ diff --git a/data/art824/lri/lri_jog_7_2.png b/data/art824/lri/lri_jog_7_2.png new file mode 100644 index 0000000..6cbae9e Binary files /dev/null and b/data/art824/lri/lri_jog_7_2.png differ diff --git a/data/art824/lri/lri_jog_7_3.png b/data/art824/lri/lri_jog_7_3.png new file mode 100644 index 0000000..7d7190c Binary files /dev/null and b/data/art824/lri/lri_jog_7_3.png differ diff --git a/data/art824/lri/lri_jog_7_4.png b/data/art824/lri/lri_jog_7_4.png new file mode 100644 index 0000000..b4aa3d2 Binary files /dev/null and b/data/art824/lri/lri_jog_7_4.png differ diff --git a/data/art824/lri/lri_jog_7_5.png b/data/art824/lri/lri_jog_7_5.png new file mode 100644 index 0000000..dcab8dd Binary files /dev/null and b/data/art824/lri/lri_jog_7_5.png differ diff --git a/data/art824/ltank.amx b/data/art824/ltank.amx new file mode 100644 index 0000000..628ac28 --- /dev/null +++ b/data/art824/ltank.amx @@ -0,0 +1,1512 @@ + + + + + +80 +true + + +<_x0030_ href="#ref-5"/> +<_x0031_ href="#ref-6"/> +<_x0032_ href="#ref-7"/> +<_x0033_ href="#ref-8"/> +<_x0034_ href="#ref-9"/> +<_x0035_ href="#ref-10"/> +<_x0036_ href="#ref-11"/> +<_x0037_ href="#ref-12"/> +<_x0038_ href="#ref-13"/> +<_x0039_ href="#ref-14"/> +<_x0031_0 href="#ref-15"/> +<_x0031_1 href="#ref-16"/> +<_x0031_2 href="#ref-17"/> +<_x0031_3 href="#ref-18"/> +<_x0031_4 href="#ref-19"/> +<_x0031_5 href="#ref-20"/> +<_x0031_6 href="#ref-21"/> +<_x0031_7 href="#ref-22"/> +<_x0031_8 href="#ref-23"/> +<_x0031_9 href="#ref-24"/> +<_x0032_0 href="#ref-25"/> +<_x0032_1 href="#ref-26"/> +<_x0032_2 href="#ref-27"/> +<_x0032_3 href="#ref-28"/> +<_x0032_4 href="#ref-29"/> +<_x0032_5 href="#ref-30"/> +<_x0032_6 href="#ref-31"/> +<_x0032_7 href="#ref-32"/> +<_x0032_8 href="#ref-33"/> +<_x0032_9 href="#ref-34"/> +<_x0033_0 href="#ref-35"/> +<_x0033_1 href="#ref-36"/> +<_x0033_2 href="#ref-37"/> +<_x0033_3 href="#ref-38"/> +<_x0033_4 href="#ref-39"/> +<_x0033_5 href="#ref-40"/> +<_x0033_6 href="#ref-41"/> +<_x0033_7 href="#ref-42"/> +<_x0033_8 href="#ref-43"/> +<_x0033_9 href="#ref-44"/> +<_x0034_0 href="#ref-45"/> +<_x0034_1 href="#ref-46"/> +<_x0034_2 href="#ref-47"/> +<_x0034_3 href="#ref-48"/> +<_x0034_4 href="#ref-49"/> +<_x0034_5 href="#ref-50"/> +<_x0034_6 href="#ref-51"/> +<_x0034_7 href="#ref-52"/> +<_x0034_8 href="#ref-53"/> +<_x0034_9 href="#ref-54"/> +<_x0035_0 href="#ref-55"/> +<_x0035_1 href="#ref-56"/> +<_x0035_2 href="#ref-57"/> +<_x0035_3 href="#ref-58"/> +<_x0035_4 href="#ref-59"/> +<_x0035_5 href="#ref-60"/> + + +<_x0030_ href="#ref-61"/> +<_x0031_ href="#ref-62"/> +<_x0032_ href="#ref-63"/> +<_x0033_ href="#ref-64"/> +<_x0034_ href="#ref-65"/> +<_x0035_ href="#ref-66"/> +<_x0036_ href="#ref-67"/> +<_x0037_ href="#ref-68"/> +<_x0038_ href="#ref-69"/> +<_x0039_ href="#ref-70"/> +<_x0031_0 href="#ref-71"/> +<_x0031_1 href="#ref-72"/> +<_x0031_2 href="#ref-73"/> +<_x0031_3 href="#ref-74"/> +<_x0031_4 href="#ref-75"/> +<_x0031_5 href="#ref-76"/> +<_x0031_6 href="#ref-77"/> +<_x0031_7 href="#ref-78"/> +<_x0031_8 href="#ref-79"/> +<_x0031_9 href="#ref-80"/> +<_x0032_0 href="#ref-81"/> +<_x0032_1 href="#ref-82"/> +<_x0032_2 href="#ref-83"/> +<_x0032_3 href="#ref-84"/> +<_x0032_4 href="#ref-85"/> +<_x0032_5 href="#ref-86"/> + + +ltank\ltank_base_7_0.png + + +ltank\ltank_base_1_0.PNG + + +ltank\ltank_base_2_0.PNG + + +ltank\ltank_base_3_0.PNG + + +ltank\ltank_base_4_0.PNG + + +ltank\ltank_base_5_0.PNG + + +ltank\ltank_base_6_0.PNG + + +ltank\ltank_base_0_0.PNG + + +ltank\ltank_fire_15_2.png + + +ltank\ltank_fire_00_1.png + + +ltank\ltank_fire_00_2.png + + +ltank\ltank_fire_01_1.png + + +ltank\ltank_fire_01_2.png + + +ltank\ltank_fire_02_1.png + + +ltank\ltank_fire_02_2.png + + +ltank\ltank_fire_03_1.png + + +ltank\ltank_fire_03_2.png + + +ltank\ltank_fire_04_1.png + + +ltank\ltank_fire_04_2.png + + +ltank\ltank_fire_05_1.png + + +ltank\ltank_fire_05_2.png + + +ltank\ltank_fire_06_1.png + + +ltank\ltank_fire_06_2.png + + +ltank\ltank_fire_07_1.png + + +ltank\ltank_fire_07_2.png + + +ltank\ltank_fire_08_1.png + + +ltank\ltank_fire_08_2.png + + +ltank\ltank_fire_09_1.png + + +ltank\ltank_fire_09_2.png + + +ltank\ltank_fire_10_1.png + + +ltank\ltank_fire_10_2.png + + +ltank\ltank_fire_11_1.png + + +ltank\ltank_fire_11_2.png + + +ltank\ltank_fire_12_1.png + + +ltank\ltank_fire_12_2.png + + +ltank\ltank_fire_13_1.png + + +ltank\ltank_fire_13_2.png + + +ltank\ltank_fire_14_1.png + + +ltank\ltank_fire_14_2.png + + +ltank\ltank_fire_15_1.png + + +ltank\ltank_fire_15_0.png + + +ltank\ltank_fire_00_0.png + + +ltank\ltank_fire_01_0.png + + +ltank\ltank_fire_02_0.png + + +ltank\ltank_fire_03_0.png + + +ltank\ltank_fire_04_0.png + + +ltank\ltank_fire_05_0.png + + +ltank\ltank_fire_06_0.png + + +ltank\ltank_fire_07_0.png + + +ltank\ltank_fire_08_0.png + + +ltank\ltank_fire_09_0.png + + +ltank\ltank_fire_10_0.png + + +ltank\ltank_fire_11_0.png + + +ltank\ltank_fire_12_0.png + + +ltank\ltank_fire_13_0.png + + +ltank\ltank_fire_14_0.png + + +base 0 +0 +1 +<_x0030_ href="#ref-144"/> + + +base 1 +0 +1 +<_x0030_ href="#ref-146"/> + + +base 2 +0 +1 +<_x0030_ href="#ref-148"/> + + +base 3 +0 +1 +<_x0030_ href="#ref-150"/> + + +base 4 +0 +1 +<_x0030_ href="#ref-152"/> + + +base 5 +0 +1 +<_x0030_ href="#ref-154"/> + + +base 6 +0 +1 +<_x0030_ href="#ref-156"/> + + +base 7 +0 +1 +<_x0030_ href="#ref-158"/> + + +fire 00 +0 +3 +<_x0030_ href="#ref-160"/> +<_x0031_ href="#ref-161"/> +<_x0032_ href="#ref-162"/> + + +fire 01 +0 +3 +<_x0030_ href="#ref-164"/> +<_x0031_ href="#ref-165"/> +<_x0032_ href="#ref-166"/> + + +fire 02 +0 +3 +<_x0030_ href="#ref-168"/> +<_x0031_ href="#ref-169"/> +<_x0032_ href="#ref-170"/> + + +fire 03 +0 +3 +<_x0030_ href="#ref-172"/> +<_x0031_ href="#ref-173"/> +<_x0032_ href="#ref-174"/> + + +fire 04 +0 +3 +<_x0030_ href="#ref-176"/> +<_x0031_ href="#ref-177"/> +<_x0032_ href="#ref-178"/> + + +fire 05 +0 +3 +<_x0030_ href="#ref-180"/> +<_x0031_ href="#ref-181"/> +<_x0032_ href="#ref-182"/> + + +fire 06 +0 +3 +<_x0030_ href="#ref-184"/> +<_x0031_ href="#ref-185"/> +<_x0032_ href="#ref-186"/> + + +fire 07 +0 +3 +<_x0030_ href="#ref-188"/> +<_x0031_ href="#ref-189"/> +<_x0032_ href="#ref-190"/> + + +fire 08 +0 +3 +<_x0030_ href="#ref-192"/> +<_x0031_ href="#ref-193"/> +<_x0032_ href="#ref-194"/> + + +fire 09 +0 +3 +<_x0030_ href="#ref-196"/> +<_x0031_ href="#ref-197"/> +<_x0032_ href="#ref-198"/> + + +fire 10 +0 +3 +<_x0030_ href="#ref-200"/> +<_x0031_ href="#ref-201"/> +<_x0032_ href="#ref-202"/> + + +fire 11 +0 +3 +<_x0030_ href="#ref-204"/> +<_x0031_ href="#ref-205"/> +<_x0032_ href="#ref-206"/> + + +fire 12 +0 +3 +<_x0030_ href="#ref-208"/> +<_x0031_ href="#ref-209"/> +<_x0032_ href="#ref-210"/> + + +fire 13 +0 +3 +<_x0030_ href="#ref-212"/> +<_x0031_ href="#ref-213"/> +<_x0032_ href="#ref-214"/> + + +fire 14 +0 +3 +<_x0030_ href="#ref-216"/> +<_x0031_ href="#ref-217"/> +<_x0032_ href="#ref-218"/> + + +fire 15 +0 +3 +<_x0030_ href="#ref-220"/> +<_x0031_ href="#ref-221"/> +<_x0032_ href="#ref-222"/> + + +icon +0 +1 +<_x0030_ href="#ref-224"/> + + +help +0 +1 +<_x0030_ href="#ref-226"/> + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +-20 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +7 +-20 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +14 +-15 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +15 +-8 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +19 +-2 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +19 +5 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +15 +13 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +7 +14 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +18 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-6 +15 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-14 +12 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-16 +4 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-18 +-2 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-16 +-8 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-12 +-12 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-7 +-17 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + +<_x0030_ href="#ref-286"/> + + +<_x0030_ href="#ref-287"/> + + +<_x0030_ href="#ref-288"/> + + +<_x0030_ href="#ref-289"/> + + +<_x0030_ href="#ref-290"/> + + +<_x0030_ href="#ref-291"/> + + +<_x0030_ href="#ref-292"/> + + +<_x0030_ href="#ref-293"/> + + +<_x0030_ href="#ref-294"/> + + +<_x0030_ href="#ref-295"/> + + +<_x0030_ href="#ref-296"/> + + +<_x0030_ href="#ref-297"/> + + +<_x0030_ href="#ref-298"/> + + +<_x0030_ href="#ref-299"/> + + +<_x0030_ href="#ref-300"/> + + +<_x0030_ href="#ref-301"/> + + +<_x0030_ href="#ref-302"/> + + +<_x0030_ href="#ref-303"/> + + +<_x0030_ href="#ref-304"/> + + +<_x0030_ href="#ref-305"/> + + +<_x0030_ href="#ref-306"/> + + +<_x0030_ href="#ref-307"/> + + +<_x0030_ href="#ref-308"/> + + +<_x0030_ href="#ref-309"/> + + +<_x0030_ href="#ref-310"/> + + +<_x0030_ href="#ref-311"/> + + +<_x0030_ href="#ref-312"/> + + +<_x0030_ href="#ref-313"/> + + +<_x0030_ href="#ref-314"/> + + +<_x0030_ href="#ref-315"/> + + +<_x0030_ href="#ref-316"/> + + +<_x0030_ href="#ref-317"/> + + +<_x0030_ href="#ref-318"/> + + +<_x0030_ href="#ref-319"/> + + +<_x0030_ href="#ref-320"/> + + +<_x0030_ href="#ref-321"/> + + +<_x0030_ href="#ref-322"/> + + +<_x0030_ href="#ref-323"/> + + +<_x0030_ href="#ref-324"/> + + +<_x0030_ href="#ref-325"/> + + +<_x0030_ href="#ref-326"/> + + +<_x0030_ href="#ref-327"/> + + +<_x0030_ href="#ref-328"/> + + +<_x0030_ href="#ref-329"/> + + +<_x0030_ href="#ref-330"/> + + +<_x0030_ href="#ref-331"/> + + +<_x0030_ href="#ref-332"/> + + +<_x0030_ href="#ref-333"/> + + +<_x0030_ href="#ref-334"/> + + +<_x0030_ href="#ref-335"/> + + +<_x0030_ href="#ref-336"/> + + +<_x0030_ href="#ref-337"/> + + +<_x0030_ href="#ref-338"/> + + +<_x0030_ href="#ref-339"/> + + +<_x0030_ href="#ref-340"/> + + +<_x0030_ href="#ref-341"/> + + +<_x0030_ href="#ref-342"/> +<_x0031_ href="#ref-343"/> + + +<_x0030_ href="#ref-344"/> +<_x0031_ href="#ref-345"/> + + + +15 +14 + + + + + +12 +17 + + + + + +14 +14 + + + + + +12 +12 + + + + + +14 +14 + + + + + +16 +12 + + + + + +15 +14 + + + + + +17 +15 + + + + + +19 +21 + + + + + +19 +21 + + + + + +19 +21 + + + + + +19 +22 + + + + + +19 +22 + + + + + +19 +22 + + + + + +19 +22 + + + + + +19 +22 + + + + + +19 +22 + + + + + +19 +21 + + + + + +19 +21 + + + + + +19 +21 + + + + + +19 +21 + + + + + +19 +21 + + + + + +19 +21 + + + + + +19 +21 + + + + + +19 +21 + + + + + +19 +21 + + + + + +19 +20 + + + + + +19 +20 + + + + + +19 +20 + + + + + +20 +20 + + + + + +20 +20 + + + + + +20 +20 + + + + + +20 +20 + + + + + +20 +20 + + + + + +20 +20 + + + + + +20 +20 + + + + + +20 +20 + + + + + +20 +20 + + + + + +21 +20 + + + + + +21 +20 + + + + + +21 +20 + + + + + +20 +20 + + + + + +20 +20 + + + + + +20 +20 + + + + + +20 +21 + + + + + +20 +21 + + + + + +20 +21 + + + + + +20 +20 + + + + + +20 +20 + + + + + +20 +20 + + + + + +21 +19 + + + + + +21 +19 + + + + + +21 +19 + + + + + +20 +20 + + + + + +20 +20 + + + + + +20 +20 + + + + + +7 +12 + + + + + +2 +5 + + + + + +20 +23 + + + + + +15 +15 + + + + + diff --git a/data/art824/ltank/ltank_base_0_0.png b/data/art824/ltank/ltank_base_0_0.png new file mode 100644 index 0000000..1873de2 Binary files /dev/null and b/data/art824/ltank/ltank_base_0_0.png differ diff --git a/data/art824/ltank/ltank_base_1_0.png b/data/art824/ltank/ltank_base_1_0.png new file mode 100644 index 0000000..879b145 Binary files /dev/null and b/data/art824/ltank/ltank_base_1_0.png differ diff --git a/data/art824/ltank/ltank_base_2_0.png b/data/art824/ltank/ltank_base_2_0.png new file mode 100644 index 0000000..cee2ff1 Binary files /dev/null and b/data/art824/ltank/ltank_base_2_0.png differ diff --git a/data/art824/ltank/ltank_base_3_0.png b/data/art824/ltank/ltank_base_3_0.png new file mode 100644 index 0000000..f65f0f4 Binary files /dev/null and b/data/art824/ltank/ltank_base_3_0.png differ diff --git a/data/art824/ltank/ltank_base_4_0.png b/data/art824/ltank/ltank_base_4_0.png new file mode 100644 index 0000000..436ab4c Binary files /dev/null and b/data/art824/ltank/ltank_base_4_0.png differ diff --git a/data/art824/ltank/ltank_base_5_0.png b/data/art824/ltank/ltank_base_5_0.png new file mode 100644 index 0000000..f2775a6 Binary files /dev/null and b/data/art824/ltank/ltank_base_5_0.png differ diff --git a/data/art824/ltank/ltank_base_6_0.png b/data/art824/ltank/ltank_base_6_0.png new file mode 100644 index 0000000..996d5d9 Binary files /dev/null and b/data/art824/ltank/ltank_base_6_0.png differ diff --git a/data/art824/ltank/ltank_base_7_0.png b/data/art824/ltank/ltank_base_7_0.png new file mode 100644 index 0000000..49892c4 Binary files /dev/null and b/data/art824/ltank/ltank_base_7_0.png differ diff --git a/data/art824/ltank/ltank_fire_00_0.png b/data/art824/ltank/ltank_fire_00_0.png new file mode 100644 index 0000000..25fab27 Binary files /dev/null and b/data/art824/ltank/ltank_fire_00_0.png differ diff --git a/data/art824/ltank/ltank_fire_00_1.png b/data/art824/ltank/ltank_fire_00_1.png new file mode 100644 index 0000000..7284363 Binary files /dev/null and b/data/art824/ltank/ltank_fire_00_1.png differ diff --git a/data/art824/ltank/ltank_fire_00_2.png b/data/art824/ltank/ltank_fire_00_2.png new file mode 100644 index 0000000..46692fc Binary files /dev/null and b/data/art824/ltank/ltank_fire_00_2.png differ diff --git a/data/art824/ltank/ltank_fire_01_0.png b/data/art824/ltank/ltank_fire_01_0.png new file mode 100644 index 0000000..5b97591 Binary files /dev/null and b/data/art824/ltank/ltank_fire_01_0.png differ diff --git a/data/art824/ltank/ltank_fire_01_1.png b/data/art824/ltank/ltank_fire_01_1.png new file mode 100644 index 0000000..d970411 Binary files /dev/null and b/data/art824/ltank/ltank_fire_01_1.png differ diff --git a/data/art824/ltank/ltank_fire_01_2.png b/data/art824/ltank/ltank_fire_01_2.png new file mode 100644 index 0000000..fe3057e Binary files /dev/null and b/data/art824/ltank/ltank_fire_01_2.png differ diff --git a/data/art824/ltank/ltank_fire_02_0.png b/data/art824/ltank/ltank_fire_02_0.png new file mode 100644 index 0000000..08c13ce Binary files /dev/null and b/data/art824/ltank/ltank_fire_02_0.png differ diff --git a/data/art824/ltank/ltank_fire_02_1.png b/data/art824/ltank/ltank_fire_02_1.png new file mode 100644 index 0000000..c917beb Binary files /dev/null and b/data/art824/ltank/ltank_fire_02_1.png differ diff --git a/data/art824/ltank/ltank_fire_02_2.png b/data/art824/ltank/ltank_fire_02_2.png new file mode 100644 index 0000000..834ab09 Binary files /dev/null and b/data/art824/ltank/ltank_fire_02_2.png differ diff --git a/data/art824/ltank/ltank_fire_03_0.png b/data/art824/ltank/ltank_fire_03_0.png new file mode 100644 index 0000000..0bc9d89 Binary files /dev/null and b/data/art824/ltank/ltank_fire_03_0.png differ diff --git a/data/art824/ltank/ltank_fire_03_1.png b/data/art824/ltank/ltank_fire_03_1.png new file mode 100644 index 0000000..2cbc8aa Binary files /dev/null and b/data/art824/ltank/ltank_fire_03_1.png differ diff --git a/data/art824/ltank/ltank_fire_03_2.png b/data/art824/ltank/ltank_fire_03_2.png new file mode 100644 index 0000000..b98a90b Binary files /dev/null and b/data/art824/ltank/ltank_fire_03_2.png differ diff --git a/data/art824/ltank/ltank_fire_04_0.png b/data/art824/ltank/ltank_fire_04_0.png new file mode 100644 index 0000000..d162ea3 Binary files /dev/null and b/data/art824/ltank/ltank_fire_04_0.png differ diff --git a/data/art824/ltank/ltank_fire_04_1.png b/data/art824/ltank/ltank_fire_04_1.png new file mode 100644 index 0000000..612881c Binary files /dev/null and b/data/art824/ltank/ltank_fire_04_1.png differ diff --git a/data/art824/ltank/ltank_fire_04_2.png b/data/art824/ltank/ltank_fire_04_2.png new file mode 100644 index 0000000..6602a05 Binary files /dev/null and b/data/art824/ltank/ltank_fire_04_2.png differ diff --git a/data/art824/ltank/ltank_fire_05_0.png b/data/art824/ltank/ltank_fire_05_0.png new file mode 100644 index 0000000..8b67640 Binary files /dev/null and b/data/art824/ltank/ltank_fire_05_0.png differ diff --git a/data/art824/ltank/ltank_fire_05_1.png b/data/art824/ltank/ltank_fire_05_1.png new file mode 100644 index 0000000..e2f394b Binary files /dev/null and b/data/art824/ltank/ltank_fire_05_1.png differ diff --git a/data/art824/ltank/ltank_fire_05_2.png b/data/art824/ltank/ltank_fire_05_2.png new file mode 100644 index 0000000..c7cff89 Binary files /dev/null and b/data/art824/ltank/ltank_fire_05_2.png differ diff --git a/data/art824/ltank/ltank_fire_06_0.png b/data/art824/ltank/ltank_fire_06_0.png new file mode 100644 index 0000000..99e3254 Binary files /dev/null and b/data/art824/ltank/ltank_fire_06_0.png differ diff --git a/data/art824/ltank/ltank_fire_06_1.png b/data/art824/ltank/ltank_fire_06_1.png new file mode 100644 index 0000000..47f4a7a Binary files /dev/null and b/data/art824/ltank/ltank_fire_06_1.png differ diff --git a/data/art824/ltank/ltank_fire_06_2.png b/data/art824/ltank/ltank_fire_06_2.png new file mode 100644 index 0000000..9cc46a7 Binary files /dev/null and b/data/art824/ltank/ltank_fire_06_2.png differ diff --git a/data/art824/ltank/ltank_fire_07_0.png b/data/art824/ltank/ltank_fire_07_0.png new file mode 100644 index 0000000..8789bc4 Binary files /dev/null and b/data/art824/ltank/ltank_fire_07_0.png differ diff --git a/data/art824/ltank/ltank_fire_07_1.png b/data/art824/ltank/ltank_fire_07_1.png new file mode 100644 index 0000000..4f385ee Binary files /dev/null and b/data/art824/ltank/ltank_fire_07_1.png differ diff --git a/data/art824/ltank/ltank_fire_07_2.png b/data/art824/ltank/ltank_fire_07_2.png new file mode 100644 index 0000000..0f4e6a1 Binary files /dev/null and b/data/art824/ltank/ltank_fire_07_2.png differ diff --git a/data/art824/ltank/ltank_fire_08_0.png b/data/art824/ltank/ltank_fire_08_0.png new file mode 100644 index 0000000..fdddcb3 Binary files /dev/null and b/data/art824/ltank/ltank_fire_08_0.png differ diff --git a/data/art824/ltank/ltank_fire_08_1.png b/data/art824/ltank/ltank_fire_08_1.png new file mode 100644 index 0000000..80d61d8 Binary files /dev/null and b/data/art824/ltank/ltank_fire_08_1.png differ diff --git a/data/art824/ltank/ltank_fire_08_2.png b/data/art824/ltank/ltank_fire_08_2.png new file mode 100644 index 0000000..b45f19e Binary files /dev/null and b/data/art824/ltank/ltank_fire_08_2.png differ diff --git a/data/art824/ltank/ltank_fire_09_0.png b/data/art824/ltank/ltank_fire_09_0.png new file mode 100644 index 0000000..80beec1 Binary files /dev/null and b/data/art824/ltank/ltank_fire_09_0.png differ diff --git a/data/art824/ltank/ltank_fire_09_1.png b/data/art824/ltank/ltank_fire_09_1.png new file mode 100644 index 0000000..97d89cc Binary files /dev/null and b/data/art824/ltank/ltank_fire_09_1.png differ diff --git a/data/art824/ltank/ltank_fire_09_2.png b/data/art824/ltank/ltank_fire_09_2.png new file mode 100644 index 0000000..8dcc2f2 Binary files /dev/null and b/data/art824/ltank/ltank_fire_09_2.png differ diff --git a/data/art824/ltank/ltank_fire_10_0.png b/data/art824/ltank/ltank_fire_10_0.png new file mode 100644 index 0000000..b65fcef Binary files /dev/null and b/data/art824/ltank/ltank_fire_10_0.png differ diff --git a/data/art824/ltank/ltank_fire_10_1.png b/data/art824/ltank/ltank_fire_10_1.png new file mode 100644 index 0000000..b39fb68 Binary files /dev/null and b/data/art824/ltank/ltank_fire_10_1.png differ diff --git a/data/art824/ltank/ltank_fire_10_2.png b/data/art824/ltank/ltank_fire_10_2.png new file mode 100644 index 0000000..b65f20b Binary files /dev/null and b/data/art824/ltank/ltank_fire_10_2.png differ diff --git a/data/art824/ltank/ltank_fire_11_0.png b/data/art824/ltank/ltank_fire_11_0.png new file mode 100644 index 0000000..338399b Binary files /dev/null and b/data/art824/ltank/ltank_fire_11_0.png differ diff --git a/data/art824/ltank/ltank_fire_11_1.png b/data/art824/ltank/ltank_fire_11_1.png new file mode 100644 index 0000000..9cb0a82 Binary files /dev/null and b/data/art824/ltank/ltank_fire_11_1.png differ diff --git a/data/art824/ltank/ltank_fire_11_2.png b/data/art824/ltank/ltank_fire_11_2.png new file mode 100644 index 0000000..65d8439 Binary files /dev/null and b/data/art824/ltank/ltank_fire_11_2.png differ diff --git a/data/art824/ltank/ltank_fire_12_0.png b/data/art824/ltank/ltank_fire_12_0.png new file mode 100644 index 0000000..be436e4 Binary files /dev/null and b/data/art824/ltank/ltank_fire_12_0.png differ diff --git a/data/art824/ltank/ltank_fire_12_1.png b/data/art824/ltank/ltank_fire_12_1.png new file mode 100644 index 0000000..05888e9 Binary files /dev/null and b/data/art824/ltank/ltank_fire_12_1.png differ diff --git a/data/art824/ltank/ltank_fire_12_2.png b/data/art824/ltank/ltank_fire_12_2.png new file mode 100644 index 0000000..b9f92db Binary files /dev/null and b/data/art824/ltank/ltank_fire_12_2.png differ diff --git a/data/art824/ltank/ltank_fire_13_0.png b/data/art824/ltank/ltank_fire_13_0.png new file mode 100644 index 0000000..0f1ed6c Binary files /dev/null and b/data/art824/ltank/ltank_fire_13_0.png differ diff --git a/data/art824/ltank/ltank_fire_13_1.png b/data/art824/ltank/ltank_fire_13_1.png new file mode 100644 index 0000000..1baaae5 Binary files /dev/null and b/data/art824/ltank/ltank_fire_13_1.png differ diff --git a/data/art824/ltank/ltank_fire_13_2.png b/data/art824/ltank/ltank_fire_13_2.png new file mode 100644 index 0000000..ea83222 Binary files /dev/null and b/data/art824/ltank/ltank_fire_13_2.png differ diff --git a/data/art824/ltank/ltank_fire_14_0.png b/data/art824/ltank/ltank_fire_14_0.png new file mode 100644 index 0000000..560882e Binary files /dev/null and b/data/art824/ltank/ltank_fire_14_0.png differ diff --git a/data/art824/ltank/ltank_fire_14_1.png b/data/art824/ltank/ltank_fire_14_1.png new file mode 100644 index 0000000..493746f Binary files /dev/null and b/data/art824/ltank/ltank_fire_14_1.png differ diff --git a/data/art824/ltank/ltank_fire_14_2.png b/data/art824/ltank/ltank_fire_14_2.png new file mode 100644 index 0000000..6200117 Binary files /dev/null and b/data/art824/ltank/ltank_fire_14_2.png differ diff --git a/data/art824/ltank/ltank_fire_15_0.png b/data/art824/ltank/ltank_fire_15_0.png new file mode 100644 index 0000000..6a6d2a7 Binary files /dev/null and b/data/art824/ltank/ltank_fire_15_0.png differ diff --git a/data/art824/ltank/ltank_fire_15_1.png b/data/art824/ltank/ltank_fire_15_1.png new file mode 100644 index 0000000..790419c Binary files /dev/null and b/data/art824/ltank/ltank_fire_15_1.png differ diff --git a/data/art824/ltank/ltank_fire_15_2.png b/data/art824/ltank/ltank_fire_15_2.png new file mode 100644 index 0000000..6d4e24a Binary files /dev/null and b/data/art824/ltank/ltank_fire_15_2.png differ diff --git a/data/art824/miner.amx b/data/art824/miner.amx new file mode 100644 index 0000000..7df6199 --- /dev/null +++ b/data/art824/miner.amx @@ -0,0 +1,296 @@ + + + + + +80 +true + + +<_x0030_ href="#ref-5"/> +<_x0031_ href="#ref-6"/> +<_x0032_ href="#ref-7"/> +<_x0033_ href="#ref-8"/> +<_x0034_ href="#ref-9"/> +<_x0035_ href="#ref-10"/> +<_x0036_ href="#ref-11"/> +<_x0037_ href="#ref-12"/> + + +<_x0030_ href="#ref-13"/> +<_x0031_ href="#ref-14"/> +<_x0032_ href="#ref-15"/> +<_x0033_ href="#ref-16"/> +<_x0034_ href="#ref-17"/> +<_x0035_ href="#ref-18"/> +<_x0036_ href="#ref-19"/> +<_x0037_ href="#ref-20"/> +<_x0038_ href="#ref-21"/> +<_x0039_ href="#ref-22"/> + + +miner\miner_run_7_0.png + + +miner\miner_run_1_0.png + + +miner\miner_run_2_0.png + + +miner\miner_run_3_0.png + + +miner\miner_run_4_0.png + + +miner\miner_run_5_0.png + + +miner\miner_run_6_0.png + + +miner\miner_run_0_0.png + + +run 0 +0 +1 +<_x0030_ href="#ref-32"/> + + +run 1 +0 +1 +<_x0030_ href="#ref-34"/> + + +run 2 +0 +1 +<_x0030_ href="#ref-36"/> + + +run 3 +0 +1 +<_x0030_ href="#ref-38"/> + + +run 4 +0 +1 +<_x0030_ href="#ref-40"/> + + +run 5 +0 +1 +<_x0030_ href="#ref-42"/> + + +run 6 +0 +1 +<_x0030_ href="#ref-44"/> + + +run 7 +0 +1 +<_x0030_ href="#ref-46"/> + + +icon +0 +1 +<_x0030_ href="#ref-48"/> + + +help +0 +1 +<_x0030_ href="#ref-50"/> + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + +<_x0030_ href="#ref-62"/> + + +<_x0030_ href="#ref-63"/> + + +<_x0030_ href="#ref-64"/> + + +<_x0030_ href="#ref-65"/> + + +<_x0030_ href="#ref-66"/> + + +<_x0030_ href="#ref-67"/> + + +<_x0030_ href="#ref-68"/> + + +<_x0030_ href="#ref-69"/> + + +<_x0030_ href="#ref-70"/> + + +<_x0030_ href="#ref-71"/> + + + +15 +14 + + + + + +16 +15 + + + + + +16 +14 + + + + + +15 +15 + + + + + +15 +16 + + + + + +15 +16 + + + + + +15 +15 + + + + + +16 +16 + + + + + +2 +5 + + + + + +16 +16 + + + + + diff --git a/data/art824/miner/miner_run_0_0.png b/data/art824/miner/miner_run_0_0.png new file mode 100644 index 0000000..9f21894 Binary files /dev/null and b/data/art824/miner/miner_run_0_0.png differ diff --git a/data/art824/miner/miner_run_1_0.png b/data/art824/miner/miner_run_1_0.png new file mode 100644 index 0000000..37d1d0e Binary files /dev/null and b/data/art824/miner/miner_run_1_0.png differ diff --git a/data/art824/miner/miner_run_2_0.png b/data/art824/miner/miner_run_2_0.png new file mode 100644 index 0000000..000530a Binary files /dev/null and b/data/art824/miner/miner_run_2_0.png differ diff --git a/data/art824/miner/miner_run_3_0.png b/data/art824/miner/miner_run_3_0.png new file mode 100644 index 0000000..728607c Binary files /dev/null and b/data/art824/miner/miner_run_3_0.png differ diff --git a/data/art824/miner/miner_run_4_0.png b/data/art824/miner/miner_run_4_0.png new file mode 100644 index 0000000..5b650dd Binary files /dev/null and b/data/art824/miner/miner_run_4_0.png differ diff --git a/data/art824/miner/miner_run_5_0.png b/data/art824/miner/miner_run_5_0.png new file mode 100644 index 0000000..9af4266 Binary files /dev/null and b/data/art824/miner/miner_run_5_0.png differ diff --git a/data/art824/miner/miner_run_6_0.png b/data/art824/miner/miner_run_6_0.png new file mode 100644 index 0000000..52bf633 Binary files /dev/null and b/data/art824/miner/miner_run_6_0.png differ diff --git a/data/art824/miner/miner_run_7_0.png b/data/art824/miner/miner_run_7_0.png new file mode 100644 index 0000000..167a456 Binary files /dev/null and b/data/art824/miner/miner_run_7_0.png differ diff --git a/data/art824/mobilehq.amx b/data/art824/mobilehq.amx new file mode 100644 index 0000000..045b4e2 --- /dev/null +++ b/data/art824/mobilehq.amx @@ -0,0 +1,296 @@ + + + + + +80 +true + + +<_x0030_ href="#ref-5"/> +<_x0031_ href="#ref-6"/> +<_x0032_ href="#ref-7"/> +<_x0033_ href="#ref-8"/> +<_x0034_ href="#ref-9"/> +<_x0035_ href="#ref-10"/> +<_x0036_ href="#ref-11"/> +<_x0037_ href="#ref-12"/> + + +<_x0030_ href="#ref-13"/> +<_x0031_ href="#ref-14"/> +<_x0032_ href="#ref-15"/> +<_x0033_ href="#ref-16"/> +<_x0034_ href="#ref-17"/> +<_x0035_ href="#ref-18"/> +<_x0036_ href="#ref-19"/> +<_x0037_ href="#ref-20"/> +<_x0038_ href="#ref-21"/> +<_x0039_ href="#ref-22"/> + + +mobilehq\mobilehq_run_0_0.PNG + + +mobilehq\mobilehq_run_2_0.png + + +mobilehq\mobilehq_run_3_0.png + + +mobilehq\mobilehq_run_4_0.png + + +mobilehq\mobilehq_run_5_0.png + + +mobilehq\mobilehq_run_6_0.png + + +mobilehq\mobilehq_run_7_0.png + + +mobilehq\mobilehq_run_1_0.PNG + + +run 0 +0 +1 +<_x0030_ href="#ref-32"/> + + +run 1 +0 +1 +<_x0030_ href="#ref-34"/> + + +run 2 +0 +1 +<_x0030_ href="#ref-36"/> + + +run 3 +0 +1 +<_x0030_ href="#ref-38"/> + + +run 4 +0 +1 +<_x0030_ href="#ref-40"/> + + +run 5 +0 +1 +<_x0030_ href="#ref-42"/> + + +run 6 +0 +1 +<_x0030_ href="#ref-44"/> + + +run 7 +0 +1 +<_x0030_ href="#ref-46"/> + + +icon +0 +1 +<_x0030_ href="#ref-48"/> + + +help +0 +1 +<_x0030_ href="#ref-50"/> + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + +<_x0030_ href="#ref-62"/> + + +<_x0030_ href="#ref-63"/> + + +<_x0030_ href="#ref-64"/> + + +<_x0030_ href="#ref-65"/> + + +<_x0030_ href="#ref-66"/> + + +<_x0030_ href="#ref-67"/> + + +<_x0030_ href="#ref-68"/> + + +<_x0030_ href="#ref-69"/> + + +<_x0030_ href="#ref-70"/> + + +<_x0030_ href="#ref-71"/> + + + +18 +12 + + + + + +14 +11 + + + + + +15 +10 + + + + + +15 +13 + + + + + +17 +13 + + + + + +15 +11 + + + + + +15 +10 + + + + + +16 +11 + + + + + +4 +2 + + + + + +16 +16 + + + + + diff --git a/data/art824/mobilehq/mobilehq_run_0_0.png b/data/art824/mobilehq/mobilehq_run_0_0.png new file mode 100644 index 0000000..a407e7b Binary files /dev/null and b/data/art824/mobilehq/mobilehq_run_0_0.png differ diff --git a/data/art824/mobilehq/mobilehq_run_1_0.png b/data/art824/mobilehq/mobilehq_run_1_0.png new file mode 100644 index 0000000..5ce3214 Binary files /dev/null and b/data/art824/mobilehq/mobilehq_run_1_0.png differ diff --git a/data/art824/mobilehq/mobilehq_run_2_0.png b/data/art824/mobilehq/mobilehq_run_2_0.png new file mode 100644 index 0000000..04a1402 Binary files /dev/null and b/data/art824/mobilehq/mobilehq_run_2_0.png differ diff --git a/data/art824/mobilehq/mobilehq_run_3_0.png b/data/art824/mobilehq/mobilehq_run_3_0.png new file mode 100644 index 0000000..432015f Binary files /dev/null and b/data/art824/mobilehq/mobilehq_run_3_0.png differ diff --git a/data/art824/mobilehq/mobilehq_run_4_0.png b/data/art824/mobilehq/mobilehq_run_4_0.png new file mode 100644 index 0000000..15a75a7 Binary files /dev/null and b/data/art824/mobilehq/mobilehq_run_4_0.png differ diff --git a/data/art824/mobilehq/mobilehq_run_5_0.png b/data/art824/mobilehq/mobilehq_run_5_0.png new file mode 100644 index 0000000..e89d2a5 Binary files /dev/null and b/data/art824/mobilehq/mobilehq_run_5_0.png differ diff --git a/data/art824/mobilehq/mobilehq_run_6_0.png b/data/art824/mobilehq/mobilehq_run_6_0.png new file mode 100644 index 0000000..5966df4 Binary files /dev/null and b/data/art824/mobilehq/mobilehq_run_6_0.png differ diff --git a/data/art824/mobilehq/mobilehq_run_7_0.png b/data/art824/mobilehq/mobilehq_run_7_0.png new file mode 100644 index 0000000..abdb993 Binary files /dev/null and b/data/art824/mobilehq/mobilehq_run_7_0.png differ diff --git a/data/art824/movetarget.amx b/data/art824/movetarget.amx new file mode 100644 index 0000000..3c65eb9 --- /dev/null +++ b/data/art824/movetarget.amx @@ -0,0 +1,135 @@ + + + + + +80 +true + + +<_x0030_ href="#ref-5"/> +<_x0031_ href="#ref-6"/> +<_x0032_ href="#ref-7"/> +<_x0033_ href="#ref-8"/> +<_x0034_ href="#ref-9"/> + + +<_x0030_ href="#ref-10"/> + + +movetarget\movetarget_0_0_4.png + + +movetarget\movetarget_0_0_1.png + + +movetarget\movetarget_0_0_2.png + + +movetarget\movetarget_0_0_3.png + + +movetarget\movetarget_0_0_0.png + + +movetarget_0_0 +0 +5 +<_x0030_ href="#ref-17"/> +<_x0031_ href="#ref-18"/> +<_x0032_ href="#ref-19"/> +<_x0033_ href="#ref-20"/> +<_x0034_ href="#ref-21"/> + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + +<_x0030_ href="#ref-28"/> + + +<_x0030_ href="#ref-29"/> + + +<_x0030_ href="#ref-30"/> + + +<_x0030_ href="#ref-31"/> + + +<_x0030_ href="#ref-32"/> + + + +12 +12 + + + + + +10 +10 + + + + + +9 +9 + + + + + +6 +6 + + + + + +3 +4 + + + + + diff --git a/data/art824/movetarget/movetarget_0_0_0.png b/data/art824/movetarget/movetarget_0_0_0.png new file mode 100644 index 0000000..6cd7a4d Binary files /dev/null and b/data/art824/movetarget/movetarget_0_0_0.png differ diff --git a/data/art824/movetarget/movetarget_0_0_1.png b/data/art824/movetarget/movetarget_0_0_1.png new file mode 100644 index 0000000..d9cdc4f Binary files /dev/null and b/data/art824/movetarget/movetarget_0_0_1.png differ diff --git a/data/art824/movetarget/movetarget_0_0_2.png b/data/art824/movetarget/movetarget_0_0_2.png new file mode 100644 index 0000000..79ec3c5 Binary files /dev/null and b/data/art824/movetarget/movetarget_0_0_2.png differ diff --git a/data/art824/movetarget/movetarget_0_0_3.png b/data/art824/movetarget/movetarget_0_0_3.png new file mode 100644 index 0000000..0a92a44 Binary files /dev/null and b/data/art824/movetarget/movetarget_0_0_3.png differ diff --git a/data/art824/movetarget/movetarget_0_0_4.png b/data/art824/movetarget/movetarget_0_0_4.png new file mode 100644 index 0000000..d42f0b1 Binary files /dev/null and b/data/art824/movetarget/movetarget_0_0_4.png differ diff --git a/data/art824/mtank.amx b/data/art824/mtank.amx new file mode 100644 index 0000000..7287b80 --- /dev/null +++ b/data/art824/mtank.amx @@ -0,0 +1,2248 @@ + + + + + +80 +true + + +<_x0030_ href="#ref-5"/> +<_x0031_ href="#ref-6"/> +<_x0032_ href="#ref-7"/> +<_x0033_ href="#ref-8"/> +<_x0034_ href="#ref-9"/> +<_x0035_ href="#ref-10"/> +<_x0036_ href="#ref-11"/> +<_x0037_ href="#ref-12"/> +<_x0038_ href="#ref-13"/> +<_x0039_ href="#ref-14"/> +<_x0031_0 href="#ref-15"/> +<_x0031_1 href="#ref-16"/> +<_x0031_2 href="#ref-17"/> +<_x0031_3 href="#ref-18"/> +<_x0031_4 href="#ref-19"/> +<_x0031_5 href="#ref-20"/> +<_x0031_6 href="#ref-21"/> +<_x0031_7 href="#ref-22"/> +<_x0031_8 href="#ref-23"/> +<_x0031_9 href="#ref-24"/> +<_x0032_0 href="#ref-25"/> +<_x0032_1 href="#ref-26"/> +<_x0032_2 href="#ref-27"/> +<_x0032_3 href="#ref-28"/> +<_x0032_4 href="#ref-29"/> +<_x0032_5 href="#ref-30"/> +<_x0032_6 href="#ref-31"/> +<_x0032_7 href="#ref-32"/> +<_x0032_8 href="#ref-33"/> +<_x0032_9 href="#ref-34"/> +<_x0033_0 href="#ref-35"/> +<_x0033_1 href="#ref-36"/> +<_x0033_2 href="#ref-37"/> +<_x0033_3 href="#ref-38"/> +<_x0033_4 href="#ref-39"/> +<_x0033_5 href="#ref-40"/> +<_x0033_6 href="#ref-41"/> +<_x0033_7 href="#ref-42"/> +<_x0033_8 href="#ref-43"/> +<_x0033_9 href="#ref-44"/> +<_x0034_0 href="#ref-45"/> +<_x0034_1 href="#ref-46"/> +<_x0034_2 href="#ref-47"/> +<_x0034_3 href="#ref-48"/> +<_x0034_4 href="#ref-49"/> +<_x0034_5 href="#ref-50"/> +<_x0034_6 href="#ref-51"/> +<_x0034_7 href="#ref-52"/> +<_x0034_8 href="#ref-53"/> +<_x0034_9 href="#ref-54"/> +<_x0035_0 href="#ref-55"/> +<_x0035_1 href="#ref-56"/> +<_x0035_2 href="#ref-57"/> +<_x0035_3 href="#ref-58"/> +<_x0035_4 href="#ref-59"/> +<_x0035_5 href="#ref-60"/> +<_x0035_6 href="#ref-61"/> +<_x0035_7 href="#ref-62"/> +<_x0035_8 href="#ref-63"/> +<_x0035_9 href="#ref-64"/> +<_x0036_0 href="#ref-65"/> +<_x0036_1 href="#ref-66"/> +<_x0036_2 href="#ref-67"/> +<_x0036_3 href="#ref-68"/> +<_x0036_4 href="#ref-69"/> +<_x0036_5 href="#ref-70"/> +<_x0036_6 href="#ref-71"/> +<_x0036_7 href="#ref-72"/> +<_x0036_8 href="#ref-73"/> +<_x0036_9 href="#ref-74"/> +<_x0037_0 href="#ref-75"/> +<_x0037_1 href="#ref-76"/> +<_x0037_2 href="#ref-77"/> +<_x0037_3 href="#ref-78"/> +<_x0037_4 href="#ref-79"/> +<_x0037_5 href="#ref-80"/> +<_x0037_6 href="#ref-81"/> +<_x0037_7 href="#ref-82"/> +<_x0037_8 href="#ref-83"/> +<_x0037_9 href="#ref-84"/> +<_x0038_0 href="#ref-85"/> +<_x0038_1 href="#ref-86"/> +<_x0038_2 href="#ref-87"/> +<_x0038_3 href="#ref-88"/> +<_x0038_4 href="#ref-89"/> +<_x0038_5 href="#ref-90"/> +<_x0038_6 href="#ref-91"/> +<_x0038_7 href="#ref-92"/> + + +<_x0030_ href="#ref-93"/> +<_x0031_ href="#ref-94"/> +<_x0032_ href="#ref-95"/> +<_x0033_ href="#ref-96"/> +<_x0034_ href="#ref-97"/> +<_x0035_ href="#ref-98"/> +<_x0036_ href="#ref-99"/> +<_x0037_ href="#ref-100"/> +<_x0038_ href="#ref-101"/> +<_x0039_ href="#ref-102"/> +<_x0031_0 href="#ref-103"/> +<_x0031_1 href="#ref-104"/> +<_x0031_2 href="#ref-105"/> +<_x0031_3 href="#ref-106"/> +<_x0031_4 href="#ref-107"/> +<_x0031_5 href="#ref-108"/> +<_x0031_6 href="#ref-109"/> +<_x0031_7 href="#ref-110"/> +<_x0031_8 href="#ref-111"/> +<_x0031_9 href="#ref-112"/> +<_x0032_0 href="#ref-113"/> +<_x0032_1 href="#ref-114"/> +<_x0032_2 href="#ref-115"/> +<_x0032_3 href="#ref-116"/> +<_x0032_4 href="#ref-117"/> +<_x0032_5 href="#ref-118"/> + + +mtank\mtank_fire_01_0.png + + +mtank\mtank_fire_02_0.png + + +mtank\mtank_fire_03_0.png + + +mtank\mtank_fire_04_0.png + + +mtank\mtank_fire_05_0.png + + +mtank\mtank_fire_06_0.png + + +mtank\mtank_fire_07_0.png + + +mtank\mtank_fire_08_0.png + + +mtank\mtank_fire_09_0.png + + +mtank\mtank_fire_10_0.png + + +mtank\mtank_fire_12_0.png + + +mtank\mtank_fire_13_0.png + + +mtank\mtank_fire_14_0.png + + +mtank\mtank_fire_00_0.png + + +mtank\mtank_fire_15_0.png + + +mtank\mtank_fire_11_0.png + + +mtank\mtank_base_7_0.png + + +mtank\mtank_base_1_0.png + + +mtank\mtank_base_2_0.png + + +mtank\mtank_base_3_0.png + + +mtank\mtank_base_4_0.png + + +mtank\mtank_base_5_0.png + + +mtank\mtank_base_6_0.png + + +mtank\mtank_base_0_0.png + + +mtank\mtank_fire_00_1.png + + +mtank\mtank_fire_00_2.png + + +mtank\mtank_fire_00_3.png + + +mtank\mtank_fire_00_4.png + + +mtank\mtank_fire_01_1.png + + +mtank\mtank_fire_01_2.png + + +mtank\mtank_fire_01_3.png + + +mtank\mtank_fire_01_4.png + + +mtank\mtank_fire_02_1.png + + +mtank\mtank_fire_02_2.png + + +mtank\mtank_fire_02_3.png + + +mtank\mtank_fire_02_4.png + + +mtank\mtank_fire_03_1.png + + +mtank\mtank_fire_03_2.png + + +mtank\mtank_fire_03_3.png + + +mtank\mtank_fire_03_4.png + + +mtank\mtank_fire_04_1.png + + +mtank\mtank_fire_04_2.png + + +mtank\mtank_fire_04_3.png + + +mtank\mtank_fire_04_4.png + + +mtank\mtank_fire_05_1.png + + +mtank\mtank_fire_05_2.png + + +mtank\mtank_fire_05_3.png + + +mtank\mtank_fire_05_4.png + + +mtank\mtank_fire_06_1.png + + +mtank\mtank_fire_06_2.png + + +mtank\mtank_fire_06_3.png + + +mtank\mtank_fire_06_4.png + + +mtank\mtank_fire_07_1.png + + +mtank\mtank_fire_07_2.png + + +mtank\mtank_fire_07_3.png + + +mtank\mtank_fire_07_4.png + + +mtank\mtank_fire_08_1.png + + +mtank\mtank_fire_08_2.png + + +mtank\mtank_fire_08_3.png + + +mtank\mtank_fire_08_4.png + + +mtank\mtank_fire_09_1.png + + +mtank\mtank_fire_09_2.png + + +mtank\mtank_fire_09_3.png + + +mtank\mtank_fire_09_4.png + + +mtank\mtank_fire_10_1.png + + +mtank\mtank_fire_10_2.png + + +mtank\mtank_fire_10_3.png + + +mtank\mtank_fire_10_4.png + + +mtank\mtank_fire_11_1.png + + +mtank\mtank_fire_11_2.png + + +mtank\mtank_fire_11_3.png + + +mtank\mtank_fire_11_4.png + + +mtank\mtank_fire_12_1.png + + +mtank\mtank_fire_12_2.png + + +mtank\mtank_fire_12_3.png + + +mtank\mtank_fire_12_4.png + + +mtank\mtank_fire_13_1.png + + +mtank\mtank_fire_13_2.png + + +mtank\mtank_fire_13_3.png + + +mtank\mtank_fire_13_4.png + + +mtank\mtank_fire_14_1.png + + +mtank\mtank_fire_14_2.png + + +mtank\mtank_fire_14_3.png + + +mtank\mtank_fire_14_4.png + + +mtank\mtank_fire_15_4.png + + +mtank\mtank_fire_15_2.png + + +mtank\mtank_fire_15_3.png + + +mtank\mtank_fire_15_1.png + + +base 0 +0 +1 +<_x0030_ href="#ref-208"/> + + +base 1 +0 +1 +<_x0030_ href="#ref-210"/> + + +base 2 +0 +1 +<_x0030_ href="#ref-212"/> + + +base 3 +0 +1 +<_x0030_ href="#ref-214"/> + + +base 4 +0 +1 +<_x0030_ href="#ref-216"/> + + +base 5 +0 +1 +<_x0030_ href="#ref-218"/> + + +base 6 +0 +1 +<_x0030_ href="#ref-220"/> + + +base 7 +0 +1 +<_x0030_ href="#ref-222"/> + + +fire 00 +0 +5 +<_x0030_ href="#ref-224"/> +<_x0031_ href="#ref-225"/> +<_x0032_ href="#ref-226"/> +<_x0033_ href="#ref-227"/> +<_x0034_ href="#ref-228"/> + + +fire 01 +0 +5 +<_x0030_ href="#ref-230"/> +<_x0031_ href="#ref-231"/> +<_x0032_ href="#ref-232"/> +<_x0033_ href="#ref-233"/> +<_x0034_ href="#ref-234"/> + + +fire 02 +0 +5 +<_x0030_ href="#ref-236"/> +<_x0031_ href="#ref-237"/> +<_x0032_ href="#ref-238"/> +<_x0033_ href="#ref-239"/> +<_x0034_ href="#ref-240"/> + + +fire 03 +0 +5 +<_x0030_ href="#ref-242"/> +<_x0031_ href="#ref-243"/> +<_x0032_ href="#ref-244"/> +<_x0033_ href="#ref-245"/> +<_x0034_ href="#ref-246"/> + + +fire 04 +0 +5 +<_x0030_ href="#ref-248"/> +<_x0031_ href="#ref-249"/> +<_x0032_ href="#ref-250"/> +<_x0033_ href="#ref-251"/> +<_x0034_ href="#ref-252"/> + + +fire 05 +0 +5 +<_x0030_ href="#ref-254"/> +<_x0031_ href="#ref-255"/> +<_x0032_ href="#ref-256"/> +<_x0033_ href="#ref-257"/> +<_x0034_ href="#ref-258"/> + + +fire 06 +0 +5 +<_x0030_ href="#ref-260"/> +<_x0031_ href="#ref-261"/> +<_x0032_ href="#ref-262"/> +<_x0033_ href="#ref-263"/> +<_x0034_ href="#ref-264"/> + + +fire 07 +0 +5 +<_x0030_ href="#ref-266"/> +<_x0031_ href="#ref-267"/> +<_x0032_ href="#ref-268"/> +<_x0033_ href="#ref-269"/> +<_x0034_ href="#ref-270"/> + + +fire 08 +0 +5 +<_x0030_ href="#ref-272"/> +<_x0031_ href="#ref-273"/> +<_x0032_ href="#ref-274"/> +<_x0033_ href="#ref-275"/> +<_x0034_ href="#ref-276"/> + + +fire 09 +0 +5 +<_x0030_ href="#ref-278"/> +<_x0031_ href="#ref-279"/> +<_x0032_ href="#ref-280"/> +<_x0033_ href="#ref-281"/> +<_x0034_ href="#ref-282"/> + + +fire 10 +0 +5 +<_x0030_ href="#ref-284"/> +<_x0031_ href="#ref-285"/> +<_x0032_ href="#ref-286"/> +<_x0033_ href="#ref-287"/> +<_x0034_ href="#ref-288"/> + + +fire 11 +0 +5 +<_x0030_ href="#ref-290"/> +<_x0031_ href="#ref-291"/> +<_x0032_ href="#ref-292"/> +<_x0033_ href="#ref-293"/> +<_x0034_ href="#ref-294"/> + + +fire 12 +0 +5 +<_x0030_ href="#ref-296"/> +<_x0031_ href="#ref-297"/> +<_x0032_ href="#ref-298"/> +<_x0033_ href="#ref-299"/> +<_x0034_ href="#ref-300"/> + + +fire 13 +0 +5 +<_x0030_ href="#ref-302"/> +<_x0031_ href="#ref-303"/> +<_x0032_ href="#ref-304"/> +<_x0033_ href="#ref-305"/> +<_x0034_ href="#ref-306"/> + + +fire 14 +0 +5 +<_x0030_ href="#ref-308"/> +<_x0031_ href="#ref-309"/> +<_x0032_ href="#ref-310"/> +<_x0033_ href="#ref-311"/> +<_x0034_ href="#ref-312"/> + + +fire 15 +0 +5 +<_x0030_ href="#ref-314"/> +<_x0031_ href="#ref-315"/> +<_x0032_ href="#ref-316"/> +<_x0033_ href="#ref-317"/> +<_x0034_ href="#ref-318"/> + + +icon +0 +1 +<_x0030_ href="#ref-320"/> + + +help +0 +1 +<_x0030_ href="#ref-322"/> + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-3 +-16 + + + + +0 + +-3 +-21 + + + + +0 + +3 +-18 + + + + +0 + +3 +-21 + + + + +0 + +0 +0 + + + + +0 + +4 +-14 + + + + +0 + +6 +-18 + + + + +0 + +10 +-13 + + + + +0 + +12 +-19 + + + + +0 + +0 +0 + + + + +0 + +11 +-14 + + + + +0 + +14 +-17 + + + + +0 + +14 +-11 + + + + +0 + +18 +-15 + + + + +0 + +0 +0 + + + + +0 + +12 +-10 + + + + +0 + +16 +-12 + + + + +0 + +16 +-6 + + + + +0 + +19 +-8 + + + + +0 + +0 +0 + + + + +0 + +18 +-3 + + + + +0 + +22 +-3 + + + + +0 + +20 +1 + + + + +0 + +24 +1 + + + + +0 + +0 +0 + + + + +0 + +21 +5 + + + + +0 + +25 +8 + + + + +0 + +17 +9 + + + + +0 + +24 +11 + + + + +0 + +0 +0 + + + + +0 + +17 +10 + + + + +0 + +20 +14 + + + + +0 + +13 +14 + + + + +0 + +17 +17 + + + + +0 + +0 +0 + + + + +0 + +11 +16 + + + + +0 + +13 +20 + + + + +0 + +6 +18 + + + + +0 + +8 +22 + + + + +0 + +0 +0 + + + + +0 + +3 +22 + + + + +0 + +3 +26 + + + + +0 + +-3 +19 + + + + +0 + +-3 +26 + + + + +0 + +0 +0 + + + + +0 + +-5 +16 + + + + +0 + +-7 +22 + + + + +0 + +-8 +15 + + + + +0 + +-10 +21 + + + + +0 + +0 +0 + + + + +0 + +-12 +15 + + + + +0 + +-16 +19 + + + + +0 + +-14 +10 + + + + +0 + +-18 +14 + + + + +0 + +0 +0 + + + + +0 + +-14 +10 + + + + +0 + +-17 +12 + + + + +0 + +-16 +6 + + + + +0 + +-21 +9 + + + + +0 + +0 +0 + + + + +0 + +-18 +3 + + + + +0 + +-24 +3 + + + + +0 + +-18 +-2 + + + + +0 + +-22 +-2 + + + + +0 + +0 +0 + + + + +0 + +-14 +-6 + + + + +0 + +-16 +-7 + + + + +0 + +-12 +-9 + + + + +0 + +-15 +-11 + + + + +0 + +0 +0 + + + + +0 + +-13 +-9 + + + + +0 + +-16 +-12 + + + + +0 + +-8 +-12 + + + + +0 + +-11 +-15 + + + + +0 + +0 +0 + + + + +0 + +-9 +-14 + + + + +0 + +-11 +-18 + + + + +0 + +-3 +-15 + + + + +0 + +-4 +-19 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + +<_x0030_ href="#ref-414"/> + + +<_x0030_ href="#ref-415"/> + + +<_x0030_ href="#ref-416"/> + + +<_x0030_ href="#ref-417"/> + + +<_x0030_ href="#ref-418"/> + + +<_x0030_ href="#ref-419"/> + + +<_x0030_ href="#ref-420"/> + + +<_x0030_ href="#ref-421"/> + + +<_x0030_ href="#ref-422"/> + + +<_x0030_ href="#ref-423"/> + + +<_x0030_ href="#ref-424"/> + + +<_x0030_ href="#ref-425"/> + + +<_x0030_ href="#ref-426"/> + + +<_x0030_ href="#ref-427"/> + + +<_x0030_ href="#ref-428"/> + + +<_x0030_ href="#ref-429"/> + + +<_x0030_ href="#ref-430"/> + + +<_x0030_ href="#ref-431"/> + + +<_x0030_ href="#ref-432"/> + + +<_x0030_ href="#ref-433"/> + + +<_x0030_ href="#ref-434"/> + + +<_x0030_ href="#ref-435"/> + + +<_x0030_ href="#ref-436"/> + + +<_x0030_ href="#ref-437"/> + + +<_x0030_ href="#ref-438"/> + + +<_x0030_ href="#ref-439"/> + + +<_x0030_ href="#ref-440"/> + + +<_x0030_ href="#ref-441"/> + + +<_x0030_ href="#ref-442"/> + + +<_x0030_ href="#ref-443"/> + + +<_x0030_ href="#ref-444"/> + + +<_x0030_ href="#ref-445"/> + + +<_x0030_ href="#ref-446"/> + + +<_x0030_ href="#ref-447"/> + + +<_x0030_ href="#ref-448"/> + + +<_x0030_ href="#ref-449"/> + + +<_x0030_ href="#ref-450"/> + + +<_x0030_ href="#ref-451"/> + + +<_x0030_ href="#ref-452"/> + + +<_x0030_ href="#ref-453"/> + + +<_x0030_ href="#ref-454"/> + + +<_x0030_ href="#ref-455"/> + + +<_x0030_ href="#ref-456"/> + + +<_x0030_ href="#ref-457"/> + + +<_x0030_ href="#ref-458"/> + + +<_x0030_ href="#ref-459"/> + + +<_x0030_ href="#ref-460"/> + + +<_x0030_ href="#ref-461"/> + + +<_x0030_ href="#ref-462"/> + + +<_x0030_ href="#ref-463"/> + + +<_x0030_ href="#ref-464"/> + + +<_x0030_ href="#ref-465"/> + + +<_x0030_ href="#ref-466"/> + + +<_x0030_ href="#ref-467"/> + + +<_x0030_ href="#ref-468"/> + + +<_x0030_ href="#ref-469"/> + + +<_x0030_ href="#ref-470"/> + + +<_x0030_ href="#ref-471"/> + + +<_x0030_ href="#ref-472"/> + + +<_x0030_ href="#ref-473"/> + + +<_x0030_ href="#ref-474"/> + + +<_x0030_ href="#ref-475"/> + + +<_x0030_ href="#ref-476"/> + + +<_x0030_ href="#ref-477"/> + + +<_x0030_ href="#ref-478"/> + + +<_x0030_ href="#ref-479"/> + + +<_x0030_ href="#ref-480"/> + + +<_x0030_ href="#ref-481"/> + + +<_x0030_ href="#ref-482"/> + + +<_x0030_ href="#ref-483"/> + + +<_x0030_ href="#ref-484"/> + + +<_x0030_ href="#ref-485"/> + + +<_x0030_ href="#ref-486"/> + + +<_x0030_ href="#ref-487"/> + + +<_x0030_ href="#ref-488"/> + + +<_x0030_ href="#ref-489"/> + + +<_x0030_ href="#ref-490"/> + + +<_x0030_ href="#ref-491"/> + + +<_x0030_ href="#ref-492"/> + + +<_x0030_ href="#ref-493"/> + + +<_x0030_ href="#ref-494"/> + + +<_x0030_ href="#ref-495"/> + + +<_x0030_ href="#ref-496"/> + + +<_x0030_ href="#ref-497"/> + + +<_x0030_ href="#ref-498"/> + + +<_x0030_ href="#ref-499"/> + + +<_x0030_ href="#ref-500"/> + + +<_x0030_ href="#ref-501"/> + + +<_x0030_ href="#ref-502"/> +<_x0031_ href="#ref-503"/> + + +<_x0030_ href="#ref-504"/> +<_x0031_ href="#ref-505"/> + + + +21 +21 + + + + + +19 +19 + + + + + +20 +19 + + + + + +20 +17 + + + + + +20 +20 + + + + + +20 +19 + + + + + +20 +20 + + + + + +22 +20 + + + + + +21 +21 + + + + + +21 +21 + + + + + +21 +21 + + + + + +21 +21 + + + + + +21 +21 + + + + + +20 +20 + + + + + +20 +20 + + + + + +20 +20 + + + + + +20 +20 + + + + + +20 +20 + + + + + +19 +21 + + + + + +19 +21 + + + + + +19 +21 + + + + + +19 +21 + + + + + +19 +21 + + + + + +20 +20 + + + + + +20 +20 + + + + + +20 +20 + + + + + +20 +20 + + + + + +20 +20 + + + + + +20 +20 + + + + + +12 +20 + + + + + +12 +20 + + + + + +12 +20 + + + + + +12 +20 + + + + + +20 +20 + + + + + +12 +15 + + + + + +12 +15 + + + + + +12 +15 + + + + + +12 +15 + + + + + +20 +20 + + + + + +12 +12 + + + + + +12 +12 + + + + + +12 +12 + + + + + +12 +12 + + + + + +20 +19 + + + + + +15 +11 + + + + + +15 +11 + + + + + +15 +11 + + + + + +15 +11 + + + + + +20 +18 + + + + + +20 +10 + + + + + +20 +10 + + + + + +20 +10 + + + + + +20 +10 + + + + + +20 +18 + + + + + +26 +10 + + + + + +26 +10 + + + + + +26 +10 + + + + + +26 +10 + + + + + +20 +17 + + + + + +26 +11 + + + + + +26 +11 + + + + + +26 +11 + + + + + +26 +11 + + + + + +20 +16 + + + + + +27 +11 + + + + + +27 +11 + + + + + +27 +11 + + + + + +27 +11 + + + + + +21 +18 + + + + + +29 +18 + + + + + +29 +18 + + + + + +29 +18 + + + + + +29 +18 + + + + + +22 +17 + + + + + +22 +17 + + + + + +22 +17 + + + + + +22 +17 + + + + + +22 +17 + + + + + +22 +17 + + + + + +22 +17 + + + + + +22 +17 + + + + + +22 +17 + + + + + +22 +17 + + + + + +20 +21 + + + + + +20 +21 + + + + + +20 +21 + + + + + +20 +21 + + + + + +20 +21 + + + + + +6 +11 + + + + + +6 +10 + + + + + +18 +21 + + + + + +20 +20 + + + + + diff --git a/data/art824/mtank/mtank_base_0_0.png b/data/art824/mtank/mtank_base_0_0.png new file mode 100644 index 0000000..c57feaf Binary files /dev/null and b/data/art824/mtank/mtank_base_0_0.png differ diff --git a/data/art824/mtank/mtank_base_1_0.png b/data/art824/mtank/mtank_base_1_0.png new file mode 100644 index 0000000..c8bbbdb Binary files /dev/null and b/data/art824/mtank/mtank_base_1_0.png differ diff --git a/data/art824/mtank/mtank_base_2_0.png b/data/art824/mtank/mtank_base_2_0.png new file mode 100644 index 0000000..8591dd6 Binary files /dev/null and b/data/art824/mtank/mtank_base_2_0.png differ diff --git a/data/art824/mtank/mtank_base_3_0.png b/data/art824/mtank/mtank_base_3_0.png new file mode 100644 index 0000000..e680fac Binary files /dev/null and b/data/art824/mtank/mtank_base_3_0.png differ diff --git a/data/art824/mtank/mtank_base_4_0.png b/data/art824/mtank/mtank_base_4_0.png new file mode 100644 index 0000000..8ee747c Binary files /dev/null and b/data/art824/mtank/mtank_base_4_0.png differ diff --git a/data/art824/mtank/mtank_base_5_0.png b/data/art824/mtank/mtank_base_5_0.png new file mode 100644 index 0000000..a76f502 Binary files /dev/null and b/data/art824/mtank/mtank_base_5_0.png differ diff --git a/data/art824/mtank/mtank_base_6_0.png b/data/art824/mtank/mtank_base_6_0.png new file mode 100644 index 0000000..01bae6c Binary files /dev/null and b/data/art824/mtank/mtank_base_6_0.png differ diff --git a/data/art824/mtank/mtank_base_7_0.png b/data/art824/mtank/mtank_base_7_0.png new file mode 100644 index 0000000..06b8e9b Binary files /dev/null and b/data/art824/mtank/mtank_base_7_0.png differ diff --git a/data/art824/mtank/mtank_fire_00_0.png b/data/art824/mtank/mtank_fire_00_0.png new file mode 100644 index 0000000..e4fed08 Binary files /dev/null and b/data/art824/mtank/mtank_fire_00_0.png differ diff --git a/data/art824/mtank/mtank_fire_00_1.png b/data/art824/mtank/mtank_fire_00_1.png new file mode 100644 index 0000000..c6f8959 Binary files /dev/null and b/data/art824/mtank/mtank_fire_00_1.png differ diff --git a/data/art824/mtank/mtank_fire_00_2.png b/data/art824/mtank/mtank_fire_00_2.png new file mode 100644 index 0000000..9d3a166 Binary files /dev/null and b/data/art824/mtank/mtank_fire_00_2.png differ diff --git a/data/art824/mtank/mtank_fire_00_3.png b/data/art824/mtank/mtank_fire_00_3.png new file mode 100644 index 0000000..df4a235 Binary files /dev/null and b/data/art824/mtank/mtank_fire_00_3.png differ diff --git a/data/art824/mtank/mtank_fire_00_4.png b/data/art824/mtank/mtank_fire_00_4.png new file mode 100644 index 0000000..928279e Binary files /dev/null and b/data/art824/mtank/mtank_fire_00_4.png differ diff --git a/data/art824/mtank/mtank_fire_01_0.png b/data/art824/mtank/mtank_fire_01_0.png new file mode 100644 index 0000000..19ae68a Binary files /dev/null and b/data/art824/mtank/mtank_fire_01_0.png differ diff --git a/data/art824/mtank/mtank_fire_01_1.png b/data/art824/mtank/mtank_fire_01_1.png new file mode 100644 index 0000000..01cedd5 Binary files /dev/null and b/data/art824/mtank/mtank_fire_01_1.png differ diff --git a/data/art824/mtank/mtank_fire_01_2.png b/data/art824/mtank/mtank_fire_01_2.png new file mode 100644 index 0000000..4bde660 Binary files /dev/null and b/data/art824/mtank/mtank_fire_01_2.png differ diff --git a/data/art824/mtank/mtank_fire_01_3.png b/data/art824/mtank/mtank_fire_01_3.png new file mode 100644 index 0000000..1f92cc1 Binary files /dev/null and b/data/art824/mtank/mtank_fire_01_3.png differ diff --git a/data/art824/mtank/mtank_fire_01_4.png b/data/art824/mtank/mtank_fire_01_4.png new file mode 100644 index 0000000..1501967 Binary files /dev/null and b/data/art824/mtank/mtank_fire_01_4.png differ diff --git a/data/art824/mtank/mtank_fire_02_0.png b/data/art824/mtank/mtank_fire_02_0.png new file mode 100644 index 0000000..4411967 Binary files /dev/null and b/data/art824/mtank/mtank_fire_02_0.png differ diff --git a/data/art824/mtank/mtank_fire_02_1.png b/data/art824/mtank/mtank_fire_02_1.png new file mode 100644 index 0000000..dcbc398 Binary files /dev/null and b/data/art824/mtank/mtank_fire_02_1.png differ diff --git a/data/art824/mtank/mtank_fire_02_2.png b/data/art824/mtank/mtank_fire_02_2.png new file mode 100644 index 0000000..ec48dbd Binary files /dev/null and b/data/art824/mtank/mtank_fire_02_2.png differ diff --git a/data/art824/mtank/mtank_fire_02_3.png b/data/art824/mtank/mtank_fire_02_3.png new file mode 100644 index 0000000..153ef4d Binary files /dev/null and b/data/art824/mtank/mtank_fire_02_3.png differ diff --git a/data/art824/mtank/mtank_fire_02_4.png b/data/art824/mtank/mtank_fire_02_4.png new file mode 100644 index 0000000..6882416 Binary files /dev/null and b/data/art824/mtank/mtank_fire_02_4.png differ diff --git a/data/art824/mtank/mtank_fire_03_0.png b/data/art824/mtank/mtank_fire_03_0.png new file mode 100644 index 0000000..6620c9d Binary files /dev/null and b/data/art824/mtank/mtank_fire_03_0.png differ diff --git a/data/art824/mtank/mtank_fire_03_1.png b/data/art824/mtank/mtank_fire_03_1.png new file mode 100644 index 0000000..31bcdca Binary files /dev/null and b/data/art824/mtank/mtank_fire_03_1.png differ diff --git a/data/art824/mtank/mtank_fire_03_2.png b/data/art824/mtank/mtank_fire_03_2.png new file mode 100644 index 0000000..fce7614 Binary files /dev/null and b/data/art824/mtank/mtank_fire_03_2.png differ diff --git a/data/art824/mtank/mtank_fire_03_3.png b/data/art824/mtank/mtank_fire_03_3.png new file mode 100644 index 0000000..b6555c2 Binary files /dev/null and b/data/art824/mtank/mtank_fire_03_3.png differ diff --git a/data/art824/mtank/mtank_fire_03_4.png b/data/art824/mtank/mtank_fire_03_4.png new file mode 100644 index 0000000..774b1ad Binary files /dev/null and b/data/art824/mtank/mtank_fire_03_4.png differ diff --git a/data/art824/mtank/mtank_fire_04_0.png b/data/art824/mtank/mtank_fire_04_0.png new file mode 100644 index 0000000..3336747 Binary files /dev/null and b/data/art824/mtank/mtank_fire_04_0.png differ diff --git a/data/art824/mtank/mtank_fire_04_1.png b/data/art824/mtank/mtank_fire_04_1.png new file mode 100644 index 0000000..56ba604 Binary files /dev/null and b/data/art824/mtank/mtank_fire_04_1.png differ diff --git a/data/art824/mtank/mtank_fire_04_2.png b/data/art824/mtank/mtank_fire_04_2.png new file mode 100644 index 0000000..297e940 Binary files /dev/null and b/data/art824/mtank/mtank_fire_04_2.png differ diff --git a/data/art824/mtank/mtank_fire_04_3.png b/data/art824/mtank/mtank_fire_04_3.png new file mode 100644 index 0000000..e61c386 Binary files /dev/null and b/data/art824/mtank/mtank_fire_04_3.png differ diff --git a/data/art824/mtank/mtank_fire_04_4.png b/data/art824/mtank/mtank_fire_04_4.png new file mode 100644 index 0000000..117c770 Binary files /dev/null and b/data/art824/mtank/mtank_fire_04_4.png differ diff --git a/data/art824/mtank/mtank_fire_05_0.png b/data/art824/mtank/mtank_fire_05_0.png new file mode 100644 index 0000000..c8e4201 Binary files /dev/null and b/data/art824/mtank/mtank_fire_05_0.png differ diff --git a/data/art824/mtank/mtank_fire_05_1.png b/data/art824/mtank/mtank_fire_05_1.png new file mode 100644 index 0000000..cce9240 Binary files /dev/null and b/data/art824/mtank/mtank_fire_05_1.png differ diff --git a/data/art824/mtank/mtank_fire_05_2.png b/data/art824/mtank/mtank_fire_05_2.png new file mode 100644 index 0000000..c39ba1c Binary files /dev/null and b/data/art824/mtank/mtank_fire_05_2.png differ diff --git a/data/art824/mtank/mtank_fire_05_3.png b/data/art824/mtank/mtank_fire_05_3.png new file mode 100644 index 0000000..0c1e4f3 Binary files /dev/null and b/data/art824/mtank/mtank_fire_05_3.png differ diff --git a/data/art824/mtank/mtank_fire_05_4.png b/data/art824/mtank/mtank_fire_05_4.png new file mode 100644 index 0000000..3a8dda7 Binary files /dev/null and b/data/art824/mtank/mtank_fire_05_4.png differ diff --git a/data/art824/mtank/mtank_fire_06_0.png b/data/art824/mtank/mtank_fire_06_0.png new file mode 100644 index 0000000..d68f395 Binary files /dev/null and b/data/art824/mtank/mtank_fire_06_0.png differ diff --git a/data/art824/mtank/mtank_fire_06_1.png b/data/art824/mtank/mtank_fire_06_1.png new file mode 100644 index 0000000..eb26a8d Binary files /dev/null and b/data/art824/mtank/mtank_fire_06_1.png differ diff --git a/data/art824/mtank/mtank_fire_06_2.png b/data/art824/mtank/mtank_fire_06_2.png new file mode 100644 index 0000000..e6604b1 Binary files /dev/null and b/data/art824/mtank/mtank_fire_06_2.png differ diff --git a/data/art824/mtank/mtank_fire_06_3.png b/data/art824/mtank/mtank_fire_06_3.png new file mode 100644 index 0000000..4ab5418 Binary files /dev/null and b/data/art824/mtank/mtank_fire_06_3.png differ diff --git a/data/art824/mtank/mtank_fire_06_4.png b/data/art824/mtank/mtank_fire_06_4.png new file mode 100644 index 0000000..f703125 Binary files /dev/null and b/data/art824/mtank/mtank_fire_06_4.png differ diff --git a/data/art824/mtank/mtank_fire_07_0.png b/data/art824/mtank/mtank_fire_07_0.png new file mode 100644 index 0000000..e994a88 Binary files /dev/null and b/data/art824/mtank/mtank_fire_07_0.png differ diff --git a/data/art824/mtank/mtank_fire_07_1.png b/data/art824/mtank/mtank_fire_07_1.png new file mode 100644 index 0000000..35794ab Binary files /dev/null and b/data/art824/mtank/mtank_fire_07_1.png differ diff --git a/data/art824/mtank/mtank_fire_07_2.png b/data/art824/mtank/mtank_fire_07_2.png new file mode 100644 index 0000000..c3fd146 Binary files /dev/null and b/data/art824/mtank/mtank_fire_07_2.png differ diff --git a/data/art824/mtank/mtank_fire_07_3.png b/data/art824/mtank/mtank_fire_07_3.png new file mode 100644 index 0000000..ff03b06 Binary files /dev/null and b/data/art824/mtank/mtank_fire_07_3.png differ diff --git a/data/art824/mtank/mtank_fire_07_4.png b/data/art824/mtank/mtank_fire_07_4.png new file mode 100644 index 0000000..055111d Binary files /dev/null and b/data/art824/mtank/mtank_fire_07_4.png differ diff --git a/data/art824/mtank/mtank_fire_08_0.png b/data/art824/mtank/mtank_fire_08_0.png new file mode 100644 index 0000000..a59dffd Binary files /dev/null and b/data/art824/mtank/mtank_fire_08_0.png differ diff --git a/data/art824/mtank/mtank_fire_08_1.png b/data/art824/mtank/mtank_fire_08_1.png new file mode 100644 index 0000000..87945d5 Binary files /dev/null and b/data/art824/mtank/mtank_fire_08_1.png differ diff --git a/data/art824/mtank/mtank_fire_08_2.png b/data/art824/mtank/mtank_fire_08_2.png new file mode 100644 index 0000000..f1a599a Binary files /dev/null and b/data/art824/mtank/mtank_fire_08_2.png differ diff --git a/data/art824/mtank/mtank_fire_08_3.png b/data/art824/mtank/mtank_fire_08_3.png new file mode 100644 index 0000000..4aeecfa Binary files /dev/null and b/data/art824/mtank/mtank_fire_08_3.png differ diff --git a/data/art824/mtank/mtank_fire_08_4.png b/data/art824/mtank/mtank_fire_08_4.png new file mode 100644 index 0000000..9c55049 Binary files /dev/null and b/data/art824/mtank/mtank_fire_08_4.png differ diff --git a/data/art824/mtank/mtank_fire_09_0.png b/data/art824/mtank/mtank_fire_09_0.png new file mode 100644 index 0000000..7df2a44 Binary files /dev/null and b/data/art824/mtank/mtank_fire_09_0.png differ diff --git a/data/art824/mtank/mtank_fire_09_1.png b/data/art824/mtank/mtank_fire_09_1.png new file mode 100644 index 0000000..0b71344 Binary files /dev/null and b/data/art824/mtank/mtank_fire_09_1.png differ diff --git a/data/art824/mtank/mtank_fire_09_2.png b/data/art824/mtank/mtank_fire_09_2.png new file mode 100644 index 0000000..7a82dbe Binary files /dev/null and b/data/art824/mtank/mtank_fire_09_2.png differ diff --git a/data/art824/mtank/mtank_fire_09_3.png b/data/art824/mtank/mtank_fire_09_3.png new file mode 100644 index 0000000..94a189b Binary files /dev/null and b/data/art824/mtank/mtank_fire_09_3.png differ diff --git a/data/art824/mtank/mtank_fire_09_4.png b/data/art824/mtank/mtank_fire_09_4.png new file mode 100644 index 0000000..90e480a Binary files /dev/null and b/data/art824/mtank/mtank_fire_09_4.png differ diff --git a/data/art824/mtank/mtank_fire_10_0.png b/data/art824/mtank/mtank_fire_10_0.png new file mode 100644 index 0000000..1ca8168 Binary files /dev/null and b/data/art824/mtank/mtank_fire_10_0.png differ diff --git a/data/art824/mtank/mtank_fire_10_1.png b/data/art824/mtank/mtank_fire_10_1.png new file mode 100644 index 0000000..3295d45 Binary files /dev/null and b/data/art824/mtank/mtank_fire_10_1.png differ diff --git a/data/art824/mtank/mtank_fire_10_2.png b/data/art824/mtank/mtank_fire_10_2.png new file mode 100644 index 0000000..ba057c6 Binary files /dev/null and b/data/art824/mtank/mtank_fire_10_2.png differ diff --git a/data/art824/mtank/mtank_fire_10_3.png b/data/art824/mtank/mtank_fire_10_3.png new file mode 100644 index 0000000..12a5062 Binary files /dev/null and b/data/art824/mtank/mtank_fire_10_3.png differ diff --git a/data/art824/mtank/mtank_fire_10_4.png b/data/art824/mtank/mtank_fire_10_4.png new file mode 100644 index 0000000..1342dfa Binary files /dev/null and b/data/art824/mtank/mtank_fire_10_4.png differ diff --git a/data/art824/mtank/mtank_fire_11_0.png b/data/art824/mtank/mtank_fire_11_0.png new file mode 100644 index 0000000..1d49ab8 Binary files /dev/null and b/data/art824/mtank/mtank_fire_11_0.png differ diff --git a/data/art824/mtank/mtank_fire_11_1.png b/data/art824/mtank/mtank_fire_11_1.png new file mode 100644 index 0000000..b6d207f Binary files /dev/null and b/data/art824/mtank/mtank_fire_11_1.png differ diff --git a/data/art824/mtank/mtank_fire_11_2.png b/data/art824/mtank/mtank_fire_11_2.png new file mode 100644 index 0000000..03b104d Binary files /dev/null and b/data/art824/mtank/mtank_fire_11_2.png differ diff --git a/data/art824/mtank/mtank_fire_11_3.png b/data/art824/mtank/mtank_fire_11_3.png new file mode 100644 index 0000000..ae2386f Binary files /dev/null and b/data/art824/mtank/mtank_fire_11_3.png differ diff --git a/data/art824/mtank/mtank_fire_11_4.png b/data/art824/mtank/mtank_fire_11_4.png new file mode 100644 index 0000000..c08b340 Binary files /dev/null and b/data/art824/mtank/mtank_fire_11_4.png differ diff --git a/data/art824/mtank/mtank_fire_12_0.png b/data/art824/mtank/mtank_fire_12_0.png new file mode 100644 index 0000000..3c712c1 Binary files /dev/null and b/data/art824/mtank/mtank_fire_12_0.png differ diff --git a/data/art824/mtank/mtank_fire_12_1.png b/data/art824/mtank/mtank_fire_12_1.png new file mode 100644 index 0000000..a589f6d Binary files /dev/null and b/data/art824/mtank/mtank_fire_12_1.png differ diff --git a/data/art824/mtank/mtank_fire_12_2.png b/data/art824/mtank/mtank_fire_12_2.png new file mode 100644 index 0000000..8cb4160 Binary files /dev/null and b/data/art824/mtank/mtank_fire_12_2.png differ diff --git a/data/art824/mtank/mtank_fire_12_3.png b/data/art824/mtank/mtank_fire_12_3.png new file mode 100644 index 0000000..1ae8a51 Binary files /dev/null and b/data/art824/mtank/mtank_fire_12_3.png differ diff --git a/data/art824/mtank/mtank_fire_12_4.png b/data/art824/mtank/mtank_fire_12_4.png new file mode 100644 index 0000000..7f52029 Binary files /dev/null and b/data/art824/mtank/mtank_fire_12_4.png differ diff --git a/data/art824/mtank/mtank_fire_13_0.png b/data/art824/mtank/mtank_fire_13_0.png new file mode 100644 index 0000000..57a3f7d Binary files /dev/null and b/data/art824/mtank/mtank_fire_13_0.png differ diff --git a/data/art824/mtank/mtank_fire_13_1.png b/data/art824/mtank/mtank_fire_13_1.png new file mode 100644 index 0000000..8086931 Binary files /dev/null and b/data/art824/mtank/mtank_fire_13_1.png differ diff --git a/data/art824/mtank/mtank_fire_13_2.png b/data/art824/mtank/mtank_fire_13_2.png new file mode 100644 index 0000000..3d1fcb4 Binary files /dev/null and b/data/art824/mtank/mtank_fire_13_2.png differ diff --git a/data/art824/mtank/mtank_fire_13_3.png b/data/art824/mtank/mtank_fire_13_3.png new file mode 100644 index 0000000..d248c2e Binary files /dev/null and b/data/art824/mtank/mtank_fire_13_3.png differ diff --git a/data/art824/mtank/mtank_fire_13_4.png b/data/art824/mtank/mtank_fire_13_4.png new file mode 100644 index 0000000..e60f1d3 Binary files /dev/null and b/data/art824/mtank/mtank_fire_13_4.png differ diff --git a/data/art824/mtank/mtank_fire_14_0.png b/data/art824/mtank/mtank_fire_14_0.png new file mode 100644 index 0000000..fbedba5 Binary files /dev/null and b/data/art824/mtank/mtank_fire_14_0.png differ diff --git a/data/art824/mtank/mtank_fire_14_1.png b/data/art824/mtank/mtank_fire_14_1.png new file mode 100644 index 0000000..375d765 Binary files /dev/null and b/data/art824/mtank/mtank_fire_14_1.png differ diff --git a/data/art824/mtank/mtank_fire_14_2.png b/data/art824/mtank/mtank_fire_14_2.png new file mode 100644 index 0000000..a988ded Binary files /dev/null and b/data/art824/mtank/mtank_fire_14_2.png differ diff --git a/data/art824/mtank/mtank_fire_14_3.png b/data/art824/mtank/mtank_fire_14_3.png new file mode 100644 index 0000000..d1356c1 Binary files /dev/null and b/data/art824/mtank/mtank_fire_14_3.png differ diff --git a/data/art824/mtank/mtank_fire_14_4.png b/data/art824/mtank/mtank_fire_14_4.png new file mode 100644 index 0000000..58c34c2 Binary files /dev/null and b/data/art824/mtank/mtank_fire_14_4.png differ diff --git a/data/art824/mtank/mtank_fire_15_0.png b/data/art824/mtank/mtank_fire_15_0.png new file mode 100644 index 0000000..a9bf4fc Binary files /dev/null and b/data/art824/mtank/mtank_fire_15_0.png differ diff --git a/data/art824/mtank/mtank_fire_15_1.png b/data/art824/mtank/mtank_fire_15_1.png new file mode 100644 index 0000000..6ef3c4a Binary files /dev/null and b/data/art824/mtank/mtank_fire_15_1.png differ diff --git a/data/art824/mtank/mtank_fire_15_2.png b/data/art824/mtank/mtank_fire_15_2.png new file mode 100644 index 0000000..1b500dc Binary files /dev/null and b/data/art824/mtank/mtank_fire_15_2.png differ diff --git a/data/art824/mtank/mtank_fire_15_3.png b/data/art824/mtank/mtank_fire_15_3.png new file mode 100644 index 0000000..276dc81 Binary files /dev/null and b/data/art824/mtank/mtank_fire_15_3.png differ diff --git a/data/art824/mtank/mtank_fire_15_4.png b/data/art824/mtank/mtank_fire_15_4.png new file mode 100644 index 0000000..745b818 Binary files /dev/null and b/data/art824/mtank/mtank_fire_15_4.png differ diff --git a/data/art824/mtower.amx b/data/art824/mtower.amx new file mode 100644 index 0000000..8b94857 --- /dev/null +++ b/data/art824/mtower.amx @@ -0,0 +1,1987 @@ + + + + + +80 +true + + +<_x0030_ href="#ref-5"/> +<_x0031_ href="#ref-6"/> +<_x0032_ href="#ref-7"/> +<_x0033_ href="#ref-8"/> +<_x0034_ href="#ref-9"/> +<_x0035_ href="#ref-10"/> +<_x0036_ href="#ref-11"/> +<_x0037_ href="#ref-12"/> +<_x0038_ href="#ref-13"/> +<_x0039_ href="#ref-14"/> +<_x0031_0 href="#ref-15"/> +<_x0031_1 href="#ref-16"/> +<_x0031_2 href="#ref-17"/> +<_x0031_3 href="#ref-18"/> +<_x0031_4 href="#ref-19"/> +<_x0031_5 href="#ref-20"/> +<_x0031_6 href="#ref-21"/> +<_x0031_7 href="#ref-22"/> +<_x0031_8 href="#ref-23"/> +<_x0031_9 href="#ref-24"/> +<_x0032_0 href="#ref-25"/> +<_x0032_1 href="#ref-26"/> +<_x0032_2 href="#ref-27"/> +<_x0032_3 href="#ref-28"/> +<_x0032_4 href="#ref-29"/> +<_x0032_5 href="#ref-30"/> +<_x0032_6 href="#ref-31"/> +<_x0032_7 href="#ref-32"/> +<_x0032_8 href="#ref-33"/> +<_x0032_9 href="#ref-34"/> +<_x0033_0 href="#ref-35"/> +<_x0033_1 href="#ref-36"/> +<_x0033_2 href="#ref-37"/> +<_x0033_3 href="#ref-38"/> +<_x0033_4 href="#ref-39"/> +<_x0033_5 href="#ref-40"/> +<_x0033_6 href="#ref-41"/> +<_x0033_7 href="#ref-42"/> +<_x0033_8 href="#ref-43"/> +<_x0033_9 href="#ref-44"/> +<_x0034_0 href="#ref-45"/> +<_x0034_1 href="#ref-46"/> +<_x0034_2 href="#ref-47"/> +<_x0034_3 href="#ref-48"/> +<_x0034_4 href="#ref-49"/> +<_x0034_5 href="#ref-50"/> +<_x0034_6 href="#ref-51"/> +<_x0034_7 href="#ref-52"/> +<_x0034_8 href="#ref-53"/> +<_x0034_9 href="#ref-54"/> +<_x0035_0 href="#ref-55"/> +<_x0035_1 href="#ref-56"/> +<_x0035_2 href="#ref-57"/> +<_x0035_3 href="#ref-58"/> + + +<_x0030_ href="#ref-59"/> +<_x0031_ href="#ref-60"/> +<_x0032_ href="#ref-61"/> +<_x0033_ href="#ref-62"/> +<_x0034_ href="#ref-63"/> +<_x0035_ href="#ref-64"/> +<_x0036_ href="#ref-65"/> +<_x0037_ href="#ref-66"/> +<_x0038_ href="#ref-67"/> +<_x0039_ href="#ref-68"/> +<_x0031_0 href="#ref-69"/> +<_x0031_1 href="#ref-70"/> +<_x0031_2 href="#ref-71"/> +<_x0031_3 href="#ref-72"/> +<_x0031_4 href="#ref-73"/> +<_x0031_5 href="#ref-74"/> +<_x0031_6 href="#ref-75"/> +<_x0031_7 href="#ref-76"/> +<_x0031_8 href="#ref-77"/> +<_x0031_9 href="#ref-78"/> +<_x0032_0 href="#ref-79"/> + + +mtower\mtower_turret_15_2.png + + +mtower\mtower_turret_15_0.png + + +mtower\mtower_turret_15_1.png + + +mtower\mtower_turret_00_2.png + + +mtower\mtower_turret_00_1.png + + +mtower\mtower_turret_00_0.png + + +mtower\mtower_turret_01_2.png + + +mtower\mtower_turret_01_1.png + + +mtower\mtower_turret_01_0.png + + +mtower\mtower_turret_02_2.png + + +mtower\mtower_turret_02_1.png + + +mtower\mtower_turret_02_0.png + + +mtower\mtower_turret_03_2.png + + +mtower\mtower_turret_03_1.png + + +mtower\mtower_turret_03_0.png + + +mtower\mtower_turret_04_2.png + + +mtower\mtower_turret_04_1.png + + +mtower\mtower_turret_04_0.png + + +mtower\mtower_turret_05_2.png + + +mtower\mtower_turret_05_1.png + + +mtower\mtower_turret_05_0.png + + +mtower\mtower_turret_06_2.png + + +mtower\mtower_turret_06_1.png + + +mtower\mtower_turret_06_0.png + + +mtower\mtower_turret_07_2.png + + +mtower\mtower_turret_07_1.png + + +mtower\mtower_turret_07_0.png + + +mtower\mtower_turret_08_2.png + + +mtower\mtower_turret_08_1.png + + +mtower\mtower_turret_08_0.png + + +mtower\mtower_turret_09_2.png + + +mtower\mtower_turret_09_1.png + + +mtower\mtower_turret_09_0.png + + +mtower\mtower_turret_10_2.png + + +mtower\mtower_turret_10_1.png + + +mtower\mtower_turret_10_0.png + + +mtower\mtower_turret_11_2.png + + +mtower\mtower_turret_11_1.png + + +mtower\mtower_turret_11_0.png + + +mtower\mtower_turret_12_2.png + + +mtower\mtower_turret_12_1.png + + +mtower\mtower_turret_12_0.png + + +mtower\mtower_turret_13_2.png + + +mtower\mtower_turret_13_1.png + + +mtower\mtower_turret_13_0.png + + +mtower\mtower_turret_14_2.png + + +mtower\mtower_turret_14_1.png + + +mtower\mtower_turret_14_0.png + + +mtower\mtower_turret_15_2.png + + +mtower\mtower_turret_15_1.png + + +mtower\mtower_turret_15_0.png + + +mtower\mtower_base_2_0.png + + +mtower\mtower_base_1_0.png + + +mtower\mtower_base_0_0.png + + +base 0 +0 +1 +<_x0030_ href="#ref-135"/> + + +base 1 +0 +1 +<_x0030_ href="#ref-137"/> + + +base 2 +0 +1 +<_x0030_ href="#ref-139"/> + + +turret 00 +0 +5 +<_x0030_ href="#ref-141"/> +<_x0031_ href="#ref-142"/> +<_x0032_ href="#ref-143"/> +<_x0033_ href="#ref-144"/> +<_x0034_ href="#ref-145"/> + + +turret 01 +0 +5 +<_x0030_ href="#ref-147"/> +<_x0031_ href="#ref-148"/> +<_x0032_ href="#ref-149"/> +<_x0033_ href="#ref-150"/> +<_x0034_ href="#ref-151"/> + + +turret 02 +0 +5 +<_x0030_ href="#ref-153"/> +<_x0031_ href="#ref-154"/> +<_x0032_ href="#ref-155"/> +<_x0033_ href="#ref-156"/> +<_x0034_ href="#ref-157"/> + + +turret 03 +0 +5 +<_x0030_ href="#ref-159"/> +<_x0031_ href="#ref-160"/> +<_x0032_ href="#ref-161"/> +<_x0033_ href="#ref-162"/> +<_x0034_ href="#ref-163"/> + + +turret 04 +0 +5 +<_x0030_ href="#ref-165"/> +<_x0031_ href="#ref-166"/> +<_x0032_ href="#ref-167"/> +<_x0033_ href="#ref-168"/> +<_x0034_ href="#ref-169"/> + + +turret 05 +0 +5 +<_x0030_ href="#ref-171"/> +<_x0031_ href="#ref-172"/> +<_x0032_ href="#ref-173"/> +<_x0033_ href="#ref-174"/> +<_x0034_ href="#ref-175"/> + + +turret 06 +0 +5 +<_x0030_ href="#ref-177"/> +<_x0031_ href="#ref-178"/> +<_x0032_ href="#ref-179"/> +<_x0033_ href="#ref-180"/> +<_x0034_ href="#ref-181"/> + + +turret 07 +0 +5 +<_x0030_ href="#ref-183"/> +<_x0031_ href="#ref-184"/> +<_x0032_ href="#ref-185"/> +<_x0033_ href="#ref-186"/> +<_x0034_ href="#ref-187"/> + + +turret 08 +0 +5 +<_x0030_ href="#ref-189"/> +<_x0031_ href="#ref-190"/> +<_x0032_ href="#ref-191"/> +<_x0033_ href="#ref-192"/> +<_x0034_ href="#ref-193"/> + + +turret 09 +0 +5 +<_x0030_ href="#ref-195"/> +<_x0031_ href="#ref-196"/> +<_x0032_ href="#ref-197"/> +<_x0033_ href="#ref-198"/> +<_x0034_ href="#ref-199"/> + + +turret 10 +0 +5 +<_x0030_ href="#ref-201"/> +<_x0031_ href="#ref-202"/> +<_x0032_ href="#ref-203"/> +<_x0033_ href="#ref-204"/> +<_x0034_ href="#ref-205"/> + + +turret 11 +0 +5 +<_x0030_ href="#ref-207"/> +<_x0031_ href="#ref-208"/> +<_x0032_ href="#ref-209"/> +<_x0033_ href="#ref-210"/> +<_x0034_ href="#ref-211"/> + + +turret 12 +0 +5 +<_x0030_ href="#ref-213"/> +<_x0031_ href="#ref-214"/> +<_x0032_ href="#ref-215"/> +<_x0033_ href="#ref-216"/> +<_x0034_ href="#ref-217"/> + + +turret 13 +0 +5 +<_x0030_ href="#ref-219"/> +<_x0031_ href="#ref-220"/> +<_x0032_ href="#ref-221"/> +<_x0033_ href="#ref-222"/> +<_x0034_ href="#ref-223"/> + + +turret 14 +0 +5 +<_x0030_ href="#ref-225"/> +<_x0031_ href="#ref-226"/> +<_x0032_ href="#ref-227"/> +<_x0033_ href="#ref-228"/> +<_x0034_ href="#ref-229"/> + + +turret 15 +0 +5 +<_x0030_ href="#ref-231"/> +<_x0031_ href="#ref-232"/> +<_x0032_ href="#ref-233"/> +<_x0033_ href="#ref-234"/> +<_x0034_ href="#ref-235"/> + + +icon +0 +1 +<_x0030_ href="#ref-237"/> + + +help +0 +1 +<_x0030_ href="#ref-239"/> + + + +0 + +12 +5 + + + + +0 + +12 +5 + + + + +0 + +12 +5 + + + + +0 + +0 +0 + + + + +0 + +0 +-21 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +7 +-21 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +16 +-16 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +20 +-9 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +21 +-1 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +18 +8 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +16 +13 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +9 +19 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +20 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-11 +18 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-17 +15 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-21 +6 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-21 +-1 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-20 +-10 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-15 +-16 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-8 +-21 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + +<_x0030_ href="#ref-326"/> + + +<_x0030_ href="#ref-327"/> + + +<_x0030_ href="#ref-328"/> + + +<_x0030_ href="#ref-329"/> + + +<_x0030_ href="#ref-330"/> + + +<_x0030_ href="#ref-331"/> + + +<_x0030_ href="#ref-332"/> + + +<_x0030_ href="#ref-333"/> + + +<_x0030_ href="#ref-334"/> + + +<_x0030_ href="#ref-335"/> + + +<_x0030_ href="#ref-336"/> + + +<_x0030_ href="#ref-337"/> + + +<_x0030_ href="#ref-338"/> + + +<_x0030_ href="#ref-339"/> + + +<_x0030_ href="#ref-340"/> + + +<_x0030_ href="#ref-341"/> + + +<_x0030_ href="#ref-342"/> + + +<_x0030_ href="#ref-343"/> + + +<_x0030_ href="#ref-344"/> + + +<_x0030_ href="#ref-345"/> + + +<_x0030_ href="#ref-346"/> + + +<_x0030_ href="#ref-347"/> + + +<_x0030_ href="#ref-348"/> + + +<_x0030_ href="#ref-349"/> + + +<_x0030_ href="#ref-350"/> + + +<_x0030_ href="#ref-351"/> + + +<_x0030_ href="#ref-352"/> + + +<_x0030_ href="#ref-353"/> + + +<_x0030_ href="#ref-354"/> + + +<_x0030_ href="#ref-355"/> + + +<_x0030_ href="#ref-356"/> + + +<_x0030_ href="#ref-357"/> + + +<_x0030_ href="#ref-358"/> + + +<_x0030_ href="#ref-359"/> + + +<_x0030_ href="#ref-360"/> + + +<_x0030_ href="#ref-361"/> + + +<_x0030_ href="#ref-362"/> + + +<_x0030_ href="#ref-363"/> + + +<_x0030_ href="#ref-364"/> + + +<_x0030_ href="#ref-365"/> + + +<_x0030_ href="#ref-366"/> + + +<_x0030_ href="#ref-367"/> + + +<_x0030_ href="#ref-368"/> + + +<_x0030_ href="#ref-369"/> + + +<_x0030_ href="#ref-370"/> + + +<_x0030_ href="#ref-371"/> + + +<_x0030_ href="#ref-372"/> + + +<_x0030_ href="#ref-373"/> + + +<_x0030_ href="#ref-374"/> + + +<_x0030_ href="#ref-375"/> + + +<_x0030_ href="#ref-376"/> + + +<_x0030_ href="#ref-377"/> + + +<_x0030_ href="#ref-378"/> + + +<_x0030_ href="#ref-379"/> + + +<_x0030_ href="#ref-380"/> + + +<_x0030_ href="#ref-381"/> + + +<_x0030_ href="#ref-382"/> + + +<_x0030_ href="#ref-383"/> + + +<_x0030_ href="#ref-384"/> + + +<_x0030_ href="#ref-385"/> + + +<_x0030_ href="#ref-386"/> + + +<_x0030_ href="#ref-387"/> + + +<_x0030_ href="#ref-388"/> + + +<_x0030_ href="#ref-389"/> + + +<_x0030_ href="#ref-390"/> + + +<_x0030_ href="#ref-391"/> + + +<_x0030_ href="#ref-392"/> + + +<_x0030_ href="#ref-393"/> + + +<_x0030_ href="#ref-394"/> + + +<_x0030_ href="#ref-395"/> + + +<_x0030_ href="#ref-396"/> + + +<_x0030_ href="#ref-397"/> + + +<_x0030_ href="#ref-398"/> + + +<_x0030_ href="#ref-399"/> + + +<_x0030_ href="#ref-400"/> + + +<_x0030_ href="#ref-401"/> + + +<_x0030_ href="#ref-402"/> + + +<_x0030_ href="#ref-403"/> + + +<_x0030_ href="#ref-404"/> + + +<_x0030_ href="#ref-405"/> + + +<_x0030_ href="#ref-406"/> + + +<_x0030_ href="#ref-407"/> + + +<_x0030_ href="#ref-408"/> + + +<_x0030_ href="#ref-409"/> +<_x0031_ href="#ref-410"/> + + +<_x0030_ href="#ref-411"/> +<_x0031_ href="#ref-412"/> + + + +2 +0 + + + + + +2 +0 + + + + + +2 +0 + + + + + +15 +16 + + + + + +15 +16 + + + + + +15 +16 + + + + + +15 +16 + + + + + +15 +16 + + + + + +15 +16 + + + + + +15 +16 + + + + + +15 +16 + + + + + +15 +16 + + + + + +15 +16 + + + + + +15 +16 + + + + + +15 +16 + + + + + +15 +16 + + + + + +15 +16 + + + + + +15 +16 + + + + + +12 +16 + + + + + +12 +16 + + + + + +12 +16 + + + + + +12 +16 + + + + + +12 +16 + + + + + +12 +16 + + + + + +12 +16 + + + + + +12 +16 + + + + + +12 +16 + + + + + +12 +16 + + + + + +11 +14 + + + + + +11 +14 + + + + + +11 +14 + + + + + +11 +14 + + + + + +11 +14 + + + + + +12 +14 + + + + + +12 +14 + + + + + +12 +14 + + + + + +12 +14 + + + + + +12 +14 + + + + + +12 +12 + + + + + +12 +12 + + + + + +12 +12 + + + + + +12 +12 + + + + + +12 +12 + + + + + +14 +10 + + + + + +14 +10 + + + + + +14 +10 + + + + + +14 +10 + + + + + +14 +10 + + + + + +15 +11 + + + + + +15 +11 + + + + + +15 +11 + + + + + +15 +11 + + + + + +15 +11 + + + + + +18 +13 + + + + + +18 +13 + + + + + +18 +13 + + + + + +18 +13 + + + + + +18 +13 + + + + + +18 +14 + + + + + +18 +14 + + + + + +18 +14 + + + + + +18 +14 + + + + + +18 +14 + + + + + +19 +16 + + + + + +19 +16 + + + + + +19 +16 + + + + + +19 +16 + + + + + +19 +16 + + + + + +18 +17 + + + + + +18 +17 + + + + + +18 +17 + + + + + +18 +17 + + + + + +18 +17 + + + + + +15 +15 + + + + + +15 +15 + + + + + +15 +15 + + + + + +15 +15 + + + + + +15 +15 + + + + + +16 +19 + + + + + +16 +19 + + + + + +16 +19 + + + + + +16 +19 + + + + + +16 +19 + + + + + +1 +11 + + + + + +4 +0 + + + + + +13 +25 + + + + + +15 +15 + + + + + diff --git a/data/art824/mtower/mtower_base_0_0.png b/data/art824/mtower/mtower_base_0_0.png new file mode 100644 index 0000000..ebd6aee Binary files /dev/null and b/data/art824/mtower/mtower_base_0_0.png differ diff --git a/data/art824/mtower/mtower_base_1_0.png b/data/art824/mtower/mtower_base_1_0.png new file mode 100644 index 0000000..b4c3268 Binary files /dev/null and b/data/art824/mtower/mtower_base_1_0.png differ diff --git a/data/art824/mtower/mtower_base_2_0.png b/data/art824/mtower/mtower_base_2_0.png new file mode 100644 index 0000000..80c2e3d Binary files /dev/null and b/data/art824/mtower/mtower_base_2_0.png differ diff --git a/data/art824/mtower/mtower_turret_00_0.png b/data/art824/mtower/mtower_turret_00_0.png new file mode 100644 index 0000000..af2a813 Binary files /dev/null and b/data/art824/mtower/mtower_turret_00_0.png differ diff --git a/data/art824/mtower/mtower_turret_00_1.png b/data/art824/mtower/mtower_turret_00_1.png new file mode 100644 index 0000000..6e89034 Binary files /dev/null and b/data/art824/mtower/mtower_turret_00_1.png differ diff --git a/data/art824/mtower/mtower_turret_00_2.png b/data/art824/mtower/mtower_turret_00_2.png new file mode 100644 index 0000000..980f7b0 Binary files /dev/null and b/data/art824/mtower/mtower_turret_00_2.png differ diff --git a/data/art824/mtower/mtower_turret_01_0.png b/data/art824/mtower/mtower_turret_01_0.png new file mode 100644 index 0000000..8b7d003 Binary files /dev/null and b/data/art824/mtower/mtower_turret_01_0.png differ diff --git a/data/art824/mtower/mtower_turret_01_1.png b/data/art824/mtower/mtower_turret_01_1.png new file mode 100644 index 0000000..b96856c Binary files /dev/null and b/data/art824/mtower/mtower_turret_01_1.png differ diff --git a/data/art824/mtower/mtower_turret_01_2.png b/data/art824/mtower/mtower_turret_01_2.png new file mode 100644 index 0000000..fa589de Binary files /dev/null and b/data/art824/mtower/mtower_turret_01_2.png differ diff --git a/data/art824/mtower/mtower_turret_02_0.png b/data/art824/mtower/mtower_turret_02_0.png new file mode 100644 index 0000000..f02d766 Binary files /dev/null and b/data/art824/mtower/mtower_turret_02_0.png differ diff --git a/data/art824/mtower/mtower_turret_02_1.png b/data/art824/mtower/mtower_turret_02_1.png new file mode 100644 index 0000000..69b460a Binary files /dev/null and b/data/art824/mtower/mtower_turret_02_1.png differ diff --git a/data/art824/mtower/mtower_turret_02_2.png b/data/art824/mtower/mtower_turret_02_2.png new file mode 100644 index 0000000..30b28a1 Binary files /dev/null and b/data/art824/mtower/mtower_turret_02_2.png differ diff --git a/data/art824/mtower/mtower_turret_03_0.png b/data/art824/mtower/mtower_turret_03_0.png new file mode 100644 index 0000000..5dccf53 Binary files /dev/null and b/data/art824/mtower/mtower_turret_03_0.png differ diff --git a/data/art824/mtower/mtower_turret_03_1.png b/data/art824/mtower/mtower_turret_03_1.png new file mode 100644 index 0000000..e74ab2e Binary files /dev/null and b/data/art824/mtower/mtower_turret_03_1.png differ diff --git a/data/art824/mtower/mtower_turret_03_2.png b/data/art824/mtower/mtower_turret_03_2.png new file mode 100644 index 0000000..baeea44 Binary files /dev/null and b/data/art824/mtower/mtower_turret_03_2.png differ diff --git a/data/art824/mtower/mtower_turret_04_0.png b/data/art824/mtower/mtower_turret_04_0.png new file mode 100644 index 0000000..73f6e44 Binary files /dev/null and b/data/art824/mtower/mtower_turret_04_0.png differ diff --git a/data/art824/mtower/mtower_turret_04_1.png b/data/art824/mtower/mtower_turret_04_1.png new file mode 100644 index 0000000..c98f0f7 Binary files /dev/null and b/data/art824/mtower/mtower_turret_04_1.png differ diff --git a/data/art824/mtower/mtower_turret_04_2.png b/data/art824/mtower/mtower_turret_04_2.png new file mode 100644 index 0000000..dc82cfd Binary files /dev/null and b/data/art824/mtower/mtower_turret_04_2.png differ diff --git a/data/art824/mtower/mtower_turret_05_0.png b/data/art824/mtower/mtower_turret_05_0.png new file mode 100644 index 0000000..e92c9a2 Binary files /dev/null and b/data/art824/mtower/mtower_turret_05_0.png differ diff --git a/data/art824/mtower/mtower_turret_05_1.png b/data/art824/mtower/mtower_turret_05_1.png new file mode 100644 index 0000000..e711f25 Binary files /dev/null and b/data/art824/mtower/mtower_turret_05_1.png differ diff --git a/data/art824/mtower/mtower_turret_05_2.png b/data/art824/mtower/mtower_turret_05_2.png new file mode 100644 index 0000000..2226d49 Binary files /dev/null and b/data/art824/mtower/mtower_turret_05_2.png differ diff --git a/data/art824/mtower/mtower_turret_06_0.png b/data/art824/mtower/mtower_turret_06_0.png new file mode 100644 index 0000000..c733ebd Binary files /dev/null and b/data/art824/mtower/mtower_turret_06_0.png differ diff --git a/data/art824/mtower/mtower_turret_06_1.png b/data/art824/mtower/mtower_turret_06_1.png new file mode 100644 index 0000000..5e4e1ec Binary files /dev/null and b/data/art824/mtower/mtower_turret_06_1.png differ diff --git a/data/art824/mtower/mtower_turret_06_2.png b/data/art824/mtower/mtower_turret_06_2.png new file mode 100644 index 0000000..517a868 Binary files /dev/null and b/data/art824/mtower/mtower_turret_06_2.png differ diff --git a/data/art824/mtower/mtower_turret_07_0.png b/data/art824/mtower/mtower_turret_07_0.png new file mode 100644 index 0000000..842a09e Binary files /dev/null and b/data/art824/mtower/mtower_turret_07_0.png differ diff --git a/data/art824/mtower/mtower_turret_07_1.png b/data/art824/mtower/mtower_turret_07_1.png new file mode 100644 index 0000000..75a0c9d Binary files /dev/null and b/data/art824/mtower/mtower_turret_07_1.png differ diff --git a/data/art824/mtower/mtower_turret_07_2.png b/data/art824/mtower/mtower_turret_07_2.png new file mode 100644 index 0000000..1a840bf Binary files /dev/null and b/data/art824/mtower/mtower_turret_07_2.png differ diff --git a/data/art824/mtower/mtower_turret_08_0.png b/data/art824/mtower/mtower_turret_08_0.png new file mode 100644 index 0000000..3160b6f Binary files /dev/null and b/data/art824/mtower/mtower_turret_08_0.png differ diff --git a/data/art824/mtower/mtower_turret_08_1.png b/data/art824/mtower/mtower_turret_08_1.png new file mode 100644 index 0000000..776060f Binary files /dev/null and b/data/art824/mtower/mtower_turret_08_1.png differ diff --git a/data/art824/mtower/mtower_turret_08_2.png b/data/art824/mtower/mtower_turret_08_2.png new file mode 100644 index 0000000..bb2b933 Binary files /dev/null and b/data/art824/mtower/mtower_turret_08_2.png differ diff --git a/data/art824/mtower/mtower_turret_09_0.png b/data/art824/mtower/mtower_turret_09_0.png new file mode 100644 index 0000000..13b614a Binary files /dev/null and b/data/art824/mtower/mtower_turret_09_0.png differ diff --git a/data/art824/mtower/mtower_turret_09_1.png b/data/art824/mtower/mtower_turret_09_1.png new file mode 100644 index 0000000..096d769 Binary files /dev/null and b/data/art824/mtower/mtower_turret_09_1.png differ diff --git a/data/art824/mtower/mtower_turret_09_2.png b/data/art824/mtower/mtower_turret_09_2.png new file mode 100644 index 0000000..d2c0848 Binary files /dev/null and b/data/art824/mtower/mtower_turret_09_2.png differ diff --git a/data/art824/mtower/mtower_turret_10_0.png b/data/art824/mtower/mtower_turret_10_0.png new file mode 100644 index 0000000..e5310ac Binary files /dev/null and b/data/art824/mtower/mtower_turret_10_0.png differ diff --git a/data/art824/mtower/mtower_turret_10_1.png b/data/art824/mtower/mtower_turret_10_1.png new file mode 100644 index 0000000..0455884 Binary files /dev/null and b/data/art824/mtower/mtower_turret_10_1.png differ diff --git a/data/art824/mtower/mtower_turret_10_2.png b/data/art824/mtower/mtower_turret_10_2.png new file mode 100644 index 0000000..51e553f Binary files /dev/null and b/data/art824/mtower/mtower_turret_10_2.png differ diff --git a/data/art824/mtower/mtower_turret_11_0.png b/data/art824/mtower/mtower_turret_11_0.png new file mode 100644 index 0000000..e4d7a03 Binary files /dev/null and b/data/art824/mtower/mtower_turret_11_0.png differ diff --git a/data/art824/mtower/mtower_turret_11_1.png b/data/art824/mtower/mtower_turret_11_1.png new file mode 100644 index 0000000..26edbb9 Binary files /dev/null and b/data/art824/mtower/mtower_turret_11_1.png differ diff --git a/data/art824/mtower/mtower_turret_11_2.png b/data/art824/mtower/mtower_turret_11_2.png new file mode 100644 index 0000000..7fa348a Binary files /dev/null and b/data/art824/mtower/mtower_turret_11_2.png differ diff --git a/data/art824/mtower/mtower_turret_12_0.png b/data/art824/mtower/mtower_turret_12_0.png new file mode 100644 index 0000000..641c5d6 Binary files /dev/null and b/data/art824/mtower/mtower_turret_12_0.png differ diff --git a/data/art824/mtower/mtower_turret_12_1.png b/data/art824/mtower/mtower_turret_12_1.png new file mode 100644 index 0000000..3e8f174 Binary files /dev/null and b/data/art824/mtower/mtower_turret_12_1.png differ diff --git a/data/art824/mtower/mtower_turret_12_2.png b/data/art824/mtower/mtower_turret_12_2.png new file mode 100644 index 0000000..b2e803a Binary files /dev/null and b/data/art824/mtower/mtower_turret_12_2.png differ diff --git a/data/art824/mtower/mtower_turret_13_0.png b/data/art824/mtower/mtower_turret_13_0.png new file mode 100644 index 0000000..b137be9 Binary files /dev/null and b/data/art824/mtower/mtower_turret_13_0.png differ diff --git a/data/art824/mtower/mtower_turret_13_1.png b/data/art824/mtower/mtower_turret_13_1.png new file mode 100644 index 0000000..e149770 Binary files /dev/null and b/data/art824/mtower/mtower_turret_13_1.png differ diff --git a/data/art824/mtower/mtower_turret_13_2.png b/data/art824/mtower/mtower_turret_13_2.png new file mode 100644 index 0000000..f67b823 Binary files /dev/null and b/data/art824/mtower/mtower_turret_13_2.png differ diff --git a/data/art824/mtower/mtower_turret_14_0.png b/data/art824/mtower/mtower_turret_14_0.png new file mode 100644 index 0000000..7ec93e4 Binary files /dev/null and b/data/art824/mtower/mtower_turret_14_0.png differ diff --git a/data/art824/mtower/mtower_turret_14_1.png b/data/art824/mtower/mtower_turret_14_1.png new file mode 100644 index 0000000..406898a Binary files /dev/null and b/data/art824/mtower/mtower_turret_14_1.png differ diff --git a/data/art824/mtower/mtower_turret_14_2.png b/data/art824/mtower/mtower_turret_14_2.png new file mode 100644 index 0000000..7f87d05 Binary files /dev/null and b/data/art824/mtower/mtower_turret_14_2.png differ diff --git a/data/art824/mtower/mtower_turret_15_0.png b/data/art824/mtower/mtower_turret_15_0.png new file mode 100644 index 0000000..a6df76e Binary files /dev/null and b/data/art824/mtower/mtower_turret_15_0.png differ diff --git a/data/art824/mtower/mtower_turret_15_1.png b/data/art824/mtower/mtower_turret_15_1.png new file mode 100644 index 0000000..b17dd1c Binary files /dev/null and b/data/art824/mtower/mtower_turret_15_1.png differ diff --git a/data/art824/mtower/mtower_turret_15_2.png b/data/art824/mtower/mtower_turret_15_2.png new file mode 100644 index 0000000..b92145c Binary files /dev/null and b/data/art824/mtower/mtower_turret_15_2.png differ diff --git a/data/art824/proc.amx b/data/art824/proc.amx new file mode 100644 index 0000000..34e7adc --- /dev/null +++ b/data/art824/proc.amx @@ -0,0 +1,592 @@ + + + + + +80 +true + + +<_x0030_ href="#ref-5"/> +<_x0031_ href="#ref-6"/> +<_x0032_ href="#ref-7"/> +<_x0033_ href="#ref-8"/> +<_x0034_ href="#ref-9"/> +<_x0035_ href="#ref-10"/> +<_x0036_ href="#ref-11"/> +<_x0037_ href="#ref-12"/> +<_x0038_ href="#ref-13"/> +<_x0039_ href="#ref-14"/> +<_x0031_0 href="#ref-15"/> +<_x0031_1 href="#ref-16"/> +<_x0031_2 href="#ref-17"/> +<_x0031_3 href="#ref-18"/> +<_x0031_4 href="#ref-19"/> +<_x0031_5 href="#ref-20"/> +<_x0031_6 href="#ref-21"/> +<_x0031_7 href="#ref-22"/> +<_x0031_8 href="#ref-23"/> +<_x0031_9 href="#ref-24"/> + + +<_x0030_ href="#ref-25"/> +<_x0031_ href="#ref-26"/> +<_x0032_ href="#ref-27"/> +<_x0033_ href="#ref-28"/> +<_x0034_ href="#ref-29"/> +<_x0035_ href="#ref-30"/> +<_x0036_ href="#ref-31"/> +<_x0037_ href="#ref-32"/> + + +proc\proc_miner_0_0.png + + +proc\proc_miner_0_1.png + + +proc\proc_miner_0_1.png + + +proc\proc_miner_0_0.png + + +proc\proc_smok_0_5.png + + +proc\proc_smok_0_1.png + + +proc\proc_smok_0_2.png + + +proc\proc_smok_0_3.png + + +proc\proc_smok_0_4.png + + +proc\proc_smok_0_0.png + + +proc\proc_base_1_0.png + + +proc\proc_base_0_0.png + + +proc\proc_base_2_0.png + + +proc\proc_door_0_5.png + + +proc\proc_door_0_1.png + + +proc\proc_door_0_2.png + + +proc\proc_door_0_3.png + + +proc\proc_door_0_4.png + + +proc\proc_door_0_0.png + + +proc\icon.png + + +base 0 +0 +1 +<_x0030_ href="#ref-54"/> + + +base 1 +0 +1 +<_x0030_ href="#ref-56"/> + + +base 2 +0 +1 +<_x0030_ href="#ref-58"/> + + +door 0 +0 +6 +<_x0030_ href="#ref-60"/> +<_x0031_ href="#ref-61"/> +<_x0032_ href="#ref-62"/> +<_x0033_ href="#ref-63"/> +<_x0034_ href="#ref-64"/> +<_x0035_ href="#ref-65"/> + + +miner 0 +0 +2 +<_x0030_ href="#ref-67"/> +<_x0031_ href="#ref-68"/> + + +smoke +0 +9 +<_x0030_ href="#ref-70"/> +<_x0031_ href="#ref-71"/> +<_x0032_ href="#ref-72"/> +<_x0033_ href="#ref-73"/> +<_x0034_ href="#ref-74"/> +<_x0035_ href="#ref-75"/> +<_x0036_ href="#ref-76"/> +<_x0037_ href="#ref-77"/> +<_x0038_ href="#ref-78"/> + + +icon +0 +1 +<_x0030_ href="#ref-80"/> + + +help +0 +1 +<_x0030_ href="#ref-82"/> + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +5 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + +<_x0030_ href="#ref-106"/> + + +<_x0030_ href="#ref-107"/> + + +<_x0030_ href="#ref-108"/> + + +<_x0030_ href="#ref-109"/> + + +<_x0030_ href="#ref-110"/> + + +<_x0030_ href="#ref-111"/> + + +<_x0030_ href="#ref-112"/> + + +<_x0030_ href="#ref-113"/> + + +<_x0030_ href="#ref-114"/> + + +<_x0030_ href="#ref-115"/> + + +<_x0030_ href="#ref-116"/> + + +<_x0030_ href="#ref-117"/> + + +<_x0030_ href="#ref-118"/> + + +<_x0030_ href="#ref-119"/> +<_x0031_ href="#ref-120"/> + + +<_x0030_ href="#ref-121"/> +<_x0031_ href="#ref-122"/> + + +<_x0030_ href="#ref-123"/> +<_x0031_ href="#ref-124"/> + + +<_x0030_ href="#ref-125"/> +<_x0031_ href="#ref-126"/> + + +<_x0030_ href="#ref-127"/> + + +<_x0030_ href="#ref-128"/> + + + + +<_x0030_ href="#ref-129"/> + + +<_x0030_ href="#ref-130"/> +<_x0031_ href="#ref-131"/> + + + +3 +0 + + + + + +3 +2 + + + + + +3 +2 + + + + + +-22 +0 + + + + + +-22 +0 + + + + + +-22 +0 + + + + + +-22 +0 + + + + + +-22 +0 + + + + + +-22 +0 + + + + + +15 +16 + + + + + +15 +15 + + + + + +-11 +2 + + + + + +-9 +10 + + + + + +-56 +-2 + + + + + +-8 +16 + + + + + +-55 +6 + + + + + +-8 +21 + + + + + +-54 +11 + + + + + +-8 +25 + + + + + +-54 +16 + + + + + +-7 +29 + + + + + +-55 +20 + + + + + +-55 +24 + + + + + +1 +0 + + + + + +13 +25 + + + + + +38 +25 + + + + + diff --git a/data/art824/proc/icon.png b/data/art824/proc/icon.png new file mode 100644 index 0000000..96035f7 Binary files /dev/null and b/data/art824/proc/icon.png differ diff --git a/data/art824/proc/proc_base_0_0.png b/data/art824/proc/proc_base_0_0.png new file mode 100644 index 0000000..ba13b82 Binary files /dev/null and b/data/art824/proc/proc_base_0_0.png differ diff --git a/data/art824/proc/proc_base_1_0.png b/data/art824/proc/proc_base_1_0.png new file mode 100644 index 0000000..efe7cf1 Binary files /dev/null and b/data/art824/proc/proc_base_1_0.png differ diff --git a/data/art824/proc/proc_base_2_0.png b/data/art824/proc/proc_base_2_0.png new file mode 100644 index 0000000..1a3b4b5 Binary files /dev/null and b/data/art824/proc/proc_base_2_0.png differ diff --git a/data/art824/proc/proc_door_0_0.png b/data/art824/proc/proc_door_0_0.png new file mode 100644 index 0000000..64d521f Binary files /dev/null and b/data/art824/proc/proc_door_0_0.png differ diff --git a/data/art824/proc/proc_door_0_1.png b/data/art824/proc/proc_door_0_1.png new file mode 100644 index 0000000..53364ab Binary files /dev/null and b/data/art824/proc/proc_door_0_1.png differ diff --git a/data/art824/proc/proc_door_0_2.png b/data/art824/proc/proc_door_0_2.png new file mode 100644 index 0000000..30d8387 Binary files /dev/null and b/data/art824/proc/proc_door_0_2.png differ diff --git a/data/art824/proc/proc_door_0_3.png b/data/art824/proc/proc_door_0_3.png new file mode 100644 index 0000000..119f5bd Binary files /dev/null and b/data/art824/proc/proc_door_0_3.png differ diff --git a/data/art824/proc/proc_door_0_4.png b/data/art824/proc/proc_door_0_4.png new file mode 100644 index 0000000..f45f7a0 Binary files /dev/null and b/data/art824/proc/proc_door_0_4.png differ diff --git a/data/art824/proc/proc_door_0_5.png b/data/art824/proc/proc_door_0_5.png new file mode 100644 index 0000000..b055c5b Binary files /dev/null and b/data/art824/proc/proc_door_0_5.png differ diff --git a/data/art824/proc/proc_miner_0_0.png b/data/art824/proc/proc_miner_0_0.png new file mode 100644 index 0000000..5b650dd Binary files /dev/null and b/data/art824/proc/proc_miner_0_0.png differ diff --git a/data/art824/proc/proc_miner_0_1.png b/data/art824/proc/proc_miner_0_1.png new file mode 100644 index 0000000..68a7de7 Binary files /dev/null and b/data/art824/proc/proc_miner_0_1.png differ diff --git a/data/art824/proc/proc_smok_0_0.png b/data/art824/proc/proc_smok_0_0.png new file mode 100644 index 0000000..9fc246d Binary files /dev/null and b/data/art824/proc/proc_smok_0_0.png differ diff --git a/data/art824/proc/proc_smok_0_1.png b/data/art824/proc/proc_smok_0_1.png new file mode 100644 index 0000000..fcdf476 Binary files /dev/null and b/data/art824/proc/proc_smok_0_1.png differ diff --git a/data/art824/proc/proc_smok_0_2.png b/data/art824/proc/proc_smok_0_2.png new file mode 100644 index 0000000..aa430ed Binary files /dev/null and b/data/art824/proc/proc_smok_0_2.png differ diff --git a/data/art824/proc/proc_smok_0_3.png b/data/art824/proc/proc_smok_0_3.png new file mode 100644 index 0000000..14adef3 Binary files /dev/null and b/data/art824/proc/proc_smok_0_3.png differ diff --git a/data/art824/proc/proc_smok_0_4.png b/data/art824/proc/proc_smok_0_4.png new file mode 100644 index 0000000..a84b0d6 Binary files /dev/null and b/data/art824/proc/proc_smok_0_4.png differ diff --git a/data/art824/proc/proc_smok_0_5.png b/data/art824/proc/proc_smok_0_5.png new file mode 100644 index 0000000..65909ad Binary files /dev/null and b/data/art824/proc/proc_smok_0_5.png differ diff --git a/data/art824/radar/icon.png b/data/art824/radar/icon.png new file mode 100644 index 0000000..cd30313 Binary files /dev/null and b/data/art824/radar/icon.png differ diff --git a/data/art824/radar/radar_a_0_0.png b/data/art824/radar/radar_a_0_0.png new file mode 100644 index 0000000..5f615e3 Binary files /dev/null and b/data/art824/radar/radar_a_0_0.png differ diff --git a/data/art824/radar/radar_a_0_1.png b/data/art824/radar/radar_a_0_1.png new file mode 100644 index 0000000..c67fdad Binary files /dev/null and b/data/art824/radar/radar_a_0_1.png differ diff --git a/data/art824/radar/radar_a_0_2.png b/data/art824/radar/radar_a_0_2.png new file mode 100644 index 0000000..988b4b9 Binary files /dev/null and b/data/art824/radar/radar_a_0_2.png differ diff --git a/data/art824/radar/radar_a_0_3.png b/data/art824/radar/radar_a_0_3.png new file mode 100644 index 0000000..03d5764 Binary files /dev/null and b/data/art824/radar/radar_a_0_3.png differ diff --git a/data/art824/radar/radar_a_1_0.png b/data/art824/radar/radar_a_1_0.png new file mode 100644 index 0000000..1580b26 Binary files /dev/null and b/data/art824/radar/radar_a_1_0.png differ diff --git a/data/art824/radar/radar_a_1_1.png b/data/art824/radar/radar_a_1_1.png new file mode 100644 index 0000000..3235be4 Binary files /dev/null and b/data/art824/radar/radar_a_1_1.png differ diff --git a/data/art824/radar/radar_a_1_2.png b/data/art824/radar/radar_a_1_2.png new file mode 100644 index 0000000..ab1dd4a Binary files /dev/null and b/data/art824/radar/radar_a_1_2.png differ diff --git a/data/art824/radar/radar_a_1_3.png b/data/art824/radar/radar_a_1_3.png new file mode 100644 index 0000000..dc75ddf Binary files /dev/null and b/data/art824/radar/radar_a_1_3.png differ diff --git a/data/art824/radar/radar_a_2_0.png b/data/art824/radar/radar_a_2_0.png new file mode 100644 index 0000000..12d1085 Binary files /dev/null and b/data/art824/radar/radar_a_2_0.png differ diff --git a/data/art824/rawbitmaps/buildformbkgd.bmp b/data/art824/rawbitmaps/buildformbkgd.bmp new file mode 100644 index 0000000..b535b5f Binary files /dev/null and b/data/art824/rawbitmaps/buildformbkgd.bmp differ diff --git a/data/art824/rawbitmaps/ecomlargebkgd.bmp b/data/art824/rawbitmaps/ecomlargebkgd.bmp new file mode 100644 index 0000000..e92efd1 Binary files /dev/null and b/data/art824/rawbitmaps/ecomlargebkgd.bmp differ diff --git a/data/art824/rawbitmaps/ecomsmallbkgd.bmp b/data/art824/rawbitmaps/ecomsmallbkgd.bmp new file mode 100644 index 0000000..6381f24 Binary files /dev/null and b/data/art824/rawbitmaps/ecomsmallbkgd.bmp differ diff --git a/data/art824/rawbitmaps/titlescreenbkgd.bmp b/data/art824/rawbitmaps/titlescreenbkgd.bmp new file mode 100644 index 0000000..2976127 Binary files /dev/null and b/data/art824/rawbitmaps/titlescreenbkgd.bmp differ diff --git a/data/art824/reactor.amx b/data/art824/reactor.amx new file mode 100644 index 0000000..0138eca --- /dev/null +++ b/data/art824/reactor.amx @@ -0,0 +1,271 @@ + + + + + +80 +true + + +<_x0030_ href="#ref-5"/> +<_x0031_ href="#ref-6"/> +<_x0032_ href="#ref-7"/> +<_x0033_ href="#ref-8"/> +<_x0034_ href="#ref-9"/> +<_x0035_ href="#ref-10"/> +<_x0036_ href="#ref-11"/> +<_x0037_ href="#ref-12"/> +<_x0038_ href="#ref-13"/> +<_x0039_ href="#ref-14"/> +<_x0031_0 href="#ref-15"/> +<_x0031_1 href="#ref-16"/> +<_x0031_2 href="#ref-17"/> +<_x0031_3 href="#ref-18"/> + + +<_x0030_ href="#ref-19"/> +<_x0031_ href="#ref-20"/> +<_x0032_ href="#ref-21"/> +<_x0033_ href="#ref-22"/> +<_x0034_ href="#ref-23"/> + + +reactor\reactor_spin_0_2.png + + +reactor\reactor_spin_0_1.png + + +reactor\reactor_spin_0_0.png + + +reactor\reactor_spin_1_2.png + + +reactor\reactor_spin_1_1.png + + +reactor\reactor_spin_1_0.png + + +reactor\reactor_spin_2_0.png + + +reactor\reactor_spin_1_2.png + + +reactor\reactor_spin_0_1.png + + +reactor\reactor_spin_0_2.png + + +reactor\reactor_spin_1_0.png + + +reactor\reactor_spin_1_1.png + + +reactor\reactor_spin_0_0.png + + +reactor\icon.png + + +spin 0 +0 +3 +<_x0030_ href="#ref-39"/> +<_x0031_ href="#ref-40"/> +<_x0032_ href="#ref-41"/> + + +spin 1 +0 +3 +<_x0030_ href="#ref-43"/> +<_x0031_ href="#ref-44"/> +<_x0032_ href="#ref-45"/> + + +spin 2 +0 +1 +<_x0030_ href="#ref-47"/> + + +icon +0 +1 +<_x0030_ href="#ref-49"/> + + +help +0 +1 +<_x0030_ href="#ref-51"/> + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + +<_x0030_ href="#ref-62"/> + + +<_x0030_ href="#ref-63"/> + + +<_x0030_ href="#ref-64"/> + + +<_x0030_ href="#ref-65"/> + + +<_x0030_ href="#ref-66"/> + + +<_x0030_ href="#ref-67"/> + + +<_x0030_ href="#ref-68"/> + + +<_x0030_ href="#ref-69"/> + + +<_x0030_ href="#ref-70"/> + + + +0 +-6 + + + + + +0 +-6 + + + + + +0 +-6 + + + + + +0 +-6 + + + + + +0 +-6 + + + + + +0 +-6 + + + + + +0 +-6 + + + + + +0 +0 + + + + + +24 +21 + + + + + diff --git a/data/art824/reactor/icon.png b/data/art824/reactor/icon.png new file mode 100644 index 0000000..6d49cd8 Binary files /dev/null and b/data/art824/reactor/icon.png differ diff --git a/data/art824/reactor/reactor_spin_0_0.png b/data/art824/reactor/reactor_spin_0_0.png new file mode 100644 index 0000000..060c6b6 Binary files /dev/null and b/data/art824/reactor/reactor_spin_0_0.png differ diff --git a/data/art824/reactor/reactor_spin_0_1.png b/data/art824/reactor/reactor_spin_0_1.png new file mode 100644 index 0000000..f58d4d3 Binary files /dev/null and b/data/art824/reactor/reactor_spin_0_1.png differ diff --git a/data/art824/reactor/reactor_spin_0_2.png b/data/art824/reactor/reactor_spin_0_2.png new file mode 100644 index 0000000..ce20044 Binary files /dev/null and b/data/art824/reactor/reactor_spin_0_2.png differ diff --git a/data/art824/reactor/reactor_spin_1_0.png b/data/art824/reactor/reactor_spin_1_0.png new file mode 100644 index 0000000..878d413 Binary files /dev/null and b/data/art824/reactor/reactor_spin_1_0.png differ diff --git a/data/art824/reactor/reactor_spin_1_1.png b/data/art824/reactor/reactor_spin_1_1.png new file mode 100644 index 0000000..3c55826 Binary files /dev/null and b/data/art824/reactor/reactor_spin_1_1.png differ diff --git a/data/art824/reactor/reactor_spin_1_2.png b/data/art824/reactor/reactor_spin_1_2.png new file mode 100644 index 0000000..ab1ad3d Binary files /dev/null and b/data/art824/reactor/reactor_spin_1_2.png differ diff --git a/data/art824/reactor/reactor_spin_2_0.png b/data/art824/reactor/reactor_spin_2_0.png new file mode 100644 index 0000000..7f4a522 Binary files /dev/null and b/data/art824/reactor/reactor_spin_2_0.png differ diff --git a/data/art824/replicator.amx b/data/art824/replicator.amx new file mode 100644 index 0000000..5f56b7b --- /dev/null +++ b/data/art824/replicator.amx @@ -0,0 +1,413 @@ + + + + + +80 +true + + +<_x0030_ href="#ref-5"/> +<_x0031_ href="#ref-6"/> +<_x0032_ href="#ref-7"/> +<_x0033_ href="#ref-8"/> +<_x0034_ href="#ref-9"/> +<_x0035_ href="#ref-10"/> +<_x0036_ href="#ref-11"/> +<_x0037_ href="#ref-12"/> +<_x0038_ href="#ref-13"/> +<_x0039_ href="#ref-14"/> +<_x0031_0 href="#ref-15"/> +<_x0031_1 href="#ref-16"/> +<_x0031_2 href="#ref-17"/> +<_x0031_3 href="#ref-18"/> +<_x0031_4 href="#ref-19"/> +<_x0031_5 href="#ref-20"/> +<_x0031_6 href="#ref-21"/> +<_x0031_7 href="#ref-22"/> +<_x0031_8 href="#ref-23"/> + + +<_x0030_ href="#ref-24"/> +<_x0031_ href="#ref-25"/> +<_x0032_ href="#ref-26"/> +<_x0033_ href="#ref-27"/> +<_x0034_ href="#ref-28"/> +<_x0035_ href="#ref-29"/> +<_x0036_ href="#ref-30"/> + + +replicator\icon.png + + +replicator\replicatorOn_04.png + + +replicator\replicatorOff_01.png + + +replicator\replicatorOff_02.png + + +replicator\replicatorOff_03.png + + +replicator\replicatorOff_04.png + + +replicator\replicatorOn_01.png + + +replicator\replicatorOn_02.png + + +replicator\replicatorOn_03.png + + +replicator\replicatorLightsA_01.png + + +replicator\replicatorLightsB_01.png + + +replicator\replicatorLightsB_05.png + + +replicator\replicatorLightsA_02.png + + +replicator\replicatorLightsA_03.png + + +replicator\replicatorLightsA_04.png + + +replicator\replicatorLightsA_05.png + + +replicator\replicatorLightsB_02.png + + +replicator\replicatorLightsB_03.png + + +replicator\replicatorLightsB_04.png + + +a 0a +0 +1 +<_x0030_ href="#ref-51"/> + + +a 0b +0 +1 +<_x0030_ href="#ref-53"/> + + +b 0a +0 +1 +<_x0030_ href="#ref-55"/> + + +b 0b +0 +1 +<_x0030_ href="#ref-57"/> + + +help +0 +1 +<_x0030_ href="#ref-59"/> + + +icon +0 +1 +<_x0030_ href="#ref-61"/> + + +l 0 +0 +5 +<_x0030_ href="#ref-63"/> +<_x0031_ href="#ref-64"/> +<_x0032_ href="#ref-65"/> +<_x0033_ href="#ref-66"/> +<_x0034_ href="#ref-67"/> + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + +<_x0030_ href="#ref-80"/> +<_x0031_ href="#ref-81"/> + + +<_x0030_ href="#ref-82"/> +<_x0031_ href="#ref-83"/> + + +<_x0030_ href="#ref-84"/> +<_x0031_ href="#ref-85"/> + + +<_x0030_ href="#ref-86"/> +<_x0031_ href="#ref-87"/> + + +<_x0030_ href="#ref-88"/> + + +<_x0030_ href="#ref-89"/> + + +<_x0030_ href="#ref-90"/> +<_x0031_ href="#ref-91"/> + + +<_x0030_ href="#ref-92"/> +<_x0031_ href="#ref-93"/> + + +<_x0030_ href="#ref-94"/> +<_x0031_ href="#ref-95"/> + + +<_x0030_ href="#ref-96"/> +<_x0031_ href="#ref-97"/> + + +<_x0030_ href="#ref-98"/> +<_x0031_ href="#ref-99"/> + + + +-53 +12 + + + + + +19 +12 + + + + + +-52 +-44 + + + + + +19 +-44 + + + + + +-53 +12 + + + + + +19 +12 + + + + + +-52 +-44 + + + + + +19 +-44 + + + + + +18 +13 + + + + + +0 +0 + + + + + +-86 +-31 + + + + + +-7 +-31 + + + + + +-86 +-31 + + + + + +-7 +-31 + + + + + +-86 +-31 + + + + + +-7 +-31 + + + + + +-86 +-31 + + + + + +-7 +-31 + + + + + +-86 +-31 + + + + + +-7 +-31 + + + + + diff --git a/data/art824/replicator/icon.png b/data/art824/replicator/icon.png new file mode 100644 index 0000000..5006e7a Binary files /dev/null and b/data/art824/replicator/icon.png differ diff --git a/data/art824/replicator/replicatorLightsA_01.png b/data/art824/replicator/replicatorLightsA_01.png new file mode 100644 index 0000000..4637592 Binary files /dev/null and b/data/art824/replicator/replicatorLightsA_01.png differ diff --git a/data/art824/replicator/replicatorLightsA_02.png b/data/art824/replicator/replicatorLightsA_02.png new file mode 100644 index 0000000..9661bf2 Binary files /dev/null and b/data/art824/replicator/replicatorLightsA_02.png differ diff --git a/data/art824/replicator/replicatorLightsA_03.png b/data/art824/replicator/replicatorLightsA_03.png new file mode 100644 index 0000000..7648046 Binary files /dev/null and b/data/art824/replicator/replicatorLightsA_03.png differ diff --git a/data/art824/replicator/replicatorLightsA_04.png b/data/art824/replicator/replicatorLightsA_04.png new file mode 100644 index 0000000..aeec302 Binary files /dev/null and b/data/art824/replicator/replicatorLightsA_04.png differ diff --git a/data/art824/replicator/replicatorLightsA_05.png b/data/art824/replicator/replicatorLightsA_05.png new file mode 100644 index 0000000..2692e20 Binary files /dev/null and b/data/art824/replicator/replicatorLightsA_05.png differ diff --git a/data/art824/replicator/replicatorLightsB_01.png b/data/art824/replicator/replicatorLightsB_01.png new file mode 100644 index 0000000..5aeb125 Binary files /dev/null and b/data/art824/replicator/replicatorLightsB_01.png differ diff --git a/data/art824/replicator/replicatorLightsB_02.png b/data/art824/replicator/replicatorLightsB_02.png new file mode 100644 index 0000000..fea9333 Binary files /dev/null and b/data/art824/replicator/replicatorLightsB_02.png differ diff --git a/data/art824/replicator/replicatorLightsB_03.png b/data/art824/replicator/replicatorLightsB_03.png new file mode 100644 index 0000000..1383b94 Binary files /dev/null and b/data/art824/replicator/replicatorLightsB_03.png differ diff --git a/data/art824/replicator/replicatorLightsB_04.png b/data/art824/replicator/replicatorLightsB_04.png new file mode 100644 index 0000000..291dfd2 Binary files /dev/null and b/data/art824/replicator/replicatorLightsB_04.png differ diff --git a/data/art824/replicator/replicatorLightsB_05.png b/data/art824/replicator/replicatorLightsB_05.png new file mode 100644 index 0000000..d8034e0 Binary files /dev/null and b/data/art824/replicator/replicatorLightsB_05.png differ diff --git a/data/art824/replicator/replicatorOff_01.png b/data/art824/replicator/replicatorOff_01.png new file mode 100644 index 0000000..a11ce6e Binary files /dev/null and b/data/art824/replicator/replicatorOff_01.png differ diff --git a/data/art824/replicator/replicatorOff_02.png b/data/art824/replicator/replicatorOff_02.png new file mode 100644 index 0000000..16197f2 Binary files /dev/null and b/data/art824/replicator/replicatorOff_02.png differ diff --git a/data/art824/replicator/replicatorOff_03.png b/data/art824/replicator/replicatorOff_03.png new file mode 100644 index 0000000..5ba6140 Binary files /dev/null and b/data/art824/replicator/replicatorOff_03.png differ diff --git a/data/art824/replicator/replicatorOff_04.png b/data/art824/replicator/replicatorOff_04.png new file mode 100644 index 0000000..5579c17 Binary files /dev/null and b/data/art824/replicator/replicatorOff_04.png differ diff --git a/data/art824/replicator/replicatorOn_01.png b/data/art824/replicator/replicatorOn_01.png new file mode 100644 index 0000000..8a31b11 Binary files /dev/null and b/data/art824/replicator/replicatorOn_01.png differ diff --git a/data/art824/replicator/replicatorOn_02.png b/data/art824/replicator/replicatorOn_02.png new file mode 100644 index 0000000..ec4cab0 Binary files /dev/null and b/data/art824/replicator/replicatorOn_02.png differ diff --git a/data/art824/replicator/replicatorOn_03.png b/data/art824/replicator/replicatorOn_03.png new file mode 100644 index 0000000..fbacfeb Binary files /dev/null and b/data/art824/replicator/replicatorOn_03.png differ diff --git a/data/art824/replicator/replicatorOn_04.png b/data/art824/replicator/replicatorOn_04.png new file mode 100644 index 0000000..f64c24f Binary files /dev/null and b/data/art824/replicator/replicatorOn_04.png differ diff --git a/data/art824/research/icon.png b/data/art824/research/icon.png new file mode 100644 index 0000000..c410aa1 Binary files /dev/null and b/data/art824/research/icon.png differ diff --git a/data/art824/research/research_a_0_0.png b/data/art824/research/research_a_0_0.png new file mode 100644 index 0000000..2715889 Binary files /dev/null and b/data/art824/research/research_a_0_0.png differ diff --git a/data/art824/research/research_a_1_0.png b/data/art824/research/research_a_1_0.png new file mode 100644 index 0000000..a582d95 Binary files /dev/null and b/data/art824/research/research_a_1_0.png differ diff --git a/data/art824/research/research_a_2_0.png b/data/art824/research/research_a_2_0.png new file mode 100644 index 0000000..9800209 Binary files /dev/null and b/data/art824/research/research_a_2_0.png differ diff --git a/data/art824/research/research_dish_0_0.png b/data/art824/research/research_dish_0_0.png new file mode 100644 index 0000000..d318c21 Binary files /dev/null and b/data/art824/research/research_dish_0_0.png differ diff --git a/data/art824/research/research_dish_0_1.png b/data/art824/research/research_dish_0_1.png new file mode 100644 index 0000000..6517fdb Binary files /dev/null and b/data/art824/research/research_dish_0_1.png differ diff --git a/data/art824/research/research_dish_0_2.png b/data/art824/research/research_dish_0_2.png new file mode 100644 index 0000000..acd41ab Binary files /dev/null and b/data/art824/research/research_dish_0_2.png differ diff --git a/data/art824/ricochet.amx b/data/art824/ricochet.amx new file mode 100644 index 0000000..4919584 --- /dev/null +++ b/data/art824/ricochet.amx @@ -0,0 +1,239 @@ + + + + + +80 +false + + +<_x0030_ href="#ref-5"/> +<_x0031_ href="#ref-6"/> +<_x0032_ href="#ref-7"/> +<_x0033_ href="#ref-8"/> +<_x0034_ href="#ref-9"/> +<_x0035_ href="#ref-10"/> +<_x0036_ href="#ref-11"/> +<_x0037_ href="#ref-12"/> +<_x0038_ href="#ref-13"/> + + +<_x0030_ href="#ref-14"/> +<_x0031_ href="#ref-15"/> +<_x0032_ href="#ref-16"/> + + +ricochet\ricochet_a_3_2.png + + +ricochet\ricochet_a_1_0.png + + +ricochet\ricochet_a_1_1.png + + +ricochet\ricochet_a_1_2.png + + +ricochet\ricochet_a_2_0.png + + +ricochet\ricochet_a_2_1.png + + +ricochet\ricochet_a_2_2.png + + +ricochet\ricochet_a_3_0.png + + +ricochet\ricochet_a_3_1.png + + +a 1 +0 +3 +<_x0030_ href="#ref-27"/> +<_x0031_ href="#ref-28"/> +<_x0032_ href="#ref-29"/> + + +a 2 +0 +3 +<_x0030_ href="#ref-31"/> +<_x0031_ href="#ref-32"/> +<_x0032_ href="#ref-33"/> + + +a 3 +0 +3 +<_x0030_ href="#ref-35"/> +<_x0031_ href="#ref-36"/> +<_x0032_ href="#ref-37"/> + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + +<_x0030_ href="#ref-48"/> + + +<_x0030_ href="#ref-49"/> + + +<_x0030_ href="#ref-50"/> + + +<_x0030_ href="#ref-51"/> + + +<_x0030_ href="#ref-52"/> + + +<_x0030_ href="#ref-53"/> + + +<_x0030_ href="#ref-54"/> + + +<_x0030_ href="#ref-55"/> + + +<_x0030_ href="#ref-56"/> + + + +4 +4 + + + + + +4 +4 + + + + + +4 +4 + + + + + +4 +4 + + + + + +4 +4 + + + + + +4 +4 + + + + + +4 +4 + + + + + +4 +4 + + + + + +4 +4 + + + + + diff --git a/data/art824/ricochet/ricochet_a_1_0.png b/data/art824/ricochet/ricochet_a_1_0.png new file mode 100644 index 0000000..d728c56 Binary files /dev/null and b/data/art824/ricochet/ricochet_a_1_0.png differ diff --git a/data/art824/ricochet/ricochet_a_1_1.png b/data/art824/ricochet/ricochet_a_1_1.png new file mode 100644 index 0000000..cdfad77 Binary files /dev/null and b/data/art824/ricochet/ricochet_a_1_1.png differ diff --git a/data/art824/ricochet/ricochet_a_1_2.png b/data/art824/ricochet/ricochet_a_1_2.png new file mode 100644 index 0000000..3c419ca Binary files /dev/null and b/data/art824/ricochet/ricochet_a_1_2.png differ diff --git a/data/art824/ricochet/ricochet_a_2_0.png b/data/art824/ricochet/ricochet_a_2_0.png new file mode 100644 index 0000000..9c98ca0 Binary files /dev/null and b/data/art824/ricochet/ricochet_a_2_0.png differ diff --git a/data/art824/ricochet/ricochet_a_2_1.png b/data/art824/ricochet/ricochet_a_2_1.png new file mode 100644 index 0000000..c0f0342 Binary files /dev/null and b/data/art824/ricochet/ricochet_a_2_1.png differ diff --git a/data/art824/ricochet/ricochet_a_2_2.png b/data/art824/ricochet/ricochet_a_2_2.png new file mode 100644 index 0000000..3d063b3 Binary files /dev/null and b/data/art824/ricochet/ricochet_a_2_2.png differ diff --git a/data/art824/ricochet/ricochet_a_3_0.png b/data/art824/ricochet/ricochet_a_3_0.png new file mode 100644 index 0000000..2ee3fd5 Binary files /dev/null and b/data/art824/ricochet/ricochet_a_3_0.png differ diff --git a/data/art824/ricochet/ricochet_a_3_1.png b/data/art824/ricochet/ricochet_a_3_1.png new file mode 100644 index 0000000..6dab752 Binary files /dev/null and b/data/art824/ricochet/ricochet_a_3_1.png differ diff --git a/data/art824/ricochet/ricochet_a_3_2.png b/data/art824/ricochet/ricochet_a_3_2.png new file mode 100644 index 0000000..4e3c275 Binary files /dev/null and b/data/art824/ricochet/ricochet_a_3_2.png differ diff --git a/data/art824/rocket.amx b/data/art824/rocket.amx new file mode 100644 index 0000000..d56855c --- /dev/null +++ b/data/art824/rocket.amx @@ -0,0 +1,446 @@ + + + + + +80 +true + + +<_x0030_ href="#ref-5"/> +<_x0031_ href="#ref-6"/> +<_x0032_ href="#ref-7"/> +<_x0033_ href="#ref-8"/> +<_x0034_ href="#ref-9"/> +<_x0035_ href="#ref-10"/> +<_x0036_ href="#ref-11"/> +<_x0037_ href="#ref-12"/> +<_x0038_ href="#ref-13"/> +<_x0039_ href="#ref-14"/> +<_x0031_0 href="#ref-15"/> +<_x0031_1 href="#ref-16"/> +<_x0031_2 href="#ref-17"/> +<_x0031_3 href="#ref-18"/> +<_x0031_4 href="#ref-19"/> +<_x0031_5 href="#ref-20"/> +<_x0031_6 href="#ref-21"/> +<_x0031_7 href="#ref-22"/> + + +<_x0030_ href="#ref-23"/> +<_x0031_ href="#ref-24"/> +<_x0032_ href="#ref-25"/> + + +rocket\rocket_a_0_0.png + + +rocket\rocket_a_0_1.png + + +rocket\rocket_a_0_2.png + + +rocket\rocket_a_0_3.png + + +rocket\rocket_a_0_4.png + + +rocket\rocket_a_0_5.png + + +rocket\rocket_a_0_6.png + + +rocket\rocket_a_0_7.png + + +rocket\rocket_a_1_0.png + + +rocket\rocket_a_1_1.png + + +rocket\rocket_a_1_2.png + + +rocket\rocket_a_1_3.png + + +rocket\roc_trails_0.png + + +rocket\roc_trails_1.png + + +rocket\roc_trails_2.png + + +rocket\roc_trails_3.png + + +rocket\roc_trails_4.png + + +rocket\roc_trails_5.png + + +a 0 +0 +8 +<_x0030_ href="#ref-45"/> +<_x0031_ href="#ref-46"/> +<_x0032_ href="#ref-47"/> +<_x0033_ href="#ref-48"/> +<_x0034_ href="#ref-49"/> +<_x0035_ href="#ref-50"/> +<_x0036_ href="#ref-51"/> +<_x0037_ href="#ref-52"/> + + +a 1 +0 +4 +<_x0030_ href="#ref-54"/> +<_x0031_ href="#ref-55"/> +<_x0032_ href="#ref-56"/> +<_x0033_ href="#ref-57"/> + + +trail +0 +6 +<_x0030_ href="#ref-59"/> +<_x0031_ href="#ref-60"/> +<_x0032_ href="#ref-61"/> +<_x0033_ href="#ref-62"/> +<_x0034_ href="#ref-63"/> +<_x0035_ href="#ref-64"/> + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + +<_x0030_ href="#ref-84"/> + + +<_x0030_ href="#ref-85"/> + + +<_x0030_ href="#ref-86"/> + + +<_x0030_ href="#ref-87"/> + + +<_x0030_ href="#ref-88"/> + + +<_x0030_ href="#ref-89"/> + + +<_x0030_ href="#ref-90"/> + + +<_x0030_ href="#ref-91"/> + + +<_x0030_ href="#ref-92"/> + + +<_x0030_ href="#ref-93"/> + + +<_x0030_ href="#ref-94"/> + + +<_x0030_ href="#ref-95"/> + + +<_x0030_ href="#ref-96"/> + + +<_x0030_ href="#ref-97"/> + + +<_x0030_ href="#ref-98"/> + + +<_x0030_ href="#ref-99"/> + + +<_x0030_ href="#ref-100"/> + + +<_x0030_ href="#ref-101"/> + + + +4 +1 + + + + + +6 +1 + + + + + +6 +0 + + + + + +6 +2 + + + + + +4 +2 + + + + + +5 +2 + + + + + +5 +0 + + + + + +5 +2 + + + + + +3 +3 + + + + + +4 +4 + + + + + +6 +6 + + + + + +6 +6 + + + + + +3 +3 + + + + + +4 +3 + + + + + +4 +4 + + + + + +6 +6 + + + + + +6 +6 + + + + + +6 +6 + + + + + diff --git a/data/art824/rocket/roc_trails_0.png b/data/art824/rocket/roc_trails_0.png new file mode 100644 index 0000000..37016d9 Binary files /dev/null and b/data/art824/rocket/roc_trails_0.png differ diff --git a/data/art824/rocket/roc_trails_1.png b/data/art824/rocket/roc_trails_1.png new file mode 100644 index 0000000..06b71b2 Binary files /dev/null and b/data/art824/rocket/roc_trails_1.png differ diff --git a/data/art824/rocket/roc_trails_2.png b/data/art824/rocket/roc_trails_2.png new file mode 100644 index 0000000..1ae47f2 Binary files /dev/null and b/data/art824/rocket/roc_trails_2.png differ diff --git a/data/art824/rocket/roc_trails_3.png b/data/art824/rocket/roc_trails_3.png new file mode 100644 index 0000000..ba24522 Binary files /dev/null and b/data/art824/rocket/roc_trails_3.png differ diff --git a/data/art824/rocket/roc_trails_4.png b/data/art824/rocket/roc_trails_4.png new file mode 100644 index 0000000..1bea39a Binary files /dev/null and b/data/art824/rocket/roc_trails_4.png differ diff --git a/data/art824/rocket/roc_trails_5.png b/data/art824/rocket/roc_trails_5.png new file mode 100644 index 0000000..1a400e1 Binary files /dev/null and b/data/art824/rocket/roc_trails_5.png differ diff --git a/data/art824/rocket/rocket_a_0_0.png b/data/art824/rocket/rocket_a_0_0.png new file mode 100644 index 0000000..4de6534 Binary files /dev/null and b/data/art824/rocket/rocket_a_0_0.png differ diff --git a/data/art824/rocket/rocket_a_0_1.png b/data/art824/rocket/rocket_a_0_1.png new file mode 100644 index 0000000..37c68e1 Binary files /dev/null and b/data/art824/rocket/rocket_a_0_1.png differ diff --git a/data/art824/rocket/rocket_a_0_2.png b/data/art824/rocket/rocket_a_0_2.png new file mode 100644 index 0000000..6377c72 Binary files /dev/null and b/data/art824/rocket/rocket_a_0_2.png differ diff --git a/data/art824/rocket/rocket_a_0_3.png b/data/art824/rocket/rocket_a_0_3.png new file mode 100644 index 0000000..8b43597 Binary files /dev/null and b/data/art824/rocket/rocket_a_0_3.png differ diff --git a/data/art824/rocket/rocket_a_0_4.png b/data/art824/rocket/rocket_a_0_4.png new file mode 100644 index 0000000..e78f741 Binary files /dev/null and b/data/art824/rocket/rocket_a_0_4.png differ diff --git a/data/art824/rocket/rocket_a_0_5.png b/data/art824/rocket/rocket_a_0_5.png new file mode 100644 index 0000000..63a3f26 Binary files /dev/null and b/data/art824/rocket/rocket_a_0_5.png differ diff --git a/data/art824/rocket/rocket_a_0_6.png b/data/art824/rocket/rocket_a_0_6.png new file mode 100644 index 0000000..25069d0 Binary files /dev/null and b/data/art824/rocket/rocket_a_0_6.png differ diff --git a/data/art824/rocket/rocket_a_0_7.png b/data/art824/rocket/rocket_a_0_7.png new file mode 100644 index 0000000..a6dc558 Binary files /dev/null and b/data/art824/rocket/rocket_a_0_7.png differ diff --git a/data/art824/rocket/rocket_a_1_0.png b/data/art824/rocket/rocket_a_1_0.png new file mode 100644 index 0000000..858a3b1 Binary files /dev/null and b/data/art824/rocket/rocket_a_1_0.png differ diff --git a/data/art824/rocket/rocket_a_1_1.png b/data/art824/rocket/rocket_a_1_1.png new file mode 100644 index 0000000..7f39177 Binary files /dev/null and b/data/art824/rocket/rocket_a_1_1.png differ diff --git a/data/art824/rocket/rocket_a_1_2.png b/data/art824/rocket/rocket_a_1_2.png new file mode 100644 index 0000000..c3e442c Binary files /dev/null and b/data/art824/rocket/rocket_a_1_2.png differ diff --git a/data/art824/rocket/rocket_a_1_3.png b/data/art824/rocket/rocket_a_1_3.png new file mode 100644 index 0000000..d238fbc Binary files /dev/null and b/data/art824/rocket/rocket_a_1_3.png differ diff --git a/data/art824/rtower.amx b/data/art824/rtower.amx new file mode 100644 index 0000000..bad2411 --- /dev/null +++ b/data/art824/rtower.amx @@ -0,0 +1,1559 @@ + + + + + +80 +false + + +<_x0030_ href="#ref-5"/> +<_x0031_ href="#ref-6"/> +<_x0032_ href="#ref-7"/> +<_x0033_ href="#ref-8"/> +<_x0034_ href="#ref-9"/> +<_x0035_ href="#ref-10"/> +<_x0036_ href="#ref-11"/> +<_x0037_ href="#ref-12"/> +<_x0038_ href="#ref-13"/> +<_x0039_ href="#ref-14"/> +<_x0031_0 href="#ref-15"/> +<_x0031_1 href="#ref-16"/> +<_x0031_2 href="#ref-17"/> +<_x0031_3 href="#ref-18"/> +<_x0031_4 href="#ref-19"/> +<_x0031_5 href="#ref-20"/> +<_x0031_6 href="#ref-21"/> +<_x0031_7 href="#ref-22"/> +<_x0031_8 href="#ref-23"/> +<_x0031_9 href="#ref-24"/> +<_x0032_0 href="#ref-25"/> +<_x0032_1 href="#ref-26"/> +<_x0032_2 href="#ref-27"/> +<_x0032_3 href="#ref-28"/> +<_x0032_4 href="#ref-29"/> +<_x0032_5 href="#ref-30"/> +<_x0032_6 href="#ref-31"/> +<_x0032_7 href="#ref-32"/> +<_x0032_8 href="#ref-33"/> +<_x0032_9 href="#ref-34"/> +<_x0033_0 href="#ref-35"/> +<_x0033_1 href="#ref-36"/> +<_x0033_2 href="#ref-37"/> +<_x0033_3 href="#ref-38"/> +<_x0033_4 href="#ref-39"/> +<_x0033_5 href="#ref-40"/> +<_x0033_6 href="#ref-41"/> +<_x0033_7 href="#ref-42"/> +<_x0033_8 href="#ref-43"/> +<_x0033_9 href="#ref-44"/> +<_x0034_0 href="#ref-45"/> +<_x0034_1 href="#ref-46"/> +<_x0034_2 href="#ref-47"/> +<_x0034_3 href="#ref-48"/> +<_x0034_4 href="#ref-49"/> +<_x0034_5 href="#ref-50"/> +<_x0034_6 href="#ref-51"/> +<_x0034_7 href="#ref-52"/> +<_x0034_8 href="#ref-53"/> +<_x0034_9 href="#ref-54"/> +<_x0035_0 href="#ref-55"/> +<_x0035_1 href="#ref-56"/> +<_x0035_2 href="#ref-57"/> +<_x0035_3 href="#ref-58"/> +<_x0035_4 href="#ref-59"/> +<_x0035_5 href="#ref-60"/> +<_x0035_6 href="#ref-61"/> +<_x0035_7 href="#ref-62"/> +<_x0035_8 href="#ref-63"/> +<_x0035_9 href="#ref-64"/> +<_x0036_0 href="#ref-65"/> +<_x0036_1 href="#ref-66"/> +<_x0036_2 href="#ref-67"/> +<_x0036_3 href="#ref-68"/> +<_x0036_4 href="#ref-69"/> +<_x0036_5 href="#ref-70"/> +<_x0036_6 href="#ref-71"/> +<_x0036_7 href="#ref-72"/> +<_x0036_8 href="#ref-73"/> +<_x0036_9 href="#ref-74"/> +<_x0037_0 href="#ref-75"/> +<_x0037_1 href="#ref-76"/> +<_x0037_2 href="#ref-77"/> +<_x0037_3 href="#ref-78"/> +<_x0037_4 href="#ref-79"/> +<_x0037_5 href="#ref-80"/> +<_x0037_6 href="#ref-81"/> +<_x0037_7 href="#ref-82"/> +<_x0037_8 href="#ref-83"/> +<_x0037_9 href="#ref-84"/> +<_x0038_0 href="#ref-85"/> +<_x0038_1 href="#ref-86"/> +<_x0038_2 href="#ref-87"/> +<_x0038_3 href="#ref-88"/> +<_x0038_4 href="#ref-89"/> +<_x0038_5 href="#ref-90"/> +<_x0038_6 href="#ref-91"/> +<_x0038_7 href="#ref-92"/> +<_x0038_8 href="#ref-93"/> +<_x0038_9 href="#ref-94"/> +<_x0039_0 href="#ref-95"/> +<_x0039_1 href="#ref-96"/> +<_x0039_2 href="#ref-97"/> +<_x0039_3 href="#ref-98"/> +<_x0039_4 href="#ref-99"/> +<_x0039_5 href="#ref-100"/> +<_x0039_6 href="#ref-101"/> +<_x0039_7 href="#ref-102"/> +<_x0039_8 href="#ref-103"/> + + +<_x0030_ href="#ref-104"/> +<_x0031_ href="#ref-105"/> +<_x0032_ href="#ref-106"/> +<_x0033_ href="#ref-107"/> +<_x0034_ href="#ref-108"/> +<_x0035_ href="#ref-109"/> +<_x0036_ href="#ref-110"/> +<_x0037_ href="#ref-111"/> +<_x0038_ href="#ref-112"/> +<_x0039_ href="#ref-113"/> +<_x0031_0 href="#ref-114"/> +<_x0031_1 href="#ref-115"/> +<_x0031_2 href="#ref-116"/> +<_x0031_3 href="#ref-117"/> +<_x0031_4 href="#ref-118"/> +<_x0031_5 href="#ref-119"/> +<_x0031_6 href="#ref-120"/> +<_x0031_7 href="#ref-121"/> +<_x0031_8 href="#ref-122"/> +<_x0031_9 href="#ref-123"/> +<_x0032_0 href="#ref-124"/> + + +rtower\rtower_turret_00_0.png + + +rtower\rtower_turret_00_1.png + + +rtower\rtower_turret_00_2.png + + +rtower\rtower_turret_01_0.png + + +rtower\rtower_turret_01_1.png + + +rtower\rtower_turret_01_2.png + + +rtower\rtower_turret_02_0.png + + +rtower\rtower_turret_02_1.png + + +rtower\rtower_turret_02_2.png + + +rtower\rtower_turret_03_0.png + + +rtower\rtower_turret_03_1.png + + +rtower\rtower_turret_03_2.png + + +rtower\rtower_turret_04_0.png + + +rtower\rtower_turret_04_1.png + + +rtower\rtower_turret_04_2.png + + +rtower\rtower_turret_05_0.png + + +rtower\rtower_turret_05_1.png + + +rtower\rtower_turret_05_2.png + + +rtower\rtower_turret_06_0.png + + +rtower\rtower_turret_06_1.png + + +rtower\rtower_turret_06_2.png + + +rtower\rtower_turret_07_0.png + + +rtower\rtower_turret_07_1.png + + +rtower\rtower_turret_07_2.png + + +rtower\rtower_turret_08_0.png + + +rtower\rtower_turret_08_1.png + + +rtower\rtower_turret_08_2.png + + +rtower\rtower_turret_09_0.png + + +rtower\rtower_turret_09_1.png + + +rtower\rtower_turret_09_2.png + + +rtower\rtower_turret_10_0.png + + +rtower\rtower_turret_10_1.png + + +rtower\rtower_turret_10_2.png + + +rtower\rtower_turret_11_0.png + + +rtower\rtower_turret_11_1.png + + +rtower\rtower_turret_11_2.png + + +rtower\rtower_turret_12_0.png + + +rtower\rtower_turret_12_1.png + + +rtower\rtower_turret_12_2.png + + +rtower\rtower_turret_13_0.png + + +rtower\rtower_turret_13_1.png + + +rtower\rtower_turret_13_2.png + + +rtower\rtower_turret_14_0.png + + +rtower\rtower_turret_14_1.png + + +rtower\rtower_turret_14_2.png + + +rtower\rtower_turret_15_0.png + + +rtower\rtower_turret_15_1.png + + +rtower\rtower_turret_15_2.png + + +rtower\rtower_turret_15_2.png + + +rtower\rtower_turret_00_1.png + + +rtower\rtower_turret_00_2.png + + +rtower\rtower_turret_01_0.png + + +rtower\rtower_turret_01_1.png + + +rtower\rtower_turret_01_2.png + + +rtower\rtower_turret_02_0.png + + +rtower\rtower_turret_02_1.png + + +rtower\rtower_turret_02_2.png + + +rtower\rtower_turret_03_0.png + + +rtower\rtower_turret_03_1.png + + +rtower\rtower_turret_03_2.png + + +rtower\rtower_turret_04_0.png + + +rtower\rtower_turret_04_1.png + + +rtower\rtower_turret_04_2.png + + +rtower\rtower_turret_05_0.png + + +rtower\rtower_turret_05_1.png + + +rtower\rtower_turret_05_2.png + + +rtower\rtower_turret_06_0.png + + +rtower\rtower_turret_06_1.png + + +rtower\rtower_turret_06_2.png + + +rtower\rtower_turret_07_0.png + + +rtower\rtower_turret_07_1.png + + +rtower\rtower_turret_07_2.png + + +rtower\rtower_turret_08_0.png + + +rtower\rtower_turret_08_1.png + + +rtower\rtower_turret_08_2.png + + +rtower\rtower_turret_09_0.png + + +rtower\rtower_turret_09_1.png + + +rtower\rtower_turret_09_2.png + + +rtower\rtower_turret_10_0.png + + +rtower\rtower_turret_10_1.png + + +rtower\rtower_turret_10_2.png + + +rtower\rtower_turret_11_0.png + + +rtower\rtower_turret_11_1.png + + +rtower\rtower_turret_11_2.png + + +rtower\rtower_turret_12_0.png + + +rtower\rtower_turret_12_1.png + + +rtower\rtower_turret_12_2.png + + +rtower\rtower_turret_13_0.png + + +rtower\rtower_turret_13_1.png + + +rtower\rtower_turret_13_2.png + + +rtower\rtower_turret_14_0.png + + +rtower\rtower_turret_14_1.png + + +rtower\rtower_turret_14_2.png + + +rtower\rtower_turret_15_0.png + + +rtower\rtower_turret_15_1.png + + +rtower\rtower_turret_00_0.png + + +rtower\rtower_base_2_0.png + + +rtower\rtower_base_1_0.png + + +rtower\rtower_base_0_0.png + + +base 0 +0 +1 +<_x0030_ href="#ref-225"/> + + +base 1 +0 +1 +<_x0030_ href="#ref-227"/> + + +base 2 +0 +1 +<_x0030_ href="#ref-229"/> + + +turret 00 +0 +3 +<_x0030_ href="#ref-231"/> +<_x0031_ href="#ref-232"/> +<_x0032_ href="#ref-233"/> + + +turret 01 +0 +3 +<_x0030_ href="#ref-235"/> +<_x0031_ href="#ref-236"/> +<_x0032_ href="#ref-237"/> + + +turret 02 +0 +3 +<_x0030_ href="#ref-239"/> +<_x0031_ href="#ref-240"/> +<_x0032_ href="#ref-241"/> + + +turret 03 +0 +3 +<_x0030_ href="#ref-243"/> +<_x0031_ href="#ref-244"/> +<_x0032_ href="#ref-245"/> + + +turret 04 +0 +3 +<_x0030_ href="#ref-247"/> +<_x0031_ href="#ref-248"/> +<_x0032_ href="#ref-249"/> + + +turret 05 +0 +3 +<_x0030_ href="#ref-251"/> +<_x0031_ href="#ref-252"/> +<_x0032_ href="#ref-253"/> + + +turret 06 +0 +3 +<_x0030_ href="#ref-255"/> +<_x0031_ href="#ref-256"/> +<_x0032_ href="#ref-257"/> + + +turret 07 +0 +3 +<_x0030_ href="#ref-259"/> +<_x0031_ href="#ref-260"/> +<_x0032_ href="#ref-261"/> + + +turret 08 +0 +3 +<_x0030_ href="#ref-263"/> +<_x0031_ href="#ref-264"/> +<_x0032_ href="#ref-265"/> + + +turret 09 +0 +3 +<_x0030_ href="#ref-267"/> +<_x0031_ href="#ref-268"/> +<_x0032_ href="#ref-269"/> + + +turret 10 +0 +3 +<_x0030_ href="#ref-271"/> +<_x0031_ href="#ref-272"/> +<_x0032_ href="#ref-273"/> + + +turret 11 +0 +3 +<_x0030_ href="#ref-275"/> +<_x0031_ href="#ref-276"/> +<_x0032_ href="#ref-277"/> + + +turret 12 +0 +3 +<_x0030_ href="#ref-279"/> +<_x0031_ href="#ref-280"/> +<_x0032_ href="#ref-281"/> + + +turret 13 +0 +3 +<_x0030_ href="#ref-283"/> +<_x0031_ href="#ref-284"/> +<_x0032_ href="#ref-285"/> + + +turret 14 +0 +3 +<_x0030_ href="#ref-287"/> +<_x0031_ href="#ref-288"/> +<_x0032_ href="#ref-289"/> + + +turret 15 +0 +3 +<_x0030_ href="#ref-291"/> +<_x0031_ href="#ref-292"/> +<_x0032_ href="#ref-293"/> + + +icon +0 +1 +<_x0030_ href="#ref-295"/> + + +help +0 +1 +<_x0030_ href="#ref-297"/> + + + +0 + +12 +5 + + + + +0 + +12 +5 + + + + +0 + +12 +5 + + + + +0 + +0 +0 + + + + +0 + +-4 +-13 + + + + +0 + +-10 +-4 + + + + +0 + +0 +0 + + + + +0 + +11 +-10 + + + + +0 + +-10 +-4 + + + + +0 + +0 +0 + + + + +0 + +7 +-13 + + + + +0 + +-10 +-4 + + + + +0 + +0 +0 + + + + +0 + +14 +-4 + + + + +0 + +-10 +-4 + + + + +0 + +0 +0 + + + + +0 + +14 +-4 + + + + +0 + +-10 +-4 + + + + +0 + +0 +0 + + + + +0 + +11 +10 + + + + +0 + +-10 +-4 + + + + +0 + +0 +0 + + + + +0 + +13 +6 + + + + +0 + +-10 +-4 + + + + +0 + +0 +0 + + + + +0 + +2 +13 + + + + +0 + +-10 +-4 + + + + +0 + +0 +0 + + + + +0 + +5 +13 + + + + +0 + +-10 +-4 + + + + +0 + +0 +0 + + + + +0 + +-11 +9 + + + + +0 + +-10 +-4 + + + + +0 + +0 +0 + + + + +0 + +-7 +13 + + + + +0 + +-10 +-4 + + + + +0 + +0 +0 + + + + +0 + +-11 +9 + + + + +0 + +-10 +-4 + + + + +0 + +0 +0 + + + + +0 + +-13 +-4 + + + + +0 + +-10 +-4 + + + + +0 + +0 +0 + + + + +0 + +-13 +-4 + + + + +0 + +-10 +-4 + + + + +0 + +0 +0 + + + + +0 + +-7 +-13 + + + + +0 + +-10 +-4 + + + + +0 + +0 +0 + + + + +0 + +-10 +-14 + + + + +0 + +-10 +-4 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + +<_x0030_ href="#ref-352"/> + + +<_x0030_ href="#ref-353"/> + + +<_x0030_ href="#ref-354"/> + + +<_x0030_ href="#ref-355"/> + + +<_x0030_ href="#ref-356"/> + + +<_x0030_ href="#ref-357"/> + + +<_x0030_ href="#ref-358"/> + + +<_x0030_ href="#ref-359"/> + + +<_x0030_ href="#ref-360"/> + + +<_x0030_ href="#ref-361"/> + + +<_x0030_ href="#ref-362"/> + + +<_x0030_ href="#ref-363"/> + + +<_x0030_ href="#ref-364"/> + + +<_x0030_ href="#ref-365"/> + + +<_x0030_ href="#ref-366"/> + + +<_x0030_ href="#ref-367"/> + + +<_x0030_ href="#ref-368"/> + + +<_x0030_ href="#ref-369"/> + + +<_x0030_ href="#ref-370"/> + + +<_x0030_ href="#ref-371"/> + + +<_x0030_ href="#ref-372"/> + + +<_x0030_ href="#ref-373"/> + + +<_x0030_ href="#ref-374"/> + + +<_x0030_ href="#ref-375"/> + + +<_x0030_ href="#ref-376"/> + + +<_x0030_ href="#ref-377"/> + + +<_x0030_ href="#ref-378"/> + + +<_x0030_ href="#ref-379"/> + + +<_x0030_ href="#ref-380"/> + + +<_x0030_ href="#ref-381"/> + + +<_x0030_ href="#ref-382"/> + + +<_x0030_ href="#ref-383"/> + + +<_x0030_ href="#ref-384"/> + + +<_x0030_ href="#ref-385"/> + + +<_x0030_ href="#ref-386"/> + + +<_x0030_ href="#ref-387"/> + + +<_x0030_ href="#ref-388"/> + + +<_x0030_ href="#ref-389"/> + + +<_x0030_ href="#ref-390"/> + + +<_x0030_ href="#ref-391"/> + + +<_x0030_ href="#ref-392"/> + + +<_x0030_ href="#ref-393"/> + + +<_x0030_ href="#ref-394"/> + + +<_x0030_ href="#ref-395"/> + + +<_x0030_ href="#ref-396"/> + + +<_x0030_ href="#ref-397"/> + + +<_x0030_ href="#ref-398"/> + + +<_x0030_ href="#ref-399"/> + + +<_x0030_ href="#ref-400"/> + + +<_x0030_ href="#ref-401"/> + + +<_x0030_ href="#ref-402"/> + + +<_x0030_ href="#ref-403"/> +<_x0031_ href="#ref-404"/> + + +<_x0030_ href="#ref-405"/> +<_x0031_ href="#ref-406"/> + + + +2 +0 + + + + + +2 +0 + + + + + +2 +0 + + + + + +14 +15 + + + + + +14 +15 + + + + + +14 +15 + + + + + +14 +14 + + + + + +14 +14 + + + + + +14 +14 + + + + + +14 +14 + + + + + +14 +14 + + + + + +14 +14 + + + + + +14 +14 + + + + + +14 +14 + + + + + +14 +14 + + + + + +14 +14 + + + + + +14 +14 + + + + + +14 +14 + + + + + +13 +14 + + + + + +13 +14 + + + + + +13 +14 + + + + + +14 +14 + + + + + +14 +14 + + + + + +14 +14 + + + + + +14 +14 + + + + + +14 +14 + + + + + +14 +14 + + + + + +14 +15 + + + + + +14 +15 + + + + + +14 +15 + + + + + +14 +14 + + + + + +14 +14 + + + + + +14 +14 + + + + + +14 +14 + + + + + +14 +14 + + + + + +14 +14 + + + + + +14 +14 + + + + + +14 +14 + + + + + +14 +14 + + + + + +14 +14 + + + + + +14 +14 + + + + + +14 +14 + + + + + +13 +14 + + + + + +13 +14 + + + + + +13 +14 + + + + + +14 +14 + + + + + +14 +14 + + + + + +14 +14 + + + + + +14 +15 + + + + + +14 +15 + + + + + +14 +15 + + + + + +3 +8 + + + + + +4 +-2 + + + + + +14 +25 + + + + + +14 +14 + + + + + diff --git a/data/art824/rtower/rtower_base_0_0.png b/data/art824/rtower/rtower_base_0_0.png new file mode 100644 index 0000000..b8e0b4d Binary files /dev/null and b/data/art824/rtower/rtower_base_0_0.png differ diff --git a/data/art824/rtower/rtower_base_1_0.png b/data/art824/rtower/rtower_base_1_0.png new file mode 100644 index 0000000..7ce0767 Binary files /dev/null and b/data/art824/rtower/rtower_base_1_0.png differ diff --git a/data/art824/rtower/rtower_base_2_0.png b/data/art824/rtower/rtower_base_2_0.png new file mode 100644 index 0000000..72974d0 Binary files /dev/null and b/data/art824/rtower/rtower_base_2_0.png differ diff --git a/data/art824/rtower/rtower_turret_00_0.png b/data/art824/rtower/rtower_turret_00_0.png new file mode 100644 index 0000000..ef5e512 Binary files /dev/null and b/data/art824/rtower/rtower_turret_00_0.png differ diff --git a/data/art824/rtower/rtower_turret_00_1.png b/data/art824/rtower/rtower_turret_00_1.png new file mode 100644 index 0000000..0aea6bd Binary files /dev/null and b/data/art824/rtower/rtower_turret_00_1.png differ diff --git a/data/art824/rtower/rtower_turret_00_2.png b/data/art824/rtower/rtower_turret_00_2.png new file mode 100644 index 0000000..24bf5ee Binary files /dev/null and b/data/art824/rtower/rtower_turret_00_2.png differ diff --git a/data/art824/rtower/rtower_turret_01_0.png b/data/art824/rtower/rtower_turret_01_0.png new file mode 100644 index 0000000..124e9da Binary files /dev/null and b/data/art824/rtower/rtower_turret_01_0.png differ diff --git a/data/art824/rtower/rtower_turret_01_1.png b/data/art824/rtower/rtower_turret_01_1.png new file mode 100644 index 0000000..2e536ad Binary files /dev/null and b/data/art824/rtower/rtower_turret_01_1.png differ diff --git a/data/art824/rtower/rtower_turret_01_2.png b/data/art824/rtower/rtower_turret_01_2.png new file mode 100644 index 0000000..c3d6ecc Binary files /dev/null and b/data/art824/rtower/rtower_turret_01_2.png differ diff --git a/data/art824/rtower/rtower_turret_02_0.png b/data/art824/rtower/rtower_turret_02_0.png new file mode 100644 index 0000000..c541926 Binary files /dev/null and b/data/art824/rtower/rtower_turret_02_0.png differ diff --git a/data/art824/rtower/rtower_turret_02_1.png b/data/art824/rtower/rtower_turret_02_1.png new file mode 100644 index 0000000..1d5d965 Binary files /dev/null and b/data/art824/rtower/rtower_turret_02_1.png differ diff --git a/data/art824/rtower/rtower_turret_02_2.png b/data/art824/rtower/rtower_turret_02_2.png new file mode 100644 index 0000000..7733c8b Binary files /dev/null and b/data/art824/rtower/rtower_turret_02_2.png differ diff --git a/data/art824/rtower/rtower_turret_03_0.png b/data/art824/rtower/rtower_turret_03_0.png new file mode 100644 index 0000000..a4bd452 Binary files /dev/null and b/data/art824/rtower/rtower_turret_03_0.png differ diff --git a/data/art824/rtower/rtower_turret_03_1.png b/data/art824/rtower/rtower_turret_03_1.png new file mode 100644 index 0000000..986ac8e Binary files /dev/null and b/data/art824/rtower/rtower_turret_03_1.png differ diff --git a/data/art824/rtower/rtower_turret_03_2.png b/data/art824/rtower/rtower_turret_03_2.png new file mode 100644 index 0000000..8d16360 Binary files /dev/null and b/data/art824/rtower/rtower_turret_03_2.png differ diff --git a/data/art824/rtower/rtower_turret_04_0.png b/data/art824/rtower/rtower_turret_04_0.png new file mode 100644 index 0000000..dfbb843 Binary files /dev/null and b/data/art824/rtower/rtower_turret_04_0.png differ diff --git a/data/art824/rtower/rtower_turret_04_1.png b/data/art824/rtower/rtower_turret_04_1.png new file mode 100644 index 0000000..bed94fc Binary files /dev/null and b/data/art824/rtower/rtower_turret_04_1.png differ diff --git a/data/art824/rtower/rtower_turret_04_2.png b/data/art824/rtower/rtower_turret_04_2.png new file mode 100644 index 0000000..ba2dfc6 Binary files /dev/null and b/data/art824/rtower/rtower_turret_04_2.png differ diff --git a/data/art824/rtower/rtower_turret_05_0.png b/data/art824/rtower/rtower_turret_05_0.png new file mode 100644 index 0000000..390892b Binary files /dev/null and b/data/art824/rtower/rtower_turret_05_0.png differ diff --git a/data/art824/rtower/rtower_turret_05_1.png b/data/art824/rtower/rtower_turret_05_1.png new file mode 100644 index 0000000..53181a3 Binary files /dev/null and b/data/art824/rtower/rtower_turret_05_1.png differ diff --git a/data/art824/rtower/rtower_turret_05_2.png b/data/art824/rtower/rtower_turret_05_2.png new file mode 100644 index 0000000..5b097f4 Binary files /dev/null and b/data/art824/rtower/rtower_turret_05_2.png differ diff --git a/data/art824/rtower/rtower_turret_06_0.png b/data/art824/rtower/rtower_turret_06_0.png new file mode 100644 index 0000000..4d25227 Binary files /dev/null and b/data/art824/rtower/rtower_turret_06_0.png differ diff --git a/data/art824/rtower/rtower_turret_06_1.png b/data/art824/rtower/rtower_turret_06_1.png new file mode 100644 index 0000000..0321705 Binary files /dev/null and b/data/art824/rtower/rtower_turret_06_1.png differ diff --git a/data/art824/rtower/rtower_turret_06_2.png b/data/art824/rtower/rtower_turret_06_2.png new file mode 100644 index 0000000..516ceae Binary files /dev/null and b/data/art824/rtower/rtower_turret_06_2.png differ diff --git a/data/art824/rtower/rtower_turret_07_0.png b/data/art824/rtower/rtower_turret_07_0.png new file mode 100644 index 0000000..ea52aed Binary files /dev/null and b/data/art824/rtower/rtower_turret_07_0.png differ diff --git a/data/art824/rtower/rtower_turret_07_1.png b/data/art824/rtower/rtower_turret_07_1.png new file mode 100644 index 0000000..cfa2882 Binary files /dev/null and b/data/art824/rtower/rtower_turret_07_1.png differ diff --git a/data/art824/rtower/rtower_turret_07_2.png b/data/art824/rtower/rtower_turret_07_2.png new file mode 100644 index 0000000..31fc20e Binary files /dev/null and b/data/art824/rtower/rtower_turret_07_2.png differ diff --git a/data/art824/rtower/rtower_turret_08_0.png b/data/art824/rtower/rtower_turret_08_0.png new file mode 100644 index 0000000..ef10135 Binary files /dev/null and b/data/art824/rtower/rtower_turret_08_0.png differ diff --git a/data/art824/rtower/rtower_turret_08_1.png b/data/art824/rtower/rtower_turret_08_1.png new file mode 100644 index 0000000..114ad04 Binary files /dev/null and b/data/art824/rtower/rtower_turret_08_1.png differ diff --git a/data/art824/rtower/rtower_turret_08_2.png b/data/art824/rtower/rtower_turret_08_2.png new file mode 100644 index 0000000..609e841 Binary files /dev/null and b/data/art824/rtower/rtower_turret_08_2.png differ diff --git a/data/art824/rtower/rtower_turret_09_0.png b/data/art824/rtower/rtower_turret_09_0.png new file mode 100644 index 0000000..134a1ff Binary files /dev/null and b/data/art824/rtower/rtower_turret_09_0.png differ diff --git a/data/art824/rtower/rtower_turret_09_1.png b/data/art824/rtower/rtower_turret_09_1.png new file mode 100644 index 0000000..aaaecf2 Binary files /dev/null and b/data/art824/rtower/rtower_turret_09_1.png differ diff --git a/data/art824/rtower/rtower_turret_09_2.png b/data/art824/rtower/rtower_turret_09_2.png new file mode 100644 index 0000000..ac78485 Binary files /dev/null and b/data/art824/rtower/rtower_turret_09_2.png differ diff --git a/data/art824/rtower/rtower_turret_10_0.png b/data/art824/rtower/rtower_turret_10_0.png new file mode 100644 index 0000000..8100438 Binary files /dev/null and b/data/art824/rtower/rtower_turret_10_0.png differ diff --git a/data/art824/rtower/rtower_turret_10_1.png b/data/art824/rtower/rtower_turret_10_1.png new file mode 100644 index 0000000..aae0989 Binary files /dev/null and b/data/art824/rtower/rtower_turret_10_1.png differ diff --git a/data/art824/rtower/rtower_turret_10_2.png b/data/art824/rtower/rtower_turret_10_2.png new file mode 100644 index 0000000..ba660f0 Binary files /dev/null and b/data/art824/rtower/rtower_turret_10_2.png differ diff --git a/data/art824/rtower/rtower_turret_11_0.png b/data/art824/rtower/rtower_turret_11_0.png new file mode 100644 index 0000000..d258422 Binary files /dev/null and b/data/art824/rtower/rtower_turret_11_0.png differ diff --git a/data/art824/rtower/rtower_turret_11_1.png b/data/art824/rtower/rtower_turret_11_1.png new file mode 100644 index 0000000..b4ced6b Binary files /dev/null and b/data/art824/rtower/rtower_turret_11_1.png differ diff --git a/data/art824/rtower/rtower_turret_11_2.png b/data/art824/rtower/rtower_turret_11_2.png new file mode 100644 index 0000000..fdbf964 Binary files /dev/null and b/data/art824/rtower/rtower_turret_11_2.png differ diff --git a/data/art824/rtower/rtower_turret_12_0.png b/data/art824/rtower/rtower_turret_12_0.png new file mode 100644 index 0000000..c045a76 Binary files /dev/null and b/data/art824/rtower/rtower_turret_12_0.png differ diff --git a/data/art824/rtower/rtower_turret_12_1.png b/data/art824/rtower/rtower_turret_12_1.png new file mode 100644 index 0000000..c8c5a3d Binary files /dev/null and b/data/art824/rtower/rtower_turret_12_1.png differ diff --git a/data/art824/rtower/rtower_turret_12_2.png b/data/art824/rtower/rtower_turret_12_2.png new file mode 100644 index 0000000..e0c8c8f Binary files /dev/null and b/data/art824/rtower/rtower_turret_12_2.png differ diff --git a/data/art824/rtower/rtower_turret_13_0.png b/data/art824/rtower/rtower_turret_13_0.png new file mode 100644 index 0000000..a1e7beb Binary files /dev/null and b/data/art824/rtower/rtower_turret_13_0.png differ diff --git a/data/art824/rtower/rtower_turret_13_1.png b/data/art824/rtower/rtower_turret_13_1.png new file mode 100644 index 0000000..deea04a Binary files /dev/null and b/data/art824/rtower/rtower_turret_13_1.png differ diff --git a/data/art824/rtower/rtower_turret_13_2.png b/data/art824/rtower/rtower_turret_13_2.png new file mode 100644 index 0000000..ac7efff Binary files /dev/null and b/data/art824/rtower/rtower_turret_13_2.png differ diff --git a/data/art824/rtower/rtower_turret_14_0.png b/data/art824/rtower/rtower_turret_14_0.png new file mode 100644 index 0000000..c27d8c1 Binary files /dev/null and b/data/art824/rtower/rtower_turret_14_0.png differ diff --git a/data/art824/rtower/rtower_turret_14_1.png b/data/art824/rtower/rtower_turret_14_1.png new file mode 100644 index 0000000..ca161c3 Binary files /dev/null and b/data/art824/rtower/rtower_turret_14_1.png differ diff --git a/data/art824/rtower/rtower_turret_14_2.png b/data/art824/rtower/rtower_turret_14_2.png new file mode 100644 index 0000000..2288feb Binary files /dev/null and b/data/art824/rtower/rtower_turret_14_2.png differ diff --git a/data/art824/rtower/rtower_turret_15_0.png b/data/art824/rtower/rtower_turret_15_0.png new file mode 100644 index 0000000..8c20f93 Binary files /dev/null and b/data/art824/rtower/rtower_turret_15_0.png differ diff --git a/data/art824/rtower/rtower_turret_15_1.png b/data/art824/rtower/rtower_turret_15_1.png new file mode 100644 index 0000000..9d11cd9 Binary files /dev/null and b/data/art824/rtower/rtower_turret_15_1.png differ diff --git a/data/art824/rtower/rtower_turret_15_2.png b/data/art824/rtower/rtower_turret_15_2.png new file mode 100644 index 0000000..4a66e23 Binary files /dev/null and b/data/art824/rtower/rtower_turret_15_2.png differ diff --git a/data/art824/sexplosion.amx b/data/art824/sexplosion.amx new file mode 100644 index 0000000..6d78536 --- /dev/null +++ b/data/art824/sexplosion.amx @@ -0,0 +1,158 @@ + + + + + +80 +true + + +<_x0030_ href="#ref-5"/> +<_x0031_ href="#ref-6"/> +<_x0032_ href="#ref-7"/> +<_x0033_ href="#ref-8"/> +<_x0034_ href="#ref-9"/> +<_x0035_ href="#ref-10"/> + + +<_x0030_ href="#ref-11"/> + + +sexplosion\sexplosion_a_0_5.png + + +sexplosion\sexplosion_a_0_1.png + + +sexplosion\sexplosion_a_0_2.png + + +sexplosion\sexplosion_a_0_3.png + + +sexplosion\sexplosion_a_0_4.png + + +sexplosion\sexplosion_a_0_0.png + + +a 0 +0 +6 +<_x0030_ href="#ref-19"/> +<_x0031_ href="#ref-20"/> +<_x0032_ href="#ref-21"/> +<_x0033_ href="#ref-22"/> +<_x0034_ href="#ref-23"/> +<_x0035_ href="#ref-24"/> + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + +<_x0030_ href="#ref-32"/> + + +<_x0030_ href="#ref-33"/> + + +<_x0030_ href="#ref-34"/> + + +<_x0030_ href="#ref-35"/> + + +<_x0030_ href="#ref-36"/> + + +<_x0030_ href="#ref-37"/> + + + +39 +33 + + + + + +39 +36 + + + + + +39 +36 + + + + + +39 +36 + + + + + +39 +36 + + + + + +39 +36 + + + + + diff --git a/data/art824/sexplosion/sexplosion_a_0_0.png b/data/art824/sexplosion/sexplosion_a_0_0.png new file mode 100644 index 0000000..30519f0 Binary files /dev/null and b/data/art824/sexplosion/sexplosion_a_0_0.png differ diff --git a/data/art824/sexplosion/sexplosion_a_0_1.png b/data/art824/sexplosion/sexplosion_a_0_1.png new file mode 100644 index 0000000..ae7d13a Binary files /dev/null and b/data/art824/sexplosion/sexplosion_a_0_1.png differ diff --git a/data/art824/sexplosion/sexplosion_a_0_2.png b/data/art824/sexplosion/sexplosion_a_0_2.png new file mode 100644 index 0000000..3d245b1 Binary files /dev/null and b/data/art824/sexplosion/sexplosion_a_0_2.png differ diff --git a/data/art824/sexplosion/sexplosion_a_0_3.png b/data/art824/sexplosion/sexplosion_a_0_3.png new file mode 100644 index 0000000..dda738c Binary files /dev/null and b/data/art824/sexplosion/sexplosion_a_0_3.png differ diff --git a/data/art824/sexplosion/sexplosion_a_0_4.png b/data/art824/sexplosion/sexplosion_a_0_4.png new file mode 100644 index 0000000..c0bf71d Binary files /dev/null and b/data/art824/sexplosion/sexplosion_a_0_4.png differ diff --git a/data/art824/sexplosion/sexplosion_a_0_5.png b/data/art824/sexplosion/sexplosion_a_0_5.png new file mode 100644 index 0000000..7c8657c Binary files /dev/null and b/data/art824/sexplosion/sexplosion_a_0_5.png differ diff --git a/data/art824/shadowfont.bmp b/data/art824/shadowfont.bmp new file mode 100644 index 0000000..3af5fbd Binary files /dev/null and b/data/art824/shadowfont.bmp differ diff --git a/data/art824/shadowfont.txt b/data/art824/shadowfont.txt new file mode 100644 index 0000000..180df78 --- /dev/null +++ b/data/art824/shadowfont.txt @@ -0,0 +1 @@ +!"#$%&'()*+,-./ ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz:;<=>?\@_[] diff --git a/data/art824/shellrawbitmaps/copyright.bmp b/data/art824/shellrawbitmaps/copyright.bmp new file mode 100644 index 0000000..29d8c4f Binary files /dev/null and b/data/art824/shellrawbitmaps/copyright.bmp differ diff --git a/data/art824/shellrawbitmaps/title.bmp b/data/art824/shellrawbitmaps/title.bmp new file mode 100644 index 0000000..6919695 Binary files /dev/null and b/data/art824/shellrawbitmaps/title.bmp differ diff --git a/data/art824/smoke.amx b/data/art824/smoke.amx new file mode 100644 index 0000000..1069a9e --- /dev/null +++ b/data/art824/smoke.amx @@ -0,0 +1,498 @@ + + + + + +80 +true + + +<_x0030_ href="#ref-5"/> +<_x0031_ href="#ref-6"/> +<_x0032_ href="#ref-7"/> +<_x0033_ href="#ref-8"/> +<_x0034_ href="#ref-9"/> +<_x0035_ href="#ref-10"/> +<_x0036_ href="#ref-11"/> +<_x0037_ href="#ref-12"/> +<_x0038_ href="#ref-13"/> +<_x0039_ href="#ref-14"/> +<_x0031_0 href="#ref-15"/> +<_x0031_1 href="#ref-16"/> +<_x0031_2 href="#ref-17"/> +<_x0031_3 href="#ref-18"/> +<_x0031_4 href="#ref-19"/> +<_x0031_5 href="#ref-20"/> +<_x0031_6 href="#ref-21"/> +<_x0031_7 href="#ref-22"/> +<_x0031_8 href="#ref-23"/> +<_x0031_9 href="#ref-24"/> + + +<_x0030_ href="#ref-25"/> +<_x0031_ href="#ref-26"/> +<_x0032_ href="#ref-27"/> +<_x0033_ href="#ref-28"/> + + +smoke\smoke_big_1_4.png + + +smoke\smoke_big_0_0.png + + +smoke\smoke_big_0_1.png + + +smoke\smoke_big_0_2.png + + +smoke\smoke_big_0_3.png + + +smoke\smoke_big_0_4.png + + +smoke\smoke_big_1_0.png + + +smoke\smoke_big_1_1.png + + +smoke\smoke_big_1_2.png + + +smoke\smoke_big_1_3.png + + +smoke\smoke_small_1_4.png + + +smoke\smoke_small_0_0.png + + +smoke\smoke_small_0_1.png + + +smoke\smoke_small_0_2.png + + +smoke\smoke_small_0_3.png + + +smoke\smoke_small_0_4.png + + +smoke\smoke_small_1_0.png + + +smoke\smoke_small_1_1.png + + +smoke\smoke_small_1_2.png + + +smoke\smoke_small_1_3.png + + +big 0 +1 +5 +<_x0030_ href="#ref-50"/> +<_x0031_ href="#ref-51"/> +<_x0032_ href="#ref-52"/> +<_x0033_ href="#ref-53"/> +<_x0034_ href="#ref-54"/> + + +big 1 +1 +5 +<_x0030_ href="#ref-56"/> +<_x0031_ href="#ref-57"/> +<_x0032_ href="#ref-58"/> +<_x0033_ href="#ref-59"/> +<_x0034_ href="#ref-60"/> + + +small 0 +1 +5 +<_x0030_ href="#ref-62"/> +<_x0031_ href="#ref-63"/> +<_x0032_ href="#ref-64"/> +<_x0033_ href="#ref-65"/> +<_x0034_ href="#ref-66"/> + + +small 1 +1 +5 +<_x0030_ href="#ref-68"/> +<_x0031_ href="#ref-69"/> +<_x0032_ href="#ref-70"/> +<_x0033_ href="#ref-71"/> +<_x0034_ href="#ref-72"/> + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + +<_x0030_ href="#ref-94"/> + + +<_x0030_ href="#ref-95"/> + + +<_x0030_ href="#ref-96"/> + + +<_x0030_ href="#ref-97"/> + + +<_x0030_ href="#ref-98"/> + + +<_x0030_ href="#ref-99"/> + + +<_x0030_ href="#ref-100"/> + + +<_x0030_ href="#ref-101"/> + + +<_x0030_ href="#ref-102"/> + + +<_x0030_ href="#ref-103"/> + + +<_x0030_ href="#ref-104"/> + + +<_x0030_ href="#ref-105"/> + + +<_x0030_ href="#ref-106"/> + + +<_x0030_ href="#ref-107"/> + + +<_x0030_ href="#ref-108"/> + + +<_x0030_ href="#ref-109"/> + + +<_x0030_ href="#ref-110"/> + + +<_x0030_ href="#ref-111"/> + + +<_x0030_ href="#ref-112"/> + + +<_x0030_ href="#ref-113"/> + + + +12 +46 + + + + + +12 +46 + + + + + +12 +46 + + + + + +12 +46 + + + + + +12 +46 + + + + + +11 +46 + + + + + +11 +46 + + + + + +11 +46 + + + + + +11 +46 + + + + + +11 +46 + + + + + +5 +23 + + + + + +5 +23 + + + + + +5 +23 + + + + + +5 +23 + + + + + +5 +23 + + + + + +5 +23 + + + + + +6 +23 + + + + + +6 +23 + + + + + +6 +23 + + + + + +6 +23 + + + + + diff --git a/data/art824/smoke/smoke_big_0_0.png b/data/art824/smoke/smoke_big_0_0.png new file mode 100644 index 0000000..012d5a6 Binary files /dev/null and b/data/art824/smoke/smoke_big_0_0.png differ diff --git a/data/art824/smoke/smoke_big_0_1.png b/data/art824/smoke/smoke_big_0_1.png new file mode 100644 index 0000000..7f235e1 Binary files /dev/null and b/data/art824/smoke/smoke_big_0_1.png differ diff --git a/data/art824/smoke/smoke_big_0_2.png b/data/art824/smoke/smoke_big_0_2.png new file mode 100644 index 0000000..c118aeb Binary files /dev/null and b/data/art824/smoke/smoke_big_0_2.png differ diff --git a/data/art824/smoke/smoke_big_0_3.png b/data/art824/smoke/smoke_big_0_3.png new file mode 100644 index 0000000..637f16d Binary files /dev/null and b/data/art824/smoke/smoke_big_0_3.png differ diff --git a/data/art824/smoke/smoke_big_0_4.png b/data/art824/smoke/smoke_big_0_4.png new file mode 100644 index 0000000..d15f662 Binary files /dev/null and b/data/art824/smoke/smoke_big_0_4.png differ diff --git a/data/art824/smoke/smoke_big_1_0.png b/data/art824/smoke/smoke_big_1_0.png new file mode 100644 index 0000000..469ab0f Binary files /dev/null and b/data/art824/smoke/smoke_big_1_0.png differ diff --git a/data/art824/smoke/smoke_big_1_1.png b/data/art824/smoke/smoke_big_1_1.png new file mode 100644 index 0000000..8fcfd55 Binary files /dev/null and b/data/art824/smoke/smoke_big_1_1.png differ diff --git a/data/art824/smoke/smoke_big_1_2.png b/data/art824/smoke/smoke_big_1_2.png new file mode 100644 index 0000000..192d6a5 Binary files /dev/null and b/data/art824/smoke/smoke_big_1_2.png differ diff --git a/data/art824/smoke/smoke_big_1_3.png b/data/art824/smoke/smoke_big_1_3.png new file mode 100644 index 0000000..32f81d6 Binary files /dev/null and b/data/art824/smoke/smoke_big_1_3.png differ diff --git a/data/art824/smoke/smoke_big_1_4.png b/data/art824/smoke/smoke_big_1_4.png new file mode 100644 index 0000000..5eae011 Binary files /dev/null and b/data/art824/smoke/smoke_big_1_4.png differ diff --git a/data/art824/smoke/smoke_small_0_0.png b/data/art824/smoke/smoke_small_0_0.png new file mode 100644 index 0000000..b5b99ef Binary files /dev/null and b/data/art824/smoke/smoke_small_0_0.png differ diff --git a/data/art824/smoke/smoke_small_0_1.png b/data/art824/smoke/smoke_small_0_1.png new file mode 100644 index 0000000..174495f Binary files /dev/null and b/data/art824/smoke/smoke_small_0_1.png differ diff --git a/data/art824/smoke/smoke_small_0_2.png b/data/art824/smoke/smoke_small_0_2.png new file mode 100644 index 0000000..3e5fc1f Binary files /dev/null and b/data/art824/smoke/smoke_small_0_2.png differ diff --git a/data/art824/smoke/smoke_small_0_3.png b/data/art824/smoke/smoke_small_0_3.png new file mode 100644 index 0000000..5d6b42f Binary files /dev/null and b/data/art824/smoke/smoke_small_0_3.png differ diff --git a/data/art824/smoke/smoke_small_0_4.png b/data/art824/smoke/smoke_small_0_4.png new file mode 100644 index 0000000..4ac3b93 Binary files /dev/null and b/data/art824/smoke/smoke_small_0_4.png differ diff --git a/data/art824/smoke/smoke_small_1_0.png b/data/art824/smoke/smoke_small_1_0.png new file mode 100644 index 0000000..ed60ddf Binary files /dev/null and b/data/art824/smoke/smoke_small_1_0.png differ diff --git a/data/art824/smoke/smoke_small_1_1.png b/data/art824/smoke/smoke_small_1_1.png new file mode 100644 index 0000000..8586f4e Binary files /dev/null and b/data/art824/smoke/smoke_small_1_1.png differ diff --git a/data/art824/smoke/smoke_small_1_2.png b/data/art824/smoke/smoke_small_1_2.png new file mode 100644 index 0000000..1bb3d36 Binary files /dev/null and b/data/art824/smoke/smoke_small_1_2.png differ diff --git a/data/art824/smoke/smoke_small_1_3.png b/data/art824/smoke/smoke_small_1_3.png new file mode 100644 index 0000000..2fe3a27 Binary files /dev/null and b/data/art824/smoke/smoke_small_1_3.png differ diff --git a/data/art824/smoke/smoke_small_1_4.png b/data/art824/smoke/smoke_small_1_4.png new file mode 100644 index 0000000..d9252d1 Binary files /dev/null and b/data/art824/smoke/smoke_small_1_4.png differ diff --git a/data/art824/spi.amx b/data/art824/spi.amx new file mode 100644 index 0000000..7052540 --- /dev/null +++ b/data/art824/spi.amx @@ -0,0 +1,2399 @@ + + + + + +80 +true + + +<_x0030_ href="#ref-5"/> +<_x0031_ href="#ref-6"/> +<_x0032_ href="#ref-7"/> +<_x0033_ href="#ref-8"/> +<_x0034_ href="#ref-9"/> +<_x0035_ href="#ref-10"/> +<_x0036_ href="#ref-11"/> +<_x0037_ href="#ref-12"/> +<_x0038_ href="#ref-13"/> +<_x0039_ href="#ref-14"/> +<_x0031_0 href="#ref-15"/> +<_x0031_1 href="#ref-16"/> +<_x0031_2 href="#ref-17"/> +<_x0031_3 href="#ref-18"/> +<_x0031_4 href="#ref-19"/> +<_x0031_5 href="#ref-20"/> +<_x0031_6 href="#ref-21"/> +<_x0031_7 href="#ref-22"/> +<_x0031_8 href="#ref-23"/> +<_x0031_9 href="#ref-24"/> +<_x0032_0 href="#ref-25"/> +<_x0032_1 href="#ref-26"/> +<_x0032_2 href="#ref-27"/> +<_x0032_3 href="#ref-28"/> +<_x0032_4 href="#ref-29"/> +<_x0032_5 href="#ref-30"/> +<_x0032_6 href="#ref-31"/> +<_x0032_7 href="#ref-32"/> +<_x0032_8 href="#ref-33"/> +<_x0032_9 href="#ref-34"/> +<_x0033_0 href="#ref-35"/> +<_x0033_1 href="#ref-36"/> +<_x0033_2 href="#ref-37"/> +<_x0033_3 href="#ref-38"/> +<_x0033_4 href="#ref-39"/> +<_x0033_5 href="#ref-40"/> +<_x0033_6 href="#ref-41"/> +<_x0033_7 href="#ref-42"/> +<_x0033_8 href="#ref-43"/> +<_x0033_9 href="#ref-44"/> +<_x0034_0 href="#ref-45"/> +<_x0034_1 href="#ref-46"/> +<_x0034_2 href="#ref-47"/> +<_x0034_3 href="#ref-48"/> +<_x0034_4 href="#ref-49"/> +<_x0034_5 href="#ref-50"/> +<_x0034_6 href="#ref-51"/> +<_x0034_7 href="#ref-52"/> +<_x0034_8 href="#ref-53"/> +<_x0034_9 href="#ref-54"/> +<_x0035_0 href="#ref-55"/> +<_x0035_1 href="#ref-56"/> +<_x0035_2 href="#ref-57"/> +<_x0035_3 href="#ref-58"/> +<_x0035_4 href="#ref-59"/> +<_x0035_5 href="#ref-60"/> +<_x0035_6 href="#ref-61"/> +<_x0035_7 href="#ref-62"/> +<_x0035_8 href="#ref-63"/> +<_x0035_9 href="#ref-64"/> +<_x0036_0 href="#ref-65"/> +<_x0036_1 href="#ref-66"/> +<_x0036_2 href="#ref-67"/> +<_x0036_3 href="#ref-68"/> +<_x0036_4 href="#ref-69"/> +<_x0036_5 href="#ref-70"/> +<_x0036_6 href="#ref-71"/> +<_x0036_7 href="#ref-72"/> +<_x0036_8 href="#ref-73"/> +<_x0036_9 href="#ref-74"/> +<_x0037_0 href="#ref-75"/> +<_x0037_1 href="#ref-76"/> +<_x0037_2 href="#ref-77"/> +<_x0037_3 href="#ref-78"/> +<_x0037_4 href="#ref-79"/> +<_x0037_5 href="#ref-80"/> +<_x0037_6 href="#ref-81"/> +<_x0037_7 href="#ref-82"/> +<_x0037_8 href="#ref-83"/> +<_x0037_9 href="#ref-84"/> +<_x0038_0 href="#ref-85"/> +<_x0038_1 href="#ref-86"/> +<_x0038_2 href="#ref-87"/> +<_x0038_3 href="#ref-88"/> +<_x0038_4 href="#ref-89"/> +<_x0038_5 href="#ref-90"/> +<_x0038_6 href="#ref-91"/> +<_x0038_7 href="#ref-92"/> + + +<_x0030_ href="#ref-93"/> +<_x0031_ href="#ref-94"/> +<_x0032_ href="#ref-95"/> +<_x0033_ href="#ref-96"/> +<_x0034_ href="#ref-97"/> +<_x0035_ href="#ref-98"/> +<_x0036_ href="#ref-99"/> +<_x0037_ href="#ref-100"/> +<_x0038_ href="#ref-101"/> +<_x0039_ href="#ref-102"/> +<_x0031_0 href="#ref-103"/> +<_x0031_1 href="#ref-104"/> +<_x0031_2 href="#ref-105"/> +<_x0031_3 href="#ref-106"/> +<_x0031_4 href="#ref-107"/> +<_x0031_5 href="#ref-108"/> +<_x0031_6 href="#ref-109"/> +<_x0031_7 href="#ref-110"/> +<_x0031_8 href="#ref-111"/> + + +spi\spi_jog_0_1.png + + +spi\spi_jog_0_2.png + + +spi\spi_jog_0_3.png + + +spi\spi_jog_0_4.png + + +spi\spi_jog_0_0.png + + +spi\spi_jog_7_5.png + + +spi\spi_jog_1_1.png + + +spi\spi_jog_1_2.png + + +spi\spi_jog_1_3.png + + +spi\spi_jog_1_4.png + + +spi\spi_jog_1_5.png + + +spi\spi_jog_2_0.png + + +spi\spi_jog_2_1.png + + +spi\spi_jog_2_2.png + + +spi\spi_jog_2_3.png + + +spi\spi_jog_2_4.png + + +spi\spi_jog_2_5.png + + +spi\spi_jog_3_0.png + + +spi\spi_jog_3_1.png + + +spi\spi_jog_3_2.png + + +spi\spi_jog_3_3.png + + +spi\spi_jog_3_4.png + + +spi\spi_jog_3_5.png + + +spi\spi_jog_4_0.png + + +spi\spi_jog_4_1.png + + +spi\spi_jog_4_2.png + + +spi\spi_jog_4_3.png + + +spi\spi_jog_4_4.png + + +spi\spi_jog_4_5.png + + +spi\spi_jog_5_0.png + + +spi\spi_jog_5_1.png + + +spi\spi_jog_5_2.png + + +spi\spi_jog_5_3.png + + +spi\spi_jog_5_4.png + + +spi\spi_jog_5_5.png + + +spi\spi_jog_6_0.png + + +spi\spi_jog_6_1.png + + +spi\spi_jog_6_2.png + + +spi\spi_jog_6_3.png + + +spi\spi_jog_6_4.png + + +spi\spi_jog_6_5.png + + +spi\spi_jog_7_0.png + + +spi\spi_jog_7_1.png + + +spi\spi_jog_7_2.png + + +spi\spi_jog_7_3.png + + +spi\spi_jog_7_4.png + + +spi\spi_jog_1_0.png + + +spi\spi_die_3_8.png + + +spi\spi_die_3_1.png + + +spi\spi_die_3_2.png + + +spi\spi_die_3_3.png + + +spi\spi_die_3_4.png + + +spi\spi_die_3_5.png + + +spi\spi_die_3_6.png + + +spi\spi_die_3_7.png + + +spi\spi_die_3_0.png + + +spi\spi_idle_7_5.png + + +spi\spi_idle_0_1.png + + +spi\spi_idle_0_2.png + + +spi\spi_idle_0_3.png + + +spi\spi_idle_0_4.png + + +spi\spi_idle_1_0.png + + +spi\spi_idle_1_1.png + + +spi\spi_idle_1_2.png + + +spi\spi_idle_2_0.png + + +spi\spi_idle_2_1.png + + +spi\spi_idle_2_2.png + + +spi\spi_idle_2_3.png + + +spi\spi_idle_3_0.png + + +spi\spi_idle_3_1.png + + +spi\spi_idle_3_2.png + + +spi\spi_idle_4_0.png + + +spi\spi_idle_4_1.png + + +spi\spi_idle_4_2.png + + +spi\spi_idle_5_0.png + + +spi\spi_idle_5_1.png + + +spi\spi_idle_5_2.png + + +spi\spi_idle_5_3.png + + +spi\spi_idle_6_0.png + + +spi\spi_idle_6_1.png + + +spi\spi_idle_6_2.png + + +spi\spi_idle_6_3.png + + +spi\spi_idle_7_0.png + + +spi\spi_idle_7_1.png + + +spi\spi_idle_7_2.png + + +spi\spi_idle_7_3.png + + +spi\spi_idle_7_4.png + + +spi\spi_idle_0_0.png + + +die 3 +0 +9 +<_x0030_ href="#ref-201"/> +<_x0031_ href="#ref-202"/> +<_x0032_ href="#ref-203"/> +<_x0033_ href="#ref-204"/> +<_x0034_ href="#ref-205"/> +<_x0035_ href="#ref-206"/> +<_x0036_ href="#ref-207"/> +<_x0037_ href="#ref-208"/> +<_x0038_ href="#ref-209"/> + + +idle 0 +0 +8 +<_x0030_ href="#ref-211"/> +<_x0031_ href="#ref-212"/> +<_x0032_ href="#ref-213"/> +<_x0033_ href="#ref-214"/> +<_x0034_ href="#ref-215"/> +<_x0035_ href="#ref-216"/> +<_x0036_ href="#ref-217"/> +<_x0037_ href="#ref-218"/> + + +idle 1 +0 +4 +<_x0030_ href="#ref-220"/> +<_x0031_ href="#ref-221"/> +<_x0032_ href="#ref-222"/> +<_x0033_ href="#ref-223"/> + + +idle 2 +0 +5 +<_x0030_ href="#ref-225"/> +<_x0031_ href="#ref-226"/> +<_x0032_ href="#ref-227"/> +<_x0033_ href="#ref-228"/> +<_x0034_ href="#ref-229"/> + + +idle 3 +0 +4 +<_x0030_ href="#ref-231"/> +<_x0031_ href="#ref-232"/> +<_x0032_ href="#ref-233"/> +<_x0033_ href="#ref-234"/> + + +idle 4 +0 +4 +<_x0030_ href="#ref-236"/> +<_x0031_ href="#ref-237"/> +<_x0032_ href="#ref-238"/> +<_x0033_ href="#ref-239"/> + + +idle 5 +0 +6 +<_x0030_ href="#ref-241"/> +<_x0031_ href="#ref-242"/> +<_x0032_ href="#ref-243"/> +<_x0033_ href="#ref-244"/> +<_x0034_ href="#ref-245"/> +<_x0035_ href="#ref-246"/> + + +idle 6 +0 +6 +<_x0030_ href="#ref-248"/> +<_x0031_ href="#ref-249"/> +<_x0032_ href="#ref-250"/> +<_x0033_ href="#ref-251"/> +<_x0034_ href="#ref-252"/> +<_x0035_ href="#ref-253"/> + + +idle 7 +0 +6 +<_x0030_ href="#ref-255"/> +<_x0031_ href="#ref-256"/> +<_x0032_ href="#ref-257"/> +<_x0033_ href="#ref-258"/> +<_x0034_ href="#ref-259"/> +<_x0035_ href="#ref-260"/> + + +jog 0 +0 +5 +<_x0030_ href="#ref-262"/> +<_x0031_ href="#ref-263"/> +<_x0032_ href="#ref-264"/> +<_x0033_ href="#ref-265"/> +<_x0034_ href="#ref-266"/> + + +jog 1 +0 +6 +<_x0030_ href="#ref-268"/> +<_x0031_ href="#ref-269"/> +<_x0032_ href="#ref-270"/> +<_x0033_ href="#ref-271"/> +<_x0034_ href="#ref-272"/> +<_x0035_ href="#ref-273"/> + + +jog 2 +0 +6 +<_x0030_ href="#ref-275"/> +<_x0031_ href="#ref-276"/> +<_x0032_ href="#ref-277"/> +<_x0033_ href="#ref-278"/> +<_x0034_ href="#ref-279"/> +<_x0035_ href="#ref-280"/> + + +jog 3 +0 +6 +<_x0030_ href="#ref-282"/> +<_x0031_ href="#ref-283"/> +<_x0032_ href="#ref-284"/> +<_x0033_ href="#ref-285"/> +<_x0034_ href="#ref-286"/> +<_x0035_ href="#ref-287"/> + + +jog 4 +0 +6 +<_x0030_ href="#ref-289"/> +<_x0031_ href="#ref-290"/> +<_x0032_ href="#ref-291"/> +<_x0033_ href="#ref-292"/> +<_x0034_ href="#ref-293"/> +<_x0035_ href="#ref-294"/> + + +jog 5 +0 +6 +<_x0030_ href="#ref-296"/> +<_x0031_ href="#ref-297"/> +<_x0032_ href="#ref-298"/> +<_x0033_ href="#ref-299"/> +<_x0034_ href="#ref-300"/> +<_x0035_ href="#ref-301"/> + + +jog 6 +0 +6 +<_x0030_ href="#ref-303"/> +<_x0031_ href="#ref-304"/> +<_x0032_ href="#ref-305"/> +<_x0033_ href="#ref-306"/> +<_x0034_ href="#ref-307"/> +<_x0035_ href="#ref-308"/> + + +jog 7 +0 +6 +<_x0030_ href="#ref-310"/> +<_x0031_ href="#ref-311"/> +<_x0032_ href="#ref-312"/> +<_x0033_ href="#ref-313"/> +<_x0034_ href="#ref-314"/> +<_x0035_ href="#ref-315"/> + + +icon +0 +1 +<_x0030_ href="#ref-317"/> + + +help +0 +1 +<_x0030_ href="#ref-319"/> + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +15 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +20 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +15 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +10 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +20 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +20 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + +<_x0030_ href="#ref-422"/> + + +<_x0030_ href="#ref-423"/> + + +<_x0030_ href="#ref-424"/> + + +<_x0030_ href="#ref-425"/> + + +<_x0030_ href="#ref-426"/> + + +<_x0030_ href="#ref-427"/> + + +<_x0030_ href="#ref-428"/> + + +<_x0030_ href="#ref-429"/> + + +<_x0030_ href="#ref-430"/> + + +<_x0030_ href="#ref-431"/> + + +<_x0030_ href="#ref-432"/> + + +<_x0030_ href="#ref-433"/> + + +<_x0030_ href="#ref-434"/> + + +<_x0030_ href="#ref-435"/> + + +<_x0030_ href="#ref-436"/> + + +<_x0030_ href="#ref-437"/> + + +<_x0030_ href="#ref-438"/> + + +<_x0030_ href="#ref-439"/> + + +<_x0030_ href="#ref-440"/> + + +<_x0030_ href="#ref-441"/> + + +<_x0030_ href="#ref-442"/> + + +<_x0030_ href="#ref-443"/> + + +<_x0030_ href="#ref-444"/> + + +<_x0030_ href="#ref-445"/> + + +<_x0030_ href="#ref-446"/> + + +<_x0030_ href="#ref-447"/> + + +<_x0030_ href="#ref-448"/> + + +<_x0030_ href="#ref-449"/> + + +<_x0030_ href="#ref-450"/> + + +<_x0030_ href="#ref-451"/> + + +<_x0030_ href="#ref-452"/> + + +<_x0030_ href="#ref-453"/> + + +<_x0030_ href="#ref-454"/> + + +<_x0030_ href="#ref-455"/> + + +<_x0030_ href="#ref-456"/> + + +<_x0030_ href="#ref-457"/> + + +<_x0030_ href="#ref-458"/> + + +<_x0030_ href="#ref-459"/> + + +<_x0030_ href="#ref-460"/> + + +<_x0030_ href="#ref-461"/> + + +<_x0030_ href="#ref-462"/> + + +<_x0030_ href="#ref-463"/> + + +<_x0030_ href="#ref-464"/> + + +<_x0030_ href="#ref-465"/> + + +<_x0030_ href="#ref-466"/> + + +<_x0030_ href="#ref-467"/> + + +<_x0030_ href="#ref-468"/> + + +<_x0030_ href="#ref-469"/> + + +<_x0030_ href="#ref-470"/> + + +<_x0030_ href="#ref-471"/> + + +<_x0030_ href="#ref-472"/> + + +<_x0030_ href="#ref-473"/> + + +<_x0030_ href="#ref-474"/> + + +<_x0030_ href="#ref-475"/> + + +<_x0030_ href="#ref-476"/> + + +<_x0030_ href="#ref-477"/> + + +<_x0030_ href="#ref-478"/> + + +<_x0030_ href="#ref-479"/> + + +<_x0030_ href="#ref-480"/> + + +<_x0030_ href="#ref-481"/> + + +<_x0030_ href="#ref-482"/> + + +<_x0030_ href="#ref-483"/> + + +<_x0030_ href="#ref-484"/> + + +<_x0030_ href="#ref-485"/> + + +<_x0030_ href="#ref-486"/> + + +<_x0030_ href="#ref-487"/> + + +<_x0030_ href="#ref-488"/> + + +<_x0030_ href="#ref-489"/> + + +<_x0030_ href="#ref-490"/> + + +<_x0030_ href="#ref-491"/> + + +<_x0030_ href="#ref-492"/> + + +<_x0030_ href="#ref-493"/> + + +<_x0030_ href="#ref-494"/> + + +<_x0030_ href="#ref-495"/> + + +<_x0030_ href="#ref-496"/> + + +<_x0030_ href="#ref-497"/> + + +<_x0030_ href="#ref-498"/> + + +<_x0030_ href="#ref-499"/> + + +<_x0030_ href="#ref-500"/> + + +<_x0030_ href="#ref-501"/> + + +<_x0030_ href="#ref-502"/> + + +<_x0030_ href="#ref-503"/> + + +<_x0030_ href="#ref-504"/> + + +<_x0030_ href="#ref-505"/> + + +<_x0030_ href="#ref-506"/> + + +<_x0030_ href="#ref-507"/> + + +<_x0030_ href="#ref-508"/> + + +<_x0030_ href="#ref-509"/> + + +<_x0030_ href="#ref-510"/> + + +<_x0030_ href="#ref-511"/> + + +<_x0030_ href="#ref-512"/> + + +<_x0030_ href="#ref-513"/> + + +<_x0030_ href="#ref-514"/> + + +<_x0030_ href="#ref-515"/> + + +<_x0030_ href="#ref-516"/> + + +<_x0030_ href="#ref-517"/> + + +<_x0030_ href="#ref-518"/> + + +<_x0030_ href="#ref-519"/> + + +<_x0030_ href="#ref-520"/> + + +<_x0030_ href="#ref-521"/> + + +<_x0030_ href="#ref-522"/> + + + +14 +12 + + + + + +14 +14 + + + + + +15 +15 + + + + + +18 +17 + + + + + +19 +17 + + + + + +21 +16 + + + + + +23 +17 + + + + + +23 +17 + + + + + +24 +17 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +6 +9 + + + + + +6 +9 + + + + + +6 +9 + + + + + +6 +9 + + + + + +9 +9 + + + + + +10 +9 + + + + + +11 +9 + + + + + +11 +9 + + + + + +10 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +8 +9 + + + + + +9 +9 + + + + + +10 +9 + + + + + +9 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +4 +0 + + + + + +8 +9 + + + + + diff --git a/data/art824/spi/spi_die_3_0.png b/data/art824/spi/spi_die_3_0.png new file mode 100644 index 0000000..2ebc2a7 Binary files /dev/null and b/data/art824/spi/spi_die_3_0.png differ diff --git a/data/art824/spi/spi_die_3_1.png b/data/art824/spi/spi_die_3_1.png new file mode 100644 index 0000000..99d1d8d Binary files /dev/null and b/data/art824/spi/spi_die_3_1.png differ diff --git a/data/art824/spi/spi_die_3_2.png b/data/art824/spi/spi_die_3_2.png new file mode 100644 index 0000000..2614708 Binary files /dev/null and b/data/art824/spi/spi_die_3_2.png differ diff --git a/data/art824/spi/spi_die_3_3.png b/data/art824/spi/spi_die_3_3.png new file mode 100644 index 0000000..33736cc Binary files /dev/null and b/data/art824/spi/spi_die_3_3.png differ diff --git a/data/art824/spi/spi_die_3_4.png b/data/art824/spi/spi_die_3_4.png new file mode 100644 index 0000000..6367340 Binary files /dev/null and b/data/art824/spi/spi_die_3_4.png differ diff --git a/data/art824/spi/spi_die_3_5.png b/data/art824/spi/spi_die_3_5.png new file mode 100644 index 0000000..d4b3c87 Binary files /dev/null and b/data/art824/spi/spi_die_3_5.png differ diff --git a/data/art824/spi/spi_die_3_6.png b/data/art824/spi/spi_die_3_6.png new file mode 100644 index 0000000..d74ad0a Binary files /dev/null and b/data/art824/spi/spi_die_3_6.png differ diff --git a/data/art824/spi/spi_die_3_7.png b/data/art824/spi/spi_die_3_7.png new file mode 100644 index 0000000..2e81d9e Binary files /dev/null and b/data/art824/spi/spi_die_3_7.png differ diff --git a/data/art824/spi/spi_die_3_8.png b/data/art824/spi/spi_die_3_8.png new file mode 100644 index 0000000..64e6aa1 Binary files /dev/null and b/data/art824/spi/spi_die_3_8.png differ diff --git a/data/art824/spi/spi_idle_0_0.png b/data/art824/spi/spi_idle_0_0.png new file mode 100644 index 0000000..321cb06 Binary files /dev/null and b/data/art824/spi/spi_idle_0_0.png differ diff --git a/data/art824/spi/spi_idle_0_1.png b/data/art824/spi/spi_idle_0_1.png new file mode 100644 index 0000000..e67b404 Binary files /dev/null and b/data/art824/spi/spi_idle_0_1.png differ diff --git a/data/art824/spi/spi_idle_0_2.png b/data/art824/spi/spi_idle_0_2.png new file mode 100644 index 0000000..d03f2de Binary files /dev/null and b/data/art824/spi/spi_idle_0_2.png differ diff --git a/data/art824/spi/spi_idle_0_3.png b/data/art824/spi/spi_idle_0_3.png new file mode 100644 index 0000000..76f8d91 Binary files /dev/null and b/data/art824/spi/spi_idle_0_3.png differ diff --git a/data/art824/spi/spi_idle_0_4.png b/data/art824/spi/spi_idle_0_4.png new file mode 100644 index 0000000..12c8630 Binary files /dev/null and b/data/art824/spi/spi_idle_0_4.png differ diff --git a/data/art824/spi/spi_idle_1_0.png b/data/art824/spi/spi_idle_1_0.png new file mode 100644 index 0000000..3def860 Binary files /dev/null and b/data/art824/spi/spi_idle_1_0.png differ diff --git a/data/art824/spi/spi_idle_1_1.png b/data/art824/spi/spi_idle_1_1.png new file mode 100644 index 0000000..749c6b2 Binary files /dev/null and b/data/art824/spi/spi_idle_1_1.png differ diff --git a/data/art824/spi/spi_idle_1_2.png b/data/art824/spi/spi_idle_1_2.png new file mode 100644 index 0000000..3f2f201 Binary files /dev/null and b/data/art824/spi/spi_idle_1_2.png differ diff --git a/data/art824/spi/spi_idle_2_0.png b/data/art824/spi/spi_idle_2_0.png new file mode 100644 index 0000000..e29bfe9 Binary files /dev/null and b/data/art824/spi/spi_idle_2_0.png differ diff --git a/data/art824/spi/spi_idle_2_1.png b/data/art824/spi/spi_idle_2_1.png new file mode 100644 index 0000000..601a6bb Binary files /dev/null and b/data/art824/spi/spi_idle_2_1.png differ diff --git a/data/art824/spi/spi_idle_2_2.png b/data/art824/spi/spi_idle_2_2.png new file mode 100644 index 0000000..e39406a Binary files /dev/null and b/data/art824/spi/spi_idle_2_2.png differ diff --git a/data/art824/spi/spi_idle_2_3.png b/data/art824/spi/spi_idle_2_3.png new file mode 100644 index 0000000..b9b94de Binary files /dev/null and b/data/art824/spi/spi_idle_2_3.png differ diff --git a/data/art824/spi/spi_idle_3_0.png b/data/art824/spi/spi_idle_3_0.png new file mode 100644 index 0000000..4705eee Binary files /dev/null and b/data/art824/spi/spi_idle_3_0.png differ diff --git a/data/art824/spi/spi_idle_3_1.png b/data/art824/spi/spi_idle_3_1.png new file mode 100644 index 0000000..25a3e57 Binary files /dev/null and b/data/art824/spi/spi_idle_3_1.png differ diff --git a/data/art824/spi/spi_idle_3_2.png b/data/art824/spi/spi_idle_3_2.png new file mode 100644 index 0000000..70b1fc2 Binary files /dev/null and b/data/art824/spi/spi_idle_3_2.png differ diff --git a/data/art824/spi/spi_idle_4_0.png b/data/art824/spi/spi_idle_4_0.png new file mode 100644 index 0000000..45cda96 Binary files /dev/null and b/data/art824/spi/spi_idle_4_0.png differ diff --git a/data/art824/spi/spi_idle_4_1.png b/data/art824/spi/spi_idle_4_1.png new file mode 100644 index 0000000..6de3329 Binary files /dev/null and b/data/art824/spi/spi_idle_4_1.png differ diff --git a/data/art824/spi/spi_idle_4_2.png b/data/art824/spi/spi_idle_4_2.png new file mode 100644 index 0000000..783af7a Binary files /dev/null and b/data/art824/spi/spi_idle_4_2.png differ diff --git a/data/art824/spi/spi_idle_5_0.png b/data/art824/spi/spi_idle_5_0.png new file mode 100644 index 0000000..5000900 Binary files /dev/null and b/data/art824/spi/spi_idle_5_0.png differ diff --git a/data/art824/spi/spi_idle_5_1.png b/data/art824/spi/spi_idle_5_1.png new file mode 100644 index 0000000..302f9da Binary files /dev/null and b/data/art824/spi/spi_idle_5_1.png differ diff --git a/data/art824/spi/spi_idle_5_2.png b/data/art824/spi/spi_idle_5_2.png new file mode 100644 index 0000000..66fe3f3 Binary files /dev/null and b/data/art824/spi/spi_idle_5_2.png differ diff --git a/data/art824/spi/spi_idle_5_3.png b/data/art824/spi/spi_idle_5_3.png new file mode 100644 index 0000000..29d4961 Binary files /dev/null and b/data/art824/spi/spi_idle_5_3.png differ diff --git a/data/art824/spi/spi_idle_6_0.png b/data/art824/spi/spi_idle_6_0.png new file mode 100644 index 0000000..caee391 Binary files /dev/null and b/data/art824/spi/spi_idle_6_0.png differ diff --git a/data/art824/spi/spi_idle_6_1.png b/data/art824/spi/spi_idle_6_1.png new file mode 100644 index 0000000..0f38b77 Binary files /dev/null and b/data/art824/spi/spi_idle_6_1.png differ diff --git a/data/art824/spi/spi_idle_6_2.png b/data/art824/spi/spi_idle_6_2.png new file mode 100644 index 0000000..f925bcf Binary files /dev/null and b/data/art824/spi/spi_idle_6_2.png differ diff --git a/data/art824/spi/spi_idle_6_3.png b/data/art824/spi/spi_idle_6_3.png new file mode 100644 index 0000000..691ecbc Binary files /dev/null and b/data/art824/spi/spi_idle_6_3.png differ diff --git a/data/art824/spi/spi_idle_7_0.png b/data/art824/spi/spi_idle_7_0.png new file mode 100644 index 0000000..7cb4242 Binary files /dev/null and b/data/art824/spi/spi_idle_7_0.png differ diff --git a/data/art824/spi/spi_idle_7_1.png b/data/art824/spi/spi_idle_7_1.png new file mode 100644 index 0000000..7debfb0 Binary files /dev/null and b/data/art824/spi/spi_idle_7_1.png differ diff --git a/data/art824/spi/spi_idle_7_2.png b/data/art824/spi/spi_idle_7_2.png new file mode 100644 index 0000000..bbfaf1c Binary files /dev/null and b/data/art824/spi/spi_idle_7_2.png differ diff --git a/data/art824/spi/spi_idle_7_3.png b/data/art824/spi/spi_idle_7_3.png new file mode 100644 index 0000000..cbe58b1 Binary files /dev/null and b/data/art824/spi/spi_idle_7_3.png differ diff --git a/data/art824/spi/spi_idle_7_4.png b/data/art824/spi/spi_idle_7_4.png new file mode 100644 index 0000000..51f6733 Binary files /dev/null and b/data/art824/spi/spi_idle_7_4.png differ diff --git a/data/art824/spi/spi_idle_7_5.png b/data/art824/spi/spi_idle_7_5.png new file mode 100644 index 0000000..b212a03 Binary files /dev/null and b/data/art824/spi/spi_idle_7_5.png differ diff --git a/data/art824/spi/spi_jog_0_0.png b/data/art824/spi/spi_jog_0_0.png new file mode 100644 index 0000000..dbc2bb3 Binary files /dev/null and b/data/art824/spi/spi_jog_0_0.png differ diff --git a/data/art824/spi/spi_jog_0_1.png b/data/art824/spi/spi_jog_0_1.png new file mode 100644 index 0000000..08855cd Binary files /dev/null and b/data/art824/spi/spi_jog_0_1.png differ diff --git a/data/art824/spi/spi_jog_0_2.png b/data/art824/spi/spi_jog_0_2.png new file mode 100644 index 0000000..943536d Binary files /dev/null and b/data/art824/spi/spi_jog_0_2.png differ diff --git a/data/art824/spi/spi_jog_0_3.png b/data/art824/spi/spi_jog_0_3.png new file mode 100644 index 0000000..0f6019c Binary files /dev/null and b/data/art824/spi/spi_jog_0_3.png differ diff --git a/data/art824/spi/spi_jog_0_4.png b/data/art824/spi/spi_jog_0_4.png new file mode 100644 index 0000000..b328004 Binary files /dev/null and b/data/art824/spi/spi_jog_0_4.png differ diff --git a/data/art824/spi/spi_jog_1_0.png b/data/art824/spi/spi_jog_1_0.png new file mode 100644 index 0000000..993c342 Binary files /dev/null and b/data/art824/spi/spi_jog_1_0.png differ diff --git a/data/art824/spi/spi_jog_1_1.png b/data/art824/spi/spi_jog_1_1.png new file mode 100644 index 0000000..55c7969 Binary files /dev/null and b/data/art824/spi/spi_jog_1_1.png differ diff --git a/data/art824/spi/spi_jog_1_2.png b/data/art824/spi/spi_jog_1_2.png new file mode 100644 index 0000000..8b48998 Binary files /dev/null and b/data/art824/spi/spi_jog_1_2.png differ diff --git a/data/art824/spi/spi_jog_1_3.png b/data/art824/spi/spi_jog_1_3.png new file mode 100644 index 0000000..0bfda1c Binary files /dev/null and b/data/art824/spi/spi_jog_1_3.png differ diff --git a/data/art824/spi/spi_jog_1_4.png b/data/art824/spi/spi_jog_1_4.png new file mode 100644 index 0000000..3ab27fe Binary files /dev/null and b/data/art824/spi/spi_jog_1_4.png differ diff --git a/data/art824/spi/spi_jog_1_5.png b/data/art824/spi/spi_jog_1_5.png new file mode 100644 index 0000000..5e096ee Binary files /dev/null and b/data/art824/spi/spi_jog_1_5.png differ diff --git a/data/art824/spi/spi_jog_2_0.png b/data/art824/spi/spi_jog_2_0.png new file mode 100644 index 0000000..89b8be8 Binary files /dev/null and b/data/art824/spi/spi_jog_2_0.png differ diff --git a/data/art824/spi/spi_jog_2_1.png b/data/art824/spi/spi_jog_2_1.png new file mode 100644 index 0000000..3a05ac7 Binary files /dev/null and b/data/art824/spi/spi_jog_2_1.png differ diff --git a/data/art824/spi/spi_jog_2_2.png b/data/art824/spi/spi_jog_2_2.png new file mode 100644 index 0000000..6a893fc Binary files /dev/null and b/data/art824/spi/spi_jog_2_2.png differ diff --git a/data/art824/spi/spi_jog_2_3.png b/data/art824/spi/spi_jog_2_3.png new file mode 100644 index 0000000..ba38c98 Binary files /dev/null and b/data/art824/spi/spi_jog_2_3.png differ diff --git a/data/art824/spi/spi_jog_2_4.png b/data/art824/spi/spi_jog_2_4.png new file mode 100644 index 0000000..912ecb6 Binary files /dev/null and b/data/art824/spi/spi_jog_2_4.png differ diff --git a/data/art824/spi/spi_jog_2_5.png b/data/art824/spi/spi_jog_2_5.png new file mode 100644 index 0000000..3b7d96c Binary files /dev/null and b/data/art824/spi/spi_jog_2_5.png differ diff --git a/data/art824/spi/spi_jog_3_0.png b/data/art824/spi/spi_jog_3_0.png new file mode 100644 index 0000000..5fbf874 Binary files /dev/null and b/data/art824/spi/spi_jog_3_0.png differ diff --git a/data/art824/spi/spi_jog_3_1.png b/data/art824/spi/spi_jog_3_1.png new file mode 100644 index 0000000..afed821 Binary files /dev/null and b/data/art824/spi/spi_jog_3_1.png differ diff --git a/data/art824/spi/spi_jog_3_2.png b/data/art824/spi/spi_jog_3_2.png new file mode 100644 index 0000000..ae53f1d Binary files /dev/null and b/data/art824/spi/spi_jog_3_2.png differ diff --git a/data/art824/spi/spi_jog_3_3.png b/data/art824/spi/spi_jog_3_3.png new file mode 100644 index 0000000..c1b7293 Binary files /dev/null and b/data/art824/spi/spi_jog_3_3.png differ diff --git a/data/art824/spi/spi_jog_3_4.png b/data/art824/spi/spi_jog_3_4.png new file mode 100644 index 0000000..f50a646 Binary files /dev/null and b/data/art824/spi/spi_jog_3_4.png differ diff --git a/data/art824/spi/spi_jog_3_5.png b/data/art824/spi/spi_jog_3_5.png new file mode 100644 index 0000000..23b18c0 Binary files /dev/null and b/data/art824/spi/spi_jog_3_5.png differ diff --git a/data/art824/spi/spi_jog_4_0.png b/data/art824/spi/spi_jog_4_0.png new file mode 100644 index 0000000..95ee14b Binary files /dev/null and b/data/art824/spi/spi_jog_4_0.png differ diff --git a/data/art824/spi/spi_jog_4_1.png b/data/art824/spi/spi_jog_4_1.png new file mode 100644 index 0000000..1cf8a56 Binary files /dev/null and b/data/art824/spi/spi_jog_4_1.png differ diff --git a/data/art824/spi/spi_jog_4_2.png b/data/art824/spi/spi_jog_4_2.png new file mode 100644 index 0000000..ba783f1 Binary files /dev/null and b/data/art824/spi/spi_jog_4_2.png differ diff --git a/data/art824/spi/spi_jog_4_3.png b/data/art824/spi/spi_jog_4_3.png new file mode 100644 index 0000000..196c6c3 Binary files /dev/null and b/data/art824/spi/spi_jog_4_3.png differ diff --git a/data/art824/spi/spi_jog_4_4.png b/data/art824/spi/spi_jog_4_4.png new file mode 100644 index 0000000..1cfb0fd Binary files /dev/null and b/data/art824/spi/spi_jog_4_4.png differ diff --git a/data/art824/spi/spi_jog_4_5.png b/data/art824/spi/spi_jog_4_5.png new file mode 100644 index 0000000..536eea3 Binary files /dev/null and b/data/art824/spi/spi_jog_4_5.png differ diff --git a/data/art824/spi/spi_jog_5_0.png b/data/art824/spi/spi_jog_5_0.png new file mode 100644 index 0000000..d2fe314 Binary files /dev/null and b/data/art824/spi/spi_jog_5_0.png differ diff --git a/data/art824/spi/spi_jog_5_1.png b/data/art824/spi/spi_jog_5_1.png new file mode 100644 index 0000000..0129c2f Binary files /dev/null and b/data/art824/spi/spi_jog_5_1.png differ diff --git a/data/art824/spi/spi_jog_5_2.png b/data/art824/spi/spi_jog_5_2.png new file mode 100644 index 0000000..e8b1abb Binary files /dev/null and b/data/art824/spi/spi_jog_5_2.png differ diff --git a/data/art824/spi/spi_jog_5_3.png b/data/art824/spi/spi_jog_5_3.png new file mode 100644 index 0000000..d1557d9 Binary files /dev/null and b/data/art824/spi/spi_jog_5_3.png differ diff --git a/data/art824/spi/spi_jog_5_4.png b/data/art824/spi/spi_jog_5_4.png new file mode 100644 index 0000000..ccd7c26 Binary files /dev/null and b/data/art824/spi/spi_jog_5_4.png differ diff --git a/data/art824/spi/spi_jog_5_5.png b/data/art824/spi/spi_jog_5_5.png new file mode 100644 index 0000000..c8a8eb7 Binary files /dev/null and b/data/art824/spi/spi_jog_5_5.png differ diff --git a/data/art824/spi/spi_jog_6_0.png b/data/art824/spi/spi_jog_6_0.png new file mode 100644 index 0000000..7c0652a Binary files /dev/null and b/data/art824/spi/spi_jog_6_0.png differ diff --git a/data/art824/spi/spi_jog_6_1.png b/data/art824/spi/spi_jog_6_1.png new file mode 100644 index 0000000..b58e43c Binary files /dev/null and b/data/art824/spi/spi_jog_6_1.png differ diff --git a/data/art824/spi/spi_jog_6_2.png b/data/art824/spi/spi_jog_6_2.png new file mode 100644 index 0000000..fe32431 Binary files /dev/null and b/data/art824/spi/spi_jog_6_2.png differ diff --git a/data/art824/spi/spi_jog_6_3.png b/data/art824/spi/spi_jog_6_3.png new file mode 100644 index 0000000..5091a9e Binary files /dev/null and b/data/art824/spi/spi_jog_6_3.png differ diff --git a/data/art824/spi/spi_jog_6_4.png b/data/art824/spi/spi_jog_6_4.png new file mode 100644 index 0000000..1a796b9 Binary files /dev/null and b/data/art824/spi/spi_jog_6_4.png differ diff --git a/data/art824/spi/spi_jog_6_5.png b/data/art824/spi/spi_jog_6_5.png new file mode 100644 index 0000000..b33d396 Binary files /dev/null and b/data/art824/spi/spi_jog_6_5.png differ diff --git a/data/art824/spi/spi_jog_7_0.png b/data/art824/spi/spi_jog_7_0.png new file mode 100644 index 0000000..2d13e4f Binary files /dev/null and b/data/art824/spi/spi_jog_7_0.png differ diff --git a/data/art824/spi/spi_jog_7_1.png b/data/art824/spi/spi_jog_7_1.png new file mode 100644 index 0000000..62e4fde Binary files /dev/null and b/data/art824/spi/spi_jog_7_1.png differ diff --git a/data/art824/spi/spi_jog_7_2.png b/data/art824/spi/spi_jog_7_2.png new file mode 100644 index 0000000..dfb23e4 Binary files /dev/null and b/data/art824/spi/spi_jog_7_2.png differ diff --git a/data/art824/spi/spi_jog_7_3.png b/data/art824/spi/spi_jog_7_3.png new file mode 100644 index 0000000..7b6615a Binary files /dev/null and b/data/art824/spi/spi_jog_7_3.png differ diff --git a/data/art824/spi/spi_jog_7_4.png b/data/art824/spi/spi_jog_7_4.png new file mode 100644 index 0000000..eddc3d5 Binary files /dev/null and b/data/art824/spi/spi_jog_7_4.png differ diff --git a/data/art824/spi/spi_jog_7_5.png b/data/art824/spi/spi_jog_7_5.png new file mode 100644 index 0000000..d7b1d5b Binary files /dev/null and b/data/art824/spi/spi_jog_7_5.png differ diff --git a/data/art824/sri.amx b/data/art824/sri.amx new file mode 100644 index 0000000..2eaba13 --- /dev/null +++ b/data/art824/sri.amx @@ -0,0 +1,3207 @@ + + + + + +80 +true + + +<_x0030_ href="#ref-5"/> +<_x0031_ href="#ref-6"/> +<_x0032_ href="#ref-7"/> +<_x0033_ href="#ref-8"/> +<_x0034_ href="#ref-9"/> +<_x0035_ href="#ref-10"/> +<_x0036_ href="#ref-11"/> +<_x0037_ href="#ref-12"/> +<_x0038_ href="#ref-13"/> +<_x0039_ href="#ref-14"/> +<_x0031_0 href="#ref-15"/> +<_x0031_1 href="#ref-16"/> +<_x0031_2 href="#ref-17"/> +<_x0031_3 href="#ref-18"/> +<_x0031_4 href="#ref-19"/> +<_x0031_5 href="#ref-20"/> +<_x0031_6 href="#ref-21"/> +<_x0031_7 href="#ref-22"/> +<_x0031_8 href="#ref-23"/> +<_x0031_9 href="#ref-24"/> +<_x0032_0 href="#ref-25"/> +<_x0032_1 href="#ref-26"/> +<_x0032_2 href="#ref-27"/> +<_x0032_3 href="#ref-28"/> +<_x0032_4 href="#ref-29"/> +<_x0032_5 href="#ref-30"/> +<_x0032_6 href="#ref-31"/> +<_x0032_7 href="#ref-32"/> +<_x0032_8 href="#ref-33"/> +<_x0032_9 href="#ref-34"/> +<_x0033_0 href="#ref-35"/> +<_x0033_1 href="#ref-36"/> +<_x0033_2 href="#ref-37"/> +<_x0033_3 href="#ref-38"/> +<_x0033_4 href="#ref-39"/> +<_x0033_5 href="#ref-40"/> +<_x0033_6 href="#ref-41"/> +<_x0033_7 href="#ref-42"/> +<_x0033_8 href="#ref-43"/> +<_x0033_9 href="#ref-44"/> +<_x0034_0 href="#ref-45"/> +<_x0034_1 href="#ref-46"/> +<_x0034_2 href="#ref-47"/> +<_x0034_3 href="#ref-48"/> +<_x0034_4 href="#ref-49"/> +<_x0034_5 href="#ref-50"/> +<_x0034_6 href="#ref-51"/> +<_x0034_7 href="#ref-52"/> +<_x0034_8 href="#ref-53"/> +<_x0034_9 href="#ref-54"/> +<_x0035_0 href="#ref-55"/> +<_x0035_1 href="#ref-56"/> +<_x0035_2 href="#ref-57"/> +<_x0035_3 href="#ref-58"/> +<_x0035_4 href="#ref-59"/> +<_x0035_5 href="#ref-60"/> +<_x0035_6 href="#ref-61"/> +<_x0035_7 href="#ref-62"/> +<_x0035_8 href="#ref-63"/> +<_x0035_9 href="#ref-64"/> +<_x0036_0 href="#ref-65"/> +<_x0036_1 href="#ref-66"/> +<_x0036_2 href="#ref-67"/> +<_x0036_3 href="#ref-68"/> +<_x0036_4 href="#ref-69"/> +<_x0036_5 href="#ref-70"/> +<_x0036_6 href="#ref-71"/> +<_x0036_7 href="#ref-72"/> +<_x0036_8 href="#ref-73"/> +<_x0036_9 href="#ref-74"/> +<_x0037_0 href="#ref-75"/> +<_x0037_1 href="#ref-76"/> +<_x0037_2 href="#ref-77"/> +<_x0037_3 href="#ref-78"/> +<_x0037_4 href="#ref-79"/> +<_x0037_5 href="#ref-80"/> +<_x0037_6 href="#ref-81"/> +<_x0037_7 href="#ref-82"/> +<_x0037_8 href="#ref-83"/> +<_x0037_9 href="#ref-84"/> +<_x0038_0 href="#ref-85"/> +<_x0038_1 href="#ref-86"/> +<_x0038_2 href="#ref-87"/> +<_x0038_3 href="#ref-88"/> +<_x0038_4 href="#ref-89"/> +<_x0038_5 href="#ref-90"/> +<_x0038_6 href="#ref-91"/> +<_x0038_7 href="#ref-92"/> +<_x0038_8 href="#ref-93"/> +<_x0038_9 href="#ref-94"/> +<_x0039_0 href="#ref-95"/> +<_x0039_1 href="#ref-96"/> +<_x0039_2 href="#ref-97"/> +<_x0039_3 href="#ref-98"/> +<_x0039_4 href="#ref-99"/> +<_x0039_5 href="#ref-100"/> +<_x0039_6 href="#ref-101"/> +<_x0039_7 href="#ref-102"/> +<_x0039_8 href="#ref-103"/> +<_x0039_9 href="#ref-104"/> +<_x0031_00 href="#ref-105"/> +<_x0031_01 href="#ref-106"/> +<_x0031_02 href="#ref-107"/> +<_x0031_03 href="#ref-108"/> +<_x0031_04 href="#ref-109"/> +<_x0031_05 href="#ref-110"/> +<_x0031_06 href="#ref-111"/> + + +<_x0030_ href="#ref-112"/> +<_x0031_ href="#ref-113"/> +<_x0032_ href="#ref-114"/> +<_x0033_ href="#ref-115"/> +<_x0034_ href="#ref-116"/> +<_x0035_ href="#ref-117"/> +<_x0036_ href="#ref-118"/> +<_x0037_ href="#ref-119"/> +<_x0038_ href="#ref-120"/> +<_x0039_ href="#ref-121"/> +<_x0031_0 href="#ref-122"/> +<_x0031_1 href="#ref-123"/> +<_x0031_2 href="#ref-124"/> +<_x0031_3 href="#ref-125"/> +<_x0031_4 href="#ref-126"/> +<_x0031_5 href="#ref-127"/> +<_x0031_6 href="#ref-128"/> +<_x0031_7 href="#ref-129"/> +<_x0031_8 href="#ref-130"/> +<_x0031_9 href="#ref-131"/> +<_x0032_0 href="#ref-132"/> +<_x0032_1 href="#ref-133"/> +<_x0032_2 href="#ref-134"/> +<_x0032_3 href="#ref-135"/> +<_x0032_4 href="#ref-136"/> +<_x0032_5 href="#ref-137"/> +<_x0032_6 href="#ref-138"/> + + +sri\sri_jog_4_5.png + + +sri\sri_jog_4_1.png + + +sri\sri_jog_4_2.png + + +sri\sri_jog_4_3.png + + +sri\sri_jog_4_4.png + + +sri\sri_jog_4_0.png + + +sri\sri_jog_2_5.png + + +sri\sri_jog_2_1.png + + +sri\sri_jog_2_2.png + + +sri\sri_jog_2_3.png + + +sri\sri_jog_2_4.png + + +sri\sri_jog_2_0.png + + +sri\sri_jog_3_5.png + + +sri\sri_jog_3_1.png + + +sri\sri_jog_3_2.png + + +sri\sri_jog_3_3.png + + +sri\sri_jog_3_4.png + + +sri\sri_jog_3_0.png + + +sri\sri_jog_7_0.png + + +sri\sri_jog_7_1.png + + +sri\sri_jog_7_2.png + + +sri\sri_jog_7_3.png + + +sri\sri_jog_7_4.png + + +sri\sri_jog_7_5.png + + +sri\sri_jog_1_0.png + + +sri\sri_jog_1_1.png + + +sri\sri_jog_1_2.png + + +sri\sri_jog_1_3.png + + +sri\sri_jog_1_4.png + + +sri\sri_jog_1_5.png + + +sri\sri_jog_6_0.png + + +sri\sri_jog_6_1.png + + +sri\sri_jog_6_2.png + + +sri\sri_jog_6_3.png + + +sri\sri_jog_6_4.png + + +sri\sri_jog_6_5.png + + +sri\sri_jog_5_5.png + + +sri\sri_jog_5_1.png + + +sri\sri_jog_5_2.png + + +sri\sri_jog_5_3.png + + +sri\sri_jog_5_4.png + + +sri\sri_jog_5_0.png + + +sri\sri_jog_0_5.png + + +sri\sri_jog_0_1.png + + +sri\sri_jog_0_2.png + + +sri\sri_jog_0_3.png + + +sri\sri_jog_0_4.png + + +sri\sri_jog_0_0.png + + +sri\sri_fire_0_2.png + + +sri\sri_fire_0_1.png + + +sri\sri_fire_0_0.png + + +sri\sri_fire_1_2.png + + +sri\sri_fire_1_1.png + + +sri\sri_fire_1_0.png + + +sri\sri_fire_2_2.png + + +sri\sri_fire_2_1.png + + +sri\sri_fire_2_0.png + + +sri\sri_fire_3_2.png + + +sri\sri_fire_3_1.png + + +sri\sri_fire_3_0.png + + +sri\sri_fire_4_2.png + + +sri\sri_fire_4_1.png + + +sri\sri_fire_4_0.png + + +sri\sri_fire_5_2.png + + +sri\sri_fire_5_1.png + + +sri\sri_fire_5_0.png + + +sri\sri_fire_6_2.png + + +sri\sri_fire_6_1.png + + +sri\sri_fire_6_0.png + + +sri\sri_fire_7_2.png + + +sri\sri_fire_7_1.png + + +sri\sri_fire_7_0.png + + +sri\sri_die_0_8.png + + +sri\sri_die_0_1.png + + +sri\sri_die_0_2.png + + +sri\sri_die_0_3.png + + +sri\sri_die_0_4.png + + +sri\sri_die_0_5.png + + +sri\sri_die_0_6.png + + +sri\sri_die_0_7.png + + +sri\sri_die_0_0.png + + +sri\sri_idle_0_2.png + + +sri\sri_idle_0_1.png + + +sri\sri_idle_0_0.png + + +sri\sri_idle_1_2.png + + +sri\sri_idle_1_1.png + + +sri\sri_idle_1_0.png + + +sri\sri_idle_2_3.png + + +sri\sri_idle_2_1.png + + +sri\sri_idle_2_2.png + + +sri\sri_idle_2_0.png + + +sri\sri_idle_3_2.png + + +sri\sri_idle_3_1.png + + +sri\sri_idle_3_0.png + + +sri\sri_idle_4_2.png + + +sri\sri_idle_4_1.png + + +sri\sri_idle_4_0.png + + +sri\sri_idle_5_2.png + + +sri\sri_idle_5_1.png + + +sri\sri_idle_5_0.png + + +sri\sri_idle_6_2.png + + +sri\sri_idle_6_1.png + + +sri\sri_idle_6_0.png + + +sri\sri_idle_7_3.png + + +sri\sri_idle_7_1.png + + +sri\sri_idle_7_2.png + + +sri\sri_idle_7_0.png + + +die 3 +0 +9 +<_x0030_ href="#ref-247"/> +<_x0031_ href="#ref-248"/> +<_x0032_ href="#ref-249"/> +<_x0033_ href="#ref-250"/> +<_x0034_ href="#ref-251"/> +<_x0035_ href="#ref-252"/> +<_x0036_ href="#ref-253"/> +<_x0037_ href="#ref-254"/> +<_x0038_ href="#ref-255"/> + + +fire 0 +0 +5 +<_x0030_ href="#ref-257"/> +<_x0031_ href="#ref-258"/> +<_x0032_ href="#ref-259"/> +<_x0033_ href="#ref-260"/> +<_x0034_ href="#ref-261"/> + + +fire 1 +0 +5 +<_x0030_ href="#ref-263"/> +<_x0031_ href="#ref-264"/> +<_x0032_ href="#ref-265"/> +<_x0033_ href="#ref-266"/> +<_x0034_ href="#ref-267"/> + + +fire 2 +0 +5 +<_x0030_ href="#ref-269"/> +<_x0031_ href="#ref-270"/> +<_x0032_ href="#ref-271"/> +<_x0033_ href="#ref-272"/> +<_x0034_ href="#ref-273"/> + + +fire 3 +0 +5 +<_x0030_ href="#ref-275"/> +<_x0031_ href="#ref-276"/> +<_x0032_ href="#ref-277"/> +<_x0033_ href="#ref-278"/> +<_x0034_ href="#ref-279"/> + + +fire 4 +0 +5 +<_x0030_ href="#ref-281"/> +<_x0031_ href="#ref-282"/> +<_x0032_ href="#ref-283"/> +<_x0033_ href="#ref-284"/> +<_x0034_ href="#ref-285"/> + + +fire 5 +0 +5 +<_x0030_ href="#ref-287"/> +<_x0031_ href="#ref-288"/> +<_x0032_ href="#ref-289"/> +<_x0033_ href="#ref-290"/> +<_x0034_ href="#ref-291"/> + + +fire 6 +0 +5 +<_x0030_ href="#ref-293"/> +<_x0031_ href="#ref-294"/> +<_x0032_ href="#ref-295"/> +<_x0033_ href="#ref-296"/> +<_x0034_ href="#ref-297"/> + + +fire 7 +0 +5 +<_x0030_ href="#ref-299"/> +<_x0031_ href="#ref-300"/> +<_x0032_ href="#ref-301"/> +<_x0033_ href="#ref-302"/> +<_x0034_ href="#ref-303"/> + + +idle 0 +0 +4 +<_x0030_ href="#ref-305"/> +<_x0031_ href="#ref-306"/> +<_x0032_ href="#ref-307"/> +<_x0033_ href="#ref-308"/> + + +idle 1 +0 +4 +<_x0030_ href="#ref-310"/> +<_x0031_ href="#ref-311"/> +<_x0032_ href="#ref-312"/> +<_x0033_ href="#ref-313"/> + + +idle 2 +0 +6 +<_x0030_ href="#ref-315"/> +<_x0031_ href="#ref-316"/> +<_x0032_ href="#ref-317"/> +<_x0033_ href="#ref-318"/> +<_x0034_ href="#ref-319"/> +<_x0035_ href="#ref-320"/> + + +idle 3 +0 +4 +<_x0030_ href="#ref-322"/> +<_x0031_ href="#ref-323"/> +<_x0032_ href="#ref-324"/> +<_x0033_ href="#ref-325"/> + + +idle 4 +0 +4 +<_x0030_ href="#ref-327"/> +<_x0031_ href="#ref-328"/> +<_x0032_ href="#ref-329"/> +<_x0033_ href="#ref-330"/> + + +idle 5 +0 +4 +<_x0030_ href="#ref-332"/> +<_x0031_ href="#ref-333"/> +<_x0032_ href="#ref-334"/> +<_x0033_ href="#ref-335"/> + + +idle 6 +0 +4 +<_x0030_ href="#ref-337"/> +<_x0031_ href="#ref-338"/> +<_x0032_ href="#ref-339"/> +<_x0033_ href="#ref-340"/> + + +idle 7 +0 +8 +<_x0030_ href="#ref-342"/> +<_x0031_ href="#ref-343"/> +<_x0032_ href="#ref-344"/> +<_x0033_ href="#ref-345"/> +<_x0034_ href="#ref-346"/> +<_x0035_ href="#ref-347"/> +<_x0036_ href="#ref-348"/> +<_x0037_ href="#ref-349"/> + + +jog 0 +0 +6 +<_x0030_ href="#ref-351"/> +<_x0031_ href="#ref-352"/> +<_x0032_ href="#ref-353"/> +<_x0033_ href="#ref-354"/> +<_x0034_ href="#ref-355"/> +<_x0035_ href="#ref-356"/> + + +jog 1 +0 +6 +<_x0030_ href="#ref-358"/> +<_x0031_ href="#ref-359"/> +<_x0032_ href="#ref-360"/> +<_x0033_ href="#ref-361"/> +<_x0034_ href="#ref-362"/> +<_x0035_ href="#ref-363"/> + + +jog 2 +0 +6 +<_x0030_ href="#ref-365"/> +<_x0031_ href="#ref-366"/> +<_x0032_ href="#ref-367"/> +<_x0033_ href="#ref-368"/> +<_x0034_ href="#ref-369"/> +<_x0035_ href="#ref-370"/> + + +jog 3 +0 +6 +<_x0030_ href="#ref-372"/> +<_x0031_ href="#ref-373"/> +<_x0032_ href="#ref-374"/> +<_x0033_ href="#ref-375"/> +<_x0034_ href="#ref-376"/> +<_x0035_ href="#ref-377"/> + + +jog 4 +0 +6 +<_x0030_ href="#ref-379"/> +<_x0031_ href="#ref-380"/> +<_x0032_ href="#ref-381"/> +<_x0033_ href="#ref-382"/> +<_x0034_ href="#ref-383"/> +<_x0035_ href="#ref-384"/> + + +jog 5 +0 +6 +<_x0030_ href="#ref-386"/> +<_x0031_ href="#ref-387"/> +<_x0032_ href="#ref-388"/> +<_x0033_ href="#ref-389"/> +<_x0034_ href="#ref-390"/> +<_x0035_ href="#ref-391"/> + + +jog 6 +0 +6 +<_x0030_ href="#ref-393"/> +<_x0031_ href="#ref-394"/> +<_x0032_ href="#ref-395"/> +<_x0033_ href="#ref-396"/> +<_x0034_ href="#ref-397"/> +<_x0035_ href="#ref-398"/> + + +jog 7 +0 +6 +<_x0030_ href="#ref-400"/> +<_x0031_ href="#ref-401"/> +<_x0032_ href="#ref-402"/> +<_x0033_ href="#ref-403"/> +<_x0034_ href="#ref-404"/> +<_x0035_ href="#ref-405"/> + + +icon +0 +1 +<_x0030_ href="#ref-407"/> + + +help +0 +1 +<_x0030_ href="#ref-409"/> + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-1 +-17 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +11 +-11 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +15 +-4 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +9 +6 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-2 +12 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-11 +6 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-16 +-4 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-11 +-13 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +15 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +15 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +15 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +15 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +15 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +15 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +15 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + +<_x0030_ href="#ref-548"/> + + +<_x0030_ href="#ref-549"/> + + +<_x0030_ href="#ref-550"/> + + +<_x0030_ href="#ref-551"/> + + +<_x0030_ href="#ref-552"/> + + +<_x0030_ href="#ref-553"/> + + +<_x0030_ href="#ref-554"/> + + +<_x0030_ href="#ref-555"/> + + +<_x0030_ href="#ref-556"/> + + +<_x0030_ href="#ref-557"/> + + +<_x0030_ href="#ref-558"/> + + +<_x0030_ href="#ref-559"/> + + +<_x0030_ href="#ref-560"/> + + +<_x0030_ href="#ref-561"/> + + +<_x0030_ href="#ref-562"/> + + +<_x0030_ href="#ref-563"/> + + +<_x0030_ href="#ref-564"/> + + +<_x0030_ href="#ref-565"/> + + +<_x0030_ href="#ref-566"/> + + +<_x0030_ href="#ref-567"/> + + +<_x0030_ href="#ref-568"/> + + +<_x0030_ href="#ref-569"/> + + +<_x0030_ href="#ref-570"/> + + +<_x0030_ href="#ref-571"/> + + +<_x0030_ href="#ref-572"/> + + +<_x0030_ href="#ref-573"/> + + +<_x0030_ href="#ref-574"/> + + +<_x0030_ href="#ref-575"/> + + +<_x0030_ href="#ref-576"/> + + +<_x0030_ href="#ref-577"/> + + +<_x0030_ href="#ref-578"/> + + +<_x0030_ href="#ref-579"/> + + +<_x0030_ href="#ref-580"/> + + +<_x0030_ href="#ref-581"/> + + +<_x0030_ href="#ref-582"/> + + +<_x0030_ href="#ref-583"/> + + +<_x0030_ href="#ref-584"/> + + +<_x0030_ href="#ref-585"/> + + +<_x0030_ href="#ref-586"/> + + +<_x0030_ href="#ref-587"/> + + +<_x0030_ href="#ref-588"/> + + +<_x0030_ href="#ref-589"/> + + +<_x0030_ href="#ref-590"/> + + +<_x0030_ href="#ref-591"/> + + +<_x0030_ href="#ref-592"/> + + +<_x0030_ href="#ref-593"/> + + +<_x0030_ href="#ref-594"/> + + +<_x0030_ href="#ref-595"/> + + +<_x0030_ href="#ref-596"/> + + +<_x0030_ href="#ref-597"/> + + +<_x0030_ href="#ref-598"/> + + +<_x0030_ href="#ref-599"/> + + +<_x0030_ href="#ref-600"/> + + +<_x0030_ href="#ref-601"/> + + +<_x0030_ href="#ref-602"/> + + +<_x0030_ href="#ref-603"/> + + +<_x0030_ href="#ref-604"/> + + +<_x0030_ href="#ref-605"/> + + +<_x0030_ href="#ref-606"/> + + +<_x0030_ href="#ref-607"/> + + +<_x0030_ href="#ref-608"/> + + +<_x0030_ href="#ref-609"/> + + +<_x0030_ href="#ref-610"/> + + +<_x0030_ href="#ref-611"/> + + +<_x0030_ href="#ref-612"/> + + +<_x0030_ href="#ref-613"/> + + +<_x0030_ href="#ref-614"/> + + +<_x0030_ href="#ref-615"/> + + +<_x0030_ href="#ref-616"/> + + +<_x0030_ href="#ref-617"/> + + +<_x0030_ href="#ref-618"/> + + +<_x0030_ href="#ref-619"/> + + +<_x0030_ href="#ref-620"/> + + +<_x0030_ href="#ref-621"/> + + +<_x0030_ href="#ref-622"/> + + +<_x0030_ href="#ref-623"/> + + +<_x0030_ href="#ref-624"/> + + +<_x0030_ href="#ref-625"/> + + +<_x0030_ href="#ref-626"/> + + +<_x0030_ href="#ref-627"/> + + +<_x0030_ href="#ref-628"/> + + +<_x0030_ href="#ref-629"/> + + +<_x0030_ href="#ref-630"/> + + +<_x0030_ href="#ref-631"/> + + +<_x0030_ href="#ref-632"/> + + +<_x0030_ href="#ref-633"/> + + +<_x0030_ href="#ref-634"/> + + +<_x0030_ href="#ref-635"/> + + +<_x0030_ href="#ref-636"/> + + +<_x0030_ href="#ref-637"/> + + +<_x0030_ href="#ref-638"/> + + +<_x0030_ href="#ref-639"/> + + +<_x0030_ href="#ref-640"/> + + +<_x0030_ href="#ref-641"/> + + +<_x0030_ href="#ref-642"/> + + +<_x0030_ href="#ref-643"/> + + +<_x0030_ href="#ref-644"/> + + +<_x0030_ href="#ref-645"/> + + +<_x0030_ href="#ref-646"/> + + +<_x0030_ href="#ref-647"/> + + +<_x0030_ href="#ref-648"/> + + +<_x0030_ href="#ref-649"/> + + +<_x0030_ href="#ref-650"/> + + +<_x0030_ href="#ref-651"/> + + +<_x0030_ href="#ref-652"/> + + +<_x0030_ href="#ref-653"/> + + +<_x0030_ href="#ref-654"/> + + +<_x0030_ href="#ref-655"/> + + +<_x0030_ href="#ref-656"/> + + +<_x0030_ href="#ref-657"/> + + +<_x0030_ href="#ref-658"/> + + +<_x0030_ href="#ref-659"/> + + +<_x0030_ href="#ref-660"/> + + +<_x0030_ href="#ref-661"/> + + +<_x0030_ href="#ref-662"/> + + +<_x0030_ href="#ref-663"/> + + +<_x0030_ href="#ref-664"/> + + +<_x0030_ href="#ref-665"/> + + +<_x0030_ href="#ref-666"/> + + +<_x0030_ href="#ref-667"/> + + +<_x0030_ href="#ref-668"/> + + +<_x0030_ href="#ref-669"/> + + +<_x0030_ href="#ref-670"/> + + +<_x0030_ href="#ref-671"/> + + +<_x0030_ href="#ref-672"/> + + +<_x0030_ href="#ref-673"/> + + +<_x0030_ href="#ref-674"/> + + +<_x0030_ href="#ref-675"/> + + +<_x0030_ href="#ref-676"/> + + +<_x0030_ href="#ref-677"/> + + +<_x0030_ href="#ref-678"/> + + +<_x0030_ href="#ref-679"/> + + +<_x0030_ href="#ref-680"/> + + +<_x0030_ href="#ref-681"/> + + +<_x0030_ href="#ref-682"/> + + +<_x0030_ href="#ref-683"/> + + +<_x0030_ href="#ref-684"/> + + + +12 +9 + + + + + +12 +9 + + + + + +13 +9 + + + + + +14 +10 + + + + + +14 +11 + + + + + +14 +11 + + + + + +15 +11 + + + + + +15 +10 + + + + + +15 +10 + + + + + +8 +12 + + + + + +8 +12 + + + + + +8 +12 + + + + + +8 +12 + + + + + +8 +12 + + + + + +11 +11 + + + + + +11 +11 + + + + + +11 +11 + + + + + +11 +11 + + + + + +11 +11 + + + + + +9 +8 + + + + + +9 +8 + + + + + +9 +8 + + + + + +9 +8 + + + + + +9 +8 + + + + + +9 +8 + + + + + +9 +8 + + + + + +9 +8 + + + + + +9 +8 + + + + + +9 +8 + + + + + +8 +8 + + + + + +8 +8 + + + + + +8 +8 + + + + + +8 +8 + + + + + +8 +8 + + + + + +10 +9 + + + + + +10 +9 + + + + + +10 +9 + + + + + +10 +9 + + + + + +10 +9 + + + + + +13 +8 + + + + + +13 +8 + + + + + +13 +8 + + + + + +13 +8 + + + + + +13 +8 + + + + + +11 +10 + + + + + +11 +10 + + + + + +11 +10 + + + + + +11 +10 + + + + + +11 +10 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +8 + + + + + +8 +8 + + + + + +8 +8 + + + + + +8 +8 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +7 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +6 +9 + + + + + +6 +9 + + + + + +6 +9 + + + + + +6 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +8 + + + + + +8 +8 + + + + + +8 +8 + + + + + +8 +8 + + + + + +8 +8 + + + + + +8 +8 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +8 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +7 +9 + + + + + +4 +0 + + + + + +8 +9 + + + + + diff --git a/data/art824/sri/sri_die_0_0.png b/data/art824/sri/sri_die_0_0.png new file mode 100644 index 0000000..a88dbd3 Binary files /dev/null and b/data/art824/sri/sri_die_0_0.png differ diff --git a/data/art824/sri/sri_die_0_1.png b/data/art824/sri/sri_die_0_1.png new file mode 100644 index 0000000..9578060 Binary files /dev/null and b/data/art824/sri/sri_die_0_1.png differ diff --git a/data/art824/sri/sri_die_0_2.png b/data/art824/sri/sri_die_0_2.png new file mode 100644 index 0000000..2f69bf4 Binary files /dev/null and b/data/art824/sri/sri_die_0_2.png differ diff --git a/data/art824/sri/sri_die_0_3.png b/data/art824/sri/sri_die_0_3.png new file mode 100644 index 0000000..ec64708 Binary files /dev/null and b/data/art824/sri/sri_die_0_3.png differ diff --git a/data/art824/sri/sri_die_0_4.png b/data/art824/sri/sri_die_0_4.png new file mode 100644 index 0000000..05b14b5 Binary files /dev/null and b/data/art824/sri/sri_die_0_4.png differ diff --git a/data/art824/sri/sri_die_0_5.png b/data/art824/sri/sri_die_0_5.png new file mode 100644 index 0000000..b675cc0 Binary files /dev/null and b/data/art824/sri/sri_die_0_5.png differ diff --git a/data/art824/sri/sri_die_0_6.png b/data/art824/sri/sri_die_0_6.png new file mode 100644 index 0000000..4ad6ead Binary files /dev/null and b/data/art824/sri/sri_die_0_6.png differ diff --git a/data/art824/sri/sri_die_0_7.png b/data/art824/sri/sri_die_0_7.png new file mode 100644 index 0000000..c7a7c2e Binary files /dev/null and b/data/art824/sri/sri_die_0_7.png differ diff --git a/data/art824/sri/sri_die_0_8.png b/data/art824/sri/sri_die_0_8.png new file mode 100644 index 0000000..dd133f2 Binary files /dev/null and b/data/art824/sri/sri_die_0_8.png differ diff --git a/data/art824/sri/sri_die_3_0.png b/data/art824/sri/sri_die_3_0.png new file mode 100644 index 0000000..67f12e2 Binary files /dev/null and b/data/art824/sri/sri_die_3_0.png differ diff --git a/data/art824/sri/sri_die_3_1.png b/data/art824/sri/sri_die_3_1.png new file mode 100644 index 0000000..690f6ce Binary files /dev/null and b/data/art824/sri/sri_die_3_1.png differ diff --git a/data/art824/sri/sri_die_3_2.png b/data/art824/sri/sri_die_3_2.png new file mode 100644 index 0000000..eb8da27 Binary files /dev/null and b/data/art824/sri/sri_die_3_2.png differ diff --git a/data/art824/sri/sri_die_3_3.png b/data/art824/sri/sri_die_3_3.png new file mode 100644 index 0000000..638710f Binary files /dev/null and b/data/art824/sri/sri_die_3_3.png differ diff --git a/data/art824/sri/sri_die_3_4.png b/data/art824/sri/sri_die_3_4.png new file mode 100644 index 0000000..2e3ce8a Binary files /dev/null and b/data/art824/sri/sri_die_3_4.png differ diff --git a/data/art824/sri/sri_die_3_5.png b/data/art824/sri/sri_die_3_5.png new file mode 100644 index 0000000..68d8538 Binary files /dev/null and b/data/art824/sri/sri_die_3_5.png differ diff --git a/data/art824/sri/sri_fire_0_0.png b/data/art824/sri/sri_fire_0_0.png new file mode 100644 index 0000000..764d867 Binary files /dev/null and b/data/art824/sri/sri_fire_0_0.png differ diff --git a/data/art824/sri/sri_fire_0_1.png b/data/art824/sri/sri_fire_0_1.png new file mode 100644 index 0000000..b6db3fe Binary files /dev/null and b/data/art824/sri/sri_fire_0_1.png differ diff --git a/data/art824/sri/sri_fire_0_2.png b/data/art824/sri/sri_fire_0_2.png new file mode 100644 index 0000000..0353650 Binary files /dev/null and b/data/art824/sri/sri_fire_0_2.png differ diff --git a/data/art824/sri/sri_fire_1_0.png b/data/art824/sri/sri_fire_1_0.png new file mode 100644 index 0000000..2267ac4 Binary files /dev/null and b/data/art824/sri/sri_fire_1_0.png differ diff --git a/data/art824/sri/sri_fire_1_1.png b/data/art824/sri/sri_fire_1_1.png new file mode 100644 index 0000000..7e8e328 Binary files /dev/null and b/data/art824/sri/sri_fire_1_1.png differ diff --git a/data/art824/sri/sri_fire_1_2.png b/data/art824/sri/sri_fire_1_2.png new file mode 100644 index 0000000..869829c Binary files /dev/null and b/data/art824/sri/sri_fire_1_2.png differ diff --git a/data/art824/sri/sri_fire_2_0.png b/data/art824/sri/sri_fire_2_0.png new file mode 100644 index 0000000..551f9d4 Binary files /dev/null and b/data/art824/sri/sri_fire_2_0.png differ diff --git a/data/art824/sri/sri_fire_2_1.png b/data/art824/sri/sri_fire_2_1.png new file mode 100644 index 0000000..69c1ebe Binary files /dev/null and b/data/art824/sri/sri_fire_2_1.png differ diff --git a/data/art824/sri/sri_fire_2_2.png b/data/art824/sri/sri_fire_2_2.png new file mode 100644 index 0000000..080fecc Binary files /dev/null and b/data/art824/sri/sri_fire_2_2.png differ diff --git a/data/art824/sri/sri_fire_3_0.png b/data/art824/sri/sri_fire_3_0.png new file mode 100644 index 0000000..1e804ca Binary files /dev/null and b/data/art824/sri/sri_fire_3_0.png differ diff --git a/data/art824/sri/sri_fire_3_1.png b/data/art824/sri/sri_fire_3_1.png new file mode 100644 index 0000000..ab77c97 Binary files /dev/null and b/data/art824/sri/sri_fire_3_1.png differ diff --git a/data/art824/sri/sri_fire_3_2.png b/data/art824/sri/sri_fire_3_2.png new file mode 100644 index 0000000..35f5575 Binary files /dev/null and b/data/art824/sri/sri_fire_3_2.png differ diff --git a/data/art824/sri/sri_fire_4_0.png b/data/art824/sri/sri_fire_4_0.png new file mode 100644 index 0000000..fdb7b46 Binary files /dev/null and b/data/art824/sri/sri_fire_4_0.png differ diff --git a/data/art824/sri/sri_fire_4_1.png b/data/art824/sri/sri_fire_4_1.png new file mode 100644 index 0000000..5b20934 Binary files /dev/null and b/data/art824/sri/sri_fire_4_1.png differ diff --git a/data/art824/sri/sri_fire_4_2.png b/data/art824/sri/sri_fire_4_2.png new file mode 100644 index 0000000..1e047c6 Binary files /dev/null and b/data/art824/sri/sri_fire_4_2.png differ diff --git a/data/art824/sri/sri_fire_5_0.png b/data/art824/sri/sri_fire_5_0.png new file mode 100644 index 0000000..4611988 Binary files /dev/null and b/data/art824/sri/sri_fire_5_0.png differ diff --git a/data/art824/sri/sri_fire_5_1.png b/data/art824/sri/sri_fire_5_1.png new file mode 100644 index 0000000..e868cbe Binary files /dev/null and b/data/art824/sri/sri_fire_5_1.png differ diff --git a/data/art824/sri/sri_fire_5_2.png b/data/art824/sri/sri_fire_5_2.png new file mode 100644 index 0000000..850c5ac Binary files /dev/null and b/data/art824/sri/sri_fire_5_2.png differ diff --git a/data/art824/sri/sri_fire_6_0.png b/data/art824/sri/sri_fire_6_0.png new file mode 100644 index 0000000..bb7d696 Binary files /dev/null and b/data/art824/sri/sri_fire_6_0.png differ diff --git a/data/art824/sri/sri_fire_6_1.png b/data/art824/sri/sri_fire_6_1.png new file mode 100644 index 0000000..21197f4 Binary files /dev/null and b/data/art824/sri/sri_fire_6_1.png differ diff --git a/data/art824/sri/sri_fire_6_2.png b/data/art824/sri/sri_fire_6_2.png new file mode 100644 index 0000000..949b034 Binary files /dev/null and b/data/art824/sri/sri_fire_6_2.png differ diff --git a/data/art824/sri/sri_fire_7_0.png b/data/art824/sri/sri_fire_7_0.png new file mode 100644 index 0000000..0e3e826 Binary files /dev/null and b/data/art824/sri/sri_fire_7_0.png differ diff --git a/data/art824/sri/sri_fire_7_1.png b/data/art824/sri/sri_fire_7_1.png new file mode 100644 index 0000000..44cb480 Binary files /dev/null and b/data/art824/sri/sri_fire_7_1.png differ diff --git a/data/art824/sri/sri_fire_7_2.png b/data/art824/sri/sri_fire_7_2.png new file mode 100644 index 0000000..dd52eca Binary files /dev/null and b/data/art824/sri/sri_fire_7_2.png differ diff --git a/data/art824/sri/sri_idle_0_0.png b/data/art824/sri/sri_idle_0_0.png new file mode 100644 index 0000000..9174f77 Binary files /dev/null and b/data/art824/sri/sri_idle_0_0.png differ diff --git a/data/art824/sri/sri_idle_0_1.png b/data/art824/sri/sri_idle_0_1.png new file mode 100644 index 0000000..a6561af Binary files /dev/null and b/data/art824/sri/sri_idle_0_1.png differ diff --git a/data/art824/sri/sri_idle_0_2.png b/data/art824/sri/sri_idle_0_2.png new file mode 100644 index 0000000..f45f06c Binary files /dev/null and b/data/art824/sri/sri_idle_0_2.png differ diff --git a/data/art824/sri/sri_idle_1_0.png b/data/art824/sri/sri_idle_1_0.png new file mode 100644 index 0000000..d9e55bb Binary files /dev/null and b/data/art824/sri/sri_idle_1_0.png differ diff --git a/data/art824/sri/sri_idle_1_1.png b/data/art824/sri/sri_idle_1_1.png new file mode 100644 index 0000000..1b4e13e Binary files /dev/null and b/data/art824/sri/sri_idle_1_1.png differ diff --git a/data/art824/sri/sri_idle_1_2.png b/data/art824/sri/sri_idle_1_2.png new file mode 100644 index 0000000..3078581 Binary files /dev/null and b/data/art824/sri/sri_idle_1_2.png differ diff --git a/data/art824/sri/sri_idle_2_0.png b/data/art824/sri/sri_idle_2_0.png new file mode 100644 index 0000000..820c2d2 Binary files /dev/null and b/data/art824/sri/sri_idle_2_0.png differ diff --git a/data/art824/sri/sri_idle_2_1.png b/data/art824/sri/sri_idle_2_1.png new file mode 100644 index 0000000..fbf7b5b Binary files /dev/null and b/data/art824/sri/sri_idle_2_1.png differ diff --git a/data/art824/sri/sri_idle_2_2.png b/data/art824/sri/sri_idle_2_2.png new file mode 100644 index 0000000..b010126 Binary files /dev/null and b/data/art824/sri/sri_idle_2_2.png differ diff --git a/data/art824/sri/sri_idle_2_3.png b/data/art824/sri/sri_idle_2_3.png new file mode 100644 index 0000000..2e136f7 Binary files /dev/null and b/data/art824/sri/sri_idle_2_3.png differ diff --git a/data/art824/sri/sri_idle_3_0.png b/data/art824/sri/sri_idle_3_0.png new file mode 100644 index 0000000..056ed9a Binary files /dev/null and b/data/art824/sri/sri_idle_3_0.png differ diff --git a/data/art824/sri/sri_idle_3_1.png b/data/art824/sri/sri_idle_3_1.png new file mode 100644 index 0000000..25c4fa0 Binary files /dev/null and b/data/art824/sri/sri_idle_3_1.png differ diff --git a/data/art824/sri/sri_idle_3_2.png b/data/art824/sri/sri_idle_3_2.png new file mode 100644 index 0000000..143011f Binary files /dev/null and b/data/art824/sri/sri_idle_3_2.png differ diff --git a/data/art824/sri/sri_idle_4_0.png b/data/art824/sri/sri_idle_4_0.png new file mode 100644 index 0000000..55beb95 Binary files /dev/null and b/data/art824/sri/sri_idle_4_0.png differ diff --git a/data/art824/sri/sri_idle_4_1.png b/data/art824/sri/sri_idle_4_1.png new file mode 100644 index 0000000..7a9728d Binary files /dev/null and b/data/art824/sri/sri_idle_4_1.png differ diff --git a/data/art824/sri/sri_idle_4_2.png b/data/art824/sri/sri_idle_4_2.png new file mode 100644 index 0000000..105cc8d Binary files /dev/null and b/data/art824/sri/sri_idle_4_2.png differ diff --git a/data/art824/sri/sri_idle_5_0.png b/data/art824/sri/sri_idle_5_0.png new file mode 100644 index 0000000..e6e70c0 Binary files /dev/null and b/data/art824/sri/sri_idle_5_0.png differ diff --git a/data/art824/sri/sri_idle_5_1.png b/data/art824/sri/sri_idle_5_1.png new file mode 100644 index 0000000..ac9e1c3 Binary files /dev/null and b/data/art824/sri/sri_idle_5_1.png differ diff --git a/data/art824/sri/sri_idle_5_2.png b/data/art824/sri/sri_idle_5_2.png new file mode 100644 index 0000000..d463490 Binary files /dev/null and b/data/art824/sri/sri_idle_5_2.png differ diff --git a/data/art824/sri/sri_idle_6_0.png b/data/art824/sri/sri_idle_6_0.png new file mode 100644 index 0000000..74913d5 Binary files /dev/null and b/data/art824/sri/sri_idle_6_0.png differ diff --git a/data/art824/sri/sri_idle_6_1.png b/data/art824/sri/sri_idle_6_1.png new file mode 100644 index 0000000..d157dcf Binary files /dev/null and b/data/art824/sri/sri_idle_6_1.png differ diff --git a/data/art824/sri/sri_idle_6_2.png b/data/art824/sri/sri_idle_6_2.png new file mode 100644 index 0000000..bd535ef Binary files /dev/null and b/data/art824/sri/sri_idle_6_2.png differ diff --git a/data/art824/sri/sri_idle_7_0.png b/data/art824/sri/sri_idle_7_0.png new file mode 100644 index 0000000..e174978 Binary files /dev/null and b/data/art824/sri/sri_idle_7_0.png differ diff --git a/data/art824/sri/sri_idle_7_1.png b/data/art824/sri/sri_idle_7_1.png new file mode 100644 index 0000000..bd6635d Binary files /dev/null and b/data/art824/sri/sri_idle_7_1.png differ diff --git a/data/art824/sri/sri_idle_7_2.png b/data/art824/sri/sri_idle_7_2.png new file mode 100644 index 0000000..c0ba648 Binary files /dev/null and b/data/art824/sri/sri_idle_7_2.png differ diff --git a/data/art824/sri/sri_idle_7_3.png b/data/art824/sri/sri_idle_7_3.png new file mode 100644 index 0000000..d325452 Binary files /dev/null and b/data/art824/sri/sri_idle_7_3.png differ diff --git a/data/art824/sri/sri_jog_0_0.png b/data/art824/sri/sri_jog_0_0.png new file mode 100644 index 0000000..703e61f Binary files /dev/null and b/data/art824/sri/sri_jog_0_0.png differ diff --git a/data/art824/sri/sri_jog_0_1.png b/data/art824/sri/sri_jog_0_1.png new file mode 100644 index 0000000..f82a6fb Binary files /dev/null and b/data/art824/sri/sri_jog_0_1.png differ diff --git a/data/art824/sri/sri_jog_0_2.png b/data/art824/sri/sri_jog_0_2.png new file mode 100644 index 0000000..5475c0f Binary files /dev/null and b/data/art824/sri/sri_jog_0_2.png differ diff --git a/data/art824/sri/sri_jog_0_3.png b/data/art824/sri/sri_jog_0_3.png new file mode 100644 index 0000000..57c9703 Binary files /dev/null and b/data/art824/sri/sri_jog_0_3.png differ diff --git a/data/art824/sri/sri_jog_0_4.png b/data/art824/sri/sri_jog_0_4.png new file mode 100644 index 0000000..d979f9e Binary files /dev/null and b/data/art824/sri/sri_jog_0_4.png differ diff --git a/data/art824/sri/sri_jog_0_5.png b/data/art824/sri/sri_jog_0_5.png new file mode 100644 index 0000000..589dc2c Binary files /dev/null and b/data/art824/sri/sri_jog_0_5.png differ diff --git a/data/art824/sri/sri_jog_1_0.png b/data/art824/sri/sri_jog_1_0.png new file mode 100644 index 0000000..d59cf71 Binary files /dev/null and b/data/art824/sri/sri_jog_1_0.png differ diff --git a/data/art824/sri/sri_jog_1_1.png b/data/art824/sri/sri_jog_1_1.png new file mode 100644 index 0000000..5449420 Binary files /dev/null and b/data/art824/sri/sri_jog_1_1.png differ diff --git a/data/art824/sri/sri_jog_1_2.png b/data/art824/sri/sri_jog_1_2.png new file mode 100644 index 0000000..e6ac4eb Binary files /dev/null and b/data/art824/sri/sri_jog_1_2.png differ diff --git a/data/art824/sri/sri_jog_1_3.png b/data/art824/sri/sri_jog_1_3.png new file mode 100644 index 0000000..920ba61 Binary files /dev/null and b/data/art824/sri/sri_jog_1_3.png differ diff --git a/data/art824/sri/sri_jog_1_4.png b/data/art824/sri/sri_jog_1_4.png new file mode 100644 index 0000000..e24ff2a Binary files /dev/null and b/data/art824/sri/sri_jog_1_4.png differ diff --git a/data/art824/sri/sri_jog_1_5.png b/data/art824/sri/sri_jog_1_5.png new file mode 100644 index 0000000..f3430fb Binary files /dev/null and b/data/art824/sri/sri_jog_1_5.png differ diff --git a/data/art824/sri/sri_jog_2_0.png b/data/art824/sri/sri_jog_2_0.png new file mode 100644 index 0000000..c57bc0b Binary files /dev/null and b/data/art824/sri/sri_jog_2_0.png differ diff --git a/data/art824/sri/sri_jog_2_1.png b/data/art824/sri/sri_jog_2_1.png new file mode 100644 index 0000000..624b746 Binary files /dev/null and b/data/art824/sri/sri_jog_2_1.png differ diff --git a/data/art824/sri/sri_jog_2_2.png b/data/art824/sri/sri_jog_2_2.png new file mode 100644 index 0000000..002816c Binary files /dev/null and b/data/art824/sri/sri_jog_2_2.png differ diff --git a/data/art824/sri/sri_jog_2_3.png b/data/art824/sri/sri_jog_2_3.png new file mode 100644 index 0000000..6dd7625 Binary files /dev/null and b/data/art824/sri/sri_jog_2_3.png differ diff --git a/data/art824/sri/sri_jog_2_4.png b/data/art824/sri/sri_jog_2_4.png new file mode 100644 index 0000000..d1b4ff4 Binary files /dev/null and b/data/art824/sri/sri_jog_2_4.png differ diff --git a/data/art824/sri/sri_jog_2_5.png b/data/art824/sri/sri_jog_2_5.png new file mode 100644 index 0000000..baa6fa0 Binary files /dev/null and b/data/art824/sri/sri_jog_2_5.png differ diff --git a/data/art824/sri/sri_jog_3_0.png b/data/art824/sri/sri_jog_3_0.png new file mode 100644 index 0000000..ffea6bf Binary files /dev/null and b/data/art824/sri/sri_jog_3_0.png differ diff --git a/data/art824/sri/sri_jog_3_1.png b/data/art824/sri/sri_jog_3_1.png new file mode 100644 index 0000000..52f6367 Binary files /dev/null and b/data/art824/sri/sri_jog_3_1.png differ diff --git a/data/art824/sri/sri_jog_3_2.png b/data/art824/sri/sri_jog_3_2.png new file mode 100644 index 0000000..c28e735 Binary files /dev/null and b/data/art824/sri/sri_jog_3_2.png differ diff --git a/data/art824/sri/sri_jog_3_3.png b/data/art824/sri/sri_jog_3_3.png new file mode 100644 index 0000000..856400c Binary files /dev/null and b/data/art824/sri/sri_jog_3_3.png differ diff --git a/data/art824/sri/sri_jog_3_4.png b/data/art824/sri/sri_jog_3_4.png new file mode 100644 index 0000000..1f73bae Binary files /dev/null and b/data/art824/sri/sri_jog_3_4.png differ diff --git a/data/art824/sri/sri_jog_3_5.png b/data/art824/sri/sri_jog_3_5.png new file mode 100644 index 0000000..4f95cdc Binary files /dev/null and b/data/art824/sri/sri_jog_3_5.png differ diff --git a/data/art824/sri/sri_jog_4_0.png b/data/art824/sri/sri_jog_4_0.png new file mode 100644 index 0000000..6095831 Binary files /dev/null and b/data/art824/sri/sri_jog_4_0.png differ diff --git a/data/art824/sri/sri_jog_4_1.png b/data/art824/sri/sri_jog_4_1.png new file mode 100644 index 0000000..3734172 Binary files /dev/null and b/data/art824/sri/sri_jog_4_1.png differ diff --git a/data/art824/sri/sri_jog_4_2.png b/data/art824/sri/sri_jog_4_2.png new file mode 100644 index 0000000..b3c91c7 Binary files /dev/null and b/data/art824/sri/sri_jog_4_2.png differ diff --git a/data/art824/sri/sri_jog_4_3.png b/data/art824/sri/sri_jog_4_3.png new file mode 100644 index 0000000..3e4d34b Binary files /dev/null and b/data/art824/sri/sri_jog_4_3.png differ diff --git a/data/art824/sri/sri_jog_4_4.png b/data/art824/sri/sri_jog_4_4.png new file mode 100644 index 0000000..54f32e0 Binary files /dev/null and b/data/art824/sri/sri_jog_4_4.png differ diff --git a/data/art824/sri/sri_jog_4_5.png b/data/art824/sri/sri_jog_4_5.png new file mode 100644 index 0000000..7d26e73 Binary files /dev/null and b/data/art824/sri/sri_jog_4_5.png differ diff --git a/data/art824/sri/sri_jog_5_0.png b/data/art824/sri/sri_jog_5_0.png new file mode 100644 index 0000000..8c5e083 Binary files /dev/null and b/data/art824/sri/sri_jog_5_0.png differ diff --git a/data/art824/sri/sri_jog_5_1.png b/data/art824/sri/sri_jog_5_1.png new file mode 100644 index 0000000..8fd2d73 Binary files /dev/null and b/data/art824/sri/sri_jog_5_1.png differ diff --git a/data/art824/sri/sri_jog_5_2.png b/data/art824/sri/sri_jog_5_2.png new file mode 100644 index 0000000..91e6330 Binary files /dev/null and b/data/art824/sri/sri_jog_5_2.png differ diff --git a/data/art824/sri/sri_jog_5_3.png b/data/art824/sri/sri_jog_5_3.png new file mode 100644 index 0000000..f1ca235 Binary files /dev/null and b/data/art824/sri/sri_jog_5_3.png differ diff --git a/data/art824/sri/sri_jog_5_4.png b/data/art824/sri/sri_jog_5_4.png new file mode 100644 index 0000000..3501bca Binary files /dev/null and b/data/art824/sri/sri_jog_5_4.png differ diff --git a/data/art824/sri/sri_jog_5_5.png b/data/art824/sri/sri_jog_5_5.png new file mode 100644 index 0000000..9d13bf8 Binary files /dev/null and b/data/art824/sri/sri_jog_5_5.png differ diff --git a/data/art824/sri/sri_jog_6_0.png b/data/art824/sri/sri_jog_6_0.png new file mode 100644 index 0000000..97183c1 Binary files /dev/null and b/data/art824/sri/sri_jog_6_0.png differ diff --git a/data/art824/sri/sri_jog_6_1.png b/data/art824/sri/sri_jog_6_1.png new file mode 100644 index 0000000..0ed09e9 Binary files /dev/null and b/data/art824/sri/sri_jog_6_1.png differ diff --git a/data/art824/sri/sri_jog_6_2.png b/data/art824/sri/sri_jog_6_2.png new file mode 100644 index 0000000..2f75071 Binary files /dev/null and b/data/art824/sri/sri_jog_6_2.png differ diff --git a/data/art824/sri/sri_jog_6_3.png b/data/art824/sri/sri_jog_6_3.png new file mode 100644 index 0000000..f2bf45a Binary files /dev/null and b/data/art824/sri/sri_jog_6_3.png differ diff --git a/data/art824/sri/sri_jog_6_4.png b/data/art824/sri/sri_jog_6_4.png new file mode 100644 index 0000000..184b322 Binary files /dev/null and b/data/art824/sri/sri_jog_6_4.png differ diff --git a/data/art824/sri/sri_jog_6_5.png b/data/art824/sri/sri_jog_6_5.png new file mode 100644 index 0000000..889005b Binary files /dev/null and b/data/art824/sri/sri_jog_6_5.png differ diff --git a/data/art824/sri/sri_jog_7_0.png b/data/art824/sri/sri_jog_7_0.png new file mode 100644 index 0000000..47d896c Binary files /dev/null and b/data/art824/sri/sri_jog_7_0.png differ diff --git a/data/art824/sri/sri_jog_7_1.png b/data/art824/sri/sri_jog_7_1.png new file mode 100644 index 0000000..2eab295 Binary files /dev/null and b/data/art824/sri/sri_jog_7_1.png differ diff --git a/data/art824/sri/sri_jog_7_2.png b/data/art824/sri/sri_jog_7_2.png new file mode 100644 index 0000000..abaa06a Binary files /dev/null and b/data/art824/sri/sri_jog_7_2.png differ diff --git a/data/art824/sri/sri_jog_7_3.png b/data/art824/sri/sri_jog_7_3.png new file mode 100644 index 0000000..9bdb0d3 Binary files /dev/null and b/data/art824/sri/sri_jog_7_3.png differ diff --git a/data/art824/sri/sri_jog_7_4.png b/data/art824/sri/sri_jog_7_4.png new file mode 100644 index 0000000..772d8b2 Binary files /dev/null and b/data/art824/sri/sri_jog_7_4.png differ diff --git a/data/art824/sri/sri_jog_7_5.png b/data/art824/sri/sri_jog_7_5.png new file mode 100644 index 0000000..3383037 Binary files /dev/null and b/data/art824/sri/sri_jog_7_5.png differ diff --git a/data/art824/standardfont.bmp b/data/art824/standardfont.bmp new file mode 100644 index 0000000..9eb6312 Binary files /dev/null and b/data/art824/standardfont.bmp differ diff --git a/data/art824/standardfont.txt b/data/art824/standardfont.txt new file mode 100644 index 0000000..1510581 --- /dev/null +++ b/data/art824/standardfont.txt @@ -0,0 +1 @@ +!"#$%&'()*+,-./ ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz:;<=>?\@~ \ No newline at end of file diff --git a/data/art824/tankshot.amx b/data/art824/tankshot.amx new file mode 100644 index 0000000..a6b179b --- /dev/null +++ b/data/art824/tankshot.amx @@ -0,0 +1,187 @@ + + + + + +80 +true + + +<_x0030_ href="#ref-5"/> +<_x0031_ href="#ref-6"/> +<_x0032_ href="#ref-7"/> +<_x0033_ href="#ref-8"/> +<_x0034_ href="#ref-9"/> +<_x0035_ href="#ref-10"/> +<_x0036_ href="#ref-11"/> + + +<_x0030_ href="#ref-12"/> +<_x0031_ href="#ref-13"/> + + +tankshot\tankshot_a_1_3.png + + +tankshot\tankshot_a_0_0.png + + +tankshot\tankshot_a_0_1.png + + +tankshot\tankshot_a_0_2.png + + +tankshot\tankshot_a_1_0.png + + +tankshot\tankshot_a_1_1.png + + +tankshot\tankshot_a_1_2.png + + +a 0 +0 +3 +<_x0030_ href="#ref-22"/> +<_x0031_ href="#ref-23"/> +<_x0032_ href="#ref-24"/> + + +a 1 +0 +4 +<_x0030_ href="#ref-26"/> +<_x0031_ href="#ref-27"/> +<_x0032_ href="#ref-28"/> +<_x0033_ href="#ref-29"/> + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + +<_x0030_ href="#ref-38"/> + + +<_x0030_ href="#ref-39"/> + + +<_x0030_ href="#ref-40"/> + + +<_x0030_ href="#ref-41"/> + + +<_x0030_ href="#ref-42"/> + + +<_x0030_ href="#ref-43"/> + + +<_x0030_ href="#ref-44"/> + + + +4 +4 + + + + + +4 +4 + + + + + +4 +4 + + + + + +6 +6 + + + + + +6 +6 + + + + + +6 +6 + + + + + +6 +6 + + + + + diff --git a/data/art824/tankshot/tankshot_a_0_0.png b/data/art824/tankshot/tankshot_a_0_0.png new file mode 100644 index 0000000..527116a Binary files /dev/null and b/data/art824/tankshot/tankshot_a_0_0.png differ diff --git a/data/art824/tankshot/tankshot_a_0_1.png b/data/art824/tankshot/tankshot_a_0_1.png new file mode 100644 index 0000000..645ce53 Binary files /dev/null and b/data/art824/tankshot/tankshot_a_0_1.png differ diff --git a/data/art824/tankshot/tankshot_a_0_2.png b/data/art824/tankshot/tankshot_a_0_2.png new file mode 100644 index 0000000..1db624a Binary files /dev/null and b/data/art824/tankshot/tankshot_a_0_2.png differ diff --git a/data/art824/tankshot/tankshot_a_1_0.png b/data/art824/tankshot/tankshot_a_1_0.png new file mode 100644 index 0000000..c20729e Binary files /dev/null and b/data/art824/tankshot/tankshot_a_1_0.png differ diff --git a/data/art824/tankshot/tankshot_a_1_1.png b/data/art824/tankshot/tankshot_a_1_1.png new file mode 100644 index 0000000..b9802b1 Binary files /dev/null and b/data/art824/tankshot/tankshot_a_1_1.png differ diff --git a/data/art824/tankshot/tankshot_a_1_2.png b/data/art824/tankshot/tankshot_a_1_2.png new file mode 100644 index 0000000..3cdf51a Binary files /dev/null and b/data/art824/tankshot/tankshot_a_1_2.png differ diff --git a/data/art824/tankshot/tankshot_a_1_3.png b/data/art824/tankshot/tankshot_a_1_3.png new file mode 100644 index 0000000..6b17832 Binary files /dev/null and b/data/art824/tankshot/tankshot_a_1_3.png differ diff --git a/data/art824/titlefont.bmp b/data/art824/titlefont.bmp new file mode 100644 index 0000000..72f628b Binary files /dev/null and b/data/art824/titlefont.bmp differ diff --git a/data/art824/titlefont.txt b/data/art824/titlefont.txt new file mode 100644 index 0000000..2edf19f --- /dev/null +++ b/data/art824/titlefont.txt @@ -0,0 +1 @@ +!"#$%&'()*+,-./ ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz:;<=>?\@ \ No newline at end of file diff --git a/data/art824/tmac.amx b/data/art824/tmac.amx new file mode 100644 index 0000000..1bb5a8b --- /dev/null +++ b/data/art824/tmac.amx @@ -0,0 +1,3336 @@ + + + + + +80 +true + + +<_x0030_ href="#ref-5"/> +<_x0031_ href="#ref-6"/> +<_x0032_ href="#ref-7"/> +<_x0033_ href="#ref-8"/> +<_x0034_ href="#ref-9"/> +<_x0035_ href="#ref-10"/> +<_x0036_ href="#ref-11"/> +<_x0037_ href="#ref-12"/> +<_x0038_ href="#ref-13"/> +<_x0039_ href="#ref-14"/> +<_x0031_0 href="#ref-15"/> +<_x0031_1 href="#ref-16"/> +<_x0031_2 href="#ref-17"/> +<_x0031_3 href="#ref-18"/> +<_x0031_4 href="#ref-19"/> +<_x0031_5 href="#ref-20"/> +<_x0031_6 href="#ref-21"/> +<_x0031_7 href="#ref-22"/> +<_x0031_8 href="#ref-23"/> +<_x0031_9 href="#ref-24"/> +<_x0032_0 href="#ref-25"/> +<_x0032_1 href="#ref-26"/> +<_x0032_2 href="#ref-27"/> +<_x0032_3 href="#ref-28"/> +<_x0032_4 href="#ref-29"/> +<_x0032_5 href="#ref-30"/> +<_x0032_6 href="#ref-31"/> +<_x0032_7 href="#ref-32"/> +<_x0032_8 href="#ref-33"/> +<_x0032_9 href="#ref-34"/> +<_x0033_0 href="#ref-35"/> +<_x0033_1 href="#ref-36"/> +<_x0033_2 href="#ref-37"/> +<_x0033_3 href="#ref-38"/> +<_x0033_4 href="#ref-39"/> +<_x0033_5 href="#ref-40"/> +<_x0033_6 href="#ref-41"/> +<_x0033_7 href="#ref-42"/> +<_x0033_8 href="#ref-43"/> +<_x0033_9 href="#ref-44"/> +<_x0034_0 href="#ref-45"/> +<_x0034_1 href="#ref-46"/> +<_x0034_2 href="#ref-47"/> +<_x0034_3 href="#ref-48"/> +<_x0034_4 href="#ref-49"/> +<_x0034_5 href="#ref-50"/> +<_x0034_6 href="#ref-51"/> +<_x0034_7 href="#ref-52"/> +<_x0034_8 href="#ref-53"/> +<_x0034_9 href="#ref-54"/> +<_x0035_0 href="#ref-55"/> +<_x0035_1 href="#ref-56"/> +<_x0035_2 href="#ref-57"/> +<_x0035_3 href="#ref-58"/> +<_x0035_4 href="#ref-59"/> +<_x0035_5 href="#ref-60"/> + + +<_x0030_ href="#ref-61"/> +<_x0031_ href="#ref-62"/> +<_x0032_ href="#ref-63"/> +<_x0033_ href="#ref-64"/> +<_x0034_ href="#ref-65"/> +<_x0035_ href="#ref-66"/> +<_x0036_ href="#ref-67"/> +<_x0037_ href="#ref-68"/> +<_x0038_ href="#ref-69"/> +<_x0039_ href="#ref-70"/> +<_x0031_0 href="#ref-71"/> +<_x0031_1 href="#ref-72"/> +<_x0031_2 href="#ref-73"/> +<_x0031_3 href="#ref-74"/> +<_x0031_4 href="#ref-75"/> +<_x0031_5 href="#ref-76"/> +<_x0031_6 href="#ref-77"/> +<_x0031_7 href="#ref-78"/> +<_x0031_8 href="#ref-79"/> +<_x0031_9 href="#ref-80"/> +<_x0032_0 href="#ref-81"/> +<_x0032_1 href="#ref-82"/> +<_x0032_2 href="#ref-83"/> +<_x0032_3 href="#ref-84"/> +<_x0032_4 href="#ref-85"/> +<_x0032_5 href="#ref-86"/> + + +tmac\tmac_base_0_0.png + + +tmac\tmac_base_1_0.png + + +tmac\tmac_base_7_0.png + + +tmac\tmac_base_3_0.png + + +tmac\tmac_base_5_0.png + + +tmac\tmac_base_6_0.png + + +tmac\tmac_base_2_0.png + + +tmac\tmac_base_4_0.png + + +tmac\tmac_fire_15_2.png + + +tmac\tmac_fire_00_1.png + + +tmac\tmac_fire_00_2.png + + +tmac\tmac_fire_01_0.png + + +tmac\tmac_fire_01_1.png + + +tmac\tmac_fire_01_2.png + + +tmac\tmac_fire_02_0.png + + +tmac\tmac_fire_02_1.png + + +tmac\tmac_fire_02_2.png + + +tmac\tmac_fire_03_0.png + + +tmac\tmac_fire_03_1.png + + +tmac\tmac_fire_03_2.png + + +tmac\tmac_fire_04_0.png + + +tmac\tmac_fire_04_1.png + + +tmac\tmac_fire_04_2.png + + +tmac\tmac_fire_05_0.png + + +tmac\tmac_fire_05_1.png + + +tmac\tmac_fire_05_2.png + + +tmac\tmac_fire_06_0.png + + +tmac\tmac_fire_06_1.png + + +tmac\tmac_fire_06_2.png + + +tmac\tmac_fire_07_0.png + + +tmac\tmac_fire_07_1.png + + +tmac\tmac_fire_07_2.png + + +tmac\tmac_fire_08_0.png + + +tmac\tmac_fire_08_1.png + + +tmac\tmac_fire_08_2.png + + +tmac\tmac_fire_09_0.png + + +tmac\tmac_fire_09_1.png + + +tmac\tmac_fire_09_2.png + + +tmac\tmac_fire_10_0.png + + +tmac\tmac_fire_10_1.png + + +tmac\tmac_fire_10_2.png + + +tmac\tmac_fire_11_0.png + + +tmac\tmac_fire_11_1.png + + +tmac\tmac_fire_11_2.png + + +tmac\tmac_fire_12_0.png + + +tmac\tmac_fire_12_1.png + + +tmac\tmac_fire_12_2.png + + +tmac\tmac_fire_13_0.png + + +tmac\tmac_fire_13_1.png + + +tmac\tmac_fire_13_2.png + + +tmac\tmac_fire_14_0.png + + +tmac\tmac_fire_14_1.png + + +tmac\tmac_fire_14_2.png + + +tmac\tmac_fire_15_0.png + + +tmac\tmac_fire_15_1.png + + +tmac\tmac_fire_00_0.png + + +base 0 +0 +1 +<_x0030_ href="#ref-144"/> + + +base 1 +0 +1 +<_x0030_ href="#ref-146"/> + + +base 2 +0 +1 +<_x0030_ href="#ref-148"/> + + +base 3 +0 +1 +<_x0030_ href="#ref-150"/> + + +base 4 +0 +1 +<_x0030_ href="#ref-152"/> + + +base 5 +0 +1 +<_x0030_ href="#ref-154"/> + + +base 6 +0 +1 +<_x0030_ href="#ref-156"/> + + +base 7 +0 +1 +<_x0030_ href="#ref-158"/> + + +fire 00 +0 +9 +<_x0030_ href="#ref-160"/> +<_x0031_ href="#ref-161"/> +<_x0032_ href="#ref-162"/> +<_x0033_ href="#ref-163"/> +<_x0034_ href="#ref-164"/> +<_x0035_ href="#ref-165"/> +<_x0036_ href="#ref-166"/> +<_x0037_ href="#ref-167"/> +<_x0038_ href="#ref-168"/> + + +fire 01 +0 +9 +<_x0030_ href="#ref-170"/> +<_x0031_ href="#ref-171"/> +<_x0032_ href="#ref-172"/> +<_x0033_ href="#ref-173"/> +<_x0034_ href="#ref-174"/> +<_x0035_ href="#ref-175"/> +<_x0036_ href="#ref-176"/> +<_x0037_ href="#ref-177"/> +<_x0038_ href="#ref-178"/> + + +fire 02 +0 +9 +<_x0030_ href="#ref-180"/> +<_x0031_ href="#ref-181"/> +<_x0032_ href="#ref-182"/> +<_x0033_ href="#ref-183"/> +<_x0034_ href="#ref-184"/> +<_x0035_ href="#ref-185"/> +<_x0036_ href="#ref-186"/> +<_x0037_ href="#ref-187"/> +<_x0038_ href="#ref-188"/> + + +fire 03 +0 +9 +<_x0030_ href="#ref-190"/> +<_x0031_ href="#ref-191"/> +<_x0032_ href="#ref-192"/> +<_x0033_ href="#ref-193"/> +<_x0034_ href="#ref-194"/> +<_x0035_ href="#ref-195"/> +<_x0036_ href="#ref-196"/> +<_x0037_ href="#ref-197"/> +<_x0038_ href="#ref-198"/> + + +fire 04 +0 +9 +<_x0030_ href="#ref-200"/> +<_x0031_ href="#ref-201"/> +<_x0032_ href="#ref-202"/> +<_x0033_ href="#ref-203"/> +<_x0034_ href="#ref-204"/> +<_x0035_ href="#ref-205"/> +<_x0036_ href="#ref-206"/> +<_x0037_ href="#ref-207"/> +<_x0038_ href="#ref-208"/> + + +fire 05 +0 +9 +<_x0030_ href="#ref-210"/> +<_x0031_ href="#ref-211"/> +<_x0032_ href="#ref-212"/> +<_x0033_ href="#ref-213"/> +<_x0034_ href="#ref-214"/> +<_x0035_ href="#ref-215"/> +<_x0036_ href="#ref-216"/> +<_x0037_ href="#ref-217"/> +<_x0038_ href="#ref-218"/> + + +fire 06 +0 +9 +<_x0030_ href="#ref-220"/> +<_x0031_ href="#ref-221"/> +<_x0032_ href="#ref-222"/> +<_x0033_ href="#ref-223"/> +<_x0034_ href="#ref-224"/> +<_x0035_ href="#ref-225"/> +<_x0036_ href="#ref-226"/> +<_x0037_ href="#ref-227"/> +<_x0038_ href="#ref-228"/> + + +fire 07 +0 +9 +<_x0030_ href="#ref-230"/> +<_x0031_ href="#ref-231"/> +<_x0032_ href="#ref-232"/> +<_x0033_ href="#ref-233"/> +<_x0034_ href="#ref-234"/> +<_x0035_ href="#ref-235"/> +<_x0036_ href="#ref-236"/> +<_x0037_ href="#ref-237"/> +<_x0038_ href="#ref-238"/> + + +fire 08 +0 +9 +<_x0030_ href="#ref-240"/> +<_x0031_ href="#ref-241"/> +<_x0032_ href="#ref-242"/> +<_x0033_ href="#ref-243"/> +<_x0034_ href="#ref-244"/> +<_x0035_ href="#ref-245"/> +<_x0036_ href="#ref-246"/> +<_x0037_ href="#ref-247"/> +<_x0038_ href="#ref-248"/> + + +fire 09 +0 +9 +<_x0030_ href="#ref-250"/> +<_x0031_ href="#ref-251"/> +<_x0032_ href="#ref-252"/> +<_x0033_ href="#ref-253"/> +<_x0034_ href="#ref-254"/> +<_x0035_ href="#ref-255"/> +<_x0036_ href="#ref-256"/> +<_x0037_ href="#ref-257"/> +<_x0038_ href="#ref-258"/> + + +fire 10 +0 +9 +<_x0030_ href="#ref-260"/> +<_x0031_ href="#ref-261"/> +<_x0032_ href="#ref-262"/> +<_x0033_ href="#ref-263"/> +<_x0034_ href="#ref-264"/> +<_x0035_ href="#ref-265"/> +<_x0036_ href="#ref-266"/> +<_x0037_ href="#ref-267"/> +<_x0038_ href="#ref-268"/> + + +fire 11 +0 +9 +<_x0030_ href="#ref-270"/> +<_x0031_ href="#ref-271"/> +<_x0032_ href="#ref-272"/> +<_x0033_ href="#ref-273"/> +<_x0034_ href="#ref-274"/> +<_x0035_ href="#ref-275"/> +<_x0036_ href="#ref-276"/> +<_x0037_ href="#ref-277"/> +<_x0038_ href="#ref-278"/> + + +fire 12 +0 +9 +<_x0030_ href="#ref-280"/> +<_x0031_ href="#ref-281"/> +<_x0032_ href="#ref-282"/> +<_x0033_ href="#ref-283"/> +<_x0034_ href="#ref-284"/> +<_x0035_ href="#ref-285"/> +<_x0036_ href="#ref-286"/> +<_x0037_ href="#ref-287"/> +<_x0038_ href="#ref-288"/> + + +fire 13 +0 +9 +<_x0030_ href="#ref-290"/> +<_x0031_ href="#ref-291"/> +<_x0032_ href="#ref-292"/> +<_x0033_ href="#ref-293"/> +<_x0034_ href="#ref-294"/> +<_x0035_ href="#ref-295"/> +<_x0036_ href="#ref-296"/> +<_x0037_ href="#ref-297"/> +<_x0038_ href="#ref-298"/> + + +fire 14 +0 +9 +<_x0030_ href="#ref-300"/> +<_x0031_ href="#ref-301"/> +<_x0032_ href="#ref-302"/> +<_x0033_ href="#ref-303"/> +<_x0034_ href="#ref-304"/> +<_x0035_ href="#ref-305"/> +<_x0036_ href="#ref-306"/> +<_x0037_ href="#ref-307"/> +<_x0038_ href="#ref-308"/> + + +fire 15 +0 +9 +<_x0030_ href="#ref-310"/> +<_x0031_ href="#ref-311"/> +<_x0032_ href="#ref-312"/> +<_x0033_ href="#ref-313"/> +<_x0034_ href="#ref-314"/> +<_x0035_ href="#ref-315"/> +<_x0036_ href="#ref-316"/> +<_x0037_ href="#ref-317"/> +<_x0038_ href="#ref-318"/> + + +icon +0 +1 +<_x0030_ href="#ref-320"/> + + +help +0 +1 +<_x0030_ href="#ref-322"/> + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +-18 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +11 +-17 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +17 +-14 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +19 +-6 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +19 +-1 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +18 +6 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +16 +15 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +11 +19 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +21 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-11 +18 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-18 +15 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-21 +6 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-21 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-21 +-8 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-17 +-14 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-12 +-18 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + +<_x0030_ href="#ref-478"/> + + +<_x0030_ href="#ref-479"/> + + +<_x0030_ href="#ref-480"/> + + +<_x0030_ href="#ref-481"/> + + +<_x0030_ href="#ref-482"/> + + +<_x0030_ href="#ref-483"/> + + +<_x0030_ href="#ref-484"/> + + +<_x0030_ href="#ref-485"/> + + +<_x0030_ href="#ref-486"/> + + +<_x0030_ href="#ref-487"/> + + +<_x0030_ href="#ref-488"/> + + +<_x0030_ href="#ref-489"/> + + +<_x0030_ href="#ref-490"/> + + +<_x0030_ href="#ref-491"/> + + +<_x0030_ href="#ref-492"/> + + +<_x0030_ href="#ref-493"/> + + +<_x0030_ href="#ref-494"/> + + +<_x0030_ href="#ref-495"/> + + +<_x0030_ href="#ref-496"/> + + +<_x0030_ href="#ref-497"/> + + +<_x0030_ href="#ref-498"/> + + +<_x0030_ href="#ref-499"/> + + +<_x0030_ href="#ref-500"/> + + +<_x0030_ href="#ref-501"/> + + +<_x0030_ href="#ref-502"/> + + +<_x0030_ href="#ref-503"/> + + +<_x0030_ href="#ref-504"/> + + +<_x0030_ href="#ref-505"/> + + +<_x0030_ href="#ref-506"/> + + +<_x0030_ href="#ref-507"/> + + +<_x0030_ href="#ref-508"/> + + +<_x0030_ href="#ref-509"/> + + +<_x0030_ href="#ref-510"/> + + +<_x0030_ href="#ref-511"/> + + +<_x0030_ href="#ref-512"/> + + +<_x0030_ href="#ref-513"/> + + +<_x0030_ href="#ref-514"/> + + +<_x0030_ href="#ref-515"/> + + +<_x0030_ href="#ref-516"/> + + +<_x0030_ href="#ref-517"/> + + +<_x0030_ href="#ref-518"/> + + +<_x0030_ href="#ref-519"/> + + +<_x0030_ href="#ref-520"/> + + +<_x0030_ href="#ref-521"/> + + +<_x0030_ href="#ref-522"/> + + +<_x0030_ href="#ref-523"/> + + +<_x0030_ href="#ref-524"/> + + +<_x0030_ href="#ref-525"/> + + +<_x0030_ href="#ref-526"/> + + +<_x0030_ href="#ref-527"/> + + +<_x0030_ href="#ref-528"/> + + +<_x0030_ href="#ref-529"/> + + +<_x0030_ href="#ref-530"/> + + +<_x0030_ href="#ref-531"/> + + +<_x0030_ href="#ref-532"/> + + +<_x0030_ href="#ref-533"/> + + +<_x0030_ href="#ref-534"/> + + +<_x0030_ href="#ref-535"/> + + +<_x0030_ href="#ref-536"/> + + +<_x0030_ href="#ref-537"/> + + +<_x0030_ href="#ref-538"/> + + +<_x0030_ href="#ref-539"/> + + +<_x0030_ href="#ref-540"/> + + +<_x0030_ href="#ref-541"/> + + +<_x0030_ href="#ref-542"/> + + +<_x0030_ href="#ref-543"/> + + +<_x0030_ href="#ref-544"/> + + +<_x0030_ href="#ref-545"/> + + +<_x0030_ href="#ref-546"/> + + +<_x0030_ href="#ref-547"/> + + +<_x0030_ href="#ref-548"/> + + +<_x0030_ href="#ref-549"/> + + +<_x0030_ href="#ref-550"/> + + +<_x0030_ href="#ref-551"/> + + +<_x0030_ href="#ref-552"/> + + +<_x0030_ href="#ref-553"/> + + +<_x0030_ href="#ref-554"/> + + +<_x0030_ href="#ref-555"/> + + +<_x0030_ href="#ref-556"/> + + +<_x0030_ href="#ref-557"/> + + +<_x0030_ href="#ref-558"/> + + +<_x0030_ href="#ref-559"/> + + +<_x0030_ href="#ref-560"/> + + +<_x0030_ href="#ref-561"/> + + +<_x0030_ href="#ref-562"/> + + +<_x0030_ href="#ref-563"/> + + +<_x0030_ href="#ref-564"/> + + +<_x0030_ href="#ref-565"/> + + +<_x0030_ href="#ref-566"/> + + +<_x0030_ href="#ref-567"/> + + +<_x0030_ href="#ref-568"/> + + +<_x0030_ href="#ref-569"/> + + +<_x0030_ href="#ref-570"/> + + +<_x0030_ href="#ref-571"/> + + +<_x0030_ href="#ref-572"/> + + +<_x0030_ href="#ref-573"/> + + +<_x0030_ href="#ref-574"/> + + +<_x0030_ href="#ref-575"/> + + +<_x0030_ href="#ref-576"/> + + +<_x0030_ href="#ref-577"/> + + +<_x0030_ href="#ref-578"/> + + +<_x0030_ href="#ref-579"/> + + +<_x0030_ href="#ref-580"/> + + +<_x0030_ href="#ref-581"/> + + +<_x0030_ href="#ref-582"/> + + +<_x0030_ href="#ref-583"/> + + +<_x0030_ href="#ref-584"/> + + +<_x0030_ href="#ref-585"/> + + +<_x0030_ href="#ref-586"/> + + +<_x0030_ href="#ref-587"/> + + +<_x0030_ href="#ref-588"/> + + +<_x0030_ href="#ref-589"/> + + +<_x0030_ href="#ref-590"/> + + +<_x0030_ href="#ref-591"/> + + +<_x0030_ href="#ref-592"/> + + +<_x0030_ href="#ref-593"/> + + +<_x0030_ href="#ref-594"/> + + +<_x0030_ href="#ref-595"/> + + +<_x0030_ href="#ref-596"/> + + +<_x0030_ href="#ref-597"/> + + +<_x0030_ href="#ref-598"/> + + +<_x0030_ href="#ref-599"/> + + +<_x0030_ href="#ref-600"/> + + +<_x0030_ href="#ref-601"/> + + +<_x0030_ href="#ref-602"/> + + +<_x0030_ href="#ref-603"/> + + +<_x0030_ href="#ref-604"/> + + +<_x0030_ href="#ref-605"/> + + +<_x0030_ href="#ref-606"/> + + +<_x0030_ href="#ref-607"/> + + +<_x0030_ href="#ref-608"/> + + +<_x0030_ href="#ref-609"/> + + +<_x0030_ href="#ref-610"/> + + +<_x0030_ href="#ref-611"/> + + +<_x0030_ href="#ref-612"/> + + +<_x0030_ href="#ref-613"/> + + +<_x0030_ href="#ref-614"/> + + +<_x0030_ href="#ref-615"/> + + +<_x0030_ href="#ref-616"/> + + +<_x0030_ href="#ref-617"/> + + +<_x0030_ href="#ref-618"/> + + +<_x0030_ href="#ref-619"/> + + +<_x0030_ href="#ref-620"/> + + +<_x0030_ href="#ref-621"/> + + +<_x0030_ href="#ref-622"/> + + +<_x0030_ href="#ref-623"/> + + +<_x0030_ href="#ref-624"/> + + +<_x0030_ href="#ref-625"/> + + +<_x0030_ href="#ref-626"/> + + +<_x0030_ href="#ref-627"/> + + +<_x0030_ href="#ref-628"/> + + +<_x0030_ href="#ref-629"/> + + +<_x0030_ href="#ref-630"/> +<_x0031_ href="#ref-631"/> + + +<_x0030_ href="#ref-632"/> +<_x0031_ href="#ref-633"/> + + + +13 +14 + + + + + +11 +13 + + + + + +11 +11 + + + + + +10 +9 + + + + + +13 +8 + + + + + +16 +8 + + + + + +17 +10 + + + + + +16 +14 + + + + + +15 +12 + + + + + +15 +12 + + + + + +15 +12 + + + + + +15 +12 + + + + + +15 +12 + + + + + +15 +12 + + + + + +15 +12 + + + + + +15 +12 + + + + + +15 +12 + + + + + +15 +12 + + + + + +15 +12 + + + + + +15 +12 + + + + + +15 +12 + + + + + +15 +12 + + + + + +15 +12 + + + + + +15 +12 + + + + + +15 +12 + + + + + +15 +12 + + + + + +15 +13 + + + + + +15 +13 + + + + + +15 +13 + + + + + +15 +13 + + + + + +15 +13 + + + + + +15 +13 + + + + + +15 +13 + + + + + +15 +13 + + + + + +15 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +16 +13 + + + + + +15 +13 + + + + + +15 +13 + + + + + +15 +13 + + + + + +15 +13 + + + + + +15 +13 + + + + + +15 +13 + + + + + +15 +13 + + + + + +15 +13 + + + + + +15 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +15 +13 + + + + + +15 +13 + + + + + +15 +13 + + + + + +15 +13 + + + + + +15 +13 + + + + + +15 +13 + + + + + +15 +13 + + + + + +15 +13 + + + + + +15 +13 + + + + + +9 +5 + + + + + +4 +4 + + + + + +12 +16 + + + + + +13 +13 + + + + + diff --git a/data/art824/tmac/tmac_base_0_0.png b/data/art824/tmac/tmac_base_0_0.png new file mode 100644 index 0000000..e5fd518 Binary files /dev/null and b/data/art824/tmac/tmac_base_0_0.png differ diff --git a/data/art824/tmac/tmac_base_1_0.png b/data/art824/tmac/tmac_base_1_0.png new file mode 100644 index 0000000..9a67e2b Binary files /dev/null and b/data/art824/tmac/tmac_base_1_0.png differ diff --git a/data/art824/tmac/tmac_base_2_0.png b/data/art824/tmac/tmac_base_2_0.png new file mode 100644 index 0000000..eb36ad1 Binary files /dev/null and b/data/art824/tmac/tmac_base_2_0.png differ diff --git a/data/art824/tmac/tmac_base_3_0.png b/data/art824/tmac/tmac_base_3_0.png new file mode 100644 index 0000000..87277fd Binary files /dev/null and b/data/art824/tmac/tmac_base_3_0.png differ diff --git a/data/art824/tmac/tmac_base_4_0.png b/data/art824/tmac/tmac_base_4_0.png new file mode 100644 index 0000000..75b0e2c Binary files /dev/null and b/data/art824/tmac/tmac_base_4_0.png differ diff --git a/data/art824/tmac/tmac_base_5_0.png b/data/art824/tmac/tmac_base_5_0.png new file mode 100644 index 0000000..a2dc56b Binary files /dev/null and b/data/art824/tmac/tmac_base_5_0.png differ diff --git a/data/art824/tmac/tmac_base_6_0.png b/data/art824/tmac/tmac_base_6_0.png new file mode 100644 index 0000000..3e1e1ad Binary files /dev/null and b/data/art824/tmac/tmac_base_6_0.png differ diff --git a/data/art824/tmac/tmac_base_7_0.png b/data/art824/tmac/tmac_base_7_0.png new file mode 100644 index 0000000..7c39fb9 Binary files /dev/null and b/data/art824/tmac/tmac_base_7_0.png differ diff --git a/data/art824/tmac/tmac_fire_00_0.png b/data/art824/tmac/tmac_fire_00_0.png new file mode 100644 index 0000000..57e0555 Binary files /dev/null and b/data/art824/tmac/tmac_fire_00_0.png differ diff --git a/data/art824/tmac/tmac_fire_00_1.png b/data/art824/tmac/tmac_fire_00_1.png new file mode 100644 index 0000000..d58417c Binary files /dev/null and b/data/art824/tmac/tmac_fire_00_1.png differ diff --git a/data/art824/tmac/tmac_fire_00_2.png b/data/art824/tmac/tmac_fire_00_2.png new file mode 100644 index 0000000..d6603ce Binary files /dev/null and b/data/art824/tmac/tmac_fire_00_2.png differ diff --git a/data/art824/tmac/tmac_fire_01_0.png b/data/art824/tmac/tmac_fire_01_0.png new file mode 100644 index 0000000..83ac10e Binary files /dev/null and b/data/art824/tmac/tmac_fire_01_0.png differ diff --git a/data/art824/tmac/tmac_fire_01_1.png b/data/art824/tmac/tmac_fire_01_1.png new file mode 100644 index 0000000..47bbd71 Binary files /dev/null and b/data/art824/tmac/tmac_fire_01_1.png differ diff --git a/data/art824/tmac/tmac_fire_01_2.png b/data/art824/tmac/tmac_fire_01_2.png new file mode 100644 index 0000000..deaf872 Binary files /dev/null and b/data/art824/tmac/tmac_fire_01_2.png differ diff --git a/data/art824/tmac/tmac_fire_02_0.png b/data/art824/tmac/tmac_fire_02_0.png new file mode 100644 index 0000000..62981aa Binary files /dev/null and b/data/art824/tmac/tmac_fire_02_0.png differ diff --git a/data/art824/tmac/tmac_fire_02_1.png b/data/art824/tmac/tmac_fire_02_1.png new file mode 100644 index 0000000..2f04729 Binary files /dev/null and b/data/art824/tmac/tmac_fire_02_1.png differ diff --git a/data/art824/tmac/tmac_fire_02_2.png b/data/art824/tmac/tmac_fire_02_2.png new file mode 100644 index 0000000..b910669 Binary files /dev/null and b/data/art824/tmac/tmac_fire_02_2.png differ diff --git a/data/art824/tmac/tmac_fire_03_0.png b/data/art824/tmac/tmac_fire_03_0.png new file mode 100644 index 0000000..e422b82 Binary files /dev/null and b/data/art824/tmac/tmac_fire_03_0.png differ diff --git a/data/art824/tmac/tmac_fire_03_1.png b/data/art824/tmac/tmac_fire_03_1.png new file mode 100644 index 0000000..feccc55 Binary files /dev/null and b/data/art824/tmac/tmac_fire_03_1.png differ diff --git a/data/art824/tmac/tmac_fire_03_2.png b/data/art824/tmac/tmac_fire_03_2.png new file mode 100644 index 0000000..dd51f55 Binary files /dev/null and b/data/art824/tmac/tmac_fire_03_2.png differ diff --git a/data/art824/tmac/tmac_fire_04_0.png b/data/art824/tmac/tmac_fire_04_0.png new file mode 100644 index 0000000..eb5afdc Binary files /dev/null and b/data/art824/tmac/tmac_fire_04_0.png differ diff --git a/data/art824/tmac/tmac_fire_04_1.png b/data/art824/tmac/tmac_fire_04_1.png new file mode 100644 index 0000000..d4f3aaa Binary files /dev/null and b/data/art824/tmac/tmac_fire_04_1.png differ diff --git a/data/art824/tmac/tmac_fire_04_2.png b/data/art824/tmac/tmac_fire_04_2.png new file mode 100644 index 0000000..aba623f Binary files /dev/null and b/data/art824/tmac/tmac_fire_04_2.png differ diff --git a/data/art824/tmac/tmac_fire_05_0.png b/data/art824/tmac/tmac_fire_05_0.png new file mode 100644 index 0000000..4308aab Binary files /dev/null and b/data/art824/tmac/tmac_fire_05_0.png differ diff --git a/data/art824/tmac/tmac_fire_05_1.png b/data/art824/tmac/tmac_fire_05_1.png new file mode 100644 index 0000000..26f9c7e Binary files /dev/null and b/data/art824/tmac/tmac_fire_05_1.png differ diff --git a/data/art824/tmac/tmac_fire_05_2.png b/data/art824/tmac/tmac_fire_05_2.png new file mode 100644 index 0000000..f19983f Binary files /dev/null and b/data/art824/tmac/tmac_fire_05_2.png differ diff --git a/data/art824/tmac/tmac_fire_06_0.png b/data/art824/tmac/tmac_fire_06_0.png new file mode 100644 index 0000000..5b2fc2e Binary files /dev/null and b/data/art824/tmac/tmac_fire_06_0.png differ diff --git a/data/art824/tmac/tmac_fire_06_1.png b/data/art824/tmac/tmac_fire_06_1.png new file mode 100644 index 0000000..ee60c44 Binary files /dev/null and b/data/art824/tmac/tmac_fire_06_1.png differ diff --git a/data/art824/tmac/tmac_fire_06_2.png b/data/art824/tmac/tmac_fire_06_2.png new file mode 100644 index 0000000..206a4cd Binary files /dev/null and b/data/art824/tmac/tmac_fire_06_2.png differ diff --git a/data/art824/tmac/tmac_fire_07_0.png b/data/art824/tmac/tmac_fire_07_0.png new file mode 100644 index 0000000..f194662 Binary files /dev/null and b/data/art824/tmac/tmac_fire_07_0.png differ diff --git a/data/art824/tmac/tmac_fire_07_1.png b/data/art824/tmac/tmac_fire_07_1.png new file mode 100644 index 0000000..1a5ca78 Binary files /dev/null and b/data/art824/tmac/tmac_fire_07_1.png differ diff --git a/data/art824/tmac/tmac_fire_07_2.png b/data/art824/tmac/tmac_fire_07_2.png new file mode 100644 index 0000000..65764cf Binary files /dev/null and b/data/art824/tmac/tmac_fire_07_2.png differ diff --git a/data/art824/tmac/tmac_fire_08_0.png b/data/art824/tmac/tmac_fire_08_0.png new file mode 100644 index 0000000..a4f1d19 Binary files /dev/null and b/data/art824/tmac/tmac_fire_08_0.png differ diff --git a/data/art824/tmac/tmac_fire_08_1.png b/data/art824/tmac/tmac_fire_08_1.png new file mode 100644 index 0000000..42eebde Binary files /dev/null and b/data/art824/tmac/tmac_fire_08_1.png differ diff --git a/data/art824/tmac/tmac_fire_08_2.png b/data/art824/tmac/tmac_fire_08_2.png new file mode 100644 index 0000000..7ff2d7e Binary files /dev/null and b/data/art824/tmac/tmac_fire_08_2.png differ diff --git a/data/art824/tmac/tmac_fire_09_0.png b/data/art824/tmac/tmac_fire_09_0.png new file mode 100644 index 0000000..be2488e Binary files /dev/null and b/data/art824/tmac/tmac_fire_09_0.png differ diff --git a/data/art824/tmac/tmac_fire_09_1.png b/data/art824/tmac/tmac_fire_09_1.png new file mode 100644 index 0000000..7fff143 Binary files /dev/null and b/data/art824/tmac/tmac_fire_09_1.png differ diff --git a/data/art824/tmac/tmac_fire_09_2.png b/data/art824/tmac/tmac_fire_09_2.png new file mode 100644 index 0000000..22b432f Binary files /dev/null and b/data/art824/tmac/tmac_fire_09_2.png differ diff --git a/data/art824/tmac/tmac_fire_10_0.png b/data/art824/tmac/tmac_fire_10_0.png new file mode 100644 index 0000000..7c40049 Binary files /dev/null and b/data/art824/tmac/tmac_fire_10_0.png differ diff --git a/data/art824/tmac/tmac_fire_10_1.png b/data/art824/tmac/tmac_fire_10_1.png new file mode 100644 index 0000000..8c6a53f Binary files /dev/null and b/data/art824/tmac/tmac_fire_10_1.png differ diff --git a/data/art824/tmac/tmac_fire_10_2.png b/data/art824/tmac/tmac_fire_10_2.png new file mode 100644 index 0000000..ef18a1b Binary files /dev/null and b/data/art824/tmac/tmac_fire_10_2.png differ diff --git a/data/art824/tmac/tmac_fire_11_0.png b/data/art824/tmac/tmac_fire_11_0.png new file mode 100644 index 0000000..2797678 Binary files /dev/null and b/data/art824/tmac/tmac_fire_11_0.png differ diff --git a/data/art824/tmac/tmac_fire_11_1.png b/data/art824/tmac/tmac_fire_11_1.png new file mode 100644 index 0000000..c51b75d Binary files /dev/null and b/data/art824/tmac/tmac_fire_11_1.png differ diff --git a/data/art824/tmac/tmac_fire_11_2.png b/data/art824/tmac/tmac_fire_11_2.png new file mode 100644 index 0000000..7e45143 Binary files /dev/null and b/data/art824/tmac/tmac_fire_11_2.png differ diff --git a/data/art824/tmac/tmac_fire_12_0.png b/data/art824/tmac/tmac_fire_12_0.png new file mode 100644 index 0000000..5a664d3 Binary files /dev/null and b/data/art824/tmac/tmac_fire_12_0.png differ diff --git a/data/art824/tmac/tmac_fire_12_1.png b/data/art824/tmac/tmac_fire_12_1.png new file mode 100644 index 0000000..dffa82b Binary files /dev/null and b/data/art824/tmac/tmac_fire_12_1.png differ diff --git a/data/art824/tmac/tmac_fire_12_2.png b/data/art824/tmac/tmac_fire_12_2.png new file mode 100644 index 0000000..dd4f8b9 Binary files /dev/null and b/data/art824/tmac/tmac_fire_12_2.png differ diff --git a/data/art824/tmac/tmac_fire_13_0.png b/data/art824/tmac/tmac_fire_13_0.png new file mode 100644 index 0000000..05c4873 Binary files /dev/null and b/data/art824/tmac/tmac_fire_13_0.png differ diff --git a/data/art824/tmac/tmac_fire_13_1.png b/data/art824/tmac/tmac_fire_13_1.png new file mode 100644 index 0000000..0f5dd2a Binary files /dev/null and b/data/art824/tmac/tmac_fire_13_1.png differ diff --git a/data/art824/tmac/tmac_fire_13_2.png b/data/art824/tmac/tmac_fire_13_2.png new file mode 100644 index 0000000..d057ee3 Binary files /dev/null and b/data/art824/tmac/tmac_fire_13_2.png differ diff --git a/data/art824/tmac/tmac_fire_14_0.png b/data/art824/tmac/tmac_fire_14_0.png new file mode 100644 index 0000000..9765173 Binary files /dev/null and b/data/art824/tmac/tmac_fire_14_0.png differ diff --git a/data/art824/tmac/tmac_fire_14_1.png b/data/art824/tmac/tmac_fire_14_1.png new file mode 100644 index 0000000..cd6fa10 Binary files /dev/null and b/data/art824/tmac/tmac_fire_14_1.png differ diff --git a/data/art824/tmac/tmac_fire_14_2.png b/data/art824/tmac/tmac_fire_14_2.png new file mode 100644 index 0000000..590136f Binary files /dev/null and b/data/art824/tmac/tmac_fire_14_2.png differ diff --git a/data/art824/tmac/tmac_fire_15_0.png b/data/art824/tmac/tmac_fire_15_0.png new file mode 100644 index 0000000..42a6e0f Binary files /dev/null and b/data/art824/tmac/tmac_fire_15_0.png differ diff --git a/data/art824/tmac/tmac_fire_15_1.png b/data/art824/tmac/tmac_fire_15_1.png new file mode 100644 index 0000000..3944193 Binary files /dev/null and b/data/art824/tmac/tmac_fire_15_1.png differ diff --git a/data/art824/tmac/tmac_fire_15_2.png b/data/art824/tmac/tmac_fire_15_2.png new file mode 100644 index 0000000..bb6de14 Binary files /dev/null and b/data/art824/tmac/tmac_fire_15_2.png differ diff --git a/data/art824/troc.amx b/data/art824/troc.amx new file mode 100644 index 0000000..6defa94 --- /dev/null +++ b/data/art824/troc.amx @@ -0,0 +1,1512 @@ + + + + + +80 +true + + +<_x0030_ href="#ref-5"/> +<_x0031_ href="#ref-6"/> +<_x0032_ href="#ref-7"/> +<_x0033_ href="#ref-8"/> +<_x0034_ href="#ref-9"/> +<_x0035_ href="#ref-10"/> +<_x0036_ href="#ref-11"/> +<_x0037_ href="#ref-12"/> +<_x0038_ href="#ref-13"/> +<_x0039_ href="#ref-14"/> +<_x0031_0 href="#ref-15"/> +<_x0031_1 href="#ref-16"/> +<_x0031_2 href="#ref-17"/> +<_x0031_3 href="#ref-18"/> +<_x0031_4 href="#ref-19"/> +<_x0031_5 href="#ref-20"/> +<_x0031_6 href="#ref-21"/> +<_x0031_7 href="#ref-22"/> +<_x0031_8 href="#ref-23"/> +<_x0031_9 href="#ref-24"/> +<_x0032_0 href="#ref-25"/> +<_x0032_1 href="#ref-26"/> +<_x0032_2 href="#ref-27"/> +<_x0032_3 href="#ref-28"/> +<_x0032_4 href="#ref-29"/> +<_x0032_5 href="#ref-30"/> +<_x0032_6 href="#ref-31"/> +<_x0032_7 href="#ref-32"/> +<_x0032_8 href="#ref-33"/> +<_x0032_9 href="#ref-34"/> +<_x0033_0 href="#ref-35"/> +<_x0033_1 href="#ref-36"/> +<_x0033_2 href="#ref-37"/> +<_x0033_3 href="#ref-38"/> +<_x0033_4 href="#ref-39"/> +<_x0033_5 href="#ref-40"/> +<_x0033_6 href="#ref-41"/> +<_x0033_7 href="#ref-42"/> +<_x0033_8 href="#ref-43"/> +<_x0033_9 href="#ref-44"/> +<_x0034_0 href="#ref-45"/> +<_x0034_1 href="#ref-46"/> +<_x0034_2 href="#ref-47"/> +<_x0034_3 href="#ref-48"/> +<_x0034_4 href="#ref-49"/> +<_x0034_5 href="#ref-50"/> +<_x0034_6 href="#ref-51"/> +<_x0034_7 href="#ref-52"/> +<_x0034_8 href="#ref-53"/> +<_x0034_9 href="#ref-54"/> +<_x0035_0 href="#ref-55"/> +<_x0035_1 href="#ref-56"/> +<_x0035_2 href="#ref-57"/> +<_x0035_3 href="#ref-58"/> +<_x0035_4 href="#ref-59"/> +<_x0035_5 href="#ref-60"/> + + +<_x0030_ href="#ref-61"/> +<_x0031_ href="#ref-62"/> +<_x0032_ href="#ref-63"/> +<_x0033_ href="#ref-64"/> +<_x0034_ href="#ref-65"/> +<_x0035_ href="#ref-66"/> +<_x0036_ href="#ref-67"/> +<_x0037_ href="#ref-68"/> +<_x0038_ href="#ref-69"/> +<_x0039_ href="#ref-70"/> +<_x0031_0 href="#ref-71"/> +<_x0031_1 href="#ref-72"/> +<_x0031_2 href="#ref-73"/> +<_x0031_3 href="#ref-74"/> +<_x0031_4 href="#ref-75"/> +<_x0031_5 href="#ref-76"/> +<_x0031_6 href="#ref-77"/> +<_x0031_7 href="#ref-78"/> +<_x0031_8 href="#ref-79"/> +<_x0031_9 href="#ref-80"/> +<_x0032_0 href="#ref-81"/> +<_x0032_1 href="#ref-82"/> +<_x0032_2 href="#ref-83"/> +<_x0032_3 href="#ref-84"/> +<_x0032_4 href="#ref-85"/> +<_x0032_5 href="#ref-86"/> + + +troc\troc_base_7_0.png + + +troc\troc_base_1_0.png + + +troc\troc_base_2_0.png + + +troc\troc_base_3_0.png + + +troc\troc_base_4_0.png + + +troc\troc_base_5_0.png + + +troc\troc_base_6_0.png + + +troc\troc_base_0_0.png + + +troc\troc_fire_15_2.png + + +troc\troc_fire_00_1.png + + +troc\troc_fire_00_2.png + + +troc\troc_fire_01_0.png + + +troc\troc_fire_01_1.png + + +troc\troc_fire_01_2.png + + +troc\troc_fire_02_0.png + + +troc\troc_fire_02_1.png + + +troc\troc_fire_02_2.png + + +troc\troc_fire_03_0.png + + +troc\troc_fire_03_1.png + + +troc\troc_fire_03_2.png + + +troc\troc_fire_04_0.png + + +troc\troc_fire_04_1.png + + +troc\troc_fire_04_2.png + + +troc\troc_fire_05_0.png + + +troc\troc_fire_05_1.png + + +troc\troc_fire_05_2.png + + +troc\troc_fire_06_0.png + + +troc\troc_fire_06_1.png + + +troc\troc_fire_06_2.png + + +troc\troc_fire_08_0.png + + +troc\troc_fire_08_1.png + + +troc\troc_fire_08_2.png + + +troc\troc_fire_09_0.png + + +troc\troc_fire_09_1.png + + +troc\troc_fire_09_2.png + + +troc\troc_fire_10_0.png + + +troc\troc_fire_10_1.png + + +troc\troc_fire_10_2.png + + +troc\troc_fire_11_0.png + + +troc\troc_fire_11_1.png + + +troc\troc_fire_11_2.png + + +troc\troc_fire_12_0.png + + +troc\troc_fire_12_1.png + + +troc\troc_fire_13_0.png + + +troc\troc_fire_13_1.png + + +troc\troc_fire_13_2.png + + +troc\troc_fire_14_0.png + + +troc\troc_fire_14_1.png + + +troc\troc_fire_14_2.png + + +troc\troc_fire_15_0.png + + +troc\troc_fire_15_1.png + + +troc\troc_fire_00_0.png + + +troc\troc_fire_07_2.png + + +troc\troc_fire_07_1.png + + +troc\troc_fire_07_0.png + + +troc\troc_fire_12_2.png + + +base 0 +0 +1 +<_x0030_ href="#ref-144"/> + + +base 1 +0 +1 +<_x0030_ href="#ref-146"/> + + +base 2 +0 +1 +<_x0030_ href="#ref-148"/> + + +base 3 +0 +1 +<_x0030_ href="#ref-150"/> + + +base 4 +0 +1 +<_x0030_ href="#ref-152"/> + + +base 5 +0 +1 +<_x0030_ href="#ref-154"/> + + +base 6 +0 +1 +<_x0030_ href="#ref-156"/> + + +base 7 +0 +1 +<_x0030_ href="#ref-158"/> + + +fire 00 +0 +3 +<_x0030_ href="#ref-160"/> +<_x0031_ href="#ref-161"/> +<_x0032_ href="#ref-162"/> + + +fire 01 +0 +3 +<_x0030_ href="#ref-164"/> +<_x0031_ href="#ref-165"/> +<_x0032_ href="#ref-166"/> + + +fire 02 +0 +3 +<_x0030_ href="#ref-168"/> +<_x0031_ href="#ref-169"/> +<_x0032_ href="#ref-170"/> + + +fire 03 +0 +3 +<_x0030_ href="#ref-172"/> +<_x0031_ href="#ref-173"/> +<_x0032_ href="#ref-174"/> + + +fire 04 +0 +3 +<_x0030_ href="#ref-176"/> +<_x0031_ href="#ref-177"/> +<_x0032_ href="#ref-178"/> + + +fire 05 +0 +3 +<_x0030_ href="#ref-180"/> +<_x0031_ href="#ref-181"/> +<_x0032_ href="#ref-182"/> + + +fire 06 +0 +3 +<_x0030_ href="#ref-184"/> +<_x0031_ href="#ref-185"/> +<_x0032_ href="#ref-186"/> + + +fire 07 +0 +3 +<_x0030_ href="#ref-188"/> +<_x0031_ href="#ref-189"/> +<_x0032_ href="#ref-190"/> + + +fire 08 +0 +3 +<_x0030_ href="#ref-192"/> +<_x0031_ href="#ref-193"/> +<_x0032_ href="#ref-194"/> + + +fire 09 +0 +3 +<_x0030_ href="#ref-196"/> +<_x0031_ href="#ref-197"/> +<_x0032_ href="#ref-198"/> + + +fire 10 +0 +3 +<_x0030_ href="#ref-200"/> +<_x0031_ href="#ref-201"/> +<_x0032_ href="#ref-202"/> + + +fire 11 +0 +3 +<_x0030_ href="#ref-204"/> +<_x0031_ href="#ref-205"/> +<_x0032_ href="#ref-206"/> + + +fire 12 +0 +3 +<_x0030_ href="#ref-208"/> +<_x0031_ href="#ref-209"/> +<_x0032_ href="#ref-210"/> + + +fire 13 +0 +3 +<_x0030_ href="#ref-212"/> +<_x0031_ href="#ref-213"/> +<_x0032_ href="#ref-214"/> + + +fire 14 +0 +3 +<_x0030_ href="#ref-216"/> +<_x0031_ href="#ref-217"/> +<_x0032_ href="#ref-218"/> + + +fire 15 +0 +3 +<_x0030_ href="#ref-220"/> +<_x0031_ href="#ref-221"/> +<_x0032_ href="#ref-222"/> + + +icon +0 +1 +<_x0030_ href="#ref-224"/> + + +help +0 +1 +<_x0030_ href="#ref-226"/> + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +1 +-13 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +6 +-12 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +10 +-9 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +13 +-6 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +13 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +13 +7 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +9 +10 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +7 +11 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +13 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-6 +10 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-9 +10 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-13 +6 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-13 +1 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-12 +-3 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-10 +-9 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +-5 +-11 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + +<_x0030_ href="#ref-286"/> + + +<_x0030_ href="#ref-287"/> + + +<_x0030_ href="#ref-288"/> + + +<_x0030_ href="#ref-289"/> + + +<_x0030_ href="#ref-290"/> + + +<_x0030_ href="#ref-291"/> + + +<_x0030_ href="#ref-292"/> + + +<_x0030_ href="#ref-293"/> + + +<_x0030_ href="#ref-294"/> + + +<_x0030_ href="#ref-295"/> + + +<_x0030_ href="#ref-296"/> + + +<_x0030_ href="#ref-297"/> + + +<_x0030_ href="#ref-298"/> + + +<_x0030_ href="#ref-299"/> + + +<_x0030_ href="#ref-300"/> + + +<_x0030_ href="#ref-301"/> + + +<_x0030_ href="#ref-302"/> + + +<_x0030_ href="#ref-303"/> + + +<_x0030_ href="#ref-304"/> + + +<_x0030_ href="#ref-305"/> + + +<_x0030_ href="#ref-306"/> + + +<_x0030_ href="#ref-307"/> + + +<_x0030_ href="#ref-308"/> + + +<_x0030_ href="#ref-309"/> + + +<_x0030_ href="#ref-310"/> + + +<_x0030_ href="#ref-311"/> + + +<_x0030_ href="#ref-312"/> + + +<_x0030_ href="#ref-313"/> + + +<_x0030_ href="#ref-314"/> + + +<_x0030_ href="#ref-315"/> + + +<_x0030_ href="#ref-316"/> + + +<_x0030_ href="#ref-317"/> + + +<_x0030_ href="#ref-318"/> + + +<_x0030_ href="#ref-319"/> + + +<_x0030_ href="#ref-320"/> + + +<_x0030_ href="#ref-321"/> + + +<_x0030_ href="#ref-322"/> + + +<_x0030_ href="#ref-323"/> + + +<_x0030_ href="#ref-324"/> + + +<_x0030_ href="#ref-325"/> + + +<_x0030_ href="#ref-326"/> + + +<_x0030_ href="#ref-327"/> + + +<_x0030_ href="#ref-328"/> + + +<_x0030_ href="#ref-329"/> + + +<_x0030_ href="#ref-330"/> + + +<_x0030_ href="#ref-331"/> + + +<_x0030_ href="#ref-332"/> + + +<_x0030_ href="#ref-333"/> + + +<_x0030_ href="#ref-334"/> + + +<_x0030_ href="#ref-335"/> + + +<_x0030_ href="#ref-336"/> + + +<_x0030_ href="#ref-337"/> + + +<_x0030_ href="#ref-338"/> + + +<_x0030_ href="#ref-339"/> + + +<_x0030_ href="#ref-340"/> + + +<_x0030_ href="#ref-341"/> + + +<_x0030_ href="#ref-342"/> +<_x0031_ href="#ref-343"/> + + +<_x0030_ href="#ref-344"/> +<_x0031_ href="#ref-345"/> + + + +21 +21 + + + + + +19 +19 + + + + + +20 +19 + + + + + +20 +17 + + + + + +20 +20 + + + + + +20 +19 + + + + + +20 +20 + + + + + +22 +20 + + + + + +15 +14 + + + + + +15 +14 + + + + + +15 +14 + + + + + +15 +15 + + + + + +15 +15 + + + + + +15 +15 + + + + + +14 +15 + + + + + +14 +15 + + + + + +14 +15 + + + + + +14 +15 + + + + + +14 +15 + + + + + +14 +15 + + + + + +14 +15 + + + + + +14 +15 + + + + + +14 +15 + + + + + +14 +14 + + + + + +14 +14 + + + + + +14 +14 + + + + + +14 +14 + + + + + +14 +14 + + + + + +14 +14 + + + + + +13 +14 + + + + + +13 +14 + + + + + +13 +14 + + + + + +14 +14 + + + + + +14 +14 + + + + + +14 +14 + + + + + +13 +14 + + + + + +13 +14 + + + + + +13 +14 + + + + + +13 +14 + + + + + +13 +14 + + + + + +13 +14 + + + + + +14 +14 + + + + + +14 +14 + + + + + +14 +14 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +12 + + + + + +14 +12 + + + + + +14 +12 + + + + + +14 +12 + + + + + +14 +12 + + + + + +14 +12 + + + + + +14 +13 + + + + + +14 +13 + + + + + +14 +13 + + + + + +0 +6 + + + + + +6 +10 + + + + + +16 +11 + + + + + +20 +20 + + + + + diff --git a/data/art824/troc/troc_base_0_0.png b/data/art824/troc/troc_base_0_0.png new file mode 100644 index 0000000..c57feaf Binary files /dev/null and b/data/art824/troc/troc_base_0_0.png differ diff --git a/data/art824/troc/troc_base_1_0.png b/data/art824/troc/troc_base_1_0.png new file mode 100644 index 0000000..c8bbbdb Binary files /dev/null and b/data/art824/troc/troc_base_1_0.png differ diff --git a/data/art824/troc/troc_base_2_0.png b/data/art824/troc/troc_base_2_0.png new file mode 100644 index 0000000..8591dd6 Binary files /dev/null and b/data/art824/troc/troc_base_2_0.png differ diff --git a/data/art824/troc/troc_base_3_0.png b/data/art824/troc/troc_base_3_0.png new file mode 100644 index 0000000..e680fac Binary files /dev/null and b/data/art824/troc/troc_base_3_0.png differ diff --git a/data/art824/troc/troc_base_4_0.png b/data/art824/troc/troc_base_4_0.png new file mode 100644 index 0000000..8ee747c Binary files /dev/null and b/data/art824/troc/troc_base_4_0.png differ diff --git a/data/art824/troc/troc_base_5_0.png b/data/art824/troc/troc_base_5_0.png new file mode 100644 index 0000000..a76f502 Binary files /dev/null and b/data/art824/troc/troc_base_5_0.png differ diff --git a/data/art824/troc/troc_base_6_0.png b/data/art824/troc/troc_base_6_0.png new file mode 100644 index 0000000..01bae6c Binary files /dev/null and b/data/art824/troc/troc_base_6_0.png differ diff --git a/data/art824/troc/troc_base_7_0.png b/data/art824/troc/troc_base_7_0.png new file mode 100644 index 0000000..06b8e9b Binary files /dev/null and b/data/art824/troc/troc_base_7_0.png differ diff --git a/data/art824/troc/troc_fire_00_0.png b/data/art824/troc/troc_fire_00_0.png new file mode 100644 index 0000000..5c54551 Binary files /dev/null and b/data/art824/troc/troc_fire_00_0.png differ diff --git a/data/art824/troc/troc_fire_00_1.png b/data/art824/troc/troc_fire_00_1.png new file mode 100644 index 0000000..2fdb83f Binary files /dev/null and b/data/art824/troc/troc_fire_00_1.png differ diff --git a/data/art824/troc/troc_fire_00_2.png b/data/art824/troc/troc_fire_00_2.png new file mode 100644 index 0000000..d51f95a Binary files /dev/null and b/data/art824/troc/troc_fire_00_2.png differ diff --git a/data/art824/troc/troc_fire_01_0.png b/data/art824/troc/troc_fire_01_0.png new file mode 100644 index 0000000..02623b4 Binary files /dev/null and b/data/art824/troc/troc_fire_01_0.png differ diff --git a/data/art824/troc/troc_fire_01_1.png b/data/art824/troc/troc_fire_01_1.png new file mode 100644 index 0000000..e19f043 Binary files /dev/null and b/data/art824/troc/troc_fire_01_1.png differ diff --git a/data/art824/troc/troc_fire_01_2.png b/data/art824/troc/troc_fire_01_2.png new file mode 100644 index 0000000..e9556b7 Binary files /dev/null and b/data/art824/troc/troc_fire_01_2.png differ diff --git a/data/art824/troc/troc_fire_02_0.png b/data/art824/troc/troc_fire_02_0.png new file mode 100644 index 0000000..7c85503 Binary files /dev/null and b/data/art824/troc/troc_fire_02_0.png differ diff --git a/data/art824/troc/troc_fire_02_1.png b/data/art824/troc/troc_fire_02_1.png new file mode 100644 index 0000000..002f099 Binary files /dev/null and b/data/art824/troc/troc_fire_02_1.png differ diff --git a/data/art824/troc/troc_fire_02_2.png b/data/art824/troc/troc_fire_02_2.png new file mode 100644 index 0000000..fedc7c4 Binary files /dev/null and b/data/art824/troc/troc_fire_02_2.png differ diff --git a/data/art824/troc/troc_fire_03_0.png b/data/art824/troc/troc_fire_03_0.png new file mode 100644 index 0000000..eac8ef4 Binary files /dev/null and b/data/art824/troc/troc_fire_03_0.png differ diff --git a/data/art824/troc/troc_fire_03_1.png b/data/art824/troc/troc_fire_03_1.png new file mode 100644 index 0000000..4bc3c9b Binary files /dev/null and b/data/art824/troc/troc_fire_03_1.png differ diff --git a/data/art824/troc/troc_fire_03_2.png b/data/art824/troc/troc_fire_03_2.png new file mode 100644 index 0000000..f968e54 Binary files /dev/null and b/data/art824/troc/troc_fire_03_2.png differ diff --git a/data/art824/troc/troc_fire_04_0.png b/data/art824/troc/troc_fire_04_0.png new file mode 100644 index 0000000..d46318e Binary files /dev/null and b/data/art824/troc/troc_fire_04_0.png differ diff --git a/data/art824/troc/troc_fire_04_1.png b/data/art824/troc/troc_fire_04_1.png new file mode 100644 index 0000000..8250e95 Binary files /dev/null and b/data/art824/troc/troc_fire_04_1.png differ diff --git a/data/art824/troc/troc_fire_04_2.png b/data/art824/troc/troc_fire_04_2.png new file mode 100644 index 0000000..525b352 Binary files /dev/null and b/data/art824/troc/troc_fire_04_2.png differ diff --git a/data/art824/troc/troc_fire_05_0.png b/data/art824/troc/troc_fire_05_0.png new file mode 100644 index 0000000..dc223be Binary files /dev/null and b/data/art824/troc/troc_fire_05_0.png differ diff --git a/data/art824/troc/troc_fire_05_1.png b/data/art824/troc/troc_fire_05_1.png new file mode 100644 index 0000000..e868753 Binary files /dev/null and b/data/art824/troc/troc_fire_05_1.png differ diff --git a/data/art824/troc/troc_fire_05_2.png b/data/art824/troc/troc_fire_05_2.png new file mode 100644 index 0000000..caa7a11 Binary files /dev/null and b/data/art824/troc/troc_fire_05_2.png differ diff --git a/data/art824/troc/troc_fire_06_0.png b/data/art824/troc/troc_fire_06_0.png new file mode 100644 index 0000000..3510ec7 Binary files /dev/null and b/data/art824/troc/troc_fire_06_0.png differ diff --git a/data/art824/troc/troc_fire_06_1.png b/data/art824/troc/troc_fire_06_1.png new file mode 100644 index 0000000..e42c383 Binary files /dev/null and b/data/art824/troc/troc_fire_06_1.png differ diff --git a/data/art824/troc/troc_fire_06_2.png b/data/art824/troc/troc_fire_06_2.png new file mode 100644 index 0000000..54ed8df Binary files /dev/null and b/data/art824/troc/troc_fire_06_2.png differ diff --git a/data/art824/troc/troc_fire_07_0.png b/data/art824/troc/troc_fire_07_0.png new file mode 100644 index 0000000..a8137e1 Binary files /dev/null and b/data/art824/troc/troc_fire_07_0.png differ diff --git a/data/art824/troc/troc_fire_07_1.png b/data/art824/troc/troc_fire_07_1.png new file mode 100644 index 0000000..4a18c8c Binary files /dev/null and b/data/art824/troc/troc_fire_07_1.png differ diff --git a/data/art824/troc/troc_fire_07_2.png b/data/art824/troc/troc_fire_07_2.png new file mode 100644 index 0000000..176067c Binary files /dev/null and b/data/art824/troc/troc_fire_07_2.png differ diff --git a/data/art824/troc/troc_fire_08_0.png b/data/art824/troc/troc_fire_08_0.png new file mode 100644 index 0000000..f2f4f9a Binary files /dev/null and b/data/art824/troc/troc_fire_08_0.png differ diff --git a/data/art824/troc/troc_fire_08_1.png b/data/art824/troc/troc_fire_08_1.png new file mode 100644 index 0000000..e90df6b Binary files /dev/null and b/data/art824/troc/troc_fire_08_1.png differ diff --git a/data/art824/troc/troc_fire_08_2.png b/data/art824/troc/troc_fire_08_2.png new file mode 100644 index 0000000..35ccbd4 Binary files /dev/null and b/data/art824/troc/troc_fire_08_2.png differ diff --git a/data/art824/troc/troc_fire_09_0.png b/data/art824/troc/troc_fire_09_0.png new file mode 100644 index 0000000..cf32410 Binary files /dev/null and b/data/art824/troc/troc_fire_09_0.png differ diff --git a/data/art824/troc/troc_fire_09_1.png b/data/art824/troc/troc_fire_09_1.png new file mode 100644 index 0000000..dd384ae Binary files /dev/null and b/data/art824/troc/troc_fire_09_1.png differ diff --git a/data/art824/troc/troc_fire_09_2.png b/data/art824/troc/troc_fire_09_2.png new file mode 100644 index 0000000..548579f Binary files /dev/null and b/data/art824/troc/troc_fire_09_2.png differ diff --git a/data/art824/troc/troc_fire_10_0.png b/data/art824/troc/troc_fire_10_0.png new file mode 100644 index 0000000..b8875c1 Binary files /dev/null and b/data/art824/troc/troc_fire_10_0.png differ diff --git a/data/art824/troc/troc_fire_10_1.png b/data/art824/troc/troc_fire_10_1.png new file mode 100644 index 0000000..a21fdd2 Binary files /dev/null and b/data/art824/troc/troc_fire_10_1.png differ diff --git a/data/art824/troc/troc_fire_10_2.png b/data/art824/troc/troc_fire_10_2.png new file mode 100644 index 0000000..91c8044 Binary files /dev/null and b/data/art824/troc/troc_fire_10_2.png differ diff --git a/data/art824/troc/troc_fire_11_0.png b/data/art824/troc/troc_fire_11_0.png new file mode 100644 index 0000000..cab51c5 Binary files /dev/null and b/data/art824/troc/troc_fire_11_0.png differ diff --git a/data/art824/troc/troc_fire_11_1.png b/data/art824/troc/troc_fire_11_1.png new file mode 100644 index 0000000..f6d523e Binary files /dev/null and b/data/art824/troc/troc_fire_11_1.png differ diff --git a/data/art824/troc/troc_fire_11_2.png b/data/art824/troc/troc_fire_11_2.png new file mode 100644 index 0000000..b709016 Binary files /dev/null and b/data/art824/troc/troc_fire_11_2.png differ diff --git a/data/art824/troc/troc_fire_12_0.png b/data/art824/troc/troc_fire_12_0.png new file mode 100644 index 0000000..5808f26 Binary files /dev/null and b/data/art824/troc/troc_fire_12_0.png differ diff --git a/data/art824/troc/troc_fire_12_1.png b/data/art824/troc/troc_fire_12_1.png new file mode 100644 index 0000000..39f75cc Binary files /dev/null and b/data/art824/troc/troc_fire_12_1.png differ diff --git a/data/art824/troc/troc_fire_12_2.png b/data/art824/troc/troc_fire_12_2.png new file mode 100644 index 0000000..3e1a0c8 Binary files /dev/null and b/data/art824/troc/troc_fire_12_2.png differ diff --git a/data/art824/troc/troc_fire_13_0.png b/data/art824/troc/troc_fire_13_0.png new file mode 100644 index 0000000..d57e894 Binary files /dev/null and b/data/art824/troc/troc_fire_13_0.png differ diff --git a/data/art824/troc/troc_fire_13_1.png b/data/art824/troc/troc_fire_13_1.png new file mode 100644 index 0000000..3826be8 Binary files /dev/null and b/data/art824/troc/troc_fire_13_1.png differ diff --git a/data/art824/troc/troc_fire_13_2.png b/data/art824/troc/troc_fire_13_2.png new file mode 100644 index 0000000..b4cfc0b Binary files /dev/null and b/data/art824/troc/troc_fire_13_2.png differ diff --git a/data/art824/troc/troc_fire_14_0.png b/data/art824/troc/troc_fire_14_0.png new file mode 100644 index 0000000..45241f9 Binary files /dev/null and b/data/art824/troc/troc_fire_14_0.png differ diff --git a/data/art824/troc/troc_fire_14_1.png b/data/art824/troc/troc_fire_14_1.png new file mode 100644 index 0000000..9376f3a Binary files /dev/null and b/data/art824/troc/troc_fire_14_1.png differ diff --git a/data/art824/troc/troc_fire_14_2.png b/data/art824/troc/troc_fire_14_2.png new file mode 100644 index 0000000..8c0151b Binary files /dev/null and b/data/art824/troc/troc_fire_14_2.png differ diff --git a/data/art824/troc/troc_fire_15_0.png b/data/art824/troc/troc_fire_15_0.png new file mode 100644 index 0000000..2a23c4d Binary files /dev/null and b/data/art824/troc/troc_fire_15_0.png differ diff --git a/data/art824/troc/troc_fire_15_1.png b/data/art824/troc/troc_fire_15_1.png new file mode 100644 index 0000000..d3503e5 Binary files /dev/null and b/data/art824/troc/troc_fire_15_1.png differ diff --git a/data/art824/troc/troc_fire_15_2.png b/data/art824/troc/troc_fire_15_2.png new file mode 100644 index 0000000..990a4fa Binary files /dev/null and b/data/art824/troc/troc_fire_15_2.png differ diff --git a/data/art824/upgrades.amx b/data/art824/upgrades.amx new file mode 100644 index 0000000..019dffc --- /dev/null +++ b/data/art824/upgrades.amx @@ -0,0 +1,101 @@ + + + + + +80 +true + + +<_x0030_ href="#ref-5"/> +<_x0031_ href="#ref-6"/> +<_x0032_ href="#ref-7"/> + + +<_x0030_ href="#ref-8"/> +<_x0031_ href="#ref-9"/> +<_x0032_ href="#ref-10"/> + + +upgrades\ahrc.png + + +upgrades\avts.png + + +upgrades\miner.png + + +hrc1 +0 +1 +<_x0030_ href="#ref-15"/> + + +vts1 +0 +1 +<_x0030_ href="#ref-17"/> + + +bullpup1 +0 +1 +<_x0030_ href="#ref-19"/> + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + +<_x0030_ href="#ref-24"/> + + +<_x0030_ href="#ref-25"/> + + +<_x0030_ href="#ref-26"/> + + + +1 +0 + + + + + +0 +0 + + + + + +1 +5 + + + + + diff --git a/data/art824/upgrades/ahrc.png b/data/art824/upgrades/ahrc.png new file mode 100644 index 0000000..6e815e0 Binary files /dev/null and b/data/art824/upgrades/ahrc.png differ diff --git a/data/art824/upgrades/avts.png b/data/art824/upgrades/avts.png new file mode 100644 index 0000000..e5aa23a Binary files /dev/null and b/data/art824/upgrades/avts.png differ diff --git a/data/art824/upgrades/miner.png b/data/art824/upgrades/miner.png new file mode 100644 index 0000000..000530a Binary files /dev/null and b/data/art824/upgrades/miner.png differ diff --git a/data/art824/vacuum.amx b/data/art824/vacuum.amx new file mode 100644 index 0000000..449014e --- /dev/null +++ b/data/art824/vacuum.amx @@ -0,0 +1,798 @@ + + + + + +80 +true + + +<_x0030_ href="#ref-5"/> +<_x0031_ href="#ref-6"/> +<_x0032_ href="#ref-7"/> +<_x0033_ href="#ref-8"/> +<_x0034_ href="#ref-9"/> +<_x0035_ href="#ref-10"/> +<_x0036_ href="#ref-11"/> +<_x0037_ href="#ref-12"/> +<_x0038_ href="#ref-13"/> +<_x0039_ href="#ref-14"/> +<_x0031_0 href="#ref-15"/> +<_x0031_1 href="#ref-16"/> +<_x0031_2 href="#ref-17"/> +<_x0031_3 href="#ref-18"/> +<_x0031_4 href="#ref-19"/> +<_x0031_5 href="#ref-20"/> +<_x0031_6 href="#ref-21"/> +<_x0031_7 href="#ref-22"/> +<_x0031_8 href="#ref-23"/> +<_x0031_9 href="#ref-24"/> +<_x0032_0 href="#ref-25"/> +<_x0032_1 href="#ref-26"/> +<_x0032_2 href="#ref-27"/> +<_x0032_3 href="#ref-28"/> +<_x0032_4 href="#ref-29"/> +<_x0032_5 href="#ref-30"/> +<_x0032_6 href="#ref-31"/> +<_x0032_7 href="#ref-32"/> +<_x0032_8 href="#ref-33"/> +<_x0032_9 href="#ref-34"/> +<_x0033_0 href="#ref-35"/> +<_x0033_1 href="#ref-36"/> + + +<_x0030_ href="#ref-37"/> +<_x0031_ href="#ref-38"/> +<_x0032_ href="#ref-39"/> +<_x0033_ href="#ref-40"/> +<_x0034_ href="#ref-41"/> +<_x0035_ href="#ref-42"/> +<_x0036_ href="#ref-43"/> +<_x0037_ href="#ref-44"/> + + +vacuum\vacuum_a_0_0.png + + +vacuum\vacuum_a_0_1.png + + +vacuum\vacuum_a_0_2.png + + +vacuum\vacuum_a_0_3.png + + +vacuum\vacuum_a_1_0.png + + +vacuum\vacuum_a_1_1.png + + +vacuum\vacuum_a_1_2.png + + +vacuum\vacuum_a_1_3.png + + +vacuum\vacuum_a_2_0.png + + +vacuum\vacuum_a_2_1.png + + +vacuum\vacuum_a_2_2.png + + +vacuum\vacuum_a_2_3.png + + +vacuum\vacuum_a_3_0.png + + +vacuum\vacuum_a_3_1.png + + +vacuum\vacuum_a_3_2.png + + +vacuum\vacuum_a_3_3.png + + +vacuum\vacuum_a_4_0.png + + +vacuum\vacuum_a_4_1.png + + +vacuum\vacuum_a_4_2.png + + +vacuum\vacuum_a_4_3.png + + +vacuum\vacuum_a_5_0.png + + +vacuum\vacuum_a_5_1.png + + +vacuum\vacuum_a_5_2.png + + +vacuum\vacuum_a_5_3.png + + +vacuum\vacuum_a_6_0.png + + +vacuum\vacuum_a_6_1.png + + +vacuum\vacuum_a_6_2.png + + +vacuum\vacuum_a_6_3.png + + +vacuum\vacuum_a_7_0.png + + +vacuum\vacuum_a_7_1.png + + +vacuum\vacuum_a_7_2.png + + +vacuum\vacuum_a_7_3.png + + +a 0 +1 +4 +<_x0030_ href="#ref-78"/> +<_x0031_ href="#ref-79"/> +<_x0032_ href="#ref-80"/> +<_x0033_ href="#ref-81"/> + + +a 1 +1 +4 +<_x0030_ href="#ref-83"/> +<_x0031_ href="#ref-84"/> +<_x0032_ href="#ref-85"/> +<_x0033_ href="#ref-86"/> + + +a 2 +1 +4 +<_x0030_ href="#ref-88"/> +<_x0031_ href="#ref-89"/> +<_x0032_ href="#ref-90"/> +<_x0033_ href="#ref-91"/> + + +a 3 +1 +4 +<_x0030_ href="#ref-93"/> +<_x0031_ href="#ref-94"/> +<_x0032_ href="#ref-95"/> +<_x0033_ href="#ref-96"/> + + +a 4 +1 +4 +<_x0030_ href="#ref-98"/> +<_x0031_ href="#ref-99"/> +<_x0032_ href="#ref-100"/> +<_x0033_ href="#ref-101"/> + + +a 5 +1 +4 +<_x0030_ href="#ref-103"/> +<_x0031_ href="#ref-104"/> +<_x0032_ href="#ref-105"/> +<_x0033_ href="#ref-106"/> + + +a 6 +1 +4 +<_x0030_ href="#ref-108"/> +<_x0031_ href="#ref-109"/> +<_x0032_ href="#ref-110"/> +<_x0033_ href="#ref-111"/> + + +a 7 +1 +4 +<_x0030_ href="#ref-113"/> +<_x0031_ href="#ref-114"/> +<_x0032_ href="#ref-115"/> +<_x0033_ href="#ref-116"/> + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + +<_x0030_ href="#ref-150"/> + + +<_x0030_ href="#ref-151"/> + + +<_x0030_ href="#ref-152"/> + + +<_x0030_ href="#ref-153"/> + + +<_x0030_ href="#ref-154"/> + + +<_x0030_ href="#ref-155"/> + + +<_x0030_ href="#ref-156"/> + + +<_x0030_ href="#ref-157"/> + + +<_x0030_ href="#ref-158"/> + + +<_x0030_ href="#ref-159"/> + + +<_x0030_ href="#ref-160"/> + + +<_x0030_ href="#ref-161"/> + + +<_x0030_ href="#ref-162"/> + + +<_x0030_ href="#ref-163"/> + + +<_x0030_ href="#ref-164"/> + + +<_x0030_ href="#ref-165"/> + + +<_x0030_ href="#ref-166"/> + + +<_x0030_ href="#ref-167"/> + + +<_x0030_ href="#ref-168"/> + + +<_x0030_ href="#ref-169"/> + + +<_x0030_ href="#ref-170"/> + + +<_x0030_ href="#ref-171"/> + + +<_x0030_ href="#ref-172"/> + + +<_x0030_ href="#ref-173"/> + + +<_x0030_ href="#ref-174"/> + + +<_x0030_ href="#ref-175"/> + + +<_x0030_ href="#ref-176"/> + + +<_x0030_ href="#ref-177"/> + + +<_x0030_ href="#ref-178"/> + + +<_x0030_ href="#ref-179"/> + + +<_x0030_ href="#ref-180"/> + + +<_x0030_ href="#ref-181"/> + + + +4 +26 + + + + + +3 +26 + + + + + +3 +26 + + + + + +6 +24 + + + + + +-10 +20 + + + + + +-8 +18 + + + + + +-8 +15 + + + + + +-8 +20 + + + + + +-16 +4 + + + + + +-14 +3 + + + + + +-14 +2 + + + + + +-14 +0 + + + + + +-9 +-10 + + + + + +-9 +-12 + + + + + +-8 +-10 + + + + + +-9 +-9 + + + + + +4 +-15 + + + + + +6 +-15 + + + + + +4 +-15 + + + + + +6 +-16 + + + + + +18 +-10 + + + + + +15 +-9 + + + + + +12 +-9 + + + + + +15 +-9 + + + + + +26 +4 + + + + + +22 +3 + + + + + +26 +2 + + + + + +26 +0 + + + + + +20 +20 + + + + + +20 +18 + + + + + +18 +15 + + + + + +18 +20 + + + + + diff --git a/data/art824/vacuum/vacuum_a_0_0.png b/data/art824/vacuum/vacuum_a_0_0.png new file mode 100644 index 0000000..0c6ca02 Binary files /dev/null and b/data/art824/vacuum/vacuum_a_0_0.png differ diff --git a/data/art824/vacuum/vacuum_a_0_1.png b/data/art824/vacuum/vacuum_a_0_1.png new file mode 100644 index 0000000..28b3afe Binary files /dev/null and b/data/art824/vacuum/vacuum_a_0_1.png differ diff --git a/data/art824/vacuum/vacuum_a_0_2.png b/data/art824/vacuum/vacuum_a_0_2.png new file mode 100644 index 0000000..685d84d Binary files /dev/null and b/data/art824/vacuum/vacuum_a_0_2.png differ diff --git a/data/art824/vacuum/vacuum_a_0_3.png b/data/art824/vacuum/vacuum_a_0_3.png new file mode 100644 index 0000000..39ef3df Binary files /dev/null and b/data/art824/vacuum/vacuum_a_0_3.png differ diff --git a/data/art824/vacuum/vacuum_a_1_0.png b/data/art824/vacuum/vacuum_a_1_0.png new file mode 100644 index 0000000..e6aa4da Binary files /dev/null and b/data/art824/vacuum/vacuum_a_1_0.png differ diff --git a/data/art824/vacuum/vacuum_a_1_1.png b/data/art824/vacuum/vacuum_a_1_1.png new file mode 100644 index 0000000..da8b13a Binary files /dev/null and b/data/art824/vacuum/vacuum_a_1_1.png differ diff --git a/data/art824/vacuum/vacuum_a_1_2.png b/data/art824/vacuum/vacuum_a_1_2.png new file mode 100644 index 0000000..5756ca4 Binary files /dev/null and b/data/art824/vacuum/vacuum_a_1_2.png differ diff --git a/data/art824/vacuum/vacuum_a_1_3.png b/data/art824/vacuum/vacuum_a_1_3.png new file mode 100644 index 0000000..0161a39 Binary files /dev/null and b/data/art824/vacuum/vacuum_a_1_3.png differ diff --git a/data/art824/vacuum/vacuum_a_2_0.png b/data/art824/vacuum/vacuum_a_2_0.png new file mode 100644 index 0000000..bb51883 Binary files /dev/null and b/data/art824/vacuum/vacuum_a_2_0.png differ diff --git a/data/art824/vacuum/vacuum_a_2_1.png b/data/art824/vacuum/vacuum_a_2_1.png new file mode 100644 index 0000000..b832ed6 Binary files /dev/null and b/data/art824/vacuum/vacuum_a_2_1.png differ diff --git a/data/art824/vacuum/vacuum_a_2_2.png b/data/art824/vacuum/vacuum_a_2_2.png new file mode 100644 index 0000000..842f341 Binary files /dev/null and b/data/art824/vacuum/vacuum_a_2_2.png differ diff --git a/data/art824/vacuum/vacuum_a_2_3.png b/data/art824/vacuum/vacuum_a_2_3.png new file mode 100644 index 0000000..5d87853 Binary files /dev/null and b/data/art824/vacuum/vacuum_a_2_3.png differ diff --git a/data/art824/vacuum/vacuum_a_3_0.png b/data/art824/vacuum/vacuum_a_3_0.png new file mode 100644 index 0000000..e8114de Binary files /dev/null and b/data/art824/vacuum/vacuum_a_3_0.png differ diff --git a/data/art824/vacuum/vacuum_a_3_1.png b/data/art824/vacuum/vacuum_a_3_1.png new file mode 100644 index 0000000..940bf93 Binary files /dev/null and b/data/art824/vacuum/vacuum_a_3_1.png differ diff --git a/data/art824/vacuum/vacuum_a_3_2.png b/data/art824/vacuum/vacuum_a_3_2.png new file mode 100644 index 0000000..c3d459c Binary files /dev/null and b/data/art824/vacuum/vacuum_a_3_2.png differ diff --git a/data/art824/vacuum/vacuum_a_3_3.png b/data/art824/vacuum/vacuum_a_3_3.png new file mode 100644 index 0000000..17aa176 Binary files /dev/null and b/data/art824/vacuum/vacuum_a_3_3.png differ diff --git a/data/art824/vacuum/vacuum_a_4_0.png b/data/art824/vacuum/vacuum_a_4_0.png new file mode 100644 index 0000000..a7a1aec Binary files /dev/null and b/data/art824/vacuum/vacuum_a_4_0.png differ diff --git a/data/art824/vacuum/vacuum_a_4_1.png b/data/art824/vacuum/vacuum_a_4_1.png new file mode 100644 index 0000000..ed6d02e Binary files /dev/null and b/data/art824/vacuum/vacuum_a_4_1.png differ diff --git a/data/art824/vacuum/vacuum_a_4_2.png b/data/art824/vacuum/vacuum_a_4_2.png new file mode 100644 index 0000000..a9507d6 Binary files /dev/null and b/data/art824/vacuum/vacuum_a_4_2.png differ diff --git a/data/art824/vacuum/vacuum_a_4_3.png b/data/art824/vacuum/vacuum_a_4_3.png new file mode 100644 index 0000000..5c334d7 Binary files /dev/null and b/data/art824/vacuum/vacuum_a_4_3.png differ diff --git a/data/art824/vacuum/vacuum_a_5_0.png b/data/art824/vacuum/vacuum_a_5_0.png new file mode 100644 index 0000000..2eed887 Binary files /dev/null and b/data/art824/vacuum/vacuum_a_5_0.png differ diff --git a/data/art824/vacuum/vacuum_a_5_1.png b/data/art824/vacuum/vacuum_a_5_1.png new file mode 100644 index 0000000..16aa594 Binary files /dev/null and b/data/art824/vacuum/vacuum_a_5_1.png differ diff --git a/data/art824/vacuum/vacuum_a_5_2.png b/data/art824/vacuum/vacuum_a_5_2.png new file mode 100644 index 0000000..9575dbd Binary files /dev/null and b/data/art824/vacuum/vacuum_a_5_2.png differ diff --git a/data/art824/vacuum/vacuum_a_5_3.png b/data/art824/vacuum/vacuum_a_5_3.png new file mode 100644 index 0000000..ee13f00 Binary files /dev/null and b/data/art824/vacuum/vacuum_a_5_3.png differ diff --git a/data/art824/vacuum/vacuum_a_6_0.png b/data/art824/vacuum/vacuum_a_6_0.png new file mode 100644 index 0000000..cc73ec5 Binary files /dev/null and b/data/art824/vacuum/vacuum_a_6_0.png differ diff --git a/data/art824/vacuum/vacuum_a_6_1.png b/data/art824/vacuum/vacuum_a_6_1.png new file mode 100644 index 0000000..86dd969 Binary files /dev/null and b/data/art824/vacuum/vacuum_a_6_1.png differ diff --git a/data/art824/vacuum/vacuum_a_6_2.png b/data/art824/vacuum/vacuum_a_6_2.png new file mode 100644 index 0000000..75779f1 Binary files /dev/null and b/data/art824/vacuum/vacuum_a_6_2.png differ diff --git a/data/art824/vacuum/vacuum_a_6_3.png b/data/art824/vacuum/vacuum_a_6_3.png new file mode 100644 index 0000000..c5f4ca4 Binary files /dev/null and b/data/art824/vacuum/vacuum_a_6_3.png differ diff --git a/data/art824/vacuum/vacuum_a_7_0.png b/data/art824/vacuum/vacuum_a_7_0.png new file mode 100644 index 0000000..34e02df Binary files /dev/null and b/data/art824/vacuum/vacuum_a_7_0.png differ diff --git a/data/art824/vacuum/vacuum_a_7_1.png b/data/art824/vacuum/vacuum_a_7_1.png new file mode 100644 index 0000000..3824651 Binary files /dev/null and b/data/art824/vacuum/vacuum_a_7_1.png differ diff --git a/data/art824/vacuum/vacuum_a_7_2.png b/data/art824/vacuum/vacuum_a_7_2.png new file mode 100644 index 0000000..08c3441 Binary files /dev/null and b/data/art824/vacuum/vacuum_a_7_2.png differ diff --git a/data/art824/vacuum/vacuum_a_7_3.png b/data/art824/vacuum/vacuum_a_7_3.png new file mode 100644 index 0000000..e1b664c Binary files /dev/null and b/data/art824/vacuum/vacuum_a_7_3.png differ diff --git a/data/art824/vexplosion.amx b/data/art824/vexplosion.amx new file mode 100644 index 0000000..81585bf --- /dev/null +++ b/data/art824/vexplosion.amx @@ -0,0 +1,135 @@ + + + + + +80 +true + + +<_x0030_ href="#ref-5"/> +<_x0031_ href="#ref-6"/> +<_x0032_ href="#ref-7"/> +<_x0033_ href="#ref-8"/> +<_x0034_ href="#ref-9"/> + + +<_x0030_ href="#ref-10"/> + + +vexplosion\vexplosion_a_0_4.png + + +vexplosion\vexplosion_a_0_1.png + + +vexplosion\vexplosion_a_0_2.png + + +vexplosion\vexplosion_a_0_3.png + + +vexplosion\vexplosion_a_0_0.png + + +a 0 +0 +5 +<_x0030_ href="#ref-17"/> +<_x0031_ href="#ref-18"/> +<_x0032_ href="#ref-19"/> +<_x0033_ href="#ref-20"/> +<_x0034_ href="#ref-21"/> + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + +<_x0030_ href="#ref-28"/> + + +<_x0030_ href="#ref-29"/> + + +<_x0030_ href="#ref-30"/> + + +<_x0030_ href="#ref-31"/> + + +<_x0030_ href="#ref-32"/> + + + +25 +22 + + + + + +23 +24 + + + + + +24 +24 + + + + + +24 +24 + + + + + +24 +24 + + + + + diff --git a/data/art824/vexplosion/vexplosion_a_0_0.png b/data/art824/vexplosion/vexplosion_a_0_0.png new file mode 100644 index 0000000..86f346e Binary files /dev/null and b/data/art824/vexplosion/vexplosion_a_0_0.png differ diff --git a/data/art824/vexplosion/vexplosion_a_0_1.png b/data/art824/vexplosion/vexplosion_a_0_1.png new file mode 100644 index 0000000..a18a1de Binary files /dev/null and b/data/art824/vexplosion/vexplosion_a_0_1.png differ diff --git a/data/art824/vexplosion/vexplosion_a_0_2.png b/data/art824/vexplosion/vexplosion_a_0_2.png new file mode 100644 index 0000000..f75552e Binary files /dev/null and b/data/art824/vexplosion/vexplosion_a_0_2.png differ diff --git a/data/art824/vexplosion/vexplosion_a_0_3.png b/data/art824/vexplosion/vexplosion_a_0_3.png new file mode 100644 index 0000000..b0ea48d Binary files /dev/null and b/data/art824/vexplosion/vexplosion_a_0_3.png differ diff --git a/data/art824/vexplosion/vexplosion_a_0_4.png b/data/art824/vexplosion/vexplosion_a_0_4.png new file mode 100644 index 0000000..07999a0 Binary files /dev/null and b/data/art824/vexplosion/vexplosion_a_0_4.png differ diff --git a/data/art824/vts/icon.png b/data/art824/vts/icon.png new file mode 100644 index 0000000..bcf89c2 Binary files /dev/null and b/data/art824/vts/icon.png differ diff --git a/data/art824/vts/vts_a_0_0.png b/data/art824/vts/vts_a_0_0.png new file mode 100644 index 0000000..a0adb6d Binary files /dev/null and b/data/art824/vts/vts_a_0_0.png differ diff --git a/data/art824/vts/vts_a_1_0.png b/data/art824/vts/vts_a_1_0.png new file mode 100644 index 0000000..f8db889 Binary files /dev/null and b/data/art824/vts/vts_a_1_0.png differ diff --git a/data/art824/vts/vts_a_2_0.png b/data/art824/vts/vts_a_2_0.png new file mode 100644 index 0000000..dcb74a5 Binary files /dev/null and b/data/art824/vts/vts_a_2_0.png differ diff --git a/data/art824/vts/vts_b_0_0.png b/data/art824/vts/vts_b_0_0.png new file mode 100644 index 0000000..a32977a Binary files /dev/null and b/data/art824/vts/vts_b_0_0.png differ diff --git a/data/art824/vts/vts_b_1_0.png b/data/art824/vts/vts_b_1_0.png new file mode 100644 index 0000000..f846780 Binary files /dev/null and b/data/art824/vts/vts_b_1_0.png differ diff --git a/data/art824/vts/vts_b_2_0.png b/data/art824/vts/vts_b_2_0.png new file mode 100644 index 0000000..06ecc13 Binary files /dev/null and b/data/art824/vts/vts_b_2_0.png differ diff --git a/data/art824/walls.amx b/data/art824/walls.amx new file mode 100644 index 0000000..659a8f7 --- /dev/null +++ b/data/art824/walls.amx @@ -0,0 +1,762 @@ + + + + + +80 +true + + +<_x0030_ href="#ref-5"/> +<_x0031_ href="#ref-6"/> +<_x0032_ href="#ref-7"/> +<_x0033_ href="#ref-8"/> +<_x0034_ href="#ref-9"/> +<_x0035_ href="#ref-10"/> +<_x0036_ href="#ref-11"/> +<_x0037_ href="#ref-12"/> +<_x0038_ href="#ref-13"/> +<_x0039_ href="#ref-14"/> +<_x0031_0 href="#ref-15"/> +<_x0031_1 href="#ref-16"/> +<_x0031_2 href="#ref-17"/> +<_x0031_3 href="#ref-18"/> +<_x0031_4 href="#ref-19"/> +<_x0031_5 href="#ref-20"/> +<_x0031_6 href="#ref-21"/> +<_x0031_7 href="#ref-22"/> +<_x0031_8 href="#ref-23"/> +<_x0031_9 href="#ref-24"/> +<_x0032_0 href="#ref-25"/> +<_x0032_1 href="#ref-26"/> +<_x0032_2 href="#ref-27"/> +<_x0032_3 href="#ref-28"/> +<_x0032_4 href="#ref-29"/> +<_x0032_5 href="#ref-30"/> +<_x0032_6 href="#ref-31"/> +<_x0032_7 href="#ref-32"/> +<_x0032_8 href="#ref-33"/> +<_x0032_9 href="#ref-34"/> +<_x0033_0 href="#ref-35"/> +<_x0033_1 href="#ref-36"/> + + +<_x0030_ href="#ref-37"/> +<_x0031_ href="#ref-38"/> + + +walls\wall_b_15.png + + +walls\wall_a_00.png + + +walls\wall_a_01.png + + +walls\wall_a_02.png + + +walls\wall_a_03.png + + +walls\wall_a_04.png + + +walls\wall_a_05.png + + +walls\wall_a_06.png + + +walls\wall_a_07.png + + +walls\wall_a_08.png + + +walls\wall_a_09.png + + +walls\wall_a_10.png + + +walls\wall_a_11.png + + +walls\wall_a_12.png + + +walls\wall_a_13.png + + +walls\wall_a_14.png + + +walls\wall_a_15.png + + +walls\wall_b_00.png + + +walls\wall_b_01.png + + +walls\wall_b_02.png + + +walls\wall_b_03.png + + +walls\wall_b_04.png + + +walls\wall_b_05.png + + +walls\wall_b_06.png + + +walls\wall_b_07.png + + +walls\wall_b_08.png + + +walls\wall_b_09.png + + +walls\wall_b_10.png + + +walls\wall_b_11.png + + +walls\wall_b_12.png + + +walls\wall_b_13.png + + +walls\wall_b_14.png + + +a +0 +16 +<_x0030_ href="#ref-72"/> +<_x0031_ href="#ref-73"/> +<_x0032_ href="#ref-74"/> +<_x0033_ href="#ref-75"/> +<_x0034_ href="#ref-76"/> +<_x0035_ href="#ref-77"/> +<_x0036_ href="#ref-78"/> +<_x0037_ href="#ref-79"/> +<_x0038_ href="#ref-80"/> +<_x0039_ href="#ref-81"/> +<_x0031_0 href="#ref-82"/> +<_x0031_1 href="#ref-83"/> +<_x0031_2 href="#ref-84"/> +<_x0031_3 href="#ref-85"/> +<_x0031_4 href="#ref-86"/> +<_x0031_5 href="#ref-87"/> + + +b +0 +16 +<_x0030_ href="#ref-89"/> +<_x0031_ href="#ref-90"/> +<_x0032_ href="#ref-91"/> +<_x0033_ href="#ref-92"/> +<_x0034_ href="#ref-93"/> +<_x0035_ href="#ref-94"/> +<_x0036_ href="#ref-95"/> +<_x0037_ href="#ref-96"/> +<_x0038_ href="#ref-97"/> +<_x0039_ href="#ref-98"/> +<_x0031_0 href="#ref-99"/> +<_x0031_1 href="#ref-100"/> +<_x0031_2 href="#ref-101"/> +<_x0031_3 href="#ref-102"/> +<_x0031_4 href="#ref-103"/> +<_x0031_5 href="#ref-104"/> + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + +<_x0030_ href="#ref-138"/> + + +<_x0030_ href="#ref-139"/> + + +<_x0030_ href="#ref-140"/> + + +<_x0030_ href="#ref-141"/> + + +<_x0030_ href="#ref-142"/> + + +<_x0030_ href="#ref-143"/> + + +<_x0030_ href="#ref-144"/> + + +<_x0030_ href="#ref-145"/> + + +<_x0030_ href="#ref-146"/> + + +<_x0030_ href="#ref-147"/> + + +<_x0030_ href="#ref-148"/> + + +<_x0030_ href="#ref-149"/> + + +<_x0030_ href="#ref-150"/> + + +<_x0030_ href="#ref-151"/> + + +<_x0030_ href="#ref-152"/> + + +<_x0030_ href="#ref-153"/> + + +<_x0030_ href="#ref-154"/> + + +<_x0030_ href="#ref-155"/> + + +<_x0030_ href="#ref-156"/> + + +<_x0030_ href="#ref-157"/> + + +<_x0030_ href="#ref-158"/> + + +<_x0030_ href="#ref-159"/> + + +<_x0030_ href="#ref-160"/> + + +<_x0030_ href="#ref-161"/> + + +<_x0030_ href="#ref-162"/> + + +<_x0030_ href="#ref-163"/> + + +<_x0030_ href="#ref-164"/> + + +<_x0030_ href="#ref-165"/> + + +<_x0030_ href="#ref-166"/> + + +<_x0030_ href="#ref-167"/> + + +<_x0030_ href="#ref-168"/> + + +<_x0030_ href="#ref-169"/> + + + +1 +0 + + + + + +-1 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +-1 +0 + + + + + +-1 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +1 +-1 + + + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +-1 +0 + + + + + +-1 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + +0 +0 + + + + + diff --git a/data/art824/walls/wall_a_00.png b/data/art824/walls/wall_a_00.png new file mode 100644 index 0000000..1b08235 Binary files /dev/null and b/data/art824/walls/wall_a_00.png differ diff --git a/data/art824/walls/wall_a_01.png b/data/art824/walls/wall_a_01.png new file mode 100644 index 0000000..faf9617 Binary files /dev/null and b/data/art824/walls/wall_a_01.png differ diff --git a/data/art824/walls/wall_a_02.png b/data/art824/walls/wall_a_02.png new file mode 100644 index 0000000..4d1e442 Binary files /dev/null and b/data/art824/walls/wall_a_02.png differ diff --git a/data/art824/walls/wall_a_03.png b/data/art824/walls/wall_a_03.png new file mode 100644 index 0000000..66a761e Binary files /dev/null and b/data/art824/walls/wall_a_03.png differ diff --git a/data/art824/walls/wall_a_04.png b/data/art824/walls/wall_a_04.png new file mode 100644 index 0000000..e06f2b9 Binary files /dev/null and b/data/art824/walls/wall_a_04.png differ diff --git a/data/art824/walls/wall_a_05.png b/data/art824/walls/wall_a_05.png new file mode 100644 index 0000000..df4e682 Binary files /dev/null and b/data/art824/walls/wall_a_05.png differ diff --git a/data/art824/walls/wall_a_06.png b/data/art824/walls/wall_a_06.png new file mode 100644 index 0000000..b7c8675 Binary files /dev/null and b/data/art824/walls/wall_a_06.png differ diff --git a/data/art824/walls/wall_a_07.png b/data/art824/walls/wall_a_07.png new file mode 100644 index 0000000..b30089f Binary files /dev/null and b/data/art824/walls/wall_a_07.png differ diff --git a/data/art824/walls/wall_a_08.png b/data/art824/walls/wall_a_08.png new file mode 100644 index 0000000..45075df Binary files /dev/null and b/data/art824/walls/wall_a_08.png differ diff --git a/data/art824/walls/wall_a_09.png b/data/art824/walls/wall_a_09.png new file mode 100644 index 0000000..e3e4490 Binary files /dev/null and b/data/art824/walls/wall_a_09.png differ diff --git a/data/art824/walls/wall_a_10.png b/data/art824/walls/wall_a_10.png new file mode 100644 index 0000000..4e4eccf Binary files /dev/null and b/data/art824/walls/wall_a_10.png differ diff --git a/data/art824/walls/wall_a_11.png b/data/art824/walls/wall_a_11.png new file mode 100644 index 0000000..616aeef Binary files /dev/null and b/data/art824/walls/wall_a_11.png differ diff --git a/data/art824/walls/wall_a_12.png b/data/art824/walls/wall_a_12.png new file mode 100644 index 0000000..d6dd060 Binary files /dev/null and b/data/art824/walls/wall_a_12.png differ diff --git a/data/art824/walls/wall_a_13.png b/data/art824/walls/wall_a_13.png new file mode 100644 index 0000000..ba51df6 Binary files /dev/null and b/data/art824/walls/wall_a_13.png differ diff --git a/data/art824/walls/wall_a_14.png b/data/art824/walls/wall_a_14.png new file mode 100644 index 0000000..00dbb71 Binary files /dev/null and b/data/art824/walls/wall_a_14.png differ diff --git a/data/art824/walls/wall_a_15.png b/data/art824/walls/wall_a_15.png new file mode 100644 index 0000000..98d7669 Binary files /dev/null and b/data/art824/walls/wall_a_15.png differ diff --git a/data/art824/walls/wall_b_00.png b/data/art824/walls/wall_b_00.png new file mode 100644 index 0000000..83dd879 Binary files /dev/null and b/data/art824/walls/wall_b_00.png differ diff --git a/data/art824/walls/wall_b_01.png b/data/art824/walls/wall_b_01.png new file mode 100644 index 0000000..ad44d40 Binary files /dev/null and b/data/art824/walls/wall_b_01.png differ diff --git a/data/art824/walls/wall_b_02.png b/data/art824/walls/wall_b_02.png new file mode 100644 index 0000000..c642f8f Binary files /dev/null and b/data/art824/walls/wall_b_02.png differ diff --git a/data/art824/walls/wall_b_03.png b/data/art824/walls/wall_b_03.png new file mode 100644 index 0000000..c3dbfba Binary files /dev/null and b/data/art824/walls/wall_b_03.png differ diff --git a/data/art824/walls/wall_b_04.png b/data/art824/walls/wall_b_04.png new file mode 100644 index 0000000..bc94b05 Binary files /dev/null and b/data/art824/walls/wall_b_04.png differ diff --git a/data/art824/walls/wall_b_05.png b/data/art824/walls/wall_b_05.png new file mode 100644 index 0000000..19271f7 Binary files /dev/null and b/data/art824/walls/wall_b_05.png differ diff --git a/data/art824/walls/wall_b_06.png b/data/art824/walls/wall_b_06.png new file mode 100644 index 0000000..aa4e89b Binary files /dev/null and b/data/art824/walls/wall_b_06.png differ diff --git a/data/art824/walls/wall_b_07.png b/data/art824/walls/wall_b_07.png new file mode 100644 index 0000000..c2a9b21 Binary files /dev/null and b/data/art824/walls/wall_b_07.png differ diff --git a/data/art824/walls/wall_b_08.png b/data/art824/walls/wall_b_08.png new file mode 100644 index 0000000..a31ae8d Binary files /dev/null and b/data/art824/walls/wall_b_08.png differ diff --git a/data/art824/walls/wall_b_09.png b/data/art824/walls/wall_b_09.png new file mode 100644 index 0000000..9ec90aa Binary files /dev/null and b/data/art824/walls/wall_b_09.png differ diff --git a/data/art824/walls/wall_b_10.png b/data/art824/walls/wall_b_10.png new file mode 100644 index 0000000..2e65247 Binary files /dev/null and b/data/art824/walls/wall_b_10.png differ diff --git a/data/art824/walls/wall_b_11.png b/data/art824/walls/wall_b_11.png new file mode 100644 index 0000000..c910693 Binary files /dev/null and b/data/art824/walls/wall_b_11.png differ diff --git a/data/art824/walls/wall_b_12.png b/data/art824/walls/wall_b_12.png new file mode 100644 index 0000000..3b71425 Binary files /dev/null and b/data/art824/walls/wall_b_12.png differ diff --git a/data/art824/walls/wall_b_13.png b/data/art824/walls/wall_b_13.png new file mode 100644 index 0000000..ca840e7 Binary files /dev/null and b/data/art824/walls/wall_b_13.png differ diff --git a/data/art824/walls/wall_b_14.png b/data/art824/walls/wall_b_14.png new file mode 100644 index 0000000..f836842 Binary files /dev/null and b/data/art824/walls/wall_b_14.png differ diff --git a/data/art824/walls/wall_b_15.png b/data/art824/walls/wall_b_15.png new file mode 100644 index 0000000..8e8a721 Binary files /dev/null and b/data/art824/walls/wall_b_15.png differ diff --git a/data/art824/warehouse.amx b/data/art824/warehouse.amx new file mode 100644 index 0000000..6e36d2d --- /dev/null +++ b/data/art824/warehouse.amx @@ -0,0 +1,339 @@ + + + + + +80 +true + + +<_x0030_ href="#ref-5"/> +<_x0031_ href="#ref-6"/> +<_x0032_ href="#ref-7"/> +<_x0033_ href="#ref-8"/> +<_x0034_ href="#ref-9"/> +<_x0035_ href="#ref-10"/> +<_x0036_ href="#ref-11"/> +<_x0037_ href="#ref-12"/> +<_x0038_ href="#ref-13"/> +<_x0039_ href="#ref-14"/> +<_x0031_0 href="#ref-15"/> +<_x0031_1 href="#ref-16"/> + + +<_x0030_ href="#ref-17"/> +<_x0031_ href="#ref-18"/> +<_x0032_ href="#ref-19"/> +<_x0033_ href="#ref-20"/> +<_x0034_ href="#ref-21"/> + + +warehouse\warehouse_a_2_2.png + + +warehouse\warehouse_a_0_1.png + + +warehouse\warehouse_a_0_2.png + + +warehouse\warehouse_a_0_3.png + + +warehouse\warehouse_a_1_0.png + + +warehouse\warehouse_a_1_1.png + + +warehouse\warehouse_a_1_2.png + + +warehouse\warehouse_a_1_3.png + + +warehouse\warehouse_a_2_0.png + + +warehouse\warehouse_a_2_1.png + + +warehouse\warehouse_a_0_0.png + + +warehouse\icon.png + + +a 0 +0 +4 +<_x0030_ href="#ref-35"/> +<_x0031_ href="#ref-36"/> +<_x0032_ href="#ref-37"/> +<_x0033_ href="#ref-38"/> + + +a 1 +0 +4 +<_x0030_ href="#ref-40"/> +<_x0031_ href="#ref-41"/> +<_x0032_ href="#ref-42"/> +<_x0033_ href="#ref-43"/> + + +a 2 +0 +3 +<_x0030_ href="#ref-45"/> +<_x0031_ href="#ref-46"/> +<_x0032_ href="#ref-47"/> + + +icon +0 +1 +<_x0030_ href="#ref-49"/> + + +help +0 +1 +<_x0030_ href="#ref-51"/> + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + + +0 + +0 +0 + + + +<_x0030_ href="#ref-66"/> + + +<_x0030_ href="#ref-67"/> + + +<_x0030_ href="#ref-68"/> + + +<_x0030_ href="#ref-69"/> + + +<_x0030_ href="#ref-70"/> + + +<_x0030_ href="#ref-71"/> + + +<_x0030_ href="#ref-72"/> + + +<_x0030_ href="#ref-73"/> + + +<_x0030_ href="#ref-74"/> + + +<_x0030_ href="#ref-75"/> + + +<_x0030_ href="#ref-76"/> + + +<_x0030_ href="#ref-77"/> + + +<_x0030_ href="#ref-78"/> + + + +3 +3 + + + + + +3 +3 + + + + + +3 +3 + + + + + +3 +3 + + + + + +3 +3 + + + + + +3 +3 + + + + + +3 +3 + + + + + +3 +3 + + + + + +3 +3 + + + + + +3 +3 + + + + + +3 +3 + + + + + +0 +0 + + + + + +26 +26 + + + + + diff --git a/data/art824/warehouse/icon.png b/data/art824/warehouse/icon.png new file mode 100644 index 0000000..1573bfc Binary files /dev/null and b/data/art824/warehouse/icon.png differ diff --git a/data/art824/warehouse/warehouse_a_0_0.png b/data/art824/warehouse/warehouse_a_0_0.png new file mode 100644 index 0000000..3122d80 Binary files /dev/null and b/data/art824/warehouse/warehouse_a_0_0.png differ diff --git a/data/art824/warehouse/warehouse_a_0_1.png b/data/art824/warehouse/warehouse_a_0_1.png new file mode 100644 index 0000000..1975a69 Binary files /dev/null and b/data/art824/warehouse/warehouse_a_0_1.png differ diff --git a/data/art824/warehouse/warehouse_a_0_2.png b/data/art824/warehouse/warehouse_a_0_2.png new file mode 100644 index 0000000..54237bd Binary files /dev/null and b/data/art824/warehouse/warehouse_a_0_2.png differ diff --git a/data/art824/warehouse/warehouse_a_0_3.png b/data/art824/warehouse/warehouse_a_0_3.png new file mode 100644 index 0000000..e0bced7 Binary files /dev/null and b/data/art824/warehouse/warehouse_a_0_3.png differ diff --git a/data/art824/warehouse/warehouse_a_1_0.png b/data/art824/warehouse/warehouse_a_1_0.png new file mode 100644 index 0000000..71ecadd Binary files /dev/null and b/data/art824/warehouse/warehouse_a_1_0.png differ diff --git a/data/art824/warehouse/warehouse_a_1_1.png b/data/art824/warehouse/warehouse_a_1_1.png new file mode 100644 index 0000000..73aabf6 Binary files /dev/null and b/data/art824/warehouse/warehouse_a_1_1.png differ diff --git a/data/art824/warehouse/warehouse_a_1_2.png b/data/art824/warehouse/warehouse_a_1_2.png new file mode 100644 index 0000000..a05d0aa Binary files /dev/null and b/data/art824/warehouse/warehouse_a_1_2.png differ diff --git a/data/art824/warehouse/warehouse_a_1_3.png b/data/art824/warehouse/warehouse_a_1_3.png new file mode 100644 index 0000000..9735622 Binary files /dev/null and b/data/art824/warehouse/warehouse_a_1_3.png differ diff --git a/data/art824/warehouse/warehouse_a_2_0.png b/data/art824/warehouse/warehouse_a_2_0.png new file mode 100644 index 0000000..439d2f8 Binary files /dev/null and b/data/art824/warehouse/warehouse_a_2_0.png differ diff --git a/data/art824/warehouse/warehouse_a_2_1.png b/data/art824/warehouse/warehouse_a_2_1.png new file mode 100644 index 0000000..258ecba Binary files /dev/null and b/data/art824/warehouse/warehouse_a_2_1.png differ diff --git a/data/art824/warehouse/warehouse_a_2_2.PNG b/data/art824/warehouse/warehouse_a_2_2.PNG new file mode 100644 index 0000000..fcd129b Binary files /dev/null and b/data/art824/warehouse/warehouse_a_2_2.PNG differ diff --git a/data/desert.tc b/data/desert.tc new file mode 100644 index 0000000..906d01e Binary files /dev/null and b/data/desert.tc differ diff --git a/data/fonts/font1.psd b/data/fonts/font1.psd new file mode 100644 index 0000000..8bf4621 Binary files /dev/null and b/data/fonts/font1.psd differ diff --git a/data/fonts/font3.psd b/data/fonts/font3.psd new file mode 100644 index 0000000..656a39d Binary files /dev/null and b/data/fonts/font3.psd differ diff --git a/data/fonts/takeover font.bmp b/data/fonts/takeover font.bmp new file mode 100644 index 0000000..7eda757 Binary files /dev/null and b/data/fonts/takeover font.bmp differ diff --git a/data/forms.ini.pp b/data/forms.ini.pp new file mode 100644 index 0000000..9216951 --- /dev/null +++ b/data/forms.ini.pp @@ -0,0 +1,882 @@ +#include + +[kidfSimUI] + FORM=(0 0 160 152) kidcOk + ALERT=kidcAlert (0 143 75 8) + LABEL=kidcFps (74 0 12 0) "FPS" kifntShadow right + LABEL=kidcObjective (0 9 0 0) "" kifntShadow + LABEL=kidcCountdown (132 9 27 0) "" kifntShadow right + BUTTON=kidcChat (128 2 40 12) "Chat" kifntShadow + +[kidfContinueGame] + FORM=(0 0 120 60) kidcOk + LABEL=kidcTitle (0 3 120 8) "BEGIN NEW GAME" kifntTitle center + LABEL=0 (12 15 100 0) "Would you like to replay the first two missions?" kifntDefault center multiline + BUTTON=kidcOk (21 38 29 12) "Yes" kifntButton + BUTTON=kidcCancel (72 38 27 12) "No" kifntButton + +[kidfInputUI] + FORM=(0 0 160 68) kidcOk + FORMBACKCOLOR=kiclrFormBackground + BUTTON=kidcMenuButton (0 0 23 7) "" kifntButton nocenter menuup.tbm menudown.tbm + GRAFFITISCROLL=kidcGraffitiScroll (0 9 0 0) 4 + SILKBUTTON=kidcAppsSilkButton (0 0 0 0) + SILKBUTTON=kidcMenuSilkButton (0 0 0 0) + SILKBUTTON=kidcCalcSilkButton (0 0 0 0) + SILKBUTTON=kidcFindSilkButton (0 0 0 0) + POWER=kidcPower (86 0 40 0) + CREDITS=kidcCreditsLabel (40 0 40 0) + +[kidfMiniMap] + FORM=(0 0 0 0) kidcOk + MINIMAP=kidcMiniMap (0 0 0 0) + +[kidfStartup] + FORM=(0 0 240 160) kidcCancel center + FORMBACKCOLOR=kiclrFormBackground + BITMAP=0 (28 7 0 0) title.rbm + BUTTON=kidcPlay (65 59 110 14) "PLAY" kifntButton + BUTTON=kidcLeaderboard (65 76 110 14) "LEADERBOARD" kifntButton + BUTTON=kidcLoadSavedGame (65 93 110 14) "LOAD SAVED GAME" kifntButton + BUTTON=kidcBuyMe (65 93 110 14) "PURCHASE" kifntButton + BUTTON=kidcDownloadMissions (65 110 110 14) "ADD-ON MISSION PACKS" kifntButton + BUTTON=kidcSetupGame (65 127 39 14) "OPTIONS" kifntButton + BUTTON=kidcHelp (105 127 30 14) "HELP" kifntButton + BUTTON=kidcForums (136 127 39 14) "FORUMS" kifntButton + BITMAP=0 (52 148 0 0) copyright.rbm + LABEL=kidcVersion (239 148 0 12) "" kifntShadow right + +[kidfPlay] + FORM=(0 0 240 160) kidcCancel center + FORMBACKCOLOR=kiclrFormBackground + LABEL=0 (2 6 236 0) "PLAY GAME" kifntTitle center + BUTTON=kidcPlaySinglePlayer (65 42 110 14) "SINGLE PLAYER" kifntButton + BUTTON=kidcPlayMultiPlayer (65 59 110 14) "MULTIPLAYER" kifntButton + BUTTON=kidcCancel (130 140 40 14) "BACK" kifntButton + +[kidfDeleteMissionPack] + FORM=(0 0 160 160) kidcCancel center + FORMBACKCOLOR=kiclrFormBackground + LABEL=0 (28 6 104 0) "DELETE MISSION PACK" kifntTitle center + LIST=kidcMissionPackList (3 19 153 115) kifntShadow + BUTTON=kidcOk (31 140 40 12) "DELETE" kifntButton + BUTTON=kidcCancel (86 140 40 12) "BACK" kifntButton + +[kidfGameOptions] + FORM=(0 0 160 160) kidcCancel center + LABEL=0 (40 6 80 8) "OPTIONS" kifntTitle center + BUTTON=kidcInGameOptions (30 22 100 14) "GAME" kifntButton + BUTTON=kidcSoundOptions (30 40 100 14) "SOUND" kifntButton + BUTTON=kidcPerformanceOptions (30 58 100 14) "PERFORMANCE" kifntButton + BUTTON=kidcColorOptions (30 76 100 14) "COLOR" kifntButton + BUTTON=kidcDisplayOptions (30 94 100 14) "DISPLAY" kifntButton + BUTTON=kidcDeleteMissionPack (30 112 100 14) "DELETE MISSION PACK" kifntButton + BUTTON=kidcCancel (90 140 40 14) "BACK" kifntButton + +[kidfInGameOptions] + FORM=(0 0 160 160) kidcCancel center + LABEL=0 (38 6 83 0) "GAME OPTIONS" kifntTitle center + LABEL=0 (11 26 68 0) "Game Speed" kifntShadow + SLIDER=kidcGameSpeed (11 40 95 8) + LABEL=kidcGameSpeedLabel (121 40 17 0) "0" kifntShadow right + LABEL=0 (11 56 68 0) "Scroll Speed" kifntShadow + SLIDER=kidcScrollSpeed (11 70 95 8) + LABEL=kidcScrollSpeedLabel (121 70 17 0) "0" kifntShadow right + CHECKBOX=kidcLassoSelection (11 62 67 0) "Lasso Selection" kifntShadow + LABEL=0 (11 89 68 0) "Difficulty" kifntShadow + CHECKBOX=kidcEasy (11 99 67 0) "Easy" kifntShadow + CHECKBOX=kidcNormal (11 109 67 0) "Normal" kifntShadow + CHECKBOX=kidcHard (11 119 67 0) "Hard" kifntShadow + BUTTON=kidcOk (13 140 40 12) "OK" kifntButton + BUTTON=kidcCancel (60 140 40 12) "CANCEL" kifntButton + BUTTON=kidcDefault (107 140 40 12) "DEFAULT" kifntButton + +[kidfSoundOptions] + FORM=(0 0 160 160) kidcCancel center + LABEL=0 (38 6 83 0) "SOUND OPTIONS" kifntTitle center + LABEL=kidcVolumeString (11 28 68 0) "Volume" kifntShadow + SLIDER=kidcVol (11 42 95 8) + LABEL=kidcVolLabel (121 42 17 0) "0" kifntShadow + CHECKBOX=kidcMute (11 62 67 0) "Mute" kifntShadow + BUTTON=kidcOk (13 140 40 12) "OK" kifntButton + BUTTON=kidcCancel (60 140 40 12) "CANCEL" kifntButton + +[kidfColorOptions] + FORM=(0 92 160 68) kidcCancel center + LABEL=0 (50 6 59 8) "COLOR OPTIONS" kifntTitle center + LABEL=kidcHueLabelString (8 19 35 8) "Hue" kifntShadow right + SLIDER=kidcHue (48 19 80 8) + LABEL=kidcHueLabel (135 19 12 8) "0" kifntShadow + LABEL=kidcSatLabelString (8 29 35 8) "Saturation" kifntShadow right + SLIDER=kidcSat (48 29 80 8) + LABEL=kidcSatLabel (135 29 12 8) "0" kifntShadow + LABEL=kidcLumLabelString (8 39 35 8) "Brightness" kifntShadow right + SLIDER=kidcLum (48 39 80 8) + LABEL=kidcLumLabel (135 39 12 8) "0" kifntShadow + BUTTON=kidcOk (13 52 40 12) "OK" kifntButton + BUTTON=kidcCancel (60 52 40 12) "CANCEL" kifntButton + BUTTON=kidcDefault (107 52 40 12) "DEFAULT" kifntButton + +[kidfDisplayOptions] + FORM=(0 0 160 160) kidcCancel center + LABEL=0 (38 6 83 0) "DISPLAY OPTIONS" kifntTitle center + LIST=kidcModesList (7 21 146 60) kifntShadow + BUTTON=kidcOk (13 140 40 12) "OK" kifntButton + BUTTON=kidcCancel (60 140 40 12) "CANCEL" kifntButton + BUTTON=kidcDefault (107 140 40 12) "DEFAULT" kifntButton + +[kidfPerformanceOptions] + FORM=(0 0 160 160) kidcCancel center + LABEL=0 (38 6 83 0) "PERFORMANCE OPTIONS" kifntTitle center + CHECKBOX=kidcRocketShots (13 21 134 0) "Rocket Shots" kifntShadow + CHECKBOX=kidcRocketTrails (13 32 134 0) "Rocket Trails" kifntShadow + CHECKBOX=kidcRocketImpacts (13 43 134 0) "Rocket Impacts" kifntShadow + CHECKBOX=kidcShots (13 54 134 0) "Gun & Tank Shots" kifntShadow + CHECKBOX=kidcShotImpacts (13 65 134 0) "Gun & Tank Shot Impacts" kifntShadow + CHECKBOX=kidcSelectionBrackets (13 76 134 0) "Selection Brackets" kifntShadow + CHECKBOX=kidcSmoke (13 87 134 0) "Smoke" kifntShadow + CHECKBOX=kidcEnemyDamageIndicator (13 98 134 0) "Enemy Damage Indicator" kifntShadow + CHECKBOX=kidcScorchMarks (13 109 134 0) "Scorch Marks" kifntShadow + CHECKBOX=kidcSymbolFlashing (13 120 134 0) "Power / Repair / Credits Symbol Flashing" kifntShadow + BUTTON=kidcOk (13 140 40 12) "OK" kifntButton + BUTTON=kidcCancel (60 140 40 12) "CANCEL" kifntButton + BUTTON=kidcDefault (107 140 40 12) "DEFAULT" kifntButton + +[kidfLogin] + FORM=(0 0 240 160) kidcCancel center + FORMBACKCOLOR=kiclrFormBackground + LABEL=0 (0 6 240 0) "MULTIPLAYER LOGIN" kifntTitle center + LABEL=kidcForums (42 28 52 0) "Enter Player Name and Password:" kifntShadow + LABEL=kidcPlayerNameLabel (42 56 52 0) "Player Name:" kifntShadow + EDIT=kidcPlayerName (88 56 90 0) "" kifntShadow + BUTTON=kidcPlayerNamePanel (180 51 18 12) "..." kifntButton + LABEL=kidcPasswordLabel (42 84 52 0) "Password:" kifntShadow + EDIT=kidcPassword (88 84 90 0) "" kifntShadow + BUTTON=kidcPasswordPanel (180 79 18 12) "..." kifntButton + CHECKBOX=kidcAnonymous (42 112 57 0) "Login Anonymously" kifntShadow + BUTTON=kidcLogin (10 140 36 12) "LOGIN" kifntButton + BUTTON=kidcRegister (51 140 48 12) "REGISTER" kifntButton + BUTTON=kidcUpdateAccount (104 140 80 12) "UPDATE ACCOUNT" kifntButton + BUTTON=kidcCancel (189 140 40 12) "CANCEL" kifntButton + +[kidfLobby] + FORM=(0 0 240 160) kidcCancel center + FORMBACKCOLOR=kiclrFormBackground + LABEL=0 (0 6 240 0) "MULTIPLAYER LOBBY" kifntTitle center + LABEL=kidcPlayerNameLabel (2 18 52 0) "Player name:" kifntShadow + LABEL=kidcPlayerName (48 18 190 0) "" kifntShadow + LABEL=kidcLurkerCount (238 18 0 0) "" kifntShadow right + LIST=kidcRoomList (2 34 236 103) kifntShadow + BUTTON=kidcJoinRoom (15 140 48 12) "JOIN ROOM" kifntButton + BUTTON=kidcNewRoom (68 140 59 12) "CREATE ROOM" kifntButton + BUTTON=kidcSignOut (132 140 48 12) "SIGN OUT" kifntButton + BUTTON=kidcCancel (185 140 40 12) "BACK" kifntButton + +[kidfCreateRoom] + FORM=(0 0 240 160) kidcCancel center + FORMBACKCOLOR=kiclrFormBackground + LABEL=0 (0 6 240 0) "CREATE ROOM" kifntTitle center + LABEL=0 (42 28 52 0) "Enter Room Name:" kifntShadow + LABEL=kidcRoomNameLabel (42 56 52 0) "Room Name:" kifntShadow + EDIT=kidcRoomName (88 56 90 0) "" kifntShadow + BUTTON=kidcRoomNamePanel (180 51 18 12) "..." kifntButton + LABEL=kidcPasswordLabel (42 84 52 0) "Password:" kifntShadow + EDIT=kidcPassword (88 84 90 0) "" kifntShadow + BUTTON=kidcPasswordPanel (180 79 18 12) "..." kifntButton + CHECKBOX=kidcPrivate (42 112 57 0) "Private" kifntShadow + BUTTON=kidcOk (75 140 40 12) "OK" kifntButton + BUTTON=kidcCancel (125 140 40 12) "CANCEL" kifntButton + +[kidfRoom] + FORM=(0 0 240 160) kidcCancel center + FORMBACKCOLOR=kiclrFormBackground + LABEL=0 (0 6 240 0) "GAME ROOM" kifntTitle center + LABEL=kidcRoomName (2 18 236 0) "" kifntShadow + LABEL=kidcNoGames (2 76 236 0) "No games in this room. To create one, press Create Game." kifntShadow center + LIST=kidcGameList (2 34 236 103) kifntShadow + BUTTON=kidcJoinGame (11 140 48 12) "JOIN GAME" kifntButton + BUTTON=kidcNewGame (69 140 59 12) "CREATE GAME" kifntButton + BUTTON=kidcChat (138 140 40 12) "CHAT" kifntButton + BUTTON=kidcCancel (188 140 40 12) "BACK" kifntButton + +[kidfCreateGame] + FORM=(0 0 160 160) kidcCancel center + FORMBACKCOLOR=kiclrFormBackground + LABEL=0 (31 6 99 0) "HOST MULTIPLAYER GAME" kifntTitle center + LABEL=kidcGameNameLabel (2 18 46 0) "Game Name:" kifntShadow + EDIT=kidcGameName (50 18 88 0) "" kifntShadow + BUTTON=kidcGameNamePanel (140 13 18 12) "..." kifntButton + LABEL=0 (2 30 50 0) "Game Speed:" kifntShadow + SLIDER=kidcGameSpeed (50 30 88 8) + LABEL=kidcGameSpeedLabel (140 30 17 0) "0" kifntShadow right + LABEL=0 (2 42 18 0) "Choose a map to play on:" kifntShadow + LIST=kidcMapList (2 52 156 76) kifntShadow + BUTTON=kidcOk (17 140 60 12) "CREATE GAME" kifntButton + BUTTON=kidcCancel (93 140 50 12) "CANCEL" kifntButton + +[kidfCreateGameWide] + FORM=(0 0 240 160) kidcCancel center + FORMBACKCOLOR=kiclrFormBackground + LABEL=0 (0 6 240 0) "CREATE MULTIPLAYER GAME" kifntTitle center + LABEL=0 (42 18 50 0) "Game Speed:" kifntShadow + SLIDER=kidcGameSpeed (90 18 88 8) + LABEL=kidcGameSpeedLabel (180 18 17 0) "0" kifntShadow right + RADIOBUTTONBAR=kidcCategories (0 32 0 0) "Challenge|Add-On" kifntShadow + LIST=kidcChallengeList (2 47 236 79) kifntShadow + LIST=kidcAddOnList (2 47 236 79) kifntShadow + LABEL=kidcMissionPackInfo (2 127 236 12) "" kifntShadow + LABEL=kidcAddOnMessage (2 114 236 0) "Install Add-On Mission Packs from the Main Menu" kifntShadow center + BUTTON=kidcOk (35 141 60 12) "CREATE GAME" kifntButton + BUTTON=kidcChat(105 141 40 12) "CHAT" kifntButton + BUTTON=kidcCancel (155 141 50 12) "CANCEL" kifntButton + +[kidfGameStart] + FORM=(0 0 240 160) kidcCancel center + FORMBACKCOLOR=kiclrFormBackground + LABEL=0 (74 6 91 0) "WAIT FOR JOINING PLAYERS" kifntTitle center + LABEL=0 (42 18 23 0) "Game:" kifntShadow + LABEL=kidcGameName (84 18 10 0) "Acquiring game details..." kifntShadow + LABEL=0 (42 29 18 0) "Map:" kifntShadow + LABEL=kidcMapName (84 29 10 0) "" kifntShadow + LABEL=0 (42 40 37 0) "# players:" kifntShadow + LABEL=kidcNumPlayers (84 40 10 0) "" kifntShadow + LABEL=0 (122 40 37 0) "Game Speed:" kifntShadow + LABEL=kidcGameSpeedLabel (172 40 37 0) "" kifntShadow + LABEL=0 (104 66 32 0) "PLAYERS" kifntTitle center + LIST=kidcPlayerList (52 76 136 57) kifntShadow + BUTTON=kidcOk (40 140 50 12) "READY" kifntButton + BUTTON=kidcChat (100 140 40 12) "CHAT" kifntButton + BUTTON=kidcCancel (150 140 50 12) "CANCEL" kifntButton + +[kidfPickLevel] + FORM=(0 0 160 160) kidcCancel center + FORMBACKCOLOR=kiclrFormBackground + LABEL=0 (28 6 104 0) "SELECT MISSION" kifntTitle center + LIST=kidcLevelList (3 19 153 97) kifntShadow + LABEL=0 (80 118 0 0) "For more missions go to:" kifntShadow center + LABEL=0 (80 128 0 0) "http://www.WarfareIncorporated.com" kifntShadow center + BUTTON=kidcOk (31 140 40 12) "PLAY" kifntButton + BUTTON=kidcCancel (86 140 40 12) "BACK" kifntButton + +[kidfSelectMissionWide] + FORM=(0 0 240 160) kidcCancel center + FORMBACKCOLOR=kiclrFormBackground + LABEL=0 (0 6 240 0) "PLAY SINGLE PLAYER MISSION" kifntTitle center + RADIOBUTTONBAR=kidcCategories (0 19 0 0) "Story|Challenge|Add-On" kifntShadow + LIST=kidcStoryList (2 33 236 93) kifntShadow + LIST=kidcChallengeList (2 33 236 93) kifntShadow + LIST=kidcAddOnList (2 33 236 93) kifntShadow + LABEL=kidcMissionPackInfo (2 127 236 12) "" kifntShadow + LABEL=kidcAddOnMessage (2 114 236 0) "Install Add-On Mission Packs from the Main Menu" kifntShadow center + BUTTON=kidcOk (71 141 40 12) "PLAY" kifntButton + BUTTON=kidcCancel (126 141 40 12) "BACK" kifntButton + +[kidfAddOnSingleMulti] + FORM=(0 0 240 160) kidcCancel center + FORMBACKCOLOR=kiclrFormBackground + LABEL=0 (2 6 236 0) "ADD-ON MISSION PACKS" kifntTitle center + BUTTON=kidcPlaySinglePlayer (55 42 120 14) "SINGLE PLAYER MISSION PACKS" kifntButton + BUTTON=kidcPlayMultiPlayer (55 59 120 14) "MULTIPLAYER MISSION PACKS" kifntButton + BUTTON=kidcCancel (130 140 40 14) "BACK" kifntButton + +[kidfDownloadMissionPackWide] + FORM=(0 0 240 160) kidcCancel center + FORMBACKCOLOR=kiclrFormBackground + LABEL=0 (0 6 240 0) "DOWNLOAD ADD-ON MISSION PACKS" kifntTitle center + LIST=kidcMissionPackList (2 24 236 86) kifntShadow + LABEL=kidcStatus (0 16 0 0) "+" kifntShadow + LABEL=kidcTitle (0 16 0 0) "Title" kifntShadow + LABEL=kidcNumPlayers (0 16 0 0) "# Players" kifntShadow + LABEL=kidcNumMissions (0 16 0 0) "# Missions" kifntShadow + LABEL=kidcMissionPackInfo (2 112 236 25) "" kifntShadow multiline clipvert + BUTTON=kidcOk (40 141 55 12) "DOWNLOAD" kifntButton + BUTTON=kidcDiscuss (103 141 48 12) "DISCUSS" kifntButton + BUTTON=kidcCancel (159 141 40 12) "DONE" kifntButton + +[kidfChooseServer] + FORM=(0 0 240 160) kidcCancel center + FORMBACKCOLOR=kiclrFormBackground + LABEL=0 (0 6 240 0) "CHOOSE SERVER" kifntTitle center + LABEL=kidcServerName (0 20 0 0) "Name" kifntShadow + LABEL=kidcServerLocation (0 20 0 0) "Location" kifntShadow + LABEL=kidcServerStatus (0 20 0 0) "Status" kifntShadow + LABEL=kidcNumPlayers (0 20 0 0) "# Players" kifntShadow + LIST=kidcServerList (2 32 236 111) kifntShadow + BUTTON=kidcOk (40 141 50 12) "CONNECT" kifntButton + BUTTON=kidcRefresh (100 141 50 12) "REFRESH" kifntButton + BUTTON=kidcCancel (160 141 40 12) "BACK" kifntButton + +[kidfPickTransport] + FORM=(0 0 160 160) kidcCancel center + FORMBACKCOLOR=kiclrFormBackground + LABEL=0 (28 6 104 0) "CHOOSE A TRANSPORT" kifntTitle center + BUTTON=kidcTransport1 (30 34 100 14) "TRANSPORT 1" kifntButton + BUTTON=kidcTransport2 (30 51 100 14) "TRANSPORT 2" kifntButton + BUTTON=kidcTransport3 (30 68 100 14) "TRANSPORT 3" kifntButton + BUTTON=kidcTransport4 (30 85 100 14) "TRANSPORT 4" kifntButton + BUTTON=kidcTransport5 (30 102 100 14) "TRANSPORT 5" kifntButton + BUTTON=kidcTransport6 (30 119 100 14) "TRANSPORT 6" kifntButton + LABEL=kidcNoTransportsAvailable (10 73 140 0) "No supported communication methods are available." kifntShadow center multiline + BUTTON=kidcCancel (90 140 40 12) "BACK" kifntButton + +[kidfInGameMenu] + FORM=(0 0 160 160) kidcCancel center + FORMBACKCOLOR=kiclrFormBackground + LABEL=0 (62 6 35 0) "MENU" kifntTitle center + BUTTON=kidcObjectives (30 19 100 14) "STATE OBJECTIVES" kifntButton + BUTTON=kidcOptions (30 36 100 14) "OPTIONS" kifntButton + BUTTON=kidcSaveGame (30 53 100 14) "SAVE GAME" kifntButton + BUTTON=kidcLoadGame (30 70 100 14) "LOAD GAME" kifntButton + BUTTON=kidcRestartMission (30 87 100 14) "RESTART MISSION" kifntButton + BUTTON=kidcAbortMission (30 104 100 14) "ABORT MISSION" kifntButton + BUTTON=kidcChat (30 121 100 14) "CHAT" kifntButton + BUTTON=kidcExitGame (30 121 100 14) "EXIT WARFARE" kifntButton + BUTTON=kidcHelp (30 138 48 14) "HELP" kifntButton + BUTTON=kidcCancel (82 138 48 14) "BACK" kifntButton + +[kidfTestOptions] + FORM=(0 0 160 151) kidcOk center + LABEL=0 (53 2 54 0) "TEST OPTIONS" kifntTitle center + CHECKBOX=kidcDrawPaths (2 15 57 0) "Draw paths" kifntShadow + CHECKBOX=kidcDrawLines (2 24 82 0) "Draw target lines" kifntShadow + CHECKBOX=kidcOvermind (2 33 77 0) "Enable Overmind" kifntShadow + CHECKBOX=kidcMaxRepaint (2 42 59 0) "Max repaint" kifntShadow + CHECKBOX=kidcShowFPS (2 51 48 0) "Show FPS" kifntShadow + CHECKBOX=kidcGodMode (2 60 50 0) "God mode" kifntShadow + CHECKBOX=kidcSuspendUpdates (2 69 81 0) "Suspend Updates" kifntShadow + CHECKBOX=kidcAutosave (2 78 81 0) "Autosave" kifntShadow + CHECKBOX=kidcDrawUpdateRects (2 87 70 0) "Draw Update Rects" kifntShadow + CHECKBOX=kidcStylusUI (2 96 56 0) "Stylus UI" kifntShadow + CHECKBOX=kidcShowStats (2 96 56 0) "Show stats" kifntShadow + CHECKBOX=kidcLockStep (2 105 82 0) "Lock Step Simulations" kifntShadow + BUTTON=kidcClearFog (104 14 54 12) "Clear Fog" kifntButton + BUTTON=kidcMemoryUse (104 30 54 12) "Memory..." kifntButton + BUTTON=kidcHelp (104 46 54 12) "Help" kifntButton + BUTTON=kidcGobCount (104 62 54 12) "Counts..." kifntButton + BUTTON=kidcBreak (104 78 54 12) "Break" kifntButton + BUTTON=kidcOk (51 135 54 12) "OK" kifntButton + +[kidfMemoryUse] + FORM=(0 0 160 151) kidcOk center + FORMBACKCOLOR=kiclrFormBackground + LABEL=0 (55 2 50 0) "MEMORY USE" kifntTitle center + LABEL=kidcDynDbInitial (2 15 0 0) "" kifntShadow + LABEL=kidcDynUse (2 25 0 0) "" kifntShadow + LABEL=kidcMmgrDynDbReserve (2 35 0 0) "" kifntShadow + LABEL=kidcMmgrUse (2 45 0 0) "" kifntShadow + LABEL=kidcCacheUse (2 65 0 0) "" kifntShadow + BUTTON=kidcClearCache (2 79 70 12) "Clear Cache" kifntButton + CHECKBOX=kidcLimitCache (2 96 56 0) "Limit Cache" kifntShadow + BUTTON=kidcAdd10KCache (2 110 25 12) "+10k" kifntButton + BUTTON=kidcSub10KCache (31 110 25 12) "-10k" kifntButton + LABEL=kidcCacheLimit (2 127 50 0) "Cache Limit: " kifntShadow + BUTTON=kidcOk (51 135 54 12) "OK" kifntButton + +[kidfObjectives] + FORM=(0 0 160 160) kidcCancel center + FORMBACKCOLOR=kiclrFormBackground + LABEL=kidcMissionTitle (62 6 35 0) "" kifntTitle center + LABEL=0 (62 14 35 0) "OBJECTIVES" kifntTitle center + LABEL=kidcObjectiveText1 (4 25 0 0) "" kifntShadow + LABEL=kidcObjectiveStatus1 (155 25 0 0) "" kifntShadow right + LABEL=kidcObjectiveText2 (4 34 0 0) "" kifntShadow + LABEL=kidcObjectiveStatus2 (155 34 0 0) "" kifntShadow right + LABEL=kidcObjectiveText3 (4 43 0 0) "" kifntShadow + LABEL=kidcObjectiveStatus3 (155 43 0 0) "" kifntShadow right + LABEL=kidcObjectiveText4 (4 52 0 0) "" kifntShadow + LABEL=kidcObjectiveStatus4 (155 52 0 0) "" kifntShadow right + LABEL=kidcPage1 (80 63 0 0) "BRIEFING" kifntTitle center + LABEL=kidcPage2 (80 63 0 0) "STATISTICS" kifntTitle center + LABEL=kidcObjectiveInfo (4 74 152 69) "" kifntShadow left multiline + LABEL=kidcPage2 (4 74 0 0) "Enemy units destroyed" kifntShadow + LABEL=kidcMobileUnitsKilled (155 74 0 0) "" kifntShadow right + LABEL=kidcPage2 (4 83 0 0) "Enemy buildings destroyed" kifntShadow + LABEL=kidcStructuresKilled (155 83 0 0) "" kifntShadow right + LABEL=kidcPage2 (4 92 0 0) "Friendly units lost" kifntShadow + LABEL=kidcMobileUnitsLost (155 92 0 0) "" kifntShadow right + LABEL=kidcPage2 (4 101 0 0) "Friendly buildings lost" kifntShadow + LABEL=kidcStructuresLost (155 101 0 0) "" kifntShadow right + LABEL=kidcPage2 (4 110 0 0) "Credits acquired / spent" kifntShadow + LABEL=kidcCreditsAction (155 110 0 0) "" kifntShadow right + LABEL=kidcPage2 (4 119 0 0) "Elapsed time" kifntShadow + LABEL=kidcGameTime (155 119 0 0) "" kifntShadow right + LABEL=kidcPage2 (4 128 0 0) "Title" kifntShadow + BUTTON=kidcStatistics (50 140 60 14) "STATISTICS" kifntButton center + LABEL=kidcRankTitle (155 128 0 0) "{$ranktitle}" kifntShadow right + BUTTON=kidcInfo (50 140 60 14) "BRIEFING" kifntButton center + BUTTON=kidcCancel (120 140 40 14) "OK" kifntButton center + +[kidfWinSummary] + FORM=(0 0 160 160) kidcCancel center + FORMBACKCOLOR=kiclrFormBackground + LABEL=kidcMissionTitle (62 6 35 0) "" kifntTitle center + LABEL=0 (62 14 35 0) "PERFORMANCE REVIEW" kifntTitle center + LABEL=kidcObjectiveText1 (4 25 0 0) "" kifntShadow + LABEL=kidcObjectiveStatus1 (155 25 0 0) "" kifntShadow right + LABEL=kidcObjectiveText2 (4 34 0 0) "" kifntShadow + LABEL=kidcObjectiveStatus2 (155 34 0 0) "" kifntShadow right + LABEL=kidcObjectiveText3 (4 43 0 0) "" kifntShadow + LABEL=kidcObjectiveStatus3 (155 43 0 0) "" kifntShadow right + LABEL=kidcObjectiveText4 (4 52 0 0) "" kifntShadow + LABEL=kidcObjectiveStatus4 (155 52 0 0) "" kifntShadow right + LABEL=kidcMissionResult (80 63 0 0) "OBJECTIVES COMPLETED!" kifntTitle center + LABEL=kidcPage2 (4 74 0 0) "Enemy units destroyed" kifntShadow + LABEL=kidcMobileUnitsKilled (155 74 0 0) "" kifntShadow right + LABEL=kidcPage2 (4 83 0 0) "Enemy buildings destroyed" kifntShadow + LABEL=kidcStructuresKilled (155 83 0 0) "" kifntShadow right + LABEL=kidcPage2 (4 92 0 0) "Friendly units lost" kifntShadow + LABEL=kidcMobileUnitsLost (155 92 0 0) "" kifntShadow right + LABEL=kidcPage2 (4 101 0 0) "Friendly buildings lost" kifntShadow + LABEL=kidcStructuresLost (155 101 0 0) "" kifntShadow right + LABEL=kidcPage2 (4 110 0 0) "Credits acquired / spent" kifntShadow + LABEL=kidcCreditsAction (155 110 0 0) "" kifntShadow right + LABEL=kidcPage2 (4 119 0 0) "Elapsed time" kifntShadow + LABEL=kidcGameTime (155 119 0 0) "" kifntShadow right + LABEL=kidcPage2 (4 128 0 0) "Title" kifntShadow + LABEL=kidcRankTitle (155 128 0 0) "{$ranktitle}" kifntShadow right + BUTTON=kidcCancel (80 140 40 14) "OK" kifntButton center + +[kidfLoseSummary] + FORM=(0 0 160 160) kidcCancel center + FORMBACKCOLOR=kiclrFormBackground + LABEL=kidcMissionTitle (62 6 35 0) "" kifntTitle center + LABEL=0 (62 14 35 0) "PERFORMANCE REVIEW" kifntTitle center + LABEL=kidcObjectiveText1 (4 25 0 0) "" kifntShadow + LABEL=kidcObjectiveStatus1 (155 25 0 0) "" kifntShadow right + LABEL=kidcObjectiveText2 (4 34 0 0) "" kifntShadow + LABEL=kidcObjectiveStatus2 (155 34 0 0) "" kifntShadow right + LABEL=kidcObjectiveText3 (4 43 0 0) "" kifntShadow + LABEL=kidcObjectiveStatus3 (155 43 0 0) "" kifntShadow right + LABEL=kidcObjectiveText4 (4 52 0 0) "" kifntShadow + LABEL=kidcObjectiveStatus4 (155 52 0 0) "" kifntShadow right + LABEL=0 (80 63 0 0) "MISSION FAILED" kifntTitle center + LABEL=kidcPage2 (4 74 0 0) "Enemy units destroyed" kifntShadow + LABEL=kidcMobileUnitsKilled (155 74 0 0) "" kifntShadow right + LABEL=kidcPage2 (4 83 0 0) "Enemy buildings destroyed" kifntShadow + LABEL=kidcStructuresKilled (155 83 0 0) "" kifntShadow right + LABEL=kidcPage2 (4 92 0 0) "Friendly units lost" kifntShadow + LABEL=kidcMobileUnitsLost (155 92 0 0) "" kifntShadow right + LABEL=kidcPage2 (4 101 0 0) "Friendly buildings lost" kifntShadow + LABEL=kidcStructuresLost (155 101 0 0) "" kifntShadow right + LABEL=kidcPage2 (4 110 0 0) "Credits acquired / spent" kifntShadow + LABEL=kidcCreditsAction (155 110 0 0) "" kifntShadow right + LABEL=kidcPage2 (4 119 0 0) "Elapsed time" kifntShadow + LABEL=kidcGameTime (155 119 0 0) "" kifntShadow right + LABEL=kidcPage2 (4 128 0 0) "Title" kifntShadow + LABEL=kidcRankTitle (155 128 0 0) "{$ranktitle}" kifntShadow right + BUTTON=kidcRestartMission (3 140 40 14) "RETRY" kifntButton + BUTTON=kidcAbortMission (50 140 40 14) "ABORT" kifntButton + BUTTON=kidcLoadGame (97 140 60 14) "LOAD SAVED" kifntButton + +[kidfBuildInfantry] + FORM=(0 0 160 143) kidcCancel + LABEL=0 (2 5 82 0) "RECRUIT PERSONNEL" kifntTitle + BUTTON=kidcHelp (135 3 10 12) "" kifntButton nocenter helpup.tbm helpdown.tbm + BUTTON=kidcCancel (147 3 10 12) "" kifntButton nocenter cancelup.tbm canceldown.tbm + ANIMLIST=kidcList (-1 17 28 121) kifntShadow 17 + LABEL=kidcDescription (30 79 127 8) "" kifntShadow left multiline + LABEL=kidcName (30 19 75 0) "" kifntShadow + LABEL=0 (30 31 22 0) "COST" kifntShadow + LABEL=0 (30 40 22 0) "ARMOR" kifntShadow + LABEL=0 (30 49 22 0) "DAMAGE" kifntShadow + LABEL=0 (30 58 22 0) "SPEED" kifntShadow + LABEL=0 (30 67 22 0) "RANGE" kifntShadow + LABEL=kidcCost (136 31 22 0) "" kifntShadow right + LABEL=kidcArmorStrength (136 40 22 0) "" kifntShadow right + LABEL=kidcMoveRate (136 58 22 0) "" kifntShadow right + LABEL=kidcWeaponRange (136 67 22 0) "" kifntShadow right + LABEL=kidcLimitReached (33 123 32 12) "Limit Reached" kifntShadow multiline center + BUTTON=kidcOrder (30 124 32 12) "Order" kifntButton + BUTTON=kidcCancelOrder (65 124 59 12) "Cancel Order" kifntButton + BUTTON=kidcOk (127 124 31 12) "Close" kifntButton + PIPMETER=kidcCostMeter (64 31 71 9) + PIPMETER=kidcArmorStrengthMeter (64 40 71 9) + DAMAGEMETER=kidcWeaponStrengthMeter (64 49 94 9) + PIPMETER=kidcMoveRateMeter (64 58 71 9) + PIPMETER=kidcWeaponRangeMeter (64 67 71 9) + +[kidfBuildVehicle] + FORM=(0 0 160 143) kidcCancel + LABEL=0 (2 5 82 0) "ORDER VEHICLE" kifntTitle + BUTTON=kidcHelp (135 3 10 12) "" kifntButton nocenter helpup.tbm helpdown.tbm + BUTTON=kidcCancel (147 3 10 12) "" kifntButton nocenter cancelup.tbm canceldown.tbm + ANIMLIST=kidcList (-1 17 28 121) kifntShadow 17 + LABEL=kidcDescription (30 79 127 8) "" kifntShadow left multiline + LABEL=kidcName (30 19 75 0) "" kifntShadow + LABEL=0 (30 31 22 0) "COST" kifntShadow + LABEL=0 (30 40 22 0) "ARMOR" kifntShadow + LABEL=0 (30 49 22 0) "DAMAGE" kifntShadow + LABEL=0 (30 58 22 0) "SPEED" kifntShadow + LABEL=0 (30 67 22 0) "RANGE" kifntShadow + LABEL=kidcCost (136 31 22 0) "" kifntShadow right + LABEL=kidcArmorStrength (136 40 22 0) "" kifntShadow right + LABEL=kidcMoveRate (136 58 22 0) "" kifntShadow right + LABEL=kidcWeaponRange (136 67 22 0) "" kifntShadow right + LABEL=kidcLimitReached (33 123 32 12) "Limit Reached" kifntShadow multiline center + BUTTON=kidcOrder (30 124 32 12) "Order" kifntButton + BUTTON=kidcCancelOrder (65 124 59 12) "Cancel Order" kifntButton + BUTTON=kidcOk (127 124 31 12) "Close" kifntButton + PIPMETER=kidcCostMeter (64 31 71 9) + PIPMETER=kidcArmorStrengthMeter (64 40 71 9) + DAMAGEMETER=kidcWeaponStrengthMeter (64 49 94 9) + PIPMETER=kidcMoveRateMeter (64 58 71 9) + PIPMETER=kidcWeaponRangeMeter (64 67 71 9) + +[kidfBuildStructure] + FORM=(0 0 160 143) kidcCancel + LABEL=0 (2 5 82 0) "ORDER BUILDING" kifntTitle + BUTTON=kidcHelp (135 3 10 12) "" kifntButton nocenter helpup.tbm helpdown.tbm + BUTTON=kidcCancel (147 3 10 12) "" kifntButton nocenter cancelup.tbm canceldown.tbm + ANIMLIST=kidcList (-1 17 28 121) kifntShadow 18 + LABEL=kidcDescription (30 79 127 8) "" kifntShadow left multiline + LABEL=kidcName (30 19 75 0) "UNIT NAME" kifntShadow + LABEL=0 (30 31 22 0) "COST" kifntShadow + LABEL=0 (30 40 22 0) "ARMOR" kifntShadow + LABEL=0 (30 49 22 0) "DAMAGE" kifntShadow + LABEL=0 (30 58 22 0) "P DEMAND" kifntShadow + LABEL=0 (30 67 22 0) "P SUPPLY" kifntShadow + LABEL=kidcCost (136 31 22 0) "2000" kifntShadow right + LABEL=kidcArmorStrength (136 40 22 0) "HEAVY" kifntShadow right + LABEL=kidcPowerDemand (136 58 22 0) "500" kifntShadow right + LABEL=kidcPowerSupply (136 67 22 0) "0" kifntShadow right + LABEL=kidcLimitReached (30 127 93 12) "Building Limit Reached" kifntShadow center + BUTTON=kidcOk (86 124 34 12) "Build" kifntButton + BUTTON=kidcCancel (124 124 34 12) "Close" kifntButton + PIPMETER=kidcCostMeter (64 31 71 9) + PIPMETER=kidcArmorStrengthMeter (64 40 71 9) + DAMAGEMETER=kidcWeaponStrengthMeter (64 49 94 9) + PIPMETER=kidcPowerDemandMeter (64 58 71 9) + PIPMETER=kidcPowerSupplyMeter (64 67 71 9) + +[kidfUpgrade] + FORM=(0 0 160 143) kidcCancel + LABEL=0 (2 5 82 0) "R&D TECHNOLOGIES" kifntTitle + BUTTON=kidcHelp (135 3 10 12) "" kifntButton nocenter helpup.tbm helpdown.tbm + BUTTON=kidcCancel (147 3 10 12) "" kifntButton nocenter cancelup.tbm canceldown.tbm + ANIMLIST=kidcList (-1 17 28 121) kifntShadow 18 + LABEL=kidcName (30 19 75 0) "UPGRADE NAME" kifntShadow + LABEL=kidcCostLabel (30 31 22 0) "COST" kifntShadow + LABEL=kidcCost (136 31 22 0) "2000" kifntShadow right + LABEL=kidcDescription (30 49 127 8) "" kifntShadow left multiline + LABEL=kidcPrerequisitesLabel (30 97 22 0) "PREREQUISITES" kifntShadow + LABEL=kidcPrerequisites (30 106 127 8) "" kifntShadow left multiline + BUTTON=kidcOk (78 124 42 12) "Research" kifntButton + BUTTON=kidcCancel (124 124 34 12) "Close" kifntButton + PIPMETER=kidcCostMeter (64 31 71 9) + +[kidfPlaceStructure] + FORM=(0 0 320 320) kidcCancel + BUTTON=kidcOk (120 135 10 12) "" kifntButton nocenter okup.tbm okdown.tbm + BUTTON=kidcCancel (0 135 10 12) "" kifntButton nocenter cancelup.tbm canceldown.tbm + +[kidfHrcMenu] + FORM=(0 0 66 68) kidcCancel + LABEL=kidcTitle (0 2 66 0) "" kifntTitle center + BUTTON=kidcBuild (3 14 60 12) "" kifntButton nocenter build_btn_up.tbm build_btn_down.tbm build_btn_disabled.tbm + BUTTON=kidcRepair (3 31 60 12) "" kifntButton nocenter repair_btn_up.tbm repair_btn_down.tbm repair_btn_disabled.tbm + BUTTON=kidcAbortRepair (3 31 60 12) "" kifntButton nocenter abort_repair_btn_up.tbm abort_repair_btn_down.tbm + BUTTON=kidcSelfDestruct (3 48 60 12) "" kifntButton nocenter sell_btn_up.tbm sell_btn_down.tbm sell_btn_disabled.tbm + +[kidfVtsMenu] + FORM=(0 0 66 68) kidcCancel + LABEL=kidcTitle (0 2 66 0) "" kifntTitle center + BUTTON=kidcBuild (3 14 60 12) "" kifntButton nocenter build_btn_up.tbm build_btn_down.tbm build_btn_disabled.tbm + BUTTON=kidcRepair (3 31 60 12) "" kifntButton nocenter repair_btn_up.tbm repair_btn_down.tbm repair_btn_disabled.tbm + BUTTON=kidcAbortRepair (3 31 60 12) "" kifntButton nocenter abort_repair_btn_up.tbm abort_repair_btn_down.tbm + BUTTON=kidcSelfDestruct (3 48 60 12) "" kifntButton nocenter sell_btn_up.tbm sell_btn_down.tbm sell_btn_disabled.tbm + +[kidfHqMenu] + FORM=(0 0 66 68) kidcCancel + LABEL=kidcTitle (0 2 66 0) "" kifntTitle center + BUTTON=kidcBuild (0 14 0 0) "" kifntButton nocenter build_btn_up.tbm build_btn_down.tbm build_btn_disabled.tbm + BUTTON=kidcAbortBuild (0 14 0 0) "" kifntButton nocenter abort_build_btn_up.tbm abort_build_btn_down.tbm + BUTTON=kidcRepair (14 14 0 0) "" kifntButton nocenter repair_btn_up.tbm repair_btn_down.tbm repair_btn_disabled.tbm + BUTTON=kidcAbortRepair (14 14 0 0) "" kifntButton nocenter abort_repair_btn_up.tbm abort_repair_btn_down.tbm + BUTTON=kidcSelfDestruct (28 14 0 0) "" kifntButton nocenter sell_btn_up.tbm sell_btn_down.tbm sell_btn_disabled.tbm + +[kidfResearchMenu] + FORM=(0 0 76 68) kidcCancel + LABEL=kidcTitle (0 2 76 0) "" kifntTitle center + BUTTON=kidcResearch (3 14 60 12) "" kifntButton nocenter build_btn_up.tbm build_btn_down.tbm build_btn_disabled.tbm + BUTTON=kidcAbortUpgrade (3 14 60 12) "" kifntButton nocenter abort_build_btn_up.tbm abort_build_btn_down.tbm + BUTTON=kidcRepair (3 31 60 12) "" kifntButton nocenter repair_btn_up.tbm repair_btn_down.tbm repair_btn_disabled.tbm + BUTTON=kidcAbortRepair (3 31 60 12) "" kifntButton nocenter abort_repair_btn_up.tbm abort_repair_btn_down.tbm + BUTTON=kidcSelfDestruct (3 48 60 12) "" kifntButton nocenter sell_btn_up.tbm sell_btn_down.tbm sell_btn_disabled.tbm + +[kidfStructMenu] + FORM=(0 0 66 51) kidcCancel + LABEL=kidcTitle (0 2 66 0) "" kifntTitle center + BUTTON=kidcRepair (3 31 60 12) "" kifntButton nocenter repair_btn_up.tbm repair_btn_down.tbm repair_btn_disabled.tbm + BUTTON=kidcAbortRepair (3 31 60 12) "" kifntButton nocenter abort_repair_btn_up.tbm abort_repair_btn_down.tbm + BUTTON=kidcSelfDestruct (3 48 60 12) "" kifntButton nocenter sell_btn_up.tbm sell_btn_down.tbm sell_btn_disabled.tbm + +[kidfUnitMenu] + FORM=(0 0 66 51) kidcCancel + LABEL=kidcTitle (0 2 66 0) "" kifntTitle center + +[kidfMobileHqMenu] + FORM=(0 0 66 51) kidcCancel + LABEL=kidcTitle (0 2 66 0) "" kifntTitle center + BUTTON=kidcTransform (3 14 60 12) "Transform" kifntButton + LABEL=kidcCantTransform (2 14 62 24) "Can't Transform. Move to clear area." kifntShadow left multiline + +[kidfMinerMenu] + FORM=(0 0 66 51) kidcCancel + LABEL=kidcTitle (0 2 66 0) "" kifntTitle center + BUTTON=kidcDeliver (3 14 60 12) "Deliver" kifntButton + +[kidfMessageBox] + FORM=(0 0 140 50) kidcOk center topmost + LABEL=kidcTitle (61 2 18 0) "Title" kifntTitle center + LABEL=kidcMessage (6 15 128 8) "Message" kifntShadow center multiline + BUTTON=kidcOk (44 32 54 12) "OK" kifntButton + +[kidfMessageBoxQuery] + FORM=(0 0 140 50) kidcOk center + LABEL=kidcTitle (61 2 18 0) "Title" kifntTitle center + LABEL=kidcMessage (6 15 128 8) "Message" kifntShadow center multiline + BUTTON=kidcOk (10 32 54 12) "Yes" kifntButton + BUTTON=kidcCancel (74 32 54 12) "No" kifntButton + +[kidfDownloadBox] + FORM=(0 0 190 70) kidcOk center topmost + LABEL=kidcTitle (61 2 18 0) "Download Mission Pack" kifntTitle center + LABEL=kidcMessage (6 15 178 8) "Message" kifntShadow center multiline + LABEL=kidcStatus (6 33 178 8) "Status" kifntShadow center multiline + BUTTON=kidcOk (36 52 54 12) "OK" kifntButton + BUTTON=kidcPlay (100 52 54 12) "PLAY" kifntButton + +[kidfBluetoothPatchQuery] + FORM=(0 0 140 50) kidcOk center + LABEL=kidcTitle (61 2 18 0) "Title" kifntTitle center + LABEL=kidcMessage (6 15 128 8) "Message" kifntShadow center multiline + BUTTON=kidcOk (6 32 69 12) "Already installed" kifntButton + BUTTON=kidcCancel (80 32 54 12) "Cancel" kifntButton + +[kidfLoadGame] + FORM=(0 0 160 160) kidcCancel center + FORMBACKCOLOR=kiclrFormBackground + LIST=kidcGameList (3 19 153 115) kifntShadow + LABEL=0 (38 6 84 0) "LOAD SAVED GAME" kifntTitle center + BUTTON=kidcOk (31 140 40 12) "LOAD" kifntButton + BUTTON=kidcCancel (86 140 40 12) "BACK" kifntButton + +[kidfSaveGame] + FORM=(0 0 160 160) kidcCancel center + FORMBACKCOLOR=kiclrFormBackground + LIST=kidcGameList (3 19 153 115) kifntShadow + LABEL=0 (38 6 84 0) "SAVE GAME" kifntTitle center + BUTTON=kidcOk (31 140 40 12) "Save" kifntButton + BUTTON=kidcCancel (86 140 40 12) "Cancel" kifntButton + +[kidfAreYouSure] + FORM=(0 0 73 45) kidcCancel + LABEL=kidcTitle (0 2 73 0) "SELL BUILDING" kifntTitle center + LABEL=0 (12 15 55 0) "Are you sure?" kifntShadow center + BUTTON=kidcOk (4 29 29 12) "Yes" kifntButton + BUTTON=kidcCancel (42 29 27 12) "No" kifntButton + +[kidfEcomLarge] + FORM=(0 0 160 150) kidcOk + LABEL=kidcFrom (3 19 56 0) "Olstrom@@acme" kifntShadow + LABEL=kidcTo (3 34 54 0) "Olstrom@@acme" kifntShadow + BITMAP=kidcFromBitmap (80 4 0 0) olstrom.tbm + BITMAP=kidcToBitmap (121 4 0 0) jana.tbm + ECOMTEXT=kidcMessage (4 50 152 81) "EcomText" kifntEcom left + BUTTON=kidcOk (108 135 50 12) "OK" kifntButton + +[kidfEcomSmall] + FORM=(0 0 160 75) kidcOk + LABEL=kidcFrom (30 6 49 0) "olstrom@@acme" kifntShadow + LABEL=kidcTo (96 6 53 0) "olstrom@@acme" kifntShadow + ECOMTEXT=kidcMessage (4 16 152 40) "EcomText" kifntEcom left + BUTTON=kidcOk (108 60 50 12) "OK" kifntButton + +[kidfHelp] + FORM=(0 0 160 160) kidcOk center + HELP=kidcHelp (5 6 150 130) + BUTTON=kidcOk (5 140 45 12) "OK" kifntButton + BUTTON=kidcIndex (55 140 45 12) "Index" kifntButton + BUTTON=kidcBack (119 142 6 6) "" kifntButton nocenter backup.tbm backdown.tbm + BUTTON=kidcNextPage (130 144 4 4) "" kifntButton nocenter scrolldownup.tbm scrolldowndown.tbm + BUTTON=kidcPrevPage (140 144 4 4) "" kifntButton nocenter scrollupup.tbm scrollupdown.tbm + +[kidfHelpWide] + FORM=(0 0 240 160) kidcOk center + HELP=kidcHelp (5 6 228 130) + BUTTON=kidcOk (70 140 45 12) "OK" kifntButton + BUTTON=kidcIndex (125 140 45 12) "Index" kifntButton + BUTTON=kidcBack (189 142 6 6) "" kifntButton nocenter backup.tbm backdown.tbm + BUTTON=kidcNextPage (200 144 4 4) "" kifntButton nocenter scrolldownup.tbm scrolldowndown.tbm + BUTTON=kidcPrevPage (210 144 4 4) "" kifntButton nocenter scrollupup.tbm scrollupdown.tbm + +[kidfCutScene] + FORM=(0 0 160 160) kidcOk center + BITMAP=kidcBitmap (5 5 150 0) + ECOMTEXT=kidcMessage (5 51 150 70) "EcomText" kifntShadow left + BUTTON=kidcOk (105 141 50 12) "More..." kifntButton + +[kidfGobCount] + FORM=(0 0 160 160) kidcOk center + FORMBACKCOLOR=kiclrFormBackground + +[kidfDrmCode] + FORM=(0 0 160 160) kidcExitGame center + FORMBACKCOLOR=kiclrFormBackground + LABEL=0 (80 9 0 0) "HANDMARK PRESENTS" kifntTitle center + BITMAP=0 (0 17 0 0) title.rbm + LABEL=0 (30 60 100 8) "Copyright 2003, 2004 Spiffcode, Inc." kifntDefault center + LABEL=0 (30 70 100 8) "All Rights Reserved" kifntDefault center + LABEL=0 (8 91 143 8) "Purchase at www.WarfareIncorporated.com" kifntDefault center + LABEL=kidcCode (30 104 100 8) "C-AAAA-BBBB-CCCC-DDDD" kifntShadow center + BUTTON=kidcEnterKey (42 119 74 12) "Enter License Key" kifntDefault + BUTTON=kidcPlayDemo (42 137 74 12) "Play Demo" kifntDefault + +[kidfDrmKey] + FORM=(0 0 160 160) kidcOk center + LABEL=0 (42 6 75 8) "HOSTILE TAKEOVER" kifntTitle center + LABEL=0 (28 20 104 8) "Please enter your License Key:" kifntDefault center + LABEL=kidcKey (35 35 87 8) "K-??????-??????-??????" kifntShadow center + BUTTON=kidc0 (6 50 34 14) "0" kifntTitle + BUTTON=kidc1 (44 50 34 14) "1" kifntTitle + BUTTON=kidc2 (83 50 34 14) "2" kifntTitle + BUTTON=kidc3 (121 50 34 14) "3" kifntTitle + BUTTON=kidc4 (6 67 34 14) "4" kifntTitle + BUTTON=kidc5 (44 67 34 14) "5" kifntTitle + BUTTON=kidc6 (83 67 34 14) "6" kifntTitle + BUTTON=kidc7 (121 67 34 14) "7" kifntTitle + BUTTON=kidc8 (6 84 34 14) "8" kifntTitle + BUTTON=kidc9 (44 84 34 14) "9" kifntTitle + BUTTON=kidcA (83 84 34 14) "A" kifntTitle + BUTTON=kidcB (121 84 34 14) "B" kifntTitle + BUTTON=kidcC (6 101 34 14) "C" kifntTitle + BUTTON=kidcD (44 101 34 14) "D" kifntTitle + BUTTON=kidcE (83 101 34 14) "E" kifntTitle + BUTTON=kidcF (121 101 34 14) "F" kifntTitle + BUTTON=kidcBackspace (121 118 34 14) "<--" kifntTitle + BUTTON=kidcOk (32 139 40 12) "OK" kifntButton + BUTTON=kidcCancel (88 139 40 12) "CANCEL" kifntButton + +[kidfRegisterNow] + FORM=(0 0 160 160) kidcOk center + LABEL=0 (56 6 48 8) "Purchase Now!" kifntTitle center + LABEL=0 (12 22 136 8) "Find out what happens to Andy, Jana and" kifntShadow + LABEL=0 (12 32 121 8) "crew! Purchase now and get:" kifntShadow + LABEL=0 (31 50 97 8) "Rich Story Driven Experience" kifntShadow center + LABEL=0 (44 60 71 8) "20 Single Player Missions" kifntShadow center + LABEL=0 (45 70 69 8) "21 Multi Player Missions" kifntShadow center + LABEL=0 (46 80 67 8) "2 Play Environments" kifntShadow center + LABEL=0 (53 90 54 8) "11 Building Types" kifntShadow center + LABEL=0 (60 100 40 8) "11 Unit Types" kifntShadow center + LABEL=0 (42 110 76 8) "Digitized Sound Effects" kifntShadow center + LABEL=0 (50 120 60 8) "Mission Statistics" kifntShadow center + LABEL=0 (63 130 33 8) "Mission Editor, and more!" kifntShadow center + LABEL=0 (8 147 143 8) "Purchase at www.WarfareIncorporated.com" kifntShadow center + +[kidfInputPanel] + FORM=(0 0 160 160) kidcCancel center + FORMBACKCOLOR=kiclrFormBackground + LABEL=0 (42 6 77 0) "INPUT PANEL" kifntTitle center + LABEL=kidcInputLabel (2 18 52 0) "???" kifntShadow + EDIT=kidcInputEdit (48 18 110 0) "???" kifntShadow + BUTTON=kidcBackspace (121 118 34 14) "<--" kifntTitle + BUTTON=kidcOk (32 139 40 12) "OK" kifntButton + BUTTON=kidcCancel (88 139 40 12) "CANCEL" kifntButton + +[kidfLoading] + FORM=(0 0 80 25) kidcCancel center + LABEL=kidcTitle (0 9 80 0) "LOADING..." kifntTitle center + +[kidfWaiting] + FORM=(0 0 150 25) kidcCancel center topmost + FORMBACKCOLOR=kiclrBlack + LABEL=kidcTitle (0 9 150 0) "" kifntTitle center + +[kidfMultiplayerWinSummary] + FORM=(0 0 160 160) kidcCancel center + FORMBACKCOLOR=kiclrFormBackground + LABEL=kidcMissionTitle (62 6 35 0) "" kifntTitle center + LABEL=0 (62 14 35 0) "PERFORMANCE REVIEW" kifntTitle center + LABEL=kidcObjectiveText1 (4 25 0 0) "" kifntShadow + LABEL=kidcObjectiveStatus1 (155 25 0 0) "" kifntShadow right + LABEL=kidcObjectiveText2 (4 34 0 0) "" kifntShadow + LABEL=kidcObjectiveStatus2 (155 34 0 0) "" kifntShadow right + LABEL=kidcObjectiveText3 (4 43 0 0) "" kifntShadow + LABEL=kidcObjectiveStatus3 (155 43 0 0) "" kifntShadow right + LABEL=kidcObjectiveText4 (4 52 0 0) "" kifntShadow + LABEL=kidcObjectiveStatus4 (155 52 0 0) "" kifntShadow right + LABEL=kidcMissionResult (80 63 0 0) "YOU WIN!" kifntTitle center + LABEL=kidcPage2 (4 74 0 0) "Enemy units destroyed" kifntShadow + LABEL=kidcMobileUnitsKilled (155 74 0 0) "" kifntShadow right + LABEL=kidcPage2 (4 83 0 0) "Enemy buildings destroyed" kifntShadow + LABEL=kidcStructuresKilled (155 83 0 0) "" kifntShadow right + LABEL=kidcPage2 (4 92 0 0) "Friendly units lost" kifntShadow + LABEL=kidcMobileUnitsLost (155 92 0 0) "" kifntShadow right + LABEL=kidcPage2 (4 101 0 0) "Friendly buildings lost" kifntShadow + LABEL=kidcStructuresLost (155 101 0 0) "" kifntShadow right + LABEL=kidcPage2 (4 110 0 0) "Credits acquired / spent" kifntShadow + LABEL=kidcCreditsAction (155 110 0 0) "" kifntShadow right + LABEL=kidcPage2 (4 119 0 0) "Elapsed time" kifntShadow + LABEL=kidcGameTime (155 119 0 0) "" kifntShadow right + BUTTON=kidcCancel (80 140 40 14) "OK" kifntButton center + +[kidfMultiplayerLoseSummary] + FORM=(0 0 160 160) kidcCancel center + FORMBACKCOLOR=kiclrFormBackground + LABEL=kidcMissionTitle (62 6 35 0) "" kifntTitle center + LABEL=0 (62 14 35 0) "PERFORMANCE REVIEW" kifntTitle center + LABEL=kidcObjectiveText1 (4 25 0 0) "" kifntShadow + LABEL=kidcObjectiveStatus1 (155 25 0 0) "" kifntShadow right + LABEL=kidcObjectiveText2 (4 34 0 0) "" kifntShadow + LABEL=kidcObjectiveStatus2 (155 34 0 0) "" kifntShadow right + LABEL=kidcObjectiveText3 (4 43 0 0) "" kifntShadow + LABEL=kidcObjectiveStatus3 (155 43 0 0) "" kifntShadow right + LABEL=kidcObjectiveText4 (4 52 0 0) "" kifntShadow + LABEL=kidcObjectiveStatus4 (155 52 0 0) "" kifntShadow right + LABEL=0 (80 63 0 0) "YOU LOSE!" kifntTitle center + LABEL=kidcPage2 (4 74 0 0) "Enemy units destroyed" kifntShadow + LABEL=kidcMobileUnitsKilled (155 74 0 0) "" kifntShadow right + LABEL=kidcPage2 (4 83 0 0) "Enemy buildings destroyed" kifntShadow + LABEL=kidcStructuresKilled (155 83 0 0) "" kifntShadow right + LABEL=kidcPage2 (4 92 0 0) "Friendly units lost" kifntShadow + LABEL=kidcMobileUnitsLost (155 92 0 0) "" kifntShadow right + LABEL=kidcPage2 (4 101 0 0) "Friendly buildings lost" kifntShadow + LABEL=kidcStructuresLost (155 101 0 0) "" kifntShadow right + LABEL=kidcPage2 (4 110 0 0) "Credits acquired / spent" kifntShadow + LABEL=kidcCreditsAction (155 110 0 0) "" kifntShadow right + LABEL=kidcPage2 (4 119 0 0) "Elapsed time" kifntShadow + LABEL=kidcGameTime (155 119 0 0) "" kifntShadow right + BUTTON=kidcCancel (80 140 40 14) "OK" kifntButton center + +[kidfMultiplayerObjectives] + FORM=(0 0 160 160) kidcCancel center + FORMBACKCOLOR=kiclrFormBackground + LABEL=kidcMissionTitle (62 6 35 0) "" kifntTitle center + LABEL=0 (62 14 35 0) "OBJECTIVES" kifntTitle center + LABEL=kidcObjectiveText1 (4 25 0 0) "" kifntShadow + LABEL=kidcObjectiveStatus1 (155 25 0 0) "" kifntShadow right + LABEL=kidcObjectiveText2 (4 34 0 0) "" kifntShadow + LABEL=kidcObjectiveStatus2 (155 34 0 0) "" kifntShadow right + LABEL=kidcObjectiveText3 (4 43 0 0) "" kifntShadow + LABEL=kidcObjectiveStatus3 (155 43 0 0) "" kifntShadow right + LABEL=kidcObjectiveText4 (4 52 0 0) "" kifntShadow + LABEL=kidcObjectiveStatus4 (155 52 0 0) "" kifntShadow right + LABEL=kidcPage1 (80 63 0 0) "BRIEFING" kifntTitle center + LABEL=kidcPage2 (80 63 0 0) "STATISTICS" kifntTitle center + LABEL=kidcObjectiveInfo (4 74 152 69) "" kifntShadow left multiline + LABEL=kidcPage2 (4 74 0 0) "Enemy units destroyed" kifntShadow + LABEL=kidcMobileUnitsKilled (155 74 0 0) "" kifntShadow right + LABEL=kidcPage2 (4 83 0 0) "Enemy buildings destroyed" kifntShadow + LABEL=kidcStructuresKilled (155 83 0 0) "" kifntShadow right + LABEL=kidcPage2 (4 92 0 0) "Friendly units lost" kifntShadow + LABEL=kidcMobileUnitsLost (155 92 0 0) "" kifntShadow right + LABEL=kidcPage2 (4 101 0 0) "Friendly buildings lost" kifntShadow + LABEL=kidcStructuresLost (155 101 0 0) "" kifntShadow right + LABEL=kidcPage2 (4 110 0 0) "Credits acquired / spent" kifntShadow + LABEL=kidcCreditsAction (155 110 0 0) "" kifntShadow right + LABEL=kidcPage2 (4 119 0 0) "Elapsed time" kifntShadow + LABEL=kidcGameTime (155 119 0 0) "" kifntShadow right + BUTTON=kidcStatistics (50 140 60 14) "STATISTICS" kifntButton center + BUTTON=kidcInfo (50 140 60 14) "BRIEFING" kifntButton center + BUTTON=kidcCancel (120 140 40 14) "OK" kifntButton center + diff --git a/data/game.ini.pp b/data/game.ini.pp new file mode 100644 index 0000000..f256022 --- /dev/null +++ b/data/game.ini.pp @@ -0,0 +1,11 @@ +#include + +[General] + +[Fonts] +kifntDefault=standardfont.fnt +kifntShadow=shadowfont.fnt +kifntButton=buttonfont.fnt +kifntTitle=titlefont.fnt +kifntEcom=standardfont.fnt +kifntHud=hudfont.fnt diff --git a/data/grassy.tc b/data/grassy.tc new file mode 100644 index 0000000..448e600 Binary files /dev/null and b/data/grassy.tc differ diff --git a/data/grassyNames.txt b/data/grassyNames.txt new file mode 100644 index 0000000..ff36e11 --- /dev/null +++ b/data/grassyNames.txt @@ -0,0 +1,43 @@ +road00 +road01 +road02 +road03 +road04 +road05 +road06 +road07 +road08 +road09 +road10 +road11 +road12 +road13 +road14 +road15 +road16 +road17 +road18 +road19 +road20 +road21 +road22 +road23 +road24 +road25 +road26 +road27 +road28 +road29 +road30 +road31 +road32 +road33 +road34 +road35 +road36 +road37 +road38 +road39 +road40 +road41 +road42 diff --git a/data/halftonefog.zip b/data/halftonefog.zip new file mode 100644 index 0000000..a338de2 Binary files /dev/null and b/data/halftonefog.zip differ diff --git a/data/help.txt b/data/help.txt new file mode 100644 index 0000000..a9c8810 --- /dev/null +++ b/data/help.txt @@ -0,0 +1,496 @@ +

TABLE OF CONTENTS

+
+

Story Background
+
+Getting Started
+ Begin New Game
+ Playing a Challenge Mission
+ Selecting a Story Mission
+ Changing the Game Options
+
+Options
+ Game
+ Sound
+ Performance
+ Color
+ Display
+
+Game Play Controls
+ Menu
+ Useful Indicators
+ The Mini Map
+ Game Icons
+
+In Game Menu
+ State Objectives
+ Options
+ Saving Your Game
+ Loading a Saved Game
+ Restart Mission
+ Abort Mission
+ Exit Game
+
+Personnel
+ Security Guard
+ Rocket Trooper
+ Corporate Raider
+
+Vehicles
+ SR-98 Eagle
+ T-29 Broadsword
+ M-18 Hydra
+ T-33 Liberator
+ G-4 Bullpup
+ H-7 Dominion
+ A-3 Cyclops
+
+Buildings
+ Headquarters
+ Power Generator
+ Galaxite Processor
+ Galaxite Storage Warehouse
+ Human Resource Center (HRC)
+ Vehicle Transport Station (VTS)
+ Surveillance Center
+ Research & Development Center
+ Gatling Tower
+ Rocket Tower
+ Research Upgrades
+
+Advanced Features
+ Graffiti Scroll
+ Button Scroll
+ Selecting Multiple Units
+
+Tips
+ Building Options
+ Transforming your Headquarters
+ Changing the game difficulty
+ Reaching your Unit Limit
+ Speed Ecom Display
+ Switching Applications
+
+Credits +

+
+

STORY BACKGROUND

+

The year is 2065 and mega corporations rule the galaxy. Two of the largest companies, ACME Exploration and OMNI Industries, are vying for dominance in the mining of an extremely rare and incredibly valuable mineral called Galaxite. Galaxite can be used to 'rip' particles from one location to another almost instantly and over any distance. This is accomplished by creating matching quirk fields which, when synchronized, create a momentary Einstein-Rosen wormhole bridge between the two locations. OMNI Industries is the largest corporation in existence with a Gross National Product (GNP) that exceeds that of all but the richest countries. OMNI has a stranglehold on a variety of different markets ranging from diamonds to Galaxite mining. ACME Exploration is the second largest corporation in existence and is urgently trying to secure the number 1 spot.
+Six weeks ago an ACME owned, unmanned probe en route to the star Tau Ceti reported back that an unexplored planet, Icarus, contained 'massive' Galaxite deposits. ACME has dispatched a colony ship to the location. On board are Gordon Fox, Mining Base Commander, Jana Perez, a Senior Research Scientist, her fiance, Andy Whitmore, a Mining Operations Trainee, and Arthur Olstrom, a Sr. Mining Executive; it includes the equipment and people necessary to exploit and defend their claim. If ACME can successfully claim Icarus it may well become the richest and most powerful mega corporation of them all!

+
+

GETTING STARTED

+

To begin a new game, or continue after completing the sampler mission and registering, refer to Begin New Game. Once you begin, if you switch away to another program using a device button, turn off your device, or you select "Exit Game" from the In Game Menu, your current place in the game will be preserved. If a mission ends in failure you will have an option to retry that mission. If you wish to preserve incremental progress within a mission, you can save the game at any time. Choosing Abort at the end of a mission will abort your game.

+
+

BEGIN NEW GAME

+

This launches you into Mission 1, a training mission which is an excellent place to start. You'll learn how to create buildings, build units and move your forces around. However, there are some features that are not covered in the training mission and are helpful to know ahead of time. Refer to Advanced Features for more information. As you finish each mission you will be taken to the next mission in the adventures of Andy, Jana, Olstrom and Fox. Watch your rank increase with each successfully completed mission.

+
+

PLAYING A CHALLENGE MISSION

+

If you would like to play a challenge mission tap "Play Mission" on the Main Menu. From the Play Mission Menu select "Play Challenge Mission" and you are presented with a list of missions to choose from. Tap on a mission to highlight it and then tap the "Play" button.

+
+

SELECTING A STORY MISSION

+

To maximize replayability, you can re-play individual story missions. From the Main Menu select "Play Mission". Then from the Play Mission Menu select "Play Story Mission". You can select any of the fourteen story missions. Note: if you launch the story missions in this way rather than playing through the game from the beginning, you will not be able to attain the top rank possible in the game. To start as a trainee and complete as a CEO you should use the Begin New Game option on the Main Menu.

+
+

CHANGING THE GAME OPTIONS

+

If you would like to adjust the audio, display, or game settings tap on the Options button in the Main Menu. Options are also accessible from the In Game Menu. +

+
+

OPTIONS

+
+

GAME

+

You can adjust the game speed by adjusting the slider to the right for faster game play and to the left for a slower game play. Once the game is playing as fast as the device allows, increasing the game speed will not have an effect.
+You can check the Lasso Selection box to change the way you select multiple units, from tapping and dragging a square containing the units you would like to select, to freehand drawing a loop around the units you would like to select.
+There are three difficulty settings for the game. This setting can be changed at any time and will take effect immediately.
+Tapping the Default button will restore all three settings: game speed, lasso selection and difficulty, to their default values.

+
+

SOUND

+

Moving the volume slider to the right will increase the audio volume, moving it to the left will decrease the audio volume.
+You can mute all game sound by selecting the Mute checkbox.

+
+

PERFORMANCE

+

For very low-end Palm devices, performance of the game can be improved by turning off some graphics options. By default these are all on.

+
+

COLOR

+

On some devices the game will benefit from adjustments to the Hue, Saturation and Brightness. We have included default settings for several devices. Select this option from the In Game Menu to see the Hostile Takeover background and units as you make adjustments. There are two different types of terrain: grass and desert. Experimentation may be required.
+Selecting the Default button will return all three values to their default setting.

+
+

DISPLAY

+

Some devices can display multiple screen resolutions. Some devices are capable of displaying in a landscape mode. If your device is capable of more than one display configuration you will have multiple choices here. These will be formatted as follows: horizontal-pixels x vertical-pixels, color if your device can do color, high or low resolution as suits your device and then degrees of rotation from the default display orientation if your device supports it. +

+
+

GAME PLAY CONTROLS

+
+

MENU

+

While playing Hostile Takeover you can access the In Game Menu by tapping on the "Menu" button in the bottom-left corner of the display. The game will pause while you are looking at the In Game Menu. Selecting Back will return you to gameplay.

+
+

INDICATORS

+

To the right of the Menu button is your Credit Indicator. It shows your current financial resources. The dollar symbol will flash if you are too low on credits to complete a task. Note that the building constructing units when you run out of credits will also flash a dollar sign. If you have no Bullpups you can sell a building to get more credits to continue unit production. Sell a Galaxite Warehouse only as a last resort, you will lose its contents when you sell.

+
+

To the right of the Credit Indicator is your Power Indicator. It shows your current power supply in bars and your current demand with small triangles above and below the bars. The power symbol will flash when your power is too low. On color devices the bars will be green when you have plenty of power, yellow when you have just enough power and red when your power is too low.

+
+

MINI MAP

+

In the lower right-hand corner of your screen is a satellite overview of the nearby region. There is a square inside that shows which part of the region is currently displayed on your device. Use your stylus to tap and drag that rectangle to change your view.

+
+

GAME ICONS

+

POP UP MENU ICONS
+White Hammer: used to display build menu for selected resource
+Shaded Hammer: indicates that the selected resource is currently building something. If you tap the shaded hammer, the resource stops unit construction and your credits are returned.
+White Wrench: Indicates that the selected resource is damaged and needs repair. Note that repairing a building consumes credits. If you are low on credits you may wish to wait on repairing a non-critical building.
+Grey Wrench: Indicates that the selected resource is not damaged.
+White Dollar Sign: Can be used to sell a resource. Your entire investment will not be returned. However, sometimes it is necessary to sell one resource to create a more critical one. Be aware that selling a Galaxite Warehouse will lose any credits it contains.
+
+PLACE BUILDING ICONS
+When you tap the HQ and select build, the Order Building screen is displayed. Once you select something to build you then get a hashed square which represents the footprint of the building. There are two icons associated with the hashed square:
+White Check Mark: When you have no dark hash marks, you can select the white check mark to confirm building placement.
+White X: Select the white x to cancel construction of the building.
+
+

+
+

IN GAME MENU

+
+

STATE OBJECTIVES

+

You can review your mission objectives at any time by selecting State Objectives from the In Game Menu The Statistics button will show you your current statistics and promotion level. The elapsed time shown is in game time, if you have altered the game speed this will differ from real time. You can more quickly access the objectives screen by tapping the Credit Indicator visible while playing the game.

+
+

OPTIONS

+

This will take you to the game options.

+
+

SAVING YOUR GAME

+

While playing you can save your progress from the In Game Menu by tapping on the "Save Game" button. Select the slot where you would like to save your game and then tap the "Save" button.

+
+

LOADING A SAVED GAME

+

Once you have saved a game you can load it again by tapping the "Load Saved Game" button from the Main Menu. You will see a list of all the games that have been saved. Tap on the one you would like to load and then tap on the "Load" button. The saved games are named with the time saved (in 24-hour time) followed by the name of the mission.

+
+

RESTART MISSION

+

At any time during the mission you can choose to restart that mission by choosing Restart Mission. It will put you back at the beginning of the mission with the promotion level you had when you began that mission.

+
+

ABORT MISSION

+

Choosing Abort Mission will return you to the game main menu without preserving your game. You may wish to save your game before choosing this option.

+
+

EXIT GAME

+

Choosing Exit Game will completely exit Hostile Takeover and preserve your current view and play status. Your game will continue from there the next time you play Hostile Takeover.

+
+
+

PERSONNEL

+
+

SECURITY GUARD


+
+ +
+

Security Guards are cheap and cheerful but with limited firepower and defensive capabilities. The Security Guard is useful as a scout and is very effective in large groups. +
+
+Cost: 150
+Armor: Light
+Damage: Low
+Speed: Medium
+Range: Medium
+


+

ROCKET TROOPER


+
+ +
+

Rocket Troopers are particularly effective against armored vehicles. They pack a bit more punch and have a longer range than a security guard but can't travel as quickly.
+
+Cost: 300
+Armor: Light
+Damage: Low
+Speed: Slow
+Range: High
+


+

CORPORATE RAIDER


+
+ +
+

Every organization's worst nightmare. Send a Raider into an enemy building and it becomes yours! Corporate raiders need to be carefully protected since they have very little armor and no conventional weapons. Each raider can be used once.
+Corporate Raiders are available from the Human Resources Center after completing the HRC upgrade from the Research & Development Center. Note that the R & D Center must be intact for you to build the Corporate Raiders. If you sell the center or it is destroyed by enemies you will no longer be able to create Raiders.
+
+Armor: Light
+Damage: None
+Speed: Slow
+Range: Low
+


+

VEHICLES

+
+

SR-98 EAGLE


+
+ +
+

The SR-98 is a light, speedy tank that comes equipped with a machine gun. They aren't particularly powerful on their own but are very effective in groups and make nice scouts.
+
+Cost: 300
+Armor: Light
+Damage: Medium
+Speed: Fast
+Range: High
+


+

T-29 BROADSWORD


+
+ +
+

The T-29 comes equipped with a large mortar cannon that does a lot of damage. It is effective in combat with other units and particularly effective against buildings.
+
+Cost: 450
+Armor: Medium
+Damage: High
+Speed: Medium
+Range: High
+


+

M-18 HYDRA


+
+ +
+

The Hydra shoots high-explosive rockets that pack a large punch ideal for armored vehicles. It is very well rounded making it an excellent candidate for both offensive and defensive roles.
+
+Cost: 450
+Armor: Medium
+Damage: High
+Speed: Medium
+Range: High
+


+

T-33 LIBERATOR


+
+ +
+

The Liberator comes equipped with dual-cannons which allow it to make short work of enemy infantry.
+
+Cost: 600
+Armor: Heavy
+Damage: High
+Speed: Medium
+Range: High
+


+

G-4 BULLPUP


+
+ +
+

The G-4 Bullpup gathers Galaxite and returns it to your Processor. Order more to harvest faster or replace ones that have been destroyed. Their tough armor can get them through trouble spots but they often need to be guarded as they have no offensive capabilities.
+
+Cost: 600
+Armor: Heavy
+Damage: None
+Speed: Medium
+Range: None
+


+

H-7 DOMINION

+
+ +
+

The H-7 Dominion transforms into an HQ allowing you to start a new base anywhere you want. It has strong armor but no weapons. Once transformed it cannot be remobilized by the player. The Dominion is available from the Vehicle Transport Station after completing the VTS upgrade from the Research & Development Center.
+
+Cost: 1200
+Armor: Heavy
+Damage: None
+Speed: Slow
+Range: None
+


+

A-3 CYCLOPS

+
+ +
+

The A-3 Cyclops is a long range artillery unit particularly effective against buildings. Almost any other offensive unit can destroy it, so it should be escorted by other defensive units.
+
+Cost: 500
+Armor: Light
+Damage: High
+Speed: Slow
+Range: High
+


+

BUILDINGS

+
+

HEADQUARTERS


+
+ +
+

Your entire base is built around your headquarters (HQ). Without it you are unable to create new buildings and as such it should be carefully protected from enemy attack.
+
+Armor: Heavy
+Damage: None
+Power Demand: 0
+Power Supply: 0
+


+

POWER GENERATOR


+
+ +
+

Power Generators supply the rest of your buildings with power. Running low on power reduces the function of many structures making it imperative that you keep an eye on your power supply. A lightening bolt symbol will flash on underpowered units. Note that when the lightening bolt is flashing, these buildings will not function as expected. For example, Rocket Towers will no longer fire. When it is your own Rocket Tower that is disabled, this spells bad news for you. However, when enemy towers are underpowered you can take full advantage of their helplessness!
+
+Cost: 750
+Armor: Light
+Damage: None
+Power Demand: 0
+Power Supply: 40
+


+

GALAXITE PROCESSOR


+
+ +
+

A Galaxite Processor is used to process mined Galaxite and provides funding for your operation. It should be well defended. If the Bullpup remains in the processor, it usually indicates that you need to build a Galaxite Storage Warehouse. When Bullpups are destroyed you can build additional ones at the Vehicle Transport Station. +
+Cost: 1500
+Armor: Heavy
+Damage: None
+Power Demand: 10
+Power Supply: 0
+


+

GALAXITE STORAGE WAREHOUSE


+
+ +
+

Add warehouses to increase processed Galaxite capacity. If it is destroyed, sold, or taken over you will lose whatever Galaxite it contains. On the other hand using Corporate Raiders to take over an enemy's warehouse will garner the your enemy's stored credits. This is an excellent offensive maneuver.
+
+Cost: 750
+Armor: Light
+Damage: None
+Power Demand: 7
+Power Supply: 0
+


+

HUMAN RESOURCE CENTER (HRC)


+
+ +
+

The HRC is used to recruit personnel. You can use the Research & Development Center to upgrade the HRC and enable production of more advanced units. You can build up to ten units at a time by selecting the unit type and repeatedly tapping on the order button.
+
+Cost: 1000
+Armor: Medium
+Damage: None
+Power Demand: 10
+Power Supply: 0
+


+

VEHICLE TRANSPORT STATION (VTS)


+
+ +
+

Use the VTS to order vehicles. Upgrading the VTS gives you the ability to create more advanced vehicles. You can build up to ten units at a time by selecting the unit type and repeatedly tapping on the order button.
+
+Cost: 1250
+Armor: Heavy
+Damage: None
+Power Demand: 10
+Power Supply: 0
+


+

SURVEILLANCE CENTER


+
+ +
+

The Surveillance Center controls targeting for both your Gatling Tower and your Rocket Tower. It is required before you can build towers.
+
+Cost: 750
+Armor: Medium
+Damage: None
+Power Demand: 15
+Power Supply: 0
+


+

RESEARCH & DEVELOPEMENT CENTER


+
+ +
+

Investing in a R&D Center will allow you to upgrade your HRC and VTS, giving you access to the newest technologies available. Note that once the VTS is upgraded you can sell your R&D center to return some of your credits. However, even after upgrading the HRC, if you lose your R&D Center you will be unable to create Corporate Raiders. The R&D center must be intact for these very special units to be available.
+
+Cost: 750
+Armor: Medium
+Damage: None
+Power Demand: 15
+Power Supply: 0
+


+

GATLING TOWER

+
+

These guard towers are equipped with machine guns and are fairly effective at keeping the competition out. A fully powered Surveillance Center is required for the targeting systems to function so that towers will fire automatically as enemies come into range. You can also direct their fire by tapping on the tower and then tapping on the enemy you wish to fire upon. Note that these towers take a great deal of power to remain operational. As you create towers, be sure to keep an eye on your power meter. A flashing lightening bolt symbol on your tower indicates that it is under powered and will not fire at the enemy.
+
+Cost: 750
+Armor: Light
+Damage: Medium
+Power Demand: 10
+Power Supply: 0
+


+

ROCKET TOWER

+
+ +

A guard tower equipped with a rocket launcher. These towers are more effective than Gatling Towers but cost more. Like the Gatling Tower the Rocket Tower requires a Surveillance Center and sufficient power to operate. Towers will fire automatically as enemies come into range. You can also direct the tower's fire by tapping on the tower and then tapping on the enemy you wish to fire upon. Note that these towers take a great deal of power to remain operational. As you create towers, be sure to keep an eye on your power meter. A flashing lightening bolt symbol on your tower indicates that it is under powered and will not fire at the enemy.
+
+Cost: 850
+Armor: Light
+Damage: High
+Power Demand: 10
+Power Supply: 0
+


+

Research Upgrades

+
+

Research upgrades allow you to improve the units available at your Human Resource Center (HRC) and Vehicle Transport Station (VTS). The Research & Development Center is available starting on Mission 3. Select this building and use the build icon to view available upgrades. Upgrading the HRC enables production of Rocket Troopers and later Corporate Raiders. Upgrading the VTS enables production of Dominions, Cyclops and other advanced tanks. Not all upgrades are available in all missions. It is worth noting that even after you have upgraded your HRC, the R&D Center must survive to enable production of Corporate Raiders. Therefore you cannot sell this building if you wish to produce these very special units.
+


+

ADVANCED FEATURES

+
+

GRAFFITI SCROLL

+

On many devices you can use the graffiti area to move your view of the map. Tap and hold with your stylus and then move it around within the graffiti area.

+
+

BUTTON SCROLL

+

Many devices have pads or buttons that can be used to scroll the map, allowing convenient two-handed play. Experiment with your device.

+
+

SELECT MULTIPLE UNITS

+

You can select multiple units at once. Tap your stylus and drag it diagonally across a group of units. A box will be drawn. When you lift your stylus all the units in the box will be selected. You can change this from a box to a 'lasso' that you draw around the units by checking a box in the Options, Game Menu.
+To select units only of a particular type you can double-tap one unit. Other on-screen units of the same type will also be selected.

+
+

TIPS

+
+

BUILDING OPTIONS

+

To display the options associated with a particular building tap on the building and select the Build Icon (the white hammer). The units associated with that building are displayed. Note that not all units are available on all missions. As you advance through more difficult missions, additional options are added to the build menus. Some units are only available after upgrading via the Research & Development Center.

+
+

TRANSFORMING YOUR HEADQUARTERS

+

To change your Dominion into a Headquarters you need to bring up the Transform Menu. Tap your stylus on the Dominion and hold it until the Transform Menu appears.

+
+

CHANGING THE DIFFICULTY

+

You can change the challenge level of the game in the Options, Game Menu

+
+

UNIT LIMITS

+

The number of units you can create is limited based on the available memory in your device. When the game reaches the unit limit for your device, a notice is displayed on the lower left-hand corner of your screen. If you notice that all your build buttons are disabled, you may have reached your unit limit.

+
+

SPEED ECOM DISPLAY

+

By default, ECOM messages are displayed a few characters at a time. To display the entire message at once, tap the ECOM text area.

+
+

SWITCHING APPLICATIONS

+

If you switch to another application while playing the game, Hostile Takeover will pause and be restored when you return.

+
+ +

CREDITS

+

Hostile Takeover(TM)
+
+Developed by: +Spiffcode, Inc +http://www.spiffcode.com +
+Game Missions, Forum, and Information, and Support:
+http://www.WarfareIncorporated.com +
+
+Hostile Takeover is a trademark of Spiffcode, Inc +Copyright 2003-2008 Spiffcode, Inc. +All Rights Reserved. +
+
+Credits: +
+Primary Developers: +Scott Ludwig +Darrin Massena +
+Additional Coding: +Casey Margell +Shaula Massena +
+Mission Design: +Zion A. Dutro +Hans Olav Elseb~ +Rob Girling +Brian Margell +Brian Outhwaite +
+Test: +Andrew Massena +
+Story Editing and Dialog: +Elizabeth Chapman +
+Art/Animation: +Rob Girling +Mark Soderwall +
+Marketing Consultant: +Lisa Linnenkohl +

diff --git a/data/helpiphone-multiplayer.txt b/data/helpiphone-multiplayer.txt new file mode 100644 index 0000000..2ab4fe1 --- /dev/null +++ b/data/helpiphone-multiplayer.txt @@ -0,0 +1,479 @@ +

HOSTILE TAKEOVER

+

REPLACE ME: MAIN SITE URL

+
+

TABLE OF CONTENTS

+
+

Story Background
+
+Getting Started
+ Play Single Player
+ Play Multiplayer
+ Leaderboard
+ Loading a Saved Game
+ Add-On Mission Packs
+ Options
+
+Game Play Controls
+ Scrolling the Map
+ Selecting Multiple Units
+ Power Select
+ Clearing Selection
+ Menu
+ Useful Indicators
+ The Mini Map
+ Game Icons
+
+In Game Menu
+ State Objectives
+ Options
+ Saving Your Game
+ Loading a Saved Game
+ Restart Mission
+ Abort Mission
+
+Personnel
+ Security Guard
+ Rocket Trooper
+ Corporate Raider
+
+Vehicles
+ SR-98 Eagle
+ T-29 Broadsword
+ M-18 Hydra
+ T-33 Liberator
+ G-4 Bullpup
+ H-7 Dominion
+ A-3 Cyclops
+
+Buildings
+ Headquarters
+ Power Generator
+ Galaxite Processor
+ Galaxite Storage Warehouse
+ Human Resource Center (HRC)
+ Vehicle Transport Station (VTS)
+ Surveillance Center
+ Research & Development Center
+ Gatling Tower
+ Rocket Tower
+ Research Upgrades
+
+Tips
+ Building Options
+ Transforming your Headquarters
+ Changing the game difficulty
+ Reaching your Unit Limit
+ Speed Ecom Display
+ Switching Applications
+
+Authoring Add-On Mission Packs
+Credits +

+
+

STORY BACKGROUND

+

The year is 2065 and mega corporations rule the galaxy. Two of the largest companies, ACME Exploration and OMNI Industries, are vying for dominance in the mining of an extremely rare and incredibly valuable mineral called Galaxite. Galaxite can be used to 'rip' particles from one location to another almost instantly and over any distance. This is accomplished by creating matching quirk fields which, when synchronized, create a momentary Einstein-Rosen wormhole bridge between the two locations. OMNI Industries is the largest corporation in existence with a Gross National Product (GNP) that exceeds that of all but the richest countries. OMNI has a stranglehold on a variety of different markets ranging from diamonds to Galaxite mining. ACME Exploration is the second largest corporation in existence and is urgently trying to secure the number 1 spot.
+Six weeks ago an ACME owned, unmanned probe en route to the star Tau Ceti reported back that an unexplored planet, Icarus, contained 'massive' Galaxite deposits. ACME has dispatched a colony ship to the location. On board are Gordon Fox, Mining Base Commander, Jana Perez, a Senior Research Scientist, her fiance, Andy Whitmore, a Mining Operations Trainee, and Arthur Olstrom, a Sr. Mining Executive; it includes the equipment and people necessary to exploit and defend their claim. If ACME can successfully claim Icarus it may well become the richest and most powerful mega corporation of them all!

+
+

GETTING STARTED

+

When you begin the game, you will be presented with a number of options. New players will likely wish to begin the story missions from the beginning. From the main menu, you can press "Play" to play Single Player or Multiplayer, you can browse the Leaderboard, resume a previously saved game with Load Saved Game, or download new mission packs from the hundreds of user authored Add-on Mission Packs (a mission pack is a collection of one or more missions).

+
+

BEGINNING A NEW GAME

+

To start the story missions from the beginning, select Play, then Single Player, then select the first mission in the list, Story Mission "M0: Introduction". This is a training mission which is an excellent place to start. You'll learn how to create buildings, build units and move your forces around. However, there are some features that are not covered in the training mission and are helpful to know ahead of time. Refer to Tips for more information. As you finish each mission you will be taken to the next mission in the adventures of Andy, Jana, Olstrom and Fox. Watch your rank increase with each successfully completed mission.

+
+

PLAY SINGLE PLAYER

+

To play a single player mission, choose Play, then Single Player. This will show you a list of the story, challenge, and add-on single player missions installed your device, and allow you to play any one of them. New players are encouraged to begin the story missions by playing Story Mission "M0: Introduction". The story missions start out LOCKED. Missions unlock as you play through the game. Challenge missions are fun, challenging single missions, that come with the game. Add-on missions are authored by players and enthusiasts of the game using the Mission Editor that can be found on REPLACE ME: MAIN SITE URL. Any missions installed from Add-on Mission Packs will appear here. To scroll the list, flick the list with your finger. Once you begin a mission, if you switch away to another program, your current game will be auto-saved. If a mission ends in failure you will have an option to retry that mission. If you wish to preserve incremental progress within a mission, you can save the game at any time. Choosing Abort at the end of a mission will abort your game.

+
+

PLAY MULTIPLAYER

+

To play a multiplayer mission, choose Play, then Multiplayer. If you haven't logged in before, you will be presented with a login prompt. Once logged in, you will be in the multiplayer game lobby. The lobby have a list of all the current game rooms. Game rooms are places players go to find other players to play a game with. You can choose a room to join, or create a room yourself. Once in the room, you will see a list of all the current multiplayer games in that room. You can scroll this list with your finger. You have the option of joining an existing game in this list, or creating your own multiplayer game. To join an existing game, select one of the games from the list. If this game is accepting players, a Join button will become visible. Choose Join, then you will be shown a list of the other players in that game. Select Ready. Once all players are Ready, the creator of the game be allowed to begin the game. You can also create a new multiplayer game. To do this, press the Create button. You will be shown a list of all the multiplayer missions installed on your device. You can scroll this list with your finger. Select a mission you like, and press Create Game. This will list your game in the game room, for other players to join. Once enough players have joined, you will be shown a Begin Game button. Press this to start the game. Note you can find many more multiplayer missions to play in the Add-On Mission Packs. Finally, you can chat with other players in the room by pressing the Chat button.

+
+

MULTIPLAYER LOGIN

+

When playing multiplayer, you can either play anonymously, or with your own player name. If you log in with your own player name, your game results will get recorded on the Leaderboard. Your player name is also visible in multiplayer room Chat, or in-game Chat. When you start multiplayer for the first time, you will see a login screen. You can log in anonymously, or with a player name. If you choose to login anonymously, your player name will be "anon" followed by a number, and your game results will not be recorded by the Leaderboard. If you wish to log in with your own player name, but don't yet have one, you can register for one by pressing the Register button. If you have a player name, but forgot your password, you can retrieve it by pressing the Forgot Password button.

+
+

LEADERBOARD

+

The Leaderboard shows the top rated Hostile Takeover players by their rating. To provide this, the Leaderboard collects results from multiplayer games and analyzes the winners and losers. Winners are awarded a rating increase relative to the likelihood of a win. For example if a player plays an opponent with a higher rating, the rating increase will be higher than if the opponent had a lower rating. There are also special provisions for team games. Players in the winning team are similarly awarded based on the likelihood of the win. However an individual player's share of the win is proportional to the number of enemies that player killed, out of all enemies killed by the team. To have a rating, a player must login with a player name. Logged in players start at a rating of 1500. Computer players have a rating of 1200. Anonymous players have a rating of 1300. The leaderboard also provides per-player aggregate statistics, game history for all players as well as per-player, and gives game detail on each game including winners, losers, units built, killed, lost, credits collected and consumed. It also has player Search.

+
+

ADD-ON MISSION PACKS

+

Choose "Add-On Mission Packs" to browse among and download any of the hundreds of user authored add-on mission packs. A mission pack is a collection of one or more missions created by a single author. Your device will download the mission pack list, which you can scroll through with your finger. Each item in the list is a mission pack. You can see the title of the pack, a brief description, the number of players the missions in this pack require, and the number of missions in the pack. To download a pack, simply select it, then press Download. You will see a progress indicator as the mission pack gets downloaded. Once downloaded, you can play the first mission in the mission pack right away by pressing the "Play" button, or select "Done" to go back to the mission pack list. A "+" indicator appears next mission packs that are installed on your device. To remove a mission pack, select it, and press the "Remove" button. To join others in a discussion of the mission pack, press the Discuss button to be taken to a discussion thread for that mission pack. The columns are sortable. Tap on Title, # Players, or # Missions to sort those columns. The "+" column is also sortable. Finally, to author missions of your own, visit the forums at REPLACE ME: MAIN SITE URL for more information.

+
+

OPTIONS

+

Adjust the game speed by adjusting the slider to the right for faster game play and to the left for a slower game play. Once the game is playing as fast as the device allows, increasing the game speed will not have an effect.
+There are three difficulty settings for the game. This setting can be changed at any time and will take effect immediately.
+Moving the scroll speed slider to the right will increase the rate the map scrolls when you move your finger.
+Tapping the Default button will restore these settings to their default values.

+
+

GAME PLAY CONTROLS

+

The best way to learn the game play controls is to play M0: Introduction, which will introduce the game play elements to you as you play. Briefly, to move a unit, tap on the unit, then tap on the destination. To move multiple units, multi-select the units then tap on the destination. To multi-select, touch two fingers on the map to get a selection rectangle. Drag one or two corners to move and size the rectangle around the units you wish to select. To attack, select your desired units, then tap on the enemy unit you wish to attack.

+
+

SCROLLING THE MAP

+

To scroll the map, simply tap down on the map with your finger, and drag it in the direction you wish. If you wish, from Options you can adjust how fast the map scrolls when you move your finger.

+
+

SELECTING MULTIPLE UNITS

+

You can select multiple units at once by using multi-touch. Touch any two fingers onto the map to get a selection rectangle. Any units inside of this rectangle will get selected, and any outside will be deselected. Drag any two corners to move, size, or rotate the selection rectangle. Drag any one corner to make it larger in one direction.

+
+

POWER SELECT

+

Often in battle you will need to quickly select units. Double tap on a unit will multi-select all units of that same type that are on-screen!

+
+

CLEARING SELECTION

+

To clear the unit selection, touch an empty part of the map with two fingers.

+
+

MENU

+

While playing Hostile Takeover you can access the In Game Menu by tapping on the "Menu" button in the bottom-left corner of the display. The game will pause while you are looking at the In Game Menu. Selecting Back will return you to gameplay.

+
+

INDICATORS

+

To the right of the Menu button is your Credit Indicator. It shows your current financial resources. The dollar symbol will flash if you are too low on credits to complete a task. Note that the building constructing units when you run out of credits will also flash a dollar sign. If you have no Bullpups you can sell a building to get more credits to continue unit production. Sell a Galaxite Warehouse only as a last resort, you will lose its contents when you sell.

+
+

To the right of the Credit Indicator is your Power Indicator. It shows your current power supply in bars and your current demand with small triangles above and below the bars. The power symbol will flash when your power is too low. On color devices the bars will be green when you have plenty of power, yellow when you have just enough power and red when your power is too low.

+
+

MINI MAP

+

In the lower right-hand corner of your screen is a satellite overview of the nearby region. There is a square inside that shows which part of the region is currently displayed on your device. Use your finger to tap and drag that rectangle to scroll the map. The most convenient way to scroll the map however, is to place your finger directly on the map, and move it. The scroll speed can be changed from Game Options.

+
+

GAME ICONS

+

POP UP MENU ICONS
+White Hammer: used to display build menu for selected resource
+Shaded Hammer: indicates that the selected resource is currently building something. If you tap the shaded hammer, the resource stops unit construction and your credits are returned.
+White Wrench: Indicates that the selected resource is damaged and needs repair. Note that repairing a building consumes credits. If you are low on credits you may wish to wait on repairing a non-critical building.
+Grey Wrench: Indicates that the selected resource is not damaged.
+White Dollar Sign: Can be used to sell a resource. Your entire investment will not be returned. However, sometimes it is necessary to sell one resource to create a more critical one. Be aware that selling a Galaxite Warehouse will lose any credits it contains.
+
+PLACE BUILDING ICONS
+When you tap the HQ and select build, the Order Building screen is displayed. Once you select something to build you then get a hashed square which represents the footprint of the building. There are two icons associated with the hashed square:
+White Check Mark: When you have no dark hash marks, you can select the white check mark to confirm building placement.
+White X: Select the white x to cancel construction of the building.
+
+

+
+

IN GAME MENU

+
+

STATE OBJECTIVES

+

You can review your mission objectives at any time by selecting State Objectives from the In Game Menu The Statistics button will show you your current statistics and promotion level. The elapsed time shown is in game time, if you have altered the game speed this will differ from real time. You can more quickly access the objectives screen by tapping the Credit Indicator visible while playing the game.

+
+

SAVING YOUR GAME

+

While playing you can save your progress from the In Game Menu by tapping on the "Save Game" button. Select the slot where you would like to save your game and then tap the "Save" button.

+
+

LOADING A SAVED GAME

+

Once you have saved a game you can load it again by tapping the "Load Saved Game" button from the Main Menu. You will see a list of all the games that have been saved. Tap on the one you would like to load and then tap on the "Load" button. The saved games are named with the time saved (in 24-hour time) followed by the name of the mission.

+
+

RESTART MISSION

+

At any time during the mission you can choose to restart that mission by choosing Restart Mission. It will put you back at the beginning of the mission with the promotion level you had when you began that mission.

+
+

ABORT MISSION

+

Choosing Abort Mission will return you to the game main menu without preserving your game. You may wish to save your game before choosing this option.

+
+

PERSONNEL

+
+

SECURITY GUARD


+
+ +
+

Security Guards are cheap and cheerful but with limited firepower and defensive capabilities. The Security Guard is useful as a scout and is very effective in large groups. +
+
+Cost: 150
+Armor: Light
+Damage: Low
+Speed: Medium
+Range: Medium
+


+

ROCKET TROOPER


+
+ +
+

Rocket Troopers are particularly effective against armored vehicles. They pack a bit more punch and have a longer range than a security guard but can't travel as quickly.
+
+Cost: 300
+Armor: Light
+Damage: Low
+Speed: Slow
+Range: High
+


+

CORPORATE RAIDER


+
+ +
+

Every organization's worst nightmare. Send a Raider into an enemy building and it becomes yours! Corporate raiders need to be carefully protected since they have very little armor and no conventional weapons. Each raider can be used once.
+Corporate Raiders are available from the Human Resources Center after completing the HRC upgrade from the Research & Development Center. Note that the R & D Center must be intact for you to build the Corporate Raiders. If you sell the center or it is destroyed by enemies you will no longer be able to create Raiders.
+
+Armor: Light
+Damage: None
+Speed: Slow
+Range: Low
+


+

VEHICLES

+
+

SR-98 EAGLE


+
+ +
+

The SR-98 is a light, speedy tank that comes equipped with a machine gun. They aren't particularly powerful on their own but are very effective in groups and make nice scouts.
+
+Cost: 300
+Armor: Light
+Damage: Medium
+Speed: Fast
+Range: High
+


+

T-29 BROADSWORD


+
+ +
+

The T-29 comes equipped with a large mortar cannon that does a lot of damage. It is effective in combat with other units and particularly effective against buildings.
+
+Cost: 450
+Armor: Medium
+Damage: High
+Speed: Medium
+Range: High
+


+

M-18 HYDRA


+
+ +
+

The Hydra shoots high-explosive rockets that pack a large punch ideal for armored vehicles. It is very well rounded making it an excellent candidate for both offensive and defensive roles.
+
+Cost: 450
+Armor: Medium
+Damage: High
+Speed: Medium
+Range: High
+


+

T-33 LIBERATOR


+
+ +
+

The Liberator comes equipped with dual-cannons which allow it to make short work of enemy infantry.
+
+Cost: 600
+Armor: Heavy
+Damage: High
+Speed: Medium
+Range: High
+


+

G-4 BULLPUP


+
+ +
+

The G-4 Bullpup gathers Galaxite and returns it to your Processor. Order more to harvest faster or replace ones that have been destroyed. Their tough armor can get them through trouble spots but they often need to be guarded as they have no offensive capabilities.
+
+Cost: 600
+Armor: Heavy
+Damage: None
+Speed: Medium
+Range: None
+


+

H-7 DOMINION

+
+ +
+

The H-7 Dominion transforms into an HQ allowing you to start a new base anywhere you want. It has strong armor but no weapons. Once transformed it cannot be remobilized by the player. The Dominion is available from the Vehicle Transport Station after completing the VTS upgrade from the Research & Development Center.
+
+Cost: 1200
+Armor: Heavy
+Damage: None
+Speed: Slow
+Range: None
+


+

A-3 CYCLOPS

+
+ +
+

The A-3 Cyclops is a long range artillery unit particularly effective against buildings. Almost any other offensive unit can destroy it, so it should be escorted by other defensive units.
+
+Cost: 500
+Armor: Light
+Damage: High
+Speed: Slow
+Range: High
+


+

BUILDINGS

+
+

HEADQUARTERS


+
+ +
+

Your entire base is built around your headquarters (HQ). Without it you are unable to create new buildings and as such it should be carefully protected from enemy attack.
+
+Armor: Heavy
+Damage: None
+Power Demand: 0
+Power Supply: 0
+


+

POWER GENERATOR


+
+ +
+

Power Generators supply the rest of your buildings with power. Running low on power reduces the function of many structures making it imperative that you keep an eye on your power supply. A lightening bolt symbol will flash on underpowered units. Note that when the lightening bolt is flashing, these buildings will not function as expected. For example, Rocket Towers will no longer fire. When it is your own Rocket Tower that is disabled, this spells bad news for you. However, when enemy towers are underpowered you can take full advantage of their helplessness!
+
+Cost: 750
+Armor: Light
+Damage: None
+Power Demand: 0
+Power Supply: 40
+


+

GALAXITE PROCESSOR


+
+ +
+

A Galaxite Processor is used to process mined Galaxite and provides funding for your operation. It should be well defended. If the Bullpup remains in the processor, it usually indicates that you need to build a Galaxite Storage Warehouse. When Bullpups are destroyed you can build additional ones at the Vehicle Transport Station. +
+Cost: 1500
+Armor: Heavy
+Damage: None
+Power Demand: 10
+Power Supply: 0
+


+

GALAXITE STORAGE WAREHOUSE


+
+ +
+

Add warehouses to increase processed Galaxite capacity. If it is destroyed, sold, or taken over you will lose whatever Galaxite it contains. On the other hand using Corporate Raiders to take over an enemy's warehouse will garner the your enemy's stored credits. This is an excellent offensive maneuver.
+
+Cost: 750
+Armor: Light
+Damage: None
+Power Demand: 7
+Power Supply: 0
+


+

HUMAN RESOURCE CENTER (HRC)


+
+ +
+

The HRC is used to recruit personnel. You can use the Research & Development Center to upgrade the HRC and enable production of more advanced units. You can build up to ten units at a time by selecting the unit type and repeatedly tapping on the order button.
+
+Cost: 1000
+Armor: Medium
+Damage: None
+Power Demand: 10
+Power Supply: 0
+


+

VEHICLE TRANSPORT STATION (VTS)


+
+ +
+

Use the VTS to order vehicles. Upgrading the VTS gives you the ability to create more advanced vehicles. You can build up to ten units at a time by selecting the unit type and repeatedly tapping on the order button.
+
+Cost: 1250
+Armor: Heavy
+Damage: None
+Power Demand: 10
+Power Supply: 0
+


+

SURVEILLANCE CENTER


+
+ +
+

The Surveillance Center controls targeting for both your Gatling Tower and your Rocket Tower. It is required before you can build towers.
+
+Cost: 750
+Armor: Medium
+Damage: None
+Power Demand: 15
+Power Supply: 0
+


+

RESEARCH & DEVELOPEMENT CENTER


+
+ +
+

Investing in a R&D Center will allow you to upgrade your HRC and VTS, giving you access to the newest technologies available. Note that once the VTS is upgraded you can sell your R&D center to return some of your credits. However, even after upgrading the HRC, if you lose your R&D Center you will be unable to create Corporate Raiders. The R&D center must be intact for these very special units to be available.
+
+Cost: 750
+Armor: Medium
+Damage: None
+Power Demand: 15
+Power Supply: 0
+


+

GATLING TOWER

+
+

These guard towers are equipped with machine guns and are fairly effective at keeping the competition out. A fully powered Surveillance Center is required for the targeting systems to function so that towers will fire automatically as enemies come into range. You can also direct their fire by tapping on the tower and then tapping on the enemy you wish to fire upon. Note that these towers take a great deal of power to remain operational. As you create towers, be sure to keep an eye on your power meter. A flashing lightening bolt symbol on your tower indicates that it is under powered and will not fire at the enemy.
+
+Cost: 750
+Armor: Light
+Damage: Medium
+Power Demand: 10
+Power Supply: 0
+


+

ROCKET TOWER

+
+ +

A guard tower equipped with a rocket launcher. These towers are more effective than Gatling Towers but cost more. Like the Gatling Tower the Rocket Tower requires a Surveillance Center and sufficient power to operate. Towers will fire automatically as enemies come into range. You can also direct the tower's fire by tapping on the tower and then tapping on the enemy you wish to fire upon. Note that these towers take a great deal of power to remain operational. As you create towers, be sure to keep an eye on your power meter. A flashing lightening bolt symbol on your tower indicates that it is under powered and will not fire at the enemy.
+
+Cost: 850
+Armor: Light
+Damage: High
+Power Demand: 10
+Power Supply: 0
+


+

Research Upgrades

+
+

Research upgrades allow you to improve the units available at your Human Resource Center (HRC) and Vehicle Transport Station (VTS). The Research & Development Center is available starting on Mission 3. Select this building and use the build icon to view available upgrades. Upgrading the HRC enables production of Rocket Troopers and later Corporate Raiders. Upgrading the VTS enables production of Dominions, Cyclops and other advanced tanks. Not all upgrades are available in all missions. It is worth noting that even after you have upgraded your HRC, the R&D Center must survive to enable production of Corporate Raiders. Therefore you cannot sell this building if you wish to produce these very special units.
+


+

TIPS

+
+

BUILDING OPTIONS

+

To display the options associated with a particular building tap on the building and select the Build Icon (the white hammer). The units associated with that building are displayed. Note that not all units are available on all missions. As you advance through more difficult missions, additional options are added to the build menus. Some units are only available after upgrading via the Research & Development Center.

+
+

TRANSFORMING YOUR HEADQUARTERS

+

To change your Dominion into a Headquarters you need to bring up the Transform Menu. Tap your finger on the Dominion and hold it until the Transform Menu appears.

+
+

CHANGING THE DIFFICULTY

+

You can change the difficulty level of the game in the Options.

+
+

UNIT LIMITS

+

The number of units you can create is limited based on the available memory in your device. When the game reaches the unit limit for your device, a notice is displayed on the lower left-hand corner of your screen. If you notice that all your build buttons are disabled, you may have reached your unit limit.

+
+

SPEED ECOM DISPLAY

+

By default, ECOM messages are displayed a few characters at a time. To display the entire message at once, tap the ECOM text area.

+
+

SWITCHING APPLICATIONS

+

If you switch to another application while playing the game, Hostile Takeover will pause and be restored when you return.

+
+

Authoring Add-On Mission Packs

+

Players can create new Add-On Mission Packs using the mission authoring toolkit. Using this tool, you can design, test, and share the missions you have authored with the player community. To begin, visit the Mission Authoring section of the Hostile Takeover forums and read everything there. Once you have a mission pack that you want to test, you'll want to get it on to your device. To do this, upload your mission to any location on the internet. Start the game, tap on Add-On Mission Packs. Tap on the Title label to sort by name, and you'll see "Custom URL..." near the top. Tap on this and select DOWNLOAD, and enter the custom URL to your mission pack and tap OK, and it will be downloaded to your device. Now you can play and refine your mission pack until it is ready to share with the community. To share it, visit the Mission Sharing section of the website for instructions on how to post it there. Posting it there will add it to the Add-On Mission Pack list that all players see.

+
+ +

CREDITS

+

Hostile Takeover(TM)
+
+Developed by: +Spiffcode, Inc +http://www.spiffcode.com +
+Game Missions, Forum, Information, and Support:
+REPLACE ME: MAIN SITE URL +
+
+Hostile Takeover is a trademark of Spiffcode, Inc +Copyright 2003-2014 Spiffcode, Inc. +All Rights Reserved. +
+
+Credits: +
+Primary Developers: +Scott Ludwig +Darrin Massena +
+Additional Coding: +Casey Margell +Shaula Massena +
+Mission Design: +Zion A. Dutro +Hans Olav Elseb~ +Rob Girling +Brian Margell +Brian Outhwaite +
+Test: +Andrew Massena +Brian Outhwaite +
+Story Editing and Dialog: +Elizabeth Chapman +
+Art/Animation: +Rob Girling +Mark Soderwall +
+Marketing Consultant: +Lisa Linnenkohl +

diff --git a/data/helpiphone.txt b/data/helpiphone.txt new file mode 100644 index 0000000..4e09ccb --- /dev/null +++ b/data/helpiphone.txt @@ -0,0 +1,470 @@ +

HOSTILE TAKEOVER

+

REPLACE ME: MAIN SITE URL

+
+

TABLE OF CONTENTS

+
+

Story Background
+
+Getting Started
+ New Game
+ Play Game
+ Loading a Saved Game
+ Add-On Mission Packs
+ Options
+
+Game Play Controls
+ Scrolling the Map
+ Selecting Multiple Units
+ Power Select
+ Clearing Selection
+ Menu
+ Useful Indicators
+ The Mini Map
+ Game Icons
+
+In Game Menu
+ State Objectives
+ Options
+ Saving Your Game
+ Loading a Saved Game
+ Restart Mission
+ Abort Mission
+
+Personnel
+ Security Guard
+ Rocket Trooper
+ Corporate Raider
+
+Vehicles
+ SR-98 Eagle
+ T-29 Broadsword
+ M-18 Hydra
+ T-33 Liberator
+ G-4 Bullpup
+ H-7 Dominion
+ A-3 Cyclops
+
+Buildings
+ Headquarters
+ Power Generator
+ Galaxite Processor
+ Galaxite Storage Warehouse
+ Human Resource Center (HRC)
+ Vehicle Transport Station (VTS)
+ Surveillance Center
+ Research & Development Center
+ Gatling Tower
+ Rocket Tower
+ Research Upgrades
+
+Tips
+ Building Options
+ Transforming your Headquarters
+ Changing the game difficulty
+ Reaching your Unit Limit
+ Speed Ecom Display
+ Switching Applications
+
+Authoring Add-On Mission Packs
+Credits +

+
+

STORY BACKGROUND

+

The year is 2065 and mega corporations rule the galaxy. Two of the largest companies, ACME Exploration and OMNI Industries, are vying for dominance in the mining of an extremely rare and incredibly valuable mineral called Galaxite. Galaxite can be used to 'rip' particles from one location to another almost instantly and over any distance. This is accomplished by creating matching quirk fields which, when synchronized, create a momentary Einstein-Rosen wormhole bridge between the two locations. OMNI Industries is the largest corporation in existence with a Gross National Product (GNP) that exceeds that of all but the richest countries. OMNI has a stranglehold on a variety of different markets ranging from diamonds to Galaxite mining. ACME Exploration is the second largest corporation in existence and is urgently trying to secure the number 1 spot.
+Six weeks ago an ACME owned, unmanned probe en route to the star Tau Ceti reported back that an unexplored planet, Icarus, contained 'massive' Galaxite deposits. ACME has dispatched a colony ship to the location. On board are Gordon Fox, Mining Base Commander, Jana Perez, a Senior Research Scientist, her fiance, Andy Whitmore, a Mining Operations Trainee, and Arthur Olstrom, a Sr. Mining Executive; it includes the equipment and people necessary to exploit and defend their claim. If ACME can successfully claim Icarus it may well become the richest and most powerful mega corporation of them all!

+
+

GETTING STARTED

+

When you begin the game, you will be presented with a number of options. Choose Play Game to begin. New players will likely wish to begin the story missions from the beginning. Load Saved Game allows you to resume any previously saved game. Finally, Add-on Mission Packs will allow you to download from the hundreds of user authored add-on mission packs. A mission pack is a collection of one or more missions.

+
+

BEGINNING A NEW GAME

+

To start the story missions from the beginning, select Play Game, then select the first mission in the list, Story Mission "M0: Introduction". This is a training mission which is an excellent place to start. You'll learn how to create buildings, build units and move your forces around. However, there are some features that are not covered in the training mission and are helpful to know ahead of time. Refer to Tips for more information. As you finish each mission you will be taken to the next mission in the adventures of Andy, Jana, Olstrom and Fox. Watch your rank increase with each successfully completed mission.

+
+

PLAY GAME

+

To play a single player mission, choose "Play Game". This will show you a list of the story, challenge, and add-on single player missions installed your device, and allow you to play any one of them. New players are encouraged to begin the story missions by playing Story Mission "M0: Introduction". The story missions start out LOCKED. Missions unlock as you play through the game. Challenge missions are fun, challenging single missions, that come with the game. Add-on missions are authored by players and enthusiasts of the game using the Mission Editor that can be found on REPLACE ME: MAIN SITE URL. Any missions installed from Add-on Mission Packs will appear here. To scroll the list, flick the list with your finger. Once you begin a mission, if you switch away to another program, your current game will be auto-saved. If a mission ends in failure you will have an option to retry that mission. If you wish to preserve incremental progress within a mission, you can save the game at any time. Choosing Abort at the end of a mission will abort your game.

+
+

ADD-ON MISSION PACKS

+

Choose "Add-On Mission Packs" to browse among and download any of the hundreds of user authored add-on mission packs. A mission pack is a collection of one or more missions created by a single author. Your device will download the mission pack list, which you can scroll through with your finger. Each item in the list is a mission pack. You can see the title of the pack, a brief description, the number of players the missions in this pack require, and the number of missions in the pack. To download a pack, simply select it, then press Download. You will see a progress indicator as the mission pack gets downloaded. Once downloaded, you can play the first mission in the mission pack right away by pressing the "Play" button, or select "Done" to go back to the mission pack list. A "+" indicator appears next mission packs that are installed on your device. To remove a mission pack, select it, and press the "Remove" button. To join others in a discussion of the mission pack, press the Discuss button to be taken to a discussion thread for that mission pack. The columns are sortable. Tap on Title, # Players, or # Missions to sort those columns. The "+" column is also sortable. Finally, to author missions of your own, visit the forums at REPLACE ME: MAIN SITE URL for more information.

+
+

OPTIONS

+

Adjust the game speed by adjusting the slider to the right for faster game play and to the left for a slower game play. Once the game is playing as fast as the device allows, increasing the game speed will not have an effect.
+There are three difficulty settings for the game. This setting can be changed at any time and will take effect immediately.
+Moving the volume slider to the right will increase the audio volume, moving it to the left will decrease the audio volume. You can mute all game sound by selecting the Mute checkbox.
+Moving the scroll speed slider to the right will increase the rate the map scrolls when you move your finger.
+Tapping the Default button will restore these settings to their default values.

+
+

GAME PLAY CONTROLS

+

The best way to learn the game play controls is to play M0: Introduction, which will introduce the game play elements to you as you play. Briefly, to move a unit, tap on the unit, then tap on the destination. To move multiple units, multi-select the units then tap on the destination. To multi-select, touch two fingers on the map to get a selection rectangle. Drag one or two corners to move and size the rectangle around the units you wish to select. To attack, select your desired units, then tap on the enemy unit you wish to attack.

+
+

SCROLLING THE MAP

+

To scroll the map, simply tap down on the map with your finger, and drag it in the direction you wish. If you wish, from Options you can adjust how fast the map scrolls when you move your finger.

+
+

SELECTING MULTIPLE UNITS

+

You can select multiple units at once by using multi-touch. Touch any two fingers onto the map to get a selection rectangle. Any units inside of this rectangle will get selected, and any outside will be deselected. Drag any two corners to move, size, or rotate the selection rectangle. Drag any one corner to make it larger in one direction.

+
+

POWER SELECT

+

Often in battle you will need to quickly select units. Double tap on a unit will multi-select all units of that same type that are on-screen!

+
+

CLEARING SELECTION

+

To clear the unit selection, touch an empty part of the map with two fingers.

+
+

MENU

+

While playing Hostile Takeover you can access the In Game Menu by tapping on the "Menu" button in the bottom-left corner of the display. The game will pause while you are looking at the In Game Menu. Selecting Back will return you to gameplay.

+
+

INDICATORS

+

To the right of the Menu button is your Credit Indicator. It shows your current financial resources. The dollar symbol will flash if you are too low on credits to complete a task. Note that the building constructing units when you run out of credits will also flash a dollar sign. If you have no Bullpups you can sell a building to get more credits to continue unit production. Sell a Galaxite Warehouse only as a last resort, you will lose its contents when you sell.

+
+

To the right of the Credit Indicator is your Power Indicator. It shows your current power supply in bars and your current demand with small triangles above and below the bars. The power symbol will flash when your power is too low. On color devices the bars will be green when you have plenty of power, yellow when you have just enough power and red when your power is too low.

+
+

MINI MAP

+

In the lower right-hand corner of your screen is a satellite overview of the nearby region. There is a square inside that shows which part of the region is currently displayed on your device. Use your finger to tap and drag that rectangle to scroll the map. The most convenient way to scroll the map however, is to place your finger directly on the map, and move it. The scroll speed can be changed from Game Options.

+
+

GAME ICONS

+

POP UP MENU ICONS
+White Hammer: used to display build menu for selected resource
+Shaded Hammer: indicates that the selected resource is currently building something. If you tap the shaded hammer, the resource stops unit construction and your credits are returned.
+White Wrench: Indicates that the selected resource is damaged and needs repair. Note that repairing a building consumes credits. If you are low on credits you may wish to wait on repairing a non-critical building.
+Grey Wrench: Indicates that the selected resource is not damaged.
+White Dollar Sign: Can be used to sell a resource. Your entire investment will not be returned. However, sometimes it is necessary to sell one resource to create a more critical one. Be aware that selling a Galaxite Warehouse will lose any credits it contains.
+
+PLACE BUILDING ICONS
+When you tap the HQ and select build, the Order Building screen is displayed. Once you select something to build you then get a hashed square which represents the footprint of the building. There are two icons associated with the hashed square:
+White Check Mark: When you have no dark hash marks, you can select the white check mark to confirm building placement.
+White X: Select the white x to cancel construction of the building.
+
+

+
+

IN GAME MENU

+
+

STATE OBJECTIVES

+

You can review your mission objectives at any time by selecting State Objectives from the In Game Menu The Statistics button will show you your current statistics and promotion level. The elapsed time shown is in game time, if you have altered the game speed this will differ from real time. You can more quickly access the objectives screen by tapping the Credit Indicator visible while playing the game.

+
+

SAVING YOUR GAME

+

While playing you can save your progress from the In Game Menu by tapping on the "Save Game" button. Select the slot where you would like to save your game and then tap the "Save" button.

+
+

LOADING A SAVED GAME

+

Once you have saved a game you can load it again by tapping the "Load Saved Game" button from the Main Menu. You will see a list of all the games that have been saved. Tap on the one you would like to load and then tap on the "Load" button. The saved games are named with the time saved (in 24-hour time) followed by the name of the mission.

+
+

RESTART MISSION

+

At any time during the mission you can choose to restart that mission by choosing Restart Mission. It will put you back at the beginning of the mission with the promotion level you had when you began that mission.

+
+

ABORT MISSION

+

Choosing Abort Mission will return you to the game main menu without preserving your game. You may wish to save your game before choosing this option.

+
+

PERSONNEL

+
+

SECURITY GUARD


+
+ +
+

Security Guards are cheap and cheerful but with limited firepower and defensive capabilities. The Security Guard is useful as a scout and is very effective in large groups. +
+
+Cost: 150
+Armor: Light
+Damage: Low
+Speed: Medium
+Range: Medium
+


+

ROCKET TROOPER


+
+ +
+

Rocket Troopers are particularly effective against armored vehicles. They pack a bit more punch and have a longer range than a security guard but can't travel as quickly.
+
+Cost: 300
+Armor: Light
+Damage: Low
+Speed: Slow
+Range: High
+


+

CORPORATE RAIDER


+
+ +
+

Every organization's worst nightmare. Send a Raider into an enemy building and it becomes yours! Corporate raiders need to be carefully protected since they have very little armor and no conventional weapons. Each raider can be used once.
+Corporate Raiders are available from the Human Resources Center after completing the HRC upgrade from the Research & Development Center. Note that the R & D Center must be intact for you to build the Corporate Raiders. If you sell the center or it is destroyed by enemies you will no longer be able to create Raiders.
+
+Armor: Light
+Damage: None
+Speed: Slow
+Range: Low
+


+

VEHICLES

+
+

SR-98 EAGLE


+
+ +
+

The SR-98 is a light, speedy tank that comes equipped with a machine gun. They aren't particularly powerful on their own but are very effective in groups and make nice scouts.
+
+Cost: 300
+Armor: Light
+Damage: Medium
+Speed: Fast
+Range: High
+


+

T-29 BROADSWORD


+
+ +
+

The T-29 comes equipped with a large mortar cannon that does a lot of damage. It is effective in combat with other units and particularly effective against buildings.
+
+Cost: 450
+Armor: Medium
+Damage: High
+Speed: Medium
+Range: High
+


+

M-18 HYDRA


+
+ +
+

The Hydra shoots high-explosive rockets that pack a large punch ideal for armored vehicles. It is very well rounded making it an excellent candidate for both offensive and defensive roles.
+
+Cost: 450
+Armor: Medium
+Damage: High
+Speed: Medium
+Range: High
+


+

T-33 LIBERATOR


+
+ +
+

The Liberator comes equipped with dual-cannons which allow it to make short work of enemy infantry.
+
+Cost: 600
+Armor: Heavy
+Damage: High
+Speed: Medium
+Range: High
+


+

G-4 BULLPUP


+
+ +
+

The G-4 Bullpup gathers Galaxite and returns it to your Processor. Order more to harvest faster or replace ones that have been destroyed. Their tough armor can get them through trouble spots but they often need to be guarded as they have no offensive capabilities.
+
+Cost: 600
+Armor: Heavy
+Damage: None
+Speed: Medium
+Range: None
+


+

H-7 DOMINION

+
+ +
+

The H-7 Dominion transforms into an HQ allowing you to start a new base anywhere you want. It has strong armor but no weapons. Once transformed it cannot be remobilized by the player. The Dominion is available from the Vehicle Transport Station after completing the VTS upgrade from the Research & Development Center.
+
+Cost: 1200
+Armor: Heavy
+Damage: None
+Speed: Slow
+Range: None
+


+

A-3 CYCLOPS

+
+ +
+

The A-3 Cyclops is a long range artillery unit particularly effective against buildings. Almost any other offensive unit can destroy it, so it should be escorted by other defensive units.
+
+Cost: 500
+Armor: Light
+Damage: High
+Speed: Slow
+Range: High
+


+

BUILDINGS

+
+

HEADQUARTERS


+
+ +
+

Your entire base is built around your headquarters (HQ). Without it you are unable to create new buildings and as such it should be carefully protected from enemy attack.
+
+Armor: Heavy
+Damage: None
+Power Demand: 0
+Power Supply: 0
+


+

POWER GENERATOR


+
+ +
+

Power Generators supply the rest of your buildings with power. Running low on power reduces the function of many structures making it imperative that you keep an eye on your power supply. A lightening bolt symbol will flash on underpowered units. Note that when the lightening bolt is flashing, these buildings will not function as expected. For example, Rocket Towers will no longer fire. When it is your own Rocket Tower that is disabled, this spells bad news for you. However, when enemy towers are underpowered you can take full advantage of their helplessness!
+
+Cost: 750
+Armor: Light
+Damage: None
+Power Demand: 0
+Power Supply: 40
+


+

GALAXITE PROCESSOR


+
+ +
+

A Galaxite Processor is used to process mined Galaxite and provides funding for your operation. It should be well defended. If the Bullpup remains in the processor, it usually indicates that you need to build a Galaxite Storage Warehouse. When Bullpups are destroyed you can build additional ones at the Vehicle Transport Station. +
+Cost: 1500
+Armor: Heavy
+Damage: None
+Power Demand: 10
+Power Supply: 0
+


+

GALAXITE STORAGE WAREHOUSE


+
+ +
+

Add warehouses to increase processed Galaxite capacity. If it is destroyed, sold, or taken over you will lose whatever Galaxite it contains. On the other hand using Corporate Raiders to take over an enemy's warehouse will garner the your enemy's stored credits. This is an excellent offensive maneuver.
+
+Cost: 750
+Armor: Light
+Damage: None
+Power Demand: 7
+Power Supply: 0
+


+

HUMAN RESOURCE CENTER (HRC)


+
+ +
+

The HRC is used to recruit personnel. You can use the Research & Development Center to upgrade the HRC and enable production of more advanced units. You can build up to ten units at a time by selecting the unit type and repeatedly tapping on the order button.
+
+Cost: 1000
+Armor: Medium
+Damage: None
+Power Demand: 10
+Power Supply: 0
+


+

VEHICLE TRANSPORT STATION (VTS)


+
+ +
+

Use the VTS to order vehicles. Upgrading the VTS gives you the ability to create more advanced vehicles. You can build up to ten units at a time by selecting the unit type and repeatedly tapping on the order button.
+
+Cost: 1250
+Armor: Heavy
+Damage: None
+Power Demand: 10
+Power Supply: 0
+


+

SURVEILLANCE CENTER


+
+ +
+

The Surveillance Center controls targeting for both your Gatling Tower and your Rocket Tower. It is required before you can build towers.
+
+Cost: 750
+Armor: Medium
+Damage: None
+Power Demand: 15
+Power Supply: 0
+


+

RESEARCH & DEVELOPEMENT CENTER


+
+ +
+

Investing in a R&D Center will allow you to upgrade your HRC and VTS, giving you access to the newest technologies available. Note that once the VTS is upgraded you can sell your R&D center to return some of your credits. However, even after upgrading the HRC, if you lose your R&D Center you will be unable to create Corporate Raiders. The R&D center must be intact for these very special units to be available.
+
+Cost: 750
+Armor: Medium
+Damage: None
+Power Demand: 15
+Power Supply: 0
+


+

GATLING TOWER

+
+

These guard towers are equipped with machine guns and are fairly effective at keeping the competition out. A fully powered Surveillance Center is required for the targeting systems to function so that towers will fire automatically as enemies come into range. You can also direct their fire by tapping on the tower and then tapping on the enemy you wish to fire upon. Note that these towers take a great deal of power to remain operational. As you create towers, be sure to keep an eye on your power meter. A flashing lightening bolt symbol on your tower indicates that it is under powered and will not fire at the enemy.
+
+Cost: 750
+Armor: Light
+Damage: Medium
+Power Demand: 10
+Power Supply: 0
+


+

ROCKET TOWER

+
+ +

A guard tower equipped with a rocket launcher. These towers are more effective than Gatling Towers but cost more. Like the Gatling Tower the Rocket Tower requires a Surveillance Center and sufficient power to operate. Towers will fire automatically as enemies come into range. You can also direct the tower's fire by tapping on the tower and then tapping on the enemy you wish to fire upon. Note that these towers take a great deal of power to remain operational. As you create towers, be sure to keep an eye on your power meter. A flashing lightening bolt symbol on your tower indicates that it is under powered and will not fire at the enemy.
+
+Cost: 850
+Armor: Light
+Damage: High
+Power Demand: 10
+Power Supply: 0
+


+

Research Upgrades

+
+

Research upgrades allow you to improve the units available at your Human Resource Center (HRC) and Vehicle Transport Station (VTS). The Research & Development Center is available starting on Mission 3. Select this building and use the build icon to view available upgrades. Upgrading the HRC enables production of Rocket Troopers and later Corporate Raiders. Upgrading the VTS enables production of Dominions, Cyclops and other advanced tanks. Not all upgrades are available in all missions. It is worth noting that even after you have upgraded your HRC, the R&D Center must survive to enable production of Corporate Raiders. Therefore you cannot sell this building if you wish to produce these very special units.
+


+

TIPS

+
+

BUILDING OPTIONS

+

To display the options associated with a particular building tap on the building and select the Build Icon (the white hammer). The units associated with that building are displayed. Note that not all units are available on all missions. As you advance through more difficult missions, additional options are added to the build menus. Some units are only available after upgrading via the Research & Development Center.

+
+

TRANSFORMING YOUR HEADQUARTERS

+

To change your Dominion into a Headquarters you need to bring up the Transform Menu. Tap your finger on the Dominion and hold it until the Transform Menu appears.

+
+

CHANGING THE DIFFICULTY

+

You can change the difficulty level of the game in the Options.

+
+

UNIT LIMITS

+

The number of units you can create is limited based on the available memory in your device. When the game reaches the unit limit for your device, a notice is displayed on the lower left-hand corner of your screen. If you notice that all your build buttons are disabled, you may have reached your unit limit.

+
+

SPEED ECOM DISPLAY

+

By default, ECOM messages are displayed a few characters at a time. To display the entire message at once, tap the ECOM text area.

+
+

SWITCHING APPLICATIONS

+

If you switch to another application while playing the game, Hostile Takeover will pause and be restored when you return.

+
+

Authoring Add-On Mission Packs

+

Players can create new Add-On Mission Packs using the mission authoring toolkit. Using this tool, you can design, test, and share the missions you have authored with the player community. To begin, visit the Mission Authoring section of the Hostile Takeover forums and read everything there. Once you have a mission pack that you want to test, you'll want to get it on to your device. To do this, upload your mission to any location on the internet. Start the game, tap on Add-On Mission Packs. Tap on the Title label to sort by name, and you'll see "Custom URL..." near the top. Tap on this and select DOWNLOAD, and enter the custom URL to your mission pack and tap OK, and it will be downloaded to your device. Now you can play and refine your mission pack until it is ready to share with the community. To share it, visit the Mission Sharing section of the website for instructions on how to post it there. Posting it there will add it to the Add-On Mission Pack list that all players see.

+
+ +

CREDITS

+

Hostile Takeover(TM)
+
+Developed by: +Spiffcode, Inc +http://www.spiffcode.com +
+Game Missions, Forum, and Information, and Support:
+REPLACE ME: MAIN SITE URL +
+
+Hostile Takeover is a trademark of Spiffcode, Inc +Copyright 2003-2014 Spiffcode, Inc. +All Rights Reserved. +
+
+Credits: +
+Primary Developers: +Scott Ludwig +Darrin Massena +
+Additional Coding: +Casey Margell +Shaula Massena +
+Mission Design: +Zion A. Dutro +Hans Olav Elseb~ +Rob Girling +Brian Margell +Brian Outhwaite +
+Test: +Andrew Massena +Brian Outhwaite +
+Story Editing and Dialog: +Elizabeth Chapman +
+Art/Animation: +Rob Girling +Mark Soderwall +
+Marketing Consultant: +Lisa Linnenkohl +

diff --git a/data/level1.lvl.pp b/data/level1.lvl.pp new file mode 100644 index 0000000..c5f8f59 --- /dev/null +++ b/data/level1.lvl.pp @@ -0,0 +1,54 @@ +#include + +[General] +Title=Our Very First Test Level! +TileMap=test.tmap +TerrainMap=test.trmap +Palette=palette1.palbin +InitialView=0,0 +PlayerCredits=5000 +WinCondition=5 +LoseCondition=10 + +[kidfDefault] +FORM=(0 0 160 160) kidcOk LevelIntroBkgnd.tbm +LABEL=0 (4 4 0 0) "This test level is our simplest repre-" knfntTest +LABEL=0 (4 15 0 0) "sentation of a playable RTS game." knfntTest +LABEL=0 (4 29 0 0) "You are given 5,000 credits and a" knfntTest +LABEL=0 (4 40 0 0) "Human Resource Center capable of" knfntTest +LABEL=0 (4 51 0 0) "recruiting Short Range Infantry." knfntTest +LABEL=0 (4 66 0 0) "The enemy has the same capability." knfntTest +LABEL=0 (4 89 0 0) "Objective:" knfntTest +LABEL=0 (4 100 0 0) "Destroy the enemy base before they" knfntTest +LABEL=0 (4 111 0 0) "destroy yours!" knfntTest +BUTTON=kidcOk (52 140 0 0) "OK!" knfntTest + +; GameObject prototypes: +; name=kgtSurfaceDecal,bitmap name,x,y[,flags] +; name=kgtScenery,bitmap name,x,y[,flags] // UNDONE: animation instead of bitmap +; name=kgtShortRangeInfantry,side,x,y[,flags] +; name=kgtHumanResourceCenter,side,x,y[,flags] + +[GameObjects] +nil=kgtHumanResourceCenter,ksideA,38,100 +nil=kgtProcessor,ksideA,22,68 +nil=kgtShortRangeInfantry,ksideA,30,56 +nil=kgtShortRangeInfantry,ksideA,62,138 +nil=kgtGalaxMiner,ksideA,46,154 +nil=kgtGalaxMiner,ksideA,62,154 + +nil=kgtHumanResourceCenter,ksideB,170,158 +nil=kgtReactor,ksideB,138,56 +nil=kgtShortRangeInfantry,ksideB,142,40 +nil=kgtShortRangeInfantry,ksideB,158,120 +nil=kgtProcessor,ksideB,168,110 +nil=kgtGalaxMiner,ksideB,158,154 + +nil=kgtScenery,tree2.tbm,80,100 +nil=kgtScenery,tree2.tbm,197,67 +nil=kgtScenery,tree2.tbm,242,289 +nil=kgtScenery,tree2.tbm,194,407 +nil=kgtScenery,tree2.tbm,208,219 +nil=kgtScenery,tree2.tbm, 94,112 +nil=kgtSurfaceDecal,tree2.tbm,395,306 +nil=kgtScenery,tree2.tbm,266,290 diff --git a/data/makedirs.bat b/data/makedirs.bat new file mode 100644 index 0000000..b3b5827 --- /dev/null +++ b/data/makedirs.bat @@ -0,0 +1,4 @@ +@echo off +if not exist 824 md 824 +if not exist trg md trg +exit /B 0 diff --git a/data/makedirs.sh b/data/makedirs.sh new file mode 100755 index 0000000..f631a5d --- /dev/null +++ b/data/makedirs.sh @@ -0,0 +1,4 @@ +mkdir 824 2> /dev/null > /dev/null && false +mkdir 832 2> /dev/null > /dev/null && false +mkdir trg 2> /dev/null > /dev/null && false +exit 0 diff --git a/data/makefile b/data/makefile new file mode 100644 index 0000000..b1ad8f8 --- /dev/null +++ b/data/makefile @@ -0,0 +1,405 @@ +# +# Depth related settings, default 8 +# + +MONO=mono +#CPP=gcc -E -P -x c -Wno-trigraphs +CPP=cpp -P +INICRUNCH=inicrunch + +HELPFILE=help.txt + +ifdef IPHONE +TILESIZE=32 +ifdef MULTIPLAYER +DEFINI=-DIPHONE -DMULTIPLAYER +HELPFILE=helpiphone-multiplayer.txt +else +DEFINI=-DIPHONE +HELPFILE=helpiphone.txt +endif +endif + +ifdef DEV_BUILD +DEFDEV=-DDEV_BUILD=1 +endif + +ifndef DEPTH +DEPTH=8 +endif + +ifeq ($(DEPTH),8) +PAL_SIZE=256 +FIXED_PAL_SIZE=131 +endif + +ifeq ($(DEPTH),4) +PAL_SIZE=16 +FIXED_PAL_SIZE=16 +endif + +ifdef TS +TILESIZE=$(TS) +endif + +ifndef TILESIZE +TILESIZE=16 +endif + +OUTDIR:=$(DEPTH)$(TILESIZE) +ARTDIR:=art$(DEPTH)$(TILESIZE) +PALDIR:=art$(DEPTH)$(TILESIZE) + +ifeq ($(DEPTH),4) +ARTDIR:=art8$(TILESIZE) +endif +DT:=$(DEPTH)$(TILESIZE) + +# Hack to support 240x320 mode which blends hires and custom res elements +# NOTE: all assignments using TILESIZE above this point must be of the ':=' +# variety. +# NOTE: must rm -rf art820 first since none of it is actually "checked in" + +ifeq ($(TILESIZE),20) +override TILESIZE=24 +ifdef QUICK +COPYART=xcopy art824 art820 /Q /Y /R /D /E /I /EXCLUDE:excludecvs.txt && xcopy art820.true art820 /Q /Y /R /E /I /EXCLUDE:excludecvs.txt +else +COPYART=@rm -rf art820 && mkdir art820 && xcopy art824 art820 /Q /Y /R /D /E /I /EXCLUDE:excludecvs.txt && xcopy art820.true art820 /Q /Y /R /E /I /EXCLUDE:excludecvs.txt +endif +endif + +# Hack to support iPhone's 32 tile size. Use all the art from 24, but scale +# tiles and animations. + +ifeq ($(TILESIZE),32) +ARTDIR:=art$(DEPTH)24 +PALDIR:=art$(DEPTH)24 +AMX_SCALEARG:=-scale 1.3333333333333 -noscaleicon +TILE_SCALEARG:=-scale 1.3333333333333 +FORMBMP_SCALEARG:=-scale 1.0 +MAPBMP_SCALEARG:=-scale 1.3333333333333 +RAWBMP_SCALEARG:=-scale 1.0 +endif + + +# +# Palette settings +# + +PRELOAD_PAL=preload_$(DEPTH)bpp.pal +COMMON_PAL=$(ARTDIR)/common_$(DEPTH)bpp.pal +SHELL_PAL=$(ARTDIR)/shell_$(DEPTH)bpp.pal +FIXED_PAL=$(PALDIR)/fixed_$(DEPTH)bpp.pal +CUTSCENE_PAL=$(PALDIR)/cutscene_$(DEPTH)bpp.pal +DESERT_PAL=$(PALDIR)/desert_$(DEPTH)bpp.pal +GRASSY_PAL=$(PALDIR)/grassy_$(DEPTH)bpp.pal + +# +# Palette space for background template +# + +ifeq ($(DEPTH),4) +DESERT_BACKGROUND_PAL_SIZE=0 +GRASSY_BACKGROUND_PAL_SIZE=0 +endif + +ifeq ($(DEPTH),8) +DESERT_BACKGROUND_PAL_SIZE=32 +GRASSY_BACKGROUND_PAL_SIZE=32 +endif + +# +# Global defines +# + +CPP_INCS=-I../game +TOOLDIR=../bin +PDB=htdata$(DT).pdb +CREATORID=WARI + +# +# amx categorization +# + +FIXED_AMXS=$(filter-out $(CUTSCENE_AMXS),$(wildcard $(ARTDIR)/*.amx)) + +# +# Non-amx image categorization +# + +NOTSRC_BMPS= +DESERT_BMPS= +GRASSY_BMPS= +FONT_BMPS=$(ARTDIR)/standardfont.bmp $(ARTDIR)/shadowfont.bmp $(ARTDIR)/buttonfont.bmp $(ARTDIR)/titlefont.bmp $(ARTDIR)/hudfont.bmp +CUTSCENE_RAW_BMPS=$(wildcard $(ARTDIR)/cutscenebitmaps/*.bmp) +SHELL_RAW_BMPS=$(wildcard $(ARTDIR)/shellrawbitmaps/*.bmp) +FIXED_RAW_BMPS=$(wildcard $(ARTDIR)/rawbitmaps/*.bmp) +FIXED_BMPS=$(filter-out $(DESERT_BMPS) $(GRASSY_BMPS) $(FONT_BMPS) $(NOTSRC_BMPS),$(wildcard $(ARTDIR)/bitmaps/*.bmp)) + +# Would be better to put these in their own directory if this scaling +# experiment works + +FIXED_MAP_BMPS=\ +$(ARTDIR)/bitmaps/arrow0.bmp \ +$(ARTDIR)/bitmaps/arrow1.bmp \ +$(ARTDIR)/bitmaps/arrow2.bmp \ +$(ARTDIR)/bitmaps/arrow3.bmp \ +$(ARTDIR)/bitmaps/arrow4.bmp \ +$(ARTDIR)/bitmaps/arrow5.bmp \ +$(ARTDIR)/bitmaps/arrow6.bmp \ +$(ARTDIR)/bitmaps/arrow7.bmp \ +$(ARTDIR)/bitmaps/fog0001.bmp \ +$(ARTDIR)/bitmaps/fog0010.bmp \ +$(ARTDIR)/bitmaps/fog0011.bmp \ +$(ARTDIR)/bitmaps/fog0100.bmp \ +$(ARTDIR)/bitmaps/fog0101.bmp \ +$(ARTDIR)/bitmaps/fog0111.bmp \ +$(ARTDIR)/bitmaps/fog1000.bmp \ +$(ARTDIR)/bitmaps/fog1010.bmp \ +$(ARTDIR)/bitmaps/fog1011.bmp \ +$(ARTDIR)/bitmaps/fog1100.bmp \ +$(ARTDIR)/bitmaps/fog1101.bmp \ +$(ARTDIR)/bitmaps/fog1110.bmp \ +$(ARTDIR)/bitmaps/fog1111.bmp \ +$(ARTDIR)/bitmaps/galax1a.bmp \ +$(ARTDIR)/bitmaps/galax1b.bmp \ +$(ARTDIR)/bitmaps/galax1c.bmp \ +$(ARTDIR)/bitmaps/galax2a.bmp \ +$(ARTDIR)/bitmaps/galax2b.bmp \ +$(ARTDIR)/bitmaps/galax2c.bmp \ +$(ARTDIR)/bitmaps/galax3a.bmp \ +$(ARTDIR)/bitmaps/galax3b.bmp \ +$(ARTDIR)/bitmaps/galax3c.bmp \ +$(ARTDIR)/bitmaps/placementBad.bmp \ +$(ARTDIR)/bitmaps/placementGood.bmp \ +$(ARTDIR)/bitmaps/plant.bmp \ +$(ARTDIR)/bitmaps/plant1.bmp \ +$(ARTDIR)/bitmaps/plant2.bmp \ +$(ARTDIR)/bitmaps/plant3.bmp \ +$(ARTDIR)/bitmaps/plant4.bmp \ +$(ARTDIR)/bitmaps/plant5.bmp \ +$(ARTDIR)/bitmaps/RocketArtifact.bmp \ +$(ARTDIR)/bitmaps/Rocks.bmp \ +$(ARTDIR)/bitmaps/tree.bmp \ +$(ARTDIR)/bitmaps/tree1.bmp \ +$(ARTDIR)/bitmaps/tree2.bmp \ +$(ARTDIR)/bitmaps/tree3.bmp \ +$(ARTDIR)/bitmaps/tree4.bmp \ +$(ARTDIR)/bitmaps/tree5.bmp \ +$(ARTDIR)/bitmaps/tree6.bmp \ +$(ARTDIR)/bitmaps/tree7.bmp \ +$(ARTDIR)/bitmaps/scorch_8x8.bmp \ +$(ARTDIR)/bitmaps/scorch_16x16.bmp \ +$(ARTDIR)/bitmaps/scorch_32x16.bmp \ +$(ARTDIR)/bitmaps/scorch_48x48.bmp \ +$(ARTDIR)/bitmaps/arrowheadleft.bmp \ +$(ARTDIR)/bitmaps/arrowheadup.bmp \ +$(ARTDIR)/bitmaps/arrowheadright.bmp \ +$(ARTDIR)/bitmaps/arrowheaddown.bmp \ +$(ARTDIR)/bitmaps/build_btn_up.bmp \ +$(ARTDIR)/bitmaps/build_btn_down.bmp \ +$(ARTDIR)/bitmaps/build_btn_disabled.bmp \ +$(ARTDIR)/bitmaps/abort_build_btn_up.bmp \ +$(ARTDIR)/bitmaps/abort_build_btn_down.bmp \ +$(ARTDIR)/bitmaps/repair_btn_up.bmp \ +$(ARTDIR)/bitmaps/repair_btn_down.bmp \ +$(ARTDIR)/bitmaps/repair_btn_disabled.bmp \ +$(ARTDIR)/bitmaps/abort_repair_btn_up.bmp \ +$(ARTDIR)/bitmaps/abort_repair_btn_down.bmp \ +$(ARTDIR)/bitmaps/sell_btn_up.bmp \ +$(ARTDIR)/bitmaps/sell_btn_down.bmp \ +$(ARTDIR)/bitmaps/sell_btn_disabled.bmp + +FIXED_FORM_BMPS=$(filter-out $(FIXED_MAP_BMPS),$(FIXED_BMPS)) + + +# +# Level categorization +# + +GRASSY_LEVELS=S_00.ld S_01.ld S_02.ld S_03.ld S_04.ld S_05.ld S_06.ld C_02.ld C_03.ld M_05.ld M_11.ld M_13.ld M_14.ld M_16.ld M_17.ld M_19.ld M_22.ld +DESERT_LEVELS=S_07.ld S_08.ld S_09.ld S_10.ld S_11.ld S_12.ld S_13.ld S_14.ld C_01.ld C_04.ld C_05.ld D_03.ld M_02.ld M_03.ld M_04.ld M_06.ld M_07.ld M_08.ld M_09.ld M_10.ld M_12.ld M_15.ld M_18.ld M_20.ld M_21.ld +ifdef DEV_BUILD +GRASSY_LEVELS:=$(GRASSY_LEVELS) testlevel.ld M_01.ld +endif +ALL_LEVELS=$(DESERT_LEVELS) $(GRASSY_LEVELS) + +# +# Targets +# +# $(subst sounds/,,$(subst .wav,.snd,$(wildcard sounds/*.wav))) \ +# + +BINS=\ +$(patsubst %.ini.pp,%.ini,$(wildcard *.ini.pp)) \ +$(patsubst %.amx,%.anir,$(patsubst $(ARTDIR)/%,%,$(wildcard $(ARTDIR)/*.amx))) \ +$(patsubst %.ld,%.lvl,$(ALL_LEVELS)) + +TRGS=\ +trg/desert_bmps_$(DT).trg trg/grassy_bmps_$(DT).trg \ +trg/desert_levels_$(DT).trg trg/grassy_levels_$(DT).trg \ +trg/fixed_map_bmps_$(DT).trg trg/fixed_form_bmps_$(DT).trg \ +trg/fixed_amxs_$(DT).trg trg/fixed_raw_bmps_$(DT).trg \ +trg/shell_raw_bmps_$(DT).trg trg/cutscene_raw_bmps_$(DT).trg \ +trg/help_$(DT).trg trg/strings_$(DT).trg trg/font_bmps_$(DT).trg + +all: + @. ./makedirs.sh + $(COPYART) + @make --no-print-directory $(OUTDIR)/$(PDB) + +clean: + @. ./makedirs.sh + $(COPYART) + rm -f $(OUTDIR)/*.* + rm -f trg/*$(DT).trg + rm -f $(FIXED_PAL) $(DESERT_PAL) $(GRASSY_PAL) + +special: + make + cp proc_base_cropped.png tbmtest.png + $(MONO) $(TOOLDIR)/bcr2.exe -code $(FIXED_PAL) $(OUTDIR) tbmtest.png + $(MONO) $(TOOLDIR)/packpdb2.exe -p $(CREATORID) $(OUTDIR)/$(PDB) $(OUTDIR)/*.* + cp -f $(OUTDIR)/$(PDB) ../game/$(PDB) + +# +# Make .pdb +# + +$(OUTDIR)/$(PDB): $(TRGS) $(BINS:%=$(OUTDIR)/%) + @echo +++VERSION+++ > $(OUTDIR)/version.txt + $(MONO) $(TOOLDIR)/packpdb2.exe -p $(CREATORID) $(OUTDIR)/$(PDB) "$(OUTDIR)/*.*" -nocompress "*.snd" "*.tset*" "*.tbm" version.txt forms.ini + cp -f $(OUTDIR)/$(PDB) ../game/$(PDB) + +# +# Crunch .amx's +# + +trg/fixed_amxs_$(DT).trg: $(FIXED_AMXS) $(FIXED_PAL) $(patsubst %.amx,%/*.png,$(FIXED_AMXS)) $(TOOLDIR)/acrunch.exe + for i in $(FIXED_AMXS); do echo $(TOOLDIR)/acrunch $$i && $(MONO) $(TOOLDIR)/acrunch.exe $(AMX_SCALEARG) $(FIXED_PAL) $$i $(OUTDIR); done + @echo x >trg/fixed_amxs_$(DT).trg + +# +# Crunch .ini's +# + +$(OUTDIR)/%.ini: %.ini.pp ../game/res.h + @echo Preprocessing $(@F).pp... + @$(CPP) -DTILESIZE=$(TILESIZE) $(DEFDEMO) $(DEFDEV) $(DEFINI) $(CPP_INCS) $(@F).pp ini.tmp + @if [ -d "prepre" ]; then cp ini.tmp prepre/$(@F); fi + @$(TOOLDIR)/$(INICRUNCH) ini.tmp $@ + +# @if EXIST prepre. @cp ini.tmp prepre/$(@F) + +# +# Help +# + +trg/help_$(DT).trg: $(HELPFILE) + @cp $(HELPFILE) $(OUTDIR)/help.txt + @echo x > trg/help_$(DT).trg + +# +# StringTable +# + +trg/strings_$(DT).trg: ../game/strings.h + $(MONO) $(TOOLDIR)/stringtable.exe ../game/strings.h $(OUTDIR)/strings.bin + @echo x > trg/strings_$(DT).trg + +# +# Crunch .bmps +# + +trg/desert_bmps_$(DT).trg : $(DESERT_PAL) $(DESERT_BMPS) + $(MONO) $(TOOLDIR)/bcr2.exe $(BMP_SCALEARG) $(DESERT_PAL) $(OUTDIR) $(DESERT_BMPS) + @echo x > trg/desert_bmps_$(DT).trg + +trg/grassy_bmps_$(DT).trg : $(GRASSY_PAL) $(GRASSY_BMPS) + $(MONO) $(TOOLDIR)/bcr2.exe $(BMP_SCALEARG) $(GRASSY_PAL) $(OUTDIR) $(GRASSY_BMPS) + @echo x > trg/grassy_bmps_$(DT).trg + +trg/fixed_form_bmps_$(DT).trg : $(FIXED_PAL) $(FIXED_FORM_BMPS) + $(MONO) $(TOOLDIR)/bcr2.exe $(FORMBMP_SCALEARG) $(FIXED_PAL) $(OUTDIR) $(FIXED_FORM_BMPS) + @echo x > trg/fixed_form_bmps_$(DT).trg + +trg/fixed_map_bmps_$(DT).trg : $(FIXED_PAL) $(FIXED_MAP_BMPS) + $(MONO) $(TOOLDIR)/bcr2.exe $(MAPBMP_SCALEARG) $(FIXED_PAL) $(OUTDIR) $(FIXED_MAP_BMPS) + @echo x > trg/fixed_map_bmps_$(DT).trg + +trg/fixed_raw_bmps_$(DT).trg : $(FIXED_PAL) $(FIXED_RAW_BMPS) + $(MONO) $(TOOLDIR)/bcr2.exe -raw $(RAWBMP_SCALEARG) $(FIXED_PAL) $(OUTDIR) $(FIXED_RAW_BMPS) + @echo x > trg/fixed_raw_bmps_$(DT).trg + +trg/shell_raw_bmps_$(DT).trg : $(SHELL_PAL) $(SHELL_RAW_BMPS) + $(MONO) $(TOOLDIR)/bcr2.exe -raw $(RAWBMP_SCALEARG) $(SHELL_PAL) $(OUTDIR) $(SHELL_RAW_BMPS) + @echo x > trg/shell_raw_bmps_$(DT).trg + +trg/font_bmps_$(DT).trg : $(FIXED_PAL) $(FONT_BMPS) + $(MONO) $(TOOLDIR)/bcr2.exe -font $(FIXED_PAL) $(OUTDIR) $(FONT_BMPS) + @echo x > trg/font_bmps_$(DT).trg + +# +# Cutscene bitmaps are special because each bitmap has its own palette +# which also must be crunched to the output dir +# + +trg/cutscene_raw_bmps_$(DT).trg : $(patsubst %.bmp,%.act,$(CUTSCENE_RAW_BMPS)) $(CUTSCENE_RAW_BMPS) $(TOOLDIR)/palbin.exe $(TOOLDIR)/paltool.exe + for i in $(CUTSCENE_RAW_BMPS); do echo Crunching cutscene $$i && $(MONO) ../bin/paltool.exe -6 -c $(PAL_SIZE) -p $(PAL_SIZE) -s -t -o _tmp.pal $(COMMON_PAL) $$i && $(MONO) ../bin/bcr2.exe -raw $(RAWBMP_SCALEARG) _tmp.pal $(OUTDIR) $$i && $(MONO) ../bin/palbin.exe _tmp.pal $(OUTDIR)/`basename $$i .bmp`.palbin && rm _tmp.pal; done + @echo x > trg/cutscene_raw_bmps_$(DT).trg + +# +# Crunch levels +# +# Parameter order: +# tile size (16 / 24) +# depth (4 / 8) +# nThresholdBackground (% of tile consumed by background before being considered background - for minimap) +# nLuminanceBackgroundMult (luminance multiplier for background minimap tiles) +# nSaturationBackgroundMult (saturation multiplier for background minimap tiles) +# nLuminanceForegroundMult (luminance multiplier for foreground minimap tiles) +# nSaturationForegroundMult (saturation multiplier for foreground minimap tiles) +# palette directory +# out directory +# levels (*.ld's) + +trg/desert_levels_$(DT).trg : $(DESERT_LEVELS) $(DESERT_PAL) $(TOOLDIR)/mcl.exe + $(MONO) $(TOOLDIR)/mcl.exe -levels $(TILESIZE) $(DEPTH) -1.0 1.0 1.0 1.0 1.0 $(PALDIR) $(OUTDIR) $(DESERT_LEVELS) + @if [ -d "prepre" ]; then for i in $(subst .ld,.lvl,$(DESERT_LEVELS)); do cp $(OUTDIR)/$$i prepre; done; fi + @for i in $(subst .ld,.lvl,$(DESERT_LEVELS)); do echo $$i; $(CPP) -imacros ../game/res.h $(OUTDIR)/$$i > ini.tmp; ../bin/$(INICRUNCH) ini.tmp $(OUTDIR)/$$i; done + @echo x > trg/desert_levels_$(DT).trg + +trg/grassy_levels_$(DT).trg : $(GRASSY_LEVELS) $(GRASSY_PAL) $(TOOLDIR)/mcl.exe + $(MONO) $(TOOLDIR)/mcl.exe -levels $(TILESIZE) $(DEPTH) 0.8 0.9 0.9 1.1 1.3 $(PALDIR) $(OUTDIR) $(GRASSY_LEVELS) + @if [ -d "prepre" ]; then for i in $(subst .ld,.lvl,$(GRASSY_LEVELS)); do cp $(OUTDIR)/$$i prepre; done; fi + @for i in $(subst .ld,.lvl,$(GRASSY_LEVELS)); do echo $$i; $(CPP) -imacros ../game/res.h $(OUTDIR)/$$i > ini.tmp; ../bin/$(INICRUNCH) ini.tmp $(OUTDIR)/$$i; done + @echo x > trg/grassy_levels_$(DT).trg + +# +# Crunch palettes +# Don't apply dependencies that will make these rebuild all the time, because it is +# not a fast process. They will get made if they don't exist, otherwise they can +# be forced to rebuild with "make clean". +# + +$(FIXED_PAL) : $(COMMON_PAL) $(TOOLDIR)/paltool.exe + @echo $(patsubst %.amx,%/*.png,$(FIXED_AMXS)) > files.tmp + $(MONO) $(TOOLDIR)/paltool.exe -6 -c $(FIXED_PAL_SIZE) -p $(PAL_SIZE) -s -t -o $(FIXED_PAL) -f files.tmp $(COMMON_PAL) + +# Shell palette contains the special colors needed for the title (main) screen + +$(SHELL_PAL) : $(FIXED_PAL) $(TOOLDIR)/paltool.exe $(TOOLDIR)/palbin.exe $(SHELL_RAW_BMPS) + $(MONO) $(TOOLDIR)/paltool.exe -6 -c $(PAL_SIZE) -p $(PAL_SIZE) -s -t -o $(SHELL_PAL) $(FIXED_PAL) $(SHELL_RAW_BMPS) + $(MONO) $(TOOLDIR)/palbin.exe $(SHELL_PAL) $(OUTDIR)/shell.palbin + $(MONO) $(TOOLDIR)/shadowmap.exe $(SHELL_PAL) $(OUTDIR)/shell.palbin.shadowmap 0.6 + +$(DESERT_PAL) : $(FIXED_PAL) $(TOOLDIR)/palbin.exe + $(MONO) $(TOOLDIR)/mcl.exe -makepal $(TILESIZE) desert.tc $(PAL_SIZE) $(FIXED_PAL_SIZE) $(DESERT_BACKGROUND_PAL_SIZE) $(FIXED_PAL) $(DESERT_PAL) + $(MONO) $(TOOLDIR)/palbin.exe $(DESERT_PAL) $(OUTDIR)/desert.palbin + $(MONO) $(TOOLDIR)/shadowmap.exe $(DESERT_PAL) $(OUTDIR)/desert.palbin.shadowmap 0.6 + +$(GRASSY_PAL) : $(FIXED_PAL) $(TOOLDIR)/palbin.exe + $(MONO) $(TOOLDIR)/mcl.exe -makepal $(TILESIZE) grassy.tc $(PAL_SIZE) $(FIXED_PAL_SIZE) $(GRASSY_BACKGROUND_PAL_SIZE) $(FIXED_PAL) $(GRASSY_PAL) + $(MONO) $(TOOLDIR)/palbin.exe $(GRASSY_PAL) $(OUTDIR)/grassy.palbin + $(MONO) $(TOOLDIR)/shadowmap.exe $(GRASSY_PAL) $(OUTDIR)/grassy.palbin.shadowmap 0.6 diff --git a/data/makepals.bat b/data/makepals.bat new file mode 100644 index 0000000..8900b4d --- /dev/null +++ b/data/makepals.bat @@ -0,0 +1,3 @@ +..\bin\paltool -p -s -t -6 -o fixed_8bpp.pal preload.pal sri\* reactor\* proc\* miner\* hrc\* testfont.bmp +..\bin\paltool -p -s -t -6 -o level1_8bpp.pal fixed_8bpp.pal cc.bmp tree2.bmp tree3.bmp +..\bin\paltool -p -s -t -6 -o level3_8bpp.pal fixed_8bpp.pal cc4.bmp tree.bmp diff --git a/data/mk b/data/mk new file mode 100644 index 0000000..7d15d30 --- /dev/null +++ b/data/mk @@ -0,0 +1 @@ +make IPHONE=1 MULTIPLAYER=1 diff --git a/data/preload_4bpp.pal b/data/preload_4bpp.pal new file mode 100644 index 0000000..57afaea --- /dev/null +++ b/data/preload_4bpp.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +255 255 255 +238 238 238 +221 221 221 +204 204 204 +187 187 187 +170 170 170 +153 153 153 +136 136 136 +119 119 119 +102 102 102 +85 85 85 +68 68 68 +51 51 51 +34 34 34 +17 17 17 +0 0 0 diff --git a/data/preload_8bpp.pal b/data/preload_8bpp.pal new file mode 100644 index 0000000..1c5f01c --- /dev/null +++ b/data/preload_8bpp.pal @@ -0,0 +1,56 @@ +JASC-PAL +0100 +53 +0 0 0 +255 255 255 +255 0 0 +0 255 0 +255 255 0 +0 192 196 +187 250 250 +52 97 110 +28 99 114 +37 71 78 +17 43 45 +107 252 252 +87 161 173 +56 121 134 +43 80 91 +30 58 62 +0 116 232 +0 96 196 +0 64 120 +0 48 92 +0 32 64 +232 33 0 +196 28 0 +120 11 0 +92 11 0 +64 9 0 +232 229 0 +196 194 0 +120 119 0 +92 91 0 +64 63 0 +156 88 140 +216 216 216 +168 168 168 +144 144 144 +132 128 128 +120 120 120 +112 112 112 +100 100 100 +96 96 96 +92 92 92 +84 84 84 +76 76 76 +72 72 72 +68 68 68 +64 64 64 +60 60 60 +52 52 48 +48 48 44 +24 24 24 +20 20 16 +8 8 8 +252 0 252 diff --git a/data/shell.act b/data/shell.act new file mode 100644 index 0000000..d5595f3 Binary files /dev/null and b/data/shell.act differ diff --git a/data/sounds/soundeffects.zip b/data/sounds/soundeffects.zip new file mode 100644 index 0000000..ae8f1c6 Binary files /dev/null and b/data/sounds/soundeffects.zip differ diff --git a/drmutil/README.txt b/drmutil/README.txt new file mode 100644 index 0000000..86c0e2e --- /dev/null +++ b/drmutil/README.txt @@ -0,0 +1,2 @@ +This utility is for generating "K-" keys from "C-" codes, in the old +Palm and PocketPC versions of Warfare Incorporated. diff --git a/drmutil/drmutil.sln b/drmutil/drmutil.sln new file mode 100644 index 0000000..95aa890 --- /dev/null +++ b/drmutil/drmutil.sln @@ -0,0 +1,21 @@ +Microsoft Visual Studio Solution File, Format Version 7.00 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "drmutil", "drmutil.vcproj", "{649D88AB-617E-4C96-AD20-07E21D32A293}" +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + ConfigName.0 = Debug + ConfigName.1 = Release + EndGlobalSection + GlobalSection(ProjectDependencies) = postSolution + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {649D88AB-617E-4C96-AD20-07E21D32A293}.Debug.ActiveCfg = Debug|Win32 + {649D88AB-617E-4C96-AD20-07E21D32A293}.Debug.Build.0 = Debug|Win32 + {649D88AB-617E-4C96-AD20-07E21D32A293}.Release.ActiveCfg = Release|Win32 + {649D88AB-617E-4C96-AD20-07E21D32A293}.Release.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/drmutil/drmutil.vcproj b/drmutil/drmutil.vcproj new file mode 100644 index 0000000..a5e8d79 --- /dev/null +++ b/drmutil/drmutil.vcproj @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/drmutil/main.cpp b/drmutil/main.cpp new file mode 100644 index 0000000..1086ca6 --- /dev/null +++ b/drmutil/main.cpp @@ -0,0 +1,498 @@ +// DRM utility +// Spiffcode Confidential +// Copyright 2003 Spiffcode, Inc. + +#include +#include +#include +#include +#include + +typedef unsigned char byte; +typedef unsigned short word; +typedef unsigned long dword; + +// The code is derived from the Owner name. The Owner name is backed up and is the same +// after a cold boot; that's why we use it. The Code is simply a non-obvious binary +// translation of the name into a form that can be validated. + +struct Code // code +{ + byte ab[4]; + byte bXor; + byte bSum; + byte abHash[2]; +}; + +// ESD keys gets generated via one way translation from the code +// Retail keys are self-validating and don't require a code + +struct Key // key +{ + byte ab[9]; +}; + +dword HashBytes(byte *pb, int cb) +{ + dword dwHash = 0; + while (cb-- != 0) + dwHash = (dwHash << 5) + dwHash + *pb++; + return dwHash; +} + +// Returns a pseudo-random number 0 through 32767. + +long glSeed; +int GetRandom() +{ + return ((glSeed = glSeed * 214013L + 2531011L) >> 16) & 0x7fff; +} + +void SetRandomSeed(unsigned long nSeed) +{ + glSeed = (long)nSeed; +} + +bool GetCode(char *szName, Code *pcode) +{ + if (szName == NULL || strlen(szName) == 0) + return false; + + SetRandomSeed((long)HashBytes((byte *)szName, (int)strlen(szName))); + + // Serialize in endian independent way + + Code code; + memset(&code, 0, sizeof(code)); + + int n; + for (n = 0; n < 4; n++) { + code.ab[n] = (byte)GetRandom(); + code.bXor ^= code.ab[n]; + code.bSum += code.ab[n]; + } + + // Hash what we have so far - more validation + + word wHash = (word)(HashBytes(code.ab, 6) >> 16); + code.abHash[0] = (byte)wHash; + code.abHash[1] = (byte)(wHash >> 8); + + // Now xor the first 6 with abHash[0] + + byte *pb = (byte *)&code; + for (n = 0; n < 6; n++) + *pb++ ^= code.abHash[0]; + + // Now xor the first 7 with abHash[1] + + pb = (byte *)&code; + for (n = 0; n < 7; n++) + *pb++ ^= code.abHash[1]; + + // Done + + *pcode = code; + return true; +} + +bool ValidateCode(Code *pcode) +{ + Code code = *pcode; + + // Now xor the first 7 with abHash[1] + + byte *pb = (byte *)&code; + int n; + for (n = 0; n < 7; n++) + *pb++ ^= code.abHash[1]; + + // Now xor the first 6 with abHash[0] + + pb = (byte *)&code; + for (n = 0; n < 6; n++) + *pb++ ^= code.abHash[0]; + + // Hash the first 6 and compare to our hash + + word wHash = (word)(HashBytes(code.ab, 6) >> 16); + if (code.abHash[0] != (byte)wHash) + return false; + if (code.abHash[1] != (byte)(wHash >> 8)) + return false; + + // Xor and add the first 4 + + byte bXor = 0; + byte bSum = 0; + for (n = 0; n < 4; n++) { + bXor ^= code.ab[n]; + bSum += code.ab[n]; + } + if (bXor != code.bXor) + return false; + if (bSum != code.bSum) + return false; + + return true; +} + +void GetKeyFromCode(Code *pcode, Key *pkey) +{ + byte *abCode = (byte *)pcode; + int cbCode = sizeof(*pcode); + + // Generate a seed + + long lSeed = 0x29a; + int n; + for (n = 0; n < cbCode; n++) + lSeed = lSeed * abCode[n] + n; + + // Initialize rotors + + byte abR1[256]; + byte abR2[256]; + byte abR3[256]; + + for (n = 0; n < 256; n++) + abR1[n] = n; + memset(abR2, 0, sizeof(abR2)); + memset(abR3, 0, sizeof(abR3)); + + for (n = 0; n < 256; n++) { + lSeed = 3 * lSeed + abCode[n & 7]; + long lRandom = lSeed % 65521; + int ibR1 = (lRandom & 255) % (256 - n); + lRandom >>= 8; + byte bTemp = abR1[255 - n]; + abR1[255 - n] = abR1[ibR1]; + abR1[ibR1] = bTemp; + if (abR3[255 - n] == 0) { + int nT = (lRandom & 255) % (255 - n); + while (abR3[nT] != 0) + nT = (nT + 1) % (255 - n); + abR3[255 - n] = nT & 255; + abR3[nT] = 255 - n; + } + } + for (n = 0; n < 256; n++) + abR2[abR1[n]] = n; + + // Encrypt the code into a key + + int nT = 0; + byte *abKey = (byte *)pkey; + int cbKey = sizeof(*pkey); + for (n = 0; n < cbKey; n++) { + int nSlotR1 = abR1[(n + nT) & 255]; + int nSlotR3 = abR3[nSlotR1 & 255]; + int nSlotR2 = abR2[nSlotR3 & 255]; + abKey[n] = abR2[nSlotR2 & 255] - nT; + nT = (nT + 1) & 255; + } +} + +void FillTemplate(char *psz, char *pszFormat, byte *pb) +{ + char *pchSrc = pszFormat; + char *pchDst = psz; + + bool fMSB = true; + byte bT = 0; + int cb = (int)strlen(pszFormat) + 1; + while (cb-- != 0) { + if (*pchSrc != '?') { + *pchDst++ = *pchSrc++; + continue; + } + + int nNibble = 0; + if (fMSB) { + fMSB = false; + bT = *pb++; + nNibble = (bT >> 4) & 0x0f; + } else { + fMSB = true; + nNibble = (bT & 0x0f); + } + + char chT; + if (nNibble < 0x0a) { + chT = '0' + nNibble; + } else { + chT = 'A' + nNibble - 0x0a; + } + + *pchDst++ = chT; + pchSrc++; + } +} + +bool ParseCode(char *psz, Code *pcode) +{ + int an[4]; + int c = sscanf(psz, "C-%04x-%04x-%04x-%04x", &an[0], &an[1], &an[2], &an[3]); + if (c != 4) + return false; + + + byte *pb = (byte *)pcode; + for (int i = 0; i < 4; i++) { + for (int j = 1; j >= 0; j--) { + *pb++ = (byte)(an[i] >> (j * 8)) & 0xff; + } + } + + return true; +} + +bool ParseKey(char *psz, Key *pkey) +{ + int an[3]; + int c = sscanf(psz, "K-%06x-%06x-%06x", &an[0], &an[1], &an[2]); + if (c != 3) + return false; + + byte *pb = (byte *)pkey; + for (int i = 0; i < 3; i++) { + for (int j = 2; j >= 0; j--) { + *pb++ = (byte)(an[i] >> (j * 8)) & 0xff; + } + } + + return true; +} + +void Usage() +{ + printf("\n"); + printf("Usage:\n"); + printf("\n"); + printf("drmutil key code\t\tGenerate a key given a code\n"); + printf("drmutil name \"name\"\t\tGenerate code and key given a name\n"); + printf("drmutil valid code\t\tCheck if a code is valid\n"); + printf("drmutil retailkey number\tGenerate retail key. Number range 101-16777215\n"); + printf("drmutil retailkeyvalid key\tCheck if a retail key is valid\n"); + printf("drmutil pc\t\t\tPrint codes for this desktop PC\n"); + printf("\n"); + printf("Code format:\tC-XXXX-XXXX-XXXX-XXXX\n"); + printf("Key format:\tK-XXXXXX-XXXXXX-XXXXXX\n"); + printf("'X' is a digit 0-9,A-F\n"); + exit(1); +} + +void OutputCodeAndKey(char *sz) +{ + // Gen / output code + + Code code; + if (!GetCode(sz, &code)) { + printf("Can't generate code; invalid name!\n"); + Usage(); + } + printf("Code and key for \"%s\":\n", sz); + FillTemplate(sz, "C-\?\?\?\?-\?\?\?\?-\?\?\?\?-\?\?\?\?", (byte *)&code); + printf("%s\n", sz); + + // Gen / output key + + Key key; + GetKeyFromCode(&code, &key); + FillTemplate(sz, "K-\?\?\?\?\?\?-\?\?\?\?\?\?-\?\?\?\?\?\?", (byte *)&key); + printf("%s\n", sz); +} + +void GetRetailKey(int nNumber, Key *pkey) +{ + SetRandomSeed(nNumber); + char szT[4]; + szT[0] = (byte)GetRandom(); + szT[1] = (byte)GetRandom(); + szT[2] = (byte)GetRandom(); + szT[3] = 0; + + // Ensure none of these have zeros in them + + for (int n = 0; n < sizeof(szT) - 1; n++) { + while (szT[n] == 0) + szT[n] = (byte)GetRandom(); + } + + // Get code and key + + Code code; + GetCode(szT, &code); + Key key; + GetKeyFromCode(&code, &key); + + // Now fill in the first 3 bytes with the encoded "name" + + key.ab[0] = szT[0]; + SetRandomSeed((dword)szT[0]); + key.ab[1] = szT[1] ^ (byte)GetRandom(); + key.ab[2] = szT[2] ^ (byte)GetRandom(); + *pkey = key; +} + +void OutputRetailKey(char *sz) +{ + // Number can be between 101 and 2^24-1 (3 bytes) + + int nNumber = atoi(sz); + if (nNumber < 101 || nNumber > 16777215) { + printf("Seed number is invalid\n"); + Usage(); + } + + // Generate retail key + + Key key; + GetRetailKey(nNumber, &key); + + // Output + + printf("Seed: %d\n", nNumber); + char szKey[64]; + FillTemplate(szKey, "K-\?\?\?\?\?\?-\?\?\?\?\?\?-\?\?\?\?\?\?", (byte *)&key); + printf("%s\n", szKey); +} + +bool CompareKeys(Key *pkeyA, Key *pkeyB) +{ + byte *pbA = (byte *)pkeyA; + byte *pbB = (byte *)pkeyB; + int cb = sizeof(*pkeyA); + while (cb-- != 0) { + if (*pbA++ != *pbB++) + return false; + } + return true; +} + +bool ValidateRetailKey(Key *pkey) +{ + char szT[4]; + + // Derive the "name" from the key + + szT[0] = pkey->ab[0]; + SetRandomSeed((dword)szT[0]); + szT[1] = pkey->ab[1] ^ (byte)GetRandom(); + szT[2] = pkey->ab[2] ^ (byte)GetRandom(); + szT[3] = 0; + + // Fill in zeros + + int n; + for (n = 0; n < sizeof(szT) - 1; n++) { + while (szT[n] == 0) + szT[n] = (byte)GetRandom(); + } + + // Get code + + Code code; + GetCode(szT, &code); + + // Gen full key from this + + Key keyT; + GetKeyFromCode(&code, &keyT); + + // Compare keys - compares the last 6 bytes because we copy over the first 3 + + Key keyReconstructed = *pkey; + for (n = 0; n < sizeof(szT) - 1; n++) + keyReconstructed.ab[n] = keyT.ab[n]; + + // Now compare these two keys + + return CompareKeys(&keyT, &keyReconstructed); +} + +int main(int argc, char **argv) +{ + char *pszCommand = argv[1]; + char *pszArg = argv[2]; + + if (argc == 2) { + if (strcmp(pszCommand, "pc") == 0) { + char sz[64]; + sz[0] = 0; + + // Get host name + + WSADATA wsad; + WSAStartup(MAKEWORD(1, 1), &wsad); + gethostname(sz, sizeof(sz) - 1); + WSACleanup(); + + OutputCodeAndKey(sz); + return 0; + } + Usage(); + } + + if (argc == 3) { + if (strcmp(pszCommand, "key") == 0) { + Code code; + if (!ParseCode(pszArg, &code)) { + printf("Code entered in incorrect format!\n"); + Usage(); + } + if (!ValidateCode(&code)) { + printf("This code is invalid. It fails internal consistency check\n"); + Usage(); + } + Key key; + GetKeyFromCode(&code, &key); + char szKey[64]; + FillTemplate(szKey, "K-\?\?\?\?\?\?-\?\?\?\?\?\?-\?\?\?\?\?\?", (byte *)&key); + printf("%s\n", szKey); + return 0; + } + + if (strcmp(pszCommand, "name") == 0) { + OutputCodeAndKey(pszArg); + return 0; + } + + if (strcmp(pszCommand, "valid") == 0) { + Code code; + if (!ParseCode(pszArg, &code)) { + printf("Code entered in incorrect format!\n"); + Usage(); + } + if (!ValidateCode(&code)) { + printf("Invalid code\n"); + return 1; + } + + printf("Valid code\n"); + return 0; + } + + if (strcmp(pszCommand, "retailkey") == 0) { + OutputRetailKey(pszArg); + return 0; + } + + if (strcmp(pszCommand, "retailkeyvalid") == 0) { + Key key; + if (!ParseKey(pszArg, &key)) { + printf("Key entered in incorrect format!\n"); + Usage(); + } + if (!ValidateRetailKey(&key)) { + printf("Retail key invalid.\n"); + } else { + printf("Retail key valid.\n"); + } + return 0; + } + } + + Usage(); +} + diff --git a/game/.cvsignore b/game/.cvsignore new file mode 100644 index 0000000..cacbcde --- /dev/null +++ b/game/.cvsignore @@ -0,0 +1,21 @@ +ARMDbg +ARMRel +bug.bat +ce.vcb +ce.vcl +ce.vco +ce.vcw +gdbcmds.txt +*.rec +*.bin +ht.dsw +ht.map +ht.vcb +ht.vco +ht.vcw +Palm_Debug +Palm_Release +Win_Debug +Win_Release +X86Dbg +X86Rel diff --git a/game/.gitignore b/game/.gitignore new file mode 100644 index 0000000..67037ca --- /dev/null +++ b/game/.gitignore @@ -0,0 +1 @@ +htdata824.pdb \ No newline at end of file diff --git a/game/Andy.cpp b/game/Andy.cpp new file mode 100644 index 0000000..eb0d059 --- /dev/null +++ b/game/Andy.cpp @@ -0,0 +1,277 @@ +#include "ht.h" + +namespace wi { + +static MobileUnitConsts gmuntcAndy; + +#if defined(DEBUG_HELPERS) +char *AndyGob::GetName() +{ + return "Andy"; +} +#endif + +static int s_anFiringStripIndices[8] = { 1, 2, 3, 4, 5, 6, 7, 8 }; +static int s_anMovingStripIndices[8] = { 17, 18, 19, 20, 21, 22, 23, 24 }; +static int s_anIdleStripIndices[8] = { 9, 10, 11, 12, 13, 14, 15, 16 }; + +bool AndyGob::InitClass(IniReader *pini) +{ + gmuntcAndy.gt = kgtAndy; + gmuntcAndy.ut = kutAndy; + gmuntcAndy.wf |= kfUntcNotifyEnemyNearby | kfUntcLargeDefog; + + // Initialize the frame indices arrays + + gmuntcAndy.anFiringStripIndices = s_anFiringStripIndices; + gmuntcAndy.anMovingStripIndices = s_anMovingStripIndices; + gmuntcAndy.anIdleStripIndices = s_anIdleStripIndices; + + // Sound effects + + gmuntcAndy.sfxFire = ksfxAndyFire; + gmuntcAndy.sfxImpact = ksfxNothing; + gmuntcAndy.sfxcDestroyed = ksfxcAndyDestroyed; + gmuntcAndy.sfxcSelect = ksfxcAndySelect; + gmuntcAndy.sfxcMove = ksfxcAndyMove; + gmuntcAndy.sfxcAttack = ksfxcAndyAttack; + + return MobileUnitGob::InitClass(&gmuntcAndy, pini); +} + +void AndyGob::ExitClass() +{ + MobileUnitGob::ExitClass(&gmuntcAndy); +} + +AndyGob::AndyGob() : MobileUnitGob(&gmuntcAndy) +{ +} + +// For the benefit of Fox + +AndyGob::AndyGob(MobileUnitConsts *pmuntc) : MobileUnitGob(pmuntc) +{ +} + +bool AndyGob::Init(WCoord wx, WCoord wy, Player *pplr, fix fxHealth, dword ff, const char *pszName) +{ + bool f = MobileUnitGob::Init(wx, wy, pplr, fxHealth, ff, pszName); + + // Start the healing process + + gsmm.SendDelayedMsg(kmidHeal, 100, m_gid, m_gid); // 1 second + + return f; +} + +bool AndyGob::Fire(UnitGob *puntTarget, WCoord wx, WCoord wy, WCoord wdx, WCoord wdy) +{ + // MobileUnitGob handles rotating towards the target, firing delay, + // and starting the fire animation + + if (!MobileUnitGob::Fire(puntTarget, wx, wy, wdx, wdy)) + return false; + + // Fire off the shot! + + WCoord wdxRnd = ((GetRandom() & 7) - 3) * kwcTile16th; + WCoord wdyRnd = ((GetRandom() & 7) - 3) * kwcTile16th; + Point ptSpecial; + m_ani.GetSpecialPoint(&ptSpecial, 1); // frame #1 (second frame) has the firing point + + CreateAndyShotGob(m_wx + WcFromPc(ptSpecial.x), m_wy + WcFromPc(ptSpecial.y), wx + wdxRnd, wy + wdyRnd, + GetDamageTo(puntTarget), m_gid, puntTarget->GetId()); + + // Play sound + + gsndm.PlaySfx(m_pmuntc->sfxFire); + + return true; +} + +void AndyGob::Idle() +{ + // 1/4 of the time we pivot left, 1/4 we pivot right, and 1/2 we play the idle + + switch (GetRandom() & 3) { + case 0: + m_dir--; + if (m_dir < 0) + m_dir = 7; + StartAnimation(&m_ani, m_pmuntc->anIdleStripIndices[m_dir], 0, kfAniResetWhenDone); + break; + + case 1: + m_dir++; + if (m_dir > 7) + m_dir = 0; + StartAnimation(&m_ani, m_pmuntc->anIdleStripIndices[m_dir], 0, kfAniResetWhenDone); + break; + + default: + StartAnimation(&m_ani, m_pmuntc->anIdleStripIndices[m_dir], 0, kfAniResetWhenDone); + break; + } +} + +int AndyGob::ProcessStateMachineMessage(State st, Message *pmsg) +{ +BeginStateMachine + + // Andy recovers full health in 1 minute + + OnMsg(kmidHeal) + if (m_ff & kfGobActive) + gsmm.SendDelayedMsg(kmidHeal, 100, m_gid, m_gid); // 1 second + + fix fxArmorStrength = m_pmuntc->GetArmorStrength(); + if (m_fxHealth < fxArmorStrength) { + // Add 1/60th of total health + fix fxHealth = addfx(m_fxHealth, divfx(fxArmorStrength, itofx(60))); + if (fxHealth > fxArmorStrength) + fxHealth = fxArmorStrength; + SetHealth(fxHealth); + } + + State(kstDying) + OnEnter + TRect trc; + GetTileRect(&trc); + + Deactivate(); + + // Redraw this part of minimap. It will skip inactive munts + + gpmm->RedrawTRect(&trc); + + m_ff ^= kfGobLayerDepthSorted | kfGobLayerSurfaceDecal; + + gsndm.PlaySfx(SfxFromCategory(m_pmuntc->sfxcDestroyed)); + m_ani.Start("die 3", kfAniIgnoreFirstAdvance); + MarkRedraw(); + + // Remove corpse after 10 seconds + + gsmm.SendDelayedMsg(kmidDelete, 1000, m_gid, m_gid); + + OnUpdate + AdvanceAnimation(&m_ani); + +#if 0 +EndStateMachineInherit(MobileUnitGob) +#else + return knHandled; + } + } else { + return (int)MobileUnitGob::ProcessStateMachineMessage(st, pmsg); + } + + return (int)MobileUnitGob::ProcessStateMachineMessage(st, pmsg); +#endif +} + + +// +// Fox is a clone of Andy with tweaked GobTemplates.ini parameters +// + +static MobileUnitConsts gmuntcFox; + +#if defined(DEBUG_HELPERS) +char *FoxGob::GetName() +{ + return "Fox"; +} +#endif + +FoxGob::FoxGob() : AndyGob(&gmuntcFox) +{ +} + +bool FoxGob::InitClass(IniReader *pini) +{ + gmuntcFox.gt = kgtFox; + gmuntcFox.ut = kutFox; + gmuntcFox.wf |= kfUntcNotifyEnemyNearby | kfUntcLargeDefog; + + // Initialize the frame indices arrays + + gmuntcFox.anFiringStripIndices = s_anFiringStripIndices; + gmuntcFox.anMovingStripIndices = s_anMovingStripIndices; + gmuntcFox.anIdleStripIndices = s_anIdleStripIndices; + + // Sound effects + + + //gmuntcFox.sfxFire = ksfxAndyFire; he never shoots + + gmuntcFox.sfxFire = ksfxLightTankFire; + gmuntcFox.sfxImpact = ksfxNothing; + + gmuntcFox.sfxcDestroyed = ksfxcFoxDestroyed; + gmuntcFox.sfxcSelect = ksfxcMale03Select; + gmuntcFox.sfxcMove = ksfxcMale03Move; + gmuntcFox.sfxcAttack = ksfxcMale03Attack; + + return MobileUnitGob::InitClass(&gmuntcFox, pini); +} + +void FoxGob::ExitClass() +{ + MobileUnitGob::ExitClass(&gmuntcFox); +} + +// +// AndyShotGob implementation +// + +AnimationData *AndyShotGob::s_panidShot = NULL; + +// this will actually create & fire +AndyShotGob *CreateAndyShotGob(WCoord wx, WCoord wy, WCoord wxTarget, WCoord wyTarget, int nDamage, Gid gidOwner, Gid gidTarget, bool fEagle) +{ + if (!ggobm.IsBelowLimit(knLimitSupport)) + return NULL; + + AndyShotGob *pgob = new AndyShotGob(); + Assert(pgob != NULL, "out of memory!"); + if (pgob == NULL) + return NULL; + + if (!pgob->Init(AndyShotGob::s_panidShot, wx, wy, wxTarget, wyTarget, nDamage, gidOwner, gidTarget, kwcTile)) { + delete pgob; + return NULL; + } + + pgob->Launch(); + return pgob; +} + +bool AndyShotGob::InitClass(IniReader *pini) +{ + s_panidShot = LoadAnimationData("andyshot.anir"); + if (s_panidShot == NULL) + return false; + return true; +} + +void AndyShotGob::ExitClass() +{ + delete s_panidShot; + s_panidShot = NULL; +} + +GobType AndyShotGob::GetType() +{ + return kgtAndyShot; +} + +#if defined(DEBUG_HELPERS) +char *AndyShotGob::GetName() +{ + return "AndyShot"; +} +#endif + +} // namespace wi \ No newline at end of file diff --git a/game/Animation.cpp b/game/Animation.cpp new file mode 100644 index 0000000..230a528 --- /dev/null +++ b/game/Animation.cpp @@ -0,0 +1,335 @@ +#include "ht.h" + +namespace wi { + +AnimationData *LoadAnimationData(const char *pszAniName) +{ + AnimationData *panid = new AnimationData(); + Assert(panid != NULL, "out of memory!"); + if (panid == NULL) + return NULL; + + if (!panid->Init(pszAniName)) { + delete panid; + return NULL; + } + + return panid; +} + +AnimationData::AnimationData() +{ + m_panih = NULL; + m_ptbm = NULL; +} + +AnimationData::~AnimationData() +{ + if (m_panih != NULL) + gpakr.UnmapFile(&m_fmap); + delete m_ptbm; +} + +bool AnimationData::Init(const char *pszAniName) +{ + m_panih = (AnimationFileHeader *)gpakr.MapFile((char *)pszAniName, &m_fmap); + if (m_panih == NULL) + return false; + + // UNDONE: incorporate the bitmaps directly inside the animation file? + + char szTbmName[kcbFilename]; + int cch = strlen(pszAniName); + + // Animation files must end with ".anir" for this to work + + Assert((pszAniName[cch - 4] | 0x20) == 'a' && (pszAniName[cch - 3] | 0x20) == 'n' && + (pszAniName[cch - 2] | 0x20) == 'i' && (pszAniName[cch - 1] | 0x20) == 'r', + "Animation files must end with extension \".anir\""); + strncpy(szTbmName, pszAniName, cch - 4); + szTbmName[cch - 4] = 0; + strcat(szTbmName, "tbm"); + + m_ptbm = LoadTBitmap(szTbmName); + if (m_ptbm == NULL) { + Assert("unable to load TBitmap %s", szTbmName); + return false; + } + + return true; +} + +// OPT: dynamically build an index of pointers to each StripData, FrameData if perf critical + +#define GetStripDataPtr(nStrip) ((StripData *)(((byte *)m_panih) + BigWord(m_panih->aoffStpd[nStrip]))) +#define GetFrameDataPtr(nStrip, nFrame) (GetStripDataPtr(nStrip)->GetFrameData(nFrame)) + +int AnimationData::GetStripCount() +{ + return BigWord(m_panih->cstpd); +} + +int AnimationData::GetFrameCount(int nStrip) +{ + Assert(nStrip >= 0 && nStrip < GetStripCount()); + + return GetStripDataPtr(nStrip)->GetFrameCount(); +} + +void AnimationData::GetFrameOrigin(int nStrip, int nFrame, Point *pptOrigin) +{ + Assert(nStrip >= 0 && nStrip < GetStripCount()); + Assert(nFrame >= 0 && nFrame < GetStripDataPtr(nStrip)->GetFrameCount()); + + FrameData *pfrmd = GetFrameDataPtr(nStrip, nFrame); + pptOrigin->x = pfrmd->xOrigin; + pptOrigin->y = pfrmd->yOrigin; +} + +void AnimationData::GetSpecialPoint(int nStrip, int nFrame, Point *pptSpecial) +{ + Assert(nStrip >= 0 && nStrip < GetStripCount()); + Assert(nFrame >= 0 && nFrame < GetStripDataPtr(nStrip)->GetFrameCount()); + + FrameData *pfrmd = GetFrameDataPtr(nStrip, nFrame); + pptSpecial->x = (char)pfrmd->bCustomData1; + pptSpecial->y = (char)pfrmd->bCustomData2; +} + +int AnimationData::GetFrameDelay(int nStrip, int nFrame) +{ + Assert(nStrip >= 0 && nStrip < GetStripCount()); + Assert(nFrame >= 0 && nFrame < GetStripDataPtr(nStrip)->GetFrameCount()); + + FrameData *pfrmd = GetFrameDataPtr(nStrip, nFrame); + return pfrmd->cHold; +} + +void AnimationData::GetBounds(int nStrip, int nFrame, Rect *prc) +{ + Assert(nStrip >= 0 && nStrip < GetStripCount()); + Assert(nFrame >= 0 && nFrame < GetStripDataPtr(nStrip)->GetFrameCount()); + + FrameData *pfrmd = GetFrameDataPtr(nStrip, nFrame); + Size siz; + + // If there is no first bitmap (e.g., delay-only frame) set the bounds + // to empty. + + if (pfrmd->ibm == 255) { + prc->SetEmpty(); + } else { + m_ptbm->GetSize(pfrmd->ibm, &siz); + prc->left = -pfrmd->xOrigin; + prc->right = prc->left + siz.cx; + prc->top = -pfrmd->yOrigin; + prc->bottom = prc->top + siz.cy; + } + + // If there is a second bitmap return the union of its bounds and + // the bounds of the first bitmap. + + if (pfrmd->ibm2 != 255) { + m_ptbm->GetSize(pfrmd->ibm2, &siz); + int xL = -pfrmd->xOrigin2; + if (prc->left > xL) + prc->left = xL; + int yT = -pfrmd->yOrigin2; + if (prc->top > yT) + prc->top = yT; + int xR = xL + siz.cx; + if (prc->right < xR) + prc->right = xR; + int yB = yT + siz.cy; + if (prc->bottom < yB) + prc->bottom = yB; + } +} + +void AnimationData::DrawFrame(int nStrip, int nFrame, DibBitmap *pbm, int x, int y, Side side, Rect *prcSrc) +{ + Assert(nStrip >= 0 && nStrip < GetStripCount()); + Assert(nFrame >= 0 && nFrame < GetStripDataPtr(nStrip)->GetFrameCount()); + + FrameData *pfrmd = GetFrameDataPtr(nStrip, nFrame); + if (pfrmd->ibm2 != 255) + m_ptbm->BltTo(pfrmd->ibm2, pbm, x - pfrmd->xOrigin2, y - pfrmd->yOrigin2, side, prcSrc); + if (pfrmd->ibm != 255) + m_ptbm->BltTo(pfrmd->ibm, pbm, x - pfrmd->xOrigin, y - pfrmd->yOrigin, side, prcSrc); +} + +int AnimationData::GetStripIndex(const char *pszStripName) +{ + // OPT: Can switch to a binary search if needed + word *poffStpd = m_panih->aoffStpd; + int cstpd = GetStripCount(); + for (int i = 0; i < cstpd; i++, poffStpd++) { + StripData *pstpd = (StripData *)(((byte *)m_panih) + BigWord(*poffStpd)); + if (stricmp(pszStripName, pstpd->GetName()) == 0) + return i; + } + + Assert("Strip not found"); + return -1; +} + +int AnimationData::GetStripDelay(int nStrip) +{ + Assert(nStrip >= 0 && nStrip < GetStripCount()); + + return GetStripDataPtr(nStrip)->GetDefaultDelay(); +} + +// +// Animation class implementation +// + +void Animation::Init(AnimationData *panid) +{ + m_panid = panid; + m_nStrip = 0; + m_nFrame = 0; + m_cDelay = m_panid->GetStripDelay(m_nStrip); +} + +#define knVerAnimationState 1 +bool Animation::LoadState(Stream *pstm) +{ + byte nVer = pstm->ReadByte(); + if (nVer != knVerAnimationState) + return false; + m_nStrip = pstm->ReadByte(); + m_nFrame = pstm->ReadByte(); + + m_cDelay = pstm->ReadByte(); + m_cCountdown = pstm->ReadByte(); + m_wf = pstm->ReadWord(); + + // This hack deals with the fact that different resolution versions + // of the same animation may have different numbers of frames. When an + // animation is saved it may include a frame index that exceeds what + // is allowed when it is reloaded at a different resolution. In this + // case we force the frame within the range and presume that nobody + // will notice. + + int cfrm = m_panid->GetFrameCount(m_nStrip); + if (m_nFrame >= cfrm) { + m_nFrame = cfrm - 1; + m_cCountdown = m_cDelay + m_panid->GetFrameDelay(m_nStrip, m_nFrame); + } + + return pstm->IsSuccess(); +} + +bool Animation::SaveState(Stream *pstm) +{ + pstm->WriteByte(knVerAnimationState); + pstm->WriteByte(m_nStrip); + pstm->WriteByte(m_nFrame); + pstm->WriteByte(m_cDelay); + pstm->WriteByte(m_cCountdown); + pstm->WriteWord(m_wf); + return pstm->IsSuccess(); +} + +void Animation::SetStrip(const char *pszStripName) +{ + m_nStrip = m_panid->GetStripIndex(pszStripName); + m_cCountdown = m_panid->GetStripDelay(m_nStrip) + m_panid->GetFrameDelay(m_nStrip, 0); +} + +bool Animation::Start(const char *pszStripName, word wf) +{ + return Start(m_panid->GetStripIndex(pszStripName), 0, wf); +} + +bool Animation::Start(int nStrip, int nFrame, word wf) +{ + Assert(nStrip >= 0 && nStrip < m_panid->GetStripCount()); + + m_nStrip = nStrip; + if (nFrame == -1) + m_nFrame = m_panid->GetFrameCount(m_nStrip) - 1; + else + m_nFrame = nFrame; + + Assert(m_nFrame >= 0 && m_nFrame < m_panid->GetFrameCount(m_nStrip)); + + m_wf = wf | (m_wf & kfAniFreeAnimationData); + m_cCountdown = m_panid->GetStripDelay(m_nStrip) + m_panid->GetFrameDelay(m_nStrip, m_nFrame); + + // Don't actually advance the animation the first time called. + // This may seem strange but it ensures that the first frame will + // be displayed for at least one Update before moving on. + + if (m_wf & kfAniIgnoreFirstAdvance) { + if (m_cCountdown < 255) + m_cCountdown++; + } + + // If this animation only has one frame, remove the loop bit. + // This will take much less cpu since it won't be calling ::Advance() much. + + if ((m_wf & kfAniLoop) && m_panid->GetFrameCount(m_nStrip) == 1) + m_wf &= ~kfAniLoop; + + return (m_wf & kfAniDone) == 0; +} + + +// update m_nFrame as appropriate given delays that may be in the animation and +// deal with end conditions +bool Animation::Advance(int cAdvance) +{ + if (m_wf & kfAniDone) + return false; + + if (m_cCountdown >= cAdvance) { + m_cCountdown -= cAdvance; + return true; + } + + if (m_nFrame + 1 < m_panid->GetFrameCount(m_nStrip)) { + m_nFrame++; + } else { + if (m_wf & kfAniLoop) { + m_nFrame = 0; + } else { + m_wf |= kfAniDone; + if (m_wf & kfAniResetWhenDone) { + m_nFrame = 0; + m_cCountdown = 255; + return true; + } + return false; + } + } + + m_cCountdown = m_cDelay + m_panid->GetFrameDelay(m_nStrip, m_nFrame); + return true; +} + +long Animation::GetRemainingStripTime() +{ + // NOTE: doesn't take update interval into account. We can add it if needed. + + int c = m_cCountdown; + int cfrm = m_panid->GetFrameCount(m_nStrip); + for (int ifrm = m_nFrame + 1; ifrm < cfrm; ifrm++) + c += 1 + m_cDelay + m_panid->GetFrameDelay(m_nStrip, ifrm); + + return c * kctUpdate; +} + +#ifdef MP_DEBUG_SHAREDMEM +void Animation::MPValidate(Animation *paniRemote) +{ + MPValidateMember(Animation, m_nStrip, paniRemote); + MPValidateMember(Animation, m_nFrame, paniRemote); + MPValidateMember(Animation, m_wf, paniRemote); + MPValidateMember(Animation, m_cCountdown, paniRemote); + MPValidateMember(Animation, m_cDelay, paniRemote); +} +#endif + +} // namespace wi \ No newline at end of file diff --git a/game/Artillery.cpp b/game/Artillery.cpp new file mode 100644 index 0000000..f8de297 --- /dev/null +++ b/game/Artillery.cpp @@ -0,0 +1,140 @@ +#include "ht.h" + +namespace wi { + +static MobileUnitConsts gConsts; + +#if defined(DEBUG_HELPERS) +char *ArtilleryGob::GetName() +{ + return "Artillery"; +} +#endif + +static int s_anFiringStripIndices[8] = { 0, 1, 2, 3, 4, 5, 6, 7 }; +static int s_anMovingStripIndices[8] = { 8, 9, 10, 11, 12, 13, 14, 15 }; + +bool ArtilleryGob::InitClass(IniReader *pini) +{ + gConsts.gt = kgtArtillery; + gConsts.ut = kutArtillery; + gConsts.upgmPrerequisites = kupgmAdvancedVTS; + gConsts.wf |= kfUntcNotifyEnemyNearby | kfUntcLargeDefog; + + // Initialize the frame indices arrays + + gConsts.anFiringStripIndices = s_anFiringStripIndices; + gConsts.anMovingStripIndices = s_anMovingStripIndices; + gConsts.anIdleStripIndices = s_anMovingStripIndices; + + // Sound effects + + // + + gConsts.sfxFire = ksfxArtilleryFire; + gConsts.sfxImpact = ksfxArtilleryImpact; + gConsts.sfxcDestroyed = ksfxcVehicleDestroyed; + gConsts.sfxcSelect = ksfxcMajor01Select; + gConsts.sfxcMove = ksfxcMajor01Move; + gConsts.sfxcAttack = ksfxcMajor01Attack; + + return MobileUnitGob::InitClass(&gConsts, pini); +} + +void ArtilleryGob::ExitClass() +{ + MobileUnitGob::ExitClass(&gConsts); +} + +ArtilleryGob::ArtilleryGob() : MobileUnitGob(&gConsts) +{ +} + +bool ArtilleryGob::Fire(UnitGob *puntTarget, WCoord wx, WCoord wy, WCoord wdx, WCoord wdy) +{ + // UNDONE: artillery should rotate slower than most units + // MobileUnitGob handles rotating towards the target, firing delay, + // and starting the fire animation + + if (!MobileUnitGob::Fire(puntTarget, wx, wy, wdx, wdy)) + return false; + + // Fire off the shot! + + WCoord wdxRnd = ((GetRandom() & 7) - 3) * kwcTile16th; + WCoord wdyRnd = ((GetRandom() & 7) - 3) * kwcTile16th; + Point ptSpecial; + m_ani.GetSpecialPoint(&ptSpecial, 1); // frame #1 (second frame) has the firing point + + CreateArtilleryShotGob(m_wx + WcFromPc(ptSpecial.x), m_wy + WcFromPc(ptSpecial.y), wx + wdxRnd, wy + wdyRnd, + GetDamageTo(puntTarget), m_gid, puntTarget->GetId()); + + // Play sound + + gsndm.PlaySfx(m_pmuntc->sfxFire); + + return true; +} + +#if 0 +int ArtilleryGob::ProcessStateMachineMessage(State st, Message *pmsg) +{ +BeginStateMachine + +EndStateMachineInherit(MobileUnitGob) +} +#endif + +// +// ArtilleryShotGob implementation +// + +AnimationData *ArtilleryShotGob::s_panidShot = NULL; + +// this will actually create & fire +ArtilleryShotGob *CreateArtilleryShotGob(WCoord wx, WCoord wy, WCoord wxTarget, WCoord wyTarget, int nDamage, Gid gidOwner, Gid gidTarget) +{ + if (!ggobm.IsBelowLimit(knLimitSupport)) + return NULL; + + ArtilleryShotGob *pgob = new ArtilleryShotGob(); + Assert(pgob != NULL, "out of memory!"); + if (pgob == NULL) + return NULL; + + if (!pgob->Init(ArtilleryShotGob::s_panidShot, wx, wy, wxTarget, wyTarget, nDamage, gidOwner, gidTarget, kwcTile / 2)) { + delete pgob; + return NULL; + } + + pgob->Launch(); + return pgob; +} + +bool ArtilleryShotGob::InitClass(IniReader *pini) +{ + s_panidShot = LoadAnimationData("artilleryshot.anir"); + if (s_panidShot == NULL) + return false; + return true; +} + +void ArtilleryShotGob::ExitClass() +{ + delete s_panidShot; + s_panidShot = NULL; +} + +GobType ArtilleryShotGob::GetType() +{ + return kgtArtilleryShot; +} + +#if defined(DEBUG_HELPERS) +char *ArtilleryShotGob::GetName() +{ + return "ArtilleryShot"; +} +#endif + +} // namespace wi \ No newline at end of file diff --git a/game/BuildMgr.cpp b/game/BuildMgr.cpp new file mode 100644 index 0000000..ba03087 --- /dev/null +++ b/game/BuildMgr.cpp @@ -0,0 +1,204 @@ +#include "ht.h" + +namespace wi { + +// UNDONE: load/save + +BuildMgr::BuildMgr() +{ + m_pbldeFirst = NULL; +} + +BuildMgr::~BuildMgr() +{ + BuildEntry *pblde = m_pbldeFirst; + while (pblde != NULL) { + BuildEntry *pbldeT = pblde; + pblde = pblde->pbldeNext; + delete pbldeT; + } +} + +// Add the requested UnitType to the build list. The specified UnitGroup +// will be notified when the Unit is built. + +bool BuildMgr::BuildUnit(UnitType ut, UnitGroup *pug, int nArea) +{ + BuildEntry *pblde = new BuildEntry; + Assert(pblde != NULL, "out of memory!"); + if (pblde == NULL) + return false; + + pblde->ut = ut; + pblde->pug = pug; + pblde->nArea = nArea; + pblde->gidBuilder = kgidNull; + + // Miners have special priority and jump to the head of list. + // This is to reduce the likelihood that level AI will run out + // of money because the player destroyed its Miners + + if (pblde->ut == kutGalaxMiner) { + pblde->pbldeNext = m_pbldeFirst; + m_pbldeFirst = pblde; + return true; + } + + // Link the new BuildEntry to the end of the build list so it will be built + // chronologically after previous requests. (Not strictly though, because it + // may be possible to start building this Unit sooner than a Unit earlier on + // the list because that Unit relies on a Builder type that isn't available). + + pblde->pbldeNext = NULL; + BuildEntry **ppblde = &m_pbldeFirst; + while (*ppblde != NULL) + ppblde = &((*ppblde)->pbldeNext); + *ppblde = pblde; + + return true; +} + +// BuilderGobs call this method when they have completed building a Unit + +void BuildMgr::OnBuilt(UnitGob *punt, BuilderGob *pbldr) +{ + UnitType utBuilt = punt->GetConsts()->ut; + Gid gidBuilder = pbldr->GetId(); + Player *pplrBuilt = punt->GetOwner(); + + BuildEntry **ppblde = &m_pbldeFirst; + while (*ppblde != NULL) { + BuildEntry *pblde = *ppblde; + + // Match the built Unit with the right BuildEntry (must be of correct + // UnitType, produced by the expected BuilderGob which must be owned + // by the UnitGroup's owner) + + if (utBuilt == pblde->ut && gidBuilder == pblde->gidBuilder && pplrBuilt == pblde->pug->GetOwner()) { + + // Unlink BuildEntry + + *ppblde = pblde->pbldeNext; + + // Yay, it's been built! + + pblde->pug->OnBuilt(punt); + + delete pblde; + return; + } + + ppblde = &((*ppblde)->pbldeNext); + } +} + +// OPT: Doesn't need to be called every Simulation::Update + +void BuildMgr::Update() +{ + if (m_pbldeFirst == NULL) + return; + + for (BuildEntry *pblde = m_pbldeFirst; pblde != NULL; pblde = pblde->pbldeNext) { + + // if gidBuilder is invalid/inactive or is no longer owned by the same side + // clear gidBuilder + + BuilderGob *pbldr = (BuilderGob *)ggobm.GetGob(pblde->gidBuilder); + if (pbldr == NULL || pbldr->GetOwner() != pblde->pug->GetOwner()) + pblde->gidBuilder = kgidNull; + } + + // Check each BuilderGob to see if it can build one of the BuildEntries + // (it must be idle, owned by the same side, and be the right type of builder) + + // Scan the Gob list looking for BuilderGobs + + Gob *pgob = ggobm.GetFirstGob(); + for (; pgob != NULL; pgob = ggobm.GetNextGob(pgob)) { + + // Is this a Structure? + + if (!(pgob->GetFlags() & kfGobStructure)) + continue; // no + + StructGob *pstru = (StructGob *)pgob; + StructConsts *pstruc = (StructConsts *)pstru->GetConsts(); + + // Is it a Builder? + + if (!(pstruc->um & kumBuilder)) + continue; // no + + // Is this builder busy? + + BuilderGob *pbldr = (BuilderGob *)pstru; + if (pbldr->IsBuildInProgress()) + continue; // yes + + // Let's see if it can build any of the UnitTypes we have pending + + Player *pplrBuilderOwner = pbldr->GetOwner(); + BuilderConsts *pbldrc = (BuilderConsts *)pbldr->GetConsts(); + + for (BuildEntry *pblde = m_pbldeFirst; pblde != NULL; pblde = pblde->pbldeNext) { + + // Is this entry already being built? + + if (pblde->gidBuilder != kgidNull) + continue; // yes + + // Does the Player wanting to build own the builder? + + if (pblde->pug->GetOwner() != pplrBuilderOwner) + continue; // no + + // Can this builder build the type we want? + + UnitMask um = (1UL << pblde->ut); + if (pbldrc->umCanBuild & um) { + // Yes! + + if (um & kumStructures) { + // Check to see if limits have been reached + + if (!ggobm.IsBelowLimit(knLimitStruct, pplrBuilderOwner)) + continue; // limit reached + + // Ok + + Message msg; + memset(&msg, 0, sizeof(msg)); + msg.mid = kmidBuildOtherCommand; + msg.smidSender = ksmidNull; + msg.smidReceiver = pbldr->GetId(); + msg.BuildOtherCommand.ut = pblde->ut; + TRect trc; + ggobm.GetAreaRect(pblde->nArea, &trc); + + // Is space required by the structure free? + + StructConsts *pstruc = (StructConsts *)gapuntc[pblde->ut]; + if (gsim.GetLevel()->GetTerrainMap()->IsOccupied(trc.left, trc.top, pstruc->ctxReserve, pstruc->ctyReserve, kbfStructure | kbfMobileUnit)) + continue; // no + + msg.BuildOtherCommand.wpt.wx = WcFromTc(trc.left); + msg.BuildOtherCommand.wpt.wy = WcFromTc(trc.top); + gsmm.SendMsg(&msg); + + } else { + // Check to see if limits have been reached + + if (!ggobm.IsBelowLimit(knLimitMobileUnit, pplrBuilderOwner)) + continue; // limit reached + + pbldr->Build(pblde->ut); + } + pblde->gidBuilder = pbldr->GetId(); + break; // Move on to the next BuilderGob for any remaining BuildEntries + } + } + } +} + +} // namespace wi \ No newline at end of file diff --git a/game/Builder.cpp b/game/Builder.cpp new file mode 100644 index 0000000..93c7e3a --- /dev/null +++ b/game/Builder.cpp @@ -0,0 +1,1199 @@ +#include "ht.h" + +namespace wi { + +const short kcyBuildProgress = 3; + +//=========================================================================== +// BuilderGob implementation +// + +bool BuilderGob::InitClass(BuilderConsts *pbldrc, IniReader *pini) +{ + if (!StructGob::InitClass(pbldrc, pini)) + return false; + + char szTemplate[10]; + itoa(pbldrc->gt, szTemplate, 10); + + // Required properties + + if (pini->GetPropertyValue(szTemplate, "BuildRate", "%d", &pbldrc->nBuildRate) != 1) + return false; + + return true; +} + +void BuilderGob::ExitClass(BuilderConsts *pbldrc) +{ + StructGob::ExitClass(pbldrc); +} + +BuilderGob::BuilderGob(BuilderConsts *pbldrc) : StructGob(pbldrc) +{ + m_gidBuildVisible = kgidNull; + m_fxHealthBuilding = itofx(0); + m_nCreditsSpentOnBuilding = 0; + m_nCostRemainder = 0; + m_iLastSelection = 0; + m_tptRally.tx = m_tptRally.ty = ktxInvalid; +} + +BuilderGob::~BuilderGob() +{ +} + +bool BuilderGob::Init(WCoord wx, WCoord wy, Player *pplr, fix fxHealth, dword ff, const char *pszName) +{ + if (!StructGob::Init(wx, wy, pplr, fxHealth, ff, pszName)) + return false; + + // Initalize the rally point for units built by this Builder + + WRect wrc; + GetTilePaddedWRect(&wrc); + + // Center spot "at the bottom" middle + + m_tptRally.tx = TcFromWc(wrc.left + wrc.Width() / 3); + m_tptRally.ty = TcFromWc(wrc.bottom); + return true; +} + +#define knVerBuilderGobState 4 +bool BuilderGob::LoadState(Stream *pstm) +{ + byte nVer = pstm->ReadByte(); + if (nVer != knVerBuilderGobState) + return false; + m_bq.LoadState(pstm); + if (!m_bq.IsEmpty()) { + m_gidBuildVisible = pstm->ReadWord(); + m_fxHealthBuilding = pstm->ReadWord(); + m_nCreditsSpentOnBuilding = pstm->ReadWord(); + m_nCostRemainder = pstm->ReadWord(); + m_tptRally.tx = pstm->ReadWord(); + m_tptRally.ty = pstm->ReadWord(); + } + return StructGob::LoadState(pstm); +} + +bool BuilderGob::SaveState(Stream *pstm) +{ + pstm->WriteByte(knVerBuilderGobState); + m_bq.SaveState(pstm); + if (!m_bq.IsEmpty()) { + pstm->WriteWord(m_gidBuildVisible); + pstm->WriteWord(m_fxHealthBuilding); + pstm->WriteWord(m_nCreditsSpentOnBuilding); + pstm->WriteWord(m_nCostRemainder); + pstm->WriteWord(m_tptRally.tx); + pstm->WriteWord(m_tptRally.ty); + } + return StructGob::SaveState(pstm); +} + +void BuilderGob::Deactivate() +{ + // half-baked buildings blow when builder goes bye-bye + // either through takeover or being destroyed + // on Deactivate is the only way to respond to being destroyed + // by the enemy + + AbortBuild(); + + StructGob::Deactivate(); +} + +int BuilderGob::GetGlobalQueuedCount(Player *pplr, word wfTest) +{ + int cQueued = 0; + word wfPlr = (pplr->GetFlags() & kfPlrComputer); + for (Gob *pgobT = ggobm.GetFirstGob(); pgobT != NULL; pgobT = ggobm.GetNextGob(pgobT)) { + if (!(pgobT->GetFlags() & kfGobUnit)) + continue; + UnitGob *puntT = (UnitGob *)pgobT; + if ((puntT->GetOwner()->GetFlags() & kfPlrComputer) != wfPlr) + continue; + if (!(puntT->GetConsts()->wf & wfTest)) + continue; + BuilderGob *pbldr = (BuilderGob *)puntT; + cQueued += pbldr->m_bq.GetUnitCount(kutNone); + } + + return cQueued; +} + +int BuilderGob::GetQueuedCount(Player *pplr, word wfTest) +{ + int cQueued = 0; + for (Gob *pgobT = ggobm.GetFirstGob(); pgobT != NULL; pgobT = ggobm.GetNextGob(pgobT)) { + if (!(pgobT->GetFlags() & kfGobUnit)) + continue; + UnitGob *puntT = (UnitGob *)pgobT; + if (puntT->GetOwner() != pplr) + continue; + if (!(puntT->GetConsts()->wf & wfTest)) + continue; + BuilderGob *pbldr = (BuilderGob *)puntT; + cQueued += pbldr->m_bq.GetUnitCount(kutNone); + } + + return cQueued; +} + +void BuilderGob::Build(UnitType ut, Gid gid) +{ + // put it on the public build queue + + m_bq.Enqueue(ut); + m_gidBuildVisible = gid; + + // The build queue is acted on during Update + + m_unvl.MinSkip(); +} + +void BuilderGob::AbortBuild(bool fRefundCreditsSpentOnBuilding, UnitType ut) +{ + // this needs to be called by a queued message if triggered by the player + // because it affects credits, especially if they're recycling called by + // kmidAbortBuildOtherCommand and by deactivate & takeover refunds + // CreditsSpent by parameter, and tells self-destruct to not refund half + // the value of the building + // we're either aborting the build of a specific unit (chosen by the user) + // or we're flushing the queue (due to deactivate, takeover or structure + // build abort) + + // empty queue case + + if (m_bq.IsEmpty()) + return; + + bool fCurrentConstruction = true; + + if (ut == kutNone) { + + // flush the queue. If we were building a structure, clean it up + + m_bq.Clear(); + + StructGob *pgobBuild = (StructGob *)ggobm.GetGob(m_gidBuildVisible, false); + if (pgobBuild != NULL) { + pgobBuild->SetFlags(pgobBuild->GetFlags() & ~kfGobBeingBuilt); + pgobBuild->SelfDestruct(false); + } + } else { + Assert(m_gidBuildVisible == kgidNull); + fCurrentConstruction = m_bq.RemoveUnit(ut); + } + + if (fCurrentConstruction) { + + // we just aborted the current construction + + if (fRefundCreditsSpentOnBuilding) { + m_pplr->SetCredits(m_pplr->GetCredits() + m_nCreditsSpentOnBuilding, false); + m_pplr->ModifyTotalCreditsConsumed(-m_nCreditsSpentOnBuilding); + } + + m_nCreditsSpentOnBuilding = 0; + m_nCostRemainder = 0; + m_fxHealthBuilding = itofx(0); + WaitingForCredits(false); + } +} + +void BuilderGob::DefUpdate() +{ + StructGob::DefUpdate(); + + if (m_bq.IsEmpty()) + return; + + if (m_ff & kfGobBeingUpgraded) { + + // we'll pause building, but make sure we're showing credits correctly + // if we were building when the upgrade started + + long nCredits = m_pplr->GetCredits(); + WaitingForCredits(nCredits==0); + return; + } + + UnitConsts *puntc = gapuntc[m_bq.Peek()]; + UnitGob *pgobBuild = (UnitGob *)ggobm.GetGob(m_gidBuildVisible, false); + + // God can build things instantly and for free + + bool fBuild = false; + if (gfGodMode && !ggame.IsMultiplayer()) { + fBuild = true; + WaitingForCredits(false); + } else { + + // CostPerUpdate is a fraction. We track its remainder and add it + // in the next update so we charge the right amount overall. + + int nCost = puntc->GetCost() + m_nCostRemainder; + int cupdTimeToBuild = puntc->GetTimeToBuild(); + if (m_pplr->GetHandicap() & kfHcapDecreasedTimeToBuild) + cupdTimeToBuild = ((cupdTimeToBuild * (100 + knDecreasedTimeToBuildPercent)) + 50) / 100; // +50 for rounding + + // if power is low and this building is so influenced, slow production + // by inflating TimeToBuild + + if ((m_puntc->wf & kfUntcNotifyPowerLowHigh) && m_pplr->IsPowerLow()) { + cupdTimeToBuild *= 2; //TUNE: + } + + int nCostPerUpdate = nCost / cupdTimeToBuild; + + long nCredits = m_pplr->GetCredits() - nCostPerUpdate; + if (nCredits >= 0) { + WaitingForCredits(false); + + m_pplr->SetCredits(nCredits, true); + m_nCreditsSpentOnBuilding += nCostPerUpdate; + m_nCostRemainder = nCost % cupdTimeToBuild; + + fix fxHealthPerUpdate = puntc->GetArmorStrength() / cupdTimeToBuild; + + fix fxHealth = addfx(m_fxHealthBuilding, fxHealthPerUpdate); + + if (m_nCreditsSpentOnBuilding >= puntc->GetCost()) { + // power changes during building can get us off by a credit + //Assert(m_nCreditsSpentOnBuilding == puntc->GetCost()); + fxHealth = puntc->GetArmorStrength(); + fBuild = true; + } + m_fxHealthBuilding = fxHealth; + if (pgobBuild != NULL) + pgobBuild->SetHealth(m_fxHealthBuilding); + + // This gob needs to redraw its build status indicator + + MarkRedraw(); + } else { + if (!(m_wfUnit & kfUnitRepairing)) { + + // we'll only manage this if there is not repair code doing so + + WaitingForCredits(true); + } + + // pgobBuild still animates even when credits have run out + + if (pgobBuild != NULL) + pgobBuild->MarkRedraw(); + } + + // to skip more intervals while not building would mean either A) it takes + // up to kcupdSymbolFlashRate-1 updates for building to resume with a next update + // or B) we need some kind of notification system to force an update. Notification system + // is complex because builders don't drain credits to zero so can't tell just + // from credits whether or not buildings need notifications. + + m_unvl.MinSkip(); + } + + if (fBuild) { + if (pgobBuild != NULL) { + pgobBuild->SetHealth(puntc->GetArmorStrength()); + pgobBuild->SetFlags(pgobBuild->GetFlags() & ~kfGobBeingBuilt); + } + + // Kick off delivery of the new build + + if (m_pplr == gpplrLocal) + gsndm.PlaySfx(m_pbldrc->sfxUnitReady); + SetState(kstBuildOtherCompleting); + } +} + +bool BuilderGob::ShowingBuildProgress() { + if (m_bq.IsEmpty()) { + return false; + } + + if (IsAlly(gpplrLocal->GetSide())) { + return true; + } + + if (gpplrLocal->GetHandicap() & kfHcapShowEnemyBuildProgress) { + return true; + } + + return false; +} + +void BuilderGob::GetClippingBounds(Rect *prc) { + StructGob::GetClippingBounds(prc); + + // Make sure build progress is in this, if it is visible + if (ShowingBuildProgress()) { + Rect rcT; + rcT.FromWorldRect(&m_puntc->wrcUIBounds); + rcT.Offset(PcFromWc(m_wx), PcFromWc(m_wy)); + prc->left = _min(prc->left, rcT.left); + prc->right = _max(prc->right, rcT.right); + } +} + +void BuilderGob::Draw(DibBitmap *pbm, int xViewOrigin, int yViewOrigin, int nLayer) +{ + StructGob::Draw(pbm, xViewOrigin, yViewOrigin, nLayer); + + if (nLayer == knLayerDepthSorted) { + if (ShowingBuildProgress()) { + Rect rcT; + rcT.FromWorldRect(&m_puntc->wrcUIBounds); + rcT.Offset(-xViewOrigin + PcFromWc(m_wx), -yViewOrigin + PcFromWc(m_wy)); + DrawBuildProgressIndicator(pbm, &rcT, m_fxHealthBuilding, gapuntc[m_bq.Peek()]->GetArmorStrength()); + } + } +} + +void BuilderGob::DrawUpgradeEffect(DibBitmap *pbm, int xViewOrigin, int yViewOrigin) +{ + if (!(m_ff & kfGobActive)) + return; + if (!IsAlly(gpplrLocal->GetSide()) && ((gpplrLocal->GetHandicap() & kfHcapShowEnemyBuildProgress) == 0)) + return; + + // UNDONE: hack-effect + WRect wrcBounds; + GetTilePaddedWRect(&wrcBounds); + Rect rcBounds; + rcBounds.FromWorldRect(&wrcBounds); + rcBounds.Offset(-xViewOrigin, -yViewOrigin); + int nT = _min(rcBounds.Width(), rcBounds.Height()) / 3; + + int cupd = gsim.GetUpdateCount(); + int n = (cupd % 4); + Color clr = GetColor(kiclrGalaxite); + if (n < 2) { + Rect rcT = rcBounds; + rcT.Inflate(-nT, -nT); + DrawBorder(pbm, &rcT, 2, clr); + } else { + DrawBorder(pbm, &rcBounds, 2, clr); + } + + if (!(m_ff & kfGobSelected)) { + Rect rcT; + rcT.FromWorldRect(&m_puntc->wrcUIBounds); + rcT.Offset(-xViewOrigin + PcFromWc(m_wx), -yViewOrigin + PcFromWc(m_wy)); + DrawHealthIndicator(pbm, &rcT, m_fxHealth, m_puntc->GetArmorStrength()); + } +} + +UnitGob *BuilderGob::GetBuiltGob() +{ + // Buildings are visible and added to the gob mgr when building. Vehicles + // are not until the build is done. + + Assert(!m_bq.IsEmpty()); + UnitGob *pgobBuild = (UnitGob *)ggobm.GetGob(m_gidBuildVisible); + if (pgobBuild != NULL) + return pgobBuild; + return (UnitGob *)CreateGob(gapuntc[m_bq.Peek()]->gt); +} + +void BuilderGob::ClearBuiltGob() +{ + // if this assert goes off then an AbortBuild may have somehow snuck in + // between setting kstBuildOtherCompleting and the derived class calling this + + Assert((m_nCreditsSpentOnBuilding > 0) || gfGodMode); + + m_bq.Dequeue(); + m_gidBuildVisible = kgidNull; + m_nCostRemainder = 0; + m_nCreditsSpentOnBuilding = 0; + m_fxHealthBuilding = itofx(0); +} + +void BuilderGob::DrawBuildProgress(DibBitmap *pbm, Rect *prc) +{ + if (m_bq.IsEmpty()) + return; + + Color clr = GetColor(kiclrYellow); + int cxWidth = prc->right - prc->left; + int nLength = (cxWidth * (long)m_fxHealthBuilding) / gapuntc[m_bq.Peek()]->GetArmorStrength(); + pbm->Fill(prc->left, prc->top, nLength, kcyBuildProgress, clr); +} + +void BuilderGob::SyncBuildQueue(BuildQueue *pbq) +{ + // operator is overloaded + + *pbq = m_bq; +} + +// Note: s_aptInit has been fixed to not have a structure sized-hole + +static char s_aptInit[] = { + 0, 0, 0, -1, -1, 0, 1, 0, 0, 1, -1, -1, 1, -1, -1, 1, + 1, 1, 0, -2, -2, 0, 2, 0, 0, 2, -1, -2, 1, -2, -2, -1, + 2, -1, -2, 1, 2, 1, -1, 2, 1, 2, -2, -2, 2, -2, -2, 2, + 2, 2, 0, -3, -3, 0, 3, 0, 0, 3, -1, -3, 1, -3, -3, -1, + 3, -1, -3, 1, 3, 1, -1, 3, 1, 3, -2, -3, 2, -3, -3, -2, + 3, -2, -3, 2, 3, 2, -2, 3, 2, 3, 0, -4, -4, 0, 4, 0, + 0, 4, -1, -4, 1, -4, -4, -1, 4, -1, -4, 1, 4, 1, -1, 4, + 1, 4, -3, -3, 3, -3, -3, 3, 3, 3, -2, -4, 2, -4, -4, -2, + 4, -2, -4, 2, 4, 2, -2, 4, 2, 4, 0, -5, -3, -4, 3, -4, + -4, -3, 4, -3, -5, 0, 5, 0, -4, 3, 4, 3, -3, 4, 3, 4, + 0, 5, -1, -5, 1, -5, -5, -1, 5, -1, -5, 1, 5, 1, -1, 5, + 1, 5, -2, -5, 2, -5, -5, -2, 5, -2, -5, 2, 5, 2, -2, 5, + 2, 5, -4, -4, 4, -4, -4, 4, 4, 4, -3, -5, 3, -5, -5, -3, + 5, -3, -5, 3, 5, 3, -3, 5, 3, 5, 0, -6, -6, 0, 6, 0, + 0, 6, -1, -6, 1, -6, -6, -1, 6, -1, -6, 1, 6, 1, -1, 6, + 1, 6, -2, -6, 2, -6, -6, -2, 6, -2, -6, 2, 6, 2, -2, 6, +}; + +void FindNearestFreeTile(TCoord tx, TCoord ty, WPoint *pwpt, byte bf) +{ + int cpt = sizeof(s_aptInit) / 2; + for (int npt = 0; npt < cpt; npt++) { + int txT = tx + s_aptInit[npt * 2]; + int tyT = ty + s_aptInit[npt * 2 + 1]; + if (IsTileFree(txT, tyT, bf)) { + pwpt->wx = WcFromTc(txT) + kwcTileHalf; + pwpt->wy = WcFromTc(tyT) + kwcTileHalf; + return; + } + } + + // Couldn't find anything good + + pwpt->wx = WcFromTc(tx) + kwcTileHalf; + pwpt->wy = WcFromTc(ty) + kwcTileHalf; +} + +void BuilderGob::FindInitPosition(WPoint *pwpt) +{ + WRect wrc; + GetTilePaddedWRect(&wrc); + + // Center spot "at the bottom" middle + + TCoord tx = TcFromWc(wrc.left + wrc.Width() / 3); + TCoord ty = TcFromWc(wrc.bottom); + + FindNearestFreeTile(tx, ty, pwpt); +} + +//=========================================================================== +// MobileUnitBuildForm implementation (used by both VTS and HRC but HQ has its own) + +void MobileUnitBuildForm::SetOwner(BuilderGob *pbldr) +{ + // Position + + Size sizDib; + m_pfrmm->GetDib()->GetSize(&sizDib); + Rect rc; + rc.left = ((sizDib.cx - m_rc.Width()) / 2) & ~1; + rc.top = 0; // (sizDib.cy - m_rc.Height()) / 2; + rc.right = rc.left + m_rc.Width(); + rc.bottom = rc.top + m_rc.Height(); + SetRect(&rc); + + m_wf |= kfFrmAutoTakedown | kfFrmNoEcom; + m_pbldr = pbldr; + m_pbldr->SyncBuildQueue(&m_bqPrivate); + + BuildListControl *plstc = (BuildListControl *)GetControlPtr(kidcList); + plstc->Clear(); + + // Initialize list with available unit types. + // UNDONE: need to link list to dynamic build capability + + Player *pplr = pbldr->GetOwner(); + UnitMask umOwned = (pplr->GetUnitMask() & kumStructures) | pplr->GetUpgrades(); + UnitMask umAllowed = ((BuilderConsts *)pbldr->GetConsts())->umCanBuild & (gfGodMode ? kumAll : pplr->GetAllowedUnits()); + UpgradeMask upgmOwned = pplr->GetUpgradeMask(); + + ButtonControl *pbtn; + + m_fOrderValid = !m_bqPrivate.IsFull(); + + int j = 0; + for (int i = 0; i < kutMax; i++) { + UnitConsts *puntc = gapuntc[i]; + + // Does this player have what it takes to build this structure/unit? + + bool fDisabled = true; + if (gfGodMode || ((umOwned & puntc->umPrerequisites) == puntc->umPrerequisites && + (upgmOwned & puntc->upgmPrerequisites) == puntc->upgmPrerequisites)) + fDisabled = false; + + // Yep, is this one of the types this build form allows? + + if (puntc->um & umAllowed) { + + // Yep, add it to the list + + int nStripIcon = puntc->panid->GetStripIndex("icon"); + if (nStripIcon != -1) { + plstc->Add(puntc->panid, nStripIcon, 0, (void *)(int)puntc->ut, fDisabled); + j++; + if (j == m_pbldr->GetLastSelection()) { + // If disabled, orders are not valid + + if (fDisabled) + m_fOrderValid = false; + + // Disable Cancel Order button if nothing queued for that unit. + + pbtn = (ButtonControl *)GetControlPtr(kidcCancelOrder); + pbtn->Show(m_bqPrivate.GetUnitCount(puntc->ut) > 0); + + } + } + } + } + + // If nothing added to the list, the order button is invalid + + if (j == 0) { + m_fOrderValid = false; + ButtonControl *pbtn = (ButtonControl *)GetControlPtr(kidcCancelOrder); + pbtn->Show(false); + } + + // Select the last selected unit by default + + // Will be set to the current selection by OnControlSelected via plstc->Select + + s_iliCurrent = -1; + + plstc->Select(m_pbldr->GetLastSelection()); + plstc->SetQueueInfo(m_pbldr, &m_bqPrivate); + + // Calc what control needs to be visible + if (plstc->GetSelectedItemIndex() == -1) { + s_iliCurrent = -1; + m_fOrderValid = false; + UpdateOrderButton(false); + } else { + UpdateOrderButton(true); + } +} + +void MobileUnitBuildForm::UpdateUnitInfo(ListItem *pli) +{ + MobileUnitConsts *pmuntc = (MobileUnitConsts *)gapuntc[(int)pli->pvData]; + + // Update Cost + + LabelControl *plbl = (LabelControl *)GetControlPtr(kidcCost); + char szT[kcbStructUnitName]; + itoa(pmuntc->GetCost(), szT, 10); + plbl->SetText(szT); + PipMeterControl *pmtr = (PipMeterControl *)GetControlPtr(kidcCostMeter); + Assert(pmuntc->GetCost() <= GetUnitCostMax()); // make sure we're scaling correctly + pmtr->SetValue(((long)pmuntc->GetCost() * 100) / GetUnitCostMax()); + + // Update Name + + plbl = (LabelControl *)GetControlPtr(kidcName); + strcpy(szT, pmuntc->szLongName); + HtStrupr(szT); + plbl->SetText(szT); + + // Update Speed + + WCoord wcOneThirdRange = (gwcMoveDistPerUpdateMax - gwcMoveDistPerUpdateMin) / 3; + plbl = (LabelControl *)GetControlPtr(kidcMoveRate); + char *pszT = "MEDIUM"; + if (pmuntc->GetMoveDistPerUpdate() < gwcMoveDistPerUpdateMin + wcOneThirdRange) + pszT = "SLOW"; + else if (pmuntc->GetMoveDistPerUpdate() >= gwcMoveDistPerUpdateMax - wcOneThirdRange) + pszT = "FAST"; + plbl->SetText(pszT); + pmtr = (PipMeterControl *)GetControlPtr(kidcMoveRateMeter); + Assert(pmuntc->GetMoveDistPerUpdate() <= gwcMoveDistPerUpdateMax); + pmtr->SetValue((pmuntc->GetMoveDistPerUpdate() * 100) / gwcMoveDistPerUpdateMax); + + // Update Firepower + + DamageMeterControl *pdmtr = (DamageMeterControl *)GetControlPtr(kidcWeaponStrengthMeter); + pdmtr->SetUnitConsts(pmuntc); + + // Update Armor + + fix fxOneThirdRange = (fix)divfx(subfx(gfxMobileUnitArmorStrengthMax, gfxMobileUnitArmorStrengthMin), itofx(3)); + plbl = (LabelControl *)GetControlPtr(kidcArmorStrength); + pszT = "MEDIUM"; + if (pmuntc->GetArmorStrength() == 0) + pszT = "NONE"; + else if (pmuntc->GetArmorStrength() < addfx(gfxMobileUnitArmorStrengthMin, fxOneThirdRange)) + pszT = "LIGHT"; + else if (pmuntc->GetArmorStrength() >= subfx(gfxMobileUnitArmorStrengthMax, fxOneThirdRange)) + pszT = "HEAVY"; + plbl->SetText(pszT); + pmtr = (PipMeterControl *)GetControlPtr(kidcArmorStrengthMeter); + Assert(pmuntc->GetArmorStrength() <= gfxMobileUnitArmorStrengthMax); + pmtr->SetValue(((long)fxtoi(pmuntc->GetArmorStrength()) * 100) / fxtoi(gfxMobileUnitArmorStrengthMax)); + + // Update Range + + TCoord tcOneThirdRange = ((gtcFiringRangeMax - gtcFiringRangeMin) * 100) / 3; + plbl = (LabelControl *)GetControlPtr(kidcWeaponRange); + pszT = "MEDIUM"; + if (pmuntc->tcFiringRange == 0) + pszT = "NONE"; + else if (pmuntc->tcFiringRange * 100 <= tcOneThirdRange) + pszT = "LOW"; + else if (pmuntc->tcFiringRange * 100 >= (gtcFiringRangeMax * 100) - tcOneThirdRange) + pszT = "HIGH"; + plbl->SetText(pszT); + pmtr = (PipMeterControl *)GetControlPtr(kidcWeaponRangeMeter); + Assert(pmuntc->tcFiringRange-1 <= gtcFiringRangeMax); + pmtr->SetValue(((pmuntc->tcFiringRange - 1) * 100) / gtcFiringRangeMax); + + // Update Description. Substitute the list of prerequisites if the + // item is disabled. + + plbl = (LabelControl *)GetControlPtr(kidcDescription); + if (pli->fDisabled) { + char szT[120]; + GetPrerequisiteString(szT, pmuntc); + + char szT2[120]; + sprintf(szT2, "This unit requires: %s.", szT); + plbl->SetText(szT2); + + } else { + plbl->SetText(pmuntc->szDescription); + } + + // Hide the "Order"/"Recruit" button if this structure is disabled + + m_fOrderValid = !m_bqPrivate.IsFull() && !pli->fDisabled; + UpdateOrderButton(false); +} + +void MobileUnitBuildForm::EndForm(int nResult) +{ + ListControl *plstc = (ListControl *)GetControlPtr(kidcList); + m_pbldr->SetLastSelection(plstc->GetSelectedItemIndex()); + m_pbldr = NULL; + Form::EndForm(nResult); +} + +int MobileUnitBuildForm::s_iliCurrent = -1; +#define kcupdLimit 6 + +void MobileUnitBuildForm::OnControlSelected(word idc) +{ + switch (idc) { + case kidcLimitReached: + break; + + case kidcCancel: + EndForm(kidcCancel); + return; +//CRM + case kidcHelp: + Help(m_idf == kidfBuildInfantry ? "personnel" : "vehicles", !ggame.IsMultiplayer()); + break; + + case kidcOrder: + { + m_fOrderValid = !m_bqPrivate.IsFull(); + UpdateOrderButton(true); + + if (m_fOrderValid && !m_fLimitReached) { + Message msg; + memset(&msg, 0, sizeof(msg)); + msg.smidSender = ksmidNull; + msg.mid = kmidBuildOtherCommand; + ListControl *plstc = (ListControl *)GetControlPtr(kidcList); + msg.BuildOtherCommand.ut = UnitTypeFromPVoid(plstc->GetSelectedItemData()); + msg.smidReceiver = m_pbldr->GetId(); + gcmdq.Enqueue(&msg); + + if (m_pbldr->GetOwner() == gpplrLocal) + gsndm.PlaySfx(((BuilderConsts *)m_pbldr->GetConsts())->sfxUnitBuild); + + // update our private copy of the queue and see if we filled it + + m_bqPrivate.Enqueue(UnitTypeFromPVoid(plstc->GetSelectedItemData())); + + ButtonControl *pbtn = (ButtonControl *)GetControlPtr(kidcCancelOrder); + pbtn->Show(true); + + // Need to update the order button / show the limit reached label. + // Since the order was queued we don't really know if the limit has been + // reached. We'll just update it asap. + + m_cupdLast = gsim.GetUpdateCount() - kcupdLimit + 1; + m_fOrderValid = !m_bqPrivate.IsFull(); + UpdateOrderButton(false); + + // repaint the number showing the unit count + + plstc->Invalidate(); + } + } + break; + + case kidcCancelOrder: + { + // decrement the appropriate count + + ListControl *plstc = (ListControl *)GetControlPtr(kidcList); + if (m_bqPrivate.GetUnitCount(UnitTypeFromPVoid(plstc->GetSelectedItemData())) == 0) { + Assert(false); // if my input lags this might be possible... + return; + } + + Message msg; + memset(&msg, 0, sizeof(msg)); + msg.smidSender = ksmidNull; + msg.mid = kmidAbortBuildOtherCommand; + msg.AbortBuildOtherCommand.ut= UnitTypeFromPVoid(plstc->GetSelectedItemData()); + msg.smidReceiver = m_pbldr->GetId(); + gcmdq.Enqueue(&msg); + + if (m_pbldr->GetOwner() == gpplrLocal) + gsndm.PlaySfx(((BuilderConsts *)m_pbldr->GetConsts())->sfxUnitBuildAbort); + + // decrement the the queue count. private queue for immediate response + + UnitType ut = UnitTypeFromPVoid(plstc->GetSelectedItemData()); + m_bqPrivate.RemoveUnit(ut); + plstc->Invalidate(); + + // Queue is not full by definition but gob count may still be at limit + + m_fOrderValid = true; + m_fLimitReached = false; + UpdateOrderButton(false); + + if (m_bqPrivate.GetUnitCount(ut) == 0) { + ButtonControl *pbtn = (ButtonControl *)GetControlPtr(kidcCancelOrder); + pbtn->Show(false); + } + } + break; + + case kidcOk: + EndForm(); + return; + } +} + +void MobileUnitBuildForm::OnControlNotify(word idc, int nNotify) +{ + if (idc != kidcList) + return; + + if (nNotify != knNotifySelectionChange && nNotify != knNotifySelectionTap) { + return; + } + + ListControl *plstc = (ListControl *)GetControlPtr(kidcList); + ListItem *pli = plstc->GetSelectedItem(); + if (pli == NULL) { + s_iliCurrent = -1; + m_fOrderValid = false; + UpdateOrderButton(false); + return; + } + + UpdateUnitInfo(pli); + ButtonControl *pbtn = (ButtonControl *)GetControlPtr(kidcCancelOrder); + pbtn->Show(m_bqPrivate.GetUnitCount(UnitTypeFromPVoid(plstc->GetSelectedItemData())) > 0); + + // Can we order? + + m_fOrderValid = !m_bqPrivate.IsFull() && !pli->fDisabled; + UpdateOrderButton(false); + + if (m_fOrderValid && !m_fLimitReached) { + // Tap on a selected item orders it + + int ili = plstc->GetSelectedItemIndex(); + if (s_iliCurrent == ili && !pli->fDisabled) { + OnControlSelected(kidcOrder); + } else { + s_iliCurrent = ili; + } + } +} + +void MobileUnitBuildForm::UpdateOrderButton(bool fCalcLimit) +{ + Control *pctlLimit = GetControlPtr(kidcLimitReached); + Control *pctlOrder = GetControlPtr(kidcOrder); + + // Limit only gets calced + + if (fCalcLimit) { + m_fLimitReached = !ggobm.IsBelowLimit(knLimitMobileUnit, + m_pbldr->GetOwner()); + } + + if (m_fLimitReached) { + pctlLimit->Show(true); + pctlLimit->SetFlags(pctlLimit->GetFlags() | kfLblHitTest); + pctlOrder->Show(false); + } else { + pctlLimit->Show(false); + pctlOrder->Show(m_fOrderValid); + } +} + +void MobileUnitBuildForm::DefUpdate(BuilderGob *pbldr, bool fBuildInProgress) +{ + // assumes this is only called if a build is in progress + + if (!(m_wf & kfFrmVisible) || pbldr != m_pbldr) + return; + + // May have multiple builders that have caused the limit to be reached. + // Poll to figure this out. This takes into account the units already queued too. + // Don't do it every update. This gets rechecked at actual order time so it'll be + // up to date. + + long cupdCurrent = gsim.GetUpdateCount(); + if (abs(cupdCurrent - m_cupdLast) > kcupdLimit) { + m_cupdLast = cupdCurrent; + UpdateOrderButton(true); + } + + // if we're showing during building, we have a progress bar we're + // advancing. Make it update. + + if (fBuildInProgress) { + ListControl *plstc = (ListControl *)GetControlPtr(kidcList); + plstc->Invalidate(); + } +} + +void MobileUnitBuildForm::OnPaintBackground(DibBitmap *pbm, UpdateMap *pupd) +{ + RawBitmap *prbm = LoadRawBitmap("buildformbkgd.rbm"); + BltHelper(pbm, prbm, pupd, m_rc.left, m_rc.top); + delete prbm; +} + +void MobileUnitBuildForm::OnUnitCompleted(BuilderGob *pbldr, UnitType ut) +{ + if (!(m_wf & kfFrmVisible) || pbldr != m_pbldr) + return; + + m_bqPrivate.RemoveUnit(ut); + + // fix up button state + + ListControl *plstc = (ListControl *)GetControlPtr(kidcList); + ListItem *pli = plstc->GetSelectedItem(); + + m_fOrderValid = pli == NULL || !pli->fDisabled; + UpdateOrderButton(true); + + if (m_bqPrivate.GetUnitCount(ut) == 0) { + if (UnitTypeFromPVoid(plstc->GetSelectedItemData()) == ut) { + ButtonControl *pbtn = (ButtonControl *)GetControlPtr(kidcCancelOrder); + pbtn->Show(false); + } + } +} + +// +// BuildListControl +// + +bool BuildListControl::Init(Form *pfrm, IniReader *pini, FindProp *pfind) +{ + // Base initialization + + if (!ListControl::Init(pfrm, pini, pfind)) + return false; + + // idc (x y cx cy) nfnt cyItem + + int cyItem; + int cArgs = pini->GetPropertyValue(pfind, "%*d (%*d %*d %*d %*d) %*d %d", + &cyItem); + if (cArgs != 1) + return false; + + m_cyItem = PcFromFc(cyItem); + m_pbldr = NULL; + m_pbq = NULL; + SetScrollPosColorIndex(kiclrListBorder); + return true; +} + +bool BuildListControl::Add(AnimationData *panid, int nStrip, int nFrame, void *pvData, bool fDisabled) +{ + ListItem *pli = new ListItem; + if (pli == NULL) + return false; + + pli->Anim.panid = panid; + pli->Anim.nStrip = nStrip; + pli->Anim.nFrame = nFrame; + pli->pvData = pvData; + pli->fDisabled = fDisabled; + + return ListControl::Add(pli); +} + +void BuildListControl::GetSubRects(Rect *prcInterior, Rect *prcUpArrow, + Rect *prcDownArrow, Rect *prcScrollPosition) +{ + Size sizArrow; + s_ptbmScrollUpUp->GetSize(&sizArrow); + + int l = m_rc.left + gcxyBorder; + int r = m_rc.right - gcxyBorder; + int t = m_rc.top + 1; + int b = m_rc.bottom - 1; + + if (m_wf & kfLstcScrollPosition) { + if (prcUpArrow != NULL) { + prcUpArrow->SetEmpty(); + } + if (prcDownArrow != NULL) { + prcDownArrow->SetEmpty(); + } + prcInterior->Set(l, t + sizArrow.cy, r - gcxyBorder * 2, + b - sizArrow.cy); + if (prcScrollPosition != NULL) { + prcScrollPosition->Set(r - gcxyBorder, prcInterior->top, r, + prcInterior->bottom - prcInterior->Height() % m_cyItem); + } + } else { + if (prcUpArrow != NULL) { + prcUpArrow->Set(l, t, r, t + sizArrow.cy); + } + if (prcDownArrow != NULL) { + prcDownArrow->Set(l, b - sizArrow.cy, r, b); + } + prcInterior->Set(l, t + sizArrow.cy, r, b - sizArrow.cy); + if (prcScrollPosition != NULL) { + prcScrollPosition->SetEmpty(); + } + } +} + +void BuildListControl::OnPaint(DibBitmap *pbm) +{ + Rect rcForm; + m_pfrm->GetRect(&rcForm); + + Rect rc; + GetRect(&rc); + rc.Offset(rcForm.left, rcForm.top); + + DrawBorder(pbm, &rc, 1, GetColor(kiclrListBorder)); + pbm->Fill(rc.left + 1, rc.top + 1, rc.Width() - 2, rc.Height() -2, GetColor(kiclrListBackground)); + + ListControl::OnPaint(pbm); +} + +void BuildListControl::DrawItem(DibBitmap *pbm, ListItem *pli, int x, int y, int cx, int cy) +{ + HostSoundServiceProc(); + + Rect rc; + if (pli->fSelected) { + rc.Set(x, y, x + cx, y + cy); + DrawBorder(pbm, &rc, 2, GetColor(kiclrWhite)); + } else { + rc.Set(x + 1, y + 1, x + cx - 1, y + cy - 1); + DrawBorder(pbm, &rc, 1, GetColor(kiclrListBorder)); + } + + AnimationData *panid = pli->Anim.panid; + int nStrip = pli->Anim.nStrip; + int nFrame = pli->Anim.nFrame; + + // Center the image within the item area + + Rect rcAnim; + pli->Anim.panid->GetBounds(nStrip, nFrame, &rcAnim); + + int yOff = (cy - rcAnim.Height()) / 2; + int xOff = (cx - rcAnim.Width()) / 2; + + panid->DrawFrame(nStrip, nFrame, pbm, x + xOff, y + yOff, gpplrLocal->GetSide()); + + if (pli->fDisabled) + pbm->Shadow(rc.left, rc.top, rc.Width(), rc.Height()); + + // if we have a builder pointer, draw building information + + if ((m_pbq != NULL) && (m_pbq->GetUnitCount(UnitTypeFromPVoid(pli->pvData)) > 0)) { + + // draw a test health bar. offset by two pixels - either the width of the selected + // rect or the width & offset of an unselected rect + Assert(m_pbldr != NULL); + + if (m_pbldr->UnitBuildInProgress() == UnitTypeFromPVoid(pli->pvData)) { + rc.top = y + cy - (2 + kcyBuildProgress); + rc.left = x + 2; + rc.bottom = y + cy - 2; + rc.right = x + cx - 2; + m_pbldr->DrawBuildProgress(pbm, &rc); + } + + Font *pfnt = gapfnt[kifntButton]; + char szCount[3]; + sprintf(szCount,"%d", m_pbq->GetUnitCount(UnitTypeFromPVoid(pli->pvData))); + int cxQ = pfnt->GetTextExtent(szCount); + pfnt->DrawText(pbm, szCount, x+cx-2-cxQ, y+cy-2-pfnt->GetHeight()); + } +} + +// ---------------------------------------------------------------------------------- +// BuildQueue - helper class to ensure the public and private queues behave the same +// +// m_achBuildQueue is a signed byte array of unit types. as items are pulled off everything +// is scooted up with a memcopy. That way units can be canceled in a different order than +// they were ordered and gaps can be closed with memcopy while preserving the overall order +// m_achBuildQueue is bytes for size consideratons but UnitTypes are actually larger so be aware +// ---------------------------------------------------------------------------------- + +// follow the versioning for the build gob +bool BuildQueue::LoadState(Stream *pstm) +{ + m_cchQueueMax = pstm->ReadWord(); + if (m_cchQueueMax > 0) + pstm->ReadBytesRLE((byte *)m_achBuildQueue, m_cchQueueMax); + + return true; +} + +bool BuildQueue::SaveState(Stream *pstm) +{ + pstm->WriteWord(m_cchQueueMax); + if (m_cchQueueMax > 0) + pstm->WriteBytesRLE((byte *)m_achBuildQueue, m_cchQueueMax); + return true; +} + +BuildQueue::BuildQueue() +{ + Assert(kutMax < 128); + m_cchQueueMax = kcBuildQueueMax; + memset(&m_achBuildQueue, (char)kutNone, sizeof(m_achBuildQueue)); +} + +void BuildQueue::SetSize(word cutMax) +{ + Assert(cutMax <= kcBuildQueueMax); + m_cchQueueMax = cutMax; +} + +int BuildQueue::GetRemainingCapacity() +{ + int cSlotsOpen = 0; + for (int i = 0; i < m_cchQueueMax; i++) { + if (m_achBuildQueue[i] == (char)kutNone) + cSlotsOpen++; + } + return cSlotsOpen; +} + +void BuildQueue::Enqueue(UnitType ut) +{ + int i = 0; + while ((m_achBuildQueue[i] != (char)kutNone) && i < m_cchQueueMax) + i++; + + // we should have made sure there would be room before we get here, we've already + // told the user it's on the queue by this point + + Assert(i < m_cchQueueMax); + + m_achBuildQueue[i] = (char)ut; +} + +void BuildQueue::Dequeue() +{ + // normally Dequeue would return the thing it's dequeuing, but I don't really care + + memmove(&(m_achBuildQueue[0]), &(m_achBuildQueue[1]), (kcBuildQueueMax - 1)); + m_achBuildQueue[kcBuildQueueMax - 1] = (char) kutNone; +} + +bool BuildQueue::RemoveUnit(UnitType ut) +{ + // find something of that unit type and pull it out of the queue + // search from back to front so we remove a queued item before + // re-setting what's currently building. + + int i; + for(i = m_cchQueueMax - 1; i > -1; i--) { + if (m_achBuildQueue[i] == (char) ut) + break; + } + Assert(i > -1); + + // it's possible we will encounter things being removed that aren't there because + // of a lag in state. As long as it happens once in each queue we'll be ok. (IE one queue + // will fail canceling and the other will fail removing a completed unit) Since only one + // queue actually reflects game state it's ok for them to get to the same state different ways + + memmove(&(m_achBuildQueue[i]), &(m_achBuildQueue[i+1]), sizeof(char)*(m_cchQueueMax - i)); + m_achBuildQueue[m_cchQueueMax - 1] = kutNone; + + // return true if we removed the currently building item + + return i == 0; +} + +UnitType BuildQueue::Peek() +{ + return (UnitType) m_achBuildQueue[0]; +} + +bool BuildQueue::IsEmpty() +{ + return m_achBuildQueue[0] == (char) kutNone; +} + +bool BuildQueue::IsFull() +{ + return m_achBuildQueue[m_cchQueueMax-1] != (char) kutNone; +} + +int BuildQueue::GetUnitCount(UnitType ut) +{ + int cut = 0; + for (int i = 0; i < m_cchQueueMax; i++) { + if (ut == kutNone) { + if (m_achBuildQueue[i] != (char)kutNone) + cut++; + } else { + if (m_achBuildQueue[i] == (char)ut) + cut++; + } + } + return cut; +} + +void BuildQueue::Clear() +{ + memset(&m_achBuildQueue, (char)kutNone, kcBuildQueueMax); +} + +BuildQueue &BuildQueue::operator=( BuildQueue &bqRHS ) +{ + m_cchQueueMax = bqRHS.m_cchQueueMax; + memcpy(&m_achBuildQueue, bqRHS.m_achBuildQueue, m_cchQueueMax); + + return *this; +} + +} // namespace wi diff --git a/game/CutScene.cpp b/game/CutScene.cpp new file mode 100644 index 0000000..5abaec8 --- /dev/null +++ b/game/CutScene.cpp @@ -0,0 +1,308 @@ +#include "ht.h" + +namespace wi { + +// +// Cut Scene implementation +// + +class CutSceneForm : public ShellForm +{ +public: + CutSceneForm() secCutScene; + + // Form overrides + + virtual bool DoModal(const char *pszScene = NULL) secCutScene; + virtual void OnControlSelected(word idc) secCutScene; +// virtual void OnPaintBackground(DibBitmap *pbm, UpdateMap *pupd) secCutScene; + virtual bool OnPenEvent(Event *pevt) secCutScene; + + // Timer interface + + virtual void OnTimer(long tCurrent) secCutScene; + +private: + void More() secCutScene; + void Layout(char *pszBitmap) secCutScene; + void HideCutSceneBitmap() secCutScene; + void ShowCutSceneBitmap() secCutScene; + + char *m_pszText; +}; + +void CutScene(const char *pszText, bool fPauseSimulation) +{ + // Show the cut scene + + CutSceneForm *pfrm = (CutSceneForm *)gpmfrmm->LoadForm(gpiniForms, kidfCutScene, new CutSceneForm()); + if (pfrm != NULL) { + if (fPauseSimulation) + gsim.Pause(true); + + pfrm->DoModal(pszText); + + if (fPauseSimulation) + gsim.Pause(false); + delete pfrm; + } + + // Set the palette back + + Palette *ppal = gsim.GetLevel()->GetPalette(); + SetHslAdjustedPalette(ppal, gnHueOffset, gnSatMultiplier, gnLumOffset); + gmpiclriclrShadow = gsim.GetLevel()->GetShadowMap(); +} + +CutSceneForm::CutSceneForm() +{ +} + +#if 0 +void CutSceneForm::OnPaintBackground(DibBitmap *pbm, UpdateMap *pupd) +{ + ShellForm::OnPaintBackground(pbm, pupd); + + EcomTextControl *pect = (EcomTextControl *)GetControlPtr(kidcMessage); + Rect rc; + pect->GetRect(&rc); + Font *pfnt = gapfnt[kifntDefault]; + rc.left = 0; + rc.right = m_rc.Width(); + rc.bottom = rc.top + rc.Height() - (rc.Height() % pfnt->GetHeight()); + + FillHelper(pbm, pupd, &rc, GetColor(kiclrBlack)); +} +#endif + +bool CutSceneForm::DoModal(const char *pszText) +{ + m_pszText = (char *)pszText; + More(); + + int idc; + gtimm.AddTimer(this, kctEcomOutputInterval); + ShellForm::DoModal(&idc, false, false); + gtimm.RemoveTimer(this); + return true; +} + +void CutSceneForm::HideCutSceneBitmap() +{ + BitmapControl *pbmc = (BitmapControl *)GetControlPtr(kidcBitmap); + pbmc->Show(false); + gpmfrmm->DrawFrame(false); +} + +void CutSceneForm::ShowCutSceneBitmap() +{ + BitmapControl *pbmc = (BitmapControl *)GetControlPtr(kidcBitmap); + pbmc->Show(true); +} + +void CutSceneForm::More() +{ + ButtonControl *pbtn = (ButtonControl *)GetControlPtr(kidcOk); + pbtn->Show(false); + + // Spit out as much text as will fit within the EcomControl until + // we hit a tag at which time we relayout the form based on + // the new image size and go from there. + + char *pch = m_pszText; + while (*pch != 0) { + if (*pch == '<') { + + // If there's text before the tag then display it + + if (pch != m_pszText) + break; + + char szBitmap[kcbFilename]; + int cch; + // UNDONE: no image + int cArgs = IniScanf(pch, "%+", szBitmap, &cch); + if (cArgs != 0) { + if (cArgs == 1) + pch += strlen(pch); + else + pch += cch; + + { + // This little dance (hide the bitmap control, force a repaint, + // change the palette, show the bitmap control) is to avoid the + // ugly palette flash. + + char szPalette[kcbFilename]; + strcpy(szPalette, szBitmap); + char *pchDot = strchr(szPalette, '.'); + Assert(pchDot != NULL); + *pchDot = 0; + strcat(szPalette, ".palbin"); + + HideCutSceneBitmap(); + + FileMap fmapOld = m_fmapPalette; + Palette *ppal = (Palette *)gpakr.MapFile(szPalette, &m_fmapPalette); + if (ppal != NULL) { + if (m_wf & kfFrmHasPalette) + gpakr.UnmapFile(&fmapOld); + + m_wf |= kfFrmHasPalette; + + // Select palette + + SetHslAdjustedPalette(ppal, gnHueOffset, gnSatMultiplier, gnLumOffset); + } + + ShowCutSceneBitmap(); + } + + Layout(szBitmap); + m_pszText = pch; + } else { + // there was not an image, is there a sound? + int sfxSound; + int cArgs = IniScanf(pch, "%+", &sfxSound, &cch); + if (cArgs != 0) { + if (cArgs == 1) + pch += strlen(pch); + else + pch += cch; + + gsndm.PlaySfx(Sfx(ksfxHappyEnding + sfxSound)); + m_pszText = pch; + } + } + } else { + pch++; + } + } + + EcomTextControl *pect = (EcomTextControl *)GetControlPtr(kidcMessage); + int cch = pch - m_pszText; + char *pszT = new char[cch + 1]; + Assert(pszT != NULL, "out of memory!"); + if (pszT != NULL) { + strncpyz(pszT, m_pszText, cch + 1); + + // How much of the text will fit? + + Font *pfnt = gapfnt[kifntShadow]; + Rect rcT; + pect->GetRect(&rcT); + + int cyFont = pfnt->GetHeight() - pfnt->GetLineOverlap(); + int cLines = rcT.Height() / cyFont; + char *pchBreak = pszT; + while (cLines-- > 0 && pchBreak != NULL) + pfnt->CalcBreak(rcT.Width(), &pchBreak); + + // If it all fits, scan to the end. + + if (pchBreak == NULL) { + pchBreak = pszT; + while (*pchBreak != 0) + pchBreak++; + } + *pchBreak = 0; + + pect->SetText(pszT); + + // Start from the break next time around + + m_pszText += pchBreak - pszT; + delete pszT; + } + gpmfrmm->DrawFrame(false); +} + +void CutSceneForm::Layout(char *pszBitmap) +{ + Rect rcBitmap; + if (pszBitmap == NULL) { + rcBitmap.SetEmpty(); + } else { + + // Set the bitmap (which resizes its control) + + BitmapControl *pbmc = (BitmapControl *)GetControlPtr(kidcBitmap); + pbmc->SetBitmap(LoadRawBitmap(pszBitmap)); + pbmc->GetRect(&rcBitmap); + } + + ButtonControl *pbtn = (ButtonControl *)GetControlPtr(kidcOk); + Rect rcOK; + pbtn->GetRect(&rcOK); + + // Resize the Ecom control to take the remaining space above the "More..." button + + EcomTextControl *pect = (EcomTextControl *)GetControlPtr(kidcMessage); + Rect rcT; + pect->GetRect(&rcT); + rcT.top = rcBitmap.bottom + PcFromFc(1); + rcT.bottom = rcOK.top - PcFromFc(1); + pect->SetRect(&rcT); +} + +void CutSceneForm::OnControlSelected(word idc) +{ + if (idc != kidcOk) { + EcomTextControl *pect = (EcomTextControl *)GetControlPtr(kidcMessage); + pect->ShowAll(); + return; + } + + if (*m_pszText == 0) { + + // Hide the bitmap and force a repaint to avoid an ugly color flash + // if the palette changes upon return from the cut scene + + HideCutSceneBitmap(); + EndForm(); + } else { + More(); + } +} + +void CutSceneForm::OnTimer(long tCurrent) +{ + if (m_wf & kfFrmDoModal) { + + // invalidate the space for the next character. if it's done, + // make sure the More/Close button shows + + EcomTextControl *pect = (EcomTextControl *)GetControlPtr(kidcMessage); + if (pect->ShowMoreText()) { + ButtonControl *pbtn = (ButtonControl *)GetControlPtr(kidcOk); + if (*m_pszText == 0) + pbtn->SetText("OK"); + else + pbtn->SetText("More..."); + + pbtn->Show(true); + } + } +} + +bool CutSceneForm::OnPenEvent(Event *pevt) +{ + if (pevt->eType == penDownEvent) { + for (int n = m_cctl - 1; n >= 0; n--) { + // Is it on this control? + + Control *pctl = m_apctl[n]; + if (pctl->OnHitTest(pevt) >= 0) { + return Form::OnPenEvent(pevt); + } + } + + // Not on a control + + EcomTextControl *pect = (EcomTextControl *)GetControlPtr(kidcMessage); + pect->ShowAll(); + } + return Form::OnPenEvent(pevt); +} + +} // namespace wi \ No newline at end of file diff --git a/game/DebugHelpers.cpp b/game/DebugHelpers.cpp new file mode 100644 index 0000000..9a4f8d2 --- /dev/null +++ b/game/DebugHelpers.cpp @@ -0,0 +1,1836 @@ +#include "..\ht.h" + +#ifdef DEBUG_HELPERS + +#include + +#ifdef GDIPLUS +#include +using namespace Gdiplus; +struct GdiplusStartupInput s_siGdiPlus; +ULONG_PTR s_pulGdiPlusToken; +#endif + +const int kcySmallFont = 13; +const int kcyLabel = kcySmallFont - 2; +const int kcxDebugWindow = 320 + 250; +const int kcyDebugWindow = 480; +const int kcxLogWindow = 450; +const int kcyButton = 16; + +struct Toggle { + word vk; + char *sz; + bool f; + HWND hwnd; +}; + +#define kidShowSelectedUnitsOnly 0 +#define kidShowStateMachineLog 1 +#define kidShowHealth 2 +#define kidShowState 3 +#define kidShowAction 4 +#define kidShowMuntFlags 5 +#define kidShowUnitGroup 6 +#define kidShowFiringRange 7 +#define kidShowOccupied 8 +#define kidShowGobPointer 9 +#define kidShowMuntAggressiveness 10 +#define kidDebugSelectedStateMachine 11 +#define kidSuspendUpdates 12 +#define kidShowCoordinates 13 +#define kidShowDst 14 +#define kidShowUpdaters 15 +#define kidStep 16 +#define kidToggleInvincibility 17 + +static Toggle s_atgl[] = { + { 'T', "Only show selected units' info (t)", false, NULL }, + { 'L', "Show StateMachine log (l)", false, NULL }, + { 'H', "Show Unit health (h)", false, NULL }, + { 'S', "Show Unit state (s)", true, NULL }, + { 'A', "Show Mobile Unit actions (a)", true, NULL }, + { 'F', "Show Mobile Unit flags (f)", false, NULL }, + { 'G', "Show Unit group (g)", false, NULL }, + { 'I', "Show Unit firing range (i)", false, NULL }, + { 'O', "Show occupied tiles (o)", false, NULL }, + { 'B', "Show Gobs' memory address (b)", false, NULL }, + { 'E', "Show Unit aggressiveness (e)", true , NULL }, + { 'M', "Debug selected StateMachine (m)", false, NULL }, + { 'U', "Suspend Updates (u)", false, NULL }, + { 'C', "Show Coordinates (c)", false, NULL }, + { 'Q', "Show txDst, tyDst (q)", false, NULL }, + { 'Z', "Show Updaters", false, NULL }, + { VK_SPACE, "Single Step (spacebar)", false, (HWND)1 }, + { 'V', "Toggle invincibility", false, (HWND)1 }, +// { 'D', "Debug selected Unit (d)", false, NULL }, +}; + +class DebugViewer { +public: + DebugViewer(char *pszTitle, int x, int y, int cx, int cy); + ~DebugViewer(); + void ToggleVisibility(); + void ScrollTo(int dx, int dy); + + virtual void Update(); + virtual void Paint(HDC hdc); + virtual void OnRightClick(int x, int y); + +protected: + int m_xView, m_yView; + HWND m_hwnd; +}; + +class TriggerViewer : public DebugViewer { +public: + TriggerViewer(); + virtual void Paint(HDC hdc); + virtual void OnRightClick(int x, int y); +}; + +class UnitGroupViewer : public DebugViewer { +public: + UnitGroupViewer(); + virtual void Paint(HDC hdc); +}; + +class DelayedMessageViewer : public DebugViewer { +public: + DelayedMessageViewer(); + virtual void Update(); + virtual void Paint(HDC hdc); +}; + +class CommandQueueViewer : public DebugViewer { +public: + CommandQueueViewer(); + virtual void Update(); + virtual void Paint(HDC hdc); +}; + +static TriggerViewer *s_ptgrv; +static UnitGroupViewer *s_pugv; +static DelayedMessageViewer *s_pdmv; +static CommandQueueViewer *s_pcmdqv; + +HWND s_hwndDebug; +bool gfBack; +extern RECT g_rcOldWindowPos; +static int s_dyLabel; +HFONT s_hfntSmall; + +char Condition::s_szDebugHelpers[300]; +char QualifiedNumber::s_szDebugHelpers[200]; +char TriggerAction::s_szDebugHelpers[200]; +char UnitGroupAction::s_szDebugHelpers[200]; + +// Forward declarations + +LRESULT CALLBACK DebugWndProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp); +void UpdateControlStates(); +void DebugHelperCommandHandler(WPARAM wp, LPARAM lp); +void DrawLabel(HDC hdc, int x, int y, char *psz); + +void InitLog(); +void ExitLog(); +void DrawLog(HDC hdc); + +// + +void InitDebugHelpers() +{ + WNDCLASS wc; + memset(&wc, 0, sizeof(wc)); + wc.lpfnWndProc = DebugWndProc; + wc.hInstance = ghInst; + wc.hIcon = NULL; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH); + wc.lpszClassName = "HostileTakeoverDebug"; + RegisterClass(&wc); + + POINT ptT = { 0, 0 }; + HMONITOR hmon = MonitorFromPoint(ptT, MONITOR_DEFAULTTOPRIMARY); + + MONITORINFOEX mi; + mi.cbSize = sizeof(mi); + GetMonitorInfo(hmon, &mi); + + // Adjust so the client area is the size we want + + dword dwStyle = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CLIPCHILDREN; + + RECT rc; + SetRect(&rc, 0, 0, kcxDebugWindow, kcyDebugWindow); + AdjustWindowRect(&rc, dwStyle, false); + s_hwndDebug = CreateWindowEx(WS_EX_TOOLWINDOW, "HostileTakeoverDebug", "HT Debug", + dwStyle, mi.rcWork.left, mi.rcWork.bottom - (rc.bottom - rc.top), // g_rcOldWindowPos.top + 320 + (60 * 2 /* Graffiti area */) + 30 /* title bar */, + rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, ghInst, 0); + + // Create checkboxes/buttons + + int y = 0; + for (int i = 0; i < ARRAYSIZE(s_atgl); i++) { + s_atgl[i].hwnd = CreateWindow("BUTTON", s_atgl[i].sz, WS_VISIBLE | WS_CHILD | (s_atgl[i].hwnd == NULL ? BS_CHECKBOX : 0), + 320, y, 250, kcyButton, s_hwndDebug, (HMENU)i, NULL, NULL); + y += kcyButton; + } + + // Update their state + + UpdateControlStates(); + +#ifdef GDIPLUS + // Initialize GDI+ + + GdiplusStartup(&s_pulGdiPlusToken, &s_siGdiPlus, NULL); +#endif + + HFONT hfntSystem = (HFONT)GetStockObject(SYSTEM_FONT); + LOGFONT lf; + ::GetObject(hfntSystem, sizeof(lf), &lf); + lf.lfWeight = FW_LIGHT; + lf.lfHeight = 6; + strcpy(lf.lfFaceName, "Helv"); + s_hfntSmall = ::CreateFontIndirect(&lf); + + InitLog(); + s_ptgrv = new TriggerViewer(); + s_pugv = new UnitGroupViewer(); + s_pdmv = new DelayedMessageViewer(); + s_pcmdqv = new CommandQueueViewer(); +} + +void ExitDebugHelpers() +{ + DeleteObject(s_hfntSmall); + ExitLog(); + DestroyWindow(s_hwndDebug); +#ifdef GDIPLUS + GdiplusShutdown(s_pulGdiPlusToken); +#endif + delete s_ptgrv; + delete s_pugv; + delete s_pdmv; + delete s_pcmdqv; +} + +LRESULT CALLBACK DebugWndProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp) +{ + extern void DebugHelperCommandHandler(WPARAM wp, LPARAM lp); + + switch (wm) { + case WM_PAINT: + { + PAINTSTRUCT ps; + BeginPaint(hwnd, &ps); + EndPaint(hwnd, &ps); + } + break; + + case WM_COMMAND: + DebugHelperCommandHandler(wp, lp); + break; + + case WM_CLOSE: + ShowWindow(hwnd, false); + return 1; + + case WM_ERASEBKGND: + break; + } + return DefWindowProc(hwnd, wm, wp, lp); +} + +void DebugHelperKeyHandler(word vk) +{ + for (int i = 0; i < ARRAYSIZE(s_atgl); i++) { + if (vk == s_atgl[i].vk) { + DebugHelperCommandHandler(i, 0); + return; + } + } + + switch (vk) { + case VK_TAB: + { + extern void show(bool fBackBuffer); + extern void hide(); + if (IsWindowVisible(s_hwndDebug)) + hide(); + else + show(GetKeyState(VK_CONTROL) < 0); + } + break; + + case VK_F1: + s_ptgrv->ToggleVisibility(); + break; + + case VK_F2: + s_pugv->ToggleVisibility(); + break; + + case VK_F3: + s_pdmv->ToggleVisibility(); + break; + + case VK_F4: + s_pcmdqv->ToggleVisibility(); + break; + } +} + +void DebugHelperCommandHandler(WPARAM wp, LPARAM lp) +{ + extern bool gfSuspendUpdates; + extern bool gfSingleStep; + + int id = LOWORD(wp); + Toggle *ptgl = &s_atgl[id]; + ptgl->f = !ptgl->f; + + switch (id) { + case kidSuspendUpdates: + gfSuspendUpdates = ptgl->f; + break; + + case kidStep: + if (gfSuspendUpdates) + gfSingleStep = true; + break; + + case kidToggleInvincibility: + for (Gob *pgob = ggobm.GetFirstGob(); pgob != NULL; pgob = ggobm.GetNextGob(pgob)) { + if ((pgob->GetFlags() & (kfGobSelected | kfGobUnit)) != (kfGobSelected | kfGobUnit)) + continue; + + UnitGob *punt = (UnitGob *)pgob; + punt->SetUnitFlags(punt->GetUnitFlags() ^ kfUnitInvulnerable); + } + break; + + case kidShowStateMachineLog: + { + RECT rc; + GetWindowRect(s_hwndDebug, &rc); + int cx = rc.right - rc.left; + int cy = rc.bottom - rc.top; + if (ptgl->f) { + // Show + SetWindowPos(s_hwndDebug, NULL, 0, 0, cx + kcxLogWindow, cy, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); + } else { + // Hide + SetWindowPos(s_hwndDebug, NULL, 0, 0, cx - kcxLogWindow, cy, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); + } + } + break; + } + + UpdateControlStates(); +} + +void UpdateControlStates() +{ + for (int i = 0; i < ARRAYSIZE(s_atgl); i++) + SendMessage(s_atgl[i].hwnd, BM_SETCHECK, (WPARAM)(s_atgl[i].f ? BST_CHECKED : BST_UNCHECKED), 0); +} + +// Debug helpers are named in lowercase to facilite easy use + +void show(bool fBack) +{ + extern void paint(); + gfBack = fBack; + SetWindowText(s_hwndDebug, fBack ? "HT Debug - Back Buffer" : "HT Debug - Front Buffer"); + ShowWindow(s_hwndDebug, TRUE); + paint(); +} + +void hide() +{ + ShowWindow(s_hwndDebug, FALSE); +} + +void showtile(TCoord tx, TCoord ty) +{ + if (s_hwndDebug == NULL || !IsWindowVisible(s_hwndDebug)) + return; + + WCoord wxView, wyView; + gsim.GetViewPos(&wxView, &wyView); + short xView = PcFromUwc(wxView) & 0xfffe; + short yView = PcFromUwc(wyView) & 0xfffe; + int x = PcFromTc(tx) - xView; + int y = PcFromTc(ty) - yView; + + HBRUSH hbr = CreateSolidBrush(RGB(255, 255, 100)); + RECT rcT; + rcT.left = x; + rcT.top = y; + rcT.right = x + PcFromTc(1); + rcT.bottom = y + PcFromTc(1); + extern Display *gpdisp; + + HDC hdc = GetDC(s_hwndDebug); + RECT rcT2; + rcT2 = rcT; + rcT2.right = rcT2.left + 1; + FillRect(hdc, &rcT2, hbr); + rcT2 = rcT; + rcT2.left = rcT2.right - 1; + FillRect(hdc, &rcT2, hbr); + rcT2 = rcT; + rcT2.bottom = rcT2.top + 1; + FillRect(hdc, &rcT2, hbr); + rcT2 = rcT; + rcT2.top = rcT2.bottom - 1; + FillRect(hdc, &rcT2, hbr); + + ReleaseDC(s_hwndDebug, hdc); + + DeleteObject(hbr); +} + +// Side colors + +#ifdef GDIPLUS +static Gdiplus::Color s_aclr[] = { + Gdiplus::Color(0, 0, 0), + Gdiplus::Color(40, 0, 116, 255), + Gdiplus::Color(40, 255, 32, 0), + Gdiplus::Color(40, 255, 228, 0), + Gdiplus::Color(40, 104, 255, 255) +}; +#else +static COLORREF s_acr[] = { + RGB(0, 0, 0), + RGB(0, 116, 255), + RGB(255, 32, 0), + RGB(255, 228, 0), + RGB(104, 255, 255), +}; +#endif + +char *s_aszActions[] = { + "None", + "Guard", + "GuardVicinity", + "GuardArea", + "Move", + "Attack", + "HuntEnemies", +}; + +void paint() +{ + extern Display *gpdisp; + + if (s_hwndDebug == NULL || !IsWindowVisible(s_hwndDebug)) + return; + + ModeInfo mode; + gpdisp->GetMode(&mode); + DibBitmap *pbm = gfBack ? gpdisp->GetBackDib() : gpdisp->GetFrontDib(); + Size sizDib; + pbm->GetSize(&sizDib); + + // Create display surface with default palm palette + + struct BitmapInfo // bi + { + BITMAPINFOHEADER bmiHeader; + RGBQUAD bmiColors[256]; + }; + BitmapInfo bi; + memset(&bi, 0, sizeof(bi)); + bi.bmiHeader.biSize = sizeof(bi.bmiHeader); + bi.bmiHeader.biWidth = mode.cx; + bi.bmiHeader.biHeight = -mode.cy; + bi.bmiHeader.biPlanes = 1; + bi.bmiHeader.biBitCount = 8; + bi.bmiHeader.biCompression = BI_RGB; + bi.bmiHeader.biClrUsed = 256; + gpdisp->GetWindowsPalette(bi.bmiColors); + byte *pbT; + HBITMAP hbm = CreateDIBSection(gpdisp->m_hdcMem, (BITMAPINFO *)&bi, DIB_RGB_COLORS, (void **)&pbT, NULL, 0); + if (hbm == NULL) + return; + memcpy(pbT, pbm->GetBits(), sizDib.cx * sizDib.cy); + HBITMAP hbmSav = (HBITMAP)SelectObject(gpdisp->m_hdcMem, (HGDIOBJ)hbm); + + HDC hdc = gpdisp->m_hdcMem; + HDC hdcScreen = GetDC(s_hwndDebug); + Assert(hdcScreen != NULL); + s_dyLabel = 0; + + if (gsim.GetLevel() != NULL) { + // Set up a smaller font for displaying info + + HFONT hfntOld = (HFONT)SelectObject(hdc, s_hfntSmall); + HFONT hfntScreenOld = (HFONT)SelectObject(hdcScreen, s_hfntSmall); + SetBkMode(hdc, TRANSPARENT); + SetBkColor(hdcScreen, GetSysColor(COLOR_APPWORKSPACE)); + SetTextColor(hdcScreen, RGB(255, 255, 255)); + + // + + WCoord wxView, wyView; + gsim.GetViewPos(&wxView, &wyView); + short xView = PcFromUwc(wxView) & 0xfffe; + short yView = PcFromUwc(wyView) & 0xfffe; + wxView = WcFromUpc(xView); + wyView = WcFromUpc(yView); + + ::Size siz; + ggame.GetPlayfieldSize(&siz); + + ::Rect rcVisible; + rcVisible.left = xView; + rcVisible.top = yView; + rcVisible.right = xView + siz.cx; + rcVisible.bottom = yView + siz.cy; + + Gob *apgobVisible[512]; + int cpgobVisible = ggobm.FindGobs(&rcVisible, apgobVisible, ARRAYSIZE(apgobVisible), gsim.GetLevel()->GetFogMap()->GetMapPtr()); + + if (s_atgl[kidShowOccupied].f) { + HBRUSH hbrMunt = CreateSolidBrush(RGB(0, 0, 0)); + HBRUSH hbrStructure = CreateSolidBrush(RGB(0, 255, 255)); + + Size sizT; + ggame.GetPlayfieldSize(&sizT); + int ctxView = sizT.cx / gcxTile + 1; + int ctyView = sizT.cy / gcyTile + 1; + TerrainMap *ptrmap = gsim.GetLevel()->GetTerrainMap(); + + TCoord txView = TcFromWc(wxView); + TCoord tyView = TcFromWc(wyView); + for (TCoord ty = tyView; ty < tyView + ctyView; ty++) { + for (TCoord tx = txView; tx < txView + ctxView; tx++) { + int x = PcFromTc(tx) - xView; + int y = PcFromTc(ty) - yView; + if (ptrmap->TestFlags(tx, ty, 1, 1, kbfMobileUnit)) { + RECT rcT; + rcT.left = x; + rcT.top = y; + rcT.right = x + 2; + rcT.bottom = y + 2; + FillRect(hdc, &rcT, hbrMunt); + } + if (ptrmap->TestFlags(tx, ty, 1, 1, kbfStructure)) { + RECT rcT; + rcT.left = x + 4; + rcT.top = y; + rcT.right = x + 6; + rcT.bottom = y + 2; + FillRect(hdc, &rcT, hbrStructure); + } + } + } + + DeleteObject(hbrStructure); + DeleteObject(hbrMunt); + } + + if (s_atgl[kidShowFiringRange].f) { + for (int i = 0; i < cpgobVisible; i++) { + Gob *pgob = apgobVisible[i]; + if ((pgob->GetFlags() & (kfGobUnit | kfGobActive)) != (kfGobUnit | kfGobActive)) + continue; + + UnitGob *punt = (UnitGob *)pgob; + + TCoord tcRange = punt->GetConsts()->tcFiringRange; + if (tcRange == 0) + continue; + if (s_atgl[kidShowSelectedUnitsOnly].f && !(pgob->GetFlags() & kfGobSelected)) + continue; + +#ifdef GDIPLUS + Graphics gr(hdc); + SolidBrush br(s_aclr[pgob->GetSide()]); + gr.FillRectangle(&br, rc.left, rc.top, rc.Width(), rc.Height()); +#else + HBRUSH hbr = CreateSolidBrush(s_acr[pgob->GetSide()]); + HBRUSH hbrOld = (HBRUSH)SelectObject(hdc, hbr); + + TPoint tpt; + pgob->GetTilePosition(&tpt); + TRect trc; + trc.Set(tpt.tx - tcRange, tpt.ty - tcRange, tpt.tx + tcRange + 1, tpt.ty + tcRange + 1); + + for (TCoord ty = trc.top; ty < trc.bottom; ty++) { + for (TCoord tx = trc.left; tx < trc.right; tx++) { + TCoord dtx = abs(tx - tpt.tx); + TCoord dty = abs(ty - tpt.ty); + if (gmpDistFromDxy[dtx][dty] <= tcRange) { + WRect wrc; + wrc.left = WcFromTc(tx); + wrc.right = wrc.left + kwcTile; + wrc.top = WcFromTc(ty); + wrc.bottom = wrc.top + kwcTile; + wrc.Offset(-wxView, -wyView); + ::Rect rc; + rc.FromWorldRect(&wrc); + + RECT rcT; + + if (gmpDistFromDxy[abs(tx - tpt.tx - 1)][dty] > tcRange) { + rcT.left = rc.left; + rcT.top = rc.top; + rcT.right = rc.left + 2; + rcT.bottom = rc.bottom; + FillRect(hdc, &rcT, hbr); + } + if (gmpDistFromDxy[abs(tx - tpt.tx + 1)][dty] > tcRange) { + rcT.left = rc.right - 2; + rcT.top = rc.top; + rcT.right = rc.right; + rcT.bottom = rc.bottom; + FillRect(hdc, &rcT, hbr); + } + if (gmpDistFromDxy[dtx][abs(ty - tpt.ty - 1)] > tcRange) { + rcT.left = rc.left; + rcT.top = rc.top; + rcT.right = rc.right; + rcT.bottom = rc.top + 2; + FillRect(hdc, &rcT, hbr); + } + if (gmpDistFromDxy[dtx][abs(ty - tpt.ty + 1)] > tcRange) { + rcT.left = rc.left; + rcT.top = rc.bottom - 2; + rcT.right = rc.right; + rcT.bottom = rc.bottom; + FillRect(hdc, &rcT, hbr); + } + } + } + } + + SelectObject(hdc, hbrOld); + DeleteObject(hbr); +#endif + } + } + + if (s_atgl[kidShowHealth].f) { + for (int i = 0; i < cpgobVisible; i++) { + Gob *pgob = apgobVisible[i]; + + if (!(pgob->GetFlags() & kfGobUnit)) + continue; + if (s_atgl[kidShowSelectedUnitsOnly].f && !(pgob->GetFlags() & kfGobSelected)) + continue; + + WRect wrc; + pgob->GetUIBounds(&wrc); + int x = PcFromWc(wrc.left - wxView); + int y = PcFromWc(wrc.top - wyView); + UnitGob *punt = (UnitGob *)pgob; + + fix fxHealth = punt->GetHealth(); + fix fxHealthMax = punt->GetConsts()->GetArmorStrength(); + char szT[30]; + sprintf(szT, "%d/%d", fxtoi(fxHealth), fxtoi(fxHealthMax)); + DrawLabel(hdc, x, y, szT); + } + s_dyLabel += kcyLabel; + } + + if (s_atgl[kidShowState].f) { + for (int i = 0; i < cpgobVisible; i++) { + extern char *gaszStateNames[]; + Gob *pgob = apgobVisible[i]; + + if (!(pgob->GetFlags() & kfGobUnit)) + continue; + if (s_atgl[kidShowSelectedUnitsOnly].f && !(pgob->GetFlags() & kfGobSelected)) + continue; + + WRect wrc; + pgob->GetUIBounds(&wrc); + int x = PcFromWc(wrc.left - wxView); + int y = PcFromWc(wrc.top - wyView); + + UnitGob *punt = (UnitGob *)pgob; + DrawLabel(hdc, x, y, gaszStateNames[punt->m_st]); + } + s_dyLabel += kcyLabel; + } + + if (s_atgl[kidShowAction].f) { + for (int i = 0; i < cpgobVisible; i++) { + Gob *pgob = apgobVisible[i]; + + if (!(pgob->GetFlags() & kfGobMobileUnit)) + continue; + if (s_atgl[kidShowSelectedUnitsOnly].f && !(pgob->GetFlags() & kfGobSelected)) + continue; + + WRect wrc; + pgob->GetUIBounds(&wrc); + int x = PcFromWc(wrc.left - wxView); + int y = PcFromWc(wrc.top - wyView); + + MobileUnitGob *pmunt = (MobileUnitGob *)pgob; + DrawLabel(hdc, x, y, s_aszActions[pmunt->m_mua]); + } + s_dyLabel += kcyLabel; + } + + if (s_atgl[kidShowMuntFlags].f) { + for (int i = 0; i < cpgobVisible; i++) { + Gob *pgob = apgobVisible[i]; + + if (!(pgob->GetFlags() & kfGobMobileUnit)) + continue; + if (s_atgl[kidShowSelectedUnitsOnly].f && !(pgob->GetFlags() & kfGobSelected)) + continue; + + MobileUnitGob *pmunt = (MobileUnitGob *)pgob; + word wfMunt = pmunt->GetMobileUnitFlags(); + char szT[40]; + szT[0] = 0; + if (wfMunt & kfMuntChaseEnemies) + strcat(szT, "c,"); + if (wfMunt & kfMuntReturnFire) + strcat(szT, "rf,"); + if (wfMunt & kfMuntRunAwayWhenHit) + strcat(szT, "ra,"); + if (wfMunt & kfMuntMoveWait) + strcat(szT, "mw,"); + if (wfMunt & kfMuntMoveWaitingNearby) + strcat(szT, "mwn,"); + if (wfMunt & kfMuntPathPending) + strcat(szT, "pp,"); + if (wfMunt & kfMuntCommandPending) + strcat(szT, "cp,"); + if (wfMunt & kfMuntAttackEnemiesWhenMoving) + strcat(szT, "am,"); + if (wfMunt & kfMuntAttackEnemiesWhenGuarding) + strcat(szT, "ag,"); + if (wfMunt & kfMuntStuck) + strcat(szT, "stk,"); + if (wfMunt & kfMuntAtReplicatorInput) + strcat(szT, "rep,"); + + // Trim trialing ',' + + int cch = strlen(szT); + if (cch > 0) { + cch--; + szT[cch] = 0; + } + + if (cch > 0) { + WRect wrc; + pgob->GetUIBounds(&wrc); + int x = PcFromWc(wrc.left - wxView); + int y = PcFromWc(wrc.top - wyView); + + DrawLabel(hdc, x, y, szT); + } + } + s_dyLabel += kcyLabel; + } + + if (s_atgl[kidShowMuntAggressiveness].f) { + for (int i = 0; i < cpgobVisible; i++) { + Gob *pgob = apgobVisible[i]; + + if (!(pgob->GetFlags() & kfGobMobileUnit)) + continue; + if (s_atgl[kidShowSelectedUnitsOnly].f && !(pgob->GetFlags() & kfGobSelected)) + continue; + + MobileUnitGob *pmunt = (MobileUnitGob *)pgob; + word wfMunt = pmunt->GetMobileUnitFlags() & kfMuntAggressivenessBits; + char *pszT; + switch (pmunt->GetMobileUnitFlags() & kfMuntAggressivenessBits) { + case (kfMuntRunAwayWhenHit): + pszT = "Coward"; + break; + + case (kfMuntReturnFire | kfMuntStayPut): + pszT = "SelfDefense"; + break; + + case (kfMuntReturnFire | kfMuntAttackEnemiesWhenGuarding | kfMuntAttackEnemiesWhenMoving): + pszT = "Defender"; + break; + + case (kfMuntReturnFire | kfMuntAttackEnemiesWhenGuarding | kfMuntAttackEnemiesWhenMoving | kfMuntChaseEnemies): + pszT = "Pitbull"; + break; + + case (kfMuntReturnFire | kfMuntAttackEnemiesWhenGuarding): + pszT = "Player"; + break; + + default: + pszT = "?unknown?"; + break; + } + + WRect wrc; + pgob->GetUIBounds(&wrc); + int x = PcFromWc(wrc.left - wxView); + int y = PcFromWc(wrc.top - wyView); + + DrawLabel(hdc, x, y, pszT); + } + s_dyLabel += kcyLabel; + } + + if (s_atgl[kidShowUnitGroup].f) { + for (int i = 0; i < cpgobVisible; i++) { + Gob *pgob = apgobVisible[i]; + + if (!(pgob->GetFlags() & kfGobMobileUnit)) + continue; + if (s_atgl[kidShowSelectedUnitsOnly].f && !(pgob->GetFlags() & kfGobSelected)) + continue; + + WRect wrc; + pgob->GetUIBounds(&wrc); + int x = PcFromWc(wrc.left - wxView); + int y = PcFromWc(wrc.top - wyView); + + // See if this unit is a member of any of the unit groups + + UnitGroup *pug = gsim.GetLevel()->GetUnitGroupMgr()->GetUnitGroup(pgob->GetId()); + if (pug != NULL) { + char *pszName = pug->GetName(); + if (pszName != NULL) + DrawLabel(hdc, x, y, pszName); + } + } + s_dyLabel += kcyLabel; + } + + if (s_atgl[kidShowGobPointer].f) { + for (int i = 0; i < cpgobVisible; i++) { + Gob *pgob = apgobVisible[i]; + + if (s_atgl[kidShowSelectedUnitsOnly].f && !(pgob->GetFlags() & kfGobSelected)) + continue; + + WRect wrc; + pgob->GetUIBounds(&wrc); + int x = PcFromWc(wrc.left - wxView); + int y = PcFromWc(wrc.top - wyView); + + char szT[30]; + sprintf(szT, "%lx", pgob); + DrawLabel(hdc, x, y, szT); + } + s_dyLabel += kcyLabel; + } + + if (s_atgl[kidShowCoordinates].f) { + for (int i = 0; i < cpgobVisible; i++) { + Gob *pgob = apgobVisible[i]; + + if (s_atgl[kidShowSelectedUnitsOnly].f && !(pgob->GetFlags() & kfGobSelected)) + continue; + + WPoint wpt; + pgob->GetPosition(&wpt); + char szT[30]; + sprintf(szT, "$%04lx,$%04lx", wpt.wx, wpt.wy); + + WRect wrc; + pgob->GetUIBounds(&wrc); + int x = PcFromWc(wrc.left - wxView); + int y = PcFromWc(wrc.top - wyView); + DrawLabel(hdc, x, y, szT); + } + s_dyLabel += kcyLabel; + } + + if (s_atgl[kidShowDst].f) { + for (int i = 0; i < cpgobVisible; i++) { + Gob *pgob = apgobVisible[i]; + + if (s_atgl[kidShowSelectedUnitsOnly].f && !(pgob->GetFlags() & kfGobSelected)) + continue; + + if (!(pgob->GetFlags() & kfGobMobileUnit)) + continue; + MobileUnitGob *pmunt = (MobileUnitGob *)pgob; + + char szT[30]; + sprintf(szT, "$%02lx,$%02lx", pmunt->m_txDst, pmunt->m_tyDst); + + WRect wrc; + pgob->GetUIBounds(&wrc); + int x = PcFromWc(wrc.left - wxView); + int y = PcFromWc(wrc.top - wyView); + DrawLabel(hdc, x, y, szT); + } + s_dyLabel += kcyLabel; + } + + if (s_atgl[kidShowUpdaters].f) { + for (int i = 0; i < cpgobVisible; i++) { + Gob *pgob = apgobVisible[i]; + + if (s_atgl[kidShowSelectedUnitsOnly].f && !(pgob->GetFlags() & kfGobSelected)) + continue; + + if (!(pgob->GetFlags() & kfGobStateMachine)) + continue; + + if (pgob->m_unvl.m_cDecrements != 0) + continue; + + Rect rcBounds; + pgob->GetClippingBounds(&rcBounds); + rcBounds.Offset(-PcFromUwc(wxView), -PcFromUwc(wyView)); + RECT rc; + rc.left = rcBounds.left; + rc.top = rcBounds.top; + rc.right = rcBounds.right; + rc.bottom = rcBounds.bottom; + HBRUSH hbrWhite = (HBRUSH)GetStockObject(WHITE_BRUSH); + + RECT rcT; + rcT = rc; + rcT.bottom = rcT.top + 2; + FillRect(hdc, &rcT, hbrWhite); + + rcT = rc; + rcT.right = rc.left + 2; + FillRect(hdc, &rcT, hbrWhite); + + rcT = rc; + rcT.left = rcT.right - 2; + FillRect(hdc, &rcT, hbrWhite); + + rcT = rc; + rcT.top = rcT.bottom - 2; + FillRect(hdc, &rcT, hbrWhite); + } + } + +#if 0 + // UNDONE: not acted on anywhere yet + + bool fDebugUnit = s_atgl[kidDebugSelectedUnit].f; + for (Gob *pgob = ggobm.GetFirstGob(); pgob != NULL; pgob = ggobm.GetNextGob(pgob)) { + dword ff = pgob->GetFlags(); + if (fDebugUnit && (ff & kfGobSelected)) + ff |= kfGobDebug; + else + ff &= ~kfGobDebug; + pgob->SetFlags(ff); + } +#endif + + // Causes the StateMachine to break on each received message, excluding kmidReserved* (e.g., Update) + + bool fDebugSM = s_atgl[kidDebugSelectedStateMachine].f; + for (Gob *pgob = ggobm.GetFirstGob(); pgob != NULL; pgob = ggobm.GetNextGob(pgob)) { + if (fDebugSM && (pgob->GetFlags() & kfGobSelected)) + pgob->EnableDebug(true); + else + pgob->EnableDebug(false); + } + +#define Print(sz) TextOut(hdcScreen, x, y += kcySmallFont, sz, strlen(sz)) + + int x = 322; + int y = kcyButton * ARRAYSIZE(s_atgl); + + char szT[200]; + sprintf(szT, "Update: %ld, Time: %ld sec, Gobs: %d ", + gsim.GetUpdateCount(), gsim.GetTickCount() / 100, ggobm.GetGobCount()); + Print(szT); + Print(""); + + Player *pplr = NULL; + while (true) { + pplr = gplrm.GetNextPlayer(pplr); + if (pplr == NULL) + break; + sprintf(szT, "Side %d", pplr->GetSide()); + Print(szT); + sprintf(szT, " credits/capacity: %d/%d ", pplr->GetCredits(), pplr->GetCapacity()); + Print(szT); + sprintf(szT, " power demand/supply: %d/%d ", pplr->GetPowerDemand(), pplr->GetPowerSupply()); + Print(szT); + } + + SelectObject(hdc, hfntOld); + SelectObject(hdcScreen, hfntScreenOld); + + { + void UpdateLog(); + UpdateLog(); + } + } + + // Draw to screen, default scale + + int cxMap = mode.cx * mode.nScale; + int cyMap = mode.cy * mode.nScale; + StretchBlt(hdcScreen, 0, 0, cxMap, cyMap, gpdisp->m_hdcMem, 0, 0, mode.cx, mode.cy, SRCCOPY); + + HBITMAP hbmDelete = (HBITMAP)SelectObject(gpdisp->m_hdcMem, (HGDIOBJ)hbmSav); + DeleteObject((HGDIOBJ)hbmDelete); + + ReleaseDC(s_hwndDebug, hdcScreen); + +} + +void DrawLabel(HDC hdc, int x, int y, char *psz) +{ + int cch = strlen(psz); + SetTextColor(hdc, RGB(0, 0, 0)); + TextOut(hdc, x + 1, y + 1 + s_dyLabel, psz, cch); + SetTextColor(hdc, RGB(255, 255, 255)); + TextOut(hdc, x, y + s_dyLabel, psz, cch); +} + +// +// Log +// + +const int kcleMax = 5000; + +const word kfLeString = 0x0001; +const word kfLeMessage = 0x0002; +const word kfLeStateChange = 0x0004; + +struct LogEntry { // le + LogEntry *pleNext; + LogEntry *plePrev; + word wf; + long cUpdate; + StateMachineId smid; + Message msg; + State stOld; + State stNew; + char sz[200]; +}; + +// Newest entries go to the head of the list + +static LogEntry *s_pleHead, *s_pleTail; +static int s_cle; + +void AddLogEntry(LogEntry *ple); + +// +extern char *gaszMessageNames[]; +extern char *gaszStateNames[]; + +void Log(StateMachine *psm, Message *pmsg) +{ + LogEntry *ple = new LogEntry; + memset(ple, 0, sizeof(LogEntry)); + ple->wf = kfLeMessage; + ple->cUpdate = gsim.GetUpdateCount(); + ple->smid = gsmm.GetId(psm); + ple->msg = *pmsg; + + char szT[100]; + if (pmsg->smidSender == ksmidNull) + strcpy(szT, "null"); + else if (psm == gsmm.GetStateMachine(pmsg->smidSender)) + strcpy(szT, "self"); + else { + StateMachine *psm = gsmm.GetStateMachine(pmsg->smidSender); + char *pszName = psm == NULL ? "GONE" : psm->GetName(); + sprintf(szT, "%s.%lx.%d", pszName, gsmm.GetStateMachine(pmsg->smidSender), pmsg->smidSender); + } + char szT2[100]; + if (pmsg->smidSender == ksmidNull) + szT2[0] = 0; + else + sprintf(szT2, " (from %s)", szT); + sprintf(ple->sz, "M u: %ld, %s.%lx.%d recv %s/%s%s", + ple->cUpdate, psm->GetName(), psm, ple->smid, gaszStateNames[psm->m_st], + gaszMessageNames[pmsg->mid], szT2); + AddLogEntry(ple); +} + +void Log(StateMachine *psm, State stOld, State stNew) +{ + LogEntry *ple = new LogEntry; + memset(ple, 0, sizeof(LogEntry)); + ple->wf = kfLeStateChange; + ple->cUpdate = gsim.GetUpdateCount(); + ple->smid = gsmm.GetId(psm); + ple->stOld = stOld; + ple->stNew = stNew; + + sprintf(ple->sz, "S u: %ld, %s.%lx.%d set to %s (prev %s)", + ple->cUpdate, psm->GetName(), psm, ple->smid, + gaszStateNames[stNew], gaszStateNames[stOld]); + AddLogEntry(ple); +} + +void Log(char *psz) +{ + LogEntry *ple = new LogEntry; + memset(ple, 0, sizeof(LogEntry)); + ple->wf = kfLeString; + + AddLogEntry(ple); +} + +void AddLogEntry(LogEntry *ple) +{ + ple->pleNext = s_pleHead; + if (s_pleHead != NULL) + s_pleHead->plePrev = ple; + else + s_pleTail = ple; + s_pleHead = ple; + + if (s_cle >= kcleMax) { + LogEntry *pleDel = s_pleTail; + s_pleTail->plePrev->pleNext = NULL; + s_pleTail = s_pleTail->plePrev; + delete pleDel; + } else { + s_cle++; + } +} + +void InitLog() +{ + s_pleHead = NULL; +} + +void ClearLog() +{ + LogEntry *ple = s_pleHead; + while (ple != NULL) { + LogEntry *pleNext = ple->pleNext; + delete ple; + ple = pleNext; + } + s_pleHead = NULL; + s_pleTail = NULL; + s_cle = 0; +} + +// For immediate window + +void OutputLog(int c) +{ + for (LogEntry *ple = s_pleHead; ple != NULL; ple = ple->pleNext) { + if (c-- <= 0) + break; + OutputDebugString(ple->sz); + OutputDebugString("\n"); + } +} + +void ExitLog() +{ + ClearLog(); +} + +void UpdateLog() +{ + if (!s_atgl[kidShowStateMachineLog].f) + return; + + HDC hdc = GetDC(s_hwndDebug); + HFONT hfntOld = (HFONT)SelectObject(hdc, s_hfntSmall); + + DrawLog(hdc); + + SelectObject(hdc, hfntOld); + ReleaseDC(s_hwndDebug, hdc); +} + +void DrawLog(HDC hdc) +{ + int x = kcxDebugWindow + 2; + int y = kcyDebugWindow - kcySmallFont; + + for (LogEntry *ple = s_pleHead; ple != NULL && y >= 0; ple = ple->pleNext) { + + // Filter unselected Gobs if we're in that mode + + if (s_atgl[kidShowSelectedUnitsOnly].f) { + Gob *pgob = ggobm.GetGob(ple->smid); + if (pgob == NULL || !(pgob->GetFlags() & kfGobSelected)) + continue; + } + + RECT rc; + rc.left = x; + rc.top = y; + rc.right = x + kcxLogWindow; + rc.bottom = y + kcySmallFont; + ExtTextOut(hdc, x, y, ETO_OPAQUE, &rc, ple->sz, strlen(ple->sz), NULL); + y -= kcySmallFont; + } + + // Clear any unused space above the displayed log entries + + if (y > 0) { + RECT rc; + rc.left = kcxDebugWindow; + rc.top = 0; + rc.right = rc.left + kcxLogWindow; + rc.bottom = y + kcySmallFont; + + HBRUSH hbr = CreateSolidBrush(GetBkColor(hdc)); + FillRect(hdc, &rc, hbr); + DeleteObject(hbr); + } +} + +// +// DebugViewer implementation +// + +DebugViewer::DebugViewer(char *pszTitle, int x, int y, int cx, int cy) +{ + LRESULT CALLBACK DebugViewerWndProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp); + static char s_szClass[] = "HostileTakeoverDebugViewer"; + static bool s_fRegistered = false; + + if (!s_fRegistered) { + WNDCLASS wc; + memset(&wc, 0, sizeof(wc)); + wc.lpfnWndProc = DebugViewerWndProc; + wc.hInstance = ghInst; + wc.hIcon = NULL; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH); + wc.lpszClassName = s_szClass; + RegisterClass(&wc); + s_fRegistered = true; + } + + POINT ptT = { 0, 0 }; + HMONITOR hmon = MonitorFromPoint(ptT, MONITOR_DEFAULTTOPRIMARY); + + MONITORINFOEX mi; + mi.cbSize = sizeof(mi); + GetMonitorInfo(hmon, &mi); + + // Right-align for negative x's + + x = x < 0 ? mi.rcWork.right + x : mi.rcWork.left + x; + + dword dwStyle = WS_OVERLAPPED | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX; + m_hwnd = CreateWindowEx(0, s_szClass, pszTitle, + dwStyle, x, y, cx, cy, NULL, NULL, ghInst, 0); + SetWindowLong(m_hwnd, GWL_USERDATA, (LONG)this); + + m_xView = m_yView = 0; +} + +DebugViewer::~DebugViewer() +{ + DestroyWindow(m_hwnd); +} + +void DebugViewer::OnRightClick(int x, int y) +{ +} + +LRESULT CALLBACK DebugViewerWndProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp) +{ + static bool s_fMouseDown; + static int s_xMouseDown; + static int s_yMouseDown; + + DebugViewer *pdbgv = (DebugViewer *)GetWindowLong(hwnd, GWL_USERDATA); + +// extern void DebugHelperCommandHandler(WPARAM wp, LPARAM lp); + + switch (wm) { + case WM_PAINT: + { + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hwnd, &ps); + pdbgv->Paint(hdc); + EndPaint(hwnd, &ps); + } + break; + +// case WM_COMMAND: +// DebugHelperCommandHandler(wp, lp); +// break; + + case WM_LBUTTONDOWN: + s_fMouseDown = true; + s_xMouseDown = GET_X_LPARAM(lp); + s_yMouseDown = GET_Y_LPARAM(lp); + SetCapture(hwnd); + break; + + case WM_MOUSEMOVE: + if (s_fMouseDown) { + int dx = s_xMouseDown - GET_X_LPARAM(lp); + int dy = s_yMouseDown - GET_Y_LPARAM(lp); + pdbgv->ScrollTo(dx, dy); + s_xMouseDown = GET_X_LPARAM(lp); + s_yMouseDown = GET_Y_LPARAM(lp); + } + break; + + case WM_LBUTTONUP: + s_fMouseDown = false; + ReleaseCapture(); + break; + + case WM_RBUTTONDOWN: + pdbgv->OnRightClick(GET_X_LPARAM(lp), GET_Y_LPARAM(lp)); + break; + + case WM_CLOSE: + ShowWindow(hwnd, false); + return 1; + + case WM_ERASEBKGND: + if (gsim.GetLevel() != NULL) + return 1; + break; + } + return DefWindowProc(hwnd, wm, wp, lp); +} + +void DebugViewer::ToggleVisibility() +{ + ShowWindow(m_hwnd, !IsWindowVisible(m_hwnd)); +} + +void DebugViewer::Update() +{ + InvalidateRect(m_hwnd, NULL, false); +} + +void DebugViewer::ScrollTo(int dx, int dy) +{ + m_xView += dx; + if (m_xView < 0) + m_xView = 0; + m_yView += dy; + if (m_yView < 0) + m_yView = 0; + Update(); +} + +void DebugViewer::Paint(HDC hdc) +{ +} + +// +// Trigger Viewer implementation +// + +const int kcxTriggerWindow = 650; +const int kcyTriggerWindow = 700; + +TriggerViewer::TriggerViewer() : + DebugViewer("HT Triggers", 330, 0, kcxTriggerWindow, kcyTriggerWindow) +{ +} + +void UpdateTriggerViewer() +{ + s_ptgrv->Update(); +} + +void TriggerViewer::Paint(HDC hdc) +{ + Level *plvl = gsim.GetLevel(); + if (plvl == NULL) + return; + + HFONT hfntOld = (HFONT)SelectObject(hdc, s_hfntSmall); + HBRUSH hbr = (HBRUSH)GetStockObject(GRAY_BRUSH); + + int x = -m_xView; + int y = -m_yView; + RECT rc; + + // Display per-side triggers in the per-side specified order + + TriggerMgr *ptgrm = plvl->GetTriggerMgr(); + for (Side side = ksideNeutral; side < kcSides; side++) { + byte *pntgr = &ptgrm->m_mpSide2nTrigger[side][0]; + for (; *pntgr != 0xff; pntgr++) { + Trigger *ptgr = &ptgrm->m_atgr[*pntgr]; + TriggerAction *pactn = ptgr->m_apactnLast[side]; + + for (Condition *pcdn = ptgr->m_pcdn; pcdn != NULL; pcdn = pcdn->m_pcdnNext) { + char szT[500]; + sprintf(szT, "%s", pcdn->ToString()); + Assert(strlen(szT) < sizeof(szT)); + + COLORREF clr; + // Armed? (ready to be triggered) + + if (ptgr->m_afArmed[side]) { + + // Yes. Condition satisfied? + + if (pcdn->SafeIsTrue(side)) + clr = RGB(164, 0, 0); // yes + else + clr = RGB(0, 0, 0); + } else { + + // Disarmed (already triggered) + clr = RGB(255, 0, 0); + } + + // Action in progress + + if (pactn != NULL) + clr = RGB(0, 255, 0); + + SetTextColor(hdc, clr); + + rc.left = x; + rc.top = y; + rc.right = kcxTriggerWindow; + rc.bottom = y + kcySmallFont; + ExtTextOut(hdc, x + 2, y, ETO_OPAQUE, &rc, szT, strlen(szT), NULL); + + y += kcySmallFont; + } + + if (pactn != NULL) { + char szT[200]; + sprintf(szT, "> %s", pactn->ToString()); + Assert(strlen(szT) < sizeof(szT)); + SetTextColor(hdc, RGB(0, 164, 0)); + + rc.top = y; + rc.bottom = y + kcySmallFont; + ExtTextOut(hdc, x + 2, y, ETO_OPAQUE, &rc, szT, strlen(szT), NULL); + y += kcySmallFont; + } + + rc.left = 0; + rc.top = y; + rc.right = kcxTriggerWindow; + rc.bottom = y + 2; + FillRect(hdc, &rc, hbr); + + y += 2; + } + } + + if (y < kcyTriggerWindow) { + rc.bottom = kcyTriggerWindow; + FillRect(hdc, &rc, hbr); + } + + SelectObject(hdc, hfntOld); +} + +void TriggerViewer::OnRightClick(int xClick, int yClick) +{ + Level *plvl = gsim.GetLevel(); + if (plvl == NULL) + return; + + int x = -m_xView; + int y = -m_yView; + + // Display per-side triggers in the per-side specified order + + TriggerMgr *ptgrm = plvl->GetTriggerMgr(); + for (Side side = ksideNeutral; side < kcSides; side++) { + byte *pntgr = &ptgrm->m_mpSide2nTrigger[side][0]; + for (; *pntgr != 0xff; pntgr++) { + Trigger *ptgr = &ptgrm->m_atgr[*pntgr]; + TriggerAction *pactn = ptgr->m_apactnLast[side]; + + for (Condition *pcdn = ptgr->m_pcdn; pcdn != NULL; pcdn = pcdn->m_pcdnNext) + y += kcySmallFont; + + if (pactn != NULL) + y += kcySmallFont; + + if (yClick <= y) { + ptgr->Execute(side, true); + return; + } + + y += 2; + } + } +} + +// +// Unit Group Viewer implementation +// + +const int kcxUnitGroupWindow = 300; +const int kcyUnitGroupWindow = 900; + +UnitGroupViewer::UnitGroupViewer() : + DebugViewer("HT Unit Groups", -kcxUnitGroupWindow, 0, kcxUnitGroupWindow, kcyUnitGroupWindow) +{ +} + +void UpdateUnitGroupViewer() +{ + s_pugv->Update(); +} + +void UnitGroupViewer::Paint(HDC hdc) +{ + Level *plvl = gsim.GetLevel(); + if (plvl == NULL) + return; + + HFONT hfntOld = (HFONT)SelectObject(hdc, s_hfntSmall); + HBRUSH hbr = (HBRUSH)GetStockObject(GRAY_BRUSH); + + int x = -m_xView; + int y = -m_yView; + RECT rc; + rc.left = x; + rc.right = kcxUnitGroupWindow; + + UnitGroupMgr *pugm = plvl->GetUnitGroupMgr(); + int cug = pugm->m_cug; + for (int i = 0; i < cug; i++) { + char szT[500]; + UnitGroup *pug = &pugm->m_aug[i]; + + COLORREF clr; + + word wf = pug->GetFlags(); + if (wf & kfUgActive) { + if (wf & kfUgNeedsUnit) + clr = RGB(0, 164, 0); + else + clr = RGB(0, 255, 0); + } else { + if (wf & kfUgActivatedBefore) + clr = RGB(164, 0, 0); + else + clr = RGB(0, 0, 0); + } + + SetTextColor(hdc, clr); + + char szT2[500]; + szT2[0] = 0; + + if (wf & kfUgRandomGroup) { + if (szT2[0] != 0) + strcat(szT2, ", "); + strcat(szT2, "random"); + } + + if (wf & kfUgCreateAtLevelLoad) { + if (szT2[0] != 0) + strcat(szT2, ", "); + strcat(szT2, "level load"); + } + + if (wf & kfUgReplaceGroup) { + if (szT2[0] != 0) + strcat(szT2, ", "); + strcat(szT2, "replace"); + } + + if (wf & kfUgSpawn) { + if (szT2[0] != 0) + strcat(szT2, ", "); + strcat(szT2, "spawn"); + } + + if (wf & kfUgLoopForever) { + if (szT2[0] != 0) + strcat(szT2, ", "); + strcat(szT2, "loop"); + } + + if (!(wf & kfUgNotRecentlyActivated)) { + if (szT2[0] != 0) + strcat(szT2, ", "); + strcat(szT2, "RA"); + } + + sprintf(szT, "%s (%s)", pug->GetName(), szT2); + + rc.top = y; + rc.bottom = y + kcySmallFont; + ExtTextOut(hdc, x + 2, y, ETO_OPAQUE, &rc, szT, strlen(szT), NULL); + y += kcySmallFont; + + if (pug->GetFlags() & kfUgActive) { + // Show all the group's members + + int cule = pug->GetUnitCount(); + UnitListEntry *ple = pug->GetUnitList(); + for (int iule = 0; iule < cule; iule++, ple++) { + SetTextColor(hdc, clr); + + rc.left = x; + rc.top = y; + rc.right = kcxUnitGroupWindow; + rc.bottom = y + kcySmallFont; + if (!(ple->bf & kfUleBuilt)) { + sprintf(szT, "%s (building...)", gapuntc[ple->ut]->szName); + } else { + MobileUnitGob *pmunt = (MobileUnitGob *)ggobm.GetGob(ple->gid); + char *pszT; + if (pmunt == NULL) { + SetTextColor(hdc, RGB(255, 0, 0)); + pszT = "dead"; + } else { + if (pmunt->GetFlags() & kfGobMobileUnit) + pszT = s_aszActions[pmunt->m_mua]; + else + pszT = "no action"; + } + sprintf(szT, "%s (%s)", gapuntc[ple->ut]->szName, pszT); + } + ExtTextOut(hdc, x + 10, y, ETO_OPAQUE, &rc, szT, strlen(szT), NULL); + y += kcySmallFont; + } + + // Show the currently executing action (if any) + + if (pug->m_pactnLast != NULL) { + SetTextColor(hdc, RGB(0, 164, 0)); + rc.top = y; + rc.bottom = y + kcySmallFont; + sprintf(szT, "> %s", pug->m_pactnLast->ToString()); + ExtTextOut(hdc, x + 2, y, ETO_OPAQUE, &rc, szT, strlen(szT), NULL); + y += kcySmallFont; + } + } + + rc.top = y; + rc.bottom = y + 2; + FillRect(hdc, &rc, hbr); + + y += 2; + } + + if (y < kcyUnitGroupWindow) { + rc.bottom = kcyUnitGroupWindow; + FillRect(hdc, &rc, hbr); + } + + SelectObject(hdc, hfntOld); +} + +// +// DelayedMessage Viewer implementation +// + +const int kcxDelayedMessageWindow = 300; +const int kcyDelayedMessageWindow = 900; + +DelayedMessageViewer::DelayedMessageViewer() : + DebugViewer("HT Delayed Messages", -kcxDelayedMessageWindow, 0, kcxDelayedMessageWindow, kcyDelayedMessageWindow) +{ +} + +void UpdateDelayedMessageViewer() +{ + s_pdmv->Update(); +} + +void DelayedMessageViewer::Update() +{ + int cdmsg = 0; + for (DelayedMessage *pdmsg = gsmm.m_pdmsgHead; pdmsg != NULL; pdmsg = pdmsg->pdmsgNext, cdmsg++); + + char szT[50]; + sprintf(szT, "HT Delayed Messages [%d]", cdmsg); + SetWindowText(m_hwnd, szT); + DebugViewer::Update(); +} + +void DelayedMessageViewer::Paint(HDC hdc) +{ + Level *plvl = gsim.GetLevel(); + if (plvl == NULL) + return; + + HFONT hfntOld = (HFONT)SelectObject(hdc, s_hfntSmall); + HBRUSH hbr = (HBRUSH)GetStockObject(GRAY_BRUSH); + + int x = -m_xView; + int y = -m_yView; + RECT rc; + GetClientRect(m_hwnd, &rc); + rc.left = x; + + for (DelayedMessage *pdmsg = gsmm.m_pdmsgHead; pdmsg != NULL; pdmsg = pdmsg->pdmsgNext) { + StateMachine *psmSender = gsmm.GetStateMachine(pdmsg->msg.smidSender); + StateMachine *psmReceiver = gsmm.GetStateMachine(pdmsg->msg.smidReceiver); + char *pszSender = psmSender != NULL ? psmSender->GetName() : ""; + char *pszReceiver = psmReceiver != NULL ? psmReceiver->GetName() : ""; + + char szT[500]; + sprintf(szT, "%s delayed [%d] from %s.%d to %s.%d", gaszMessageNames[pdmsg->msg.mid], + pdmsg->msg.tDelivery - gsim.GetTickCount(), + pszSender, pdmsg->msg.smidSender, pszReceiver, pdmsg->msg.smidReceiver); + + rc.top = y; + rc.bottom = y + kcySmallFont; + ExtTextOut(hdc, x + 2, y, ETO_OPAQUE, &rc, szT, strlen(szT), NULL); + y += kcySmallFont; + } + + if (y < kcyDelayedMessageWindow) { + rc.bottom = kcyDelayedMessageWindow; + FillRect(hdc, &rc, hbr); + } + + SelectObject(hdc, hfntOld); +} + +// +// CommandQueue Viewer implementation +// + +const int kcxCommandQueueWindow = 300; +const int kcyCommandQueueWindow = 900; + +CommandQueueViewer::CommandQueueViewer() : + DebugViewer("HT Command Queue", -kcxCommandQueueWindow, 0, kcxCommandQueueWindow, kcyCommandQueueWindow) +{ +} + +void UpdateCommandQueueViewer() +{ + s_pcmdqv->Update(); +} + +void CommandQueueViewer::Update() +{ + char szT[50]; + sprintf(szT, "HT Command Queue [%d]", gcmdq.GetCount()); + SetWindowText(m_hwnd, szT); + DebugViewer::Update(); +} + +void CommandQueueViewer::Paint(HDC hdc) +{ + Level *plvl = gsim.GetLevel(); + if (plvl == NULL) + return; + + HFONT hfntOld = (HFONT)SelectObject(hdc, s_hfntSmall); + HBRUSH hbr = (HBRUSH)GetStockObject(GRAY_BRUSH); + + int x = -m_xView; + int y = -m_yView; + RECT rc; + GetClientRect(m_hwnd, &rc); + rc.left = x; + + Message *pmsg = gcmdq.GetFirst(); + int cmsg = gcmdq.GetCount(); + for (int i = 0; i < cmsg; i++, pmsg++) { + StateMachine *psmSender = gsmm.GetStateMachine(pmsg->smidSender); + StateMachine *psmReceiver = gsmm.GetStateMachine(pmsg->smidReceiver); + char *pszSender = psmSender != NULL ? psmSender->GetName() : ""; + char *pszReceiver = psmReceiver != NULL ? psmReceiver->GetName() : ""; + + char szT[500]; + sprintf(szT, "%s from %s.%d to %s.%d", gaszMessageNames[pmsg->mid], + pszSender, pmsg->smidSender, pszReceiver, pmsg->smidReceiver); + + rc.top = y; + rc.bottom = y + kcySmallFont; + ExtTextOut(hdc, x + 2, y, ETO_OPAQUE, &rc, szT, strlen(szT), NULL); + y += kcySmallFont; + } + + if (y < kcyCommandQueueWindow) { + rc.bottom = kcyCommandQueueWindow; + FillRect(hdc, &rc, hbr); + } + + SelectObject(hdc, hfntOld); +} + +// Helpers + +static char s_szUnitMask[500]; + +char *PszFromUnitMask(UnitMask um) +{ + char *psz = s_szUnitMask; + *psz = 0; + if ((um & kumInfantry) == kumInfantry) { + strcat(psz, "any infantry,"); + um &= ~kumInfantry; + } + + if ((um & kumVehicles) == kumVehicles) { + strcat(psz, "any vehicle,"); + um &= ~kumVehicles; + } + + if ((um & kumStructures) == kumStructures) { + strcat(psz, "any structure,"); + um &= ~kumStructures; + } + + for (int i = 0; i < kutMax; i++) { + if (um & (1UL << i)) { + strcat(psz, gapuntc[i]->szName); + strcat(psz, ","); + } + } + + // Trim trialing ',' + + int cch = strlen(psz); + if (cch > 0) + psz[cch - 1] = 0; + + return psz; +} + +static char s_szCaSideMask[500]; + +char *PszFromCaSideMask(word wfCaSideMask) +{ + char *psz = s_szCaSideMask; + *psz = 0; + if (wfCaSideMask & (1 << knCaSideAllSides)) + strcat(psz, "all sides,"); + if (wfCaSideMask & (1 << knCaSideAllies)) + strcat(psz, "allies,"); + if (wfCaSideMask & (1 << knCaSideEnemies)) + strcat(psz, "enemies,"); + if (wfCaSideMask & (1 << knCaSideCurrentSide)) + strcat(psz, "current side,"); + if (wfCaSideMask & (1 << knCaSideSide1)) + strcat(psz, "side 1,"); + if (wfCaSideMask & (1 << knCaSideSide2)) + strcat(psz, "side 2,"); + if (wfCaSideMask & (1 << knCaSideSide3)) + strcat(psz, "side 3,"); + if (wfCaSideMask & (1 << knCaSideSide4)) + strcat(psz, "side 4,"); + + // Trim trialing ',' + + int cch = strlen(psz); + if (cch > 0) + psz[cch - 1] = 0; + + return psz; +} + +#endif // def DEBUG_HELPERS diff --git a/game/Ecom.cpp b/game/Ecom.cpp new file mode 100644 index 0000000..ac65117 --- /dev/null +++ b/game/Ecom.cpp @@ -0,0 +1,550 @@ +#include "ht.h" + +namespace wi { + +// font colors Jana, Andy, Olstrom, Fox + +#define kiaiclrJana 0 +#define kiaiclrAndy 1 +#define kiaiclrOlstrom 2 +#define kiaiclrFox 3 + +#define kcchPerSec 40 // TUNE: how many characters per second output during typed dialog +#define kctSpeechDelay 50 // TUNE: hsecs before a new speech + +#define kfEcomAutoTakedown 1 +#define kfEcomMore 2 + +class EcomForm : public Form, public Timer +{ +public: + EcomForm() secEcom; + virtual ~EcomForm() secEcom; + void SetAutoTakedown() {m_wfEcom |= kfEcomAutoTakedown;} + bool DoModal(char *pszMessage, int *pnResult = NULL, Sfx sfxShow = ksfxGuiFormShow, Sfx sfxHide = ksfxGuiFormHide) secEcom; + + // Form overrides + + virtual void OnPaintBackground(DibBitmap *pbm, UpdateMap *pupd) secEcom; + virtual void OnControlSelected(word idc) secEcom; + virtual bool OnPenEvent(Event *pevt) secEcom; + + // Timer Overrides + + virtual void OnTimer(long tCurrent) secEcom; + +private: + void More() secEcom; + + word m_wfEcom; + char *m_pszText; + char *m_pszNext; +}; + +//------------------------------------------------------------------------------ + +static char *s_aszEmailAddrs[] = { + "Nobody", "Andy@@ACME", "Jana@@ACME", "Olstrom@@ACME", "Fox@@OMNI", "ACME Security", "OMNI Security", "Anonymous", "" +}; +static char *s_aszNames[] = {\ +"", "Andy: ", "Jana: ", "Olstrom: ", "Fox: ", "ACME Security: ", "OMNI Security: ", "Anonymous: ", "" +}; +static char *s_aszPortraits[] = { + NULL, "andyportrait.tbm", "jana.tbm", "olstrom.tbm", "fox.tbm", NULL, NULL, NULL, NULL +}; + +void Ecom(int nCharFrom, int nCharTo, char *pszMessage, int nBackground, bool fMore) +{ + // Don't show ecoms in recording stress mode + +#ifdef STRESS + if (gfStress) + return; +#endif + + EcomForm *pfrm = (EcomForm *)gpmfrmm->LoadForm(gpiniForms, nBackground != knSmallLargeTypeLarge ? kidfEcomSmall : kidfEcomLarge, new EcomForm()); + if (pfrm == NULL) + return; + +#if 0 + if (nBackground != knSmallLargeTypeLarge && !fMore) + pfrm->SetAutoTakedown(); +#else + // Don't special case type large. Some resolutions the large ecom doesn't + // fill the whole screen, and autotakedown is desireable. + // Don't special case fMore since that is now automatically handled. + + pfrm->SetAutoTakedown(); +#endif + + Size sizPlayfield; + ggame.GetPlayfieldSize(&sizPlayfield); + + if (nBackground == knSmallLargeTypeSmallTop) { + Rect rc; + pfrm->GetRect(&rc); + Font *pfnt = gapfnt[kifntDefault]; + rc.Offset(0, -rc.top); + pfrm->SetRect(&rc); + } else if (nBackground == knSmallLargeTypeSmallBottom) { + + // position on bottom of screen. Different on 240 vs 160 screens + Rect rc; + pfrm->GetRect(&rc); + rc.Offset(0, sizPlayfield.cy - rc.Height() - rc.top); + pfrm->SetRect(&rc); + } + + // For all ecoms, horizontally center. Usually fills the screen horizontally unless + // we're in a weird data / screen mode combo. + // TUNE: May want to vertically adjust as well but I'm not standing on my head (at the moment) + + Rect rcForm; + pfrm->GetRect(&rcForm); + rcForm.Offset((sizPlayfield.cx - rcForm.Width()) / 2, 0); + if ((rcForm.left & 1) != 0) + rcForm.Offset(-1, 0); + pfrm->SetRect(&rcForm); + + // Initialize controls + + LabelControl *plbl = (LabelControl *)pfrm->GetControlPtr(kidcFrom); + plbl->SetText(s_aszEmailAddrs[nCharFrom]); + plbl = (LabelControl *)pfrm->GetControlPtr(kidcTo); + plbl->SetText(s_aszEmailAddrs[nCharTo]); + + // The label self-adjusts its height + + if (nBackground == knSmallLargeTypeLarge) { + BitmapControl *pbmc = (BitmapControl *)pfrm->GetControlPtr(kidcFromBitmap); + char *pszBitmap = s_aszPortraits[nCharFrom]; + if (pszBitmap != NULL) { + pbmc->SetBitmap(LoadTBitmap(pszBitmap)); + pbmc->Show(true); + } else { + pbmc->Show(false); + } + + pbmc = (BitmapControl *)pfrm->GetControlPtr(kidcToBitmap); + pszBitmap = s_aszPortraits[nCharTo]; + if (pszBitmap != NULL) { + pbmc->SetBitmap(LoadTBitmap(pszBitmap)); + pbmc->Show(true); + } else { + pbmc->Show(false); + } + } + + pfrm->DoModal(pszMessage); + delete pfrm; +} + +//=========================================================================== +// EcomForm implementation + +// This is just here so we can be explicit about what section it ends up in +EcomForm::EcomForm() +{ + m_wfEcom = 0; + m_pszText = NULL; +} + +EcomForm::~EcomForm() +{ + if (m_pszText != NULL) + delete m_pszText; +} + +bool EcomForm::DoModal(char *pszMessage, int *pnResult, Sfx sfxShow, Sfx sfxHide) +{ + // Expand the text first thing so subsequent operations like calcing the number + // of lines that will fit on the ecom are based on the expanded text. + // Use the tail end of the scratch buffer because ExpandVars (potentially) calls + // StringTable::GetString which reads from the database, decompressing to + // the front of the scratch buffer as part of the process. + + char *pszT = (char *)gpbScratch + (gcbScratch / 2); + ExpandVars(pszMessage, pszT, gcbScratch / 2); + m_pszText = new char[strlen(pszT) + 1]; + if (m_pszText == NULL) { + pszT = "ECom text too long! Out of memory."; + m_pszText = new char[strlen(pszT) + 1]; + if (m_pszText == NULL) + return false; + } + strcpy(m_pszText, pszT); + m_pszNext = m_pszText; + + More(); + + gtimm.AddTimer(this, kctEcomOutputInterval); + bool f = Form::DoModal(pnResult, sfxShow, sfxHide); + gtimm.RemoveTimer(this); + return f; +} + +void EcomForm::More() +{ + ButtonControl *pbtn = (ButtonControl *)GetControlPtr(kidcOk); + pbtn->Show(false); + + EcomTextControl *pect = (EcomTextControl *)GetControlPtr(kidcMessage); + char *pszT = new char[strlen(m_pszNext) + 1]; + strcpy(pszT, m_pszNext); + + // How much of the text will fit? + + Font *pfnt = gapfnt[kifntDefault]; + Rect rcT; + pect->GetRect(&rcT); + + int cLines = rcT.Height() / pfnt->GetHeight(); + char *pchBreak = pszT; + while (cLines-- > 0 && pchBreak != NULL) + pfnt->CalcBreak(rcT.Width(), &pchBreak); + + // If it all fits, scan to the end. + + if (pchBreak == NULL) { + pchBreak = pszT; + while (*pchBreak != 0) + pchBreak++; + } + *pchBreak = 0; + + pect->SetText(pszT); + + // Start from the break next time around + + m_pszNext += pchBreak - pszT; + delete pszT; + + bool fMore = (*m_pszNext != 0); + if (fMore) { + m_wfEcom |= kfEcomMore; + } else { + m_wfEcom &= ~kfEcomMore; + } + + pbtn->SetText((char *)(fMore ? "More..." : "OK")); +} + +bool EcomForm::OnPenEvent(Event *pevt) +{ + if (pevt->eType == penDownEvent) { + for (int n = m_cctl - 1; n >= 0; n--) { + // Is it on this control? + + Control *pctl = m_apctl[n]; + if (pctl->OnHitTest(pevt) >= 0) { + return Form::OnPenEvent(pevt); + } + } + + // Not on a control + + EcomTextControl *pect = (EcomTextControl *)GetControlPtr(kidcMessage); + pect->ShowAll(); + } + return Form::OnPenEvent(pevt); +} + +void EcomForm::OnControlSelected(word idc) +{ + switch (idc) { + case kidcCancel: + EndForm(kidcCancel); + return; + + case kidcMessage: + { + EcomTextControl *pect = (EcomTextControl *)GetControlPtr(kidcMessage); + pect->ShowAll(); + } + break; + + case kidcOk: + if (*m_pszNext == 0) + EndForm(kidcOk); + else + More(); + return; + } +} + +void EcomForm::OnPaintBackground(DibBitmap *pbm, UpdateMap *pupd) +{ + RawBitmap *prbm = LoadRawBitmap(GetId() == kidfEcomLarge ? (char *)"ecomlargebkgd.rbm" : (char *)"ecomsmallbkgd.rbm"); + BltHelper(pbm, prbm, pupd, m_rc.left, m_rc.top); + delete prbm; +} + +void EcomForm::OnTimer(long tCurrent) +{ + if (m_wf & kfFrmDoModal) { + + // invalidate the space for the next character. if it's done, + // make sure the More/Close button shows + + EcomTextControl *pect = (EcomTextControl *)GetControlPtr(kidcMessage); + if (pect->ShowMoreText()) { + ButtonControl *pbtn = (ButtonControl *)GetControlPtr(kidcOk); + pbtn->Show(true); + if ((m_wfEcom & (kfEcomMore | kfEcomAutoTakedown)) == kfEcomAutoTakedown) + m_wf |= kfFrmAutoTakedown; + } + } +} + +// EcomTextControl + +EcomTextControl::EcomTextControl() +{ + m_cchCur = 0; + m_ctPrevTime = HostGetTickCount(); +} + +EcomTextControl::~EcomTextControl() +{ +} + +bool EcomTextControl::Init(Form *pfrm, IniReader *pini, FindProp *pfind) +{ + // Base initialization + + if (!Control::Init(pfrm, pini, pfind)) + return false; + + // idc (x y cx cy) "label" nfnt + + char szLabel[256]; + char szFlag1[32]; + szLabel[sizeof(szLabel) - 1] = 0; + szFlag1[sizeof(szFlag1) - 1] = 0; + + int cArgs = pini->GetPropertyValue(pfind, "%*d (%*d %*d %*d %*d) \"%s\" %d %s", + szLabel, &m_nfnt, szFlag1); + Assert(szLabel[sizeof(szLabel) - 1] == 0); + Assert(szFlag1[sizeof(szFlag1) - 1] == 0); + + if (cArgs < 2 || cArgs > 3) + return false; + + m_szLabel = (char *)gmmgr.AllocPtr(strlen(szLabel) + 1); + gmmgr.WritePtr(m_szLabel, 0, szLabel, strlen(szLabel) + 1); + + if (cArgs == 3) { + if (strcmp(szFlag1, "center") == 0) + m_wf |= kfLblCenterText; + else if (strcmp(szFlag1, "right") == 0) + m_wf |= kfLblRightText; + } + + // always multiline + + m_wf |= kfLblMultiLine; + + m_cchCur = 0; + byte biclr; + biclr = GetColor(kiclrJana) & 0xff; + m_aiclrEcom[kiaiclrJana] = MAKEDWORD(biclr); + biclr = GetColor(kiclrAndy) & 0xff; + m_aiclrEcom[kiaiclrAndy] = MAKEDWORD(biclr); + biclr = GetColor(kiclrOlstrom) & 0xff; + m_aiclrEcom[kiaiclrOlstrom] = MAKEDWORD(biclr); + biclr = GetColor(kiclrFox) & 0xff; + m_aiclrEcom[kiaiclrFox] = MAKEDWORD(biclr); + + return true; +} + +bool EcomTextControl::ShowMoreText() +{ + // for now invalidate the whole control. Can we do better? + + // decide how many characters to output based on how much time has + // gone by (if handheld was slow to paint it can be longer than the timer interval) + // and paint what we need to to meet our chars per sec. At speech starts + // we'll delay a specified number of hsecs + // time is in hundredths of a sec + + if (m_szLabel[m_cchCur] == 0) + return true; + + long ctCurTime = HostGetTickCount(); + long ctDeltaTime = ctCurTime - m_ctPrevTime; + + // pause before a new character speaks. Make sure we don't count + // our pause time as part of our character output time when pause ends. + + if (m_szLabel[m_cchCur] == '@') { + if (ctDeltaTime < kctSpeechDelay) + return false; + else + ctDeltaTime -= kctSpeechDelay; + } + + // output the needed characters to get our output rate + + int cch = ((long)kcchPerSec * ctDeltaTime)/(long)100; + if (cch <= 0) + return false; + m_ctPrevTime = ctCurTime; + + // make sure we advance at least one character so we don't get stuck on + // speech starts. + + do { + m_cchCur++; + cch--; + } while ( (m_szLabel[m_cchCur] != 0) && (m_szLabel[m_cchCur] != '@') && cch > 0); + Invalidate(); + + return m_szLabel[m_cchCur] == 0; +} + +void EcomTextControl::ShowAll() +{ + // for now invalidate the whole control. Can we do better? + + m_cchCur = strlen(m_szLabel); + Invalidate(); +} + +void EcomTextControl::OnPaint(DibBitmap *pbm) +{ + if (m_szLabel == 0) + return; + + Rect rcForm; + m_pfrm->GetRect(&rcForm); + Font *pfnt = gapfnt[m_nfnt]; + DrawText(pbm, pfnt, m_szLabel, m_rc.left + rcForm.left, m_rc.top + rcForm.top, m_rc.Width(), m_cchCur); +} + +int EcomTextControl::OnHitTest(Event *pevt) +{ + // Label Control stubs this out since they're not usually selectable + + return Control::OnHitTest(pevt); +} + +void EcomTextControl::CalcRect() +{ + // override the label version of this. Our rect stays whatever it is in forms.pp.ini +} + +void EcomTextControl::SetText(char *psz) +{ + LabelControl::SetText(psz); + m_cchCur = 0; + m_ctPrevTime = HostGetTickCount(); +} + +//---------------------------------------------------------------- + +void EcomTextControl::DrawText(DibBitmap *pbm, Font *pfnt, char *psz, int x, int y, int cx, int cchMax) +{ + // this draws the string up to the point of cchMax. The string is broken up into character speeches + // seperated by an '@'. Scan forward to the next '@', set the color appropriately, then + // draw that speech with the multiline drawing, repeat until we reach cchMax or null + // null is possible because we don't count the @X that signify color changes, but it does. + + char *pszNextSpeech = psz; + int cchSpeech; + byte biclr = GetColor(kiclrWhite) & 0xff; // make into a dword + dword dwiclr = MAKEDWORD(biclr); // default color + dword dwiColorName = dwiclr; // save for doing names + int xStart = x; + int cxStart = cx; + + while ((*pszNextSpeech != 0) && (cchMax > 0)) { + + if (*pszNextSpeech != '@') { + + // FYI this data file's speeches are formatted wrong. + // the should have @[A | J | O | F] at the start of the text line. + //feel free to continue + + //Assert(false); + cchSpeech = 0; + }else { + + Assert(*pszNextSpeech == '@'); + pszNextSpeech++; + int iszNames = 0; + cchSpeech = 0; + + // set the color for a new speech + switch (*pszNextSpeech) { + case 'A': + dwiclr = m_aiclrEcom[kiaiclrAndy]; + iszNames = 1; + break; + case 'J': + dwiclr = m_aiclrEcom[kiaiclrJana]; + iszNames = 2; + break; + case 'O': + dwiclr = m_aiclrEcom[kiaiclrOlstrom]; + iszNames = 3; + break; + case 'F': + dwiclr = m_aiclrEcom[kiaiclrFox]; + iszNames = 4; + break; + default: + Assert(false); + } + pszNextSpeech++; + + // we don't count the @format characters in the length - so adjust cchMax. + cchMax -= 2; + + // output the character's Name: + // seems wonky to use both dwWhite and dwiscColor but palm compiler + // messes it up if I don't use the intermediate variable + + pfnt->DrawText(pbm, s_aszNames[iszNames], x, y, -1, &dwiColorName ); + int cxName = pfnt->GetTextExtent(s_aszNames[iszNames]); + x += cxName; + cx -= cxName; + + }//endif + + char *pszNextLine = pszNextSpeech; + + // point at the next speech and count length of this one + + while ((*pszNextSpeech != '@') && (*pszNextSpeech != 0)){ + cchSpeech++; + pszNextSpeech++; + } + + if (cchSpeech > cchMax) + cchSpeech = cchMax; + cchMax -= cchSpeech; + + // output this speech in a multiline way + + while (cchSpeech > 0 && pszNextLine != NULL) { + Assert(pszNextLine != 0); + char *pszStart = pszNextLine; + int cch = pfnt->CalcBreak(cx, &pszNextLine); + if (cch >= cchSpeech) + cch = cchSpeech; + cchSpeech -= cch; + int iret = pfnt->DrawText(pbm, pszStart, x, y, cch, &dwiclr); + + // cch does not include the whitespace char being used + // to break the line! + + cchSpeech--; + y += pfnt->GetHeight() - pfnt->GetLineOverlap(); + x = xStart; + cx = cxStart; + } + } +} + +} // namespace wi diff --git a/game/Entitlements.plist b/game/Entitlements.plist new file mode 100644 index 0000000..44c3622 --- /dev/null +++ b/game/Entitlements.plist @@ -0,0 +1,10 @@ + + + + + keychain-access-groups + + $(AppIdentifierPrefix)$(CFBundleIdentifier) + + + diff --git a/game/GameObjects.cpp b/game/GameObjects.cpp new file mode 100644 index 0000000..82ed63e --- /dev/null +++ b/game/GameObjects.cpp @@ -0,0 +1,2843 @@ +#include "game/ht.h" +#include "game/stateframe.h" + +namespace wi { + +byte grvlp[] = { + // ctx & cty + 9, 9, + + // reveal mask + 0xf, 0xf, 0xa, 0x2, 0x2, 0x2, 0x3, 0xf, 0xf, + 0xf, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0xf, + 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, + 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, + 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, + 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, + 0xc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, + 0xf, 0xc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0xf, + 0xf, 0xf, 0xc, 0x4, 0x4, 0x4, 0x5, 0xf, 0xf +}; + +byte grvlpLarge[] = { + // ctx & cty + 11, 11, + +#if 0 + // reveal mask + 0xf, 0xf, 0xf, 0xa, 0x2, 0x2, 0x2, 0x3, 0xf, 0xf, 0xf, + 0xf, 0xf, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0xf, 0xf, + 0xf, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0xf, + 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, + 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, + 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, + 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, + 0xc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, + 0xf, 0xc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0xf, + 0xf, 0xf, 0xc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0xf, 0xf, + 0xf, 0xf, 0xf, 0xc, 0x4, 0x4, 0x4, 0x5, 0xf, 0xf, 0xf, +#else + // reveal mask + 0xf, 0xf, 0xf, 0xa, 0x2, 0x2, 0x2, 0x3, 0xf, 0xf, 0xf, + 0xf, 0xa, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x3, 0xf, + 0xf, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xf, + 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, + 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, + 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, + 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, + 0xc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, + 0xf, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xf, + 0xf, 0xc, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x5, 0xf, + 0xf, 0xf, 0xf, 0xc, 0x4, 0x4, 0x4, 0x5, 0xf, 0xf, 0xf, +#endif +}; + +#if 0 +byte grvlpMegaLarge[] = { + // ctx & cty + 13, 13, + + // reveal mask + 0xf, 0xf, 0xf, 0xa, 0x2, 0x2, 0x2, 0x2, 0x2, 0x3, 0xf, 0xf, 0xf, + 0xf, 0xf, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0xf, 0xf, + 0xf, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0xf, + 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, + 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, + 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, + 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, + 0xc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, + 0xf, 0xc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0xf, + 0xf, 0xf, 0xc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0xf, 0xf, + 0xf, 0xf, 0xf, 0xc, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0xf, 0xf, 0xf, +}; +#endif + + +// Helper function. Creates but does not Init the Gob. + +Gob *CreateGob(GobType gt) +{ + switch (gt) { + case kgtScenery: + return new SceneryGob(); + + case kgtSurfaceDecal: + return new SurfaceDecalGob(); + + case kgtScorch: + return new ScorchGob(); + + case kgtShortRangeInfantry: + return new SRInfantryGob(); + + case kgtLongRangeInfantry: + return new LRInfantryGob(); + + case kgtHumanResourceCenter: + return new HrcGob(); + + case kgtReactor: + return new ReactorGob(); + + case kgtProcessor: + return new ProcessorGob(); + + case kgtGalaxMiner: + return new MinerGob(); + + case kgtVehicleTransportStation: + return new VtsGob(); + + case kgtHeadquarters: + return new HqGob(); + + case kgtRadar: + return new RadarGob(); + + case kgtResearchCenter: + return new ResearchGob(); + + case kgtLightTank: + return new LTankGob(); + + case kgtMediumTank: + return new MTankGob(); + + case kgtRocketVehicle: + return new RTankGob(); + + case kgtMachineGunVehicle: + return new GTankGob(); + + case kgtTakeoverSpecialist: + return new SpInfantryGob(); + + case kgtOvermind: + return new OvermindGob(); + + // NOTE: probably unnecessary. Don't plan to place TankShots in a level + case kgtTankShot: + return new TankShotGob(); + + // NOTE: probably unnecessary. Don't plan to place Rockets in a level + case kgtRocket: + return new RocketGob(); + + case kgtWarehouse: + return new WarehouseGob(); + + case kgtMachineGunTower: + return new GunTowerGob(); + + case kgtRocketTower: + return new RocketTowerGob(); + + case kgtMobileHeadquarters: + return new MobileHqGob(); + + case kgtSmoke: + return new SmokeGob(0); + + case kgtPuff: + return new PuffGob(); + + case kgtArtillery: + return new ArtilleryGob(); + + case kgtAndy: + return new AndyGob(); + + case kgtReplicator: + return new ReplicatorGob(); + + case kgtActivator: + return new ActivatorGob(); + + case kgtFox: + return new FoxGob(); + } + + Assert("Unrecognized GobType %d", gt); + return NULL; +} + +// +// Gob implementation +// + +Gob::Gob() +{ + m_pgobNext = NULL; + m_gidNext = kgidNull; + m_gid = kgidNull; + m_pplr = &gplrDummy; + m_ff = kfGobRedraw; + m_trcBoundingLast.txLeft = 127; + m_trcBoundingLast.tyTop = 127; + m_trcBoundingLast.txRight = -128; + m_trcBoundingLast.tyBottom = -128; +} + +Gob::~Gob() +{ +} + +#ifdef TRACKSTATE + +dword gmpgtQuad[] = { + 'GNON', // kgtNone 0 + 'GSRI', // kgtShortRangeInfantry 1 + 'GLRI', // kgtLongRangeInfantry 2 + 'GHRC', // kgtHumanResourceCenter 3 + 'GDCL', // kgtSurfaceDecal 4 + 'GSCN', // kgtScenery 5 + 'GANI', // kgtAnimation 6 + 'GREA', // kgtReactor 7 + 'GPRC', // kgtProcessor 8 + 'GSTU', // kgtStructure 9 + 'GUNI', // kgtUnit 10 + 'GMIN', // kgtGalaxMiner 11 + 'GHQR', // kgtHeadquarters 12 + 'GRES', // kgtResearchCenter 13 + 'GVTS', // kgtVehicleTransportStation 14 + 'GRDR', // kgtRadar 15 + 'GLTK', // kgtLightTank 16 + 'GMTK', // kgtMediumTank 17 + 'GGNV', // kgtMachineGunVehicle 18 + 'GRTV', // kgtRocketVehicle 19 + 'GTAK', // kgtTakeoverSpecialist 20 + 'GWAR', // kgtWarehouse 21 + 'GMHQ', // kgtMobileHeadquarters 22 + 'GOVR', // kgtOvermind 23 + 'GTST', // kgtTankShot 24 + 'GRKT', // kgtRocket 25 + 'GMGT', // kgtMachineGunTower 26 + 'GRTR', // kgtRocketTower 27 + 'GSCO', // kgtScorch 28 + 'GSMO', // kgtSmoke 29 + 'GPUF', // kgtPuff 30 + 'GBUL', // kgtBullet 31 + 'GART', // kgtArtillery 32 + 'GARS', // kgtArtilleryShot 33 + 'GAND', // kgtAndy 34 + 'GREP', // kgtReplicator 35 + 'GACT', // kgtActivator 36 + 'GFOX', // kgtFox 37 + 'GANS', // kgtAndyShot 38 +}; + +void Gob::TrackState(StateFrame *frame) { + int i = frame->AddCountedValue(gmpgtQuad[GetType()]); + frame->AddValue('ID ', (dword)m_gid, i); + frame->AddValue('WX ', (dword)m_wx, i); + frame->AddValue('WY ', (dword)m_wy, i); + frame->AddValue('GNXT', (dword)m_gidNext, i); + frame->AddValue('ST ', (dword)m_st, i); + frame->AddValue('STNX', (dword)m_stNext, i); + dword ff = m_ff; + ff &= ~(kfGobRedraw | kfGobFlashing | kfGobDrawFlashed | + kfGobDebug | kfGobSelected | kfGobVisibleLastFrame | + kfGobIncludeFindVisible | kfGobTransitioningToVisible | + kfGobAnimationChanged | kfGobLayerSelection | kfGobLayerMask); + frame->AddValue('FF ', ff, i); +} +#endif + +bool Gob::Invalidate() +{ + // Get the clipping bounds, the rect that surrounds the visible pixels of + // this gob + + Rect rc; + GetClippingBounds(&rc); + + // Change into a tile rect + + TRectSmall trcNew; + trcNew.txLeft = (char)TcFromPc(rc.left); + trcNew.tyTop = (char)TcFromPc(rc.top); + int xT = _min(rc.right + gcxTile - 1, (int)kpcMax); + trcNew.txRight = (char)TcFromPc(xT); + int yT = _min(rc.bottom + gcyTile - 1, (int)kpcMax); + trcNew.tyBottom = (char)TcFromPc(yT); + + // If the gob was transitioning from off screen to on screen, use the + // current invalidation rect. Otherwise Union with the old rect so that the + // screen redraws the old location and the new location + + if (m_ff & kfGobTransitioningToVisible) { + m_trcBoundingLast = trcNew; + } else { + m_trcBoundingLast.Union(&trcNew); + } + + // Is this opaqued? If so nothing to do. Clip to edges of opaque rect; if all inside + // return false + + if (gptrcMapOpaque != NULL) { + if (gptrcMapOpaque->left <= m_trcBoundingLast.txLeft) { + if (gptrcMapOpaque->right >= m_trcBoundingLast.txRight) { + if (gptrcMapOpaque->top <= m_trcBoundingLast.tyTop) { + if (gptrcMapOpaque->bottom >= m_trcBoundingLast.tyBottom) { + return false; + } + } + } + } + } + + // Perform invalidation of what's visible + + gpupdSim->InvalidateMapTileRect(&m_trcBoundingLast); + + // Remember new + + m_trcBoundingLast = trcNew; + return true; +} + +// Marks the gob for redraw; will cause update map invalidation later +// If the gob was visible last frame, include this gob in the next +// FindVisibleGobs call. This is so that gobs transitioning from +// visible to invisible have a chance to redraw the old on-screen +// location + +void Gob::MarkRedraw() { + m_ff |= kfGobRedraw; + if (m_ff & kfGobVisibleLastFrame) + m_ff |= kfGobIncludeFindVisible; +} + +bool Gob::AdvanceAnimation(Animation *pani) { + if (pani->Advance(m_ff & kfGobAnimationChanged ? 1 : m_unvl.GetUpdateCount())) { + m_unvl.MinSkip(pani->GetRemainingFrameTime()); + MarkRedraw(); + m_ff &= ~kfGobAnimationChanged; + return true; + } + m_ff &= ~kfGobAnimationChanged; + return false; +} + +void Gob::StartAnimation(Animation *pani, int nStrip, int nFrame, word wfAni) { + if (nStrip != pani->GetStrip() || nFrame != pani->GetFrame()) + MarkRedraw(); + if (pani->Start(nStrip, nFrame, wfAni)) { + m_unvl.MinSkip(pani->GetRemainingFrameTime()); + m_ff |= kfGobAnimationChanged; + } +} + +void Gob::SetAnimationStrip(Animation *pani, int nStrip) { + if (nStrip != pani->GetStrip()) + MarkRedraw(); + pani->SetStrip(nStrip); + m_ff |= kfGobAnimationChanged; +} + +void Gob::SetAnimationFrame(Animation *pani, int nFrame) { + if (nFrame != pani->GetFrame()) + MarkRedraw(); + pani->SetFrame(nFrame); + m_ff |= kfGobAnimationChanged; +} + +dword Gob::GetSortKey() +{ + return MakeSortKey(m_wy, m_gid); +} + +#if defined(WIN) && defined(DEBUG) +void Gob::ToString(char *psz) +{ + sprintf(psz, "this=0x%08lx, gid=0x%08lx, gt=0x%lx, wx=0x%04lx, wy=0x%04lx", this, m_gid, GetType(), m_wx, m_wy); +} +#endif + +// Clipping bounds is used to determine on-display visibility +// This bounds is a rectangle tightly enclosing all the pixels the Gob +// will be drawing to. Clipping bounds may change in size depending +// on the animation state of the Gob. +// NOTE: the values returned are in world coordinates + +void Gob::GetClippingBounds(Rect *prc) +{ + prc->SetEmpty(); +} + +// UI bounds is reused for input hit testing and for drawing the selection +// indicator. The UI bounds is an end-user 'logical' box surrounding Gobs +// they'll be tapping on. Once determined, it does not change size as the Gob +// animates. NOTE: the values returned are in world coordinates + +void Gob::GetUIBounds(WRect *pwrc) +{ + pwrc->SetEmpty(); +} + +void Gob::GetPosition(WPoint *pwpt) +{ + pwpt->wx = m_wx; + pwpt->wy = m_wy; +} + +void Gob::SetPosition(WCoord wx, WCoord wy) +{ + ggobm.MoveGob(this, m_wx, m_wy, wx, wy); + m_wx = wx; + m_wy = wy; + MarkRedraw(); +} + +void Gob::GetCenter(WPoint *pwpt) +{ + pwpt->wx = m_wx; + pwpt->wy = m_wy; +} + +void Gob::GetTilePosition(TPoint *ptpt) +{ + ptpt->tx = TcFromWc(m_wx); + ptpt->ty = TcFromWc(m_wy); +} + +void Gob::GetTileRect(TRect *ptrc) +{ + ptrc->left = TcFromWc(m_wx); + ptrc->top = TcFromWc(m_wy); + ptrc->right = ptrc->left + 1; + ptrc->bottom = ptrc->top + 1; +} + +void Gob::GetTilePaddedWRect(WRect *pwrc) +{ + pwrc->left = WcTrunc(m_wx); + pwrc->top = WcTrunc(m_wy); + pwrc->right = pwrc->left + kwcTile; + pwrc->bottom = pwrc->top + kwcTile; +} + +/* Javascript (Jscript + Windows Scripting Host, actually) to generate the table below: +WScript.Echo("byte gmpDistFromDxy[10][10] = {"); +for (y = 0; y < 10; y++) { + str = ""; + for (x = 0; x < 10; x++) { + d = Math.sqrt(x * x + y * y); + strT = Math.round(d).toString(); + if (strT.length < 2) + strT = " " + strT; + str += strT + ","; + } + WScript.Echo("\t{ " + str + " },"); +} +WScript.Echo("};"); +*/ + +byte gmpDistFromDxy[10][10] = { + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, }, + { 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, }, + { 2, 2, 3, 4, 4, 5, 6, 7, 8, 9, }, + { 3, 3, 4, 4, 5, 6, 7, 8, 9, 9, }, + { 4, 4, 4, 5, 6, 6, 7, 8, 9,10, }, + { 5, 5, 5, 6, 6, 7, 8, 9, 9,10, }, + { 6, 6, 6, 7, 7, 8, 8, 9,10,11, }, + { 7, 7, 7, 8, 8, 9, 9,10,11,11, }, + { 8, 8, 8, 9, 9, 9,10,11,11,12, }, + { 9, 9, 9, 9,10,10,11,11,12,13, }, +}; + +bool Gob::IsGobWithinRange(Gob *pgobTarget, TCoord tcRange) +{ + WPoint wpt; + wpt.wx = m_wx; + wpt.wy = m_wy; + return IsTargetWithinRange(&wpt, pgobTarget, tcRange); +} + +bool Gob::IsTargetWithinRange(WPoint *pwptTarget, Gob *pgobTarget, TCoord tcRange) +{ + Assert(tcRange < 10, "IsGobWithinRange tcRange must be less than 10"); + TCoord tx = TcFromWc(pwptTarget->wx); + TCoord ty = TcFromWc(pwptTarget->wy); + + // Test every tile covered by structures + + if (pgobTarget->GetFlags() & kfGobStructure) { + TRect trcTarget; + pgobTarget->GetTileRect(&trcTarget); + for (TCoord tyTarget = trcTarget.top; tyTarget < trcTarget.bottom; tyTarget++) { + for (TCoord txTarget = trcTarget.left; txTarget < trcTarget.right; txTarget++) { + TCoord dtx = abs(tx - txTarget); + TCoord dty = abs(ty - tyTarget); + if (dtx >= 10 || dty >= 10) + continue; + if (gmpDistFromDxy[dtx][dty] <= tcRange) + return true; + } + } + return false; + + } else { + TCoord dtx = abs(tx - TcFromWc(pgobTarget->m_wx)); + TCoord dty = abs(ty - TcFromWc(pgobTarget->m_wy)); + if (dtx >= 10 || dty >= 10) + return false; + return gmpDistFromDxy[dtx][dty] <= tcRange; + } +} + +// Do-nothing default implementations + +void Gob::PopupMenu() +{ +} + +void Gob::InitMenu(Form *pfrm) +{ +} + +void Gob::OnMenuItemSelected(int id) +{ +} + +#define knVerGobState 2 +bool Gob::LoadState(Stream *pstm) +{ + byte nVer = pstm->ReadByte(); + if (nVer != knVerGobState) + return false; + m_st = (State)pstm->ReadWord(); + m_ff |= pstm->ReadDword(); + m_wx = pstm->ReadWord(); + m_wy = pstm->ReadWord(); + Pid pid = pstm->ReadWord(); + m_pplr = gplrm.GetPlayerFromPid(pid); + Gid gid = pstm->ReadWord(); + Assert(gid != kgidNull); + ggobm.AddGob(this, gid); + + return pstm->IsSuccess(); +} + +bool Gob::SaveState(Stream *pstm) +{ + pstm->WriteByte(knVerGobState); + pstm->WriteWord(m_st); + pstm->WriteDword(m_ff); + pstm->WriteWord(m_wx); + pstm->WriteWord(m_wy); + if (m_pplr != NULL) { + pstm->WriteWord(m_pplr->GetId()); + } else { + pstm->WriteWord(kpidNeutral); + } + pstm->WriteWord(m_gid); + + return pstm->IsSuccess(); +} + +bool Gob::IsSavable() +{ + return true; +} + +// StateMachine methods + +#if defined(DEBUG_HELPERS) +char *Gob::GetName() +{ + return "Gob"; +} +#endif + +// +// SurfaceDecalGob implementation +// + +bool SurfaceDecalGob::InitClass(IniReader *pini) +{ + // No interesting global parameters have been defined for SurfaceDecal yet + return true; +} + +SurfaceDecalGob::SurfaceDecalGob() +{ + m_ff |= kfGobLayerSurfaceDecal; + m_ptbm = NULL; +} + +SurfaceDecalGob::~SurfaceDecalGob() +{ +} + +bool SurfaceDecalGob::Init(IniReader *pini, FindProp *pfind, const char *pszName) +{ + int tx, ty; + char szBitmap[kcbFilename]; + int cArgs = pini->GetPropertyValue(pfind, "%*d ,%s,%d ,%d", szBitmap, &tx, &ty); + if (cArgs < 3) { + Assert("SurfaceDecalGob requires at least 3 valid initialization parameters"); + return false; + } + + return Init(WcFromTc(tx), WcFromTc(ty), 0, szBitmap, NULL, pszName); +} + +bool SurfaceDecalGob::Init(WCoord wx, WCoord wy, dword ff, const char *pszBitmap, TBitmap *ptbm, const char *pszName) +{ + m_wx = wx; + m_wy = wy; + + if (pszBitmap != NULL) { + m_ptbm = GetSharedTBitmap((char *)pszBitmap); + if (m_ptbm == NULL) + return false; + } else { + // NOTE: we assume if we were passed the bitmap that the caller is + // going to take care of deleting it at an appropriate timne. + + m_ptbm = ptbm; + } + + m_ff |= ff; + + // Add the fresh Gob to the GobMgr. + + ggobm.AddGob(this); + + return true; +} + +bool SurfaceDecalGob::IsSavable() +{ + // Not currently savable + + return false; +} + +GobType SurfaceDecalGob::GetType() +{ + return kgtSurfaceDecal; +} + +void SurfaceDecalGob::GetClippingBounds(Rect *prc) +{ + Size sizBitmap; + m_ptbm->GetSize(&sizBitmap); + + prc->left = PcFromUwc(m_wx) - ((sizBitmap.cx - 1) / 2); + prc->top = PcFromUwc(m_wy) - (sizBitmap.cy - 1); + prc->right = prc->left + sizBitmap.cx; + prc->bottom = prc->top + sizBitmap.cy; +} + +void SurfaceDecalGob::Draw(DibBitmap *pbm, int xViewOrigin, int yViewOrigin, int nLayer) +{ + // UNDONE: bitmap origin + + if (nLayer == knLayerSurfaceDecal) { + Size sizSrc; + m_ptbm->GetSize(&sizSrc); + + // For now xOrigin is the center column (round up) of the bitmap, + // yOrigin is the bottom row of the bitmap + + int x = PcFromUwc(m_wx) - xViewOrigin - ((sizSrc.cx - 1) / 2); + int y = PcFromUwc(m_wy) - yViewOrigin - (sizSrc.cy - 1); + m_ptbm->BltTo(pbm, x, y, m_pplr->GetSide()); + } +} + +int gnSequenceScorch; + +ScorchGob::ScorchGob() +{ + m_nScorch = 0; + m_nSequence = gnSequenceScorch++; +} + +int ScorchGob::GetSequence() +{ + return m_nSequence; +} + +GobType ScorchGob::GetType() +{ + return kgtScorch; +} + +bool ScorchGob::Init(WCoord wx, WCoord wy, int nScorch) +{ + m_nScorch = nScorch; + return SurfaceDecalGob::Init(wx, wy, 0, NULL, gaptbmScorches[m_nScorch], NULL); +} + +#define knVerScorchGobState 2 +bool ScorchGob::LoadState(Stream *pstm) +{ + byte nVer = pstm->ReadByte(); + if (nVer != knVerScorchGobState) + return false; + m_nScorch = pstm->ReadWord(); + m_ptbm = gaptbmScorches[m_nScorch]; + m_nSequence = pstm->ReadWord(); + if (m_nSequence > gnSequenceScorch) + gnSequenceScorch = m_nSequence; + return SurfaceDecalGob::LoadState(pstm); +} + +bool ScorchGob::SaveState(Stream *pstm) +{ + pstm->WriteByte(knVerScorchGobState); + pstm->WriteWord(m_nScorch); + pstm->WriteWord(m_nSequence); + return SurfaceDecalGob::SaveState(pstm); +} + +bool ScorchGob::IsSavable() +{ + return true; +} + +// +// SceneryGob implementation +// + +bool SceneryGob::InitClass(IniReader *pini) +{ + // No interesting global parameters have been defined for Scenery yet + return true; +} + +SceneryGob::SceneryGob() +{ + m_ptbm = NULL; +} + +SceneryGob::~SceneryGob() +{ +} + +bool SceneryGob::Init(IniReader *pini, FindProp *pfind, const char *pszName) +{ + int tx, ty; + char szBitmap[kcbFilename]; + int cArgs = pini->GetPropertyValue(pfind, "%*d ,%s,%d ,%d", szBitmap, &tx, &ty); + if (cArgs < 3) { + Assert("SceneryGob requires at least 3 valid initialization parameters"); + return false; + } + + return Init(WcFromTc(tx), WcFromTc(ty), 0, szBitmap, pszName); +} + +bool SceneryGob::Init(WCoord wx, WCoord wy, dword ff, const char *pszBitmap, const char *pszName) +{ + m_wx = wx; + m_wy = wy; + + m_ptbm = GetSharedTBitmap((char *)pszBitmap); + if (m_ptbm == NULL) + return false; + + m_ff |= ff | kfGobLayerDepthSorted; + + // Add the fresh Gob to the GobMgr. GobMgr::AddGob assigns this Gob a gid + + ggobm.AddGob(this); + + return true; +} + +#define knVerSceneryGobState 2 +bool SceneryGob::LoadState(Stream *pstm) +{ + byte nVer = pstm->ReadByte(); + if (nVer != knVerSceneryGobState) + return false; + char szBitmap[kcbFilename]; + pstm->ReadString(szBitmap, sizeof(szBitmap)); + m_ptbm = GetSharedTBitmap(szBitmap); + return Gob::LoadState(pstm); +} + +bool SceneryGob::SaveState(Stream *pstm) +{ + pstm->WriteByte(knVerSceneryGobState); + char szBitmap[kcbFilename]; + FindSharedTBitmapFilename(m_ptbm, szBitmap, sizeof(szBitmap)); + pstm->WriteString(szBitmap); + return Gob::SaveState(pstm); +} + +GobType SceneryGob::GetType() +{ + return kgtScenery; +} + +dword SceneryGob::GetSortKey() +{ + return MakeSortKey(m_wy + WcFromPc(m_ptbm->GetBaseline()), m_gid); +} + +void SceneryGob::GetClippingBounds(Rect *prc) +{ + Size sizBitmap; + m_ptbm->GetSize(&sizBitmap); + + // Convert pixel coord size into world coord size + + prc->left = PcFromUwc(m_wx); + prc->top = PcFromUwc(m_wy); + prc->right = prc->left + sizBitmap.cx; + prc->bottom = prc->top + sizBitmap.cy; +} + +void SceneryGob::Draw(DibBitmap *pbm, int xViewOrigin, int yViewOrigin, int nLayer) +{ + if (nLayer == knLayerDepthSorted) { + Size sizSrc; + m_ptbm->GetSize(&sizSrc); + + // For now xOrigin is the center column (round up) of the bitmap, + // yOrigin is the baseline of the bitmap + + int x = PcFromUwc(m_wx) - xViewOrigin; + int y = PcFromUwc(m_wy) - yViewOrigin; + + m_ptbm->BltTo(pbm, x, y, m_pplr->GetSide()); + } +} + +// +// AnimGob implementation +// + +AnimGob *CreateAnimGob(WCoord wx, WCoord wy, word wfAnm, const char *pszAniName, AnimationData *panid, int nStrip, + StateMachineId smidNotify, const char *pszName) +{ + if (!ggobm.IsBelowLimit(knLimitSupport)) + return NULL; + + AnimGob *pgob = new AnimGob(); + Assert(pgob != NULL, "out of memory!"); + if (pgob == NULL) + return NULL; + + if (!pgob->Init(wx, wy, wfAnm, pszAniName, panid, nStrip, smidNotify, pszName)) { + delete pgob; + return NULL; + } + + return pgob; +} + +AnimGob::AnimGob() +{ + m_ff |= kfGobStateMachine; + m_smidNotify = ksmidNull; +} + +AnimGob::~AnimGob() +{ +} + +bool AnimGob::Init(IniReader *pini, FindProp *pfind, const char *pszName) +{ + int tx, ty; + char szAniName[kcbFilename]; + int cArgs = pini->GetPropertyValue(pfind, "%*d ,%s,%d ,%d", szAniName, &tx, &ty); + if (cArgs < 3) { + Assert("AnimGob requires at least 3 valid initialization parameters"); + return false; + } + + return Init(WcFromTc(tx), WcFromTc(ty), 0, szAniName, NULL, 0, ksmidNull, pszName); +} + +bool AnimGob::Init(WCoord wx, WCoord wy, word wfAnm, const char *pszAniName, AnimationData *panid, int nStrip, + StateMachineId smidNotify, const char *pszName) +{ + m_wx = wx; + m_wy = wy; + + bool fFreeAnimationData = false; + if (pszAniName != NULL) { + panid = LoadAnimationData(pszAniName); + fFreeAnimationData = true; + } + + // Must be either passed a panid or be able to load one using pszAniName + + if (panid == NULL) + return false; + + m_ani.Init(panid); + StartAnimation(&m_ani, nStrip, 0, fFreeAnimationData ? kfAniFreeAnimationData : 0); + + m_wfAnm = wfAnm; + m_smidNotify = smidNotify; + + if (m_wfAnm & kfAnmSmokeFireLayer) + m_ff |= kfGobLayerSmokeFire; + else if (m_wfAnm & kfAnmSurfaceDecalLayer) + m_ff |= kfGobLayerSurfaceDecal; + else + m_ff |= kfGobLayerDepthSorted; + + // Add the fresh Gob to the GobMgr. GobMgr::AddGob assigns this Gob a gid + + ggobm.AddGob(this); + + return true; +} + +bool AnimGob::IsSavable() +{ + return false; +} + +bool AnimGob::OnStripDone() +{ + return false; +} + +GobType AnimGob::GetType() +{ + return kgtAnimation; +} + +void AnimGob::GetClippingBounds(Rect *prc) +{ + m_ani.GetBounds(prc); + prc->Offset(PcFromUwc(m_wx), PcFromUwc(m_wy)); +} + +void AnimGob::Draw(DibBitmap *pbm, int xViewOrigin, int yViewOrigin, int nLayer) +{ + int nLayerDesignated; + if (m_wfAnm & kfAnmSmokeFireLayer) + nLayerDesignated = knLayerSmokeFire; + else if (m_wfAnm & kfAnmSurfaceDecalLayer) + nLayerDesignated = knLayerSurfaceDecal; + else + nLayerDesignated = knLayerDepthSorted; + + if (nLayer == nLayerDesignated) { + m_ani.Draw(pbm, PcFromUwc(m_wx) - xViewOrigin, PcFromUwc(m_wy) - yViewOrigin, m_pplr->GetSide()); + } +} + +#if defined(DEBUG_HELPERS) +char *AnimGob::GetName() +{ + return "Animation"; +} +#endif + +int AnimGob::ProcessStateMachineMessage(State st, Message *pmsg) +{ +BeginStateMachine + OnUpdate + if (!AdvanceAnimation(&m_ani)) { + if (m_wfAnm & kfAnmLoop) + StartAnimation(&m_ani, m_ani.GetStrip(), 0, 0); + + // Notify the StateMachine that wants to know when the animation is + // done + + if (m_smidNotify != ksmidNull) + gsmm.SendMsg(kmidAnimationComplete, m_gid, m_smidNotify); + + // Allow inheriters to decide what to do when the animation is done + + bool fDelete = OnStripDone(); + if (!fDelete) + m_unvl.MinSkip(); + + // Remove the AnimGob if requested + + if (fDelete || (m_wfAnm & kfAnmDeleteWhenDone)) { + ggobm.RemoveGob(this); + delete this; + return knDeleted; + } + } + +EndStateMachine +} + +// +// Helper functions +// + +#ifdef DRAW_PATHS +static TBitmap *s_aptbmArrows[9]; + +void LoadArrows() +{ + s_aptbmArrows[0] = LoadTBitmap("arrow0.tbm"); + s_aptbmArrows[1] = LoadTBitmap("arrow1.tbm"); + s_aptbmArrows[2] = LoadTBitmap("arrow2.tbm"); + s_aptbmArrows[3] = LoadTBitmap("arrow3.tbm"); + s_aptbmArrows[4] = LoadTBitmap("arrow4.tbm"); + s_aptbmArrows[5] = LoadTBitmap("arrow5.tbm"); + s_aptbmArrows[6] = LoadTBitmap("arrow6.tbm"); + s_aptbmArrows[7] = LoadTBitmap("arrow7.tbm"); + s_aptbmArrows[8] = LoadTBitmap("x.tbm"); +} + +void FreeArrows() +{ + for (int i = 0; i < 9; i++) { + delete s_aptbmArrows[i]; + s_aptbmArrows[i] = NULL; + } +} + +void DrawArrow(DibBitmap *pbm, int x, int y, Direction dir, Side side) +{ + s_aptbmArrows[dir]->BltTo(pbm, x, y, side); +} +#endif + +void GetHealthColorAndLength(int nNumerator, int nDenominator, int cxWidth, Color *pclr, int *pnLength) secCode7; +void GetHealthColorAndLength(int nNumerator, int nDenominator, int cxWidth, Color *pclr, int *pnLength) +{ + // Break it down into three pieces. + // 50% green (healthy), 25% yellow (heavily damaged), 25% red (close to death) + + if (nNumerator * 2 > nDenominator) + *pclr = GetColor(kiclrGreen); + else if (nNumerator * 4 > nDenominator) + *pclr = GetColor(kiclrYellow); + else + *pclr = GetColor(kiclrRed); + + *pnLength = ((cxWidth - 2) * (long)nNumerator) / nDenominator; + + // The health bar should show at least one pixel of life left until + // the unit/structure is completely destroyed. + + if (*pnLength == 0 && nNumerator > 0) + *pnLength = 1; +} + +void DrawSelectionIndicator(DibBitmap *pbm, Rect *prc, int nNumerator, int nDenominator) +{ + int cxWidth = prc->Width(); + int cyHeight = prc->Height(); + int nCornerWidth = cxWidth / 4; + int nCornerHeight = cyHeight / 4; + int nThickness = 1; + + bool fSelectionBrackets = (gwfPerfOptions & kfPerfSelectionBrackets) != 0; + +#ifdef __CPU_68K + //debug + //BitmapType *pbmpScreen = WinGetBitmap(WinGetDisplayWindow()); + //byte *pbBits = (byte *)BmpGetBits(pbmpScreen); + byte *pbBits = pbm->GetBits(); + + // Hack to make drawing a selection faster if not clipped + // OPT: Could still be faster as TBitmaps + + Size sizDib; + pbm->GetSize(&sizDib); + + if (prc->left >= 0 && prc->top - kcyHealthBar >= 0 && prc->right <= sizDib.cx && prc->bottom <= sizDib.cy) { + Color clrHealth; + int nLength; + GetHealthColorAndLength(nNumerator, nDenominator, cxWidth, &clrHealth, &nLength); + byte *pbDst = pbBits + (long)(prc->top - kcyHealthBar) * sizDib.cx + prc->left; + DrawSelection68k(pbDst, sizDib.cx, cxWidth, cyHeight, nCornerWidth, nCornerHeight, GetColor(kiclrWhite), nLength, clrHealth, gmpiclriclrShadow, fSelectionBrackets); + return; + } +#endif + + // Slow clipped path + + if (fSelectionBrackets) { + Color clr = GetColor(kiclrWhite); + + // Top-left corner + + pbm->Fill(prc->left, prc->top, nCornerWidth, nThickness, clr); + pbm->Fill(prc->left, prc->top, nThickness, nCornerHeight, clr); + + // Top-right corner + + pbm->Fill(prc->right - nCornerWidth, prc->top, nCornerWidth, nThickness, clr); + pbm->Fill(prc->right - nThickness, prc->top, nThickness, nCornerHeight, clr); + + // Bottom-left corner + + pbm->Fill(prc->left, prc->bottom - nThickness, nCornerWidth, nThickness, clr); + pbm->Fill(prc->left, prc->bottom - nCornerHeight, nThickness, nCornerHeight, clr); + + // Bottom-right corner + + pbm->Fill(prc->right - nCornerWidth, prc->bottom - nThickness, nCornerWidth, nThickness, clr); + pbm->Fill(prc->right - nThickness, prc->bottom - nCornerHeight, nThickness, nCornerHeight, clr); + } + + DrawHealthIndicator(pbm, prc, nNumerator, nDenominator); +} + +void DrawHealthIndicator(DibBitmap *pbm, Rect *prc, int nNumerator, int nDenominator) +{ + int cxWidth = prc->Width(); + Color clr; + int nLength; + GetHealthColorAndLength(nNumerator, nDenominator, cxWidth, &clr, &nLength); + +#ifdef __CPU_68K + Size sizDib; + pbm->GetSize(&sizDib); + if (prc->left >= 0 && prc->top - kcyHealthBar >= 0 && prc->right <= sizDib.cx && prc->bottom <= sizDib.cy) { + byte *pbDst = pbm->GetBits() + (long)(prc->top - kcyHealthBar) * sizDib.cx + prc->left; + DrawSelection68k(pbDst, sizDib.cx, cxWidth, 0, 0, 0, 0, nLength, clr, gmpiclriclrShadow, false); + return; + } +#endif + + pbm->Shadow(prc->left, prc->top - kcyHealthBar, cxWidth, kcyHealthBar); + pbm->Fill(prc->left + 1, prc->top - (kcyHealthBar - 1), nLength, kcyHealthBar - 2, clr); +} + +void DrawBuildProgressIndicator(DibBitmap *pbm, Rect *prc, int nNumerator, int nDenominator) +{ + Color clr = GetColor(kiclrYellow); + int cxWidth = prc->right - prc->left; + int nLength = ((cxWidth - 2) * (long)nNumerator) / nDenominator; + + int y = prc->top + prc->Height() / 2; +// gapfnt[0]->DrawText(pbm, "Building...", prc->left, y - gapfnt[0]->GetHeight()); + pbm->Shadow(prc->left, y, cxWidth, 6); + pbm->Fill(prc->left + 1, y + 1, nLength, 4, clr); +} + +void DrawFullnessIndicator(DibBitmap *pbm, Rect *prc, int nPips, int nPipsMax) +{ + // Don't draw anything if Miner/Processor/Warehouse is completely empty + + if (nPips == 0) + return; + + // Allow for overflow as is needed (rarely) when capacity is exceeded + // (e.g., self-destruct a structure while at capacity) + + if (nPips > nPipsMax) + nPips = nPipsMax; + + Color clr = GetColor(kiclrFullnessIndicator); + + int cxyPip = PcFromWc(kwcFullnessPip); + + int cx = (nPipsMax * (cxyPip + 1)) + 1; + int x = prc->left + ((prc->Width() - cx) / 2); + int y = prc->bottom + 1; + pbm->Shadow(x, y - 1, cx, kcyFullnessBar); + + x++; + for (int i = 0; i < nPips; i++, x += cxyPip + 1) + pbm->Fill(x, y, cxyPip, cxyPip, clr); +} + +// Given an offset in x and y, CalcDir returns one of 8 direction values. +// Directions map to 8 points on a compass, incrementing clockwise from N(orth). +// 0 = N/up, 1 = NE, 2 = E/right, 3 = SE, 4 = S/down, 5 = SW, 6 = W/left, 7 = NW +// +// NOTE: this optimized calculation is somewhat inaccurate. The 360 degrees are +// unevenly divided. N/S/E/W get 53.13 degree arcs and the diagonal angles get +// 36.88 degree arcs. + +Direction CalcDir(int dx, int dy) +{ + int dxAbs = abs(dx); + int dyAbs = abs(dy); + + if (dxAbs > dyAbs) { + if (dx >= 0) { + if (dy != 0 && (dxAbs / 2) < dyAbs) { // > 22.5 degrees + if (dy < 0) // heading up + return kdirNE; + else + return kdirSE; + } else { + return kdirE; + } + } else { + if (dy != 0 && (dxAbs / 2) < dyAbs) { // > 22.5 degrees + if (dy < 0) // heading up + return kdirNW; + else + return kdirSW; + } else { + return kdirW; + } + } + } else { + if (dy < 0) { + if (dx != 0 && (dyAbs / 2) < dxAbs) { + if (dx >= 0) + return kdirNE; + else + return kdirNW; + } else { + return kdirN; + } + } else { + if (dx != 0 && (dyAbs / 2) < dxAbs) { + if (dx >= 0) + return kdirSE; + else + return kdirSW; + } else { + return kdirS; + } + } + } +} + +// OPT: this can be optimized to 1/8 is current size by packing +// the values 4 to a byte and by exploiting its diagonal symmetry + +static byte s_mpnArcFromDxDy[16][16] = { + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 3, 2, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 2, 3, 2, 2, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 }, + { 0, 1, 2, 3, 3, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1 }, + { 0, 1, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1 }, + { 0, 1, 1, 2, 3, 3, 3, 3, 2, 2, 2, 2, 2, 1, 1, 1 }, + { 0, 0, 1, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 1 }, + { 0, 0, 1, 2, 2, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2 }, + { 0, 0, 1, 1, 2, 2, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2 }, + { 0, 0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 2, 2 }, + { 0, 0, 1, 1, 1, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 2 }, + { 0, 0, 0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3 }, + { 0, 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3 }, + { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3 }, + { 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3 }, + { 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3 }, +}; + +static int s_adirFromArc[32] = { + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, + 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 0 +}; + +Direction16 CalcDir16(int dx, int dy) +{ + int dxAbs = abs(dx); + int dyAbs = abs(dy); + + while (dxAbs >= 16 || dyAbs >= 16) { + dxAbs >>= 1; + dyAbs >>= 1; + } + + // UNDONE: maybe use 0 return value from s_mpnArc instead + if (dxAbs == 0) + return dy < 0 ? 0 : 8; + if (dyAbs == 0) + return dx >= 0 ? 4 : 12; + + int i = s_mpnArcFromDxDy[dyAbs][dxAbs]; + + if (dxAbs >= dyAbs) + i = 7 - i; + + if (dy > 0) + i = 15 - i; + + if (dx < 0) + i = 31 - i; + + return s_adirFromArc[i]; +} + +// +// GobMgr implementation +// + +#define PpgobFromGid(gid) ((Gob **)((byte *)m_apgobMaster + (gid))) +#define GidFromPpgob(ppgob) ((byte *)(ppgob) - (byte *)m_apgobMaster) +#define MakeUsedEntry(dw) (Gob **)((dword)(dw) & ~1) +#define MakeFreeEntry(dw) (Gob *)((dword)(dw) | 1) +#define IsFreeEntry(dw) ((dword)(dw) & 1) +#define IgidFromWXY(wx, wy) (TcFromWc(wy) * m_ctx + TcFromWc(wx)) + +GobMgr::GobMgr() +{ + m_pgobHead = NULL; + m_pgidMap = NULL; +#ifdef MP_DEBUG_SHAREDMEM + m_apgobMaster = NULL; +#endif + m_cpgobActive = 0; + m_car = 0; + m_cSceneryGobs = 0; + m_cScorchGobs = 0; + m_cSupportGobs = 0; +} + +GobMgr::~GobMgr() +{ + Assert(m_cpgobActive == 0); + Assert(m_pgidMap == NULL); + Assert(m_apgobMaster == NULL); + FreeAreas(); +} + +bool GobMgr::Init(TCoord ctx, TCoord cty, int cpgobMax) +{ + Assert(m_pgidMap == NULL); + Assert(m_apgobMaster == NULL); + + m_ctx = ctx; + m_cty = cty; + m_cpgobMax = cpgobMax; + + // Allocate master Gob list (+ 1 entry for free list terminator) + + Gob **apgobMaster = new Gob *[m_cpgobMax + 1]; + Assert(apgobMaster != NULL, "out of memory!"); + if (apgobMaster == NULL) + return false; + + // Alloc from storage ram + + int cbT = sizeof(Gob **) * (m_cpgobMax + 1); + m_apgobMaster = (Gob **)gmmgr.AllocPtr(cbT); + if (m_apgobMaster == NULL) { + delete apgobMaster; + return false; + } + + // Initialize free list. Notice that the Gob* in the array are holding Gob** when in the free list. + + Gob **ppgobDstT = apgobMaster; + Gob **ppgobSrcT = m_apgobMaster; + Gob **ppgobFreeTail = apgobMaster + m_cpgobMax; + for (; ppgobDstT < ppgobFreeTail; ppgobDstT++, ppgobSrcT++) + *ppgobDstT = MakeFreeEntry((Gob *)(ppgobSrcT + 1)); + *ppgobFreeTail = NULL; + + // Move to storage ram + // Free dyn ram version + + gmmgr.WritePtr(m_apgobMaster, 0, apgobMaster, cbT); + m_ppgobFreeTail = m_apgobMaster + m_cpgobMax; + m_ppgobFreeHead = m_apgobMaster; + delete apgobMaster; + + // Allocate gidMap + + Gid *pgidMap = new Gid[m_ctx * m_cty]; + Assert(pgidMap != NULL, "out of memory!"); + if (pgidMap == NULL) + return false; + + // Initialize gidMap + + int cgid = m_ctx * m_cty; + Gid *pgidT = pgidMap; + for (int i = 0; i < cgid; i++) + *pgidT++ = kgidNull; + + // Alloc from storage ram + // Free dyn ram version + + cbT = m_ctx * m_cty * sizeof(Gid); + m_pgidMap = (Gid *)gmmgr.AllocPtr(cbT); + if (m_pgidMap == NULL) { + delete pgidMap; + return false; + } + gmmgr.WritePtr(m_pgidMap, 0, pgidMap, cbT); + delete pgidMap; + +#ifdef INCL_VALIDATEHEAP + gmmgr.Validate(); +#endif + + return true; +} + +#define knVerGobMgr 5 + +bool GobMgr::SaveState(Stream *pstm) +{ + pstm->WriteByte(knVerGobMgr); + + // Write areas + + pstm->WriteWord(m_car); + for (int iar = 0; iar < m_car; iar++) { + Area *par = &m_aar[iar]; + pstm->Write(par, sizeof(*par)); + if (par->aare != NULL) + pstm->Write(par->aare, par->careAlloc * ELEMENTSIZE(par->aare)); + } + + return pstm->IsSuccess(); +} + +bool GobMgr::LoadState(Stream *pstm) +{ + byte nVer = pstm->ReadByte(); + if (nVer != knVerGobMgr) + return false; + + // Read areas + + m_car = pstm->ReadWord(); + for (int iar = 0; iar < m_car; iar++) { + Area *par = &m_aar[iar]; + pstm->Read(par, sizeof(*par)); + if (par->aare != NULL) { + par->aare = new AreaEntry[par->careAlloc]; + if (par->aare == NULL) { + pstm->Read(gpbScratch, par->careAlloc * ELEMENTSIZE(par->aare)); + memset(par, 0, sizeof(*par)); + par->iareFree = (word)-1; + par->iareHead = (word)-1; + } else { + pstm->Read(par->aare, par->careAlloc * ELEMENTSIZE(par->aare)); + } + } + } + + return pstm->IsSuccess(); +} + +// Called between levels + +void GobMgr::Reset() +{ + // Free master Gob list + + if (m_apgobMaster != NULL) { + gmmgr.FreePtr(m_apgobMaster); + m_apgobMaster = NULL; + } + + m_cpgobActive = 0; + + // Free gidMap + + if (m_pgidMap != NULL) { + gmmgr.FreePtr(m_pgidMap); + m_pgidMap = NULL; + } + + // Free all the Gobs + + Gob *pgobT = m_pgobHead; + while (pgobT != NULL) { + Gob *pgobNext = pgobT->m_pgobNext; + delete pgobT; + pgobT = pgobNext; + } + m_pgobHead = NULL; + + // Free all area lists + + FreeAreas(); + + // Reset gob counts + + m_cSceneryGobs = 0; + m_cScorchGobs = 0; + m_cSupportGobs = 0; +} + +void GobMgr::AddGob(Gob *pgob, Gid gid) +{ + // Add Gob to master Gob list + // New Gob entries are taken from the HEAD of the free list + + // Make sure we haven't already run out of free entries + + Assert(*m_ppgobFreeHead != NULL); + Assert(m_cpgobActive != m_cpgobMax); + + // Todo: return failure and deal with failure at some point + + if (m_cpgobActive >= m_cpgobMax) + return; + + // When loading a saved game, we use the same gids for gobs. + // When loading a game fresh, we take the next available gid. + + if (gid == kgidNull) { + // Add to list; take the next available gid + + Gob **ppgob = m_ppgobFreeHead; + Assert(*ppgob == MakeFreeEntry(*ppgob)); + m_ppgobFreeHead = MakeUsedEntry(*ppgob); + gmmgr.WritePtr(m_apgobMaster, (byte *)ppgob - (byte *)m_apgobMaster, &pgob, sizeof(Gob *)); + //*ppgob = pgob; + gid = GidFromPpgob(ppgob); + } else { + + // We have a gid to use. Pull it out of the free list. + // Note to self: try to make this more complicated if possible. More '*''s and macros would help. + + bool fFound = false; + + // handle head case seperately because m_ppgobFreeHead's contents are not marked free + + Gob **ppgobUse = PpgobFromGid(gid); + if (m_ppgobFreeHead == ppgobUse) { + fFound = true; + m_ppgobFreeHead = MakeUsedEntry((Gob **)*ppgobUse); + } else { + + // ppgobUse is now the address of the apgobMaster slot we're going to put a gob in. + // find the slot that points at this one in the free list & point it at the next entry + + for (Gob ***pppgobT = (Gob ***)m_ppgobFreeHead; *pppgobT != NULL; pppgobT = (Gob ***)MakeUsedEntry(*pppgobT)) { + if (MakeUsedEntry(*pppgobT) == ppgobUse) { + gmmgr.WritePtr(m_apgobMaster, (byte *)pppgobT - (byte *)m_apgobMaster, ppgobUse, sizeof(Gob *)); + //*pppgobT = (Gob **)*ppgobUse; + + // update the tail pointer if necessary + + if (m_ppgobFreeTail == ppgobUse) + m_ppgobFreeTail = (Gob **) pppgobT; + + fFound = true; + break; + } + } + } + Assert(fFound); + if (!fFound) + return; + gmmgr.WritePtr(m_apgobMaster, (byte *)ppgobUse - (byte *)m_apgobMaster, &pgob, sizeof(Gob *)); + //*ppgobUse = pgob; + } + + // Assign gid, link it in + + Assert(gid < (m_cpgobMax + 1) * sizeof(Gob *)); + Assert((gid & 3) == 0); + pgob->m_gid = gid; + pgob->m_pgobNext = m_pgobHead; + m_pgobHead = pgob; + + m_cpgobActive++; + + // Add Gob to gidMap + + MoveGob(pgob, -1, -1, pgob->m_wx, pgob->m_wy); + + // Increment gob counts + + TrackGobCounts(pgob, true); + +#ifdef INCL_VALIDATEHEAP + gmmgr.Validate(); +#endif +} + +void GobMgr::RemoveGob(Gob *pgob) +{ + // Ensure this gob isn't in any area lists still + +#if defined(WIN) && !defined(CE) && defined(DEBUG) + // Validate not already in these areas + + for (int iar = 0; iar < m_car; iar++) { + Area *par = &m_aar[iar]; + for (word iareT = par->iareHead; iareT != (word)-1; iareT = par->aare[iareT].iareNext) + Assert(par->aare[iareT].gid != pgob->m_gid); + } +#endif + + // Mark this gob invalid in the update map so this area gets drawn correctly + + pgob->Invalidate(); + + // Remove Gob from gidMap + + MoveGob(pgob, pgob->m_wx, pgob->m_wy, -1, -1); + + // Remove Gob from master Gob list + // Freed Gob list entries go on the END of the free list + + Gob **ppgob = PpgobFromGid(pgob->m_gid); + pgob->m_gid = kgidNull; + Gob *pgobT = NULL; + gmmgr.WritePtr(m_apgobMaster, (byte *)ppgob - (byte *)m_apgobMaster, &pgobT, sizeof(Gob *)); + //*ppgob = NULL; // Mark new terminator + pgobT = MakeFreeEntry(ppgob); + gmmgr.WritePtr(m_apgobMaster, (byte *)m_ppgobFreeTail - (byte *)m_apgobMaster, &pgobT, sizeof(Gob *)); + //*m_ppgobFreeTail = MakeFreeEntry(ppgob); + m_ppgobFreeTail = ppgob; + + // UNDONE: OPT: removing Gobs requires a traversal of the Gob list + + for (Gob** ppgobT = &m_pgobHead; *ppgobT != NULL; ppgobT = &(*ppgobT)->m_pgobNext) { + if (*ppgobT == pgob) { + *ppgobT = pgob->m_pgobNext; + pgob->m_pgobNext = NULL; + break; + } + } + + m_cpgobActive--; + + // Decrement gob counts + + TrackGobCounts(pgob, false); + +#ifdef INCL_VALIDATEHEAP + gmmgr.Validate(); +#endif +} + +void GobMgr::TrackGobCounts(Gob *pgob, bool fIncrement) +{ + if (!(pgob->GetFlags() & kfGobUnit)) { + int nDir = fIncrement ? 1 : -1; + switch (pgob->GetType()) { + case kgtScenery: + m_cSceneryGobs += nDir; + break; + + case kgtScorch: + m_cScorchGobs += nDir; + break; + + default: + m_cSupportGobs += nDir; + break; + } + } + + // Validate counts so we know of errors + +#ifdef DEBUG + if (ggame.IsMultiplayer()) { + for (Side side = ksideNeutral; side < kcSides; side++) { + Player *pplr = gplrm.GetPlayer(side); + if (pplr == NULL) + continue; + + int cStructGobs = pplr->GetUnitInstanceCountFromMask(kumStructures); + Assert(cStructGobs >= 0 && cStructGobs <= gcStructGobsLimitMP); + + int cMuntGobs = pplr->GetUnitInstanceCountFromMask(kumMobileUnits); + Assert(cMuntGobs >= 0 && cMuntGobs <= gcMuntGobsLimitMP); + } + } else { + int cMuntGobsComputer = gplrm.GetUnitInstanceCountFromMask(kumMobileUnits, kfPlrComputer); + Assert(cMuntGobsComputer >= 0 && cMuntGobsComputer <= gcMuntGobsComputerLimitSP); + + int cStructGobsComputer = gplrm.GetUnitInstanceCountFromMask(kumStructures, kfPlrComputer); + Assert(cStructGobsComputer >= 0 && cStructGobsComputer <= gcStructGobsComputerLimitSP + gcStructGobsComputerDeltaSP); + + int cMuntGobsHuman = gplrm.GetUnitInstanceCountFromMask(kumMobileUnits, 0); + Assert(cMuntGobsHuman >= 0 && cMuntGobsHuman <= gcMuntGobsHumanLimitSP); + + int cStructGobsHuman = gplrm.GetUnitInstanceCountFromMask(kumStructures, 0); + Assert(cStructGobsHuman >= 0 && cStructGobsHuman <= gcStructGobsHumanLimitSP + gcStructGobsHumanDeltaSP); + + Assert(m_cSceneryGobs >= 0 && m_cSceneryGobs <= gcSceneryGobsLimit); + Assert(m_cScorchGobs >= 0 && m_cScorchGobs <= gcScorchGobsLimit); + Assert(m_cSupportGobs >= 0 && m_cSupportGobs <= gcSupportGobsLimit); + } +#endif +} + +bool GobMgr::IsBelowLimit(int nLimit, Player *pplr) +{ + switch (nLimit) { + case knLimitStruct: + { + Assert(pplr != NULL); + if (!ggame.IsMultiplayer()) { + int cQueued = BuilderGob::GetGlobalQueuedCount(pplr, kfUntcStructureBuilder); + if (pplr->GetFlags() & kfPlrComputer) { + int cStructGobsComputer = gplrm.GetUnitInstanceCountFromMask(kumStructures, kfPlrComputer); + return (cStructGobsComputer + cQueued) < gcStructGobsComputerLimitSP + gcStructGobsComputerDeltaSP; + } else { + int cStructGobsHuman = gplrm.GetUnitInstanceCountFromMask(kumStructures, 0); + return (cStructGobsHuman + cQueued) < gcStructGobsHumanLimitSP + gcStructGobsHumanDeltaSP; + } + } else { + int cQueued = BuilderGob::GetQueuedCount(pplr, kfUntcStructureBuilder); + return (pplr->GetUnitInstanceCountFromMask(kumStructures) + cQueued) < gcStructGobsLimitMP; + } + } + break; + + case knLimitMobileUnit: + { + Assert(pplr != NULL); + if (!ggame.IsMultiplayer()) { + int cQueued = BuilderGob::GetGlobalQueuedCount(pplr, kfUntcMobileUnitBuilder); + if (pplr->GetFlags() & kfPlrComputer) { + int cMuntGobsComputer = gplrm.GetUnitInstanceCountFromMask(kumMobileUnits, kfPlrComputer); + return (cMuntGobsComputer + cQueued) < gcMuntGobsComputerLimitSP; + } else { + int cMuntGobsHuman = gplrm.GetUnitInstanceCountFromMask(kumMobileUnits, 0); + return (cMuntGobsHuman + cQueued) < gcMuntGobsHumanLimitSP; + } + } else { + int cQueued = BuilderGob::GetQueuedCount(pplr, kfUntcMobileUnitBuilder); + return (pplr->GetUnitInstanceCountFromMask(kumMobileUnits) + cQueued) < gcMuntGobsLimitMP; + } + } + break; + + case knLimitScenery: + return m_cSceneryGobs < gcSceneryGobsLimit; + + case knLimitScorch: + return m_cScorchGobs < gcScorchGobsLimit; + + case knLimitSupport: + return m_cSupportGobs < gcSupportGobsLimit; + } + + Assert(); + return false; +} + +bool GobMgr::MoveGob(Gob *pgob, WCoord wxOld, WCoord wyOld, WCoord wxNew, WCoord wyNew) +{ + Assert(wxOld >= -1 && TcFromWc(wxOld) < m_ctx && wyOld >= -1 && TcFromWc(wyOld) < m_cty); + Assert(wxNew >= -1 && TcFromWc(wxNew) < m_ctx && wyNew >= -1 && TcFromWc(wyNew) < m_cty); + Assert(pgob->m_gid < (m_cpgobMax + 1) * sizeof(Gob *)); + Assert((pgob->m_gid & 3) == 0); + + int igidOld = -1; + if (wxOld != -1 && wyOld != -1) + igidOld = IgidFromWXY(wxOld, wyOld); + + int igidNew = -1; + if (wxNew != -1 && wyNew != -1) + igidNew = IgidFromWXY(wxNew, wyNew); + + if (igidOld == igidNew) + return false; + + if (igidOld != -1) { + // Remove Gob from old location within gidMap + + Gid *pgidT = m_pgidMap + igidOld; + Assert((*pgidT & kwfGidEndMarker) == 0); + if (*pgidT == pgob->m_gid) { + Gid gidT = pgob->m_gidNext; + gmmgr.WritePtr(m_pgidMap, (byte *)pgidT - (byte *)m_pgidMap, &gidT, sizeof(gidT)); + } else { + while (((*pgidT) & kwfGidEndMarker) == 0) { + if (*pgidT == pgob->m_gid) { + *pgidT = pgob->m_gidNext; + Assert(pgob->m_gidNext != pgob->m_gid); + pgob->m_gidNext = kgidNull; + break; + } + pgidT = &(*PpgobFromGid(*pgidT))->m_gidNext; + } + } + } + + if (igidNew != -1) { + // Add Gob to new location within gidMap + + Gid *pgidT = m_pgidMap + igidNew; + pgob->m_gidNext = *pgidT; + Assert(pgob->m_gidNext != pgob->m_gid); + Gid gidT = pgob->m_gid; + gmmgr.WritePtr(m_pgidMap, (byte *)pgidT - (byte *)m_pgidMap, &gidT, sizeof(gidT)); + //*pgidT = pgob->m_gid; + } + +#ifdef INCL_VALIDATEHEAP + gmmgr.Validate(); +#endif + + return true; +} + +UnitGob *GobMgr::GetUnitGob(TCoord tx, TCoord ty) +{ + // Gets the unit gob at this spot. Checks shadowed gids too. + + Gid gid = m_pgidMap[ty * m_ctx + tx]; + while (true) { + if (gid == kgidNull) + return NULL; + Gob *pgob = GetGob(gid & ~kwfGidEndMarker, false); + if (pgob == NULL) + return NULL; + if (pgob->GetFlags() & kfGobUnit) + return (UnitGob *)pgob; + if (gid & kwfGidEndMarker) + return NULL; + gid = pgob->m_gidNext; + } +} + +Gob *GobMgr::GetShadowGob(TCoord tx, TCoord ty) +{ + // Get gid shadowed "inside" the end of list marker + // Don't bother clipping + + Gid gid = m_pgidMap[ty * m_ctx + tx]; + while (true) { + if (gid == kgidNull) + return NULL; + if ((gid & kwfGidEndMarker) != 0) + return GetGob(gid & ~kwfGidEndMarker, false); + Gob *pgob = GetGob(gid, false); + if (pgob == NULL) + return NULL; + gid = pgob->m_gidNext; + } +} + +void GobMgr::ShadowGob(Gob *pgob, TCoord tx, TCoord ty, int ctx, int cty) +{ + // Shadow this gid "inside" the end of list marker. + + for (TCoord tyT = ty; tyT < ty + cty; tyT++) { + for (TCoord txT = tx; txT < tx + ctx; txT++) { + Gid *pgid = &m_pgidMap[tyT * m_ctx + txT]; + if ((*pgid & kwfGidEndMarker) != 0) { + Gid gidT = pgob->m_gid | kwfGidEndMarker; + gmmgr.WritePtr(m_pgidMap, (byte *)pgid - (byte *)m_pgidMap, &gidT, sizeof(gidT)); + } else { + while ((*pgid & kwfGidEndMarker) == 0) { + Gob *pgob = *PpgobFromGid(*pgid); + pgid = &pgob->m_gidNext; + } + Assert((*pgid & kwfGidEndMarker) != 0); + *pgid = pgob->m_gid | kwfGidEndMarker; + } + } + } +} + +void GobMgr::UnshadowGob(Gob *pgob, TCoord tx, TCoord ty, int ctx, int cty) +{ + // Unshadow this gob's gid from the end of list marker + + for (TCoord tyT = ty; tyT < ty + cty; tyT++) { + for (TCoord txT = tx; txT < tx + ctx; txT++) { + Gid *pgid = &m_pgidMap[tyT * m_ctx + txT]; + if ((*pgid & kwfGidEndMarker) != 0) { + Gid gidT = kgidNull; + gmmgr.WritePtr(m_pgidMap, (byte *)pgid - (byte *)m_pgidMap, &gidT, sizeof(gidT)); + } else { + while ((*pgid & kwfGidEndMarker) == 0) { + Gob *pgob = *PpgobFromGid(*pgid); + pgid = &pgob->m_gidNext; + } + Assert((*pgid & kwfGidEndMarker) != 0); + if ((*pgid & ~kwfGidEndMarker) == pgob->m_gid) + *pgid = kgidNull; + } + } + } +} + +AreaMask GobMgr::CalcAreaMask(TCoord tx, TCoord ty, int ctx, int cty) +{ + AreaMask am = 0; + for (TCoord tyT = ty; tyT < ty + cty; tyT++) { + for (TCoord txT = tx; txT < tx + ctx; txT++) { + am |= CalcAreaMask(txT, tyT); + } + } + return am; +} + +// Rects could be sorted to speed this up (as if it's a problem) +// Also AreaMask could be stored in UnitGob for a speed boost + +AreaMask GobMgr::CalcAreaMask(TCoord tx, TCoord ty) +{ + if (gsim.GetLevel()->GetTerrainMap()->GetTerrainType(tx, ty) != kttArea) + return 0; + AreaMask am = 0; + for (int nArea = 0; nArea < m_car; nArea++) { + if (m_aar[nArea].trc.PtIn(tx, ty)) { + am |= (1UL << nArea); + } + } + Assert(am != 0); + return am; +} + +// Only called when there is a AreaMask change (rare). +// I have this in assembly (from pocketchess) if needed. + +int GobMgr::GetAreasFromMask(AreaMask am, int *pnArea) +{ + Assert(kcAreasMax <= 32); + int cArea = 0; + for (int n = 0; n < kcAreasMax && am != 0; n++) { + if (am & (1UL << n)) { + *pnArea++ = n; + cArea++; + am &= (am - 1); + } + } + return cArea; +} + +void GobMgr::MoveGobBetweenAreas(Gid gid, AreaMask amOld, AreaMask amNew) +{ + if (amOld == amNew) + return; + AreaMask amRemove = amOld & ~amNew; + if (amRemove != 0) + RemoveGobFromAreas(gid, amRemove); + AreaMask amAdd = amNew & ~amOld; + if (amAdd != 0) + AddGobToAreas(gid, amAdd); +} + +#define kcareGrow 32 + +void GobMgr::AddGobToAreas(Gid gid, AreaMask am) +{ + // Loop through areas + + int aiar[kcAreasMax]; + int car = GetAreasFromMask(am, aiar); + +#if defined(WIN) && !defined(CE) && defined(DEBUG) + // Validate not already in these areas + + for (int iar = 0; iar < car; iar++) { + Area *par = &m_aar[aiar[iar]]; + for (word iareT = par->iareHead; iareT != (word)-1; iareT = par->aare[iareT].iareNext) + Assert(par->aare[iareT].gid != gid); + } +#endif + + // Add to the following areas + + for (int iar = 0; iar < car; iar++) { + // Add to list. Need more room? + + Area *par = &m_aar[aiar[iar]]; + if (par->iareFree == (word)-1) { + Assert(par->careAlloc == par->careUsed); + int careNew = par->careAlloc + kcareGrow; + AreaEntry *aare = new AreaEntry[careNew]; + if (aare == NULL) + continue; + if (par->aare == NULL) { + par->aare = aare; + } else { + memcpy(aare, par->aare, ELEMENTSIZE(aare) * par->careAlloc); + delete par->aare; + par->aare = aare; + } + + // Initialize onto free list + + par->iareFree = par->careAlloc; + for (int iareNew = par->iareFree; iareNew < careNew - 1; iareNew++) + par->aare[iareNew].iareNext = iareNew + 1; + par->aare[careNew - 1].iareNext = (word)-1; + + // New size + + par->careAlloc = careNew; + } + + // Take one from the free list + + int iareNew = par->iareFree; + Assert(iareNew != (word)-1); + par->iareFree = par->aare[iareNew].iareNext; + + // Put on used list + + par->aare[iareNew].iareNext = par->iareHead; + par->iareHead = iareNew; + + // This gob lives here + + par->careUsed++; + par->aare[iareNew].gid = gid; + + // Update info about gobs in this area + + UnitGob *punt = (UnitGob *)*PpgobFromGid(gid); + Assert(punt == (UnitGob *)GetGob(gid, false)); + if (punt != NULL && (punt->m_ff & kfGobUnit)) { + par->sidm |= GetSideMask(punt->GetSide()); + par->um |= punt->GetConsts()->um; + } + } +} + +void GobMgr::RemoveGobFromAreas(Gid gid, AreaMask am) +{ + // Loop through areas + + int aiar[kcAreasMax]; + int car = GetAreasFromMask(am, aiar); + +#if defined(WIN) && !defined(CE) && defined(DEBUG) + // Validate already in these areas + + for (int iar = 0; iar < car; iar++) { + Area *par = &m_aar[aiar[iar]]; + bool fFound = false; + for (word iareT = par->iareHead; iareT != (word)-1; iareT = par->aare[iareT].iareNext) { + if (par->aare[iareT].gid == gid) { + fFound = true; + break; + } + } + Assert(fFound); + } +#endif + + // Remove from the following areas + + for (int iar = 0; iar < car; iar++) { + // Remove from area list, put entry on free list + // Recalc side and unit masks into along the way + + Area *par = &m_aar[aiar[iar]]; + par->sidm = 0; + par->um = 0; + + word *piareNext = &par->iareHead; + while (*piareNext != (word)-1) { + AreaEntry *pare = &par->aare[*piareNext]; + if (pare->gid == gid) { + // Remove this gid; should happen only once + + word iareT = *piareNext; + *piareNext = pare->iareNext; + pare->iareNext = par->iareFree; + par->iareFree = iareT; + par->careUsed--; + Assert((short)par->careUsed >= 0); + + // piareNext is still point to last which is what we want + } else { + // Update info about gobs in this area + + UnitGob *punt = (UnitGob *)*PpgobFromGid(pare->gid); + Assert(punt == (UnitGob *)GetGob(pare->gid, false)); + if (punt != NULL && (punt->m_ff & kfGobUnit)) { + par->sidm |= GetSideMask(punt->GetSide()); + par->um |= punt->GetConsts()->um; + } + + // Cycle to the next + + piareNext = &pare->iareNext; + } + } + } +} + +Gob *GobMgr::EnumGobsInArea(Enum *penm, int nArea, SideMask sidm, UnitMask um) +{ + // First time? Do overall filter match. + // Note kEnmFirst is -1, the same value used here for the "end of list" + // marker. + + Area *par = &m_aar[nArea]; + if (penm->m_dwUser == kEnmFirst) { + if (!(par->sidm & sidm)) + return NULL; + if (!(par->um & um)) + return NULL; + penm->m_dwUser = 0; + penm->m_wUser = par->iareHead; + } + + while (penm->m_wUser != (word)-1) { + AreaEntry *pare = &par->aare[penm->m_wUser]; + penm->m_wUser = pare->iareNext; + + // Check against filter + + Gob *pgobT = (Gob *)*PpgobFromGid(pare->gid); + Assert(pgobT == GetGob(pare->gid, false)); + Assert(pgobT->GetFlags() & kfGobUnit); + if (!(pgobT->GetFlags() & kfGobUnit)) + continue; + UnitGob *puntT = (UnitGob *)pgobT; + if (!(GetSideMask(puntT->GetSide()) & sidm)) + continue; + if (!(puntT->GetConsts()->um & um)) + continue; + + // Matches filter, return it. + + // NOTE: this doesn't return the NEAREST Gob, just the first + // found within the area + + return pgobT; + } + + return NULL; +} + +bool GobMgr::CheckUnitsInArea(int nArea, SideMask sidm, UnitMask um) +{ + // Only checks if unit type and given side, not if unit type is of given side + + Assert(nArea >= 0 && nArea < m_car); + Area *par = &m_aar[nArea]; + if (!(par->sidm & sidm)) + return false; + if (!(par->um & um)) + return false; + return true; +} + +bool GobMgr::IsGobWithinArea(Gob *pgobTarget, int nArea) +{ + TPoint tpt; + pgobTarget->GetTilePosition(&tpt); + Assert(nArea >= 0 && nArea < m_car); + return m_aar[nArea].trc.PtIn(tpt.tx, tpt.ty); +} + +void GobMgr::GetAreaRect(int nArea, TRect *ptrc, Side side) +{ + if (side == ksideNeutral || nArea >= 0) { + Assert(nArea < m_car); + *ptrc = m_aar[nArea].trc; + } else { + if (nArea == knAreaLastDiscovery) { + Assert(side != ksideNeutral); + Player *pplr = gplrm.GetPlayer(side); + TPoint tpt = pplr->GetDiscoverPoint(); + ptrc->left = tpt.tx; + ptrc->top = tpt.ty; + ptrc->right = tpt.tx + 1; + ptrc->bottom = tpt.ty + 1; + } + } +} + +bool GobMgr::LoadAreas(IniReader *pini) +{ + // Initialize areas first + + for (int iar = 0; iar < ARRAYSIZE(m_aar); iar++) { + Area *par = &m_aar[iar]; + memset(par, 0, sizeof(*par)); + par->iareFree = (word)-1; + par->iareHead = (word)-1; + } + m_car = 0; + + // Might as well have the TriggerMgr own Areas as well for now + + char szProp[128]; + FindProp findArea; + while (pini->FindNextProperty(&findArea, "Areas", szProp, sizeof(szProp))) { +#ifdef DEBUG_HELPERS + strncpyz(m_aszAreaNames[m_car], szProp, 50); +#endif + int nLeft, nTop, nWidth, nHeight; + if (!pini->GetPropertyValue(&findArea, "%d,%d,%d,%d", &nLeft, &nTop, &nWidth, &nHeight)) + return false; + m_aar[m_car].trc.Set(nLeft, nTop, nLeft + nWidth, nTop + nHeight); + m_car++; + Assert(m_car <= kcAreasMax); + } + return true; +} + +void GobMgr::FreeAreas() +{ + for (int iar = 0; iar < m_car; iar++) { + delete m_aar[iar].aare; + m_aar[iar].aare = NULL; + } + m_car = 0; +} + +Gob *GobMgr::GetGob(Gid gid, bool fActiveOnly) +{ + // Also catches kgidNull + + if (gid & kwfGidEndMarker) { + return NULL; + } + Assert(gid < (m_cpgobMax + 1) * sizeof(Gob *)); + + Gob *pgob = *PpgobFromGid(gid); + if (pgob == NULL) + return NULL; + + // Returns NULL if gid is invalid, i.e., now points to a free entry + // Free entries have their low bit set, used entries always have it clear. + + if (IsFreeEntry(pgob)) + return NULL; + + // Most callers should consider a Gob as non-existant if it is no longer + // Active + + if (fActiveOnly) { + if ((pgob->GetFlags() & kfGobActive) == 0) { + return NULL; + } + } + + return pgob; +} + +// Return true if a same-side structure is within the required distance +// (kctMaxDistFromNeighbor) of the passed-in location+structure size. NOTE: +// this routine does not guarantee that there isn't something in the way. +// NOTE: this routine doesn't need to live in GobMgr + +const int kctMaxDistFromNeighbor = 3; + +bool GobMgr::IsStructurePlacementValid(StructConsts *pstruc, TCoord tx, TCoord ty, Player *pplr) +{ + // No part of the structure can be off an edge of the map + + if (tx < 0 || ty < 0 || tx + pstruc->ctxReserve > m_ctx || ty + pstruc->ctyReserve > m_cty) { + return false; + } + + // No part of the structure can be under the inaccessible part of the + // minimap (when the map is scrolled to the bottom right corner) + + Form *pfrmMiniMap = gpmfrmm->GetFormPtr(kidfMiniMap); + if (pfrmMiniMap != NULL) { + Rect rcMiniMap; + pfrmMiniMap->GetRect(&rcMiniMap); + TCoord ctxMap, ctyMap; + ggobm.GetMapSize(&ctxMap, &ctyMap); + WCoord wxMiniMapLeft = WcFromTc(ctxMap) - WcFromUpc(rcMiniMap.Width()); + WCoord wyMiniMapTop = WcFromTc(ctyMap) - WcFromUpc(rcMiniMap.Height()); + WCoord wxPlacementLeft = WcFromTc(tx); + WCoord wyPlacementTop = WcFromTc(ty); + if (wxPlacementLeft > (wxMiniMapLeft - kwcTile) && + wyPlacementTop > (wyMiniMapTop - kwcTile)) { + return false; + } + } + + TRect trcSearch; + trcSearch.Set(tx - kctMaxDistFromNeighbor, ty - kctMaxDistFromNeighbor, + tx + pstruc->ctx + kctMaxDistFromNeighbor, ty + pstruc->cty + kctMaxDistFromNeighbor); + + // We let the player put towers a little farther away from neighboring + // structures -- but not chain them! (see below) + + if (pstruc->um & kumTowers) + trcSearch.Inflate(1, 1); + + // Don't search off any edge of the map + + if (trcSearch.left < 0) + trcSearch.left = 0; + if (trcSearch.right > m_ctx) + trcSearch.right = m_ctx; + if (trcSearch.top < 0) + trcSearch.top = 0; + if (trcSearch.bottom > m_cty) + trcSearch.bottom = m_cty; + + for (int tyT = trcSearch.top; tyT < trcSearch.bottom; tyT++) { + for (int txT = trcSearch.left; txT < trcSearch.right; txT++) { + Gob *pgob = GetShadowGob(txT, tyT); + if (pgob == NULL) + continue; + if ((pgob->GetFlags() & (kfGobStructure | kfGobActive)) == (kfGobStructure | kfGobActive)) { + if (pgob->GetOwner() != pplr) + continue; + + // Special hack to keep players from building long chains of towers + + GobType gt = pgob->GetType(); + if (gt != kgtMachineGunTower && gt != kgtRocketTower) { + return true; + } + } + } + } + + return false; +} + +struct GobSort { // gs + Gob *pgob; + dword key; +}; + +void SortGobs(Gob **apgob, int cpgob) secGob; + +#if 0 +// Called by Simulation::Draw to identify and sort the on-screen Gobs +// prior to drawing them. + +int GobMgr::FindGobs(const Rect *prcBounds, Gob **apgob, int cpgobMax, byte *pbFogMap) +{ + TCoord txLeft, txRight, tyTop, tyBottom; + txLeft = TcFromUpc(prcBounds->left); + + // This bounds check here is to avoid exceeding the range of the + // pixel -> world coord lookup table. + + int xRight = prcBounds->right + gcxTile - 1; + if (xRight >= kpcMax) + xRight = kpcMax - 1; + txRight = TcFromUpc(xRight); + tyTop = TcFromUpc(prcBounds->top); + int yBottom = prcBounds->bottom + gcyTile - 1; + if (yBottom >= kpcMax) + yBottom = kpcMax - 1; + tyBottom = TcFromUpc(yBottom); + + // Expand the cells examined to include any that may contain Gobs + // that intersect the specified bounding rectangle. + // This carries some assumptions about the maximum size of Gob + // clippinging rectangle and its relation to its origin. + + // Maximum Gob clipping rectangle is: + // -3/+2 tiles wide from its origin + // -3/+1 tiles high from its origin + + txLeft = _max(0, txLeft - 3); + txRight = _min(m_ctx - 1, txRight + 2); + tyTop = _max(0, tyTop - 3); + tyBottom = _min(m_cty - 1, tyBottom + 1); + + int ctx = txRight - txLeft; + + int cpgob = 0; + Gid *pgidLeft = m_pgidMap + (tyTop * m_ctx) + txLeft; + for (int ty = tyTop; ty <= tyBottom; ty++, pgidLeft += m_ctx) { + + Gid *pgidRight = pgidLeft + ctx; + + for (Gid *pgidT = pgidLeft; pgidT <= pgidRight; pgidT++) { + Gid gidT = *pgidT; + Gid gidNext; + + for (; (gidT & kwfGidEndMarker) == 0; gidT = gidNext) { + Gob *pgobT = *PpgobFromGid(gidT); + gidNext = pgobT->m_gidNext; + dword ffGob = pgobT->GetFlags(); + + // UNDONE: OPT: this could be sped up a little. We only need + // to test the clipping bounds of Gobs found when scanning the + // expanded edges of the bounds. + + Rect rcClip; + pgobT->GetClippingBounds(&rcClip); + if (rcClip.left >= prcBounds->right) + goto lbNextGob; + if (rcClip.right <= prcBounds->left) + goto lbNextGob; + if (rcClip.top >= prcBounds->bottom) + goto lbNextGob; + if (rcClip.bottom <= prcBounds->top) + goto lbNextGob; + + // Check if totally under fog + // Fog disappears quickly; check top left right away + + if (pbFogMap != NULL) { + int txLeft = TcFromPc(rcClip.left); + if (txLeft < 0) + txLeft = 0; + int tyTop = TcFromPc(rcClip.top); + if (tyTop < 0) + tyTop = 0; + byte *pbFogT = pbFogMap + m_ctx * tyTop + txLeft; + if (IsFogOpaque(*pbFogT)) { + int xT = rcClip.right + gcxTile - 1; + if (xT >= kpcMax) + xT = kpcMax - 1; + int txRight = TcFromPc(xT); + if (txRight > m_ctx) + txRight = m_ctx; + int yT = rcClip.bottom + gcyTile - 1; + if (yT >= kpcMax) + yT = kpcMax - 1; + int tyBottom = TcFromPc(yT); + if (tyBottom > m_cty) + tyBottom = m_cty; + bool fOpaque = true; + for (int tyT = tyTop; tyT < tyBottom; tyT++) { + for (int txT = txLeft; txT < txRight; txT++) { + word wFog = *pbFogT++; + if (!IsFogOpaque(wFog)) { + fOpaque = false; + break; + } + } + if (!fOpaque) + break; + pbFogT += m_ctx - (txRight - txLeft); + } + if (fOpaque) + goto lbNextGob; + } + } + + // Remember if this gob was not visible last frame; we'll need this info later + + if (!(ffGob & kfGobVisibleLastFrame)) { + ffGob |= kfGobTransitioningToVisible; + } else { + ffGob &= ~kfGobTransitioningToVisible; + } + + // Remember that this gob was visible in this frame + + ffGob = (ffGob & ~kfGobIncludeFindVisible) | kfGobVisibleLastFrame; + pgobT->SetFlags(ffGob); + + // Add gob to list + + apgob[cpgob++] = pgobT; + if (cpgob == cpgobMax) + goto lbFull; + continue; + +lbNextGob: + // Gob not visible, but add to list anyway if asked + // Clear the "gob was visible this frame" bit since it isn't visible this frame. + + ffGob &= ~kfGobTransitioningToVisible; + if (ffGob & kfGobIncludeFindVisible) { + pgobT->SetFlags(ffGob & ~(kfGobIncludeFindVisible | kfGobVisibleLastFrame)); + apgob[cpgob++] = pgobT; + if (cpgob == cpgobMax) + goto lbFull; + } else { + pgobT->SetFlags(ffGob & ~kfGobVisibleLastFrame); + } + } + } + } + +lbFull: + SortGobs(apgob, cpgob); + + return cpgob; +} +#else +// Called by Simulation::Draw to identify and sort the on-screen Gobs +// prior to drawing them. + +int GobMgr::FindGobs(const Rect *prcBounds, Gob **apgob, int cpgobMax, byte *pbFogMap) +{ + // This bounds check here is to avoid exceeding the range of the + // pixel -> world coord lookup table. + + TCoord txLeft, txRight, tyTop, tyBottom; + txLeft = TcFromUpc(prcBounds->left); + int xRight = prcBounds->right + gcxTile - 1; + if (xRight >= kpcMax) + xRight = kpcMax - 1; + txRight = TcFromUpc(xRight); + tyTop = TcFromUpc(prcBounds->top); + int yBottom = prcBounds->bottom + gcyTile - 1; + if (yBottom >= kpcMax) + yBottom = kpcMax - 1; + tyBottom = TcFromUpc(yBottom); + + // Increment sequence visibility counter + + static word s_nSeqLastVisible; + s_nSeqLastVisible++; + + // Expand by 1. Need to because we need to track gobs transitioning to invisible + // Also clip to the map + + txLeft = _max(0, txLeft - 1); + txRight = _min((int)m_ctx, txRight + 1); + tyTop = _max(0, tyTop - 1); + tyBottom = _min((int)m_cty, tyBottom + 1); + + int ctx = txRight - txLeft; + int cty = tyBottom - tyTop; + int cgidReturn = m_ctx - ctx; + + Gob **ppgobT = apgob; + Gob **ppgobMax = &apgob[cpgobMax]; + Gid *pgidT = m_pgidMap + (tyTop * m_ctx) + txLeft; + + for (int ctyT = cty; ctyT != 0; ctyT--) { + for (int ctxT = ctx; ctxT != 0; ctxT--) { + Gid gidNext; + for (Gid gidT = *pgidT; gidT != kgidNull; gidT = gidNext) { + // Structure? If so, also end of list + + Gob *pgobT; + if ((gidT & kwfGidEndMarker) != 0) { + // Structure + + pgobT = *PpgobFromGid(gidT & ~kwfGidEndMarker); + gidNext = kgidNull; + Assert(pgobT->GetFlags() & kfGobStructure); + } else { + // Any gob + + pgobT = *PpgobFromGid(gidT); + gidNext = pgobT->m_gidNext; + } + dword ffGob = pgobT->GetFlags(); + + // If it's a structure make sure we're visiting it once + + if (ffGob & kfGobStructure) { + StructGob *pstru = (StructGob *)pgobT; + if (pstru->m_nSeqLastVisible == s_nSeqLastVisible) + break; + pstru->m_nSeqLastVisible = s_nSeqLastVisible; + } + + // If it's an edge gob, check for visibility + + if (ctxT == ctx || ctxT == 1 || ctyT == cty || ctyT == 1) { + Rect rcClip; + pgobT->GetClippingBounds(&rcClip); + if (rcClip.left >= prcBounds->right) + goto lbNextGob; + if (rcClip.right <= prcBounds->left) + goto lbNextGob; + if (rcClip.top >= prcBounds->bottom) + goto lbNextGob; + if (rcClip.bottom <= prcBounds->top) + goto lbNextGob; + } + + // Remember if this gob was not visible last frame; we'll need this info later + + if (!(ffGob & kfGobVisibleLastFrame)) { + ffGob |= kfGobTransitioningToVisible; + } else { + ffGob &= ~kfGobTransitioningToVisible; + } + + // Remember that this gob was visible in this frame + + ffGob = (ffGob & ~kfGobIncludeFindVisible) | kfGobVisibleLastFrame; + pgobT->SetFlags(ffGob); + + // Add gob to list + + *ppgobT++ = pgobT; + if (ppgobT == ppgobMax) + goto lbFull; + continue; + +lbNextGob: + // Gob not visible, but add to list anyway if asked + // Clear the "gob was visible this frame" bit since it isn't visible this frame. + + ffGob &= ~kfGobTransitioningToVisible; + if (ffGob & kfGobIncludeFindVisible) { + pgobT->SetFlags(ffGob & ~(kfGobIncludeFindVisible | kfGobVisibleLastFrame)); + *ppgobT++ = pgobT; + if (ppgobT == ppgobMax) + goto lbFull; + } else { + pgobT->SetFlags(ffGob & ~kfGobVisibleLastFrame); + } + } + + // Next gid + + pgidT++; + } + + // Next row + + pgidT += cgidReturn; + } + +lbFull: + int cpgob = ppgobT - apgob; + SortGobs(apgob, cpgob); + return cpgob; +} +#endif + +void SortGobs(Gob **apgob, int cpgob) +{ + if (cpgob <= 1) + return; + + // Prep for sorting + +#if 1 + GobSort *ags = (GobSort *)gpbScratch; +#else + GobSort ags[kcpgobMax / 4]; +#endif + GobSort *pgsT = ags; + Gob **ppgobT = apgob; + int i; + for (i = 0; i < cpgob; i++, pgsT++) { + pgsT->pgob = *ppgobT++; + pgsT->key = pgsT->pgob->GetSortKey(); + } + + // Sort (insertion sort) + + GobSort *pgs; + GobSort *pgsEnd = ags + cpgob; + for (pgs = ags + 1; pgs < pgsEnd; pgs++) { + for (GobSort *pgsT = pgs; pgsT > ags && (pgsT - 1)->key > pgsT->key; pgsT--) { + GobSort gsT = *pgsT; + *pgsT = *(pgsT - 1); + *(pgsT - 1) = gsT; + } + } + + // Copy sorted results to passed-in buffer + + pgsT = ags; + ppgobT = apgob; + for (i = 0; i < cpgob; i++, pgsT++) + *ppgobT++ = pgsT->pgob; +} + +#if 0 +// For reference: + +/* qsort -- qsort interface implemented by faster quicksort. + J. L. Bentley and M. D. McIlroy, SPE 23 (1993) 1249-1265. + Copyright 1993, John Wiley. +*/ + + /*assume sizeof(long) is a power of 2 */ +#define SWAPINIT(a, es) swaptype = \ + (a-(char*)0 | es) % sizeof(long) ? 2 : es > sizeof(long); +#define swapcode(TYPE, parmi, parmj, n) { \ + register TYPE *pi = (TYPE *) (parmi); \ + register TYPE *pj = (TYPE *) (parmj); \ + do { \ + register TYPE t = *pi; \ + *pi++ = *pj; \ + *pj++ = t; \ + } while ((n -= sizeof(TYPE)) > 0); \ +} +#include +static void swapfunc(char *a, char *b, size_t n, int swaptype) +{ if (swaptype <= 1) swapcode(long, a, b, n) + else swapcode(char, a, b, n) +} +#define swap(a, b) \ + if (swaptype == 0) { \ + t = *(long*)(a); \ + *(long*)(a) = *(long*)(b); \ + *(long*)(b) = t; \ + } else \ + swapfunc(a, b, es, swaptype) + +#define PVINIT(pv, pm) \ + if (swaptype != 0) { pv = a; swap(pv, pm); } \ + else { pv = (char*)&v; *(long*)pv = *(long*)pm; } + +#define vecswap(a, b, n) if (n > 0) swapfunc(a, b, n, swaptype) + +static char *med3(char *a, char *b, char *c, int (*cmp)(const void *pElement1, const void *pElement2)) +{ return cmp(a, b) < 0 ? + (cmp(b, c) < 0 ? b : cmp(a, c) < 0 ? c : a) + : (cmp(b, c) > 0 ? b : cmp(a, c) > 0 ? c : a); +} + +void qsort(char *a, size_t n, size_t es, int (*cmp)(const void *pElement1, const void *pElement2)) +{ + char *pa, *pb, *pc, *pd, *pl, *pm, *pn, *pv; + int r, swaptype; + long t, v; + size_t s; + + SWAPINIT(a, es); + if (n < 7) { /* Insertion sort on smallest arrays */ + for (pm = a + es; pm < a + n*es; pm += es) + for (pl = pm; pl > a && cmp(pl-es, pl) > 0; pl -= es) + swap(pl, pl-es); + return; + } + pm = a + (n/2)*es; /* Small arrays, middle element */ + if (n > 7) { + pl = a; + pn = a + (n-1)*es; + if (n > 40) { /* Big arrays, pseudomedian of 9 */ + s = (n/8)*es; + pl = med3(pl, pl+s, pl+2*s, cmp); + pm = med3(pm-s, pm, pm+s, cmp); + pn = med3(pn-2*s, pn-s, pn, cmp); + } + pm = med3(pl, pm, pn, cmp); /* Mid-size, med of 3 */ + } + PVINIT(pv, pm); /* pv points to partition value */ + pa = pb = a; + pc = pd = a + (n-1)*es; + for (;;) { + while (pb <= pc && (r = cmp(pb, pv)) <= 0) { + if (r == 0) { swap(pa, pb); pa += es; } + pb += es; + } + while (pb <= pc && (r = cmp(pc, pv)) >= 0) { + if (r == 0) { swap(pc, pd); pd -= es; } + pc -= es; + } + if (pb > pc) break; + swap(pb, pc); + pb += es; + pc -= es; + } + pn = a + n*es; + s = _min(pa-a, pb-pa ); vecswap(a, pb-s, s); + s = _min(pd-pc, pn-pd-es); vecswap(pb, pn-s, s); + if ((s = pb-pa) > es) qsort(a, s/es, es, cmp); + if ((s = pd-pc) > es) qsort(pn-s, s/es, es, cmp); +} +#endif + +const int kcpgobInRangeMax = 100; + +// Called by GobMgr::FindEnemyWithinRange to locate Gobs within a certain tile radius +// of a Gob looking for a fight. + +int GobMgr::FindGobs(const TRect *ptrcBounds, Gob **apgob, int cpgobMax) +{ + TCoord txLeft, txRight, tyTop, tyBottom; + + txLeft = ptrcBounds->left; + txRight = ptrcBounds->right; + tyTop = ptrcBounds->top; + tyBottom = ptrcBounds->bottom; + + // Expand the cells examined to include any that may contain Gobs + // that intersect the specified bounding rectangle. + // This carries some assumptions about the maximum size of Gob + // clippinging rectangle and its relation to its origin. + + // Maximum Gob clipping rectangle is: + // -2/+2 tiles wide from its origin + // -3/+1 tiles high from its origin + + txLeft = _max(0, txLeft - 2); + txRight = _min(m_ctx - 1, txRight + 2); + tyTop = _max(0, tyTop - 3); + tyBottom = _min(m_cty - 1, tyBottom + 1); + + int ctx = txRight - txLeft; + + int cpgob = 0; + Gid *pgidLeft = m_pgidMap + (tyTop * m_ctx) + txLeft; + for (int ty = tyTop; ty <= tyBottom; ty++, pgidLeft += m_ctx) { + + Gid *pgidRight = pgidLeft + ctx; + + for (Gid *pgidT = pgidLeft; pgidT <= pgidRight; pgidT++) { + Gob *pgobT; + for (Gid gidT = *pgidT; (gidT & kwfGidEndMarker) == 0; gidT = pgobT->m_gidNext) { + pgobT = *PpgobFromGid(gidT); + + // Callers only care about active Gobs + + if ((pgobT->GetFlags() & kfGobActive) == 0) + continue; + + TRect trcClip; + pgobT->GetTileRect(&trcClip); + if (trcClip.left >= ptrcBounds->right) + continue; + if (trcClip.right <= ptrcBounds->left) + continue; + if (trcClip.top >= ptrcBounds->bottom) + continue; + if (trcClip.bottom <= ptrcBounds->top) + continue; + + apgob[cpgob++] = pgobT; + if (cpgob == cpgobMax) + goto lbFull; + } + } + } + +lbFull: + return cpgob; +} + +#if defined(DEBUG) && defined(WIN) +Gob *GobMgr::FindEnemyWithinRange(UnitGob *punt, TCoord tcRange, bool fStructures) +{ + TRect trc; + punt->GetTileRect(&trc); + trc.left -= tcRange; + trc.right += tcRange; + trc.top -= tcRange; + trc.bottom += tcRange; + + Gob *apgob[kcpgobInRangeMax]; + int cpgob = FindGobs(&trc, apgob, sizeof(apgob)); + Assert(cpgob <= kcpgobInRangeMax); + + if (cpgob == 0) + return NULL; + + Gob **ppgobT = apgob; + for (int i = 0; i < cpgob; i++, ppgobT++) { + Gob *pgobT = *ppgobT; + + // Don't fire on non-military targets or allies + + if (!punt->IsValidTarget(pgobT)) + continue; + + // or enemy structures + + if (!fStructures && (pgobT->GetFlags() & kfGobStructure)) + continue; + + // NOTE: this doesn't return the NEAREST Gob, just the first + // found within range + + return pgobT; + } + + return NULL; +} +#endif + +} // namespace wi diff --git a/game/GameOptions.cpp b/game/GameOptions.cpp new file mode 100644 index 0000000..e90841f --- /dev/null +++ b/game/GameOptions.cpp @@ -0,0 +1,1719 @@ +#include "ht.h" +#include "mpshared/netmessage.h" + +namespace wi { + +// Handy dandy global flags used for enabling various test features + +#ifdef DRAW_PATHS +bool gfDrawPaths = false; +#endif + +#ifdef DRAW_LINES +bool gfDrawLines = false; +#endif + +#ifdef STATS_DISPLAY +bool gfShowStats = false; +#endif + +bool gfOvermindEnabled = true; +bool gfSoundEnabled = true; + +#ifdef DEV_BUILD +bool gfShowFPS = true; +#else +bool gfShowFPS = false; +#endif + +bool gfLockStep = false; +bool gfSuspendUpdates = false; +bool gfSingleStep = false; +bool gfGodMode = false; +bool gfAutosave = false; +bool gfStylusUI = false; + +#define SetControlChecked(id, f) ((CheckBoxControl *)GetControlPtr(id))->SetChecked(f) +#define GetControlChecked(id) ((CheckBoxControl *)GetControlPtr(id))->IsChecked() + +// GameOptions + +class GameOptionsForm : public ShellForm +{ +public: + virtual bool Init(FormMgr *pfrmm, IniReader *pini, word idf) secGameOptionsForm; + virtual void OnControlSelected(word idc) secGameOptionsForm; +}; + +// SoundOptions + +class SoundOptionsForm : public ShellForm +{ +public: + virtual bool Init(FormMgr *pfrmm, IniReader *pini, word idf) secGameOptionsForm; + virtual void OnControlSelected(word idc) secGameOptionsForm; + +private: + void InitResettableControls() secGameOptionsForm; + void UpdateLabels() secGameOptionsForm; + + int m_nVolume; + bool m_fEnabled; +}; + +// ColorOptions + +class ColorOptionsForm : public Form +{ +public: + ColorOptionsForm(Palette *ppal) secGameOptionsForm; + virtual bool Init(FormMgr *pfrmm, IniReader *pini, word idf) secGameOptionsForm; + virtual void OnPaintBackground(DibBitmap *pbm, UpdateMap *pupd) secGameOptionsForm; + virtual void OnControlSelected(word idc) secGameOptionsForm; + +private: + void InitResettableControls() secGameOptionsForm; + void UpdateLabels() secGameOptionsForm; + + Palette *m_ppal; + int m_nHueOffset; + int m_nSatMultiplier; + int m_nLumOffset; +}; + +// DisplayOptions + +class DisplayOptionsForm : public ShellForm +{ +public: + virtual bool Init(FormMgr *pfrmm, IniReader *pini, word idf) secGameOptionsForm; + virtual void OnControlSelected(word idc) secGameOptionsForm; + +private: + void InitResettableControls() secGameOptionsForm; +}; + +// PerformanceOptions + +class PerformanceOptionsForm : public ShellForm +{ +public: + virtual bool Init(FormMgr *pfrmm, IniReader *pini, word idf) secGameOptionsForm; + virtual void OnControlSelected(word idc) secGameOptionsForm; + +private: + void InitResettableControls() secGameOptionsForm; + + word m_wfPerfOptions; +}; + +// DeleteMissionPackForm + +class DeleteMissionPackForm : public ShellForm +{ +public: + virtual bool Init(FormMgr *pfrmm, IniReader *pini, word idf) secGameOptionsForm; + virtual void OnControlSelected(word idc) secGameOptionsForm; + virtual void OnControlNotify(word idc, int nNotify) secGameOptionsForm; + +private: + void PopulateList() secGameOptionsForm; +}; + +// GobCountForm + +class GobCountForm : public Form +{ +public: + virtual void OnPaint(DibBitmap *pbm) secGameOptionsForm; + virtual void OnUpdateMapInvalidate(UpdateMap *pupd, Rect *prcOpaque) secGameOptionsForm; + virtual bool OnPenEvent(Event *pevt) secGameOptionsForm; + +private: + void OutputString(DibBitmap *pbm, char *psz, int *px, int *py) secGameOptionsForm; + void FormatSideString(char *pszIn, int *acPerSide, char *pszOut) secGameOptionsForm; +}; + +// +++ + +bool DoModalGameOptionsForm(Palette *ppal, bool fInGame) +{ +#ifndef IPHONE + ShellForm *pfrm = (ShellForm *)gpmfrmm->LoadForm(gpiniForms, kidfGameOptions, new GameOptionsForm()); + if (pfrm == NULL) + return false; + int nResult; + pfrm->DoModal(&nResult); + + if (!fInGame) + pfrm->Show(true); + + if (nResult == kidcColorOptions) { + gpmfrmm->InvalidateRect(NULL); + Form *pfrmT = gpmfrmm->LoadForm(gpiniForms, kidfColorOptions, new ColorOptionsForm(ppal)); + if (pfrmT != NULL) { + pfrmT->DoModal(); + delete pfrmT; + } + } + + pfrm->Show(false); + delete pfrm; +#else + + // iPhone only has InGameOptions + ShellForm *pfrm = (ShellForm *)gpmfrmm->LoadForm(gpiniForms, + kidfInGameOptions, new InGameOptionsForm()); + if (pfrm == NULL) { + return false; + } + pfrm->DoModal(); + delete pfrm; +#endif + + return true; +} + +// +// Format option buttons sequentially, based on visibility +// + +void FormatButtons(Form *pfrm, word *aidc, int cidc, int idcRef, int idcRefNext) +{ + Control *pctlRef = pfrm->GetControlPtr(idcRef); + Rect rcRef; + pctlRef->GetRect(&rcRef); + + Control *pctlRefNext = pfrm->GetControlPtr(idcRefNext); + Rect rcRefNext; + pctlRefNext->GetRect(&rcRefNext); + + int x = rcRef.left; + int y = rcRef.top; + int cySpacing = rcRefNext.top - rcRef.top; + + for (int n = 0; n < cidc; n++) { + Control *pctl = pfrm->GetControlPtr(aidc[n]); + if (!(pctl->GetFlags() & kfCtlVisible)) + continue; + Rect rc; + pctl->GetRect(&rc); + rc.left = x; + rc.bottom = y + rc.bottom - rc.top; + rc.top = y; + pctl->SetRect(&rc); + y += cySpacing; + } +} + +// +// GameOptions implementation +// + +bool GameOptionsForm::Init(FormMgr *pfrmm, IniReader *pini, word idf) +{ + if (!ShellForm::Init(pfrmm, pini, idf)) + return false; + + // No color options when screen depth is 4 + + ModeInfo mode; + gpdisp->GetMode(&mode); + if (mode.nDepth == 4) + GetControlPtr(kidcColorOptions)->Show(false); + + // No display options when there is only one choice + + if (ggame.GetModeMatchCount() == 1) + GetControlPtr(kidcDisplayOptions)->Show(false); + + // Multiplayer changes + + if (gfMultiplayer) { + GetControlPtr(kidcInGameOptions)->Show(false); + GetControlPtr(kidcPerformanceOptions)->Show(false); + GetControlPtr(kidcDisplayOptions)->Show(false); + } + + // Can't delete mission packs from here on Palm because they may have been bundle-bit copied + // from card to internal ram; deleting them here and then exiting causes the launcher to crash + // because it expects them still to be there. For Palm we have an external application that does + // mission pack management + +#ifdef PIL + GetControlPtr(kidcDeleteMissionPack)->Show(false); +#endif + + // Format the remaining visible buttons + + word aidcFormat[] = { kidcInGameOptions, kidcSoundOptions, kidcPerformanceOptions, kidcColorOptions, kidcDisplayOptions, kidcDeleteMissionPack }; + FormatButtons(this, aidcFormat, ARRAYSIZE(aidcFormat), kidcInGameOptions, kidcSoundOptions); + + return true; +} + +void GameOptionsForm::OnControlSelected(word idc) +{ + // Remember what button was pressed - the caller will want this + + switch (idc) { + case kidcDeleteMissionPack: + { + ShellForm *pfrm = (ShellForm *)gpmfrmm->LoadForm(gpiniForms, kidfDeleteMissionPack, new DeleteMissionPackForm()); + if (pfrm != NULL) { + pfrm->DoModal(); + delete pfrm; + return; + } + } + break; + + case kidcInGameOptions: + { + ShellForm *pfrm = (ShellForm *)gpmfrmm->LoadForm(gpiniForms, kidfInGameOptions, new InGameOptionsForm()); + if (pfrm != NULL) { + pfrm->DoModal(); + delete pfrm; + return; + } + } + break; + + case kidcSoundOptions: + { + ShellForm *pfrm = (ShellForm *)gpmfrmm->LoadForm(gpiniForms, kidfSoundOptions, new SoundOptionsForm()); + if (pfrm != NULL) { + pfrm->DoModal(); + delete pfrm; + return; + } + } + break; + + case kidcColorOptions: + // This'll get executed after return + break; + + case kidcDisplayOptions: + { + ShellForm *pfrm = (ShellForm *)gpmfrmm->LoadForm(gpiniForms, kidfDisplayOptions, new DisplayOptionsForm()); + if (pfrm != NULL) { + pfrm->DoModal(); + delete pfrm; + return; + } + } + break; + + case kidcPerformanceOptions: + { + ShellForm *pfrm = (ShellForm *)gpmfrmm->LoadForm(gpiniForms, kidfPerformanceOptions, new PerformanceOptionsForm()); + if (pfrm != NULL) { + pfrm->DoModal(); + delete pfrm; + return; + } + } + break; + } + + Form::OnControlSelected(idc); +} + +// +// InGameOptionsForm implementation +// + +bool InGameOptionsForm::Init(FormMgr *pfrmm, IniReader *pini, word idf) +{ + if (!ShellForm::Init(pfrmm, pini, idf)) + return false; + + m_fLassoSelection = gfLassoSelection; + m_tGameSpeed = gtGameSpeed; + m_wfHandicap = gwfHandicap; + m_nScrollSpeed = gnScrollSpeed; + +#ifdef IPHONE + GetControlPtr(kidcLassoSelection)->Show(false); +#endif + + InitResettableControls(); + return true; +} + +// Transplate game update rate into a 'multiplier from normal' string + +void GetSpeedMultiplierString(char *psz, long tGameSpeed) +{ + int n = 0; + if (tGameSpeed != 0) { + n = (8 * 100) / tGameSpeed; + } + int cWhole = n / 100; + int cFrac = n % 100; + sprintf(psz, "%d.%dx", cWhole, cFrac); +} + +void InGameOptionsForm::InitResettableControls() +{ + // Lasso + + SetControlChecked(kidcLassoSelection, m_fLassoSelection); + + // Game Speed + + SliderControl *psldr = (SliderControl *)GetControlPtr(kidcGameSpeed); + psldr->SetRange(0, ARRAYSIZE(gatGameSpeeds) - 1); + psldr->SetValue(8); + for (int i = 0; i < ARRAYSIZE(gatGameSpeeds); i++) { + if (gatGameSpeeds[i] == m_tGameSpeed) { + psldr->SetValue(i); + break; + } + } + + // Scroll Speed 1x to 5x, in .25 increments + +#define knScrollSpeedMax 5 + + psldr = (SliderControl *)GetControlPtr(kidcScrollSpeed); + psldr->SetRange(0, (knScrollSpeedMax - 1) * 4); + psldr->SetValue((m_nScrollSpeed - 1.0) / 0.25); + + // Difficulty + + SetControlChecked(kidcEasy, m_wfHandicap == kfHcapEasy); + SetControlChecked(kidcNormal, m_wfHandicap == kfHcapNormal); + SetControlChecked(kidcHard, m_wfHandicap == kfHcapHard); + + UpdateLabels(); +} + +void InGameOptionsForm::UpdateLabels() +{ + char szT[80]; + GetSpeedMultiplierString(szT, m_tGameSpeed); + LabelControl *plbl = (LabelControl *)GetControlPtr(kidcGameSpeedLabel); + plbl->SetText(szT); + + plbl = (LabelControl *)GetControlPtr(kidcScrollSpeedLabel); + int cWhole = (int)m_nScrollSpeed; + int cFrac = (m_nScrollSpeed - (int)m_nScrollSpeed) * 100; + sprintf(szT, "%d.%dx", cWhole, cFrac); + plbl->SetText(szT); +} + +void InGameOptionsForm::OnControlSelected(word idc) +{ + switch (idc) { + case kidcGameSpeed: + { + SliderControl *psldr = + (SliderControl *)GetControlPtr(kidcGameSpeed); + m_tGameSpeed = gatGameSpeeds[psldr->GetValue()]; + UpdateLabels(); + } + break; + + case kidcScrollSpeed: + { + SliderControl *psldr = + (SliderControl *)GetControlPtr(kidcScrollSpeed); + m_nScrollSpeed = 1.0 + psldr->GetValue() * 0.25; + UpdateLabels(); + } + break; + + case kidcEasy: + case kidcNormal: + case kidcHard: + { + SetControlChecked(kidcEasy, idc == kidcEasy); + SetControlChecked(kidcNormal, idc == kidcNormal); + SetControlChecked(kidcHard, idc == kidcHard); + } + break; + + case kidcOk: + { + // Lasso and game speed, other + + gfLassoSelection = GetControlChecked(kidcLassoSelection); + SliderControl *psldr = (SliderControl *)GetControlPtr(kidcGameSpeed); + ggame.SetGameSpeed(gatGameSpeeds[psldr->GetValue()]); + psldr = (SliderControl *)GetControlPtr(kidcScrollSpeed); + gnScrollSpeed = 1.0 + psldr->GetValue() * 0.25; + + // Difficulty + + if (GetControlChecked(kidcEasy)) + gwfHandicap = kfHcapEasy; + else if (GetControlChecked(kidcNormal)) + gwfHandicap = kfHcapNormal; + else if (GetControlChecked(kidcHard)) + gwfHandicap = kfHcapHard; + if (gpplrLocal != NULL) + gpplrLocal->SetHandicap(gwfHandicap); + + // Save prefs now in case the game crashes before exiting! + + ggame.SavePreferences(); + } + EndForm(idc); + break; + + case kidcCancel: + EndForm(idc); + break; + + case kidcDefault: + m_fLassoSelection = false; + m_tGameSpeed = kcmsUpdate / 10; + m_nScrollSpeed = 1.0; + m_wfHandicap = kfHcapDefault; + InitResettableControls(); + break; + } +} + +// +// SoundOptionsForm +// + +bool SoundOptionsForm::Init(FormMgr *pfrmm, IniReader *pini, word idf) +{ + if (!ShellForm::Init(pfrmm, pini, idf)) + return false; + + // Save away values to restore in case of cancel + + m_nVolume = gsndm.GetVolume(); + m_fEnabled = gsndm.IsEnabled(); + + InitResettableControls(); + return true; +} + +void SoundOptionsForm::InitResettableControls() +{ + SetControlChecked(kidcMute, !gsndm.IsEnabled()); + + SliderControl *psldr = (SliderControl *)GetControlPtr(kidcVol); + psldr->SetRange(0, 255); + + int nVolume = gsndm.GetVolume(); + if (nVolume < 0) { + psldr->Show(false); + GetControlPtr(kidcVolLabel)->Show(false); + GetControlPtr(kidcVolumeString)->Show(false); + } else { + psldr->SetValue(nVolume); + } + + UpdateLabels(); +} + +void SoundOptionsForm::UpdateLabels() +{ + char szT[80]; + LabelControl *plbl = (LabelControl *)GetControlPtr(kidcVolLabel); + itoa((gsndm.GetVolume() * 100) / 255, szT, 10); + plbl->SetText(szT); +} + +void SoundOptionsForm::OnControlSelected(word idc) +{ + switch (idc) { + case kidcVol: + { + SliderControl *psldr = (SliderControl *)GetControlPtr(kidcVol); + gsndm.SetVolume(psldr->GetValue()); + UpdateLabels(); + } + break; + + case kidcMute: + { + gsndm.Enable(!GetControlChecked(kidcMute)); + InitResettableControls(); + } + break; + + case kidcCancel: + gsndm.Enable(m_fEnabled); + gsndm.SetVolume(m_nVolume); + EndForm(idc); + break; + + case kidcOk: + ggame.SavePreferences(); + EndForm(idc); + break; + } +} + +// +// Color Options Form +// + +ColorOptionsForm::ColorOptionsForm(Palette *ppal) +{ + m_ppal = ppal; +} + +bool ColorOptionsForm::Init(FormMgr *pfrmm, IniReader *pini, word idf) +{ + if (!Form::Init(pfrmm, pini, idf)) + return false; + + // Save away values to restore in case of cancel + + m_nHueOffset = gnHueOffset; + m_nSatMultiplier = gnSatMultiplier; + m_nLumOffset = gnLumOffset; + + InitResettableControls(); + + // Position the form middle of screen, bottom + + Size sizDib; + m_pfrmm->GetDib()->GetSize(&sizDib); + Rect rc; + rc.left = ((sizDib.cx - m_rc.Width()) / 2) & ~1; + rc.top = sizDib.cy - m_rc.Height(); + rc.right = rc.left + m_rc.Width(); + rc.bottom = rc.top + m_rc.Height(); + SetRect(&rc); + + return true; +} + +void ColorOptionsForm::InitResettableControls() +{ + SliderControl *psldr = (SliderControl *)GetControlPtr(kidcLum); + psldr->SetRange(-100, 100); + psldr->SetValue(gnLumOffset); + + psldr = (SliderControl *)GetControlPtr(kidcSat); + psldr->SetRange(-100, 100); + psldr->SetValue(gnSatMultiplier); + + psldr = (SliderControl *)GetControlPtr(kidcHue); + psldr->SetRange(-100, 100); + psldr->SetValue(gnHueOffset); + + UpdateLabels(); +} + +void ColorOptionsForm::UpdateLabels() +{ + char szT[80]; + LabelControl *plbl = (LabelControl *)GetControlPtr(kidcHueLabel); + itoa(gnHueOffset, szT, 10); + plbl->SetText(szT); + + plbl = (LabelControl *)GetControlPtr(kidcSatLabel); + itoa(gnSatMultiplier, szT, 10); + plbl->SetText(szT); + + plbl = (LabelControl *)GetControlPtr(kidcLumLabel); + itoa(gnLumOffset, szT, 10); + plbl->SetText(szT); +} + +void ColorOptionsForm::OnControlSelected(word idc) +{ + switch (idc) { + case kidcHue: + case kidcSat: + case kidcLum: + { + SliderControl *psldr = (SliderControl *)GetControlPtr(kidcHue); + gnHueOffset = (short)psldr->GetValue(); + + psldr = (SliderControl *)GetControlPtr(kidcSat); + gnSatMultiplier = (short)psldr->GetValue(); + + psldr = (SliderControl *)GetControlPtr(kidcLum); + gnLumOffset = (short)psldr->GetValue(); + + UpdateLabels(); + + SetHslAdjustedPalette(m_ppal, gnHueOffset, gnSatMultiplier, gnLumOffset); + + // Some devices such as the PocketPC need the form redrawn because setting the palette + // is only setting an 8->16 bit translation table, and only the controls are redrawing. + +#ifdef CE + gpmfrmm->InvalidateRect(NULL); +#endif + } + break; + + case kidcCancel: + if (m_nHueOffset != gnHueOffset || m_nSatMultiplier != gnSatMultiplier || m_nLumOffset != gnLumOffset) { + gnHueOffset = m_nHueOffset; + gnSatMultiplier = m_nSatMultiplier; + gnLumOffset = m_nLumOffset; + SetHslAdjustedPalette(m_ppal, gnHueOffset, gnSatMultiplier, gnLumOffset); + } + + EndForm(idc); + break; + + case kidcOk: + // Save prefs now in case the game crashes before exiting! + + ggame.SavePreferences(); + EndForm(idc); + break; + + case kidcDefault: + if (gnHueOffset != 0 || gnSatMultiplier != 0|| gnLumOffset != 0) { + gnHueOffset = 0; + gnSatMultiplier = 0; + gnLumOffset = 0; + SetHslAdjustedPalette(m_ppal, gnHueOffset, gnSatMultiplier, gnLumOffset); + } + + InitResettableControls(); + + // Some devices such as the PocketPC need the form redrawn because setting the palette + // is only setting an 8->16 bit translation table, and only the controls are redrawing. + +#ifdef CE + gpmfrmm->InvalidateRect(NULL); +#endif + break; + } +} + +void ColorOptionsForm::OnPaintBackground(DibBitmap *pbm, UpdateMap *pupd) +{ + // Draw the fancy background bitmap + + RawBitmap *prbm = LoadRawBitmap("titlescreenbkgd.rbm"); + BltHelper(pbm, prbm, pupd, m_rc.left, m_rc.top); + delete prbm; +} + +// +// Display options form +// + +bool DisplayOptionsForm::Init(FormMgr *pfrmm, IniReader *pini, word idf) +{ + if (!ShellForm::Init(pfrmm, pini, idf)) + return false; + + InitResettableControls(); + + // Initialize display mode list + + ListControl *plstc = (ListControl *)GetControlPtr(kidcModesList); + plstc->SetFlags(plstc->GetFlags() | kfLstcBorder); + + int immCur = ggame.GetModeMatchCurrent(); + int cmm = ggame.GetModeMatchCount(); + + // Make resolution list + + for (int imm = 0; imm < cmm; imm++) { + ModeMatch mm; + ggame.GetModeMatch(imm, &mm); + + // Art + + char szArt[64]; + if (mm.nDepthData < 8) { + strcpy(szArt, "gray"); + } else { + strcpy(szArt, "color"); + } + switch (mm.nSizeData) { + case 16: + strcat(szArt, " low-res art"); + break; + + case 20: + // Don't want to insult PPC users +#ifdef CE + strcat(szArt, " art"); +#else + strcat(szArt, " med-res art"); +#endif + break; + + case 24: + strcat(szArt, " high-res art"); + break; + } + + // Orientation + + ModeInfo mode; + gpdisp->GetModeInfo(mm.imode, &mode); + char szOrientation[64]; + szOrientation[0] = 0; + if (mode.nDegreeOrientation != 0) + sprintf(szOrientation, ", %d deg", mode.nDegreeOrientation); + char szT[64]; +#ifdef DEV_BUILD + sprintf(szT, "%dx%dx%d, %s%s", mode.cx, mode.cy, mode.nDepth, szArt, szOrientation); +#else + sprintf(szT, "%dx%d, %s%s", mode.cx, mode.cy, szArt, szOrientation); +#endif + plstc->Add(szT, (void *)imm); + } + + plstc->Select(immCur); + + return true; +} + +void DisplayOptionsForm::InitResettableControls() +{ +} + +void DisplayOptionsForm::OnControlSelected(word idc) +{ + switch (idc) { + case kidcOk: + { + ListControl *plstc = (ListControl *)GetControlPtr(kidcModesList); + if (plstc->GetFlags() & kfCtlVisible) { + int immNew = (int)plstc->GetSelectedItemData(); + ggame.RequestModeChange(immNew); + } + + // Save prefs now in case the game crashes before exiting! + + ggame.SavePreferences(); + } + EndForm(idc); + break; + + case kidcCancel: + EndForm(idc); + break; + + case kidcDefault: + { + ListControl *plstc = (ListControl *)GetControlPtr(kidcModesList); + plstc->Select(ggame.GetModeMatchBest(), true); + InitResettableControls(); + } + break; + } +} + +// +// PerformanceOptionsForm implementation +// + +bool PerformanceOptionsForm::Init(FormMgr *pfrmm, IniReader *pini, word idf) +{ + if (!ShellForm::Init(pfrmm, pini, idf)) + return false; + m_wfPerfOptions = gwfPerfOptions; + InitResettableControls(); + return true; +} + +void PerformanceOptionsForm::InitResettableControls() +{ + SetControlChecked(kidcRocketShots, (m_wfPerfOptions & kfPerfRocketShots) != 0); + SetControlChecked(kidcRocketTrails, (m_wfPerfOptions & kfPerfRocketTrails) != 0); + SetControlChecked(kidcRocketImpacts, (m_wfPerfOptions & kfPerfRocketImpacts) != 0); + SetControlChecked(kidcShots, (m_wfPerfOptions & kfPerfShots) != 0); + SetControlChecked(kidcShotImpacts, (m_wfPerfOptions & kfPerfShotImpacts) != 0); + SetControlChecked(kidcSelectionBrackets, (m_wfPerfOptions & kfPerfSelectionBrackets) != 0); + SetControlChecked(kidcSmoke, (m_wfPerfOptions & kfPerfSmoke) != 0); + SetControlChecked(kidcEnemyDamageIndicator, (m_wfPerfOptions & kfPerfEnemyDamageIndicator) != 0); + SetControlChecked(kidcScorchMarks, (m_wfPerfOptions & kfPerfScorchMarks) != 0); + SetControlChecked(kidcSymbolFlashing, (m_wfPerfOptions & kfPerfSymbolFlashing) != 0); +} + +void PerformanceOptionsForm::OnControlSelected(word idc) +{ + switch (idc) { + case kidcCancel: + EndForm(idc); + break; + + case kidcOk: + { + word wfT = 0; + if (GetControlChecked(kidcRocketShots)) + wfT |= kfPerfRocketShots; + if (GetControlChecked(kidcRocketTrails)) + wfT |= kfPerfRocketTrails; + if (GetControlChecked(kidcRocketImpacts)) + wfT |= kfPerfRocketImpacts; + if (GetControlChecked(kidcShots)) + wfT |= kfPerfShots; + if (GetControlChecked(kidcShotImpacts)) + wfT |= kfPerfShotImpacts; + if (GetControlChecked(kidcSelectionBrackets)) + wfT |= kfPerfSelectionBrackets; + if (GetControlChecked(kidcSmoke)) + wfT |= kfPerfSmoke; + if (GetControlChecked(kidcEnemyDamageIndicator)) + wfT |= kfPerfEnemyDamageIndicator; + if (GetControlChecked(kidcScorchMarks)) + wfT |= kfPerfScorchMarks; + if (GetControlChecked(kidcSymbolFlashing)) + wfT |= kfPerfSymbolFlashing; + gwfPerfOptions = wfT; + ggame.SavePreferences(); + EndForm(idc); + } + break; + + case kidcDefault: + m_wfPerfOptions = kfPerfAll; + InitResettableControls(); + break; + } +} + +// +// TestOptionsForm implementation +// + +bool TestOptionsForm::Init(FormMgr *pfrmm, IniReader *pini, word idf) +{ + if (!Form::Init(pfrmm, pini, idf)) + return false; + + CheckBoxControl *pcbox = (CheckBoxControl *)GetControlPtr(kidcDrawLines); +#ifdef DRAW_LINES + pcbox->SetChecked(gfDrawLines); +#else + pcbox->Show(false); +#endif + + pcbox = (CheckBoxControl *)GetControlPtr(kidcDrawPaths); +#ifdef DRAW_PATHS + pcbox->SetChecked(gfDrawPaths); +#else + pcbox->Show(false); +#endif + + pcbox = (CheckBoxControl *)GetControlPtr(kidcShowStats); +#ifdef STATS_DISPLAY + pcbox->SetChecked(gfShowStats); +#else + pcbox->Show(false); +#endif + + pcbox = (CheckBoxControl *)GetControlPtr(kidcLockStep); + pcbox->SetChecked(gfLockStep); + + SetControlChecked(kidcOvermind, gfOvermindEnabled); + SetControlChecked(kidcShowFPS, gfShowFPS); + SetControlChecked(kidcSuspendUpdates, gfSuspendUpdates); + SetControlChecked(kidcMaxRepaint, (gevm.GetRedrawFlags() & kfRedrawMax) != 0); +#ifdef DRAW_UPDATERECTS + SetControlChecked(kidcDrawUpdateRects, gfDrawUpdateRects); +#endif + SetControlChecked(kidcGodMode, gfGodMode); + SetControlChecked(kidcAutosave, gfAutosave); + SetControlChecked(kidcStylusUI, gfStylusUI); + + dword ff = ggame.GetSimUIForm()->GetPenHandler()->GetFlags(); + + return true; +} + +void TestOptionsForm::OnControlSelected(word idc) +{ + switch (idc) { + case kidcBreak: +#if defined(PIL) && !defined(PNO) + DebugBreak(); +#endif + break; + + case kidcGobCount: + { + Form *pfrm = gpmfrmm->LoadForm(gpiniForms, kidfGobCount, new GobCountForm()); + pfrm->DoModal(); + delete pfrm; + } + break; + + case kidcMemoryUse: + { + Form *pfrm = gpmfrmm->LoadForm(gpiniForms, kidfMemoryUse, new MemoryUseForm()); + pfrm->DoModal(); + delete pfrm; + } + break; + +#ifdef DRAW_PATHS + case kidcDrawPaths: + gfDrawPaths = !gfDrawPaths; + break; +#endif + +#ifdef DRAW_LINES + case kidcDrawLines: + gfDrawLines = !gfDrawLines; + break; +#endif + + case kidcLockStep: + gfLockStep = !gfLockStep; +#ifdef MP_DEBUG_SHAREDMEM + extern bool gfMPServer; + if (gfMPServer) + gpsmw->fDetectSyncErrors = gfLockStep; +#endif + break; + + case kidcStylusUI: + gfStylusUI = !gfStylusUI; + ggame.GetSimUIForm()->SetUIType(gfStylusUI == true ? + kuitStylus : kuitFinger); + break; + + case kidcOvermind: + { + for (Gob *pgob = ggobm.GetFirstGob(); pgob != NULL; pgob = ggobm.GetNextGob(pgob)) { + if (pgob->GetType() == kgtOvermind) + ((OvermindGob *)pgob)->Toggle(); + } + gfOvermindEnabled = !gfOvermindEnabled; + } + break; + + case kidcClearFog: + gsim.GetLevel()->GetFogMap()->RevealAll(gpupdSim); + break; + +#ifdef STATS_DISPLAY + case kidcShowStats: + gfShowStats = !gfShowStats; + break; +#endif + + case kidcShowFPS: + gfShowFPS = !gfShowFPS; + ggame.GetSimUIForm()->GetControlPtr(kidcFps)->Show(gfShowFPS); + break; + + case kidcMaxRepaint: + if (gevm.GetRedrawFlags() & kfRedrawMax) { + gevm.ClearRedrawFlags(kfRedrawMax); + } else { + gevm.SetRedrawFlags(kfRedrawMax); + } + break; + + case kidcSuspendUpdates: + gfSuspendUpdates = !gfSuspendUpdates; + break; + + case kidcDrawUpdateRects: +#ifdef DRAW_UPDATERECTS + gfDrawUpdateRects = !gfDrawUpdateRects; +#endif + break; + + case kidcGodMode: + gfGodMode = !gfGodMode; + break; + + case kidcAutosave: + gfAutosave = !gfAutosave; + break; + + case kidcHelp: + Help(); + break; + + default: + EndForm(idc); + } +} + +// +// MemoryUseForm implementation +// + +bool MemoryUseForm::Init(FormMgr *pfrmm, IniReader *pini, word idf) +{ + if (!Form::Init(pfrmm, pini, idf)) + return false; + + SetControlChecked(kidcLimitCache, gcam.GetLimit() != 0); + UpdateLabels(); + + SetBackgroundColorIndex(kiclrBlack); + + return true; +} + +void MemoryUseForm::OnControlSelected(word idc) +{ + switch (idc) { + case kidcClearCache: + gcam.MakeSpace((dword)-1); + UpdateLabels(); + break; + + case kidcLimitCache: + if (gcam.GetLimit() == 0) { + gcam.SetLimit(10240); + } else { + gcam.SetLimit(0); + } + UpdateLabels(); + break; + + case kidcAdd10KCache: + gcam.SetLimit(gcam.GetLimit() + 10240); + UpdateLabels(); + break; + + case kidcSub10KCache: + gcam.SetLimit(gcam.GetLimit() - 10240); + UpdateLabels(); + break; + + default: + EndForm(idc); + } +} + +extern dword gcbDynMemAtStart; +extern dword gcbDbMemAtStart; + +void MemoryUseForm::UpdateLabels() +{ + dword cbDyn, cbDb; + dword cbTotal = gmmgr.GetTotalSize(&cbDyn, &cbDb); + +#ifdef PIL + dword cbFree; + dword cbMax; + MemHeapFreeBytes(0, (UInt32 *)&cbFree, (UInt32 *)&cbMax); +#else + dword cbFree = 0; +#endif + + char szT[128]; + + sprintf(szT, "start dyn, db free: %ld, %ld", gcbDynMemAtStart, gcbDbMemAtStart); + ((LabelControl *)GetControlPtr(kidcDynDbInitial))->SetText(szT); + sprintf(szT, "mmgr dyn, db reserve: %ld, %ld", cbDyn, cbDb); + ((LabelControl *)GetControlPtr(kidcMmgrDynDbReserve))->SetText(szT); + sprintf(szT, "dyn use: %ld / %ld", gcbDynMemAtStart - cbFree, gcbDynMemAtStart); + ((LabelControl *)GetControlPtr(kidcDynUse))->SetText(szT); + sprintf(szT, "mmgr use: %ld / %ld", cbTotal - gmmgr.GetFreeSize(), cbTotal); + ((LabelControl *)GetControlPtr(kidcMmgrUse))->SetText(szT); + sprintf(szT, "cache use: %ld", gcam.GetTotalSize()); + ((LabelControl *)GetControlPtr(kidcCacheUse))->SetText(szT); + + bool fCacheLimit = (gcam.GetLimit() != 0); + GetControlPtr(kidcAdd10KCache)->Show(fCacheLimit); + GetControlPtr(kidcSub10KCache)->Show(fCacheLimit); + GetControlPtr(kidcCacheLimit)->Show(fCacheLimit); + + sprintf(szT, "Cache Limit: %ld", gcam.GetLimit()); + ((LabelControl *)GetControlPtr(kidcCacheLimit))->SetText(szT); +} + +// +// GobCountForm +// + +// Note: matches gob types (kgt*) + +static char *s_aszGobs[] = { + "none", "guard", "trooper", "hrc", "decal", "scenery", "anim", "pwr gen", + "proc", "struc", "unit", "bullpup", "hq", "randc", "vts", "srv ctr", + "broadsw", "libertr", "eagle", "hydra", + "raider", "wareh", "domin", "overm", "tshot", "rockt", "gtower", "rtower", "scorch", + "smoke", "puff", "bullet", "cyclops", "cy shot", "andy", "repl", "actvtr", "fox" +}; + +void GobCountForm::OnPaint(DibBitmap *pbm) +{ + int acbGob[ARRAYSIZE(s_aszGobs)]; + memset(acbGob, 0, sizeof(acbGob)); + int acGobs[ARRAYSIZE(s_aszGobs)]; + memset(acGobs, 0, sizeof(acGobs)); + int acGobsSide[ARRAYSIZE(s_aszGobs)][kcSides]; + memset(acGobsSide, 0, sizeof(acGobsSide)); + bool afUnit[ARRAYSIZE(s_aszGobs)]; + memset(afUnit, 0, sizeof(afUnit)); + bool afStructure[ARRAYSIZE(s_aszGobs)]; + memset(afStructure, 0, sizeof(afStructure)); + bool afMobileUnit[ARRAYSIZE(s_aszGobs)]; + memset(afMobileUnit, 0, sizeof(afMobileUnit)); + for (Gob *pgobT = ggobm.GetFirstGob(); pgobT != NULL; pgobT = ggobm.GetNextGob(pgobT)) { + GobType gt = pgobT->GetType(); + acGobs[gt]++; +#if defined(PIL) + acbGob[gt] = MemPtrSize(pgobT); +#elif defined(IPHONE) || defined(SDL) + acbGob[gt] = 0; +#else + acbGob[gt] = _msize(pgobT); +#endif + if (pgobT->GetFlags() & kfGobUnit) { + afUnit[gt] = true; + UnitGob *punt = (UnitGob *)pgobT; + acGobsSide[gt][punt->GetSide()]++; + } + if (pgobT->GetFlags() & kfGobStructure) + afStructure[gt] = true; + if (pgobT->GetFlags() & kfGobMobileUnit) + afMobileUnit[gt] = true; + } + + int y = m_rc.top; + int x = m_rc.left; + char szT[64]; + + int gt; + for (gt = 0; gt < ARRAYSIZE(s_aszGobs); gt++) { + if (acGobs[gt] == 0) + continue; + + // Unit? + + if (afUnit[gt]) { + char szT2[64]; + sprintf(szT2, "%s=%d", s_aszGobs[gt], acbGob[gt]); + FormatSideString(szT2, acGobsSide[gt], szT); + } else { + sprintf(szT, "%s=%d:%d", s_aszGobs[gt], acbGob[gt], acGobs[gt]); + } + + OutputString(pbm, szT, &x, &y); + } + + // Units per side + + int acUnitsPerSide[kcSides]; + memset(acUnitsPerSide, 0, sizeof(acUnitsPerSide)); + for (gt = 0; gt < ARRAYSIZE(s_aszGobs); gt++) { + if (!afUnit[gt]) + continue; + for (Side side = ksideNeutral; side < kcSides; side++) + acUnitsPerSide[side] += acGobsSide[gt][side]; + } + FormatSideString("Units", acUnitsPerSide, szT); + OutputString(pbm, szT, &x, &y); + + // Structures per side + + int acStructuresPerSide[kcSides]; + memset(acStructuresPerSide, 0, sizeof(acStructuresPerSide)); + for (gt = 0; gt < ARRAYSIZE(s_aszGobs); gt++) { + if (!afStructure[gt]) + continue; + for (Side side = ksideNeutral; side < kcSides; side++) + acStructuresPerSide[side] += acGobsSide[gt][side]; + } + FormatSideString("Structs", acStructuresPerSide, szT); + OutputString(pbm, szT, &x, &y); + + // Mobile units per side + + int acMobileUnitsPerSide[kcSides]; + memset(acMobileUnitsPerSide, 0, sizeof(acMobileUnitsPerSide)); + for (gt = 0; gt < ARRAYSIZE(s_aszGobs); gt++) { + if (!afMobileUnit[gt]) + continue; + for (Side side = ksideNeutral; side < kcSides; side++) + acMobileUnitsPerSide[side] += acGobsSide[gt][side]; + } + FormatSideString("Munts", acMobileUnitsPerSide, szT); + OutputString(pbm, szT, &x, &y); + + // Total gobs + + int cGobs = 0; + for (gt = 0; gt < ARRAYSIZE(s_aszGobs); gt++) + cGobs += acGobs[gt]; + sprintf(szT, "total:%d", cGobs); + OutputString(pbm, szT, &x, &y); + + // Total memory consumed + + long cbGobs = 0; + for (gt = 0; gt < ARRAYSIZE(s_aszGobs); gt++) + cbGobs += (long)(acbGob[gt] + 8) * acGobs[gt]; + sprintf(szT, "gob mem:%ld", cbGobs); + OutputString(pbm, szT, &x, &y); + +#ifdef PIL + UInt32 cbFree, cbMax; + MemHeapFreeBytes(0, &cbFree, &cbMax); + sprintf(szT, "heap free:%ld", cbFree); + OutputString(pbm, szT, &x, &y); + sprintf(szT, "dyn use:%ld/%ld", gcbDynMemAtStart - cbFree, gcbDynMemAtStart); + OutputString(pbm, szT, &x, &y); +#endif +} + +void GobCountForm::OutputString(DibBitmap *pbm, char *psz, int *px, int *py) +{ + Font *pfnt = gapfnt[kifntDefault]; + + char *pszNext = ","; + int cxNext = pfnt->GetTextExtent(pszNext); + + int x = *px; + int y = *py; + + int cx = pfnt->GetTextExtent(psz); + if (x + cxNext + cx > m_rc.right) { + y += pfnt->GetHeight(); + x = m_rc.left; + } else { + if (x != m_rc.left) { + pfnt->DrawText(pbm, pszNext, x, y); + x += cxNext; + } + } + pfnt->DrawText(pbm, psz, x, y); + x += cx; + + *px = x; + *py = y; +} + +void GobCountForm::FormatSideString(char *pszIn, int *acPerSide, char *pszOut) +{ + int cTotal = 0; + Side sideLast = (Side)-1; + for (Side side = ksideNeutral; side < kcSides; side++) { + cTotal += acPerSide[side]; + if (acPerSide[side] != 0) + sideLast = side; + } + + sprintf(pszOut, "%s:", pszIn); + char szT[32]; + for (Side sideT = ksideNeutral; sideLast != (Side)-1 && sideT <= sideLast; sideT++) { + sprintf(szT, sideT != sideLast ? "%d/" : "%d", acPerSide[sideT]); + strcat(pszOut, szT); + } + sprintf(szT, "(%d)", cTotal); + strcat(pszOut, szT); +} + +void GobCountForm::OnUpdateMapInvalidate(UpdateMap *pupd, Rect *prcOpaque) +{ + pupd->InvalidateRect(); + Form::OnUpdateMapInvalidate(pupd, prcOpaque); +} + +bool GobCountForm::OnPenEvent(Event *pevt) +{ + if (pevt->eType == penDownEvent) { + EndForm(kidcOk); + return true; + } + return false; +} + +// +// InputPanel form +// + +class InputPanelForm : public ShellForm +{ +public: + virtual void OnControlSelected(word idc) secInputPanelForm; + virtual void OnUpdateMapInvalidate(UpdateMap *pupd, Rect *prcOpaque) secInputPanelForm; + virtual void OnPaint(DibBitmap *pbm) secInputPanelForm; + virtual bool OnPenEvent(Event *pevt) secInputPanelForm; + virtual bool EventProc(Event *pevt) secInputPanelForm; + virtual bool Init(FormMgr *pfrmm, IniReader *pini, word idf) secInputPanelForm; + + void SetLabel(const char *pszLabel) secInputPanelForm; + void SetEdit(const char *pszEdit) secInputPanelForm; + void GetEdit(char *psz, int cb) secInputPanelForm; + void SetChars(char **ppsz, int cRows) secInputPanelForm; + void SetValidateProc(bool (*pfnValidateInput)(const char *psz)) { + m_pfnValidateInput = pfnValidateInput; + } + +private: + char TrackPen(int x, int y, bool fDown) secInputPanelForm; + void OnChar(char ch) secInputPanelForm; + void OnBackspace() secInputPanelForm; + int GetCharRect(int iCol, int iRow, char *pch, Rect *prcChar) secInputPanelForm; + + char **m_ppszChars; + int m_cRows; + int m_iColLast; + int m_iRowLast; + bool m_fDown; + int m_xForm; + int m_yForm; + Font *m_pfnt; + bool (*m_pfnValidateInput)(const char *psz); +}; + +bool DoInputPanelForm(char **ppszChars, int cRows, const char *pszLabel, + const char *pszEdit, char *pszOut, int cbOut, + bool (*pfnValidateInput)(const char *psz)) +{ + InputPanelForm *pfrm = (InputPanelForm *)gpmfrmm->LoadForm(gpiniForms, kidfInputPanel, new InputPanelForm()); + if (pfrm != NULL) { + pfrm->SetValidateProc(pfnValidateInput); + pfrm->SetLabel(pszLabel); + pfrm->SetEdit(pszEdit); + if (ppszChars != NULL) + pfrm->SetChars(ppszChars, cRows); + if (pfrm->DoModal()) { + pfrm->GetEdit(pszOut, cbOut); + delete pfrm; + return true; + } else { + strncpyz(pszOut, pszEdit, cbOut); + delete pfrm; + return false; + } + } + + return false; +} + +void DoInputPanelForm(Form *pfrm, word idcLabel, word idcEdit) +{ + LabelControl *plbl = (LabelControl *)pfrm->GetControlPtr(idcLabel); + const char *pszLabel = plbl->GetText(); + char szEdit[128]; + EditControl *pedt = (EditControl *)pfrm->GetControlPtr(idcEdit); + pedt->GetText(szEdit, sizeof(szEdit)); + DoInputPanelForm(NULL, 0, pszLabel, szEdit, szEdit, sizeof(szEdit)); + pedt->SetText(szEdit); +} + +bool InputPanelForm::Init(FormMgr *pfrmm, IniReader *pini, word idf) +{ + // Get initial form dimensions before passing to ShellForm since it reformats + + char szForm[32]; + itoa(idf, szForm, 10); + int x, y, cx, cy; + pini->GetPropertyValue(szForm, "FORM", "(%d %d %d %d)", &x, &y, &cx, &cy); + Size siz; + pfrmm->GetDib()->GetSize(&siz); + m_xForm = (siz.cx - PcFromFc(cx)) / 2; + m_yForm = (siz.cy - PcFromFc(cy)) / 2; + m_pfnt = gapfnt[kifntShadow]; + m_iColLast = -1; + m_iRowLast = -1; + m_fDown = false; + + static char *s_apszChars[] = { + "ABCDEFGHIJKLM", + "NOPQRSTUVWXYZ", + "abcdefghijklm", + "nopqrstuvwxyz", + "1234567890?!*" + }; + + m_ppszChars = s_apszChars; + m_cRows = ARRAYSIZE(s_apszChars); + m_pfnValidateInput = NULL; + + return ShellForm::Init(pfrmm, pini, idf); +} + +const int kxBox = 5; +const int kyBox = 35; +const int kcxBox = (160 - (5 + 5)); +const int kcyBox = (118 - kyBox - 5); + +int InputPanelForm::GetCharRect(int iCol, int iRow, char *pch, Rect *prcChar) +{ + int cRows = m_cRows; + int cCols = strlen(m_ppszChars[0]); + prcChar->SetEmpty(); + if (iRow < 0 || iRow >= cRows) + return 0; + if (iCol < 0 || iCol >= (int)strlen(m_ppszChars[iRow])) + return 0; + + prcChar->left = PcFromFc(kxBox) + PcFromFc(kcxBox) * iCol / cCols; + prcChar->right = PcFromFc(kxBox) + PcFromFc(kcxBox) * (iCol + 1) / cCols; + prcChar->top = PcFromFc(kyBox) + PcFromFc(kcyBox) * iRow / cRows; + prcChar->bottom = PcFromFc(kyBox) + PcFromFc(kcyBox) * (iRow + 1) / cRows; + prcChar->Offset(m_xForm, m_yForm); + *pch = m_ppszChars[iRow][iCol]; + return m_pfnt->GetTextExtent(pch, 1); +} + +char InputPanelForm::TrackPen(int x, int y, bool fDown) +{ + // Hittest + + char ch = (char)-1; + Rect rcChar; + int iRow, iCol; + bool fFound = false; + for (iRow = 0; iRow < m_cRows; iRow++) { + int cCols = strlen(m_ppszChars[iRow]); + for (iCol = 0; iCol < cCols; iCol++) { + GetCharRect(iCol, iRow, &ch, &rcChar); + if (rcChar.PtIn(x, y)) { + fFound = true; + break; + } + } + if (fFound) + break; + } + + // Track + + if (iRow != m_iRowLast || iCol != m_iColLast || fDown != m_fDown) { + char chT; + Rect rcT; + GetCharRect(iCol, iRow, &chT, &rcT); + InvalidateRect(&rcT); + GetCharRect(m_iColLast, m_iRowLast, &chT, &rcT); + InvalidateRect(&rcT); + m_iRowLast = iRow; + m_iColLast = iCol; + } + m_fDown = fDown; + return fFound ? ch : (char)-1; +} + +bool InputPanelForm::OnPenEvent(Event *pevt) +{ + switch (pevt->eType) { + case penDownEvent: + TrackPen(pevt->x, pevt->y, true); + break; + + case penUpEvent: + { + char ch = TrackPen(pevt->x, pevt->y, false); + if (ch != (char)-1) { + gsndm.PlaySfx(ksfxGuiButtonTap); + OnChar(ch); + } + } + break; + + case penMoveEvent: + TrackPen(pevt->x, pevt->y, m_fDown); + break; + } + + return ShellForm::OnPenEvent(pevt); +} + +bool InputPanelForm::EventProc(Event *pevt) +{ + if (pevt->eType != keyDownEvent) + return ShellForm::EventProc(pevt); + + // Keyboard interface + + if (pevt->chr == chrBackspace || pevt->chr == chrDelete) { + OnBackspace(); + } else { + for (int iRow = 0; iRow < m_cRows; iRow++) { + int cCols = strlen(m_ppszChars[iRow]); + for (int iCol = 0; iCol < cCols; iCol++) { + if (pevt->chr == m_ppszChars[iRow][iCol]) + OnChar(pevt->chr); + } + } + } + + return false; +} + +void InputPanelForm::OnUpdateMapInvalidate(UpdateMap *pupd, Rect *prcOpaque) +{ + ShellForm::OnUpdateMapInvalidate(pupd, prcOpaque); + + Rect rc; + rc.SetEmpty(); + for (int iRow = 0; iRow < m_cRows; iRow++) { + int cCols = strlen(m_ppszChars[iRow]); + for (int iCol = 0; iCol < cCols; iCol++) { + Rect rcChar; + char ch; + GetCharRect(iCol, iRow, &ch, &rcChar); + rc.Union(&rcChar); + } + } + pupd->InvalidateRect(&rc); +} + +void InputPanelForm::OnPaint(DibBitmap *pbm) +{ + ShellForm::OnPaint(pbm); + + int cyChar = m_pfnt->GetHeight(); + for (int iRow = 0; iRow < m_cRows; iRow++) { + int cCols = strlen(m_ppszChars[iRow]); + for (int iCol = 0; iCol < cCols; iCol++) { + Rect rcChar; + char ch; + int cxChar = GetCharRect(iCol, iRow, &ch, &rcChar); + int xChar = rcChar.left + (rcChar.Width() - cxChar) / 2; + int yChar = rcChar.top + (rcChar.Height() - cyChar) / 2; + m_pfnt->DrawText(pbm, &ch, xChar, yChar, 1); + if (iCol == m_iColLast && iRow == m_iRowLast && m_fDown) + DrawBorder(pbm, &rcChar, 1, GetColor(kiclrWhite), NULL); + } + } +} + +void InputPanelForm::OnChar(char ch) +{ + char szT[128]; + GetEdit(szT, sizeof(szT)); + int cch = strlen(szT); + if (cch < sizeof(szT) - 1) { + szT[cch] = ch; + szT[cch + 1] = 0; + SetEdit(szT); + } +} + +void InputPanelForm::OnBackspace() +{ + char szT[128]; + GetEdit(szT, sizeof(szT)); + int cch = strlen(szT); + if (cch > 0) { + szT[cch - 1] = 0; + SetEdit(szT); + } +} + +void InputPanelForm::OnControlSelected(word idc) +{ + if (idc == kidcInputEdit) + return; + + if (idc == kidcBackspace) { + OnBackspace(); + return; + } + + if (idc == kidcOk) { + if (m_pfnValidateInput != NULL) { + char szT[255]; + GetEdit(szT, sizeof(szT)); + if (!m_pfnValidateInput(szT)) + return; + } + } + + ShellForm::OnControlSelected(idc); +} + +void InputPanelForm::SetLabel(const char *pszLabel) +{ + LabelControl *plbl = (LabelControl *)GetControlPtr(kidcInputLabel); + plbl->SetText(pszLabel); + Rect rcLabel; + plbl->GetRect(&rcLabel); + EditControl *pedt = (EditControl *)GetControlPtr(kidcInputEdit); + Rect rcEdit; + pedt->GetRect(&rcEdit); + rcEdit.left = rcLabel.right + 3; + pedt->SetRect(&rcEdit); +} + +void InputPanelForm::SetChars(char **ppsz, int cRows) +{ + m_ppszChars = ppsz; + m_cRows = cRows; +} + +void InputPanelForm::SetEdit(const char *pszEdit) +{ + EditControl *pedt = (EditControl *)GetControlPtr(kidcInputEdit); + pedt->SetText(pszEdit); +} + +void InputPanelForm::GetEdit(char *psz, int cb) +{ + EditControl *pedt = (EditControl *)GetControlPtr(kidcInputEdit); + pedt->GetText(psz, cb); +} + +// DeleteMissionPackForm + +bool DeleteMissionPackForm::Init(FormMgr *pfrmm, IniReader *pini, word idf) +{ + if (!ShellForm::Init(pfrmm, pini, idf)) + return false; + PopulateList(); + return true; +} + +void DeleteMissionPackForm::PopulateList() +{ + ListControl *plstc = (ListControl *)GetControlPtr(kidcMissionPackList); + plstc->Clear(); +#if 0 // TODO: + Enum enm; + char szAddon[kcbFilename]; + while (HostEnumAddonFiles(&enm, szAddon, sizeof(szAddon))) + plstc->Add(szAddon, NULL); +#endif + GetControlPtr(kidcOk)->Show(false); +} + +void DeleteMissionPackForm::OnControlNotify(word idc, int nNotify) +{ + if (idc != kidcMissionPackList) + return; + + if (nNotify == knNotifySelectionChange || nNotify == knNotifySelectionTap) { + ListControl *plstc = (ListControl *)GetControlPtr(kidcMissionPackList); + ListItem *pli = plstc->GetSelectedItem(); + GetControlPtr(kidcOk)->Show(pli != NULL); + } +} + +void DeleteMissionPackForm::OnControlSelected(word idc) +{ + switch (idc) { + case kidcMissionPackList: + return; + + case kidcOk: + // Delete + { + ListControl *plstc = (ListControl *)GetControlPtr(kidcMissionPackList); + char szFn[64]; + plstc->GetSelectedItemText(szFn, sizeof(szFn)); + if (true) { // TODO: !gpakr.Delete(gpszDataDir, szFn)) { + HtMessageBox(kfMbWhiteBorder, "Error", "Mission Pack in use!"); + } else { + PopulateList(); + } + } + return; + + case kidcCancel: + // Back + + ShellForm::OnControlSelected(idc); + break; + } +} + +} // namespace wi diff --git a/game/HRC.cpp b/game/HRC.cpp new file mode 100644 index 0000000..734ab98 --- /dev/null +++ b/game/HRC.cpp @@ -0,0 +1,68 @@ +#include "ht.h" + +namespace wi { + +// +// HrcGob implementation +// + +static MobileUnitBuilderConsts gConsts; +MobileUnitBuildForm *HrcGob::s_pfrmBuild = NULL; + +#if defined(DEBUG_HELPERS) +char *HrcGob::GetName() +{ + return "HRC"; +} +#endif + +bool HrcGob::InitClass(IniReader *pini) +{ + gConsts.gt = kgtHumanResourceCenter; + gConsts.ut = kutHumanResourceCenter; + gConsts.umPrerequisites = kumReactor; + gConsts.umCanBuild = kumInfantry & ~(kumAndy | kumFox); + gConsts.wf |= kfUntcMobileUnitBuilder; + + // Preload the Hrc's build form + + s_pfrmBuild = new MobileUnitBuildForm(); + if (s_pfrmBuild == NULL) + return false; + + if (!s_pfrmBuild->Init(gpmfrmm, gpiniForms, kidfBuildInfantry)) + return false; + gpmfrmm->RemoveForm(s_pfrmBuild); + + // Sound effects + + gConsts.sfxUnitBuildAbort = ksfxHumanResourceCenterAbortRecruiting; + gConsts.sfxUnitBuild = ksfxHumanResourceCenterRecruit; + gConsts.sfxUnitReady = ksfxHumanResourceCenterUnitReady; + gConsts.sfxAbortRepair = ksfxHumanResourceCenterAbortRepair; + gConsts.sfxDamaged = ksfxHumanResourceCenterDamaged; + gConsts.sfxDestroyed = ksfxHumanResourceCenterDestroyed; + gConsts.sfxRepair = ksfxHumanResourceCenterRepair; + gConsts.sfxSelect = ksfxHumanResourceCenterSelect; + + // MobileUnitBuilderConsts + + gConsts.fUpgrade = kfUpgradeHrc; + gConsts.fUpgradeInProgress = kfUpgradeHrcInProgress; + gConsts.pfrmBuild = s_pfrmBuild; + + return BuilderGob::InitClass(&gConsts, pini); +} + +void HrcGob::ExitClass() +{ + BuilderGob::ExitClass(&gConsts); + delete s_pfrmBuild; + s_pfrmBuild = NULL; +} + +HrcGob::HrcGob() : MobileUnitBuilderGob(&gConsts) +{ +} + +} // namespace wi \ No newline at end of file diff --git a/game/Headquarters.cpp b/game/Headquarters.cpp new file mode 100644 index 0000000..c6e5b53 --- /dev/null +++ b/game/Headquarters.cpp @@ -0,0 +1,1113 @@ +#include "ht.h" + +namespace wi { + +class StructureBuildForm : public Form +{ +public: + BuilderGob *GetOwner() { + return m_pgobOwner; + } + void SetOwner(BuilderGob *pgobOwner) secStructures; + void UpdateStructureInfo(ListItem *pli) secStructures; + + // Form overrides + + void EndForm(int nResult = kidcCancel) secStructures; + virtual void OnControlSelected(word idc) secStructures; + virtual void OnControlNotify(word idc, int nNotify); + virtual void OnPaintBackground(DibBitmap *pbm, UpdateMap *pupd) secStructures; + +private: + BuilderGob *m_pgobOwner; + bool m_fLimitReached; +}; + + +// +// HqGob implementation +// + +BuilderConsts gbldcHq; +StructureBuildForm *HqGob::s_pfrmBuild = NULL; +PlaceStructureForm *gpfrmPlace = NULL; +static TBitmap *s_ptbmPlacementGood = NULL; +static TBitmap *s_ptbmPlacementBad = NULL; + +// +// Gob methods +// + +bool HqGob::InitClass(IniReader *pini) +{ + gbldcHq.gt = kgtHeadquarters; + gbldcHq.ut = kutHeadquarters; + gbldcHq.umCanBuild = kumStructures & ~(kumHeadquarters | kumReplicator); + gbldcHq.wf |= kfUntcStructureBuilder; + + // Preload the HQ's build form + + s_pfrmBuild = new StructureBuildForm(); + if (s_pfrmBuild == NULL) { + Assert("fatal error"); + return false; + } + + if (!s_pfrmBuild->Init(gpmfrmm, gpiniForms, kidfBuildStructure)) { + Assert("fatal error"); + return false; + } + gpmfrmm->RemoveForm(s_pfrmBuild); + + // Preload the HQ's place form + + gpfrmPlace = new PlaceStructureForm(); + if (gpfrmPlace == NULL) { + Assert("fatal error"); + return false; + } + + if (!gpfrmPlace->Init(gpfrmmSim, gpiniForms, kidfPlaceStructure)) { + Assert("fatal error"); + return false; + } + gpfrmmSim->RemoveForm(gpfrmPlace); + + // Preload the placement tile bitmaps + + s_ptbmPlacementGood = LoadTBitmap("placementGood.tbm"); + s_ptbmPlacementBad = LoadTBitmap("placementBad.tbm"); + + // Sound effects + + gbldcHq.sfxUnitBuildAbort = ksfxHeadquartersAbortConstruction; + gbldcHq.sfxUnitBuild = ksfxHeadquartersConstruct; + gbldcHq.sfxUnitReady = ksfxHeadquartersStructureReady; + gbldcHq.sfxAbortRepair = ksfxHeadquartersAbortRepair; + gbldcHq.sfxRepair = ksfxHeadquartersRepair; + gbldcHq.sfxDamaged = ksfxHeadquartersDamaged; + gbldcHq.sfxDestroyed = ksfxHeadquartersDestroyed; + gbldcHq.sfxSelect = ksfxHeadquartersSelect; + + return BuilderGob::InitClass(&gbldcHq, pini); +} + +void HqGob::ExitClass() +{ + delete s_ptbmPlacementGood; + s_ptbmPlacementGood = NULL; + delete s_ptbmPlacementBad; + s_ptbmPlacementBad = NULL; + delete gpfrmPlace; + gpfrmPlace = NULL; + delete s_pfrmBuild; + s_pfrmBuild = NULL; + + BuilderGob::ExitClass(&gbldcHq); +} + +HqGob::HqGob() : BuilderGob(&gbldcHq) +{ + m_bq.SetSize(1); // no queuing +} + +HqGob::~HqGob() +{ + // if the structure placement form is up, pull it down + + if (gpfrmPlace->GetOwner() != NULL) + gpfrmPlace->OnControlSelected(kidcCancel); +} + +void HqGob::InitMenu(Form *pfrm) +{ + // toggle on/off the right stuff + + Control *pctl = pfrm->GetControlPtr(kidcBuild); + pctl->Show(!m_bq.IsFull()); + + pctl = pfrm->GetControlPtr(kidcAbortBuild); + pctl->Show(!m_bq.IsEmpty()); + + BuilderGob::InitMenu(pfrm); +} + +void HqGob::OnMenuItemSelected(int idc) +{ + switch (idc) { + case kidcBuild: + { + gpmfrmm->AddForm(s_pfrmBuild); + s_pfrmBuild->SetOwner(this); + int idc; + s_pfrmBuild->DoModal(&idc); + gpmfrmm->RemoveForm(s_pfrmBuild); + + if (idc == kidcCancel) + break; + + gpfrmPlace->SetOwner(this, GetUnitConsts(idc)->ut); + m_unvl.MinSkip(); + gpfrmmSim->AddForm(gpfrmPlace); + } + break; + + case kidcAbortBuild: + if (PopupConfirmation("ABORT BUILD")) { + gcmdq.Enqueue(kmidAbortBuildOtherCommand, m_gid); + if (m_pplr == gpplrLocal) + gsndm.PlaySfx(m_pbldrc->sfxUnitBuildAbort); + } + break; + + default: + BuilderGob::OnMenuItemSelected(idc); + break; + } +} + +// take down the build and place forms, HQ is no longer +// in working order + +void HqGob::Deactivate() +{ + BuilderGob::Deactivate(); + + if (gpfrmPlace->GetOwner() == this) + gpfrmPlace->OnControlSelected(kidcCancel); + if (s_pfrmBuild->GetOwner() == this) + s_pfrmBuild->EndForm(); +} + +// +// StateMachine methods +// + +#if defined(DEBUG_HELPERS) +char *HqGob::GetName() +{ + return "Headquarters"; +} +#endif + +void HqGob::RemoveScorch(StructGob *pstru) +{ + // Get surrounding tile rect + + TRect trc; + pstru->GetTileRect(&trc); + + // Inflate by one tile + + trc.Inflate(1, 1); + + // Clip to map + + if (trc.left < 0) + trc.left = 0; + if (trc.top < 0) + trc.top = 0; + TCoord ctx, cty; + ggobm.GetMapSize(&ctx, &cty); + if (trc.right > ctx) + trc.right = ctx; + if (trc.bottom > cty) + trc.bottom = cty; + + // Remove scorch gobs that'll be underneath this structure + + for (int ty = trc.top; ty < trc.bottom; ty++) { + for (int tx = trc.left; tx < trc.right; tx++) { + Gid gid = ggobm.GetFirstGid(tx, ty); + while (gid != kgidNull) { + Gid gidNext = ggobm.GetNextGid(gid); + Gob *pgob = ggobm.GetGob(gid, false); + if (pgob != NULL && pgob->GetType() == kgtScorch) { + ggobm.RemoveGob(pgob); + delete pgob; + } + gid = gidNext; + } + } + } +} + +int HqGob::ProcessStateMachineMessage(State st, Message *pmsg) +{ +BeginStateMachine + OnMsg(kmidBuildOtherCommand) + // Only if the queue is empty since unit only knows how to build one at a time + + if (m_bq.IsEmpty()) { + // Counts will already have been checked in the order UI. However since the order is queued, the UI only + // guesses if it is possible to build based on current state when the button is pressed. Here we make a + // bedrock decision. This ensures the limits are enforced and that the same decision gets made on all + // clients in a multiplayer game. + + if (ggobm.IsBelowLimit(knLimitStruct, m_pplr)) { + // BUGBUG: due to network lag we can't assume the destination position + // hasn't become occupied between when this command was issued and when + // it is received. + StructGob *pstru = (StructGob *)CreateGob(gapuntc[pmsg->BuildOtherCommand.ut]->gt); + if (pstru == NULL) + return knHandled; + pstru->Init(pmsg->BuildOtherCommand.wpt.wx, pmsg->BuildOtherCommand.wpt.wy, m_pplr, 0, kfGobBeingBuilt, NULL); + pstru->SetHealth(0); + + // Clear out scorch marks that are here + + RemoveScorch(pstru); + + Build(pmsg->BuildOtherCommand.ut, pstru->GetId()); + } + } + + OnMsg(kmidAbortBuildOtherCommand) + AbortBuild(true); + + OnMsg(kmidSelfDestructCommand) + + // override and call abort build here so we can tell it to refund the value. + // AbortBuild is also in BuilderGob::Deactivate but will do nothing if called + // a 2nd time, and there it wouldnt refund if we left it + + AbortBuild(true); + SelfDestruct(); + + State(kstBuildOtherCompleting) + OnEnter + + // it's now too late to decide to abort + + TakedownConfirmation(); + + gsmm.SendMsg(kmidBuildComplete, m_gidBuildVisible); + + // Notify the BuildMgr that this Unit is complete + + gsim.GetBuildMgr()->OnBuilt(GetBuiltGob(), this); + + ClearBuiltGob(); + SetState(kstIdle); + + // These are here to keep the message from routing up to BuilderGob's message handler + OnUpdate + OnExit + +#if 0 +EndStateMachineInherit(BuilderGob) +#else + return knHandled; + } + } else { + return (int)BuilderGob::ProcessStateMachineMessage(st, pmsg); + } + return (int)BuilderGob::ProcessStateMachineMessage(st, pmsg); +#endif +} + +//=========================================================================== +// StructureBuildForm implementation + +void StructureBuildForm::SetOwner(BuilderGob *pgobOwner) +{ + Size sizDib; + m_pfrmm->GetDib()->GetSize(&sizDib); + Rect rc; + rc.left = ((sizDib.cx - m_rc.Width()) / 2) & ~1; + rc.top = 0; // (sizDib.cy - m_rc.Height()) / 2; + rc.right = rc.left + m_rc.Width(); + rc.bottom = rc.top + m_rc.Height(); + SetRect(&rc); + + m_wf |= kfFrmAutoTakedown | kfFrmNoEcom; + m_pgobOwner = pgobOwner; + + BuildListControl *plstc = (BuildListControl *)GetControlPtr(kidcList); + plstc->Clear(); + + // Initialize list with available structure types. + // UNDONE: need to link list to dynamic build capability + + Player *pplr = pgobOwner->GetOwner(); + UnitMask umOwned = (pplr->GetUnitMask() & kumStructures) | pplr->GetUpgrades(); + BuilderConsts *pbldrc = (BuilderConsts *)pgobOwner->GetConsts(); + UnitMask umAllowed = pbldrc->umCanBuild & (gfGodMode ? kumAll : pplr->GetAllowedUnits()); + UpgradeMask upgmOwned = pplr->GetUpgradeMask(); + for (int i = 0; i < kutMax; i++) { + UnitConsts *puntc = gapuntc[i]; + + // Does this player have what it takes to build this structure/unit? + + bool fDisabled = true; + if (gfGodMode || ((umOwned & puntc->umPrerequisites) == puntc->umPrerequisites && + (upgmOwned & puntc->upgmPrerequisites) == puntc->upgmPrerequisites)) + fDisabled = false; + + // Yep, is this one of the types this build form allows? + + if (puntc->um & umAllowed) { + + // Yep, add it to the list + + int nStripIcon = puntc->panid->GetStripIndex("icon"); + if (nStripIcon != -1) { + plstc->Add(puntc->panid, nStripIcon, 0, (void *)(int)puntc->ut, fDisabled); + } + } + } + + // Select the first unit by default + + plstc->Select(m_pgobOwner->GetLastSelection()); + plstc->SetQueueInfo(NULL, NULL); + + // If we're at the structure limit, hide the order button, show the limit reached text + // Otherwise hide the limit reached text + + if (ggobm.IsBelowLimit(knLimitStruct, m_pgobOwner->GetOwner())) { + m_fLimitReached = false; + } else { + m_fLimitReached = true; + GetControlPtr(kidcOk)->Show(false); + } + GetControlPtr(kidcLimitReached)->Show(m_fLimitReached); + + if (!m_fLimitReached && plstc->GetSelectedItemIndex() == -1) { + GetControlPtr(kidcOk)->Show(false); + } +} + +void StructureBuildForm::UpdateStructureInfo(ListItem *pli) +{ + StructConsts *pstruc = (StructConsts *)gapuntc[(int)pli->pvData]; + + // Update Cost + + LabelControl *plbl = (LabelControl *)GetControlPtr(kidcCost); + char szT[kcbStructUnitName]; + itoa(pstruc->GetCost(), szT, 10); + plbl->SetText(szT); + PipMeterControl *pmtr = (PipMeterControl *)GetControlPtr(kidcCostMeter); + pmtr->SetValue(((long)pstruc->GetCost() * 100) / GetUnitCostMax()); + + // Update Name + + plbl = (LabelControl *)GetControlPtr(kidcName); + strcpy(szT, pstruc->szLongName); + HtStrupr(szT); + plbl->SetText(szT); + + // Update Power Demand + + plbl = (LabelControl *)GetControlPtr(kidcPowerDemand); + itoa(pstruc->nPowerDemand, szT, 10); + plbl->SetText(szT); + pmtr = (PipMeterControl *)GetControlPtr(kidcPowerDemandMeter); + + // Use gnPowerSupplyMax as the scaler instead of gnPowerDemandMax so + // both supply and demand can be compared on the same scale + + pmtr->SetValue(((long)pstruc->nPowerDemand * 100) / gnPowerSupplyMax); + + // Update Power Supply + + plbl = (LabelControl *)GetControlPtr(kidcPowerSupply); + itoa(pstruc->nPowerSupply, szT, 10); + plbl->SetText(szT); + pmtr = (PipMeterControl *)GetControlPtr(kidcPowerSupplyMeter); + pmtr->SetValue(((long)pstruc->nPowerSupply * 100) / gnPowerSupplyMax); + + // Update Firepower + + DamageMeterControl *pdmtr = (DamageMeterControl *)GetControlPtr(kidcWeaponStrengthMeter); + pdmtr->SetUnitConsts(pstruc); + + // Update Armor + + char *pszT; + fix fxOneThirdRange = (fix)divfx(subfx(gfxStructureArmorStrengthMax, gfxStructureArmorStrengthMin), itofx(3)); + plbl = (LabelControl *)GetControlPtr(kidcArmorStrength); + pszT = "MEDIUM"; + if (pstruc->GetArmorStrength() == 0) + pszT = "NONE"; + else if (pstruc->GetArmorStrength() < addfx(gfxStructureArmorStrengthMin, fxOneThirdRange)) + pszT = "LIGHT"; + else if (pstruc->GetArmorStrength() >= subfx(gfxStructureArmorStrengthMax, fxOneThirdRange)) + pszT = "HEAVY"; + plbl->SetText(pszT); + pmtr = (PipMeterControl *)GetControlPtr(kidcArmorStrengthMeter); + pmtr->SetValue(((long)fxtoi(pstruc->GetArmorStrength()) * 100) / fxtoi(gfxStructureArmorStrengthMax)); + + // Update Description. Substitute the list of prerequisites if the + // item is disabled. + + plbl = (LabelControl *)GetControlPtr(kidcDescription); + if (pli->fDisabled) { + char szT[120]; + GetPrerequisiteString(szT, pstruc); + + char szT2[120]; + sprintf(szT2, "This building requires: %s.", szT); + plbl->SetText(szT2); + + } else { + plbl->SetText(pstruc->szDescription); + } + + // Hide the "Build" button if this structure is disabled + + if (!m_fLimitReached) { + ButtonControl *pbtn = (ButtonControl *)GetControlPtr(kidcOk); + pbtn->Show(!pli->fDisabled); + } +} + +void StructureBuildForm::EndForm(int nResult) +{ + ListControl *plstc = (ListControl *)GetControlPtr(kidcList); + m_pgobOwner->SetLastSelection(plstc->GetSelectedItemIndex()); + m_pgobOwner = NULL; + Form::EndForm(nResult); +} + +void StructureBuildForm::OnControlSelected(word idc) +{ + switch (idc) { + case kidcCancel: + EndForm(kidcCancel); + return; + + case kidcHelp: + Help("buildings", !ggame.IsMultiplayer()); + break; + + case kidcList: + { + ListControl *plstc = (ListControl *)GetControlPtr(kidcList); + if (plstc->GetSelectedItem() == NULL) + return; + UpdateStructureInfo(plstc->GetSelectedItem()); + } + break; + + case kidcOk: + ListControl *plstc = (ListControl *)GetControlPtr(kidcList); + if (plstc->GetSelectedItem() != NULL) + EndForm(gapuntc[UnitTypeFromPVoid(plstc->GetSelectedItemData())]->gt); + return; + } +} + +void StructureBuildForm::OnControlNotify(word idc, int nNotify) +{ + if (idc != kidcList) { + return; + } + + if (nNotify != knNotifySelectionChange && nNotify != knNotifySelectionTap) { + return; + } + + ListControl *plstc = (ListControl *)GetControlPtr(kidcList); + if (plstc->GetSelectedItemIndex() == -1) { + GetControlPtr(kidcOk)->Show(false); + } +} + +void StructureBuildForm::OnPaintBackground(DibBitmap *pbm, UpdateMap *pupd) +{ + RawBitmap *prbm = LoadRawBitmap("buildformbkgd.rbm"); + BltHelper(pbm, prbm, pupd, m_rc.left, m_rc.top); + delete prbm; +} + +//=========================================================================== +// PlaceStructureForm implementation +// + +PlaceStructureForm::PlaceStructureForm() +{ + m_pgobOwner = NULL; + m_fDragging = false; + m_fPassOnInput = false; + m_wf |= kfFrmTranslucent; +} + +bool PlaceStructureForm::OnHitTest(Event *pevt) +{ + // If not passing on input to the form below, do normal form + // processing + + if (!m_fPassOnInput) { + return Form::OnHitTest(pevt); + } + + // Nothing goes to an invisible form + + if (!(m_wf & kfFrmVisible)) { + return false; + } + + // If the form has the mouse captured it should get input + + if (HasCapture()) { + return true; + } + + // Otherwise, input not on the placement form is passed on. This + // is because the SimUI form moves the map with finger input. + + for (int n = 0; n < m_cctl; n++) { + Control *pctl = m_apctl[n]; + if (pctl->OnHitTest(pevt) >= 0) { + return true; + } + } + + // On the placement form somewhere else? + + WCoord wxViewOrigin, wyViewOrigin; + gsim.GetViewPos(&wxViewOrigin, &wyViewOrigin); + Rect rcInside, rcOutside; + GetSubRects(wxViewOrigin, wyViewOrigin, &rcInside, &rcOutside); + if (rcOutside.PtIn(pevt->x, pevt->y)) { + return true; + } + + // Pass it on to next form + + return false; +} + +void PlaceStructureForm::OnControlSelected(word idc) +{ + gtimm.RemoveTimer(this); + + if (idc == kidcCancel) { + gpfrmmSim->RemoveForm(this); + m_pgobOwner = NULL; + return; + } + + Message msg; + memset(&msg, 0, sizeof(msg)); + msg.smidSender = ksmidNull; + msg.mid = kmidBuildOtherCommand; + msg.BuildOtherCommand.ut = m_pstruc->ut; + msg.BuildOtherCommand.wpt.wx = WcFromTc(m_tx); + msg.BuildOtherCommand.wpt.wy = WcFromTc(m_ty); + msg.smidReceiver = m_pgobOwner->GetId(); + gcmdq.Enqueue(&msg); + + if (m_pgobOwner->GetOwner() == gpplrLocal) + gsndm.PlaySfx(((BuilderConsts *)m_pgobOwner->GetConsts())->sfxUnitBuild); + + gpfrmmSim->RemoveForm(this); + + m_pgobOwner = NULL; +} + +void PlaceStructureForm::SetOwner(BuilderGob *pgobOwner, UnitType ut) +{ + gtimm.AddTimer(this, kctMapScrollRate); + + // form is loaded as the size of the whole playfield + // draw the structure placement indicator near the HQ + m_pgobOwner = pgobOwner; + m_pstruc = (StructConsts *)gapuntc[ut]; + WPoint wptT; + m_pgobOwner->GetPosition(&wptT); + + WCoord wxView, wyView; + gsim.GetViewPos(&wxView, &wyView); + Size sizPlayfield; + ggame.GetPlayfieldSize(&sizPlayfield); + WCoord wxViewMax, wyViewMax; + wxViewMax = WcFromPc(sizPlayfield.cx) + wxView; + wyViewMax = WcFromPc(sizPlayfield.cy) + wyView; + + // be consistent about appearing below the HQ unless we will be hidden + + StructConsts *pstrucOwner = (StructConsts *)pgobOwner->GetConsts(); + WCoord wxTarget = wptT.wx; + WCoord wyTarget = wptT.wy + WcFromTc(pstrucOwner->ctyReserve); + + // now make sure we're onscreen + + wxViewMax -= WcFromTc(m_pstruc->ctxReserve); + if (wxTarget < wxView) + wxTarget = wxView; + else if (wxTarget > wxViewMax) + wxTarget = wxViewMax; + m_tx = m_txStart = TcFromWc(wxTarget); + + if (wyTarget < wyView) { + wyTarget = wyView; + } else { + + // watch out for being behind the minimap as well on the screen + + Form *pfrmMiniMap = gpmfrmm->GetFormPtr(kidfMiniMap); + Rect rc; + pfrmMiniMap->GetRect(&rc); + WCoord wyMiniMap = WcFromUpc(rc.top) + wyView; + if (wyTarget > wyMiniMap - WcFromTc(m_pstruc->ctyReserve) ) { + + // below the top of the mini map - check the horizontal + + WCoord wxMiniMap = WcFromUpc(rc.left) + wxView; + if (wxTarget + WcFromTc(m_pstruc->ctxReserve) > wxMiniMap) { + + // behind the mini map, go above + + wyTarget = wyMiniMap - WcFromTc(m_pstruc->ctyReserve); + } else { + + // not behind minimap, just watch screen bottom + + wyViewMax -= WcFromTc(m_pstruc->ctyReserve); + if (wyTarget > wyViewMax) + wyTarget = wyViewMax; + } + } + } + + m_ty = m_tyStart = TcFromWc(wyTarget); + + m_wxPen = m_wxDragStart = 0; + m_wyPen = m_wyDragStart = 0; + + // use UpdatePlacementIndicator to get the buttons initialized to the correct location + + UpdatePlacementIndicator(wxView, wyView, wxView, wyView); +} + +#define kwcButtonInset 56 +#define kwcButtonWidth (50 + kwcButtonInset) + +void PlaceStructureForm::GetSubRects(WCoord wx, WCoord wy, Rect *prcInside, + Rect *prcOutside) +{ + WRect wrcInside; + wrcInside.left = WcFromTc(m_tx); + wrcInside.top = WcFromTc(m_ty); + wrcInside.right = WcFromTc(m_tx + m_pstruc->ctxReserve); + wrcInside.bottom = WcFromTc(m_ty + m_pstruc->ctyReserve); + WRect wrcOutside = wrcInside; + +#ifdef IPHONE + if (wrcOutside.Width() < WcFromTc(3)) { + wrcOutside.Inflate((WcFromTc(3) - wrcOutside.Width()) / 2, 0); + } +#endif + + Size siz; + gsim.GetLevel()->GetTileMap()->GetMapSize(&siz); + if (wrcOutside.left < kwcButtonWidth) { + wrcOutside.left = kwcButtonWidth; + } + if (wrcOutside.right > WcFromUpc(siz.cx) - kwcButtonWidth) { + wrcOutside.right = WcFromUpc(siz.cx) - kwcButtonWidth; + } + + wrcInside.Offset(-wx, -wy); + prcInside->FromWorldRect(&wrcInside); + wrcOutside.Offset(-wx, -wy); + prcOutside->FromWorldRect(&wrcOutside); +} + +// do the math to move the hash marks that denote where your building might go +// but keep it tile-aligned. We make sure the hash marks stay on the map and +// stay close to where the pen is and in penmove we make sure the pen location +// we remember is always on the screen so in theory the structure placement +// indicator will always be at at least partially on screen. + +void PlaceStructureForm::UpdatePlacementIndicator(WCoord wxViewStart, WCoord wyViewStart, + WCoord wxView, WCoord wyView) +{ + // Invalidate our old location + + Rect rcInside, rcOutside; + GetSubRects(wxViewStart, wyViewStart, &rcInside, &rcOutside); + InvalidateRect(&rcOutside); + + // always use pen movement from start, incremental pen + // movements get rounded down to zero when converted to + // tiles + + m_tx = m_txStart + TcFromWc(m_wxPen) - TcFromWc(m_wxDragStart); + m_ty = m_tyStart + TcFromWc(m_wyPen) - TcFromWc(m_wyDragStart); + + // don't let the structure outline go off map at all + + if (m_tx < 0) + m_tx = 0; + if (m_ty < 0) + m_ty = 0; + Size siz; + gsim.GetLevel()->GetTileMap()->GetMapSize(&siz); + siz.cx /= gcxTile; + siz.cy /= gcyTile; + if (m_tx + m_pstruc->ctxReserve >= siz.cx) + m_tx = siz.cx - m_pstruc->ctxReserve; + if (m_ty + m_pstruc->ctyReserve >= siz.cy) + m_ty = siz.cy - m_pstruc->ctyReserve; + + // Invalidate our new location + + GetSubRects(wxView, wyView, &rcInside, &rcOutside); + InvalidateRect(&rcOutside); + + // Draw 'x', 'check' buttons + // check gets shown/hidden in DrawPlacementTiles because other gob movement + // affects whether or not it shows + + ButtonControl *pbtn = (ButtonControl *)GetControlPtr(kidcCancel); + Rect rcCtl; + pbtn->GetRect(&rcCtl); + int y = (PcFromWc(WcFromTc(m_ty) - wyView) + (PcFromTc(m_pstruc->ctyReserve) - rcCtl.Height()) / 2); + + pbtn->SetPosition(rcOutside.right - PcFromWc(kwcButtonInset), y); + pbtn = (ButtonControl *)GetControlPtr(kidcOk); + pbtn->SetPosition(rcOutside.left - rcCtl.Width() + + PcFromWc(kwcButtonInset), y); + + // See if it's valid + + bool fValid = true; + bool fPlacementValid = ggobm.IsStructurePlacementValid(m_pstruc, m_tx, m_ty, m_pgobOwner->GetOwner()); + + // Show / hide ok button? + + TerrainMap *ptmap = gsim.GetLevel()->GetTerrainMap(); + FogMap *pfogm = gsim.GetLevel()->GetFogMap(); + + for (int ty = m_ty; ty < m_ty + m_pstruc->ctyReserve; ty++) { + for (int tx = m_tx; tx < m_tx + m_pstruc->ctxReserve; tx++) { + if (fPlacementValid && IsTileFree(tx, ty, kbfReserved | kbfStructure) && pfogm->GetGalaxite(tx, ty) == 0) { + continue; + } else { + fValid = false; + break; + } + } + } + + + // Show ok button if valid + + pbtn = (ButtonControl *)GetControlPtr(kidcOk); + pbtn->Show(fValid || gfGodMode); +} + +bool PlaceStructureForm::FingerHitTest(Event *pevt) { + // Check to see if the input is over a control, while making sure + // the control doesn't steal input from the hash mark area. + + Rect rcFinger; + Rect rcT; + + // kidcOk is on the left + + Control *pctl = GetControlPtr(kidcOk); + pctl->GetFingerRect(&rcFinger); + pctl->GetRect(&rcT); + rcFinger.right = rcT.right; + if (rcFinger.PtIn(pevt->x - m_rc.left, pevt->y - m_rc.top)) { + return true; + } + + // kidcCancel is on the right + + pctl = GetControlPtr(kidcCancel); + pctl->GetFingerRect(&rcFinger); + pctl->GetRect(&rcT); + rcFinger.left = rcT.left; + if (rcFinger.PtIn(pevt->x - m_rc.left, pevt->y - m_rc.top)) { + return true; + } + + return false; +} + +bool PlaceStructureForm::OnPenEvent(Event *pevt) +{ + // If captured, do regular processing + if (m_pctlCapture != NULL) { + return Form::OnPenEvent(pevt); + } + + // If this is finger input, hit test the buttons specially. + // This is so the controls don't take input meant for the hash mark + // part of the form (the controls have extra wide finger hit test + // rects). + if (pevt->ff & kfEvtFinger) { + if (pevt->eType == penDownEvent && FingerHitTest(pevt)) { + if (Form::OnPenEvent(pevt)) { + return true; + } + } + } else { + // Give the form controls first crack at handling the event. Form + // controls include: soft menu button, mode cancel button, status + // label) + + if (Form::OnPenEvent(pevt)) { + return true; + } + } + + // Handle drag-scrolling mode (shift-drag or graffiti-scroll) + + if (m_fDragging) { + switch (pevt->eType) { + case penDownEvent: + // Something strange is going on, fall through to penUpEvent to + // clear dragging flag and complete in-progress drag operation. + + // ...FALL THROUGH... + + case penUpEvent: + m_fDragging = false; + + // ...FALL THROUGH... + + case penMoveEvent: + { + WCoord wxView, wyView; + gsim.GetViewPos(&wxView, &wyView); + + // remember where the pen is for scrolling on update + // do everything in world coordinates so we don't get confused + // when the view scrolls + + m_wxPen = WcFromPc(pevt->x) + wxView; + m_wyPen = WcFromPc(pevt->y) + wyView; + + // Pen may move into a graffiti area, but we'll go no farther + // than the playfield to keep the Placement Indicator at least + // partly on screen. This won't mess up scrolling because it is + // triggered by pen position, not by a pen move event. + + Size sizPlayfield; + ggame.GetPlayfieldSize(&sizPlayfield); + WCoord wcxPlayfield = WcFromUpc(sizPlayfield.cx); + WCoord wcyPlayfield = WcFromUpc(sizPlayfield.cy); + + if (m_wxPen > wxView + wcxPlayfield) + m_wxPen = wxView + wcxPlayfield; + if (m_wxPen < wxView) + m_wxPen = wxView; + if (m_wyPen > wyView + wcyPlayfield) + m_wyPen = wyView + wcyPlayfield; + if (m_wyPen < wyView) + m_wyPen = wyView; + + // move structure outline, but keep it within the map boundry + // no change in view + + UpdatePlacementIndicator(wxView, wyView, wxView, wyView); + } + break; + } + } else { + // do our regular thing + + if (pevt->eType == penDownEvent) { + UpdatePosition(pevt); + m_fDragging = true; + } + } + + return true; +} + +void PlaceStructureForm::UpdatePosition(Event *pevt) +{ + // If pen down in placement form, drag it without offsetting + // If pen down outside placement form, center placement for under pen + + WCoord wxView, wyView; + gsim.GetViewPos(&wxView, &wyView); + m_wxPen = WcFromPc(pevt->x) + wxView; + m_wyPen = WcFromPc(pevt->y) + wyView; + m_wxDragStart = m_wxPen; + m_wyDragStart = m_wyPen; + + TRect trcT; + trcT.Set(m_tx, m_ty, m_tx + m_pstruc->ctxReserve, + m_ty + m_pstruc->ctyReserve); + if (!trcT.PtIn(TcFromWc(m_wxPen), TcFromWc(m_wyPen))) { + // New position for the placement indicator. Center indicator underneath + // pen + + m_txStart = TcFromWc(m_wxPen) - (m_pstruc->ctxReserve - 1) / 2; + if (m_txStart < 0) + m_txStart = 0; + m_tyStart = TcFromWc(m_wyPen) - (m_pstruc->ctyReserve - 1) / 2; + if (m_tyStart < 0) + m_tyStart = 0; + } else { + // Start from current position + + m_txStart = m_tx; + m_tyStart = m_ty; + } +} + + +// UNDONE: we should probably create an update on the form that gets called +// after all the gobs have updated their state and use that for the placement +// indicator testing too. + +// A real-time (not simulation time) timer is started when the structure +// placement form is shown so we can scroll when the form is dragged +// off-screen. + +void PlaceStructureForm::OnTimer(long tCurrent) +{ + // Something may have moved under it. + // NOTE: this could be faster by remembering during paint + + // get fancy with scrolling. World Coordinates, please + // imitated from SimUI.cpp::Update + + WCoord wx, wy; + wx = m_wxPen; // where the pen last moved + wy = m_wyPen; + + WCoord wxView, wyView; + gsim.GetViewPos(&wxView, &wyView); + + Size sizPlayfield; + ggame.GetPlayfieldSize(&sizPlayfield); + WCoord wcxPlayfield = WcFromUpc(sizPlayfield.cx); + WCoord wcyPlayfield = WcFromUpc(sizPlayfield.cy); + + WCoord wxViewNew = wxView; + WCoord wyViewNew = wyView; + + // is the pen near an edge? if so, scroll + + if (m_fDragging) { + // Screen edge + + if (wx < wxView + kwcScrollBorderSize) { + wxViewNew -= kwcScrollStepSize; + } else if (wx > wxView + wcxPlayfield - kwcScrollBorderSize) { + wxViewNew += kwcScrollStepSize; + } + if (wy < wyView + kwcScrollBorderSize) { + wyViewNew -= kwcScrollStepSize; + } else if (wy > wyView + wcyPlayfield - kwcScrollBorderSize) { + wyViewNew += kwcScrollStepSize; + } + } + + // if we should scroll: update the view, the position of the pen + // and the structure outline by the amount we scrolled. + // setting the viewpos and re-getting it lets the sim + // deal with map edges for us + + if (wxViewNew != wxView || wyViewNew != wyView) + gsim.SetViewPos(wxViewNew, wyViewNew); + + WCoord wxViewActual, wyViewActual; + gsim.GetViewPos(&wxViewActual, &wyViewActual); + + m_wxPen += wxViewActual - wxView; + m_wyPen += wyViewActual - wyView; + + UpdatePlacementIndicator(wxView, wyView, wxViewActual, wyViewActual); + + // Cause a redraw to drag independently from the game rate + + gevm.SetRedrawFlags(kfRedrawDirty | kfRedrawBeforeTimer); +} + +void PlaceStructureForm::OnScroll(int dx, int dy) +{ + // we're scrolling - don't let the buttons get left behind + + ButtonControl *pbtn = (ButtonControl *)GetControlPtr(kidcCancel); + Rect rcCtl; + + // let it invalidate, as long as the PlacementTiles draw on every paint the + // controls will need to as well. + + pbtn->GetRect(&rcCtl); + rcCtl.Offset(dx,dy); + pbtn->SetRect(&rcCtl); + + pbtn = (ButtonControl *)GetControlPtr(kidcOk); + pbtn->GetRect(&rcCtl); + rcCtl.Offset(dx,dy); + pbtn->SetRect(&rcCtl); +} + +void PlaceStructureForm::OnPaintSimUI(DibBitmap *pbm) +{ + if (m_pgobOwner == NULL) + return; + + WCoord wxViewOrigin, wyViewOrigin; + gsim.GetViewPos(&wxViewOrigin, &wyViewOrigin); + Rect rcInside, rcOutside; + GetSubRects(wxViewOrigin, wyViewOrigin, &rcInside, &rcOutside); + + // If the outside is larger than the inside, draw leader lines from + // the inside to the edge of the controls, if they are visible. + // Ok is on the left. + + if (!rcOutside.Equal(&rcInside)){ + // Ok is on the left, Cancel is on the right + Control *pctl = GetControlPtr(kidcOk); + if (pctl->GetFlags() & kfCtlVisible) { + Rect rcT; + pctl->GetRect(&rcT); + int x = m_rc.left + rcT.right; + int y = m_rc.top + rcT.top + rcT.Height() / 2; + pbm->Fill(x, y, rcInside.left - x, 1, GetColor(kiclrWhite)); + } + + // Cancel is on the right + + pctl = GetControlPtr(kidcCancel); + if (pctl->GetFlags() & kfCtlVisible) { + Rect rcT; + pctl->GetRect(&rcT); + int x = rcInside.right; + int y = m_rc.top + rcT.top + rcT.Height() / 2; + pbm->Fill(x, y, m_rc.left + rcT.left - x, 1, GetColor(kiclrWhite)); + } + } + + bool fPlacementValid = ggobm.IsStructurePlacementValid(m_pstruc, m_tx, m_ty, m_pgobOwner->GetOwner()); + + // Draw placement tiles, properly colored + + TerrainMap *ptmap = gsim.GetLevel()->GetTerrainMap(); + FogMap *pfogm = gsim.GetLevel()->GetFogMap(); + + for (int ty = m_ty; ty < m_ty + m_pstruc->ctyReserve; ty++) { + for (int tx = m_tx; tx < m_tx + m_pstruc->ctxReserve; tx++) { + TBitmap *ptbm; + if (fPlacementValid && IsTileFree(tx, ty, kbfReserved | kbfStructure) && pfogm->GetGalaxite(tx, ty) == 0) { + ptbm = s_ptbmPlacementGood; + } else { + ptbm = s_ptbmPlacementBad; + } + ptbm->BltTo(pbm, (tx * gcxTile) - PcFromUwc(wxViewOrigin), (ty * gcyTile) - PcFromUwc(wyViewOrigin)); + } + } +} + +void PlaceStructureForm::OnPaintControlsSimUI(DibBitmap *pbm, UpdateMap *pupd) +{ + if (m_pgobOwner == NULL) + return; + Form::OnPaintControls(pbm, pupd); +} + +} // namespace wi diff --git a/game/Help.cpp b/game/Help.cpp new file mode 100644 index 0000000..c8b3ce8 --- /dev/null +++ b/game/Help.cpp @@ -0,0 +1,1117 @@ +#include "ht.h" + +namespace wi { + +// Supported tags: +//

-- heading on a new line, large font +//

-- heading on a new line, normal font +//
-- line break +//
-- horizontal rule +// link text -- a hyperlink +// -- a hyperlink target +// -- uses animation strip named "help" + + +// +// Help Control +// + +bool PaintChunk(int x, int y, int cx, int cy, int curIndex, int curY, int nyBottom, Chunk *pChunk, void *ptr) secHelpControl; +bool HitTestChunk(int x, int y, int cx, int cy, int curIndex, int curY, int nyBottom, Chunk *pChunk, void *ptr) secHelpControl; +bool FindPositionCallback(int x, int y, int cx, int cy, int curIndex, int curY, int nyBottom, Chunk *pChunk, void *ptr) secHelpControl; + +HelpControl::HelpControl() +{ + m_fTimerAdded = false; + m_fDrag = false; + m_fLargeFont = true; + m_nchCurrent = 0; + m_pfil = NULL; + memset(m_nchBack, 0, sizeof(m_nchBack)); + memset(&m_hittest, 0, sizeof(m_hittest)); +} + +HelpControl::~HelpControl() +{ + if (m_pfil != NULL) { + gpakr.fclose(m_pfil); + } + if (m_fTimerAdded) { + gtimm.RemoveTimer(this); + } +} + +bool HelpControl::Init(Form *pfrm, IniReader *pini, FindProp *pfind) +{ + // Base initialization + + if (!Control::Init(pfrm, pini, pfind)) + return false; + + m_cyPageAmount = (int)((long)m_rc.Height() * 85 / 100); + +#ifdef IPHONE + m_wf |= kfHelpScrollPosition; +#endif + + return true; +} + +bool HelpControl::SetFile(const char *pszFile) +{ + m_pfil = gpakr.fopen((char *)pszFile, "rb"); + if (m_pfil != NULL) { + gpakr.fseek(m_pfil, 0, SEEK_END); + m_cb = gpakr.ftell(m_pfil); + gpakr.fseek(m_pfil, 0, SEEK_SET); + } + return m_pfil != NULL; +} + +bool HelpControl::FollowLink(const char *str, int cch) +{ + m_fLargeFont = false; + + // Get file length + + gpakr.fseek(m_pfil, 0, SEEK_END); + dword cbFile = gpakr.ftell(m_pfil); + + // Calc the tag length we're searching for + + char szTag[128]; + strcpy(szTag, " tag + // i.e., a large font + + m_fLargeFont = true; + + for(int i = 8; i >= 0; i--) + m_nchBack[i + 1] = m_nchBack[i]; + m_nchBack[0] = m_nchCurrent; + + m_nchCurrent = nchFile + (pszFind - szBuffer); + Invalidate(); + return true; + } + // max link length is 30 + 9 for ", 4) == 0 || strncmp(m_szText, "

", 4) == 0) { + // Call back to know if we're stopping since we need to stop before the . + + if (pfn(xCurrent, yCurrent, 0, gapfnt[kifntDefault]->GetHeight(), nchBuffer, cyCurrent, yBottom, NULL, pv)) + return true; + + // Format to the next line if not already there + + if (xCurrent != rcT.left) { + cLines++; + yCurrent += gapfnt[kifntDefault]->GetHeight(); + xCurrent = rcT.left; + } + fLargeFont = (m_szText[2] == '1') ? true : false; + nchBuffer += 4; + continue; + } + + // BR means line break + + if (strncmp(m_szText, "
", 4) == 0) { + // Call back to know if layout is ending + + if (pfn(xCurrent, yCurrent, 0, gapfnt[kifntDefault]->GetHeight(), nchBuffer, cyCurrent, yBottom, NULL, pv)) + return true; + + // Format to the next line + + cLines++; + yCurrent += gapfnt[kifntDefault]->GetHeight(); + xCurrent = rcT.left; + nchBuffer += 4; + continue; + } + + // HR means horizontal rule + // a HR always has it's own line, which occupies gapfnt[kifntDefault]->GetHeight() of vertical space + + if (strncmp(m_szText, "
", 4) == 0) { + Chunk chnk; + chnk.fLargeFont = fLargeFont; + chnk.nType = knChunkHRTag; + chnk.pbm = pbm; + if(xCurrent != rcT.left) { + cLines++; + xCurrent = rcT.left; + yCurrent += gapfnt[kifntDefault]->GetHeight(); + cyCurrent = yCurrent - yTop; + } + + // Call back to know if layout is ending + + if (pfn(xCurrent, yCurrent, rcT.Width(), knHelpControlBRHeight, nchBuffer, cyCurrent, yBottom, &chnk, pv)) + return true; + + // Format to the next line + + cLines++; + yCurrent += knHelpControlBRHeight; + xCurrent = rcT.left; + nchBuffer += 4; + continue; + } + + //
link text + + if (strncmp(m_szText, "') + 1; + char *pchLinkEnd = strchr(&m_szText[10], '<'); + Assert(pchLinkStart != NULL); + Assert(pchLinkEnd != NULL); + Assert(pchLinkEnd - pchLinkStart < 100); + + // Word wrap the link text + + char *pchT = pchLinkStart; + while (pchT < pchLinkEnd) { + char *pchBreakNext = pchT; + bool fNewLine = true; + int cchChunk = gapfnt[kifntDefault]->CalcBreak(rcT.right - xCurrent, &pchBreakNext, xCurrent == xStart); + if (cchChunk > pchLinkEnd - pchT) { + cchChunk = pchLinkEnd - pchT; + pchBreakNext = pchLinkEnd; + fNewLine = false; + } + + // Handle this chunk + + Chunk chnk; + chnk.fLargeFont = fLargeFont; + chnk.psz = pchT; + chnk.nType = knChunkLinkText; + chnk.pbm = pbm; + chnk.cch = cchChunk; + strncpy(chnk.szText, &m_szText[10],pchLinkStart - &m_szText[10] - 2); + chnk.szText[pchLinkStart - &m_szText[10] - 2] = 0; + + int cx = gapfnt[kifntDefault]->GetTextExtent(chnk.psz, chnk.cch); + int cy = gapfnt[kifntDefault]->GetHeight(); + if (pfn(xCurrent, yCurrent, cx, cy, nchBuffer, cyCurrent, yBottom, &chnk, pv)) + return true; + + // Adjust x, y position + + xCurrent += cx; + if (fNewLine) { + cLines++; + xCurrent = rcT.left; + yCurrent += cy; + } + + // Go to next text chunk + + pchT = pchBreakNext; + } + + // Advance past the + + nchBuffer += pchLinkEnd - m_szText + 4; + continue; + } + + // + + if (strncmp(m_szText, "'); + if (pchT != NULL) + nchBuffer += (pchT - m_szText) + 1; + continue; + } + + // Image? + // + + if (strncmp(m_szText, "GetBounds(panid->GetStripIndex("help"), 0, &rc); + + // Images always start on a new line + + if (xCurrent != rcT.left) { + yCurrent += LineHeight(fLargeFont); + cLines++; + xCurrent = rcT.left; + } + + // Handle this chunk + + Chunk chnk; + chnk.nType = knChunkAniData; + chnk.pv = panid; + chnk.fLargeFont = fLargeFont; + chnk.pbm = pbm; + if (pfn(xCurrent, yCurrent, rc.Width(), rc.Height(), nchBuffer, cyCurrent, yBottom, &chnk, pv)) { + delete panid; + return true; + } + + // Always go to the next line + + yCurrent += rc.Height(); + nchBuffer += pchFilenameEnd - m_szText + 2; + + delete panid; + } + + // skip over any '; i++); + + nchBuffer += i + 1; + } + } + return true; +} + +int HelpControl::LineHeight(bool fLargeFont) +{ + return gapfnt[fLargeFont ? kifntTitle : kifntDefault]->GetHeight(); +} + +void HelpControl::DoNextPage() +{ + //take care of updating the back array + for(int i = 8; i >= 0; i--) + m_nchBack[i + 1] = m_nchBack[i]; + m_nchBack[0] = m_nchCurrent; + + m_nchCurrent = FindNextPosition(m_nchCurrent, m_cyPageAmount, &m_fLargeFont, false, false); + Invalidate(); +} + +void HelpControl::DoPrevPage() +{ + //take care of updating the back array + for(int i = 8; i >= 0; i--) + m_nchBack[i + 1] = m_nchBack[i]; + m_nchBack[0] = m_nchCurrent; + + m_nchCurrent = FindPrevPosition(m_nchCurrent, m_cyPageAmount, &m_fLargeFont); + Invalidate(); +} + +int HelpControl::FindPrevPosition(int nchFrom, int cyAmount, bool *pfLargeFont) +{ + // If no room to scroll back, we're done + + if (nchFrom == 0) + return 0; + + // check if there is an
either directly above the current location + // or there is a link target above the location with an
above that + + char sz[64]; + gpakr.fseek(m_pfil, nchFrom - (sizeof(sz) - 1), SEEK_SET); + int cchRead = gpakr.fread(sz, 1, sizeof(sz) - 1, m_pfil); + sz[cchRead] = 0; + + // if there is a tag directly above us figure out if it is a HR or a link + // and move nchFrom to directly before those tags. Since we're finding the + // previous heading if the HR would have fit on the page anyways it will be + // displayed, but if it wouldn't have fit then we don't want to display it. + + if (sz[cchRead - 1] == '>' || sz[cchRead - 3] == '>') { int i = cchRead - +1; while(sz[i] != '<') i--; + // we found the link for the heading and need to check for an HR before + // it + if(sz[i + 1] == 'A') for(i--; sz[i] != '<'; i--); + + // we want to ignore the HR at the bottom of the screen in case the + // section above takes up the entire screen. + + if(strncmp(&sz[i], "
", 4) == 0) + nchFrom -= cchRead - i; + } + + // Search back for a header since we know that'll format starting on a left + // edge. Then layout forward until we've formatted more space than the + // scroll back amount. Then layout forward the difference between the + // heading we found and the desired scroll back amount. + + int nchSeek = nchFrom; + FindPositionHelper fph; + fph.nIndex = 0; + fph.nDistY = 0; + while (nchSeek != 0) { + // Find the position to read forward from + + char szT[256]; + szT[255] = 0; + nchSeek -= sizeof(szT) - 1; + if (nchSeek < 0) + nchSeek = 0; + + // Read in this piece + + gpakr.fseek(m_pfil, nchSeek, SEEK_SET); + int cchRead = gpakr.fread(szT, 1, sizeof(szT) - 1, m_pfil); + szT[cchRead] = 0; + + // Find prev heading

in buffer that when formatted forward from + // gives us a vertical size greater than the desired page amount + + bool fDone = false; + for (int i = cchRead - 1; i >= 0; i--) { + if (strncmp(&szT[i], "

", 4) == 0) { + fph.cySpan = 0; + fph.nCondition = knFindPosRunToIndex; + fph.nIndex = nchFrom; + + Layout(nchSeek + i, false, NULL, FindPositionCallback, &fph); + + fph.nIndex = nchSeek + i; +// CRM temp: alternate means of paging starts here + if (fph.cySpan <= m_rc.Height()) + return fph.nIndex; + else + return FindNextPosition(fph.nIndex, fph.cySpan - cyAmount, pfLargeFont, true, false); + } + } + } + + return 0; +// temp: ends here +/* CRM Removed to try alternate means of paging + if (fph.cySpan >= cyAmount) { + *pfLargeFont = fph.fLargeFont; + fDone = true; + break; + } + + } + } + if (fDone) + break; + } + if (fph.cySpan - cyAmount > 0) + return FindNextPosition(fph.nIndex, fph.cySpan - cyAmount, pfLargeFont, true, false); + + *pfLargeFont = fph.fLargeFont; + return fph.nIndex; +*/ + return 0; +} + +// fCondition is used to indicate whether we are to travel at most cyAmount or at least +// cyAmount + +int HelpControl::FindNextPosition(int nchFrom, int cyAmount, bool *pfLargeFont, bool fCondition, bool fSmooth) +{ + FindPositionHelper fph; + memset(&fph, 0, sizeof(fph)); + fph.nIndex = nchFrom; + fph.nDistY = cyAmount; + fph.cySpan = cyAmount; + fph.cyControl = m_rc.Height(); +#ifdef IPHONE + fph.nCondition = knFindPosFingerScroll; +#else + fph.nCondition = fCondition ? knFindPosAtLeastY : knFindPosAtMostY; +#endif + fph.nchLastHR = -1; + fph.nchFirstHR = -1; + fph.nchSpanMet = -1; + + // advance fph.nDistY + + Layout(nchFrom, *pfLargeFont, NULL, FindPositionCallback, &fph); + + // if we found a HR that isn't directly above the screen we are paging up + // from account for both "\cr\lf
" and "
\cr\lf + + if (!fSmooth && fph.nchLastHR != -1 && fph.nchFirstHR + 4 != m_nchCurrent + && fph.nchFirstHR + 6 != m_nchCurrent) { + + // decide which HR rule to use. If we are paging up then we use the + // first and if we are paging down we use the last + +/* CRM temp: trying the heading at a time thing + int nchIndexOfHR = fph.nchLastHR; +*/ +// trying out the one heading at a time method + int nchIndexOfHR = fph.nchFirstHR; +// end + bool fLargeFontT = fph.fLargeFontLastHR; + if (fph.nCondition == knFindPosAtLeastY) { + nchIndexOfHR = fph.nchFirstHR; + fLargeFontT = fph.fLargeFontFirstHR; + } + + // find
+ + char szBuffer[20]; + gpakr.fseek(m_pfil, nchIndexOfHR, SEEK_SET); + int cchRead = gpakr.fread(szBuffer, 1, sizeof(szBuffer) - 1, m_pfil); + szBuffer[cchRead] = 0; + char *pszFind = strstr(szBuffer, "
"); + + *pfLargeFont = fLargeFontT; + return nchIndexOfHR + (pszFind + 4 - szBuffer); + } else { + // otherwise we should use the position at which the span was met + + if (fph.nchSpanMet != -1) { + *pfLargeFont = fph.fLargeFontSpanMet; + return fph.nchSpanMet; + } else { + return nchFrom; + } + } + + // Should get to here because the span should always be met. + + Assert(false); + return nchFrom; +} + +void HelpControl::DoIndex() +{ + m_nchCurrent = 0; + m_fLargeFont = false; + Invalidate(); +} + +void HelpControl::DoBack() +{ + m_nchCurrent = m_nchBack[0]; + m_fLargeFont = false; + + for(int i = 0; i < 9; i++) + m_nchBack[i] = m_nchBack[i + 1]; + Invalidate(); +} + +bool PaintChunk(int x, int y, int cx, int cy, int nchBuffer, int cyTotal, int nyBottom, Chunk *pChunk, void *pv = NULL) +{ + PaintInfo *pinfo = (PaintInfo *)pv; + int *pnchEnd = pinfo->pnchEnd; + if (y < nyBottom) { + *pnchEnd = nchBuffer; + } + + if (y + cy >= nyBottom) + return true; + + if (pChunk != NULL) { + switch (pChunk->nType) { + case knChunkRawText: + gapfnt[kifntDefault]->DrawText(pChunk->pbm, pChunk->psz, x, y, pChunk->cch); + break; + + case knChunkLargeText: + gapfnt[kifntTitle]->DrawText(pChunk->pbm, pChunk->psz, x, y, pChunk->cch); + break; + + case knChunkBitmap: + ( (TBitmap *)pChunk->pv)->BltTo(pChunk->pbm, x, y); + break; + + case knChunkAniData: + { + int index = ( (AnimationData *)pChunk->pv)->GetStripIndex("help"); + + Rect rc; + ( (AnimationData *)pChunk->pv)->GetBounds(index, 0, &rc); + + if (rc.Height() + y > nyBottom) + return true; + ( (AnimationData *)pChunk->pv)->DrawFrame(index, 0, pChunk->pbm, x - rc.left, y - rc.top, kside1); + } + break; + + case knChunkLinkText: + { + // +10 is a hack since nchBuffer points to the anchor tag + if (pinfo->phittest->fHit && + (pinfo->phittest->nchBuffer == nchBuffer || + (pinfo->phittest->nchBuffer < nchBuffer && + pinfo->phittest->nchBuffer + 10 >= nchBuffer))) { + int cx = gapfnt[kifntDefault]->GetTextExtent(pChunk->psz, + pChunk->cch); + int cy = gapfnt[kifntDefault]->GetHeight(); + pChunk->pbm->Fill(x, y - 1, cx, cy + 2, + GetColor(kiclrButtonFillHighlight)); + } + + Color clr = 6; + gapfnt[kifntDefault]->DrawText(pChunk->pbm, pChunk->psz, x, y, pChunk->cch); + + // BUG: fix for descender height + // HACK: this will not work properly if the help control doesn't span below half the + // screen. + if (pChunk->cch != 0) { + int descenderAdjust = (nyBottom <= 160? 1: 2); + y += gapfnt[kifntDefault]->GetHeight() - descenderAdjust; + pChunk->pbm->DrawLine(x, y, x + cx, y, clr); + } + } + break; + + case knChunkHRTag: + { + // draw three rectangles + // draw center rectangle + + int cxDot = PcFromFc(2); + pChunk->pbm->Fill(x + cx * 3 / 8 - cxDot / 2, y + cy / 2 - cxDot / 2, cxDot, cxDot, kiclrWhite); + + // draw left rectangle + + pChunk->pbm->Fill(x + cx * 4 / 8 - cxDot / 2, y + cy / 2 - cxDot / 2, cxDot, cxDot, kiclrWhite); + + // draw right rectangle + + pChunk->pbm->Fill(x + cx * 5 / 8 - cxDot / 2, y + cy / 2 - cxDot / 2, cxDot, cxDot, kiclrWhite); + } + break; + } + + } + return false; +} + +bool HitTestChunk(int x, int y, int cx, int cy, int nchBuffer, int cyTotal, int nyBottom, Chunk *pChunk, void *pv) +{ + if (y + cy >= nyBottom) + return true; + + Rect rc; + if (pChunk != NULL) { + switch (pChunk->nType) { + case knChunkLinkText: // we only care to hittest text + HitTest *pHitTest = (HitTest *)pv; + rc.Set(x, y, x + cx, y + cy); + int d = rc.GetDistance(pHitTest->x, pHitTest->y); + if (d < pHitTest->dBest) { + pHitTest->dBest = d; + pHitTest->nchBuffer = nchBuffer; + strcpy(pHitTest->szText, pChunk->szText); + pHitTest->cch = strlen(pHitTest->szText); + } + break; + } + } + return false; +} + +bool FindPositionCallback(int x, int y, int cx, int cy, int nchBuffer, int cyTotal, int nyBottom, Chunk *pChunk, void *pv) +{ + FindPositionHelper *pFPH = (FindPositionHelper*)pv; + +#if 0 + if (pChunk != NULL && (pChunk->nType == knChunkRawText || + pChunk->nType == knChunkLargeText)) { + pFPH->fLargeFont = (pChunk->nType == knChunkRawText) ? false : true; + } +#else + if (pChunk != NULL) { + if (pChunk->nType == knChunkLargeText) { + pFPH->fLargeFont = true; + } else { + pFPH->fLargeFont = false; + } + } +#endif + + // we're counting the vertical (Y) distance traveled from a given point to + // pFPH->nIndex + + if (pFPH->nCondition == knFindPosRunToIndex) { + if(nchBuffer >= pFPH->nIndex) { + pFPH->cySpan = cyTotal; + return true; + } + return false; + } + + // keep track of the indexes where we found the first and last HR tags as + // well as what the font was at that HR tag. We do this check after the + // PosRunToIndex check because we may need to break prior to this point. + + if (pChunk != NULL && pChunk->nType == knChunkHRTag && cyTotal < pFPH->cyControl) { + if (pFPH->nchFirstHR == -1) { + pFPH->nchFirstHR = nchBuffer; + pFPH->fLargeFontFirstHR = pChunk->fLargeFont; + } + pFPH->nchLastHR = nchBuffer; + pFPH->fLargeFontLastHR = pChunk->fLargeFont; + } + + // in these cases we travel until we've traveled the height of the control + // so that we can be sure to find any HR tags we should break at within the + // page. + + switch (pFPH->nCondition) { + case knFindPosAtMostY: + // Used when paging forward. Remember index at cySpan (7/8ths + // currently). + + if (cy + cyTotal >= pFPH->cySpan && pFPH->nchSpanMet == -1) { + pFPH->nchSpanMet = nchBuffer; + pFPH->fLargeFontSpanMet = pFPH->fLargeFont; + } + + // check to see if we've traveled the height of the control + + if (cyTotal > pFPH->cyControl) { + pFPH->nIndex = nchBuffer; + return true; + } + break; + + case knFindPosAtLeastY: + // Used when implementing page back by doing a forward Layout by + // a specific delta. + + if (cyTotal >= pFPH->cySpan && pFPH->nchSpanMet == -1) { + pFPH->nchSpanMet = nchBuffer; + pFPH->fLargeFontSpanMet = pFPH->fLargeFont; + } + + // check to see if we've traveled the height of the control + + if (cyTotal > pFPH->cyControl && pFPH->nchSpanMet != -1) + return true; + break; + + case knFindPosFingerScroll: + pFPH->nchSpanMet = nchBuffer; + pFPH->fLargeFontSpanMet = pFPH->fLargeFont; + return cyTotal >= pFPH->cySpan; + } + return false; +} + +// +// Help implementation +// + +class HelpForm : public ShellForm +{ +public: + HelpForm() secHelpForm; + virtual bool DoModal(const char *pszLink, const char *pszFile) secHelpForm; + virtual void OnControlSelected(word idc) secHelpForm; + virtual bool EventProc(Event *pevt) secHelpForm; +}; + +void Help(const char *pszAnchor, bool fPauseSimulation, const char *pszFile) +{ + word idf; + Size siz; + gpmfrmm->GetDib()->GetSize(&siz); + if (siz.cx >= 480) { + idf = kidfHelpWide; + } else { + idf = kidfHelp; + } + + HelpForm *pfrm = (HelpForm *)gpmfrmm->LoadForm(gpiniForms, idf, new HelpForm()); + if (pfrm != NULL) { + if (fPauseSimulation) + gsim.Pause(true); + pfrm->DoModal(pszAnchor, pszFile); + if (fPauseSimulation) + gsim.Pause(false); + delete pfrm; + } +} + +HelpForm::HelpForm() +{ +} + +bool HelpForm::DoModal(const char *pszLink, const char *pszFile) +{ + HelpControl *pctl = (HelpControl *)GetControlPtr(kidcHelp); + pctl->SetFile(pszFile == NULL ? "help.txt" : pszFile); + if (pszLink != NULL) + pctl->FollowLink(pszLink); + +#ifdef IPHONE + Control *pctlT = GetControlPtr(kidcNextPage); + pctlT->Show(false); + pctlT = GetControlPtr(kidcPrevPage); + pctlT->Show(false); +#endif + + int idc; + ShellForm::DoModal(&idc, false); + if (idc == kidcCancel) + return false; + return true; +} + +void HelpForm::OnControlSelected(word idc) +{ + HelpControl *pctl = (HelpControl *)GetControlPtr(kidcHelp); + + switch (idc) { + case kidcPrevPage: + pctl->DoPrevPage(); + break; + + case kidcNextPage: + pctl->DoNextPage(); + break; + + case kidcBack: + pctl->DoBack(); + break; + + case kidcIndex: + pctl->DoIndex(); + break; + + case kidcOk: + EndForm(idc); + break; + } +} + +bool HelpForm::EventProc(Event *pevt) +{ + switch (pevt->eType) { + case keyDownEvent: + switch (pevt->chr) { + case chrUp: + OnControlSelected(kidcPrevPage); + return true; + + case chrDown: + OnControlSelected(kidcNextPage); + return true; + } + default: + return Form::EventProc(pevt); + } + return false; +} + +} // namespace wi diff --git a/game/InputUI.cpp b/game/InputUI.cpp new file mode 100644 index 0000000..3610a8a --- /dev/null +++ b/game/InputUI.cpp @@ -0,0 +1,736 @@ +#include "game/ht.h" +#include "game/chatter.h" +#include "mpshared/netmessage.h" + +namespace wi { + +extern bool gfSuspendUpdates; +extern bool gfSingleStep; + +// +// InputUIForm implementation +// + +InputUIForm::InputUIForm(Chatter *chatter) +{ + m_fTimerAdded = false; + m_pchatter = chatter; +} + +InputUIForm::~InputUIForm() +{ + if (m_fTimerAdded) + gtimm.RemoveTimer(this); +} + +bool InputUIForm::Init(FormMgr *pfrmm, IniReader *pini, word idf) +{ + if (!Form::Init(pfrmm, pini, idf)) + return false; + + // Set the form rect + + DibBitmap *pbm = m_pfrmm->GetDib(); + Size sizDib; + pbm->GetSize(&sizDib); + int cyCommandBar = sizDib.cy; + ModeInfo mode; + gpdisp->GetMode(&mode); + int cyInputForm = cyCommandBar + mode.cyGraffiti; + Rect rcT; + rcT.Set(0, 0, sizDib.cx, cyInputForm); + SetRect(&rcT); + + // Get pointers to and dimensions of all the controls involved in the layout + + Control *pctlMenuButton = GetControlPtr(kidcMenuButton); + Assert(pctlMenuButton != NULL); + Rect rcMenuButton; + pctlMenuButton->GetRect(&rcMenuButton); + + Control *pctlCredits = GetControlPtr(kidcCreditsLabel); + Assert(pctlCredits != NULL); + Rect rcCredits; + pctlCredits->GetRect(&rcCredits); + + Control *pctlPower = GetControlPtr(kidcPower); + Assert(pctlPower != NULL); + Rect rcPower; + pctlPower->GetRect(&rcPower); + + // Calc space between controls + + int xLeftMiniMap = m_rc.right - MiniMapControl::CalcWidth(); + int cxSpace = (xLeftMiniMap - (rcMenuButton.Width() + rcCredits.Width() + rcPower.Width())) / 3; + if (cxSpace < 0) + cxSpace = 0; + + // Position controls (ycoord of 1 to not overlap the separating line, except for menu button) + + pctlMenuButton->SetPosition(0, 0); + pctlCredits->SetPosition(rcMenuButton.right + cxSpace, 1); + pctlPower->SetPosition(rcMenuButton.right + cxSpace + rcCredits.Width() + cxSpace, 1); + + // Handle silkscreen area + + static int s_aidcLayout[] = { + kidcGraffitiScroll, kidcAppsSilkButton, kidcMenuSilkButton, + kidcCalcSilkButton, kidcFindSilkButton + }; + static int s_aircGraffiti[] = { + kircSilkGraffiti, kircSilkApps, kircSilkMenu, + kircSilkCalc, kircSilkFind + }; + + if (mode.cyGraffiti == 0) { + // No silkscreen graffiti area, so hide the controls + + for (int n = 0; n < ARRAYSIZE(s_aidcLayout); n++) + GetControlPtr(s_aidcLayout[n])->Show(false); + } else { + // Note the returned graffiti rects are in native coordinates + // from the top of the screen, whereas the input form starts on-screen, + // so the rects need to be adjusted + + int yOffset = mode.cy - cyCommandBar; + for (int n = 0; n < ARRAYSIZE(s_aidcLayout); n++) { + Rect rcT; + HostGetSilkRect(s_aircGraffiti[n], &rcT); + rcT.Offset(0, -yOffset); + GetControlPtr(s_aidcLayout[n])->SetRect(&rcT); + } + } + + gtimm.AddTimer(this, kctMapScrollRate); + m_fTimerAdded = true; + + return true; +} + +void InputUIForm::Update() +{ + // Update the player credits display + + CreditsControl *pctlCredits = (CreditsControl *)GetControlPtr(kidcCreditsLabel); + pctlCredits->Update(gpplrLocal->GetCredits(), gpplrLocal->GetNeedCreditsCount()); + + // Update the power indicator + + PowerControl *pctlPower = (PowerControl *)GetControlPtr(kidcPower); + pctlPower->Update(gpplrLocal->GetPowerDemand(), gpplrLocal->GetPowerSupply()); +} + +void InputUIForm::OnTimer(long tCurrent) +{ + if (gpmfrmm->GetFocus() == this) { + dword dwKeys = HostGetCurrentKeyState(keyBitDpadLeft | keyBitDpadRight | keyBitPageUp | keyBitPageDown | keyBitHard1 | keyBitHard2 | keyBitHard3 | keyBitHard4 | keyBitRockerUp | keyBitRockerDown | keyBitRockerLeft | keyBitRockerRight); + if (dwKeys != 0) { + WCoord wxView, wyView; + gsim.GetViewPos(&wxView, &wyView); + WCoord wcStep = kwcTile; + + if (dwKeys & (keyBitPageUp | keyBitRockerUp)) + wyView -= wcStep; + if (dwKeys & (keyBitPageDown | keyBitRockerDown)) + wyView += wcStep; + if (dwKeys & (keyBitDpadLeft | keyBitRockerLeft | keyBitHard1 | keyBitHard2)) + wxView -= wcStep; + if (dwKeys & (keyBitDpadRight | keyBitRockerRight | keyBitHard3 | keyBitHard4)) + wxView += wcStep; + + gsim.SetViewPos(wxView, wyView); + } + } + + + ggame.GetSimUIForm()->GetPenHandler()->CheckScroll(); + +} + +void InputUIForm::OnPaintBackground(DibBitmap *pbm, UpdateMap *pupd) +{ + // Fill background + + Rect rcT; + gpmm->GetRect(&rcT); + int xLeftMiniMap = m_rc.right - rcT.Width(); + rcT.Set(0, 0, xLeftMiniMap, m_rc.bottom); + FillHelper(pbm, pupd, &rcT, GetColor(kiclrBlack)); + + // Draw separating line + + rcT.bottom = rcT.top + 1; + + int iclr = gfMultiplayer ? gaiclrSide[gpplrLocal->GetSide()] : kiclr0CyanSide; + FillHelper(pbm, pupd, &rcT, GetColor(iclr)); +} + +bool InputUIForm::EventProc(Event *pevt) +{ + if (pevt->eType == keyDownEvent) { + switch (pevt->chr) { + case vchrMenu: + OnControlSelected(kidcMenuButton); + break; + +#ifdef DEBUG + case ' ': + if (gfSuspendUpdates) + gfSingleStep = true; + break; + + case 'z': + { + static int s_cMark; + Trace(""); + Trace("+++ MARK %d +++", s_cMark++); + Trace(""); + } + break; + + case 'r': + { + // Restart mission + + Event evt; + memset(&evt, 0, sizeof(evt)); + evt.eType = gameOverEvent; + evt.dw = knGoRetryLevel; + gevm.PostEvent(&evt); + } + break; +#endif + } + } else { + return Form::EventProc(pevt); + } + + return false; +} + +void InputUIForm::OnControlSelected(word idc) +{ + switch (idc) { + case kidcMenuButton: + InGameMenu(); + break; + +#if 0 + case kidcMapButton: + // To be replaced with control at some point + ggame.GetSimUIForm()->ToggleMiniMap(); + break; +#endif + + case kidcAppsSilkButton: + { + Event evt; + evt.eType = appStopEvent; + gevm.PostEvent(&evt); + } + break; + + case kidcMenuSilkButton: + OnControlSelected(kidcMenuButton); + break; + +#if 0 + case kidcCalcSilkButton: + OnControlSelected(kidcMapButton); + break; +#endif + +#if defined(DEV_BUILD) + case kidcFindSilkButton: + case kidcPower: + TestOptions(); + break; +#endif + + case kidcCreditsLabel: + if (!gfMultiplayer) + gsim.Pause(true); + gpplrLocal->ShowObjectives(ksoObjectives); + if (!gfMultiplayer) + gsim.Pause(false); + break; + } +} + +bool InputUIForm::OnControlHeld(word idc) +{ + return true; + +#if 0 + // Check for preset buttons as a group + + int iPreset = idc - kidcPreset1Button; + if (iPreset < 0 || iPreset > 7) + return false; + + if (m_aagidPreset[iPreset] != NULL) { + delete m_aagidPreset[iPreset]; + m_aagidPreset[iPreset] = NULL; + } + + m_awptViewPreset[iPreset].wx = kwxInvalid; + + // Collect all selected Gobs + + int cgob = 0; + Gid *pgid = (Gid *)gpbScratch; + for (Gob *pgobT = ggobm.GetFirstGob(); pgobT != NULL; pgobT = ggobm.GetNextGob(pgobT)) { + if (pgobT->GetFlags() & kfGobSelected) { + *pgid++ = pgobT->GetId(); + cgob++; + } + } + + if (cgob == 0) { + gsim.GetViewPos(&m_awptViewPreset[iPreset].wx, &m_awptViewPreset[iPreset].wy); + + // UNDONE: Play 'preset set' sound + Control *pctl = GetControlPtr(kidcPreset1Button + iPreset); + pctl->SetFlags(pctl->GetFlags() | kfCtlSet); + } else { + Gid *agid = new Gid[cgob]; + if (agid != NULL) { + memcpy(agid, gpbScratch, cgob * sizeof(Gid)); + m_aagidPreset[iPreset] = agid; + m_acgidPreset[iPreset] = cgob; + + // UNDONE: Play 'preset set' sound + Control *pctl = GetControlPtr(kidcPreset1Button + iPreset); + pctl->SetFlags(pctl->GetFlags() | kfCtlSet); + } + } + + return false; +#endif +} + +void InputUIForm::InGameMenu() +{ + // Invoke modal InGameMenu form + + ShellForm *pfrm = (ShellForm *)gpmfrmm->LoadForm(gpiniForms, kidfInGameMenu, new ShellForm()); + +#ifdef IPHONE + pfrm->GetControlPtr(kidcExitGame)->Show(false); +#endif + + if (gfMultiplayer) { + pfrm->GetControlPtr(kidcSaveGame)->Show(false); + pfrm->GetControlPtr(kidcLoadGame)->Show(false); + pfrm->GetControlPtr(kidcRestartMission)->Show(false); + pfrm->GetControlPtr(kidcHelp)->Show(false); + + // Hack + + ButtonControl *pbtn = (ButtonControl *)pfrm->GetControlPtr(kidcAbortMission); + pbtn->SetText((gpplrLocal->GetFlags() & kfPlrObserver) ? (char *)"STOP OBSERVING" : (char *)"RESIGN"); + + word aidcFormat[] = { kidcObjectives, kidcSaveGame, kidcLoadGame, kidcRestartMission, kidcAbortMission, kidcOptions, kidcChat, kidcExitGame, kidcHelp }; + FormatButtons(pfrm, aidcFormat, ARRAYSIZE(aidcFormat), kidcObjectives, kidcOptions); + + // Hiding this here ensures kidcChat is spaced below the rest of the + // buttons. + pfrm->GetControlPtr(kidcOptions)->Show(false); + } else { + pfrm->GetControlPtr(kidcChat)->Show(false); + } + + if (!gfMultiplayer) + gsim.Pause(true); + int idc; + pfrm->DoModal(&idc); + delete pfrm; + + // While the dialog was up the player might have exited the app + + if (!gevm.IsAppStopping()) { + // UNDONE: display mode change + + switch (idc) { + case kidcSaveGame: + ggame.SaveGame(); + break; + + case kidcLoadGame: + { + Stream *pstm = PickLoadGameStream(); + if (pstm == NULL) + break; + + Event evt; + memset(&evt, 0, sizeof(evt)); + evt.eType = gameOverEvent; + evt.dw = knGoLoadSavedGame; + gpstmSavedGame = pstm; + gevm.PostEvent(&evt); + } + break; + + case kidcAbortMission: + case kidcRestartMission: + { + if (gfMultiplayer) { + if (!ggame.AskResignGame()) + break; + } + + if ((gpplrLocal->GetFlags() & kfPlrObserver) == 0) { + // HACK: only ksoWinSummary has the fields necessary to + // support the fAborting flag + gpplrLocal->ShowObjectives( + gfMultiplayer ? ksoLoseSummary : ksoWinSummary, + false, gfMultiplayer ? false : true); + } + + if (!gfMultiplayer + || (gpplrLocal->GetFlags() & kfPlrObserver) != 0 + || !ggame.AskObserveGame()) { + Event evt; + memset(&evt, 0, sizeof(evt)); + evt.eType = gameOverEvent; + evt.dw = (idc == kidcAbortMission) + ? knGoAbortLevel : knGoRetryLevel; + gevm.PostEvent(&evt); + } + } + break; + + case kidcOptions: + DoModalGameOptionsForm(gsim.GetLevel()->GetPalette(), true); + break; + + case kidcHelp: + Help(); + break; + + case kidcExitGame: + { + if (gfMultiplayer) { + if (!ggame.AskResignGame()) + break; + } + + Event evt; + memset(&evt, 0, sizeof(evt)); + evt.eType = gameOverEvent; + evt.dw = knGoAppStop; + gevm.PostEvent(&evt); + + ggame.SaveReinitializeGame(); + } + break; + + case kidcObjectives: + gpplrLocal->ShowObjectives(ksoObjectives); + break; + + case kidcChat: + if (m_pchatter != NULL) { + m_pchatter->ShowChat(); + } + break; + } + } + + if (!gfMultiplayer) + gsim.Pause(false); +} + +void InputUIForm::TestOptions() +{ + // Invoke modal test options form + + Form *pfrm = gpmfrmm->LoadForm(gpiniForms, kidfTestOptions, new TestOptionsForm()); + if (!gfMultiplayer) + gsim.Pause(true); + pfrm->DoModal(); + if (!gfMultiplayer) + gsim.Pause(false); + delete pfrm; +} + +// +// Graffiti scroll control +// + +GraffitiScrollControl::GraffitiScrollControl() +{ + m_nScale = 0; + m_xDragStart = 0; + m_yDragStart = 0; + m_wxViewStart = 0; + m_wyViewStart = 0; + m_fFrame = false; +} + +bool GraffitiScrollControl::Init(Form *pfrm, IniReader *pini, FindProp *pfind) +{ + // Base initialization + + if (!Control::Init(pfrm, pini, pfind)) + return false; + + // idc (x y cx cy) nScale + + char sz[32]; + int cArgs = pini->GetPropertyValue(pfind, "%*d (%*d %*d %*d %*d) %d %s", &m_nScale, sz); + if (cArgs == 1) { + m_fFrame = false; + return true; + } + if (cArgs == 2 && strcmp(sz, "frame") == 0) { + m_fFrame = true; + return true; + } + return true; +} + +void GraffitiScrollControl::OnPenEvent(Event *pevt) +{ + switch (pevt->eType) { + case penDownEvent: + m_xDragStart = pevt->x; + m_yDragStart = pevt->y; + gsim.GetViewPos(&m_wxViewStart, &m_wyViewStart); + break; + + case penMoveEvent: +#if 0 + // BUGBUG: overflowed WcFromPc on Mission 3 ((pevt->y - m_yDragStart) * m_nScale == -1548) + gsim.SetViewPos(m_wxViewStart - WcFromPc((pevt->x - m_xDragStart) * m_nScale), + m_wyViewStart - WcFromPc((pevt->y - m_yDragStart) * m_nScale)); +#else + { + int pcMac = PcFromWc(kwcMax - 1); + int dxT = (pevt->x - m_xDragStart) * m_nScale; + if (dxT < -pcMac) + dxT = -pcMac; + if (dxT > pcMac) + dxT = pcMac; + int dyT = (pevt->y - m_yDragStart) * m_nScale; + if (dyT < -pcMac) + dyT = -pcMac; + if (dyT > pcMac) + dyT = pcMac; + gsim.SetViewPos(m_wxViewStart + WcFromPc(dxT), m_wyViewStart + WcFromPc(dyT)); + } +#endif + break; + } +} + +void GraffitiScrollControl::OnPaint(DibBitmap *pbm) +{ + if (!m_fFrame) + return; + + Rect rcForm; + m_pfrm->GetRect(&rcForm); + Rect rcT = m_rc; + rcT.Offset(rcForm.left, rcForm.top); + + int iclr = GetColor(kiclrWhite); + pbm->Fill(rcT.left + 1, rcT.top, rcT.Width() - 2, 1, iclr); + pbm->Fill(rcT.left, rcT.top + 1, 1, rcT.Height() - 2, iclr); + pbm->Fill(rcT.right - 1, rcT.top + 1, 1, rcT.Height() - 2, iclr); + pbm->Fill(rcT.left + 1, rcT.bottom - 1, rcT.Width() - 2, 1, iclr); +} + +bool GraffitiScrollControl::IsPainting() +{ + return m_fFrame; +} + +// +// Credits control +// + +bool CreditsControl::Init(Form *pfrm, IniReader *pini, FindProp *pfind) +{ + // Base initialization + + if (!Control::Init(pfrm, pini, pfind)) + return false; + + m_nCredits = 0; + Font *pfnt = gapfnt[kifntHud]; + m_rc.right = m_rc.left + pfnt->GetTextExtent("ABC"); // the credits background image + m_rc.bottom = m_rc.top + pfnt->GetHeight(); + m_fDrawCreditSymbol = true; + return true; +} + +void CreditsControl::OnPaint(DibBitmap *pbm) +{ + // Draw background + + int cx = m_rc.Width(); + Font *pfnt = gapfnt[kifntHud]; + if (m_fDrawCreditSymbol) + pfnt->DrawText(pbm, "ABC", m_rc.left, m_rc.top, cx, -1); + else + pfnt->DrawText(pbm, "JBC", m_rc.left, m_rc.top, cx, -1); + + // Draw credits + + char szT[20]; + sprintf(szT, "%ld", m_nCredits > 99999 ? 99999 : m_nCredits); + pfnt->DrawText(pbm, szT, m_rc.left + pfnt->GetTextExtent("AB") - pfnt->GetTextExtent(szT), m_rc.top, cx, -1); +} + +void CreditsControl::Update(long nCredits, word cCreditNeeders) +{ + if ((nCredits != m_nCredits) || (cCreditNeeders != m_cCreditNeeders)) { + m_nCredits = nCredits; + m_cCreditNeeders = cCreditNeeders; + + if (cCreditNeeders == 0) { + m_fDrawCreditSymbol = true; + } + Invalidate(); + } + + if (cCreditNeeders > 0) { + long cUpdates = gsim.GetUpdateCount(); + long cupdFlash = cUpdates % kcupdSymbolFlashRate; + + // Low Credits will flash OFF for odd intervals + + if (cupdFlash == 0) { + if (gwfPerfOptions & kfPerfSymbolFlashing) { + m_fDrawCreditSymbol = !((short)(cUpdates / kcupdSymbolFlashRate) & 1); + Invalidate(); + } else { + if (!m_fDrawCreditSymbol) + Invalidate(); + m_fDrawCreditSymbol = true; + } + } + } +} + +// +// Power control +// + +bool PowerControl::Init(Form *pfrm, IniReader *pini, FindProp *pfind) +{ + // Base initialization + + if (!Control::Init(pfrm, pini, pfind)) + return false; + + m_fShowPowerSymbol = true; + m_nDemand = m_nSupply = 0; + Font *pfnt = gapfnt[kifntHud]; + m_rc.right = m_rc.left + pfnt->GetTextExtent("DEF"); // the power background image + m_rc.bottom = m_rc.top + pfnt->GetHeight(); + + // Figure out the maximum amount of power required by an individual structure type + // so we can draw the 'yellow zone' at OnPaint time. + + m_nDemandMax = 0; + UnitMask um = 1; + for (int i = 0; i < 32; i++, um <<= 1) { + if (um & kumStructures) { + StructConsts *pstruc = (StructConsts *)gapuntc[i]; + if (pstruc->nPowerDemand > m_nDemandMax) + m_nDemandMax = pstruc->nPowerDemand; + } + } + + return true; +} + +// meter is crafted to divide evenly by 10 + +const int kcPowerMeterPips = 10; + +void PowerControl::OnPaint(DibBitmap *pbm) +{ + // Draw background + + int cx = m_rc.Width(); + Font *pfnt = gapfnt[kifntHud]; + pfnt->DrawText(pbm, "DEF", m_rc.left, m_rc.top, cx, -1); + + // Draw power symbol + + if (m_fShowPowerSymbol) + pfnt->DrawText(pbm, "G", m_rc.left, m_rc.top, cx, -1); + + // Draw power meter + + int cxLeftSide = pfnt->GetTextExtent("D"); + int x = m_rc.left + cxLeftSide; + int cxMeter = pfnt->GetTextExtent("E"); + int cxPip = pfnt->GetTextExtent("I"); + + Color clr; + if (m_nSupply < m_nDemand) { + clr = GetColor(kiclrRed); + } else { + // Dip into the yellow if building a structure might cause demand + // to become less than supply. + + if (m_nSupply - m_nDemand <= m_nDemandMax) + clr = GetColor(kiclrYellow); + else + clr = GetColor(kiclrGreen); + } + + // UNDONE: handle > 400 units of power + int nSupplyPerPip = ((StructConsts *)gapuntc[kutReactor])->nPowerSupply; + int cSupplyPips = m_nSupply / nSupplyPerPip; + if (cSupplyPips > kcPowerMeterPips) + cSupplyPips = kcPowerMeterPips; + + byte bclr = clr & 0xff; + dword dwClr = MAKEDWORD(bclr); + + for (int i = 0; i < cSupplyPips; i++, x += cxPip) + pfnt->DrawText(pbm, "I", x, m_rc.top, 1, &dwClr); + + // Draw demand indicator + + int cxDemand = pfnt->GetTextExtent("H"); + int xDemand = (m_nDemand * cxMeter) / (nSupplyPerPip * kcPowerMeterPips); + pfnt->DrawText(pbm, "H", m_rc.left + cxLeftSide + xDemand - (cxDemand / 2), m_rc.top, cxDemand, -1); +} + +void PowerControl::Update(int nDemand, int nSupply) +{ + if (nDemand != m_nDemand || nSupply != m_nSupply) { + m_nDemand = nDemand; + m_nSupply = nSupply; + m_fPowerLow = (m_nDemand > m_nSupply); + if (!m_fPowerLow) + m_fShowPowerSymbol = true; + Invalidate(); + } + + if (m_fPowerLow) { + long cUpdates = gsim.GetUpdateCount(); + long cupdFlash = cUpdates % kcupdSymbolFlashRate; + + // Low Power will flash ON for odd intervals + + if (cupdFlash == 0) { + if (gwfPerfOptions & kfPerfSymbolFlashing) { + m_fShowPowerSymbol = ((short)(cUpdates / kcupdSymbolFlashRate) & 1); + Invalidate(); + } else { + if (!m_fShowPowerSymbol) + Invalidate(); + m_fShowPowerSymbol = true; + } + } + } +} + +} // namespace wi diff --git a/game/LRInfantry.cpp b/game/LRInfantry.cpp new file mode 100644 index 0000000..842d362 --- /dev/null +++ b/game/LRInfantry.cpp @@ -0,0 +1,387 @@ +#include "ht.h" + +namespace wi { + +static MobileUnitConsts gConsts; + +#if defined(DEBUG_HELPERS) +char *LRInfantryGob::GetName() +{ + return "LRInfantry"; +} +#endif + +static int s_anFiringStripIndices[8] = { 1, 2, 3, 4, 5, 6, 7, 8 }; +static int s_anMovingStripIndices[8] = { 17, 18, 19, 20, 21, 22, 23, 24 }; +static int s_anIdleStripIndices[8] = { 9, 10, 11, 12, 13, 14, 15, 16 }; + +bool LRInfantryGob::InitClass(IniReader *pini) +{ + gConsts.gt = kgtLongRangeInfantry; + gConsts.ut = kutLongRangeInfantry; + gConsts.upgmPrerequisites = kupgmAdvancedHRC; + gConsts.wf |= kfUntcNotifyEnemyNearby; + + // Initialize the frame indices arrays + + gConsts.anFiringStripIndices = s_anFiringStripIndices; + gConsts.anMovingStripIndices = s_anMovingStripIndices; + gConsts.anIdleStripIndices = s_anIdleStripIndices; + + // Sound effects + + gConsts.sfxFire = ksfxRocketInfantryFire; + gConsts.sfxImpact = ksfxRocketInfantryImpact; + + gConsts.sfxcDestroyed = ksfxcInfantryDestroyed; + gConsts.sfxcSelect = ksfxcMajor02Select; + gConsts.sfxcMove = ksfxcMajor02Move; + gConsts.sfxcAttack = ksfxcMajor02Attack; + + return MobileUnitGob::InitClass(&gConsts, pini); +} + +void LRInfantryGob::ExitClass() +{ + MobileUnitGob::ExitClass(&gConsts); +} + +LRInfantryGob::LRInfantryGob() : MobileUnitGob(&gConsts) +{ +} + +bool LRInfantryGob::Fire(UnitGob *puntTarget, WCoord wx, WCoord wy, WCoord wdx, WCoord wdy) +{ + // MobileUnitGob handles rotating towards the target, firing delay, + // and starting the fire animation + + if (!MobileUnitGob::Fire(puntTarget, wx, wy, wdx, wdy)) + return false; + + // Fire off the shot! + + wdx += ((GetRandom() & 7) - 3) * kwcTile16th; + wdy += ((GetRandom() & 7) - 3) * kwcTile16th; + Point ptSpecial; + m_ani.GetSpecialPoint(&ptSpecial); // from the tip of the launcher + + CreateRocketGob(m_wx + WcFromPc(ptSpecial.x), m_wy + WcFromPc(ptSpecial.y), m_wx + wdx, m_wy + wdy, + GetDamageTo(puntTarget), m_gid, puntTarget->GetId()); + + // Play sfx + + gsndm.PlaySfx(m_pmuntc->sfxFire); + + return true; +} + +void LRInfantryGob::Idle() +{ + // 1/4 of the time we pivot left, 1/4 we pivot right, and 1/2 we play the idle + + switch (GetRandom() & 3) { + case 0: + m_dir--; + if (m_dir < 0) + m_dir = 7; + StartAnimation(&m_ani, m_pmuntc->anIdleStripIndices[m_dir], 0, kfAniResetWhenDone); + break; + + case 1: + m_dir++; + if (m_dir > 7) + m_dir = 0; + StartAnimation(&m_ani, m_pmuntc->anIdleStripIndices[m_dir], 0, kfAniResetWhenDone); + break; + + default: + StartAnimation(&m_ani, m_pmuntc->anIdleStripIndices[m_dir], 0, kfAniResetWhenDone); + break; + } +} + +int LRInfantryGob::ProcessStateMachineMessage(State st, Message *pmsg) +{ +BeginStateMachine + State(kstDying) + OnEnter + TRect trc; + GetTileRect(&trc); + + Deactivate(); + + // Redraw this part of minimap. It will skip inactive munts + + gpmm->RedrawTRect(&trc); + + m_ff ^= kfGobLayerDepthSorted | kfGobLayerSurfaceDecal; + + gsndm.PlaySfx(SfxFromCategory(m_pmuntc->sfxcDestroyed)); + m_ani.Start("die 3", kfAniIgnoreFirstAdvance); + MarkRedraw(); + + // Fade corpse away in 10 seconds + + gsmm.SendDelayedMsg(kmidDelete, 1000, m_gid, m_gid); + + OnUpdate + AdvanceAnimation(&m_ani); + +#if 0 +EndStateMachineInherit(MobileUnitGob) +#else + return knHandled; + } + } else { + return (int)MobileUnitGob::ProcessStateMachineMessage(st, pmsg); + } + return (int)MobileUnitGob::ProcessStateMachineMessage(st, pmsg); +#endif +} + + +// +// RocketGob implementation +// + +AnimationData *RocketGob::s_panidRocket = NULL; +int RocketGob::s_nTrailStrip; + +RocketGob *CreateRocketGob(WCoord wx, WCoord wy, WCoord wxTarget, WCoord wyTarget, int nDamage, Gid gidOwner, Gid gidTarget) +{ + if (!ggobm.IsBelowLimit(knLimitSupport)) + return NULL; + + RocketGob *pgob = new RocketGob(); + Assert(pgob != NULL, "out of memory!"); + if (pgob == NULL) + return NULL; + + if (!pgob->Init(wx, wy, wxTarget, wyTarget, nDamage, gidOwner, gidTarget)) { + delete pgob; + return NULL; + } + + return pgob; +} + +bool RocketGob::InitClass(IniReader *pini) +{ + s_panidRocket = LoadAnimationData("rocket.anir"); + if (s_panidRocket == NULL) + return false; + s_nTrailStrip = s_panidRocket->GetStripIndex("trail"); + return true; +} + +void RocketGob::ExitClass() +{ + delete s_panidRocket; + s_panidRocket = NULL; +} + +RocketGob::RocketGob() +{ + m_ff |= kfGobStateMachine | kfGobLayerSmokeFire; +} + +bool RocketGob::Init(WCoord wx, WCoord wy, WCoord wxTarget, WCoord wyTarget, int nDamage, Gid gidOwner, Gid gidTarget) +{ + // Units fire from their special point which may be off the edge of the map. + // We cannot allow Gobs to be off the map so here we bring it on. + + BringInBounds(&wx, &wy); + +#ifdef DEBUG + TileMap *ptmap = gsim.GetLevel()->GetTileMap(); + Size sizT; + ptmap->GetTCoordMapSize(&sizT); + Assert(wx < WcFromTc(sizT.cx) && wy < WcFromTc(sizT.cy)); + Assert(wxTarget < WcFromTc(sizT.cx) && wyTarget < WcFromTc(sizT.cy)); +#endif + + m_gidOwner = gidOwner; + m_gidTarget = gidTarget; + m_nDamage = nDamage; + + // TUNE: rocket movement rate + + m_li.Init(wx, wy, wxTarget, wyTarget, kwcTile / 3); // was 6.0 + + // LineIterator initializes x,y to the first step-integral point on the line, + // presuming that the final step should be at the target x,y + + m_wx = m_li.GetWX(); + m_wy = m_li.GetWY(); + + m_ani.Init(s_panidRocket); + StartAnimation(&m_ani, 0, CalcDir(wxTarget - wx, wyTarget - wy), kfAniDone); + + // Add the fresh Gob to the GobMgr. GobMgr::AddGob assigns this Gob a gid + + ggobm.AddGob(this); + + // Let the target know when it will be hit. Doing it this way + // means the hit will arrive in the same number of updates for all + // players (the number of updates calc'd by the shooter). Depending + // on screen resolution, the animated travel time may be off by an update + + Message msgT; + msgT.mid = kmidHit; + msgT.smidSender = m_gidOwner; + msgT.smidReceiver = m_gidTarget; + msgT.Hit.gidAssailant = m_gidOwner; + msgT.Hit.sideAssailant = ggobm.GetGob(m_gidOwner)->GetSide(); + msgT.Hit.nDamage = m_nDamage; + + // BUGBUG: m_li.GetStepsRemaining is influenced by the firing point which is resolution dependent. + // Instead, this message's delay should be derived using the distance from the world coordinate + // centers of the source and target + + gsmm.SendDelayedMsg(&msgT, (m_li.GetStepsRemaining() + 1) * (kcmsUpdate / 10)); + + return true; +} + +// RocketGobs don't get loaded + +bool RocketGob::Init(IniReader *pini, FindProp *pfind, const char *pszName) +{ + return true; +} + +bool RocketGob::IsSavable() +{ + return false; +} + +GobType RocketGob::GetType() +{ + return kgtRocket; +} + +void RocketGob::GetClippingBounds(Rect *prc) +{ + // hardcoded that the travel is strip 0 and the impact is strip 1 + + if (m_ani.GetStrip() == 0) { + if (!(gwfPerfOptions & kfPerfRocketShots)) { + prc->SetEmpty(); + return; + } + } else { + if (!(gwfPerfOptions & kfPerfRocketImpacts)) { + prc->SetEmpty(); + return; + } + } + + m_ani.GetBounds(prc); + prc->Offset(PcFromUwc(m_wx), PcFromUwc(m_wy)); +} + +void RocketGob::Draw(DibBitmap *pbm, int xViewOrigin, int yViewOrigin, int nLayer) +{ + if (nLayer == knLayerSmokeFire) { + // hardcoded that the travel is strip 0 and the impact is strip 1 + + if (m_ani.GetStrip() == 0) { + if (!(gwfPerfOptions & kfPerfRocketShots)) + return; + } else { + if (!(gwfPerfOptions & kfPerfRocketImpacts)) + return; + } + + m_ani.Draw(pbm, PcFromUwc(m_wx) - xViewOrigin, PcFromUwc(m_wy) - yViewOrigin, m_pplr->GetSide()); + } +} + +#if defined(DEBUG_HELPERS) +char *RocketGob::GetName() +{ + return "Rocket"; +} +#endif + +int RocketGob::ProcessStateMachineMessage(State st, Message *pmsg) +{ +BeginStateMachine + OnUpdate + // Advance the Rocket animation + + if (m_ani.GetStrip() == 0) { + AdvanceAnimation(&m_ani); + + // Advance the Rocket position + + if (m_li.Step()) { + WCoord wxOld = m_wx; + WCoord wyOld = m_wy; + m_wx = m_li.GetWX(); + m_wy = m_li.GetWY(); + + // Keep GobMgr in the loop so it can maintain proper depth sorting + + if (m_wx != wxOld || m_wy != wyOld) { + ggobm.MoveGob(this, wxOld, wyOld, m_wx, m_wy); + MarkRedraw(); + } + + // Spawn a puff at the old position + + if (gwfPerfOptions & kfPerfRocketTrails) { + if (m_li.GetStepsRemaining() & 1) + CreateAnimGob(wxOld, wyOld, kfAnmDeleteWhenDone | kfAnmSmokeFireLayer, NULL, s_panidRocket, s_nTrailStrip, ksmidNull, NULL); + } + + // Rocket gobs require every update + + m_unvl.MinSkip(); + + } else { + + // Play impact sound + + UnitGob *punt = (UnitGob *)ggobm.GetGob(m_gidOwner); + if (punt != NULL) { + UnitConsts *puntc = (UnitConsts *)punt->GetConsts(); + gsndm.PlaySfx(puntc->sfxImpact); + } + + // Start the impact animation + + StartAnimation(&m_ani, 1, 0, 0); + + // Expect to get called next update + + m_unvl.MinSkip(); + } + + // Assume valid if we're not drawing rockets + + if (!(gwfPerfOptions & kfPerfRocketShots)) + m_ff &= ~kfGobRedraw; + } else { + + // Advance the impact animation + + if (!AdvanceAnimation(&m_ani)) { + + // Kill this Rocket + + ggobm.RemoveGob(this); + delete this; + return knDeleted; + } + + // Assume valid if we're not drawing rocket impacts + + if (!(gwfPerfOptions & kfPerfRocketImpacts)) + m_ff &= ~kfGobRedraw; + } + +EndStateMachine +} + +} // namespace wi \ No newline at end of file diff --git a/game/Level.cpp b/game/Level.cpp new file mode 100644 index 0000000..6de84dd --- /dev/null +++ b/game/Level.cpp @@ -0,0 +1,528 @@ +#include "ht.h" + +namespace wi { + +Level::Level() +{ + m_fInitialized = false; + m_pfogm = NULL; + m_ptrmap = NULL; + m_ptmap = NULL; + m_ppal = NULL; + m_mpiclriclrShadow = NULL; + m_nPlayersMin = 1; + m_nPlayersMax = 1; + m_szTitle[0] = 0; + m_szFileLevel[0] = 0; + m_nVersion = 0; + m_dwRevision = 0; +} + +Level::~Level() +{ + if (m_fInitialized) { + // Reset gobm state + + ggobm.Reset(); + + // Clear replicator points + + ReplicatorGob::ClearReplicatorCount(); + + delete m_ptmap; + delete m_pfogm; + delete m_ptrmap; + + if (m_ppal != NULL) + gpakr.UnmapFile(&m_fmapPalette); + if (m_mpiclriclrShadow != NULL) + gpakr.UnmapFile(&m_fmapShadowMap); + + // Free up any cached paths + + MobileUnitGob::FreeCachedPaths(); + } +} + +bool Level::LoadLevelInfo(const char *pszLevelName, IniReader *piniLoaded) +{ + IniReader *pini; + + if (piniLoaded == NULL) { + pini = LoadIniFile(gpakr, pszLevelName); + if (pini == NULL) { + Assert(false); + return false; + } + } else { + pini = piniLoaded; + } + + // Get level version + + if (pini->GetPropertyValue("General", "Version", "%d", &m_nVersion) == 0) + m_nVersion = 0; + + // Get revision # + + if (pini->GetPropertyValue("General", "Revision", "%ld", &m_dwRevision) == 0) + m_dwRevision = 0; + + // Can't run if the level version is newer than what the game supports + + bool fSuccess = true; + if (m_nVersion > knVersionLevelSupported) { + fSuccess = false; + HtMessageBox(kfMbWhiteBorder, "Error", "Newer version of Hostile Takeover required to run this mission."); + } else { + // Load General level data + + if (!pini->GetPropertyValue("General", "Title", m_szTitle, sizeof(m_szTitle))) + strcpy(m_szTitle, ""); + + m_nPlayersMin = m_nPlayersMax = 0; + pini->GetPropertyValue("General", "MinPlayers", "%d", &m_nPlayersMin); + pini->GetPropertyValue("General", "MaxPlayers", "%d", &m_nPlayersMax); + + // Normalize + if (pszLevelName[0] == 'm' && pszLevelName[1] == '_') { + if (m_nPlayersMin < 2) { + m_nPlayersMin = 2; + } + if (m_nPlayersMax > 4) { + m_nPlayersMax = 4; + } + if (m_nPlayersMax < m_nPlayersMin) { + m_nPlayersMax = m_nPlayersMin; + } + } else { + m_nPlayersMin = 1; + m_nPlayersMax = 1; + } + + // Read in the side info for each side + + Side side; + for (side = ksideNeutral; side < kcSides; side++) { + if (side == ksideNeutral) { + LoadSideInfo(pini, "sideNeutral", &m_asidi[side]); + } else { + char szSideName[] = "side1"; + szSideName[4] = '0' + side; + LoadSideInfo(pini, szSideName, &m_asidi[side]); + } + } + + // If there is no human side, assume it is side 1 + // because gpplrLocal has to be set to something (because our code is intolerant) + + bool fHumanFound = false; + for (side = ksideNeutral; side < kcSides; side++) { + if (m_asidi[side].nIntelligence == knIntelligenceHuman) + fHumanFound = true; + } + if (!fHumanFound) + m_asidi[kside1].nIntelligence = knIntelligenceHuman; + } + + if (piniLoaded == NULL) + delete pini; + + return fSuccess; +} + +bool Level::LoadSideInfo(IniReader *pini, char *pszSideName, SideInfo *psidi) +{ + memset(psidi, 0, sizeof(*psidi)); + + // Include some good defaults in case this level contains no SideInfo for this side + + psidi->nInitialCredits = 5000; + pini->GetPropertyValue(pszSideName, "InitialCredits", "%ld", &psidi->nInitialCredits); + + int tx = 0; + int ty = 0; + pini->GetPropertyValue(pszSideName, "InitialView", "%d,%d", &tx, &ty); + psidi->wptInitialView.wx = WcFromTc(tx); + psidi->wptInitialView.wy = WcFromTc(ty); + psidi->nIntelligence = knIntelligenceComputer; + pini->GetPropertyValue(pszSideName, "Intelligence", "%d", &psidi->nIntelligence); + pini->GetPropertyValue(pszSideName, "InitialStructureCount", "%d", &psidi->cStructuresInitial); + pini->GetPropertyValue(pszSideName, "InitialMobileUnitCount", "%d", &psidi->cMobileUnitsInitial); + return true; +} + +bool Level::Init(const char *pszLevel, bool fConstantsOnly) +{ + // Save level filename + + strncpyz(m_szFileLevel, (char *)pszLevel, sizeof(m_szFileLevel)); + + // Init reads all useful level data and then deletes the IniReader + // to save memory. + + Status("Level::Init LoadIniFile..."); + IniReader *pini = LoadIniFile(gpakr, pszLevel); + if (pini == NULL) { + return false; + } + + if (!LoadLevelConstants(pszLevel, pini)) { + delete pini; + return false; + } + + // Establish unit count deltas + + ggame.CalcUnitCountDeltas(this); + + // Load variables + + if (!fConstantsOnly) { + if (!LoadLevelVariables(pini)) { + Assert(false); + delete pini; + return false; + } + } + delete pini; + + return true; +} + +bool Level::LoadLevelConstants(const char *pszLevelName, IniReader *pini) +{ + if (!LoadLevelInfo(pszLevelName, pini)) + return false; + + // m_fInitialized is to distinguish between just having the level parameters + // read and having been initialized enough to require some cleanup. + + m_fInitialized = true; + + // Load map + + Status("Load Tile Map..."); + char szT[kcbFilename]; + if (!pini->GetPropertyValue("General", "TileMap", szT, sizeof(szT))) { + Assert(false); + return false; + } + + Size sizPlayfield; + ggame.GetPlayfieldSize(&sizPlayfield); + m_ptmap = LoadTileMap(szT, &sizPlayfield); + Assert(m_ptmap != NULL); + if (m_ptmap == NULL) { + Assert(false); + return false; + } + + // Init GobMgr + + Size sizMap; + m_ptmap->GetMapSize(&sizMap); + Size sizTile; + m_ptmap->GetTileSize(&sizTile); + if (!ggobm.Init(sizMap.cx / sizTile.cx, sizMap.cy / sizTile.cy, kcpgobMax)) { + Assert(false); + return false; + } + + // Load terrain map + + Status("Load Terrain Map..."); + if (!pini->GetPropertyValue("General", "TerrainMap", szT, sizeof(szT))) { + Assert(false); + return false; + } + m_ptrmap = new TerrainMap; + if (m_ptrmap == NULL) { + Assert(false); + return false; + } + if (!m_ptrmap->Init(szT)) { + Assert(false); + return false; + } + + // Create fog map. + + Status("Create Fog Map..."); + m_pfogm = new FogMap; + if (m_pfogm == NULL) { + Assert(false); + return false; + } + if (!m_pfogm->Init(&sizTile, &sizMap)) { + Assert(false); + return false; + } + + // Load palette + + if (!pini->GetPropertyValue("General", "Palette", szT, sizeof(szT))) { + Assert(false); + return false; + } + m_ppal = (Palette *)gpakr.MapFile(szT, &m_fmapPalette); + if (m_ppal == NULL) { + Assert(false); + return false; + } + strcat(szT, ".shadowmap"); + m_mpiclriclrShadow = (byte *)gpakr.MapFile(szT, &m_fmapShadowMap); + + // Instantiate an OvermindGob for each Computer Player + + Player *pplr = gplrm.GetNextPlayer(NULL); + for (; pplr != NULL; pplr = gplrm.GetNextPlayer(pplr)) { +#ifdef STRESS + // Instantiate an Overmind for the human player if we're running stress + + if (gfStress) { + if ((pplr->GetFlags() & (kfPlrComputer | kfPlrComputerOvermind)) == kfPlrComputer) + continue; + } else { + if (!(pplr->GetFlags() & kfPlrComputerOvermind)) + continue; + } +#else + if (!(pplr->GetFlags() & kfPlrComputerOvermind)) + continue; +#endif + + // Instantiate and initialize an OvermindGob + + OvermindGob *pgobOvermind = (OvermindGob *)CreateGob(kgtOvermind); + if (pgobOvermind == NULL) { + Assert(false); + delete pini; + return false; + } + + if (!pgobOvermind->Init(NULL)) { + Assert(false); + delete pgobOvermind; + delete pini; + return false; + } + + // Who's your daddy? + + pgobOvermind->SetOwner(pplr); + } + + // Load the Triggers + + Status("Load Triggers..."); + if (!m_tgrm.Init(pini)) { + Assert(false); + return false; + } + + // Load the UnitGroups + + Status("Load UnitGroups..."); + if (!m_ugm.Init(pini)) { + Assert(false); + return false; + } + + return true; +} + +bool Level::LoadLevelVariables(IniReader *pini) +{ + // Load areas + + Status("Load Areas..."); + if (!ggobm.LoadAreas(pini)) { + return false; + } + + // Enumerate all Gob descriptions and instantiate in-memory versions + + Status("Load Gobs..."); + char szName[kcbFilename]; + FindProp find; + while (pini->FindNextProperty(&find, "GameObjects", szName, sizeof(szName))) { + GobType gt; + pini->GetPropertyValue(&find, "%d,", >); + + Gob *pgob = CreateGob(gt); + if (pgob == NULL) { + Assert(false); + return false; + } + + // Don't waste space on Gobs that don't need names + + char *pszName; + if (strcmp("nil", szName) == 0) + pszName = NULL; + else + pszName = szName; + + if (!pgob->Init(pini, &find, pszName)) { + Assert(false); + delete pgob; + return false; + } + +#ifdef STRESS + if (gfStress) { + // Make player's units invulnerable + + if (pgob->GetOwner() == gpplrLocal) { + if (pgob->GetFlags() & kfGobUnit) { + UnitGob *punt = (UnitGob *)pgob; + punt->SetUnitFlags(punt->GetUnitFlags() | kfUnitInvulnerable); + } + } + } +#endif + } + + // Enumerate all Galaxite positions and add them to the Fog/Galaxite map + + Status("Load Galaxite..."); + FindProp find2; + while (pini->FindNextProperty(&find2, "Galaxite", szName, sizeof(szName))) { + int nGx, tx, ty; + pini->GetPropertyValue(&find2, "%d,%d,%d", &nGx, &tx, &ty); + m_pfogm->SetGalaxite(nGx, tx, ty); + } + +// In terrain for the moment (forever?) +#if 0 + // Enumerate all Wall positions and add them to the Fog/Galaxite/Wall map + + Status("Load walls..."); + FindProp find3; + while (pini->FindNextProperty(&find3, "Walls", szName, sizeof(szName))) { + int nHealth, tx, ty; + pini->GetPropertyValue(&find3, "%d,%d,%d", &nHealth, &tx, &ty); + m_pfogm->SetWallHealth(nHealth, tx, ty); + } +#endif + + // Instantiate the "CreateAtLevelLoad" UnitGroups + + m_ugm.CreateAtLevelLoadGroups(); + + return true; +} + +#define knVerLevelState 2 +bool Level::LoadState(Stream *pstm) +{ + // Do version handling + + byte nVer = pstm->ReadByte(); + if (nVer != knVerLevelState) + return false; + + // Get level name + + char szLevel[kcbFilename]; + pstm->ReadString(szLevel, sizeof(szLevel)); + + // Load level constants + + if (!Init(szLevel, true)) { + return false; + } + + // Check to see if the save game has a different mission revision number than the mission + // itself. If so, error. This is the case if a game is saved, then the level is revised + // in M and reloaded. + + dword dwRevision = pstm->ReadDword(); + if (dwRevision != m_dwRevision) { + HtMessageBox(kfMbWhiteBorder | kfMbClearDib, "Error", "This saved game is based on an older version of this mission!"); + return false; + } + + // Load gobm state + + if (!ggobm.LoadState(pstm)) + return false; + + // Load fog and galaxite state + + if (!m_pfogm->LoadState(pstm)) { + return false; + } + if (!m_ptrmap->LoadState(pstm)) { + return false; + } + + // Load gobs + + int cGobs = pstm->ReadWord(); + while (cGobs-- != 0) { + // Get gob type + + GobType gt = pstm->ReadByte(); + Gob *pgob = CreateGob(gt); + if (pgob == NULL) { + return false; + } + + if (!pgob->LoadState(pstm)) { + return false; + } + } + + // Load triggers + + if (!m_tgrm.LoadState(pstm)) { + return false; + } + + // Load UnitGroups + + if (!m_ugm.LoadState(pstm)) { + return false; + } + + return pstm->IsSuccess(); +} + +bool Level::SaveState(Stream *pstm) +{ + pstm->WriteByte(knVerLevelState); + pstm->WriteString(m_szFileLevel); + pstm->WriteDword(m_dwRevision); + ggobm.SaveState(pstm); + m_pfogm->SaveState(pstm); + m_ptrmap->SaveState(pstm); + + int cgobs = 0; + Gob *pgobT; + for (pgobT = ggobm.GetFirstGob(); pgobT != NULL; pgobT = ggobm.GetNextGob(pgobT)) { + if (pgobT->IsSavable()) + cgobs++; + } + pstm->WriteWord(cgobs); + + for (pgobT = ggobm.GetFirstGob(); pgobT != NULL; pgobT = ggobm.GetNextGob(pgobT)) { + if (pgobT->IsSavable()) { + pstm->WriteByte(pgobT->GetType()); + pgobT->SaveState(pstm); + } + } + + // Save triggers + + m_tgrm.SaveState(pstm); + + // Save UnitGroups + + m_ugm.SaveState(pstm); + + return pstm->IsSuccess(); +} + +} // namespace wi diff --git a/game/Makefile.iphone b/game/Makefile.iphone new file mode 100644 index 0000000..dec3663 --- /dev/null +++ b/game/Makefile.iphone @@ -0,0 +1,114 @@ +OUTDIR= +CC = arm-apple-darwin-gcc +CPP_DEFINES_GLOBAL= +CPP_INCS=-I../inc -Iiphone +TARGETDEFINED=0 +LD=$(CC) +LDFLAGS=-lobjc -framework CoreGraphics -framework GraphicsServices -framework CoreSurface -framework CoreFoundation -framework Foundation -framework UIKit -framework LayerKit -framework AudioToolbox + +ifdef MAP +MAPFLAGS=-Xlinker -Map -Xlinker ht.map +endif + +# Debug is default + +ifdef REL +CPP_DEBUG= +else +CPP_DEBUG=-DDEBUG -g +endif + +ifdef ESD_BUILD +CPP_DEFINES=$(CPP_DEFINES_GLOBAL) -DESD_BUILD +ifdef REL +OUTDIR=iPhone_ESD_Release +else +OUTDIR=iPhone_ESD_Debug +endif +TARGETDEFINED=1 +endif + +ifdef RETAIL_BUILD +CPP_DEFINES=$(CPP_DEFINES_GLOBAL) -DRETAIL_BUILD +ifdef REL +OUTDIR=iPhone_Retail_Release +else +OUTDIR=iPhone_Retail_Debug +endif +TARGETDEFINED=1 +endif + +ifdef PROFILE +CPP_DEFINES=$(CPP_DEFINES_GLOBAL) -DDEV_BUILD +CPP_DEBUG=-mdebug-labels +OUTDIR=iPhone_Profile +TARGETDEFINED=1 +endif + +# DEV_BUILD is default + +ifeq ($(TARGETDEFINED),0) +DEV_BUILD=1 +endif + +ifdef DEV_BUILD +CPP_DEFINES=$(CPP_DEFINES_GLOBAL) -DDEV_BUILD +ifdef REL +OUTDIR=iPhone_Release +else +OUTDIR=iPhone_Debug +endif +TARGETDEFINED=1 +endif + +# Composite the flags together + +CFLAGS=-Os -DIPHONE $(CPP_DEFINES) $(CPP_DEBUG) $(CPP_INCS) -fno-rtti -fno-exceptions -fcheck-new -fsigned-char +MFLAGS=-Os -DIPHONE -fsigned-char $(CPP_DEFINES) $(CPP_DEBUG) $(CPP_INCS) + +OBJS=game.o misc.o bitmap.o packfile.o rip.o form.o ini.o \ +misccontrols.o tests.o timer.o event.o font.o \ +GameObjects.o Level.o Simulation.o tilemap.o StateMachine.o Animation.o \ +fogmap.o SRInfantry.o LRInfantry.o terrainmap.o HRC.o \ +Reactor.o Processor.o Miner.o Headquarters.o Research.o Radar.o VTS.o \ +MobileBuilder.o Tank.o SpInfantry.o Overmind.o Builder.o MobileUnit.o \ +memmgr.o tbitmap.o compression.o savegame.o main.o \ +cachemgr.o Multiplayer.o comm.o Player.o \ +Warehouse.o Tower.o MobileHQ.o Unit.o Struct.o SimUI.o \ +soundmgr.o host.o iphone.o iphoneview.o mp_test.o \ +updatemap.o thunks.o display.o TransportMgr.o \ +GameOptions.o InputUI.o formmgr.o loadsave.o \ +triggermgr.o TriggerConditions.o TriggerActions.o UnitGroupMgr.o BuildMgr.o \ +RawBitmap.o Shell.o Ecom.o alertcontrol.o stringtable.o Andy.o \ +Artillery.o Replicator.o Help.o CutScene.o drm.o \ +messagequeue.o socketserver.o eventdispatcher.o \ +mixer.o iphonesounddev.o + +all: $(OUTDIR)/wi + +clean: + rm -f $(OUTDIR)/* + +$(OUTDIR)/wi: $(OBJS:%=$(OUTDIR)/%) + $(LD) $(LDFLAGS) -o $@ $^ $(MAPFLAGS) + +$(OUTDIR)/packfile.o: packfile.cpp packfile.h + $(CC) $(CFLAGS) -c packfile.cpp -o $(OUTDIR)/packfile.o + +$(OUTDIR)/misc.o: misc.cpp ht.h res.h iphone/htplatform.h license.h + $(CC) $(CFLAGS) -c misc.cpp -o $(OUTDIR)/misc.o + +$(OUTDIR)/%.o: %.cpp ht.h res.h iphone/htplatform.h + $(CC) $(CFLAGS) -c $(*F).cpp -o $@ + +$(OUTDIR)/%.o: iphone/%.cpp ht.h res.h iphone/htplatform.h iphone/iphone.h + $(CC) $(CFLAGS) -c iphone/$(*F).cpp -o $@ + +$(OUTDIR)/%.o: iphone/%.mm ht.h res.h iphone/htplatform.h iphone/iphone.h iphone/iphoneview.h + $(CC) $(MFLAGS) -c iphone/$(*F).mm -o $@ + +$(OUTDIR)/%.o: iphone/%.m ht.h res.h iphone/htplatform.h iphone/iphone.h iphone/iphoneview.h + $(CC) $(MFLAGS) -c iphone/$(*F).m -o $@ + +$(OUTDIR)/rip.o: ../inc/rip.cpp + $(CC) $(CFLAGS) -c ../inc/rip.cpp -o $(OUTDIR)/rip.o diff --git a/game/Miner.cpp b/game/Miner.cpp new file mode 100644 index 0000000..77e064d --- /dev/null +++ b/game/Miner.cpp @@ -0,0 +1,919 @@ +#include "ht.h" +#include "strings.h" + +namespace wi { + +static MinerConsts gConsts; +AnimationData *MinerGob::s_panidVacuum = NULL; + +#if defined(DEBUG_HELPERS) +char *MinerGob::GetName() +{ + return "Miner"; +} +#endif + +static int s_anMovingStripIndices[8] = { 0, 1, 2, 3, 4, 5, 6, 7 }; + +bool MinerGob::InitClass(IniReader *pini) +{ + s_panidVacuum = LoadAnimationData("vacuum.anir"); + Assert(s_panidVacuum != NULL); + if (s_panidVacuum == NULL) + return false; + + gConsts.gt = kgtGalaxMiner; + gConsts.ut = kutGalaxMiner; + gConsts.umPrerequisites = kumProcessor; + gConsts.wf |= kfUntcHasFullnessIndicator; + + // Initialize the frame indices arrays + + gConsts.anFiringStripIndices = s_anMovingStripIndices; + gConsts.anMovingStripIndices = s_anMovingStripIndices; + gConsts.anIdleStripIndices = s_anMovingStripIndices; + + // Sound effects + + gConsts.sfxImpact = ksfxNothing; + gConsts.sfxMine = ksfxGalaxMinerMine; + gConsts.sfxUnderAttack = ksfxGalaxMinerUnderAttack; + gConsts.sfxFire = ksfxNothing; + + gConsts.sfxcDestroyed = ksfxcVehicleDestroyed; + gConsts.sfxcSelect = ksfxcMajor01Select; + gConsts.sfxcMove = ksfxcMajor01Move; + gConsts.sfxcAttack = ksfxcNothing; + + return MobileUnitGob::InitClass(&gConsts, pini); +} + +void MinerGob::ExitClass() +{ + MobileUnitGob::ExitClass(&gConsts); + delete s_panidVacuum; + s_panidVacuum = NULL; +} + +MinerGob::MinerGob() : MobileUnitGob(&gConsts) +{ + m_aniVacuum.Init(s_panidVacuum); + StartAnimation(&m_aniVacuum, 0, 0, kfAniLoop); + m_nGalaxiteAmount = 0; + m_gidFavoriteProcessor = kgidNull; + m_tptGalaxite.tx = kxInvalid; + m_fMinerUnderAttack = false; + m_wfMunt &= ~kfMuntAggressivenessBits; + m_fHidden = false; + m_fAttemptingToDeliver = false; +} + +#define knVerMinerGobState 2 +bool MinerGob::LoadState(Stream *pstm) +{ + byte nVer = pstm->ReadByte(); + if (nVer != knVerMinerGobState) + return false; + m_nGalaxiteAmount = pstm->ReadWord(); + m_cDelay = (char)pstm->ReadByte(); + m_gidFavoriteProcessor = pstm->ReadWord(); + m_tptGalaxite.tx = pstm->ReadWord(); + m_tptGalaxite.ty = pstm->ReadWord(); + m_fMinerUnderAttack = pstm->ReadByte() != 0 ? true : false; + m_fHidden = pstm->ReadByte() != 0 ? true : false; + m_fAttemptingToDeliver = pstm->ReadByte() != 0 ? true : false; + return MobileUnitGob::LoadState(pstm); +} + +bool MinerGob::SaveState(Stream *pstm) +{ + pstm->WriteByte(knVerMinerGobState); + pstm->WriteWord(m_nGalaxiteAmount); + pstm->WriteByte(m_cDelay); + pstm->WriteWord(m_gidFavoriteProcessor); + pstm->WriteWord(m_tptGalaxite.tx); + pstm->WriteWord(m_tptGalaxite.ty); + pstm->WriteByte(m_fMinerUnderAttack); + pstm->WriteByte(m_fHidden); + pstm->WriteByte(m_fAttemptingToDeliver); + return MobileUnitGob::SaveState(pstm); +} + +// when the miner goes into the processor, we hide the miner gob and the +// processor gob draws it pulling in and out. Leave our spot marked occupied +// so it saves it for us. This is kindof half a deactivate. + +void MinerGob::Hide(bool fHide) +{ + if (m_fHidden == fHide) + return; + + m_fHidden = fHide; + if (fHide) { + m_ff &= ~(kfGobActive | kfGobDrawFlashed); + // make sure this unit's menu is not left up + + UnitConsts *puntc = GetUnitConsts(GetType()); + Assert(puntc->pfrmMenu != NULL); + if (puntc->pfrmMenu->GetOwner() == (UnitGob *)this) + puntc->pfrmMenu->EndForm(kidcCancel); + + Invalidate(); + } else { + m_ff |= kfGobActive; + Invalidate(); + } +} + +// so far it's just an assert, but let's not violate the general rule that we +// don't deactivate inactive gobs. + +void MinerGob::Deactivate() +{ + if (m_fHidden) + m_ff |= kfGobActive; + MobileUnitGob::Deactivate(); +} + +void MinerGob::Draw(DibBitmap *pbm, int xViewOrigin, int yViewOrigin, int nLayer) +{ +#ifdef DRAW_OCCUPIED_TILE_INDICATOR + { + WRect wrcT; + GetTilePaddedWRect(&wrcT); + Rect rcT; + rcT.FromWorldRect(&wrcT); + rcT.Offset(-xViewOrigin, -yViewOrigin); + DrawBorder(pbm, &rcT, 1, GetColor(kiclrWhite)); + } +#endif + + if (m_fHidden) + return; + + MobileUnitGob::Draw(pbm, xViewOrigin, yViewOrigin, nLayer); + + if (m_st == kstMinerSuck && nLayer == knLayerDepthSorted) { + SetAnimationStrip(&m_aniVacuum, m_ani.GetStrip()); + m_aniVacuum.Draw(pbm, PcFromUwc(m_wx) - xViewOrigin, PcFromUwc(m_wy) - yViewOrigin); + } else if (nLayer == knLayerSelection && (m_ff & kfGobSelected)) { + Rect rcT; + rcT.FromWorldRect(&m_pmuntc->wrcUIBounds); + rcT.Offset(PcFromUwc(m_wx) - xViewOrigin, PcFromUwc(m_wy) - yViewOrigin); + DrawFullnessIndicator(pbm, &rcT, m_nGalaxiteAmount / knGalaxiteValue / 2, knMinerGalaxiteMax / knGalaxiteValue / 2); + } +} + +void MinerGob::InitMenu(Form *pfrm) +{ + ButtonControl *pbtn = (ButtonControl *)pfrm->GetControlPtr(kidcDeliver); + pbtn->Show(GetGalaxiteAmount() != 0); +} + +void MinerGob::OnMenuItemSelected(int idc) +{ + switch (idc) { + case kidcDeliver: + { + // The player shouldn't have been able to select this command unless + // it can be carried out. + + Assert(GetGalaxiteAmount() != 0); + + Message msg; + msg.mid = kmidDeliverCommand; + msg.smidSender = m_gid; + msg.smidReceiver = m_gid; + msg.DeliverCommand.gidTarget = kgidNull; + msg.tDelivery = 0; + gcmdq.Enqueue(&msg); + gsndm.PlaySfx(ksfxGalaxMinerDeliver); + } + break; + } +} + +bool MinerGob::IsValidTarget(Gob *pgobTarget) +{ + // no soldier squishing this version + + return (pgobTarget->GetType() == kgtProcessor && m_pplr == pgobTarget->GetOwner()) + && (pgobTarget->GetFlags() & kfGobActive); +} + +void MinerGob::GetClippingBounds(Rect *prc) +{ + UnitGob::GetClippingBounds(prc); + + if (m_st == kstMinerSuck) { + SetAnimationStrip(&m_aniVacuum, m_ani.GetStrip()); + Rect rc; + m_aniVacuum.GetBounds(&rc); + rc.Offset(PcFromUwc(m_wx), PcFromUwc(m_wy)); + prc->Union(&rc); + } +} + +void MinerGob::SetTarget(Gid gid, WCoord wx, WCoord wy, WCoord wxCenter, WCoord wyCenter, TCoord tcRadius, WCoord wcMoveDistPerUpdate) +{ + if (gid == kgidNull) { + + // If the target is Galaxite then translate this command into a MineCommand + + FogMap *pfogm = gsim.GetLevel()->GetFogMap(); + if (pfogm->GetGalaxite(TcFromWc(wx), TcFromWc(wy)) != 0) { + + // Play mine sfx - this is called in a move so the general unit + // move sound will also be called, and the mine sound is currently + // no different. Needs to somehow be an attack but SimUI.cpp + // only checks for mobile units as targets. + //if (m_pplr == gpplrLocal) + // gsndm.PlaySfx(m_pmnrc->sfxMine); + + Message msg; + memset(&msg, 0, sizeof(msg)); + msg.mid = kmidMineCommand; + msg.MineCommand.gidTarget = gid; + msg.MineCommand.wptTarget.wx = wx; + msg.MineCommand.wptTarget.wy = wy; + msg.smidReceiver = m_gid; + gcmdq.Enqueue(&msg); + return; + } + + // If target is not Galaxite let the MobileUnitGob handle command + + MobileUnitGob::SetTarget(gid, wx, wy, wxCenter, wyCenter, tcRadius, wcMoveDistPerUpdate); + return; + } + + // If the target no longer exists, discard the command + + Gob *pgobTarget = ggobm.GetGob(gid); + if (pgobTarget == NULL) + return; + + // If target is not a friendly processor let the MobileUnitGob handle command differentiation + + if (pgobTarget->GetType() != kgtProcessor || !IsAlly(pgobTarget->GetSide())) { + MobileUnitGob::SetTarget(gid, wx, wy, wxCenter, wyCenter, tcRadius, wcMoveDistPerUpdate); + return; + } + + // Play deliver sfx + + gsndm.PlaySfx(ksfxGalaxMinerDeliver); + + // Flash the target Gob + + if (m_pplr == gpplrLocal) + pgobTarget->Flash(); + + Message msg; + memset(&msg, 0, sizeof(msg)); + msg.mid = kmidDeliverCommand; + msg.MineCommand.gidTarget = gid; + msg.MineCommand.wptTarget.wx = wx; + msg.MineCommand.wptTarget.wy = wy; + msg.smidReceiver = m_gid; + gcmdq.Enqueue(&msg); +} + +void MinerGob::Mine(WCoord wx, WCoord wy) +{ + Assert(!InTransition()); + +//temp +if (!(m_ff & kfGobActive)) { + Assert(); +} + + // If we aren't being told to where to mine and we don't already + // have a previous mining location then find a new place to mine. + + if (wx == kwxInvalid) { + if (m_tptGalaxite.tx == kxInvalid) { + SetState(kstMinerFindGalaxite); + } else { + + // Go to Galaxite closest to the previous location + + FogMap *pfogm = gsim.GetLevel()->GetFogMap(); + if (pfogm->FindNearestGalaxite(m_tptGalaxite.tx, m_tptGalaxite.ty, + &m_tptGalaxite, (m_pplr->GetFlags() & kfPlrComputer) != 0 || ggame.IsMultiplayer())) { + m_wptTarget.wx = WcFromTc(m_tptGalaxite.tx); + m_wptTarget.wy = WcFromTc(m_tptGalaxite.ty); + SetState(kstMinerApproachGalaxite); + } else { + // If no Galaxite found, just stay where we are (NOTE: + // where we are is in the way of any other Miners trying + // to use the same Processor!) + + gsim.GetLevel()->GetTriggerMgr()->SetConditionTrue(knMinerCantFindGalaxiteCondition, GetSideMask(GetSide())); + if (m_pplr == gpplrLocal) + ShowAlert(kidsMinerNeedsGalaxite); + SetState(kstGuard); + } + } + } else { + // Stash mine parameters to be picked up by the Mine state + + m_wptTarget.wx = wx; + m_wptTarget.wy = wy; + m_tptGalaxite.tx = TcFromWc(m_wptTarget.wx); + m_tptGalaxite.ty = TcFromWc(m_wptTarget.wy); + SetState(kstMinerApproachGalaxite); + } +} + +void MinerGob::PerformAction(char *szAction) +{ + int nUnitAction; + if (IniScanf(szAction, "%d", &nUnitAction) == 0) { + Assert(false); + return; + } + + if (nUnitAction == knMineUnitAction) { + SendMineCommand(m_gid, kwxInvalid, kwxInvalid); + return; + } + + // Don't override this action, let base handle it + + MobileUnitGob::PerformAction(szAction); +} + +/* +MinerGob pseudo-code +----------------- +Inherit all MobileUnitGob message and state handlers + +OnDeliverCommand { +> Deliver() // Galaxite to Processor +=> Mine(last mining position) +} + +OnMineCommand(target) { +=> Mine(target) +} + +Mine(target) { + if no target + target = nearest (reachable?) Galaxite tile + +1> move within range of target + + while true { + while not full { + do { + target = nearest (reachable?) Galaxite tile + if on top of target +2> move off target, forcefully + else +3> move within range of target, forcefully + } while target tile has no Galaxite + +4> rotate to face the tile to mine from + + do { +5> take Galaxite from target + } while not full AND target has Galaxite + } + +6> Deliver() // Galaxite to Processor + +7> move to last mining position + } +} + +Deliver() { + if preferred Processor no longer exists { + find the closest friendly Processor + if no Processors exist + abort Delivery (set state to Guard) + } + +> move to position in front of Processor +> rotate for entry + notify Processor of Galaxite delivery +> wait for Galaxite to be processed +} +*/ + +int MinerGob::ProcessStateMachineMessage(State st, Message *pmsg) +{ +BeginStateMachine + OnMsg(kmidHit) + // It is possible to be hit by a shot AFTER the process of + // being pulled into a Processor has begun. We don't want the + // Processor to have to deal with its 'taken' Miner being + // destroyed so when this happens we just ignore the hit. + // UNDONE: this should be considered along with the general + // fix of removing the MinerGob when the Processor takes it + // and adding it back when the delivery is done. + + if (!(m_ff & kfGobActive)) + return knHandled; + + // If we've been hit, announce it to the user + + if (gpplrLocal == m_pplr) { + // After what interval? + + if (!m_fMinerUnderAttack) { + m_fMinerUnderAttack = true; + gsndm.PlaySfx(ksfxGalaxMinerUnderAttack); + + // update status + ShowAlert(kidsMinerUnderAttack); + } + } + return MobileUnitGob::ProcessStateMachineMessage(st, pmsg); + + OnMsg(kmidDeliverCommand) + // Return to specified Processor. If Processor is gone pick the + // closest one and deliver there. + + // If a path is being followed we have to wait until a tile center is + // reached before acting on a deliver command. + + if (InTransition()) { + m_msgPending = *pmsg; + m_wfMunt |= kfMuntCommandPending; + } else { + // Use the deliver target. If that no longer exists, find the closest processor + + Gob *pgobTarget = NULL; + m_gidTarget = pmsg->DeliverCommand.gidTarget; + if (m_gidTarget != kgidNull) + pgobTarget = ggobm.GetGob(m_gidTarget); + if (pgobTarget == NULL) { + m_gidTarget = FindClosestProcessor(); + if (m_gidTarget != kgidNull) + pgobTarget = ggobm.GetGob(m_gidTarget); + } + + // Stash deliver parameters to be picked up by the MinerMoveToProcessor state + + if (pgobTarget != NULL && !(pgobTarget->GetFlags() & kfGobBeingBuilt)) { + m_gidFavoriteProcessor = pgobTarget->GetId(); + + // Adjust target x/y to point to the center of the tile + // closest to the Processor's entrance. + + pgobTarget->GetPosition(&m_wptTarget); + m_wptTarget.wx += kwcTile + kwcTileHalf; + m_wptTarget.wy += (kwcTile * 2) + kwcTileHalf; + SetState(kstMinerMoveToProcessor); + } else { + SetState(kstGuard); + } + } + + OnMsg(kmidMineCommand) + + // If a path is being followed we have to wait until a tile center is + // reached before acting on a deliver command. + + if (InTransition()) { + m_msgPending = *pmsg; + m_wfMunt |= kfMuntCommandPending; + } else { + Mine(pmsg->MineCommand.wptTarget.wx, pmsg->MineCommand.wptTarget.wy); + } + + OnMsg(kmidAttackCommand) + + // UNDONE: Miners don't have an attack at the moment so they ignore + // their auto-response impulses to chase their attacker + + // DO NOTHING (override Unit's Attack response) + + //----------------------------------------------------------------------- + + State(kstMinerMoveToProcessor) + OnEnter + MoveEnter(true); + + OnExit + MoveExit(); + + OnUpdate + switch (MoveUpdate()) { + case knMoveTargetReached: + { + // let's take a second look at our target to be sure it was not + // destroyed or taken over whilst we were enroute + + Gob *pgobProcessor = ggobm.GetGob(m_gidTarget); + if (pgobProcessor != NULL && m_pplr == pgobProcessor->GetOwner()) { + SetState(kstMinerRotateForEntry); + } else { + // Our processor is gone. See if we can find another + + Gid gid = FindClosestProcessor(); + if (gid != kgidNull) { + SendDeliverCommand(gid); + } else { + SetState(kstGuard); + } + } + } + break; + + case knMoveStuck: + // The processor will look for candidate miners when possible. + // It'll know this miner wants to deliver because of the m_fAttemptingToDeliver + // flag. + + m_fAttemptingToDeliver = true; + SetState(kstGuard); + break; + } + +// DefUpdate(); // called inside of MoveUpdate() + + State(kstMinerRotateForEntry) + OnUpdate + m_unvl.MinSkip(); + + // Rotate to face south + + if (m_dir != kdirS) { + m_dir = TurnToward(kdirS, m_dir); + StartAnimation(&m_ani, m_pmuntc->anMovingStripIndices[m_dir], 0, kfAniLoop | kfAniIgnoreFirstAdvance); + } else { + + // When facing south send message to Processor and switch to + // guard state + + Gob *pgobProcessor = ggobm.GetGob(m_gidTarget); + if (pgobProcessor != NULL) + gsmm.SendMsg(kmidGalaxiteDelivery, m_gid, m_gidTarget); + + // We'll sit in this state while the Processor extracts the + // Galaxite. When it's done it will send a kmidMineCommand + // with a negative target. OnMsg(kmidMineCommand) will + // recognize this as a signal to continue mining where we last + // were. + + m_fAttemptingToDeliver = false; + SetState(kstGuard); + + // Miner can't be under attack now. This state is used to + // determine if it's ok to announce "miner under attack!" + + m_fMinerUnderAttack = false; + } + DefUpdate(); + + //----------------------------------------------------------------------- + +// If not already full of Galaxite, find some to mine + + State(kstMinerFindGalaxite) + OnUpdate + // Are we full? + + if (m_nGalaxiteAmount == knMinerGalaxiteMax) { + + // Yes, deliver our load to our favorite Processor + + SendDeliverCommand(m_gidFavoriteProcessor); + + } else { + + // No, let's find some Galaxite + + // Is there some right in front of us? + + TCoord tx = TcFromWc(m_wx); + TCoord ty = TcFromWc(m_wy); + TCoord txTarget = tx + g_mpDirToDx[m_dir]; + TCoord tyTarget = ty + g_mpDirToDy[m_dir]; + + m_wptTarget.wx = WcFromTc(txTarget); + m_wptTarget.wy = WcFromTc(tyTarget); + FogMap *pfogm = gsim.GetLevel()->GetFogMap(); + if (pfogm->GetGalaxite(txTarget, tyTarget) != 0) { + + // Yes, get busy with it + + m_tptGalaxite.tx = txTarget; + m_tptGalaxite.ty = tyTarget; + +//temp +if (!(m_ff & kfGobActive)) { + Assert(); +} + + + SetState(kstMinerSuck); + } else { + + // No, look around for some + // Miners controlled by computer players or in a network game can (must) ignore fog. + + bool fFound = pfogm->FindNearestGalaxite(tx, ty, &m_tptGalaxite, + (m_pplr->GetFlags() & kfPlrComputer) != 0 || ggame.IsMultiplayer()); + if (!fFound) { + + // Can't find any! Deliver what we have & then we'll park it by our + // processor. If we've never found any then record that this is the closest + // we've come to finding any + + if (m_tptGalaxite.tx == kwxInvalid){ + gsim.GetLevel()->GetTriggerMgr()->SetConditionTrue(knMinerCantFindGalaxiteCondition, GetSideMask(GetSide())); + if (m_pplr == gpplrLocal) + ShowAlert(kidsMinerNeedsGalaxite); + SetState(kstGuard); + } else { + SendDeliverCommand(m_gidFavoriteProcessor); + } + + } else { + // Are we on top of the Galaxite we want? + + if (tx == m_tptGalaxite.tx && ty == m_tptGalaxite.ty) { + + // Yes, pick an unoccupied adjacent tile to move to + + for (int dir = 0; dir < 8; dir++) { + TCoord txT = tx + g_mpDirToDx[dir]; + TCoord tyT = ty + g_mpDirToDy[dir]; + if (!IsTileFree(txT, tyT)) + continue; + + m_wptTarget.wx = WcFromTc(txT); + m_wptTarget.wy = WcFromTc(tyT); + SetState(kstMinerStepAside); + break; + } + + // NOTE: if can't move to a new spot then we'll try + // again next Update. + // NOTE: ideally we should just issue the move command and + // let the move waiting code handle this case. + + m_unvl.MinSkip(); + + } else { + + // No, move within range of the target tile, forcefully + + m_wptTarget.wx = WcFromTc(m_tptGalaxite.tx); + m_wptTarget.wy = WcFromTc(m_tptGalaxite.ty); + SetState(kstMinerApproachGalaxite); + } + } + } + } + DefUpdate(); + +// Rotate to face the desired tile and advance to state kstMinerSuck + + State(kstMinerFaceGalaxite) + OnEnter + m_cDelay = 0; + + OnUpdate + // Introduce a little rotation delay + + m_cDelay -= m_unvl.GetUpdateCount(); + if (m_cDelay < 0) { + m_cDelay = 1; + + // Make sure we're facing towards the Galaxite tile + + Direction dirTo = CalcDir(m_tptGalaxite.tx - TcFromWc(m_wx), m_tptGalaxite.ty - TcFromWc(m_wy)); + if (m_dir != dirTo) { + m_dir = TurnToward(dirTo, m_dir); + StartAnimation(&m_ani, m_pmuntc->anMovingStripIndices[m_dir], 0, kfAniLoop | kfAniIgnoreFirstAdvance); + } else { +//temp +if (!(m_ff & kfGobActive)) { +} + + SetState(kstMinerSuck); + } + } + m_unvl.MinSkip(m_cDelay); + DefUpdate(); + +// Move off a Galaxite tile so we can mine from it. +// An appropriate target tile has already been selected. + + State(kstMinerStepAside) + OnEnter + MoveEnter(true); + + OnExit + MoveExit(); + + OnUpdate + switch (MoveUpdate()) { + case knMoveTargetReached: + // Turn around to face the tile we want to mine from +//temp +if (!(m_ff & kfGobActive)) { + Assert(); +} + + SetState(kstMinerFaceGalaxite); + break; + + case knMoveStuck: + // UNDONE: what to do? Doing nothing means we repath every + // Update until a valid path can be determined. + + SetState(kstGuard); + break; + } + +// DefUpdate(); // called inside of MoveUpdate() + +// Move within range of target and advance to state kstMinerFaceGalaxite + + State(kstMinerApproachGalaxite) + OnEnter + MoveEnter(true); + + OnExit + MoveExit(); + + OnUpdate + // Are we within 1 tile of the target? + + if (!InTransition()) { + if (abs(WcTrunc(m_wx) - WcTrunc(m_wptTarget.wx)) <= kwcTile && + abs(WcTrunc(m_wy) - WcTrunc(m_wptTarget.wy)) <= kwcTile) { + + // Yes, this is where we want to be! +//temp +if (!(m_ff & kfGobActive)) { + Assert(); +} + + SetState(kstMinerFaceGalaxite); + return knHandled; + } + } + + switch (MoveUpdate()) { + case knMoveTargetReached: +//temp +if (!(m_ff & kfGobActive)) { + Assert(); +} + + + SetState(kstMinerFaceGalaxite); + break; + + case knMoveStuck: + // UNDONE: what to do? Doing nothing means we repath every + // Update until a valid path can be determined. + + SetState(kstGuard); + break; + } + +// DefUpdate(); // called inside of MoveUpdate() + +// Dig in, if there's any Galaxite left + + State(kstMinerSuck) + OnEnter +//temp +if (!(m_ff & kfGobActive)) { + Assert(); +} + // Begin countdown to Galaxite decrement + + m_cDelay = kcMinerSuckDelay; + + // Are we full? + + if (m_nGalaxiteAmount == knMinerGalaxiteMax) + SendDeliverCommand(m_gidFavoriteProcessor); + + OnUpdate + // Are we full? + + if (m_nGalaxiteAmount == knMinerGalaxiteMax) { + + // Yes, deliver our load to our favorite Processor + + MarkRedraw(); + SendDeliverCommand(m_gidFavoriteProcessor); + + } else { + // If target tile is out of Galaxite try another + + FogMap *pfogm = gsim.GetLevel()->GetFogMap(); + if (pfogm->GetGalaxite(m_tptGalaxite.tx, m_tptGalaxite.ty) == 0) { + + // Look for another + + MarkRedraw(); + SetState(kstMinerFindGalaxite); + } else { + AdvanceAnimation(&m_aniVacuum); + + // Time to consume some Galaxite? + + m_cDelay -= m_unvl.GetUpdateCount(); + if (m_cDelay < 0) { + m_cDelay = kcMinerSuckDelay; + m_unvl.MinSkip(m_cDelay); + pfogm->DecGalaxite(m_tptGalaxite.tx, m_tptGalaxite.ty); + m_nGalaxiteAmount += 10; + MarkRedraw(); + } + } + } + + DefUpdate(); + +#if 0 +EndStateMachineInherit(MobileUnitGob) +#else + return knHandled; + } + } else { + return (int)MobileUnitGob::ProcessStateMachineMessage(st, pmsg); + } + return (int)MobileUnitGob::ProcessStateMachineMessage(st, pmsg); +#endif +} + +#ifdef MP_DEBUG_SHAREDMEM +void MinerGob::MPValidate() +{ + // TODO: animation state + MPValidateGobMember(MinerGob, m_nGalaxiteAmount); + MPValidateGobMember(MinerGob, m_cDelay); + MPValidateGobMember(MinerGob, m_gidFavoriteProcessor); + MPValidateGobMember(MinerGob, m_tptGalaxite); + +// gpplrLocal specific +// MPValidateGobMember(MinerGob, m_fMinerUnderAttack); + + MPValidateGobMember(MinerGob, m_fAttemptingToDeliver); + MPValidateGobMember(MinerGob, m_fHidden); + +// Visibility specific +// m_aniVacuum.MPValidate((Animation *)(((byte *)MPGetGobPtr(m_gid)) + OFFSETOF(MinerGob, m_aniVacuum))); + + MobileUnitGob::MPValidate(); +} +#endif + +Gid MinerGob::FindClosestProcessor() +{ + // Find the closest Processor owned by the Miner's owner + TCoord tcxyClosest = ktcMax; + TPoint tpt; + TCoord tc; + + Gid gidClosest = kgidNull; + for (Gob *pgobT = ggobm.GetFirstGob(); pgobT != NULL; pgobT = ggobm.GetNextGob(pgobT)) { + if (pgobT->GetType() != kgtProcessor) + continue; + if (pgobT->GetOwner() != m_pplr) + continue; + if (pgobT->GetFlags() & kfGobBeingBuilt) + continue; + if (!(pgobT->GetFlags() & kfGobActive)) + continue; + + // candidate processor. Is it closer than our last candidate? + + pgobT->GetTilePosition(&tpt); + tc = CalcRange(tpt.tx, tpt.ty, this); + if (tc < tcxyClosest) { + gidClosest = pgobT->GetId(); + tcxyClosest = tc; + } + } + + return gidClosest; +} + +void MinerGob::SendDeliverCommand(Gid gidProcessor) +{ + // Do it this way instead of calling SetTarget... SetTarget does + // some extra "user feedback" things we don't want here + + Message msg; + memset(&msg, 0, sizeof(msg)); + msg.mid = kmidDeliverCommand; + msg.MineCommand.gidTarget = gidProcessor; + msg.MineCommand.wptTarget.wx = 0; + msg.MineCommand.wptTarget.wy = 0; + msg.smidReceiver = m_gid; + gsmm.SendMsg(&msg); +} + +} // namespace wi diff --git a/game/MobileBuilder.cpp b/game/MobileBuilder.cpp new file mode 100644 index 0000000..5e0bf6c --- /dev/null +++ b/game/MobileBuilder.cpp @@ -0,0 +1,216 @@ +#include "ht.h" + +namespace wi { + +// +// MobileUnitBuilderGob implementation +// base gob for VTS and HRC +// + +#if defined(DEBUG_HELPERS) +char *MobileUnitBuilderGob::GetName() +{ + return "MobileUnitBuilder"; +} +#endif + +MobileUnitBuilderGob::MobileUnitBuilderGob(MobileUnitBuilderConsts *pmubc) : BuilderGob(pmubc) +{ + m_puntc->wf |= kfUntcNotifyPowerLowHigh; +} + +void MobileUnitBuilderGob::Draw(DibBitmap *pbm, int xViewOrigin, int yViewOrigin, int nLayer) +{ + // UNDONE: rethink this + bool fUpgraded = (m_pplr->GetUpgrades() & ((MobileUnitBuilderConsts *)m_puntc)->fUpgrade) != 0; + if (fUpgraded) + SetAnimationStrip(&m_ani, m_ani.GetStrip() + 3); + + BuilderGob::Draw(pbm, xViewOrigin, yViewOrigin, nLayer); + + if ((nLayer == knLayerDepthSorted) && ((m_pplr->GetUpgrades() & ((MobileUnitBuilderConsts *)m_puntc)->fUpgradeInProgress) != 0)) + BuilderGob::DrawUpgradeEffect(pbm, xViewOrigin, yViewOrigin); + + if (fUpgraded) + SetAnimationStrip(&m_ani, m_ani.GetStrip() - 3); +} + +void MobileUnitBuilderGob::InitMenu(Form *pfrm) +{ + Control *pctl = pfrm->GetControlPtr(kidcBuild); + pctl->Enable((m_pplr->GetUpgrades() & ((MobileUnitBuilderConsts *)m_pmuntc)->fUpgradeInProgress) != ((MobileUnitBuilderConsts *)m_puntc)->fUpgradeInProgress); + + BuilderGob::InitMenu(pfrm); +} + +void MobileUnitBuilderGob::OnMenuItemSelected(int idc) +{ + switch (idc) { + case kidcBuild: + gpmfrmm->AddForm(((MobileUnitBuilderConsts *)m_puntc)->pfrmBuild); + ((MobileUnitBuilderConsts *)m_puntc)->pfrmBuild->SetOwner(this); + ((MobileUnitBuilderConsts *)m_puntc)->pfrmBuild->DoModal(); + gpmfrmm->RemoveForm(((MobileUnitBuilderConsts *)m_puntc)->pfrmBuild); + break; + + default: + BuilderGob::OnMenuItemSelected(idc); + break; + } +} + +void MobileUnitBuilderGob::Takeover(Player *pplr) +{ + // new owner gets to keep upgrades + + pplr->SetUpgrades(pplr->GetUpgrades() | (m_pplr->GetUpgrades() & ((MobileUnitBuilderConsts *)m_puntc)->fUpgrade)); + BuilderGob::Takeover(pplr); +} + +void MobileUnitBuilderGob::Deactivate() +{ + BuilderGob::Deactivate(); + if (m_pmubc->pfrmBuild != NULL && m_pmubc->pfrmBuild->GetOwner() == this) + m_pmubc->pfrmBuild->EndForm(); +} + +int MobileUnitBuilderGob::ProcessStateMachineMessage(State st, Message *pmsg) +{ +BeginStateMachine + OnEnter + SetState(kstIdle); + + OnMsg(kmidBuildOtherCommand) + // Counts will already have been checked in the order UI. However since + // the order is queued, the UI only guesses if it is possible to build + // based on current state when the button is pressed. Here we make a + // bedrock decision. This ensures the limits are enforced and that the + // same decision gets made on all clients in a multiplayer game. + + if (ggobm.IsBelowLimit(knLimitMobileUnit, m_pplr)) + Build(pmsg->BuildOtherCommand.ut); + + OnMsg(kmidAbortBuildOtherCommand) + // UNDONE: deal with queuing? + + AbortBuild(true, pmsg->AbortBuildOtherCommand.ut); + + OnMsg(kmidSelfDestructCommand) + // override and call abort build here so we can tell it to refund the + // value. AbortBuild is also in BuilderGob::Deactivate but will do + // nothing if called a 2nd time, and there it wouldnt refund if we left + // it + + AbortBuild(true); + SelfDestruct(); + + //----------------------------------------------------------------------- + + State(kstIdle) + OnMsg(kmidUpgradeComplete) + // Redraw to get rid of the upgrade graphics + + m_ff &= ~kfGobBeingUpgraded; + MarkRedraw(); + + OnMsg(kmidBeingUpgraded) + // Wake up so the animation can occur + + m_ff |= kfGobBeingUpgraded; + m_unvl.MinSkip(); + + OnUpdate + // Invalidate if upgrading is occuring so that this gob can draw the upgrade + // effect. + + if (m_pplr->GetUpgrades() & ((MobileUnitBuilderConsts *)m_puntc)->fUpgradeInProgress) { + // Smallest interval so we can keep invalidating ourselves while being upgraded + + MarkRedraw(); + m_unvl.MinSkip(); + } + + return BuilderGob::ProcessStateMachineMessage(st, pmsg); + + //----------------------------------------------------------------------- + + State(kstBuildOtherCompleting) + OnEnter + // init built Gob + { + UnitGob *puntBuild = GetBuiltGob(); + if (puntBuild != NULL) { + WPoint wpt; + FindInitPosition(&wpt); + puntBuild->Init(wpt.wx, wpt.wy, m_pplr, 0, 0, NULL); + + // If this is a Bullpup built by a human player send it off to mine. + + if (!(m_pplr->GetFlags() & kfPlrComputer) && puntBuild->GetType() == kgtGalaxMiner) { + SendMineCommand(puntBuild->GetId(), kwxInvalid, 0); + + } else { +#ifdef RALLY_POINTS +/* +UNDONE: issues with rally points +- rally point needs to be visible when the Builder is selected (e.g., flag) +- units are vulnerable along the way. Need to attack-move (respond to being hit, then continue to rally point) +- can't set rally point on the Replicator +x need to know when to send a unit to the rally point (e.g., it's not at the default value) +x units force others out of the way to get to the rally point +*/ + // If the rally point is not at the default, send the new unit there + + if (m_tptRally.tx != ktxInvalid) { + Message msgT; + msgT.mid = kmidMoveCommand; + msgT.smidSender = m_gid; + msgT.smidReceiver = puntBuild->GetId(); + FindNearestFreeTile(m_tptRally.tx, m_tptRally.ty, &msgT.MoveCommand.wptTarget); + msgT.MoveCommand.gidTarget = kgidNull; + msgT.MoveCommand.wptTargetCenter.wx = msgT.MoveCommand.wptTarget.wx; + msgT.MoveCommand.wptTargetCenter.wy = msgT.MoveCommand.wptTarget.wy; + msgT.MoveCommand.tcTargetRadius = 0; + msgT.MoveCommand.wcMoveDistPerUpdate = ((MobileUnitConsts *)puntBuild->GetConsts())->GetMoveDistPerUpdate(); + gsmm.SendMsg(&msgT); + } +#endif + } + } + + // Notify the BuildMgr that this Unit is complete + + gsim.GetBuildMgr()->OnBuilt(puntBuild, this); + ((MobileUnitBuilderConsts *)m_puntc)->pfrmBuild->OnUnitCompleted(this, puntBuild->GetUnitType()); + ClearBuiltGob(); + } + SetState(kstIdle); + + // These are here to keep the message from routing up to BuilderGob's message handler + OnUpdate + OnExit + +#if 0 +EndStateMachineInherit(BuilderGob) +#else + return knHandled; + } + } else { + return (int)BuilderGob::ProcessStateMachineMessage(st, pmsg); + } + return (int)BuilderGob::ProcessStateMachineMessage(st, pmsg); +#endif +} + +void MobileUnitBuilderGob::DefUpdate() +{ + // give the build form a chance to show progress + + ((MobileUnitBuilderConsts *)m_puntc)->pfrmBuild->DefUpdate(this, IsBuildInProgress()); + + // and continue with the normal idle processing + + BuilderGob::DefUpdate(); +} + +} // namespace wi diff --git a/game/MobileHQ.cpp b/game/MobileHQ.cpp new file mode 100644 index 0000000..ada0f35 --- /dev/null +++ b/game/MobileHQ.cpp @@ -0,0 +1,201 @@ +#include "ht.h" + +namespace wi { + +static MobileUnitConsts gConsts; + +#if defined(DEBUG_HELPERS) +char *MobileHqGob::GetName() +{ + return "MobileHQ"; +} +#endif + +static int s_anMovingStripIndices[8] = { 0, 1, 2, 3, 4, 5, 6, 7 }; + +bool MobileHqGob::InitClass(IniReader *pini) +{ + gConsts.gt = kgtMobileHeadquarters; + gConsts.ut = kutMobileHeadquarters; + gConsts.umPrerequisites = kumResearchCenter; + gConsts.upgmPrerequisites = kupgmAdvancedVTS; + + // Initialize the frame indices arrays + + gConsts.anFiringStripIndices = s_anMovingStripIndices; + gConsts.anMovingStripIndices = s_anMovingStripIndices; + gConsts.anIdleStripIndices = s_anMovingStripIndices; + + // Sound effects + + gConsts.sfxFire = ksfxNothing; + gConsts.sfxImpact = ksfxNothing; + + gConsts.sfxcDestroyed = ksfxcVehicleDestroyed; + gConsts.sfxcSelect = ksfxcMale01Select; + gConsts.sfxcMove = ksfxcMale01Move; + gConsts.sfxcAttack = ksfxcMale01Attack; + + return MobileUnitGob::InitClass(&gConsts, pini); +} + +void MobileHqGob::ExitClass() +{ + MobileUnitGob::ExitClass(&gConsts); +} + +MobileHqGob::MobileHqGob() : MobileUnitGob(&gConsts) +{ + m_wfMunt &= ~kfMuntAggressivenessBits; +} + +bool MobileHqGob::CanTransform(TPoint *ptp) +{ + // optionally return the point we decide is ok so we can use it when we + // decide to transform + + Assert(gbldcHq.ctx == 3 && gbldcHq.cty == 2); // Because of the various '-1's sprinkled below + + // Can't transform on top of Galaxite. Check for it. watch the map edge too. + + TPoint tpt; + GetTilePosition(&tpt); + TCoord tx = tpt.tx - 1; + TCoord ty = tpt.ty - 1; + if (tx < 0) + tx = 0; + if (ty < 0) + ty = 0; + TCoord txR = tx + gbldcHq.ctx; + TCoord tyB = ty + gbldcHq.cty; + + // this logic works because we won't actually occupy the spot in txR, just up to it. + Size siz; + gsim.GetLevel()->GetTileMap()->GetTCoordMapSize(&siz); + if (txR > siz.cx) { + txR = siz.cx; + tx = txR - gbldcHq.ctx; + } + if (tyB > siz.cy) { + tyB = siz.cy; + ty = tyB - gbldcHq.cty; + } + if (ptp != NULL) { + ptp->tx = tx; + ptp->ty = ty; + } + bool fOccupied = false; + TCoord txT = tx; + FogMap *pfogm = gsim.GetLevel()->GetFogMap(); + for (; ty < tyB; ty++) { + for (tx = txT; tx < txR; tx++) { + Gob *pgob; + if (!IsTileFree(tx, ty, kbfReserved | kbfStructure, &pgob)) { + if (pgob != this) { + fOccupied = true; + break; + } + } + if (pfogm->GetGalaxite(tx, ty) != 0) { + fOccupied = true; + break; + } + } + } + + return !fOccupied; +} + +void MobileHqGob::InitMenu(Form *pfrm) +{ + ButtonControl *pbtn = (ButtonControl *)pfrm->GetControlPtr(kidcTransform); + LabelControl *plbl = (LabelControl *)pfrm->GetControlPtr(kidcCantTransform); + bool fCanTransform = CanTransform(); + pbtn->Show(fCanTransform); + plbl->Show(!fCanTransform); +} + +void MobileHqGob::OnMenuItemSelected(int idc) +{ + switch (idc) { + case kidcTransform: + { + // The player shouldn't have been able to select this command unless + // it can be carried out. + + Assert(CanTransform()); + + gcmdq.Enqueue(kmidTransformCommand, m_gid); + } + break; + + case kidcSelfDestruct: + gcmdq.Enqueue(kmidSelfDestructCommand, m_gid); + break; + } +} + +int MobileHqGob::ProcessStateMachineMessage(State st, Message *pmsg) +{ +BeginStateMachine + OnMsg(kmidTransformCommand) + + // Due to client-server lag the space wanted for transformation + // may no longer be free. Double-check and if it isn't simply fail. + // UNDONE: bzzzt + + TPoint tptDest; + if (!CanTransform(&tptDest)) + return knHandled; + + // Deactivate the MobileHQ + + Deactivate(); + + // UNDONE: do some cool animation + + // Remove self from the Gob list + + ggobm.RemoveGob(this); + + // Start sfx + + if (m_pplr == gpplrLocal) + gsndm.PlaySfx(ksfxMobileHeadquartersDeploy); + + // Create Headquarters + + HqGob *pgobHq = (HqGob *)CreateGob(kgtHeadquarters); + if (pgobHq != NULL) { + + // Carry the MHQ's health forward to the HQ + + fix fxHealth = (fix)divfx(mulfx(m_fxHealth, gapuntc[kutHeadquarters]->GetArmorStrength()), m_puntc->GetArmorStrength()); + pgobHq->Init(WcFromTc(tptDest.tx), WcFromTc(tptDest.ty), m_pplr, fxHealth, 0, NULL); + } + + // Delete self + + delete this; + return knDeleted; + + OnMsg(kmidAttackCommand) + + // UNDONE: MobileHqs don't have an attack at the moment so they ignore + // their auto-response impulses to chase their attacker + + // DO NOTHING (override Unit's Attack response) + +#if 0 +EndStateMachineInherit(MobileUnitGob) +#else + return knHandled; + } + } else { + return (int)MobileUnitGob::ProcessStateMachineMessage(st, pmsg); + } + return (int)MobileUnitGob::ProcessStateMachineMessage(st, pmsg); +#endif +} + +} // namespace wi \ No newline at end of file diff --git a/game/MobileUnit.cpp b/game/MobileUnit.cpp new file mode 100644 index 0000000..f90a174 --- /dev/null +++ b/game/MobileUnit.cpp @@ -0,0 +1,3048 @@ +#include "ht.h" + +namespace wi { + +// Each element of this table was calculated as: +// gawcDiagonalDist[i] = floor((cos(PI / 4) * i) + 0.5) + +/* jscript to generate the table: +for (j = 0; j < 4; j++) { + var str = ""; + for (i = 0; i < 16; i++) + str += Math.floor((Math.cos(Math.PI / 4) * ((j * 16) + i) + .5)) + ", "; + WScript.Echo(str); +} +*/ +static byte gawcDiagonalDist[64] = { + 0, 1, 1, 2, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 10, 11, + 11, 12, 13, 13, 14, 15, 16, 16, 17, 18, 18, 19, 20, 21, 21, 22, + 23, 23, 24, 25, 25, 26, 27, 28, 28, 29, 30, 30, 31, 32, 33, 33, + 34, 35, 35, 36, 37, 37, 38, 39, 40, 40, 41, 42, 42, 43, 44, 45, +}; + +// Each element of this table was calculated as: +// gaDiv256byNWithRounding[i] = (256 + (i / 2)) / i + +// The "+ (i / 2)" part of this rounds the # of steps rather than +// truncating it which gives gives better results when there is a partial +// step remainder. + +/* javascript to generate the table: +for (j = 0; j < 4; j++) { + var str = ""; + for (i = 0; i < 16; i++) { + var wcMoveDist = ((j * 16) + i); + str += Math.floor((256 + (wcMoveDist / 2)) / wcMoveDist) + ", "; + } + WScript.Echo(str); +} +*/ +static byte gaDiv256byNWithRounding[64] = { + 0, 255, // Fudge to keep within 8-bit range + 128, 85, 64, 51, 43, 37, 32, 28, 26, 23, 21, 20, 18, 17, + 16, 15, 14, 13, 13, 12, 12, 11, 11, 10, 10, 9, 9, 9, 9, 8, + 8, 8, 8, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, +}; + +int g_mpDirToDx[8] = { 0, 1, 1, 1, 0, -1, -1, -1 }; +int g_mpDirToDy[8] = { -1, -1, 0, 1, 1, 1, 0, -1 }; +AnimationData *g_panidMoveTarget; +static AnimationData *s_panidVehicleExplosion; + +#define GetIdleCountdown() ((GetRandom() % 50) + 50) // somewhere between 4 & 8 seconds + +const int kcFireCountdown = 6; // 6 updates (~.5 secs) + +Path *MobileUnitGob::s_apathCached[kcPathsCache]; +int MobileUnitGob::s_cpathCached; + +//=========================================================================== +// MobileUnitGob implementation + +bool MobileUnitGob::InitClass(MobileUnitConsts *pmuntc, IniReader *pini) +{ + if (!UnitGob::InitClass(pmuntc, pini)) + return false; + + char szTemplate[10]; + itoa(pmuntc->gt, szTemplate, 10); + + // Required properties + + int nT; + if (pini->GetPropertyValue(szTemplate, "MoveRate", "%d", &nT) != 1) + return false; + pmuntc->wcMoveDistPerUpdate = (WCoord)nT; + Assert(pmuntc->wcMoveDistPerUpdate < 64); // gawcDiagonalDist only has 64 entries + gwcMoveDistPerUpdateMin = _min(gwcMoveDistPerUpdateMin, pmuntc->wcMoveDistPerUpdate); + gwcMoveDistPerUpdateMax = _max(gwcMoveDistPerUpdateMax, pmuntc->wcMoveDistPerUpdate); + + int nArmorStrength; + if (pini->GetPropertyValue(szTemplate, "ArmorStrength", "%d", &nArmorStrength) != 1) + return false; + Assert(nArmorStrength < 1 << 10); + pmuntc->fxArmorStrength = itofx(nArmorStrength); + gfxMobileUnitArmorStrengthMin = _min(gfxMobileUnitArmorStrengthMin, pmuntc->fxArmorStrength); + gfxMobileUnitArmorStrengthMax = _max(gfxMobileUnitArmorStrengthMax, pmuntc->fxArmorStrength); + + // Optional properties + + if (pini->GetPropertyValue(szTemplate, "ArmorStrengthMP", "%d", &nArmorStrength) != 1) { + pmuntc->fxArmorStrengthMP = pmuntc->fxArmorStrength; + } else { + Assert(nArmorStrength < 1 << 10); + pmuntc->fxArmorStrengthMP = itofx(nArmorStrength); + } + + if (pini->GetPropertyValue(szTemplate, "MoveRateMP", "%d", &nT) != 1) + pmuntc->wcMoveDistPerUpdateMP = pmuntc->wcMoveDistPerUpdate; + else + pmuntc->wcMoveDistPerUpdateMP = (WCoord)nT; + Assert(pmuntc->wcMoveDistPerUpdateMP < 64); // gawcDiagonalDist only has 64 entries + + // Preload the unit's menu form + + if (!LoadMenu(pmuntc, pini, szTemplate, kidfUnitMenu)) + return false; + + // MobileUnitGob owns the 'move target' and 'vehicle explosion' AnimationData + + if (g_panidMoveTarget == NULL) { + g_panidMoveTarget = LoadAnimationData("movetarget.anir"); + if (g_panidMoveTarget == NULL) + return false; + } + + if (s_panidVehicleExplosion == NULL) { + s_panidVehicleExplosion = LoadAnimationData("vexplosion.anir"); + if (s_panidVehicleExplosion == NULL) + return false; + } + + return true; +} + +void MobileUnitGob::ExitClass(MobileUnitConsts *pmuntc) +{ + // Clear this out so MobileUnitGob derivatives can safely call MobileUnitGob::ExitClass + + delete s_panidVehicleExplosion; + s_panidVehicleExplosion = NULL; + + // Clear this out so MobileUnitGob derivatives can safely call MobileUnitGob::ExitClass + + delete g_panidMoveTarget; + g_panidMoveTarget = NULL; + + UnitGob::ExitClass(pmuntc); +} + +MobileUnitGob::MobileUnitGob(MobileUnitConsts *pmuntc) : UnitGob(pmuntc) +{ + m_ff |= kfGobMobileUnit; + m_tLastFire = 0; + m_gidTarget = kgidNull; + m_wptTarget.wx = kwxInvalid; // kxInvalid = no target location + m_cMoveStepsRemaining = 0; + m_txDst = 0; + m_tyDst = 0; + m_wcMoveDistPerUpdate = 0; + m_mua = kmuaNone; + m_muaPending = kmuaNone; + m_wfMunt = kfMuntReturnFire | kfMuntAttackEnemiesWhenGuarding | kfMuntAttackEnemiesWhenMoving; + m_stPending = kstReservedNull; + m_itptPath = 0; + m_ppathUnit = NULL; + m_ppathAvoid = NULL; + m_nSeqMoveAside = 0; + + // Just to be clear this Unit or its nearby allies hasn't been hit for awhile + + m_cupdLastHitOrNearbyAllyHit = -1000000; +} + +MobileUnitGob::~MobileUnitGob() +{ + delete m_ppathUnit; +} + +void MobileUnitGob::Activate() +{ + TCoord tx = TcFromWc(m_wx); + TCoord ty = TcFromWc(m_wy); + Assert(!IsTileReserved(tx, ty)); + ReserveTile(tx, ty, true); + UnitGob::Activate(); + ggobm.MoveGobBetweenAreas(m_gid, 0, ggobm.CalcAreaMask(tx, ty)); +} + +void MobileUnitGob::Deactivate() +{ + if (m_wfMunt & kfMuntDestinationReserved) { + Assert(IsTileReserved(m_txDst, m_tyDst)); + ReserveTile(m_txDst, m_tyDst, false); + } else { +#ifndef MP_DEBUG_SHAREDMEM +// DWM: We always hit it when a Processor containing a Bullpup is destroyed. +// We should fix that but in the meantime I need to release a DEBUG build +// that won't trip up on this case so I'm commenting it out for now. +// Assert(IsTileReserved(TcFromWc(m_wx), TcFromWc(m_wy))); +#endif + ReserveTile(TcFromWc(m_wx), TcFromWc(m_wy), false); + } + UnitGob::Deactivate(); + ggobm.MoveGobBetweenAreas(m_gid, ggobm.CalcAreaMask(TcFromWc(m_wx), TcFromWc(m_wy)), 0); +} + +word AggBitsFromAgg(int nAggressiveness) +{ + switch (nAggressiveness) { + case knAggressivenessCoward: + return kfMuntRunAwayWhenHit; + + case knAggressivenessSelfDefense: + return kfMuntReturnFire | kfMuntStayPut; + + case knAggressivenessDefender: + return kfMuntReturnFire | kfMuntAttackEnemiesWhenGuarding | kfMuntAttackEnemiesWhenMoving; + + case knAggressivenessPitbull: + return kfMuntReturnFire | kfMuntAttackEnemiesWhenGuarding | kfMuntAttackEnemiesWhenMoving | kfMuntChaseEnemies; + +// Implicit +// case knAggressivenessPacifist: +// break; // non-aggressive + } + + return 0; +} + +bool MobileUnitGob::Init(IniReader *pini, FindProp *pfind, const char *pszName) +{ + // UnitGob::Init(pini, ...) calls the overridden UnitGob::Init(wx, ...) below + + if (!UnitGob::Init(pini, pfind, pszName)) + return false; + + // Note that this MobileUnitGob has already been Activated by this point + + // Translate an aggressiveness type into the MobileUnit flags that actually + // determine its behaviour. + + int nAggressiveness; + char szAction[100]; + int cArgs = pini->GetPropertyValue(pfind, "%*d ,%*d ,%*d ,%*d ,%*d ,%*d, %d ,%a", &nAggressiveness, szAction); + if (cArgs > 0) + m_wfMunt = (m_wfMunt & ~kfMuntAggressivenessBits) | AggBitsFromAgg(nAggressiveness); + + // Only computer-controlled units respond to their initial Action + + if (m_pplr->GetFlags() & kfPlrComputer) { + if (cArgs > 1) + PerformAction(szAction); + + // Human-controlled units don't attack while executing a move command + + } else { + m_wfMunt &= ~kfMuntAttackEnemiesWhenMoving; + } + + return true; +} + +bool MobileUnitGob::Init(WCoord wx, WCoord wy, Player *pplr, fix fxHealth, dword ff, const char *pszName) +{ + if (!UnitGob::Init(wx, wy, pplr, fxHealth, ff, pszName)) + return false; + + // Center Unit within tile + + m_wx += kwcTileHalf; + m_wy += kwcTileHalf; + + Activate(); + + // Human-controlled units don't attack while executing a move command + + if (IsHumanOrGodControlled()) + m_wfMunt &= ~kfMuntAttackEnemiesWhenMoving; + + // Notify nearby enemy gobs that they might want to attack this gob + + NotifyEnemyNearby(); + + return true; +} + +#define knVerMobileUnitGobState 10 +bool MobileUnitGob::LoadState(Stream *pstm) +{ + byte nVer = pstm->ReadByte(); + if (nVer != knVerMobileUnitGobState) + return false; + m_dir = (Direction)pstm->ReadByte(); + m_dirNext = (Direction)pstm->ReadByte(); + m_gidTarget = pstm->ReadWord(); + m_tLastFire = gsim.GetTickCount() - pstm->ReadDword(); + pstm->Read(&m_msgPending, sizeof(m_msgPending)); + pstm->Read(&m_msgAction, sizeof(m_msgAction)); + m_cCountdown = pstm->ReadWord(); + m_cMoveStepsRemaining = pstm->ReadWord(); + m_tptChaseInitial.tx = pstm->ReadWord(); + m_tptChaseInitial.ty = pstm->ReadWord(); +#ifdef DRAW_PATHS + m_wxDst = pstm->ReadWord(); + m_wyDst = pstm->ReadWord(); +#endif + m_txDst = pstm->ReadWord(); + m_tyDst = pstm->ReadWord(); + m_wptTarget.wx = pstm->ReadWord(); + m_wptTarget.wy = pstm->ReadWord(); + if (pstm->ReadByte() != 0) { + m_ppathUnit = new Path; + if (m_ppathUnit == NULL) + return false; + if (!m_ppathUnit->LoadState(gsim.GetLevel()->GetTerrainMap(), pstm)) + return false; + m_itptPath = pstm->ReadWord(); + } + if (pstm->ReadByte() != 0) { + m_ppathAvoid = new Path; + if (m_ppathAvoid == NULL) + return false; + if (!m_ppathAvoid->LoadState(gsim.GetLevel()->GetTerrainMap(), pstm)) + return false; + } + m_mua = (MobileUnitAction)pstm->ReadByte(); + m_muaPending = (MobileUnitAction)pstm->ReadByte(); + m_wfMunt = pstm->ReadWord(); + m_stPending = (State)pstm->ReadByte(); + m_wptTargetCenter.wx = pstm->ReadWord(); + m_wptTargetCenter.wy = pstm->ReadWord(); + m_tcTargetRadius = pstm->ReadWord(); + m_wcMoveDistPerUpdate = pstm->ReadWord(); + m_cupdLastHitOrNearbyAllyHit = pstm->ReadDword(); + return UnitGob::LoadState(pstm); +} + +bool MobileUnitGob::SaveState(Stream *pstm) +{ + pstm->WriteByte(knVerMobileUnitGobState); + pstm->WriteByte(m_dir); + pstm->WriteByte(m_dirNext); + pstm->WriteWord(m_gidTarget); + pstm->WriteDword(gsim.GetTickCount() - m_tLastFire); + pstm->Write(&m_msgPending, sizeof(m_msgPending)); + pstm->Write(&m_msgAction, sizeof(m_msgAction)); + pstm->WriteWord(m_cCountdown); + pstm->WriteWord(m_cMoveStepsRemaining); + pstm->WriteWord(m_tptChaseInitial.tx); + pstm->WriteWord(m_tptChaseInitial.ty); +#ifdef DRAW_PATHS + pstm->WriteWord(m_wxDst); + pstm->WriteWord(m_wyDst); +#endif + pstm->WriteWord(m_txDst); + pstm->WriteWord(m_tyDst); + pstm->WriteWord(m_wptTarget.wx); + pstm->WriteWord(m_wptTarget.wy); + if (m_ppathUnit == NULL) { + pstm->WriteByte(0); + } else { + pstm->WriteByte(1); + m_ppathUnit->SaveState(pstm); + pstm->WriteWord(m_itptPath); + } + if (m_ppathAvoid == NULL) { + pstm->WriteByte(0); + } else { + pstm->WriteByte(1); + m_ppathAvoid->SaveState(pstm); + } + pstm->WriteByte(m_mua); + pstm->WriteByte(m_muaPending); + pstm->WriteWord(m_wfMunt); + pstm->WriteByte(m_stPending); + pstm->WriteWord(m_wptTargetCenter.wx); + pstm->WriteWord(m_wptTargetCenter.wy); + pstm->WriteWord(m_tcTargetRadius); + pstm->WriteWord(m_wcMoveDistPerUpdate); + pstm->WriteDword(m_cupdLastHitOrNearbyAllyHit); + return UnitGob::SaveState(pstm); +} + +void MobileUnitGob::PerformAction(char *szAction) +{ + int nUnitAction; + if (IniScanf(szAction, "%d", &nUnitAction) == 0) { + Assert(false); + return; + } + + switch (nUnitAction) { + case knMoveUnitAction: + { + int nArea; + if (IniScanf(szAction, "%*d ,%d", &nArea) == 0) { + Assert(false); + return; + } + TRect trc; + ggobm.GetAreaRect(nArea, &trc); + Point ptCenter; + trc.GetCenter(&ptCenter); + SendMoveAction(m_gid, WcFromTc(ptCenter.x), WcFromTc(ptCenter.y), 1, m_pmuntc->GetMoveDistPerUpdate()); + } + break; + + case knGuardUnitAction: + break; + + case knGuardVicinityUnitAction: + SendGuardVicinityAction(m_gid); + break; + + case knGuardAreaUnitAction: + { + int nArea; + if (IniScanf(szAction, "%*d ,%d", &nArea) == 0) { + Assert(false); + return; + } + SendGuardAreaAction(m_gid, nArea); + } + break; + + case knHuntEnemiesUnitAction: + { + UnitMask um; + if (IniScanf(szAction, "%*d ,%ld", &um) == 0) { + Assert(false); + return; + } + SendHuntEnemiesAction(m_gid, um); + } + break; + } +} + +#ifdef DRAW_PATHS +void MobileUnitGob::DrawPath(DibBitmap *pbm, WCoord wxViewOrigin, WCoord wyViewOrigin) +{ + if (m_ppathUnit != NULL) + m_ppathUnit->Draw(pbm, PcFromWc(wxViewOrigin), PcFromWc(wyViewOrigin), GetSide()); +} +#endif + +#ifdef DRAW_LINES +void MobileUnitGob::DrawTargetLine(DibBitmap *pbm, int xViewOrigin, int yViewOrigin) +{ + if (m_wptTarget.wx == kwxInvalid) + return; + + pbm->DrawLine(PcFromUwc(m_wx) - xViewOrigin, PcFromUwc(m_wy) - yViewOrigin, + PcFromUwc(m_wptTarget.wx) - xViewOrigin, PcFromUwc(m_wptTarget.wy) - yViewOrigin, + GetSideColor(m_pplr->GetSide())); +} +#endif + +bool MobileUnitGob::IsIdle() +{ + // UNDONE: introduce kstIdle? + return (m_wfMunt & kfMuntCommandPending) == 0 && m_st == kstGuard; +} + +// Handle rotating toward the target and limiting the firing rate + +bool MobileUnitGob::Fire(UnitGob *puntTarget, WCoord wx, WCoord wy, WCoord wdx, WCoord wdy) +{ + // Make sure we're facing the way we want to fire before we try to fire + + Direction dirFire = CalcDir(wdx, wdy); + if (m_dir != dirFire) { + m_dir = TurnToward(dirFire, m_dir); + SetAnimationStrip(&m_ani, m_pmuntc->anMovingStripIndices[m_dir]); + m_unvl.MinSkip(); + return false; + } + + // Firing rate is limited by ctFiringRate + + long t = gsim.GetTickCount(); + + long ctWait = m_pmuntc->ctFiringRate; + long ctRemaining = ctWait - (t - m_tLastFire); + if (ctRemaining > 0) { + m_unvl.MinSkip((ctRemaining + (kctUpdate / 2)) / kctUpdate - 1); + return false; + } + + m_tLastFire = t; + + // Play firing animation (start on frame 1 where the action is) + + StartAnimation(&m_ani, m_pmuntc->anFiringStripIndices[m_dir], 1, kfAniIgnoreFirstAdvance | kfAniResetWhenDone); + m_wfMunt |= kfMuntFiring; + gsmm.SendDelayedMsg(kmidFireComplete, m_ani.GetRemainingStripTime(), m_gid, m_gid); + + return true; +} + +void MobileUnitGob::Idle() +{ +} + +void MobileUnitGob::SetTarget(Gid gid, WCoord wx, WCoord wy, WCoord wxCenter, WCoord wyCenter, TCoord tcRadius, WCoord wcMoveDistPerUpdate) +{ + if (gid == kgidNull) { + // Send formed command + + Message msgT; + msgT.mid = kmidMoveCommand; + msgT.smidSender = kgidNull; + msgT.smidReceiver = m_gid; + msgT.MoveCommand.wptTarget.wx = wx; + msgT.MoveCommand.wptTarget.wy = wy; + msgT.MoveCommand.gidTarget = kgidNull; + msgT.MoveCommand.wptTargetCenter.wx = wxCenter; + msgT.MoveCommand.wptTargetCenter.wy = wyCenter; + msgT.MoveCommand.tcTargetRadius = tcRadius; + msgT.MoveCommand.wcMoveDistPerUpdate = wcMoveDistPerUpdate; + gcmdq.Enqueue(&msgT); + return; + } + + // If the target no longer exists, discard the command + + Gob *pgobTarget = ggobm.GetGob(gid); + if (pgobTarget == NULL) + return; + + // If the target is a Replicator move to its input + + if (pgobTarget->GetType() == kgtReplicator) { + pgobTarget->SetFlags(pgobTarget->GetFlags() | kfGobFlashing); + + // Send formed command + + ReplicatorGob *prep = (ReplicatorGob *)pgobTarget; + TPoint tpt; + prep->GetInputTilePosition(&tpt); + Message msgT; + msgT.mid = kmidMoveCommand; + msgT.smidSender = kgidNull; + msgT.smidReceiver = m_gid; + msgT.MoveCommand.wptTarget.wx = WcFromTc(tpt.tx) + kwcTileHalf; + msgT.MoveCommand.wptTarget.wy = WcFromTc(tpt.ty) + kwcTileHalf; + msgT.MoveCommand.gidTarget = kgidNull; + msgT.MoveCommand.wptTargetCenter.wx = msgT.MoveCommand.wptTarget.wx; + msgT.MoveCommand.wptTargetCenter.wy = msgT.MoveCommand.wptTarget.wy; + msgT.MoveCommand.tcTargetRadius = 0; + msgT.MoveCommand.wcMoveDistPerUpdate = wcMoveDistPerUpdate; + gcmdq.Enqueue(&msgT); + return; + } + + // Get target coords if none passed in + + if (wx == 0 && wy == 0) { + Assert(pgobTarget->GetFlags() & kfGobUnit); + UnitGob *puntTarget = (UnitGob *)pgobTarget; + WPoint wpt; + puntTarget->GetAttackPoint(&wpt); + wx = wpt.wx; + wy = wpt.wy; + } + + // Friend or Foe? No special action for friends + + if (IsAlly(pgobTarget->GetSide())) + return; + + // Foe -- attack! + // Flash the target Gob + + if (m_pplr == gpplrLocal) + pgobTarget->Flash(); + + // Queue attack command + + Message msgT; + msgT.mid = kmidAttackCommand; + msgT.smidSender = m_gid; + msgT.smidReceiver = m_gid; + msgT.AttackCommand.wptTarget.wx = wx; + msgT.AttackCommand.wptTarget.wy = wy; + msgT.AttackCommand.gidTarget = gid; + msgT.AttackCommand.wptTargetCenter.wx = wxCenter; + msgT.AttackCommand.wptTargetCenter.wy = wyCenter; + msgT.AttackCommand.tcTargetRadius = tcRadius; + msgT.AttackCommand.wcMoveDistPerUpdate = wcMoveDistPerUpdate; + gcmdq.Enqueue(&msgT); +} + +void SendAttackCommand(Gid gidReceiver, Gid gidTarget) +{ + // Bail if target is already gone + + Gob *pgobTarget = ggobm.GetGob(gidTarget); + if (pgobTarget == NULL) + return; + Assert(pgobTarget->GetFlags() & kfGobUnit); + UnitGob *puntTarget = (UnitGob *)pgobTarget; + + Message msgT; + msgT.mid = kmidAttackCommand; + msgT.smidSender = gidReceiver; + msgT.smidReceiver = gidReceiver; + puntTarget->GetAttackPoint(&msgT.AttackCommand.wptTarget); + msgT.AttackCommand.gidTarget = gidTarget; + msgT.AttackCommand.tcTargetRadius = 0; + msgT.AttackCommand.wptTargetCenter = msgT.AttackCommand.wptTarget; + msgT.AttackCommand.wcMoveDistPerUpdate = 0; + gsmm.SendMsg(&msgT); +} + +void MobileUnitGob::GetAttackPoint(WPoint *pwpt) +{ + // The attack point must be terrain accessible. + + if (m_wfMunt & kfMuntDestinationReserved) { + pwpt->wx = WcFromTc(m_txDst); + pwpt->wy = WcFromTc(m_tyDst); + } else { + pwpt->wx = m_wx; + pwpt->wy = m_wy; + } +} + +// UNDONE: so far behavior can be easily parameterized +// Parameters: +// target hit animation (piff) (optional) +// move animation (w/ table for 8 directions) +// fire animation (w/ table for 8 directions) +// idle animation (w/ table for 8 directions) +// death animation +// ShotGob (optional) -- NOTE: may handle target hit animation +// other stuff already in MobileUnitConsts + +// If a path is being followed we have to wait until a tile center is +// reached before acting on a new move command. +// Returns true if command is processed (new state is set) + +bool MobileUnitGob::PendOrProcessCommand(Message *pmsg, State stNew) +{ + if (!IsReadyForCommand()) { + m_msgPending = *pmsg; + m_wfMunt |= kfMuntCommandPending; + return false; + } else { + m_gidTarget = kgidNull; + SetState(stNew); + return true; + } +} + +bool MobileUnitGob::PendOrProcessAction(Message *pmsg, State stNew, MobileUnitAction mua) +{ + if (!IsReadyForCommand()) { + m_muaPending = mua; + m_msgPending = *pmsg; + m_wfMunt |= kfMuntCommandPending; + return false; + } else { + m_mua = mua; + m_gidTarget = kgidNull; + m_msgAction = *pmsg; + SetState(stNew); + return true; + } +} + +bool MobileUnitGob::IsTargetInRange() +{ + Gob *pgobTarget = ggobm.GetGob(m_gidTarget); + if (pgobTarget == NULL) + return false; + return IsGobWithinRange(pgobTarget, m_pmuntc->tcFiringRange); +} + +bool MobileUnitGob::IsStandingOnActivator() +{ + for (Gid gid = ggobm.GetFirstGid(TcFromWc(m_wx), TcFromWc(m_wy)); gid != kgidNull; gid = ggobm.GetNextGid(gid)) { + Gob *pgob = ggobm.GetGob(gid, false); + if (pgob == NULL) + continue; + if (pgob->GetType() == kgtActivator) + return true; + } + + return false; +} + +void MobileUnitGob::SetStatePendingFireComplete(State st) +{ + if (m_wfMunt & kfMuntFiring) { + m_stPending = st; + SetState(kstChangeStatePendingFireComplete); + } else { + SetState(st); + } +} + +void MobileUnitGob::ContinueActionPendingFireComplete() +{ + if (m_wfMunt & kfMuntFiring) { + SetState(kstContinueActionPendingFireComplete); + } else { + gsmm.SendMsg(&m_msgAction); + } +} + +bool MobileUnitGob::IsAttackPointWithinFiringRangeOfTarget(UnitGob *puntTarget) +{ + WPoint wptAttack; + puntTarget->GetAttackPoint(&wptAttack); + return IsTargetWithinRange(&wptAttack, puntTarget, m_pmuntc->tcFiringRange); +} + +// TUNE: + +const TCoord ktcVicinity = 5; +const long kcupdAggressivenessBoost = 63; // 5 seconds + +int MobileUnitGob::ProcessStateMachineMessage(State st, Message *pmsg) +{ +BeginStateMachine + OnEnter + m_dir = kdirS; + SetState(kstGuard); + + // Actions are sent by Trigger/UnitActions or the Overmind + + OnMsg(kmidMoveAction) + if (PendOrProcessAction(pmsg, kstMove, kmuaMove)) + goto lbMoveCommand; + + OnMsg(kmidAttackAction) + if (PendOrProcessAction(pmsg, kstAttack, kmuaAttack)) + goto lbAttackCommand; + + OnMsg(kmidGuardAction) + PendOrProcessAction(pmsg, kstGuard, kmuaGuard); + + OnMsg(kmidGuardVicinityAction) + PendOrProcessAction(pmsg, kstGuard, kmuaGuardVicinity); + + OnMsg(kmidGuardAreaAction) + PendOrProcessAction(pmsg, kstGuard, kmuaGuardArea); + + OnMsg(kmidHuntEnemiesAction) + PendOrProcessAction(pmsg, kstHuntEnemies, kmuaHuntEnemies); + + // Commands are the result of player interactions with the SimUI + + OnMsg(kmidMoveCommand) + if (PendOrProcessCommand(pmsg, kstMove)) { +lbMoveCommand: + // Stash move parameters to be picked up by the move state + // Adjust target x/y to point to the closest tile center. We like tile centers. + + m_wptTarget.wx = WcTrunc(pmsg->MoveCommand.wptTarget.wx) + kwcTileHalf; + m_wptTarget.wy = WcTrunc(pmsg->MoveCommand.wptTarget.wy) + kwcTileHalf; + m_wptTargetCenter.wx = WcTrunc(pmsg->MoveCommand.wptTargetCenter.wx) + kwcTileHalf; + m_wptTargetCenter.wy = WcTrunc(pmsg->MoveCommand.wptTargetCenter.wy) + kwcTileHalf; + m_tcTargetRadius = pmsg->MoveCommand.tcTargetRadius; + m_wcMoveDistPerUpdate = pmsg->MoveCommand.wcMoveDistPerUpdate; + } + + OnMsg(kmidAttackCommand) + if (PendOrProcessCommand(pmsg, kstAttack)) { +lbAttackCommand: + // stash attack parameters to be picked up by the attack state + + m_gidTarget = pmsg->AttackCommand.gidTarget; + m_wptTarget.wx = WcTrunc(pmsg->AttackCommand.wptTarget.wx) + kwcTileHalf; + m_wptTarget.wy = WcTrunc(pmsg->AttackCommand.wptTarget.wy) + kwcTileHalf; + m_wptTargetCenter.wx = WcTrunc(pmsg->AttackCommand.wptTargetCenter.wx) + kwcTileHalf; + m_wptTargetCenter.wy = WcTrunc(pmsg->AttackCommand.wptTargetCenter.wy) + kwcTileHalf; + m_tcTargetRadius = pmsg->AttackCommand.tcTargetRadius; + m_wcMoveDistPerUpdate = pmsg->AttackCommand.wcMoveDistPerUpdate; + } + + // Other messages are inter and intra-Unit communication, sometimes delayed + + OnMsg(kmidNearbyAllyHit) + m_cupdLastHitOrNearbyAllyHit = gsim.GetUpdateCount(); + + // Respond pretty much as if self was the one hit + + goto lbHitNoDamage; + + OnMsg(kmidHit) + m_cupdLastHitOrNearbyAllyHit = gsim.GetUpdateCount(); + + // apply damage + // NOTE: This scoping is for the benefit of gcc which is concerned about + // the above goto skipping the initialization of fxDamage + + { + fix fxDamage = itofx(pmsg->Hit.nDamage); + if (m_pplr->GetHandicap() & kfHcapIncreasedArmor) + fxDamage = (fix)mulfx(fxDamage, (itofx(knDecreasedDamagePercent) / 100)); + SetHealth(subfx(m_fxHealth, fxDamage)); + } + + if (m_fxHealth <= 0) { + Player *pplr = gplrm.GetPlayer(pmsg->Hit.sideAssailant); + pplr->IncEnemyMobileUnitsKilled(); + m_pplr->IncMobileUnitsLost(); + + SetState(kstDying); + + // Think about fighting back + + } else { + ShowDamageIndicator(); + +lbHitNoDamage: + // UNDONE: knAggressivenessCoward + if (m_wfMunt & kfMuntRunAwayWhenHit) { + + + // If we're supposed to return fire and we know who fired at us... + + } else if ((m_wfMunt & kfMuntReturnFire) && pmsg->Hit.gidAssailant != kgidNull) { + + // and we're not already attacking an in-range target... + + if (m_gidTarget == kgidNull || !IsTargetInRange()) { + + // and we're not on a human directed mission... + + if ((m_pplr->GetFlags() & kfPlrComputer) || (m_st == kstGuard)) { + + // Fight back! + + SendAttackCommand(m_gid, pmsg->Hit.gidAssailant); + } + } + } + } + + // Because this logic is reused by the kmidNearbyAllyHit handler + + if (pmsg->mid != kmidNearbyAllyHit) + NotifyNearbyAlliesOfHit(pmsg->Hit.gidAssailant); + + OnMsg(kmidEnemyNearby) + // Notification that an enemy is nearby + + RememberEnemyNearby(pmsg->EnemyNearby.gidEnemy); + + // Wake up and check out the enemy + + m_unvl.MinSkip(); + + OnMsg(kmidMoveWaitingNearby) + // We're waiting on a gob that has decided to either go into transition + // or stop Wake up and check what to do + + m_unvl.MinSkip(); + + OnMsg(kmidFireComplete) + m_wfMunt &= ~kfMuntFiring; + m_unvl.MinSkip(); + + OnMsg(kmidDelete) + Assert("Shouldn't receive kmidDelete when not in kstDying state"); + + //----------------------------------------------------------------------- + + State(kstGuard) + OnEnter + // Play idle animation + + StartAnimation(&m_ani, m_pmuntc->anIdleStripIndices[m_dir], 0, kfAniDone); + m_wptTarget.wx = kwxInvalid; + m_gidTarget = kgidNull; + m_cCountdown = GetIdleCountdown(); + if (m_wfMunt & kfMuntMoveWaitingNearby) + NotifyMoveWaitingNearby(m_wx, m_wy); + Assert(!(m_wfMunt & (kfMuntPathPending | kfMuntMoveWaitingNearby))); + + OnUpdate + // Note if an enemy is nearby the update interval is min-ed + // by the enemy nearby notification, causing this path + // to execute right away. Otherwise most of the time the + // state machine is sleeping while in guard state. + + // A command might have been pended while we waited for the + // firing animation to complete. + + if (m_wfMunt & kfMuntCommandPending && IsReadyForCommand()) { + m_wfMunt &= ~kfMuntCommandPending; + gsmm.SendMsg(&m_msgPending); + return knHandled; + } + + // if enemy in range and we're not already firing at something + + if ((m_wfMunt & (kfMuntAttackEnemiesWhenGuarding | kfMuntFiring)) == kfMuntAttackEnemiesWhenGuarding) { + TCoord tcSightRange; + if (m_mua == kmuaGuardVicinity) + tcSightRange = ktcVicinity; + else + tcSightRange = m_pmuntc->tcFiringRange; + UnitGob *puntTarget = FindEnemyNearby(tcSightRange); + if (puntTarget != NULL) { + if (m_pplr->GetFlags() & kfPlrComputer) { + puntTarget->GetAttackPoint(&m_wptTarget); + m_gidTarget = puntTarget->GetId(); + m_wptTargetCenter = m_wptTarget; + m_tcTargetRadius = 0; + m_wcMoveDistPerUpdate = m_pmuntc->GetMoveDistPerUpdate(); + SetStatePendingFireComplete(kstAttack); + + // Pretend we're hit so nearby units will come help + + NotifyNearbyAlliesOfHit(m_gidTarget); + return knHandled; + } + + // Play firing animation + + puntTarget->GetCenter(&m_wptTarget); + Fire(puntTarget, m_wptTarget.wx, m_wptTarget.wy, m_wptTarget.wx - m_wx, m_wptTarget.wy - m_wy); + m_cCountdown = GetIdleCountdown(); + + // Pretend we're hit so nearby units will come help + + NotifyNearbyAlliesOfHit(puntTarget->GetId()); + + } else if (m_mua == kmuaGuardArea) { + // Find a valid target + + UnitGob *puntTarget = FindValidTargetInArea(m_msgAction.GuardAreaCommand.nArea); + if (puntTarget != NULL) { + puntTarget->GetAttackPoint(&m_wptTarget); + m_gidTarget = puntTarget->GetId(); + m_wptTargetCenter = m_wptTarget; + m_tcTargetRadius = 0; + m_wcMoveDistPerUpdate = m_pmuntc->GetMoveDistPerUpdate(); + SetStatePendingFireComplete(kstAttack); + return knHandled; + } + + } else { + m_wptTarget.wx = kwxInvalid; + m_gidTarget = kgidNull; + } + } + + // Animate (idle or firing) + + AdvanceAnimation(&m_ani); + + m_cCountdown -= m_unvl.GetUpdateCount(); + if (m_cCountdown < 0) { + Idle(); + m_cCountdown = GetIdleCountdown(); + } + m_unvl.MinSkip(m_cCountdown); + + // Handle flashing + + DefUpdate(); + + //----------------------------------------------------------------------- + + State(kstMove) + OnEnter + MoveEnter(); + + OnExit + MoveExit(); + + OnUpdate + if (!(m_wfMunt & kfMuntCommandPending) && !InTransition()) { + + // If the Unit is sufficiently aggressive and sees an enemy + // unit it should attack it. + + if (m_wfMunt & kfMuntAttackEnemiesWhenMoving) { + UnitGob *puntTarget = FindEnemyNearby(m_pmuntc->tcFiringRange); + if (puntTarget != NULL) { + m_gidTarget = puntTarget->GetId(); + puntTarget->GetAttackPoint(&m_wptTarget); + m_wptTargetCenter = m_wptTarget; + m_tcTargetRadius = 0; + m_wcMoveDistPerUpdate = m_pmuntc->GetMoveDistPerUpdate(); + SetState(kstAttack); + return knHandled; + } + } + } + + switch (MoveUpdate()) { + case knMoveTargetReached: + if (m_mua == kmuaMove) + m_mua = kmuaNone; // Move action complete + + SetState(kstGuard); + break; + + case knMoveStuck: + // UNDONE: what to do? Doing nothing means we repath every + // Update (jogging in place) until a valid path can be determined. + + // For now, give up and return to Guard state + + if (m_mua == kmuaMove) + m_mua = kmuaNone; // Move action complete + + SetState(kstGuard); + break; + } + + //----------------------------------------------------------------------- + + State(kstAttack) + OnExit + // Send notifications to any gobs waiting on this gob to get out + // of attack state + + if (m_wfMunt & kfMuntMoveWaitingNearby) + NotifyMoveWaitingNearby(m_wx, m_wy); + + OnUpdate + Assert(m_gidTarget != kgidNull); + + // A command might have been pended while we waited for the + // firing animation to complete. + + if (m_wfMunt & kfMuntCommandPending && IsReadyForCommand()) { + m_wfMunt &= ~kfMuntCommandPending; + gsmm.SendMsg(&m_msgPending); + return knHandled; + } + + // if enemy dead/gone or taken over, go to guard mode + + UnitGob *puntTarget = (UnitGob *)ggobm.GetGob(m_gidTarget); + if (puntTarget == NULL || !IsValidTarget(puntTarget)) { + + // If an Attack action is being carried out and successfully + // completed, indicate that it is done. + + if (m_mua == kmuaAttack && m_gidTarget == m_msgAction.AttackCommand.gidTarget) { + m_mua = kmuaNone; // Action complete + + // If some other action was interrupted to attack, return to it + + } else if (m_mua != kmuaNone) { + m_gidTarget = kgidNull; + ContinueActionPendingFireComplete(); + return knHandled; + } + + m_gidTarget = kgidNull; + SetStatePendingFireComplete(kstGuard); + + } else { + // if enemy not in range think about chasing it + + if (!IsGobWithinRange(puntTarget, m_pmuntc->tcFiringRange)) { + // Test actions and situations under which it is a good idea to chase + + if (!(m_wfMunt & kfMuntStayPut) && (m_mua != kmuaNone || !IsStandingOnActivator()) && + (m_wfMunt & kfMuntChaseEnemies || + (gsim.GetUpdateCount() - m_cupdLastHitOrNearbyAllyHit <= kcupdAggressivenessBoost) || + (m_mua == kmuaAttack && m_gidTarget == m_msgAction.AttackCommand.gidTarget) || + (m_mua == kmuaGuardArea && ggobm.IsGobWithinArea(puntTarget, m_msgAction.GuardAreaCommand.nArea)) || + (m_mua == kmuaGuardVicinity || m_mua == kmuaHuntEnemies) || + IsHumanOrGodControlled()) && IsAttackPointWithinFiringRangeOfTarget(puntTarget)) { + + SetStatePendingFireComplete(kstChase); + + // For all other situations we return to the action in-progress + + } else if (m_mua != kmuaNone) { + ContinueActionPendingFireComplete(); + return knHandled; + + // Or if no action, just fall back to guard state + + } else { + SetStatePendingFireComplete(kstGuard); + } + } else { + // Check for assumptions + + Assert(m_ppathUnit == NULL); + Assert(!IsMoveWaiting()); + + // Otherwise, fire at the enemy + + if (!(m_wfMunt & kfMuntFiring)) { + puntTarget->GetAttackPoint(&m_wptTarget); + WPoint wptFire; + puntTarget->GetCenter(&wptFire); + Fire(puntTarget, wptFire.wx, wptFire.wy, wptFire.wx - m_wx, wptFire.wy - m_wy); + } + } + } + + AdvanceAnimation(&m_ani); + + // Handle flashing + + DefUpdate(); + + //----------------------------------------------------------------------- + + State(kstChase) + OnEnter + MoveEnter(); + + // Remember where we found the target last. If it moves position we'll repath + + Gob *pgobTarget = ggobm.GetGob(m_gidTarget); + if (pgobTarget != NULL) { + Assert(pgobTarget->GetFlags() & kfGobUnit); + UnitGob *puntTarget = (UnitGob *)pgobTarget; + WPoint wptTarget; + puntTarget->GetAttackPoint(&wptTarget); + m_tptChaseInitial.tx = TcFromWc(wptTarget.wx); + m_tptChaseInitial.ty = TcFromWc(wptTarget.wy); + } + + OnExit + MoveExit(); + + OnUpdate + if (IsReadyForCommand()) { + if (m_wfMunt & kfMuntCommandPending) { + m_wfMunt &= ~kfMuntCommandPending; + gsmm.SendMsg(&m_msgPending); + return knHandled; + } + + // If the Unit is executing the AttackAction and is sufficiently + // aggressive and sees an enemy unit it should attack it. + + if (m_mua == kmuaAttack) { + if (m_wfMunt & kfMuntAttackEnemiesWhenMoving) { + UnitGob *puntTarget = FindEnemyNearby(m_pmuntc->tcFiringRange); + if (puntTarget != NULL) { + m_gidTarget = puntTarget->GetId(); + puntTarget->GetAttackPoint(&m_wptTarget); + SetState(kstAttack); + return knHandled; + } + } + } + + // Verify the target still exists + + Gob *pgobTarget = ggobm.GetGob(m_gidTarget); + if (pgobTarget == NULL || !IsValidTarget(pgobTarget)) { + + // If some other action was interrupted to attack/chase, return to it + + if (m_mua != kmuaNone) { + m_gidTarget = kgidNull; + gsmm.SendMsg(&m_msgAction); + return knHandled; + } + + m_gidTarget = kgidNull; + SetState(kstGuard); + return knHandled; + } + + // if enemy is in range, drop back into Attack mode + + if (IsGobWithinRange(pgobTarget, m_pmuntc->tcFiringRange)) { + SetState(kstAttack); + return knHandled; + + } else if (!(m_wfMunt & kfMuntChaseEnemies) && !IsHumanOrGodControlled()) { + + // If we're trying to guard an area and the enemy has left it let it go. + + if (m_mua == kmuaGuardArea) { + if (!ggobm.IsGobWithinArea(pgobTarget, m_msgAction.GuardAreaCommand.nArea)) { + SetState(kstGuard); + return knHandled; + } + + // If we're trying to guard an expanded radius and the enemy has left it let it go. + + } else if (m_mua == kmuaGuardVicinity) { + if (!IsGobWithinRange(pgobTarget, ktcVicinity) && gsim.GetUpdateCount() - m_cupdLastHitOrNearbyAllyHit > kcupdAggressivenessBoost) { + SetState(kstGuard); + return knHandled; + } + + // These are inherently chase modes. Stick with it. + + } else if (m_mua == kmuaHuntEnemies || m_mua == kmuaAttack) { + + // If we're not acting under an aggressiveness boost anymore drop back to the + // prior action or Guard + + } else if (gsim.GetUpdateCount() - m_cupdLastHitOrNearbyAllyHit > kcupdAggressivenessBoost) { + if (m_mua != kmuaNone) + gsmm.SendMsg(&m_msgAction); + else + SetState(kstGuard); + return knHandled; + } + } + + // If the target moved, force a new path to get calced + + Assert(pgobTarget->GetFlags() & kfGobUnit); + UnitGob *puntTarget = (UnitGob *)pgobTarget; + WPoint wptTarget; + puntTarget->GetAttackPoint(&wptTarget); + TCoord tx = TcFromWc(wptTarget.wx); + TCoord ty = TcFromWc(wptTarget.wy); + if (tx != m_tptChaseInitial.tx || ty != m_tptChaseInitial.ty) { + m_tptChaseInitial.tx = tx; + m_tptChaseInitial.ty = ty; + m_wptTarget = wptTarget; + MoveExit(); + } + } + + switch (MoveUpdate()) { + case knMoveTargetReached: + SetState(kstAttack); + return knHandled; + + case knMoveStuck: + // Next few updates, use the target gob's center + { + Gob *pgobTarget = ggobm.GetGob(m_gidTarget); + Assert(pgobTarget->GetFlags() & kfGobUnit); + UnitGob *puntTarget = (UnitGob *)pgobTarget; + if (puntTarget != NULL) { + WPoint wptTarget; + puntTarget->GetAttackPoint(&wptTarget); + if (WcTrunc(wptTarget.wx) != WcTrunc(m_wptTarget.wx) || WcTrunc(wptTarget.wy) != WcTrunc(m_wptTarget.wy)) { + m_wptTarget = wptTarget; + m_unvl.MinSkip(12); + break; + } + } + } + + // UNDONE: Put in waiting code so a new path doesn't get searched + // every update + + m_gidTarget = kgidNull; + +#if 0 +// Causes looping problems (kstChase->kstAttack->kstChase). Plus "stuck" cases happen less often, so try +// taking this out and revert to guard mode to see how it feels. + + // Return to any action in progress + + if (m_mua != kmuaNone) { + gsmm.SendMsg(&m_msgAction); + return knHandled; + } +#endif + + // UNDONE: Perhaps in this case try searching around waiting gobs. + + SetState(kstGuard); + return knHandled; + } + + //----------------------------------------------------------------------- + // Hunting units sit idle if they can't find a target and won't do + // anything except return fire (same as knAggressivenessSelfDefense) + + State(kstHuntEnemies) + OnEnter + // Play idle animation + + StartAnimation(&m_ani, m_pmuntc->anIdleStripIndices[m_dir], 0, kfAniDone); + m_wptTarget.wx = kwxInvalid; + m_gidTarget = kgidNull; + m_cCountdown = GetIdleCountdown(); + + OnUpdate + Gob *pgobTarget = ggobm.GetGob(m_gidTarget); + if (pgobTarget == NULL || !IsValidTarget(pgobTarget)) { + int cpuntFound = 0; + Assert(gcbScratch >= kcpgobMax * sizeof(UnitGob *)); + UnitGob **apuntFound = (UnitGob **)gpbScratch; + + // UNDONE: only do this every few updates + // Find an enemy to attack + + Gob *pgob = ggobm.GetFirstGob(); + for (; pgob != NULL; pgob = ggobm.GetNextGob(pgob)) { + // Is it an active enemy that this unit type is allowed to attack? + + if (!IsValidTarget(pgob)) + continue; // no + + // Only Units pass the IsValidTarget test + + UnitGob *punt = (UnitGob *)pgob; + + // Is it one of the type of units we're supposed to hunt? + + if (!(punt->GetConsts()->um & m_msgAction.HuntEnemiesCommand.um)) + continue; + + apuntFound[cpuntFound++] = punt; + } + + if (cpuntFound > 0) { + UnitGob *punt = apuntFound[GetRandom() % cpuntFound]; + punt->GetAttackPoint(&m_wptTarget); + m_gidTarget = punt->GetId(); + m_wptTargetCenter = m_wptTarget; + m_tcTargetRadius = 0; + m_wcMoveDistPerUpdate = m_pmuntc->GetMoveDistPerUpdate(); + SetState(kstAttack); + return knHandled; + } + } + + AdvanceAnimation(&m_ani); // idle animation + + m_cCountdown -= m_unvl.GetUpdateCount(); + if (m_cCountdown < 0) { + Idle(); + m_cCountdown = GetIdleCountdown(); + } + m_unvl.MinSkip(m_cCountdown); + + // Handle flashing + + DefUpdate(); + + //----------------------------------------------------------------------- + + State(kstDying) + OnEnter + TRect trc; + GetTileRect(&trc); + + Deactivate(); + + // Redraw this part of minimap. It will skip inactive munts + + gpmm->RedrawTRect(&trc); + +#ifdef DEBUG + if (m_wfMunt & kfMuntDestinationReserved) { + Assert(!IsTileReserved(m_txDst, m_tyDst)); + } else { + Assert(!IsTileReserved(TcFromWc(m_wx), TcFromWc(m_wy))); + } +#endif + + m_ff ^= kfGobLayerDepthSorted | kfGobLayerSurfaceDecal; + + gsndm.PlaySfx(SfxFromCategory(m_pmuntc->sfxcDestroyed)); + + // remove corpse in .2 seconds + + gsmm.SendDelayedMsg(kmidDelete, 20, m_gid, m_gid); + + Gob *pgobExpl = CreateAnimGob(m_wx, m_wy, kfAnmDeleteWhenDone | kfAnmSmokeFireLayer, NULL, s_panidVehicleExplosion); + if (pgobExpl != NULL) + pgobExpl->SetOwner(m_pplr); + + OnUpdate + // Handle flashing + + DefUpdate(); + + OnMsg(kmidDelete) + Delete(); + return knDeleted; + + // Eat all other messages (e.g., kmidHit, kmidNearbyAllyHit) + + DiscardMsgs + + //----------------------------------------------------------------------- + + State(kstChangeStatePendingFireComplete) + OnUpdate + if (!(m_wfMunt & kfMuntFiring)) { + SetState(m_stPending); + m_stPending = kstReservedNull; + } + + // Will advance any ongoing firing animation + + DefUpdate(); + + State(kstContinueActionPendingFireComplete) + OnUpdate + if (!(m_wfMunt & kfMuntFiring)) { + gsmm.SendMsg(&m_msgAction); + return knHandled; + } + + // Will advance any ongoing firing animation + + DefUpdate(); + +EndStateMachine +} + +void MobileUnitGob::ReserveTile(TCoord tx, TCoord ty, bool fReserve) +{ + if (fReserve) { + gsim.GetLevel()->GetTerrainMap()->SetFlags(tx, ty, 1, 1, kbfMobileUnit); + } else { + gsim.GetLevel()->GetTerrainMap()->ClearFlags(tx, ty, 1, 1, kbfMobileUnit); + } +} + +bool MobileUnitGob::IsTileReserved(TCoord tx, TCoord ty) +{ + return gsim.GetLevel()->GetTerrainMap()->TestFlags(tx, ty, 1, 1, kbfMobileUnit); +} + +byte MobileUnitGob::GetTileFlags(TCoord tx, TCoord ty) +{ + byte bf; + bool f = gsim.GetLevel()->GetTerrainMap()->GetFlags(tx, ty, &bf); + if (!f) + bf = kbfStructure; + return bf; +} + +bool MobileUnitGob::InTransition() +{ + if (m_ppathUnit == NULL) + return false; + + return m_cMoveStepsRemaining != 0; +} + +bool MobileUnitGob::IsReadyForCommand() +{ + if (m_wfMunt & kfMuntFiring) + return false; + return !InTransition(); +} + +void MobileUnitGob::NotifyMoveWaitingNearby(WCoord wx, WCoord wy) +{ + Assert(m_wfMunt & kfMuntMoveWaitingNearby); + m_wfMunt &= ~kfMuntMoveWaitingNearby; + + // Look around our dst to see if there are units trying to get in to our dst tile + + TCoord txDst = TcFromWc(wx); + TCoord tyDst = TcFromWc(wy); + + TCoord ctx, cty; + ggobm.GetMapSize(&ctx, &cty); + + for (int n = 0; n < ARRAYSIZE(g_mpDirToDx); n++) { + // Get valid TCoord to check + + TCoord tx = txDst + g_mpDirToDx[n]; + TCoord ty = tyDst + g_mpDirToDy[n]; + if (tx < 0 || tx >= ctx) + continue; + if (ty < 0 || ty >= cty) + continue; + + // Check it for gobs that are waiting on this gob + + for (Gid gid = ggobm.GetFirstGid(tx, ty); gid != kgidNull; gid = ggobm.GetNextGid(gid)) { + // Any mobile units in this tile? + + MobileUnitGob *pgob = (MobileUnitGob *)ggobm.GetGob(gid); + if (pgob == NULL) + continue; + if (!(pgob->m_ff & kfGobMobileUnit)) + continue; + + // See if this mobile unit is in transition towards our desired tile + + if (pgob->m_txDst == txDst && pgob->m_tyDst == tyDst) { + if (pgob->IsMoveWaiting()) { + gsmm.SendMsg(kmidMoveWaitingNearby, pgob->GetId()); + } + } + } + } +} + +MobileUnitGob *MobileUnitGob::GetReservingUnit(TCoord tx, TCoord ty) +{ + // Need to enumerate all mobile units in this tile, there can be more than one + // in certain transition situations + + for (Gid gid = ggobm.GetFirstGid(tx, ty); gid != kgidNull; gid = ggobm.GetNextGid(gid)) { + // Any mobile units in this tile? + + MobileUnitGob *pmunt = (MobileUnitGob *)ggobm.GetGob(gid); + if (pmunt == NULL) + continue; + if (!(pmunt->m_ff & kfGobMobileUnit)) + continue; + + // It is the reserving gob if it either isn't in transition or + // is in transition to this tile + + if (!pmunt->InTransition() || (pmunt->m_txDst == tx && pmunt->m_tyDst == ty)) { + Assert(IsTileReserved(tx, ty)); + return pmunt; + } + } + + // Now look for units transitioning into this tile + + MobileUnitGob *pmunt = AnyTransitionsIntoTile(tx, ty, NULL); + Assert((pmunt != NULL) == IsTileReserved(tx, ty)); + return pmunt; +} + +MobileUnitGob *MobileUnitGob::AnyTransitionsIntoTile(TCoord txDst, TCoord tyDst, Gob *pgobIgnore) +{ + // Look around our dst to see if there are units trying to get in to our dst tile + + TCoord ctx, cty; + ggobm.GetMapSize(&ctx, &cty); + + for (int n = 0; n < ARRAYSIZE(g_mpDirToDx); n++) { + // Get valid TCoord to check + + TCoord tx = txDst + g_mpDirToDx[n]; + TCoord ty = tyDst + g_mpDirToDy[n]; + if (tx < 0 || tx >= ctx) + continue; + if (ty < 0 || ty >= cty) + continue; + + // Check it for gobs that are entering our dst + + for (Gid gid = ggobm.GetFirstGid(tx, ty); gid != kgidNull; gid = ggobm.GetNextGid(gid)) { + // Any mobile units in this tile? + + MobileUnitGob *pgob = (MobileUnitGob *)ggobm.GetGob(gid); + if (pgob == NULL || pgob == pgobIgnore) + continue; + if (!(pgob->m_ff & kfGobMobileUnit)) + continue; + + // See if this mobile unit is in transition towards our desired tile + + if (pgob->m_txDst == txDst && pgob->m_tyDst == tyDst) { + if (pgob->InTransition()) { + return pgob; + } + } + } + } + + return NULL; +} + +void MobileUnitGob::MoveEnter(bool fReset) +{ + // Catch movewait bits that are set but shouldn't be + + Assert(!IsMoveWaiting()); + + // Make sure we're not in transition + + Assert(!InTransition()); + StartAnimation(&m_ani, m_pmuntc->anMovingStripIndices[m_dir], 0, kfAniLoop); + + // These get reset when this move isn't aware of group behavior + + if (fReset) { + m_wptTargetCenter = m_wptTarget; + m_tcTargetRadius = 0; + } + if (m_wcMoveDistPerUpdate == 0) + m_wcMoveDistPerUpdate = m_pmuntc->GetMoveDistPerUpdate(); + + // If the target is in blocked area, walk back toward this unit to find the first free tile, + // in a straight line. + + TerrainMap *ptrmap = gsim.GetLevel()->GetTerrainMap(); + TCoord txTo = TcFromWc(m_wptTarget.wx); + TCoord tyTo = TcFromWc(m_wptTarget.wy); + TCoord txFree, tyFree; + if (ptrmap->IsBlocked(txTo, tyTo, 0)) { + if (ptrmap->FindFirstUnoccupied(txTo, tyTo, TcFromWc(m_wx), TcFromWc(m_wy), &txFree, &tyFree)) { + m_wptTarget.wx = WcFromTc(txFree) + kwcTileHalf; + m_wptTarget.wy = WcFromTc(tyFree) + kwcTileHalf; + } + } + + // Let movement code know that a path is pending for this gob so proper waiting semantics can be + // performed. + + m_wfMunt |= kfMuntPathPending; + m_wfMunt &= ~kfMuntStuck; +} + +void MobileUnitGob::MoveExit() +{ + m_wfMunt &= ~(kfMuntMoveWait | kfMuntPathPending); + delete m_ppathUnit; + m_ppathUnit = NULL; + delete m_ppathAvoid; + m_ppathAvoid = NULL; + + // Send notifications to any gobs waiting on this gob + + if (m_wfMunt & kfMuntMoveWaitingNearby) + NotifyMoveWaitingNearby(m_wx, m_wy); +} + +Path *MobileUnitGob::FindPath(TCoord txFrom, TCoord tyFrom, TCoord txTo, TCoord tyTo) +{ + TerrainMap *ptrmap = gsim.GetLevel()->GetTerrainMap(); + +#ifdef __CPU_68K + // See if we have a cached path that we can reuse - 68K only since this + // sucks + +#define ktcPathLengthReuse 35 +#define ktcPathEndPointReuse 7 + + word nDistT = ganSquared[abs(txFrom - txTo)] + ganSquared[abs(tyFrom - tyTo)]; + if (GetType() != kgtGalaxMiner && nDistT >= ganSquared[ktcPathLengthReuse]) { + for (int nPath = 0; nPath < s_cpathCached; nPath++) { + // Long enough to be worth it? + + Path *ppathT = s_apathCached[nPath]; + if (ppathT->GetCount() < ktcPathLengthReuse) + continue; + + // In the general vicinity of our start / end points + + TPoint tptStart; + ppathT->GetStartPoint(&tptStart); + TPoint tptEnd; + ppathT->GetPointRaw(ppathT->GetCount() - 1, &tptEnd); + word nDistStart = ganSquared[abs(tptStart.tx - txFrom)] + ganSquared[abs(tptStart.ty - tyFrom)]; + word nDistEnd = ganSquared[abs(tptEnd.tx - txTo)] + ganSquared[abs(tptEnd.ty - tyTo)]; + word nDistReuse = ganSquared[ktcPathEndPointReuse] * 2; + if (nDistStart > nDistReuse || nDistEnd > nDistReuse) + continue; + + // See if our closest approach is free of obstacles + + TPoint tptClosest; + if (ppathT->FindClosestPoint(txFrom, tyFrom, 0, 5, &tptClosest) == -1) + continue; + if (ptrmap->IsLineOccupied(txFrom, tyFrom, tptClosest.tx, tptClosest.ty, 0)) + continue; + + // Looking good so far. See if our closest end point is free of obstacles + + TPoint tptDestClosest; + int itptDestClosest = ppathT->FindClosestPoint(txTo, tyTo, 0, ppathT->GetCount(), &tptDestClosest); + if (itptDestClosest == -1) + continue; + if (ptrmap->IsLineOccupied(tptDestClosest.tx, tptDestClosest.ty, txTo, tyTo, 0)) + continue; + + // We're ready to use this path. First create a copy + + ppathT = ppathT->Clone(); + if (ppathT == NULL) + continue; + + // If the ending is the same, use it + + if (tptDestClosest.tx == txTo && tptDestClosest.ty == tyTo) + return ppathT; + + // Now lop off the end and amend it with our new dest + // If this doesn't work, use it as is + + if (!ppathT->TrimEnd(itptDestClosest)) + return ppathT; + + // Add our new ending. If we run into something, use the path as is + + Path *ppathAppend = ptrmap->FindLinePath(tptDestClosest.tx, tptDestClosest.ty, txTo, tyTo, kbfStructure); + if (ppathAppend == NULL) + return ppathT; + + // Append this to our ppath + + ppathT->Append(ppathAppend); + delete ppathAppend; + + // Done + + return ppathT; + } + } +#endif + + // No cached paths that satisfy our needs. Make a new one. + // If it's close first try an easy path + +#define ktcFastPathRangeMax 20 + Path *ppathT = NULL; + if (ganSquared[abs(txFrom - txTo)] + ganSquared[abs(tyFrom - tyTo)] < ganSquared[ktcFastPathRangeMax] * 2) + ppathT = ptrmap->FindLinePath(txFrom, tyFrom, txTo, tyTo, kbfStructure); + if (ppathT == NULL) + ppathT = ptrmap->FindPath(txFrom, tyFrom, txTo, tyTo, kbfStructure); + +#ifdef __CPU_68K + // Cache this path if it is long enough to be interesting + + if (ppathT != NULL) { + if (ppathT->GetCount() >= ktcPathLengthReuse) { + // Move the existing paths that we have down one + + Path *ppathCache = ppathT->Clone(); + if (ppathCache != NULL) { + if (s_cpathCached == ARRAYSIZE(s_apathCached)) { + delete s_apathCached[ARRAYSIZE(s_apathCached) - 1]; + } else { + s_cpathCached++; + } + memmove(&s_apathCached[1], &s_apathCached[0], (ARRAYSIZE(s_apathCached) - 1) * ELEMENTSIZE(s_apathCached)); + s_apathCached[0] = ppathCache; + } + } + } +#endif + + return ppathT; +} + +void MobileUnitGob::FreeCachedPaths() +{ + for (int nPath = 0; nPath < s_cpathCached; nPath++) + delete s_apathCached[nPath]; + s_cpathCached = NULL; +} + +bool MobileUnitGob::PrepPath(WCoord wxDst, WCoord wyDst) +{ + // Get a path. + + TCoord txFrom = TcFromWc(m_wx); + TCoord tyFrom = TcFromWc(m_wy); + TCoord txTo = TcFromWc(wxDst); + TCoord tyTo = TcFromWc(wyDst); + Path *ppathT = FindPath(txFrom, tyFrom, txTo, tyTo); + + // Prep unit path + + delete m_ppathAvoid; + m_ppathAvoid = NULL; + delete m_ppathUnit; + m_ppathUnit = ppathT; + m_itptPath = 0; + m_wfMunt &= ~kfMuntPathPending; + +#ifdef DRAW_PATHS + m_wxDst = m_wx; + m_wyDst = m_wy; +#endif + m_cMoveStepsRemaining = 0; + + return m_ppathUnit != NULL; +} + +int MobileUnitGob::MoveUpdate() +{ + // If a command is pending and now is a good time to handle it, do so. + + if ((m_wfMunt & kfMuntCommandPending) && IsReadyForCommand()) { + m_wfMunt &= ~kfMuntCommandPending; + gsmm.SendMsg(&m_msgPending); + return knMoveMoving; + } + + // Initialize a path if we haven't yet. Don't start moving on it yet so + // that all mobile units in a group move have paths. This establishes + // proper waiting behavior + + if (m_ppathUnit == NULL) { + // Check if already there + + if (CheckDestinationReached()) + return knMoveTargetReached; + + // No, so calc a path + + if (!PrepPath(m_wptTarget.wx, m_wptTarget.wy)) { +// Trace("0x%08lx: 0x%08lx @ %d,%d calc path failed", HostGetTickCount(), this, TcFromWc(m_wx), TcFromWc(m_wy)); + m_wfMunt |= kfMuntStuck; + return knMoveStuck; + } +// Trace("0x%08lx: 0x%08lx @ %d,%d calc path succeeded", HostGetTickCount(), this, TcFromWc(m_wx), TcFromWc(m_wy)); + } + + // We have a path. If we're not in transition, prepare the next transition + + if (!InTransition()) { +// Trace("0x%08lx: 0x%08lx @ %d,%d not in transition.", HostGetTickCount(), this, TcFromWc(m_wx), TcFromWc(m_wy)); + + // If the next transition could not be prepared, error out + + TPoint tptNext; + int nMoveResult; + if (!PrepareNextTransition(&tptNext, &nMoveResult)) { +#if 0 // def DEBUG + static char *s_aszMoveResult[] = { "knMoveMoving", "knMoveTargetReached", "knMoveStuck", "knMoveWaiting" }; + Trace("0x%08lx @ %d,%d returning %s", this, TcFromWc(m_wx), TcFromWc(m_wy), s_aszMoveResult[nMoveResult]); + if (nMoveResult == knMoveWaiting) + Trace(" (waiting on %d, %d, reserving munt: 0x%08lx)", m_txDst, m_tyDst, GetReservingUnit(m_txDst, m_tyDst)); +#endif + return nMoveResult; + } + + // Looks good, enter transition + +// Trace("0x%08lx @ %d,%d -> %d, %d entering transition.", this, TcFromWc(m_wx), TcFromWc(m_wy), tptNext.tx, tptNext.ty); + EnterTransition(&tptNext); + } + + // We're in transition; move through the transition + + ContinueTransition(); + return knMoveMoving; +} + +void MobileUnitGob::EnterTransition(TPoint *ptptNext) +{ + // Attempt to enter the next transition + + Assert(!InTransition()); + Assert(ptptNext->tx != TcFromWc(m_wx) || ptptNext->ty != TcFromWc(m_wy)); + + // Clear the replicator input spot. This gob isn't a viable replication candidate in transition. + + m_wfMunt &= ~kfMuntAtReplicatorInput; + + // Remember where this unit is going + + m_txDst = ptptNext->tx; + m_tyDst = ptptNext->ty; + + // Remember this for "draw paths" feature + +#ifdef DRAW_PATHS + m_wxDst = WcFromTc(m_txDst) + kwcTileHalf; + m_wyDst = WcFromTc(m_tyDst) + kwcTileHalf; +#endif + + // Unreserve current tile, reserve new tile + + TCoord txOld = TcFromWc(m_wx); + TCoord tyOld = TcFromWc(m_wy); + Assert(IsTileReserved(txOld, tyOld)); + ReserveTile(txOld, tyOld, false); + m_dirNext = DirectionFromLocations(txOld, tyOld, ptptNext->tx, ptptNext->ty); + Assert(m_dirNext != kdirInvalid); + + // Tile is assumed to be available; reserve it and start a transition + + Assert(!IsTileReserved(ptptNext->tx, ptptNext->ty)); + ReserveTile(ptptNext->tx, ptptNext->ty, true); + + // Mark that we've reserved the dest tile so it can be unreserved if + // the unit becomes deactivated. + + m_wfMunt |= kfMuntDestinationReserved; + + // Send notifications to any gobs waiting on this gob's tile to clear, which it + // just did. + + if (m_wfMunt & kfMuntMoveWaitingNearby) + NotifyMoveWaitingNearby(m_wx, m_wy); + + // Prepare the appropriate animation + + StartAnimation(&m_ani, m_pmuntc->anMovingStripIndices[m_dir], 0, kfAniLoop | kfAniIgnoreFirstAdvance); + + // Calc move steps to next tile. Adjust for diagonal. + // This unit is now "in transition". + + WCoord wcMoveDist = m_wcMoveDistPerUpdate; + if (m_dirNext & 1) + wcMoveDist = gawcDiagonalDist[wcMoveDist]; + m_cMoveStepsRemaining = gaDiv256byNWithRounding[wcMoveDist]; +} + +void MobileUnitGob::ContinueTransition() +{ + // We're in transition and simply moving to the next tile + + Assert(InTransition()); + + // Transition movement doesn't skip updates + + m_unvl.MinSkip(); + + // Make sure we're facing the way we want to go before we go + + if (m_dirNext != m_dir) { + m_dir = TurnToward(m_dirNext, m_dir); + StartAnimation(&m_ani, m_pmuntc->anMovingStripIndices[m_dir], 0, kfAniLoop | kfAniIgnoreFirstAdvance); + return; + } + + WCoord wxNew = m_wx; + WCoord wyNew = m_wy; + + m_cMoveStepsRemaining--; + if (m_cMoveStepsRemaining == 0) { + + // Hop to the exact center of the destination tile + + wxNew = WcTrunc(m_wx) + kwcTileHalf; + wyNew = WcTrunc(m_wy) + kwcTileHalf; + + // No longer in transition; we're at the destination. Clear this bit + // so we know which tile is reserved + + m_wfMunt &= ~kfMuntDestinationReserved; + } else { + WCoord wcMoveDist = m_wcMoveDistPerUpdate; + + // Are we moving diagonally? + + if (m_dir & 1) + wcMoveDist = gawcDiagonalDist[wcMoveDist]; + + int dx = g_mpDirToDx[m_dir]; + if (dx == -1) { + wxNew -= wcMoveDist; + } else if (dx == 1) { + wxNew += wcMoveDist; + } + + int dy = g_mpDirToDy[m_dir]; + if (dy == -1) { + wyNew -= wcMoveDist; + } else if (dy == 1) { + wyNew += wcMoveDist; + } + } + + // Move here + + SetPosition(wxNew, wyNew); + + // Animate the little fella running there. + + AdvanceAnimation(&m_ani); + + // Handle flashing, etc + + DefUpdate(); +} + +bool MobileUnitGob::IsMoveWaitCycle(MobileUnitGob *pmunt) +{ + // This routine detects move wait loops (units waiting on units waiting on + // units, etc). If there is a loop, this move wait isn't valid. + + // Quick outs. If nobody is waiting on this unit, then by implication this won't + // be a cycle. Likewise if the unit we want to wait on isn't waiting, then by + // implication this won't be a cycle. + + if (!(m_wfMunt & kfMuntMoveWaitingNearby)) + return false; + + // Special case: if pmunt is attacking then not a wait cycle since it + // isn't mobile. + + if (pmunt->m_st == kstAttack) + return false; + + // Start at pmunt and see if we loop back to the current unit + + int cCycle = 0; + MobileUnitGob *pmuntT = pmunt; + while (pmuntT != NULL) { + // If we've looped back to ourselves, this is a cycle + + if (pmuntT == this) + return true; + + // If this unit isn't move waiting, not a cycle + + if (!(pmuntT->m_wfMunt & kfMuntMoveWait)) + return false; + + // Cycle to who pmuntT is waiting on + // Because of evaulation order it is possible that a pmuntT is waiting on a + // tile that is not reserved because the reserving munt started a transition + // during this update cycle before pmuntT gob executed. This is a break in the cycle; + // whatever gob takes this tile eventually will recheck for cycles, so it is + // ok to assume this condition is not a cycle. + + pmuntT = GetReservingUnit(pmuntT->m_txDst, pmuntT->m_tyDst); + if (pmuntT == NULL) + return false; + + // Break out of we've looped too much. + + if (cCycle++ > 15) + return false; + } + + return false; +} + +bool MobileUnitGob::MoveWaitForUnit(MobileUnitGob *pmunt, TCoord tx, TCoord ty) +{ + // Invalid if this creates a cycle + + if (IsMoveWaitCycle(pmunt)) + return false; + + // This unit will "move wait". + + m_wfMunt |= kfMuntMoveWait; + m_txDst = tx; + m_tyDst = ty; + pmunt->m_wfMunt |= kfMuntMoveWaitingNearby; + return true; +} + +bool MobileUnitGob::CheckDestinationReached() +{ + // If we're where we want to be, we're done + + TCoord tx = TcFromWc(m_wx); + TCoord ty = TcFromWc(m_wy); + if (tx == TcFromWc(m_wptTarget.wx) && ty == TcFromWc(m_wptTarget.wy)) + return true; + + // Not at dest if we have no path + + if (m_ppathUnit == NULL) + return false; + + // See if we're "close enough" + + TCoord txTargetCenter = TcFromWc(m_wptTargetCenter.wx); + TCoord tyTargetCenter = TcFromWc(m_wptTargetCenter.wy); + int dtx = abs(tx - txTargetCenter); + if (dtx >= ARRAYSIZE(gmpDistFromDxy)) + return false; + int dty = abs(ty - tyTargetCenter); + if (dty >= ARRAYSIZE(gmpDistFromDxy)) + return false; + if (gmpDistFromDxy[dtx][dty] > m_tcTargetRadius) + return false; + + // We're close enough; need to check if there is terrain between this unit + // and the center. If so we're not at the dest. + + TerrainMap *ptrmap = gsim.GetLevel()->GetTerrainMap(); + if (ptrmap->IsLineOccupied(tx, ty, txTargetCenter, tyTargetCenter, 0)) + return false; + + return true; +} + +bool MobileUnitGob::PrepareNextTransition(TPoint *ptptNext, int *pnMoveResult) +{ + Assert(!InTransition()); + + // First clear move wait since we'll be making a fresh decision + + m_wfMunt &= ~kfMuntMoveWait; + + // Get the viable transition locations + + TPoint atpt[8]; + int ctpt = GetNextLocations(atpt); + if (ctpt == 0) { + // Check to see if we're at a replicator input point. If so move wait until the replicator + // takes over + + if (CheckReplicatorPoint()) { + m_wfMunt |= kfMuntAtReplicatorInput | kfMuntMoveWait; + *pnMoveResult = knMoveTargetReached; + return false; + } + + // No move candidates. Are we close enough to the target? + + if (CheckDestinationReached()) { + *pnMoveResult = knMoveTargetReached; + return false; + } + + // No such luck. We're stuck. + + m_wfMunt |= kfMuntStuck; + *pnMoveResult = knMoveStuck; + return false; + } + + // See if our first choice is clear; if so use it (common case) + + byte bf = GetTileFlags(atpt[0].tx, atpt[0].ty); + if (!(bf & (kbfStructure | kbfMobileUnit))) { + *ptptNext = atpt[0]; + m_unvl.MinSkip(); + *pnMoveResult = knMoveMoving; + return true; + } + + // Find reserving munts once + + MobileUnitGob *apmunt[8]; + byte abf[8]; + for (int itpt = 0; itpt < ctpt; itpt++) { + // Structure or mobile unit? + + abf[itpt] = GetTileFlags(atpt[itpt].tx, atpt[itpt].ty); + if (abf[itpt] & kbfMobileUnit) { + apmunt[itpt] = GetReservingUnit(atpt[itpt].tx, atpt[itpt].ty); +#ifdef DEBUG + if (apmunt[itpt] == NULL) { + Assert(); + } + if (apmunt[itpt] == this) { + Assert(); + } +#endif + //Assert(apmunt[itpt] != NULL && apmunt[itpt] != this); + } else { + apmunt[itpt] = NULL; + } + } + + // Check open tiles or move wait opportunities + + if (ContinueMoveWaiting(apmunt, abf, atpt, ctpt, ptptNext)) { + if (IsMoveWaiting()) { + *pnMoveResult = knMoveWaiting; + return false; + } + return true; + } + + // All the move locations are currently occupied. Let's see if we're close + // enough to the destination already. + + if (CheckDestinationReached()) { + *pnMoveResult = knMoveTargetReached; + return false; + } + + // Need to deal with blockers. + + if (HandleCollision(apmunt, abf, atpt, ctpt)) { + // If move waiting, go into wait mode + + if (IsMoveWaiting()) { + *pnMoveResult = knMoveWaiting; + return false; + } + + // Otherwise wait an update so that the new path options get + // re-evaluated + + m_unvl.MinSkip(); + *pnMoveResult = knMoveMoving; + return false; + } + + // Nothing! + + m_wfMunt |= kfMuntStuck; + *pnMoveResult = knMoveStuck; + return false; +} + +bool MobileUnitGob::HandleCollision(MobileUnitGob **apmunt, byte *abf, TPoint *atpt, int ctpt) +{ + // All locations are blocked. We need to know who we're colliding with + // Ask the standing blocker to move out of the way. This usually works + // if the unit isn't an enemy and isn't attacking + + int itpt; + for (itpt = 0; itpt < ctpt; itpt++) { + MobileUnitGob *pmunt = apmunt[itpt]; + if (pmunt == NULL) + continue; + if (!pmunt->IsMobile()) { + if (PlanMoveAsidePath(pmunt, atpt[itpt].tx, atpt[itpt].ty)) + return true; + } + } + + // Try to locally path around. Not important to check all + // units since the first path will work around the other choices. + + if (FindLocalAvoidPath(atpt[0].tx, atpt[0].ty)) + return true; + + // Find a friendly attacking unit to wait on + + for (itpt = 0; itpt < ctpt; itpt++) { + if (apmunt[itpt] == NULL) + continue; + if (apmunt[itpt]->m_st == kstAttack && apmunt[itpt]->IsAlly(GetSide())) + if (MoveWaitForUnit(apmunt[itpt], atpt[itpt].tx, atpt[itpt].ty)) + return true; + } + + return false; +} + +bool MobileUnitGob::ContinueMoveWaiting(MobileUnitGob **apmunt, byte *abf, TPoint *atpt, int ctpt, TPoint *ptptNext) +{ + int itpt; +#if 0 + for (itpt = 0; itpt < ctpt; itpt++) { + // If the dest is not reserved, take it + + byte bf = abf[itpt]; + if (!(bf & (kbfStructure | kbfMobileUnit))) { + *ptptNext = atpt[itpt]; + return true; + } + + // If not a mobile unit, can't move wait on it + + if (!(bf & kbfMobileUnit)) + continue; + + // Deprioritize moving waiting on a munt that is still in transition + + MobileUnitGob *pmunt = apmunt[itpt]; + if (!pmunt->InTransition() && pmunt->IsMobile()) { + if (MoveWaitForUnit(pmunt, atpt[itpt].tx, atpt[itpt].ty)) + return true; + } + } +#endif + + // Didn't find anything; try move waiting on the first possible unit + + for (itpt = 0; itpt < ctpt; itpt++) { + // If the dest is not reserved, take it + + byte bf = abf[itpt]; + if (!(bf & (kbfStructure | kbfMobileUnit))) { + *ptptNext = atpt[itpt]; + return true; + } + + // Deprioritize moving waiting on a munt that is still in transition + + MobileUnitGob *pmunt = apmunt[itpt]; + if (pmunt != NULL && pmunt->IsMobile()) { + if (MoveWaitForUnit(pmunt, atpt[itpt].tx, atpt[itpt].ty)) + return true; + } + } + + return false; +} + +bool MobileUnitGob::FindLocalAvoidPath(TCoord tx, TCoord ty) +{ + // Get the trackpoint for the current location + + TCoord txFrom = TcFromWc(m_wx); + TCoord tyFrom = TcFromWc(m_wy); + TrackPoint trkpStart; + if (!trkpStart.Init(m_ppathUnit, txFrom, tyFrom, m_itptPath, 3)) + return false; + Direction dirBlocked = DirectionFromLocations(txFrom, tyFrom, tx, ty); + + // Find a closer track point. + +#define kcStepsAvoid 15 + + // Try clockwise first + + TPoint atptCW[kcStepsAvoid]; + TrackPoint trkpCW; + int ctptCW = FindCloserTrackPoint(&trkpStart, dirBlocked, true, kcStepsAvoid, atptCW, &trkpCW); + + // Counter-clockwise + + TPoint atptCCW[kcStepsAvoid]; + TrackPoint trkpCCW; + int ctptCCW = FindCloserTrackPoint(&trkpStart, dirBlocked, false, kcStepsAvoid, atptCCW, &trkpCCW); + + // Which one to use? + + int ctptUse; + TPoint *atptUse; + bool fSuccess = true; + if (ctptCCW > 0) { + // CCW is valid. Is CW valid? If so compare length + + if (ctptCW > 0) { + // Both valid. Which is shorter? + + if (ctptCW < ctptCCW) { + ctptUse = ctptCW; + atptUse = atptCW; + } else { + ctptUse = ctptCCW; + atptUse = atptCCW; + } + + } else { + // CW is invalid, CCW is valid + + ctptUse = ctptCCW; + atptUse = atptCCW; + } + } else { + // CCW is invalid. If CW is valid; use it + + if (ctptCW > 0) { + ctptUse = ctptCW; + atptUse = atptCW; + } else { + // Both invalid + + return false; + } + } + + // Create a path from this and use it + // If our edge finder approach worked, use it + + TerrainMap *ptrmap = gsim.GetLevel()->GetTerrainMap(); + Path *ppath = NULL; + if (fSuccess) { + Assert(ctptUse != 0); + ppath = CreatePath(ptrmap, txFrom, tyFrom, atptUse, ctptUse); + if (ppath != NULL) { + // Success. This "avoid" path gets us further along the unit path. + + delete m_ppathAvoid; + m_ppathAvoid = ppath; + return true; + } + } + + return false; +} + +// Map the last direction travelled to next directions to scan + +Direction gmpDirLastDirScanStartCW[] = { kdirNE, kdirSE, kdirSE, kdirSW, kdirSW, kdirNW, kdirNW, kdirNE }; +Direction gmpDirLastDirScanEndCW[] = { (kdirNE - 5) & 7, (kdirSE - 5) & 7, (kdirSE - 5) & 7, (kdirSW - 5) & 7, (kdirSW - 5) & 7, (kdirNW - 5) & 7, (kdirNW - 5) & 7, (kdirNE - 5) & 7}; +Direction gmpDirLastDirScanStartCCW[] = { kdirNW, kdirNW, kdirNE, kdirNE, kdirSE, kdirSE, kdirSW, kdirSW }; +Direction gmpDirLastDirScanEndCCW[] = { (kdirNW + 5) & 7, (kdirNW + 5) & 7, (kdirNE + 5) & 7, (kdirNE + 5) & 7, (kdirSE + 5) & 7, (kdirSE + 5) & 7, (kdirSW + 5) & 7, (kdirSW + 5) & 7 }; + +int MobileUnitGob::FindCloserTrackPoint(TrackPoint *ptrkpStart, Direction dirBlocked, bool fClockwise, int ctpt, TPoint *atpt, TrackPoint *ptrkpNew) +{ + Direction *pmpDirLastDirScanStart; + Direction *pmpDirLastDirScanEnd; + Direction dirStart, dirEnd; + int ddir; + if (fClockwise) { + pmpDirLastDirScanStart = gmpDirLastDirScanStartCW; + pmpDirLastDirScanEnd = gmpDirLastDirScanEndCW; + dirStart = (dirBlocked - 1) & 7; + dirEnd = (dirStart - 7) & 7; + ddir = -1; + } else { + pmpDirLastDirScanStart = gmpDirLastDirScanStartCCW; + pmpDirLastDirScanEnd = gmpDirLastDirScanEndCCW; + dirStart = (dirBlocked + 1) & 7; + dirEnd = (dirStart + 7) & 7; + ddir = 1; + } + + TCoord ctx, cty; + ggobm.GetMapSize(&ctx, &cty); + TerrainMap *ptrmap = gsim.GetLevel()->GetTerrainMap(); + int itptClosestSearch = m_itptPath > 0 ? m_itptPath - 1 : 0; + ptrkpNew->InitFrom(ptrkpStart); + + int ctptLastGood = 0; + TCoord txFrom = TcFromWc(m_wx); + TCoord tyFrom = TcFromWc(m_wy); + TCoord txLast = txFrom; + TCoord tyLast = tyFrom; + for (int itpt = 0; itpt < ctpt; itpt++) { + Direction dirBest = kdirInvalid; + for (Direction dirNew = dirStart; dirNew != dirEnd; dirNew = (dirNew + ddir) & 7) { + // Valid TCoord? + + TCoord txNew = txLast + g_mpDirToDx[dirNew]; + TCoord tyNew = tyLast + g_mpDirToDy[dirNew]; + if (txNew < 0 || txNew >= ctx) + continue; + if (tyNew < 0 || tyNew >= cty) + continue; + + // Is tile blocked? Can't rely on moving or standing units getting + // out of the way because they may not be able to + + if (ptrmap->IsBlocked(txNew, tyNew, kbfStructure | kbfMobileUnit)) + continue; + + // Open tile, we're there + + dirBest = dirNew; + break; + } + + // Anything? + + if (dirBest == kdirInvalid) + return ctptLastGood; + + // If this point is further along the path than our last track point + // then remember this position + + atpt[itpt].tx = txLast + g_mpDirToDx[dirBest]; + atpt[itpt].ty = tyLast + g_mpDirToDy[dirBest]; + TrackPoint trkpT; + if (trkpT.Init(m_ppathUnit, atpt[itpt].tx, atpt[itpt].ty, itptClosestSearch, ctpt)) { + // Remember this as the last best progress point + + if (ptrkpNew->IsProgress(&trkpT)) { +#if 0 + // Remembers a "last best" progress point without knowing access to the unit + // path is possible from there + + ptrkpNew->InitFrom(&trkpT); + ctptLastGood = itpt + 1; +#else + // If the unit path is accessible from here, return this path + + TPoint tptClosest; + trkpT.GetClosestPoint(&tptClosest); + if (!ptrmap->IsLineOccupied(atpt[itpt].tx, atpt[itpt].ty, tptClosest.tx, tptClosest.ty, kbfStructure)) { + ptrkpNew->InitFrom(&trkpT); + return itpt + 1; + } +#endif + } + } + + // Go to next point in path + + txLast = atpt[itpt].tx; + tyLast = atpt[itpt].ty; + dirStart = pmpDirLastDirScanStart[dirBest]; + dirEnd = pmpDirLastDirScanEnd[dirBest]; + } + + return ctptLastGood; +} + +MoveDirections s_movd[50]; +word s_nSeqMoveAside; +bool MobileUnitGob::PlanMoveAsidePath(MobileUnitGob *pmuntBlocking, TCoord txTo, TCoord tyTo) +{ + // Enemies won't move aside + + if (!IsAlly(pmuntBlocking->GetSide())) + return false; + + // If attacking, won't move aside + + if (pmuntBlocking->m_st == kstAttack) + return false; + + // Calc the direction this unit is trying to move. In general we prioritize + // moving orthogonal to this direction, then backwards, then forwards. + + TCoord txFrom = TcFromWc(m_wx); + TCoord tyFrom = TcFromWc(m_wy); + Direction dirMover = DirectionFromLocations(txFrom, tyFrom, txTo, tyTo); + + // Before starting inc the sequence number; this'll make sure we don't + // visit gobs that have already been enumerated. + + s_nSeqMoveAside++; + + // Mark this unit so that it isn't asked to move aside (no backwards + // support at the moment) + + m_nSeqMoveAside = s_nSeqMoveAside; + + // Add the first one to the list + + MoveDirections *pmovd = s_movd; + pmuntBlocking->m_nSeqMoveAside = s_nSeqMoveAside; + if (!pmuntBlocking->GetMoveAsideDirections(dirMover, pmovd)) + return false; + + // Enumerate mobile units in a chain until an open space to move to is + // found. + + TCoord ctx, cty; + ggobm.GetMapSize(&ctx, &cty); + bool fFound = false; + while (true) { + // Enumerate the possible moving directions for this gob. + + TCoord txUnit = TcFromWc(pmovd->pmunt->m_wx); + TCoord tyUnit = TcFromWc(pmovd->pmunt->m_wy); + MoveDirections *pmovdT = pmovd; + for (; pmovd->idir < pmovd->cdir; pmovd->idir++) { + // Enumerate the directions in order provided, get that munt + + Direction dir = pmovd->adir[pmovd->idir]; + TCoord txNext = txUnit + g_mpDirToDx[dir]; + TCoord tyNext = tyUnit + g_mpDirToDy[dir]; + if (txNext < 0 || txNext >= ctx || tyNext < 0 || tyNext >= cty) + continue; + MobileUnitGob *pmuntNext = GetMobileUnitAt(txNext, tyNext); + + // If this space is open we're done and we have a path we + // can create + + if (pmuntNext == NULL) { + fFound = true; + break; + } + + // Is there room to add another? If not then continue. + + if ((pmovd + 1) - s_movd >= ARRAYSIZE(s_movd)) + continue; + + // Is it an ally? Enemies won't move aside + + if (!IsAlly(pmuntNext->GetSide())) + continue; + + // Attacking? If attacking, won't move aside + + if (pmuntNext->m_st == kstAttack) + continue; + + // Try to add it to the list. It'll get added if it can move + // in this direction. + + if (!pmuntNext->GetMoveAsideDirections(dirMover, pmovd + 1)) + continue; + + // Added ok; advance pmovd to this new entry, break out of + // loop to re-enter + + pmovd++; + break; + } + + // If another was added to the list, enumerate it + + if (pmovd != pmovdT) + continue; + + // If we found a path, we're done; break out + + if (fFound) + break; + + // Couldn't find a munt from this munt that would move aside. Go back + // to the previous munt and continue enumerating if there are any left + + if (pmovd == s_movd) + break; + pmovd--; + pmovd->idir++; + } + if (!fFound) + return false; + + + // Walk backwards through the move directions asking units to move aside + + for (MoveDirections *pmovdT = pmovd; pmovdT >= s_movd; pmovdT--) { + if (!pmovdT->pmunt->AcceptMoveAsideRequest(pmovdT->adir[pmovdT->idir])) + return false; + } + + // This unit needs to move wait on the column that is moving aside + + MoveWaitForUnit(pmuntBlocking, txTo, tyTo); + return true; +} + +MobileUnitGob *MobileUnitGob::GetMobileUnitAt(TCoord tx, TCoord ty) +{ + // Enum gids here + + for (Gid gid = ggobm.GetFirstGid(tx, ty); gid != kgidNull; gid = ggobm.GetNextGid(gid)) { + // Any mobile units in this tile? + + MobileUnitGob *pmunt = (MobileUnitGob *)ggobm.GetGob(gid); + if (pmunt == NULL) + continue; + if (!(pmunt->m_ff & kfGobMobileUnit)) + continue; + if (pmunt->IsMobile()) + continue; + if (pmunt->IsMoveWaiting()) + continue; + return pmunt; + } + + return NULL; +} + +// Move asides look directional independently in this order: sides first, backwards, then forwards + +Direction s_mpDirToDirsSorted[8][8] = { + { kdirW, kdirE, kdirSW, kdirSE, kdirNW, kdirNE, kdirS, kdirN, }, + { kdirNW, kdirSE, kdirW, kdirS, kdirN, kdirE, kdirSW, kdirNE, }, + { kdirN, kdirS, kdirNW, kdirSW, kdirNE, kdirSE, kdirW, kdirE, }, + { kdirNE, kdirSW, kdirN, kdirW, kdirE, kdirS, kdirNW, kdirSE, }, + { kdirE, kdirW, kdirNE, kdirNW, kdirSE, kdirSW, kdirN, kdirS, }, + { kdirSE, kdirNW, kdirE, kdirN, kdirS, kdirW, kdirNE, kdirSW, }, + { kdirS, kdirN, kdirSE, kdirNE, kdirSW, kdirNW, kdirE, kdirW, }, + { kdirSW, kdirNE, kdirS, kdirE, kdirW, kdirN, kdirSE, kdirNW, }, +}; + +// NOTE: Should this be overridable (virtual or a message)? + +bool MobileUnitGob::GetMoveAsideDirections(Direction dirMover, MoveDirections *pmovd) +{ + // Special hack for miners + + if (m_st == kstMinerRotateForEntry) + return false; + + // Initialize + + pmovd->pmunt = this; + pmovd->cdir = 0; + pmovd->idir = 0; + + // If this unit is being asked for move aside directions it has already been + // marked, assert if not true. + + Assert(m_nSeqMoveAside == s_nSeqMoveAside); + + // Fill in reasonable move directons into pmovd. + + TCoord ctx, cty; + ggobm.GetMapSize(&ctx, &cty); + Direction *adir = s_mpDirToDirsSorted[dirMover]; + TCoord txFrom = TcFromWc(m_wx); + TCoord tyFrom = TcFromWc(m_wy); + + // Enumerate directions + + TerrainMap *ptrmap = gsim.GetLevel()->GetTerrainMap(); + for (int idir = 0; idir < 8; idir++) { + // Get mobile unit at this location + + Direction dir = adir[idir]; + TCoord tx = txFrom + g_mpDirToDx[dir]; + TCoord ty = tyFrom + g_mpDirToDy[dir]; + if (tx < 0 || tx >= ctx || ty < 0 || ty >= cty) + continue; + + // If in attack state this new move aside position is only + // plausible if it is still in range + + // Is this location blocked by terrain? + + if (ptrmap->IsBlocked(tx, ty, kbfStructure)) + continue; + + // See what munt is there if any + + MobileUnitGob *pmunt = GetMobileUnitAt(tx, ty); + + // Prioritize empty + + if (pmunt == NULL) { + // Even if there is no munt there, the tile may be reserved by + // another munt that is in transition to this tile. If the tile is + // reserved, it is not a suitable destination + + if (IsTileReserved(tx, ty)) + continue; + pmovd->adir[0] = dir; + pmovd->cdir = 1; + return true; + } + + // Already been asked? If not it is fresh meat + + if (pmunt->m_nSeqMoveAside == s_nSeqMoveAside) + continue; + pmunt->m_nSeqMoveAside = s_nSeqMoveAside; + + // Mobile? Only non-mobile munts are candidates + + if (pmunt->IsMobile()) + continue; + + // Either a viable munt is there or nothing is there, in any case add + // it to the list + + pmovd->adir[pmovd->cdir++] = dir; + } + + return pmovd->cdir != 0; +} + +bool MobileUnitGob::AcceptMoveAsideRequest(Direction dir) +{ + // This unit has agreed to move aside in this direction and is now being + // asked to do so. + + // Two cases: either the unit is standing or move waiting. If it is + // standing then it can just move out of the way. + + TCoord txFrom = TcFromWc(m_wx); + TCoord tyFrom = TcFromWc(m_wy); + TCoord txTo = txFrom + g_mpDirToDx[dir]; + TCoord tyTo = tyFrom + g_mpDirToDy[dir]; + + // Unit not move waiting. + // Move in this direction. This has already been evaluated to be safe and + // sane. + + Assert(m_ppathUnit == NULL); + Assert(!IsMoveWaiting()); + + WCoord wxDst = WcFromTc(txTo) + kwcTileHalf; + WCoord wyDst = WcFromTc(tyTo) + kwcTileHalf; + + // Issue move command. These occur in order so earlier units can move wait + // on later units. We don't want overridden behavior (such as the miner + // mining galaxite) to interfere with move aside, so call + // MobileUnitGob::SetTarget directly. + + MobileUnitGob::SetTarget(kgidNull, WcFromTc(txTo) + kwcTileHalf, WcFromTc(tyTo) + kwcTileHalf); + return true; +} + +int MobileUnitGob::GetNextLocations(TPoint *atpt) +{ + // Use avoid path if it exists + + int cStepsFurtherStop = 3; + if (m_ppathAvoid != NULL) { + // Are we at the destination yet? + + TCoord txFrom = TcFromWc(m_wx); + TCoord tyFrom = TcFromWc(m_wy); + TPoint tptT; + bool fEnd = false; + if (m_ppathAvoid->GetPoint(m_ppathAvoid->GetCount() - 1, &tptT, 0)) { + if (tptT.tx == txFrom && tptT.ty == tyFrom) + fEnd = true; + } else { + fEnd = true; + } + + // If we still have more to avoid, do it + + if (!fEnd) { + int itptDummy = 0; + int ctpt = GetNextLocations2(m_ppathAvoid, &itptDummy, m_ppathAvoid->GetCount(), atpt); + if (ctpt != 0) + return ctpt; + } + + // Avoid path is done; delete it + + cStepsFurtherStop = _min(cStepsFurtherStop, m_ppathAvoid->GetCount()); + delete m_ppathAvoid; + m_ppathAvoid = NULL; + } + + // Use unit path + + int ctpt = GetNextLocations2(m_ppathUnit, &m_itptPath, cStepsFurtherStop, atpt); + if (ctpt == 0) + return 0; + if (m_itptPath > 1) + m_ppathUnit->SetCacheIndex(m_itptPath - 2); + return ctpt; +} + +int MobileUnitGob::GetNextLocations2(Path *ppath, int *pitptStart, int cStepsFurtherStop, TPoint *atpt) +{ + // The current progress is measured relative to the unit path. Then + // surrounding locations are profiled for making better progress. Positions + // with better progress are returned as move destinations. + + // Initialize a trackpoint + + TCoord txFrom = TcFromWc(m_wx); + TCoord tyFrom = TcFromWc(m_wy); + Assert(GetReservingUnit(txFrom, tyFrom) == this); + Assert(IsTileReserved(txFrom, tyFrom)); + TrackPoint trkpStart; + if (!trkpStart.Init(ppath, txFrom, tyFrom, *pitptStart, cStepsFurtherStop)) + return false; + TPoint tptClosest; + trkpStart.GetClosestPoint(&tptClosest); + int itptClosest = trkpStart.GetClosestPointIndex(); + *pitptStart = itptClosest; + + // If we're at the end we're done + + if (itptClosest == ppath->GetCount() - 1) { + if (tptClosest.tx == txFrom && tptClosest.ty == tyFrom) + return 0; + } + + // See if we're close to the next step already + + TPoint tptNext; + if (ppath->GetPoint(itptClosest + 1, &tptNext, kbfStructure | kbfMobileUnit)) { + // Have a next step and it is free of obstacles. If we're on the path + // then we're close to it, return it + + if (abs(tptNext.tx - txFrom) <= 1 && abs(tptNext.ty - tyFrom) <= 1) { + Assert(tptNext.tx != txFrom || tptNext.ty != tyFrom); + atpt[0] = tptNext; + return 1; + } + } + + // Maybe not actually on "next" but closer to closest. If so take it + + byte bf = GetTileFlags(tptClosest.tx, tptClosest.ty); + if (!(bf & (kbfStructure | kbfMobileUnit))) { + if (abs(tptClosest.tx - txFrom) <= 1 && abs(tptClosest.ty - tyFrom) <= 1) { + Assert(tptClosest.tx != txFrom || tptClosest.ty != tyFrom); + atpt[0] = tptClosest; + return 1; + } + } + + // Get after point of trackpoint + + TPoint tptAfter; + trkpStart.GetAfterPoint(&tptAfter); + + // Enumerate the surrounding tiles and measure each for progress along the + // unit path. + + int itptClosestMeasure = (itptClosest == 0 ? 0 : itptClosest - 1); + TrackPoint atrkp[8]; + int ctpt = 0; + TCoord ctx, cty; + ggobm.GetMapSize(&ctx, &cty); + TerrainMap *ptrmap = gsim.GetLevel()->GetTerrainMap(); + for (Direction dir = 0; dir < 8; dir++) { + TCoord tx = txFrom + g_mpDirToDx[dir]; + TCoord ty = tyFrom + g_mpDirToDy[dir]; + if (tx < 0 || tx >= ctx || ty < 0 || ty >= cty) + continue; + if (ptrmap->IsBlocked(tx, ty, 0)) + continue; + atrkp[ctpt].Init(ppath, tx, ty, itptClosestMeasure, 1); + if (trkpStart.IsProgress(&atrkp[ctpt])) { + // If blocked by terrain, not valid progress + + if (ptrmap->IsLineOccupied(tx, ty, tptAfter.tx, tptAfter.ty, 0)) + continue; + + // Looks like a good candidate + + ctpt++; + } + } + + // If there are no unblocked positions, take the next one even if it is + // blocked and callers will try and move around it. + + if (ctpt == 0) { + for (Direction dir = 0; dir < 8; dir++) { + TCoord tx = txFrom + g_mpDirToDx[dir]; + TCoord ty = tyFrom + g_mpDirToDy[dir]; + if (tx < 0 || tx >= ctx || ty < 0 || ty >= cty) + continue; + atrkp[0].Init(ppath, tx, ty, itptClosestMeasure, 1); + if (trkpStart.IsProgress(&atrkp[0])) { + atpt[0].tx = tx; + atpt[0].ty = ty; + return 1; + } + } + } + + // Sort the candidates. + + int an[8]; + int i; + for (i = 0; i < ctpt; i++) + an[i] = i; + for (i = ctpt - 1; i >= 0; i--) { + for (int j = 1; j <= i; j++) { + if (atrkp[an[j - 1]].IsBetterSort(&atrkp[an[j]])) { + int n = an[j - 1]; + an[j - 1] = an[j]; + an[j] = n; + } + } + } + for (i = 0; i < ctpt; i++) + atrkp[an[i]].GetInitialPoint(&atpt[i]); + + // Done + + return ctpt; +} + +bool MobileUnitGob::IsMobile() +{ + if (m_ppathUnit == NULL && !(m_wfMunt & (kfMuntPathPending | kfMuntAtReplicatorInput))) + return false; + return true; +} + +bool MobileUnitGob::CheckReplicatorPoint() +{ + TCoord tx = TcFromWc(m_wx); + TCoord ty = TcFromWc(m_wy); + int cReplicators = ReplicatorGob::GetReplicatorCount(); + for (int n = 0; n < cReplicators; n++) { + TPoint tptReplicator; + ReplicatorGob::GetReplicatorInputPoint(n, &tptReplicator); + if (tptReplicator.tx == tx && tptReplicator.ty == ty) + return true; + } + return false; +} + +void MobileUnitGob::SetPosition(WCoord wx, WCoord wy) +{ + // Check if nothing to do + + if (m_wx == wx && m_wy == wy) + return; + + // Notify waiters + + if (!InTransition()) { + if (m_wfMunt & kfMuntMoveWaitingNearby) + NotifyMoveWaitingNearby(m_wx, m_wy); + } + + // Mark for redraw if this gob has changed pixel locations + + if (PcFromWc(wx) != PcFromWc(m_wx) || PcFromWc(wy) != PcFromWc(m_wy)) + MarkRedraw(); + + // Be sure not to sleep on this important new development + + m_unvl.MinSkip(); + + // Keep GobMgr in the loop so it can maintain proper depth sorting + + bool fTileChange = ggobm.MoveGob(this, m_wx, m_wy, wx, wy); + WCoord wxOld = m_wx; + WCoord wyOld = m_wy; + m_wx = wx; + m_wy = wy; + + // Special tasks if we've moved between tiles + + if (fTileChange) { + // Reveal fog + + if (m_pplr == gpplrLocal) { + WCoord wxView, wyView; + gsim.GetViewPos(&wxView, &wyView); + FogMap *pfogm = gsim.GetLevel()->GetFogMap(); + TPoint tpt; + GetTilePosition(&tpt); + RevealPattern *prvlp = (RevealPattern *)(m_puntc->wf & kfUntcLargeDefog ? grvlpLarge : grvlp); + pfogm->Reveal(tpt.tx, tpt.ty, prvlp, gpupdSim, wxView, wyView); + } + + // Update minimap + + if (gpmm != NULL) { + TRect trc; + trc.left = wxOld < wx ? TcFromWc(wxOld) : TcFromWc(wx); + trc.top = wyOld < wy ? TcFromWc(wyOld) : TcFromWc(wy); + trc.right = (wxOld > wx ? TcFromWc(wxOld) : TcFromWc(wx)) + 1; + trc.bottom = (wyOld > wy ? TcFromWc(wyOld) : TcFromWc(wy)) + 1; + gpmm->RedrawTRect(&trc); + } + + // Notify enemy of this gob nearby + + NotifyEnemyNearby(); + + // Move between areas + + AreaMask amOld = ggobm.CalcAreaMask(TcFromWc(wxOld), TcFromWc(wyOld)); + AreaMask amNew = ggobm.CalcAreaMask(TcFromWc(m_wx), TcFromWc(m_wy)); + ggobm.MoveGobBetweenAreas(m_gid, amOld, amNew); + } +} + +UnitGob *MobileUnitGob::FindValidTargetInArea(int nArea) +{ + Enum enm; + while (true) { + Gob *puntTarget = ggobm.EnumGobsInArea(&enm, m_msgAction.GuardAreaCommand.nArea, ~m_pplr->GetAllies(), kumMobileUnits); + if (puntTarget == NULL) + return NULL; + if (IsValidTarget(puntTarget)) { + Assert(puntTarget->GetFlags() & kfGobUnit); + return (UnitGob *)puntTarget; + } + } +} + +#ifdef MP_DEBUG_SHAREDMEM +void MobileUnitGob::MPValidate() +{ + MPValidateGobMember(MobileUnitGob, m_dir); + MPValidateGobMember(MobileUnitGob, m_dirNext); + MPValidateGobMember(MobileUnitGob, m_gidTarget); + MPValidateGobMember(MobileUnitGob, m_tLastFire); + MPValidateGobMember(MobileUnitGob, m_msgPending); + MPValidateGobMember(MobileUnitGob, m_msgAction); + MPValidateGobMember(MobileUnitGob, m_cCountdown); + MPValidateGobMember(MobileUnitGob, m_cMoveStepsRemaining); + MPValidateGobMember(MobileUnitGob, m_tptChaseInitial); + MPValidateGobMember(MobileUnitGob, m_mua); + MPValidateGobMember(MobileUnitGob, m_muaPending); + MPValidateGobMember(MobileUnitGob, m_wfMunt); + MPValidateGobMember(MobileUnitGob, m_stPending); + MPValidateGobMember(MobileUnitGob, m_cupdLastHitOrNearbyAllyHit); + MPValidateGobMember(MobileUnitGob, m_wxDst); + MPValidateGobMember(MobileUnitGob, m_wyDst); + MPValidateGobMember(MobileUnitGob, m_txDst); + MPValidateGobMember(MobileUnitGob, m_tyDst); + MPValidateGobMember(MobileUnitGob, m_wptTarget); + MPValidateGobMember(MobileUnitGob, m_nSeqMoveAside); + MPValidateGobMember(MobileUnitGob, m_itptPath); + MPValidateGobMember(MobileUnitGob, m_wptTargetCenter); + MPValidateGobMember(MobileUnitGob, m_tcTargetRadius); + MPValidateGobMember(MobileUnitGob, m_wcMoveDistPerUpdate); + UnitGob::MPValidate(); +} +#endif + +} // namespace wi diff --git a/game/Multiplayer.cpp b/game/Multiplayer.cpp new file mode 100644 index 0000000..0007911 --- /dev/null +++ b/game/Multiplayer.cpp @@ -0,0 +1,82 @@ +#include "game/ht.h" +#include "game/Multiplayer.h" + +namespace wi { + +Transport *gptra; +char gszUsername[kcbPlayerName]; +char gszPassword[kcbPlayerName]; +char gszToken[kcbTokenMax]; +bool gfAnonymous; + +// Waiting UI helper + +TransportWaitingUI::TransportWaitingUI(int nWaitStr, bool fShow) +{ + m_psz = ""; + switch (nWaitStr) { + case knWaitStrConnectToHost: + m_psz = "CONTACTING HOST..."; + break; + + case knWaitStrClientDisconnecting: + m_psz = "CLIENT DISCONNECTING..."; + break; + + case knWaitStrDisconnectingClient: + m_psz = "DISCONNECTING CLIENT..."; + break; + + case knWaitStrDisconnectingClients: + m_psz = "DISCONNECTING CLIENTS..."; + break; + + case knWaitStrClosingTransport: + m_psz = "CLOSING TRANSPORT..."; + break; + + case knWaitStrBeginGameSearch: + m_psz = "BEGIN GAME SEARCH..."; + break; + + case knWaitStrAdvertisingGame: + m_psz = "ADVERTISING GAME..."; + break; + } + m_pfrm = NULL; + if (fShow) { + Show(); + } +} + +TransportWaitingUI::TransportWaitingUI(char *psz, bool fShow) +{ + m_pfrm = NULL; + m_psz = psz; + if (fShow) { + Show(); + } +} + +TransportWaitingUI::~TransportWaitingUI() +{ + Hide(); +} + +void TransportWaitingUI::Show() +{ + if (m_psz != NULL) { + m_pfrm = gpmfrmm->LoadForm(gpiniForms, kidfWaiting, new WaitForm(m_psz, false)); + m_pfrm->SetFlags(m_pfrm->GetFlags() & ~kfFrmTopMost); + gpmfrmm->DrawFrame(false); + } +} + +void TransportWaitingUI::Hide() +{ + delete m_pfrm; + m_pfrm = NULL; + gpmfrmm->DrawFrame(false); +} + +} // namespace wi diff --git a/game/Overmind.cpp b/game/Overmind.cpp new file mode 100644 index 0000000..6b12f6a --- /dev/null +++ b/game/Overmind.cpp @@ -0,0 +1,301 @@ +#include "ht.h" + +namespace wi { + +// +// OvermindGob implementation +// + +bool OvermindGob::InitClass(IniReader *pini) +{ + // No interesting global parameters have been defined for Overmind yet + return true; +} + +OvermindGob::OvermindGob() +{ + m_fEnabled = true; +} + +OvermindGob::~OvermindGob() +{ +} + +// +// Gob methods +// + +bool OvermindGob::Init(const char *pszName) +{ + m_wx = m_wy = 0; + m_ff |= kfGobStateMachine; + +#if 0 // The Overmind doesn't need to take sides. It will be given an owner Player (somewhere) + // For now, Overmind takes the side the player isn't on + + if (gpplrLocal->GetSide() == kside1) + m_side = kside2; + else + m_side = kside1; +#endif + + // Add the fresh Gob to the GobMgr. GobMgr::AddGob assigns this Gob a gid + + ggobm.AddGob(this); + + // Initialize the state machine by sending the first Enter msg + // This message is posted so it will kick in at the first Update after + // all the Gobs in the level have been instantiated. + + m_fInitializationComplete = false; + return true; +} + +bool OvermindGob::Init(IniReader *pini, FindProp *pfind, const char *pszName) +{ +#if 0 + int wf = 0; + char szBitmap[kcbFilename]; + int cArgs = pini->GetPropertyValue(pfind, "%*d ,%s,%d ,%d", szBitmap, &m_x, &m_y, &wf); + if (cArgs < 3) { + Assert("OvermindGob requires at least 3 valid initialization parameters"); + return false; + } + + m_wf |= (word)wf; +#endif + + return Init(pszName); +} + +bool OvermindGob::IsSavable() +{ + // False for the moment, but probably will be saved at some future point + + return false; +} + +GobType OvermindGob::GetType() +{ + return kgtOvermind; +} + +void OvermindGob::Draw(DibBitmap *pbm, int xViewOrigin, int yViewOrigin, int nLayer) +{ + // CONSIDER: maybe draw some DEBUG status "insight into the Overmind" +} + +// +// StateMachine methods +// + +#if defined(DEBUG_HELPERS) +char *OvermindGob::GetName() +{ + return "Overmind"; +} +#endif + +void OvermindGob::Toggle() +{ + m_fEnabled = !m_fEnabled; + m_unvl.MinSkip(); +} + +static UnitType s_autVtsBuildable[] = { kutLightTank, kutMediumTank, kutRocketVehicle, kutMachineGunVehicle, kutArtillery }; +static UnitType s_autHrcBuildable[] = { kutShortRangeInfantry, kutLongRangeInfantry }; + +int OvermindGob::ProcessStateMachineMessage(State st, Message *pmsg) +{ +BeginStateMachine + OnUpdate + if (!m_fInitializationComplete) { + m_fInitializationComplete = true; + m_cAttackCountdown = 0; + m_cBuildUnitCountdown = 0; + + // Search for any GalaxMiners owned by this Overmind and send them off to mine. + + for (Gob *pgobMiner = ggobm.GetFirstGob(); pgobMiner != NULL; pgobMiner = ggobm.GetNextGob(pgobMiner)) { + if (pgobMiner->GetOwner() != m_pplr) + continue; + + if (pgobMiner->GetType() != kgtGalaxMiner) + continue; + + // Send the Miner off to mine + // NOTE: We send a message rather than call MinerGob::Mine method directly + // so the state change will occur immediately. + + SendMineCommand(pgobMiner->GetId(), kwxInvalid, 0); + } + } + + if (!m_fEnabled) + return knHandled; + + // Every 10 seconds, pick a unit with offensive capabilities and send them + // after a strategic enemy structure + + m_cAttackCountdown -= m_unvl.GetUpdateCount(); + if (m_cAttackCountdown < 0) { +#ifdef STRESS + m_cAttackCountdown = gfStress ? 13 : (int)(12.5 * 10); +#else + m_cAttackCountdown = (int)(12.5 * 10); +#endif + + // Search the Gob list for a same-side Unit that doesn't have any commands pending and doesn't already + // have a target. + + for (Gob *pgobPawn = ggobm.GetFirstGob(); pgobPawn != NULL; pgobPawn = ggobm.GetNextGob(pgobPawn)) { + if (pgobPawn->GetOwner() != m_pplr) + continue; + + dword ff = pgobPawn->GetFlags(); + if ((ff & (kfGobActive | kfGobMobileUnit)) != (kfGobActive | kfGobMobileUnit)) + continue; + + MobileUnitGob *pmuntPawn = (MobileUnitGob *)pgobPawn; + if (!pmuntPawn->IsIdle()) + continue; + + if (pmuntPawn->HasAttackTarget()) + continue; + + // Don't bother attacking with a Miner! + + GobType gt = pmuntPawn->GetType(); + if (gt == kgtGalaxMiner || gt == kgtMobileHeadquarters) + continue; + + // OK, now find a good target to attack + + int cTargets = 0; + Gob *pgobTarget; + for (pgobTarget = ggobm.GetFirstGob(); pgobTarget != NULL; pgobTarget = ggobm.GetNextGob(pgobTarget)) { + if (pgobTarget->GetOwner() != m_pplr && +#ifdef STRESS + (((pgobTarget->GetFlags() & kfGobStructure) == kfGobStructure) || + (gfStress && (pgobTarget->GetFlags() & kfGobUnit))) +#else + ((pgobTarget->GetFlags() & kfGobStructure) == kfGobStructure) +#endif + && pmuntPawn->IsValidTarget(pgobTarget)) { + cTargets++; + } + } + + if (cTargets != 0) { + cTargets = GetRandom() % cTargets; + for (pgobTarget = ggobm.GetFirstGob(); pgobTarget != NULL; pgobTarget = ggobm.GetNextGob(pgobTarget)) { + if (pgobTarget->GetOwner() != m_pplr && + ((pgobTarget->GetFlags() & kfGobStructure) == + kfGobStructure) && pmuntPawn->IsValidTarget(pgobTarget)) { + if (cTargets-- == 0) + break; + } + } + } + + // If we can't find a good target, forget about it + + if (pgobTarget == NULL) + break; + Assert(pgobTarget->GetFlags() & kfGobUnit); + UnitGob *puntTarget = (UnitGob *)pgobTarget; + + // Order our pawn to attack the target + + Message msgT; + msgT.mid = kmidAttackAction; + msgT.smidSender = pmuntPawn->GetId(); + msgT.smidReceiver = msgT.smidSender; + puntTarget->GetAttackPoint(&msgT.AttackCommand.wptTarget); + msgT.AttackCommand.gidTarget = puntTarget->GetId(); + msgT.AttackCommand.wptTargetCenter = msgT.AttackCommand.wptTarget; + msgT.AttackCommand.tcTargetRadius = 0; + msgT.AttackCommand.wcMoveDistPerUpdate = 0; + gsmm.SendMsg(&msgT); + break; + } + } + m_unvl.MinSkip(m_cAttackCountdown); + + // Every 30 seconds, build a new offensive unit + + m_cBuildUnitCountdown -= m_unvl.GetUpdateCount(); + if (m_cBuildUnitCountdown < 0) { +#ifdef STRESS + m_cBuildUnitCountdown = gfStress ? /*(int)(12.5 * 2)*/ 0 /* More! */ : (int)(12.5 * 30); +#else + m_cBuildUnitCountdown = (int)(12.5 * 30); +#endif + + // Randomly choose between using an HRC or VTS to build a unit + // and choose a random offensive unit for it to build. + + GobType gtBuilder; + UnitType utBuildee; + if ((GetRandom() & 1) == 1) { + gtBuilder = kgtVehicleTransportStation; + utBuildee = s_autVtsBuildable[GetRandom() % ARRAYSIZE(s_autVtsBuildable)]; + } else { + gtBuilder = kgtHumanResourceCenter; + utBuildee = s_autHrcBuildable[GetRandom() % ARRAYSIZE(s_autHrcBuildable)]; + } + + if (ggobm.IsBelowLimit(knLimitMobileUnit, m_pplr)) { + // Search the Gob list for a same-side Builder that doesn't have any commands pending + + bool fBuilt = false; + for (Gob *pgobBuilder = ggobm.GetFirstGob(); pgobBuilder != NULL; pgobBuilder = ggobm.GetNextGob(pgobBuilder)) { + if (pgobBuilder->GetOwner() != m_pplr) + continue; + if (pgobBuilder->GetType() != gtBuilder) + continue; + + BuilderGob *pbld = (BuilderGob *)pgobBuilder; + if (pbld->IsBuildInProgress()) + continue; + + pbld->Build(utBuildee); + fBuilt = true; + break; + } + +#ifdef STRESS + if (gfStress && !fBuilt) { + + // If it can't be built a 'legal' way, just spawn it at a random location + + MobileUnitGob *pmunt = (MobileUnitGob *)CreateGob(gapuntc[utBuildee]->gt); + Assert(pmunt != NULL); + if (pmunt != NULL) { + // Find a free spot + TerrainMap *ptrmap = gsim.GetLevel()->GetTerrainMap(); + + while (true) { + TCoord tx = GetRandom() % 64; + TCoord ty = GetRandom() % 64; + + if (ptrmap->IsOccupied(tx, ty, 1, 1, (kbfStructure | kbfMobileUnit))) + continue; + + bool fSuccess = pmunt->Init(WcFromTc(tx), WcFromTc(ty), m_pplr, 0, 0, NULL); + Assert(fSuccess); + if (!fSuccess) + delete pmunt; + break; + } + } + } +#endif + } + } + m_unvl.MinSkip(m_cBuildUnitCountdown); + +EndStateMachine +} + +} // namespace wi \ No newline at end of file diff --git a/game/Player.cpp b/game/Player.cpp new file mode 100644 index 0000000..77a9730 --- /dev/null +++ b/game/Player.cpp @@ -0,0 +1,1205 @@ +#include "ht.h" +#include "mpshared/netmessage.h" +#include "game/stateframe.h" +#include "game/completemanager.h" +#include "base/md5.h" + +namespace wi { + +#define knCreditDelta 18 //TUNE: +PlayerMgr gplrm; +Player *gpplrLocal; +Player gplrDummy; + +//--------------------------------------------------------------------------- +// PlayerMgr Implementation + +PlayerMgr::PlayerMgr() +{ + Reset(); +} + +PlayerMgr::~PlayerMgr() +{ + delete[] m_aplr; +} + +void PlayerMgr::Reset() +{ + delete[] m_aplr; + m_aplr = new Player[kcPlayersMax]; + Assert(m_aplr != NULL, "out of memory!"); + if (m_aplr != NULL) + memset(m_aplr, 0, sizeof(Player) * kcPlayersMax); + gpplrLocal = NULL; +} + +Player *PlayerMgr::AllocPlayer(word wf) +{ + Player *pplr = m_aplr; + for (int i = 0; i < kcPlayersMax; i++, pplr++) { + if (pplr->m_wf & kfPlrInUse) + continue; + + pplr->Init(i); + pplr->m_wf |= kfPlrInUse | wf; + return pplr; + } + + Assert("this shouldn't happen!"); + return NULL; +} + +Player *PlayerMgr::GetPlayer(Side side) +{ + Player *pplr = m_aplr; + for (int i = 0; i < kcPlayersMax; i++, pplr++) { + if (pplr->m_side == side) + return pplr; + } + + Assert("No player playing this side???"); + return NULL; +} + +Player *PlayerMgr::GetNextPlayer(Player *pplr) +{ + int i = (pplr == NULL) ? 0 : pplr->m_pid + 1; + pplr = &m_aplr[i]; + for (; i < kcPlayersMax; i++, pplr++) { + if (pplr->m_wf & kfPlrInUse) + return pplr; + } + + return NULL; +} + +Player *PlayerMgr::GetNextHumanPlayer(Player *pplr) +{ + int i = pplr == NULL ? 0 : pplr->m_pid + 1; + pplr = &m_aplr[i]; + for (; i < kcPlayersMax; i++, pplr++) { + if ((pplr->m_wf & (kfPlrInUse | kfPlrComputer)) == kfPlrInUse) + return pplr; + } + + return NULL; +} + +Player *PlayerMgr::GetNextObservingPlayer(Player *pplr) +{ + int i = pplr == NULL ? 0 : pplr->m_pid + 1; + pplr = &m_aplr[i]; + for (; i < kcPlayersMax; i++, pplr++) { + if (pplr->m_fLeftGame) { + continue; + } + if ((pplr->m_wf & (kfPlrInUse | kfPlrComputer | kfPlrObserver)) == + (kfPlrInUse | kfPlrComputer | kfPlrObserver)) { + return pplr; + } + } + + return NULL; +} + +int PlayerMgr::GetPlayerCount() +{ + int cplr = 0; + Player *pplr = m_aplr; + for (int i = 0; i < kcPlayersMax; i++, pplr++) { + if (pplr->m_wf & kfPlrInUse) + cplr++; + } + + return cplr; +} + +int PlayerMgr::GetHumanTeamCount(bool fExtra, Pid pidExtra) { + // Count the # of unique ally masks among active human players, + // even though it is a mask. This simulates team count. + SideMask asidmAlliesUnique[kcPlayersMax]; + int cUnique = 0; + Player *pplr = m_aplr; + for (int i = 0; i < kcPlayersMax; i++, pplr++) { + if ((pplr->m_wf & kfPlrInUse) == 0) { + continue; + } + bool fSkipCheck = false; + if (fExtra && pplr->m_pid == pidExtra) { + fSkipCheck = true; + } + if (!fSkipCheck) { + if ((pplr->m_wf & (kfPlrInUse | kfPlrComputer | kfPlrUnfulfilled)) + != kfPlrInUse) { + continue; + } + } + bool fFound = false; + for (int j = 0; j < cUnique; j++) { + if (pplr->GetAllies() == asidmAlliesUnique[j]) { + fFound = true; + break; + } + } + if (!fFound) { + asidmAlliesUnique[cUnique] = pplr->GetAllies(); + cUnique++; + } + } + return cUnique; +} + +bool PlayerMgr::DetectTransitionToSingleHumanTeam(Pid pidLeft) { + // Detect if there were two or more opposing sides, and now there are + // no opposing sides. + + int cBefore = GetHumanTeamCount(true, pidLeft); + int cAfter = GetHumanTeamCount(false, 0); + return (cAfter == 1 && cBefore > 1); +} + +void PlayerMgr::Init(PlayersUpdateNetMessage *ppunm) +{ + Reset(); + + PlayerRecord *pplrr = ppunm->aplrr; + for (int i = 0; i < ppunm->cplrr; i++, pplrr++) { + Player *pplr = &m_aplr[pplrr->pid]; + pplr->Init(pplrr->pid); + + pplr->m_wf |= kfPlrInUse; + pplr->SetSide(pplrr->side); + + strncpyz(pplr->m_szName, pplrr->szName, sizeof(pplr->m_szName)); + if (pplrr->wf & kfPlrrReady) + pplr->m_wf |= kfPlrReady; + if (pplrr->wf & kfPlrrComputer) + pplr->m_wf |= kfPlrComputer; + if (pplrr->wf & kfPlrrComputerOvermind) + pplr->m_wf |= kfPlrComputerOvermind; + if (pplrr->wf & kfPlrrUnfulfilled) + pplr->m_wf |= kfPlrUnfulfilled; + if (pplrr->wf & kfPlrrCreator) + pplr->m_wf |= kfPlrCreator; + if (pplrr->wf & kfPlrrLocal) + gpplrLocal = pplr; + + // Life is hard for everybody in a multiplayer game! + + pplr->SetHandicap(kfHcapHard); + } +} + +bool PlayerMgr::Init(char *pszLevel) +{ + Reset(); + + Level *plvl = new Level(); + Assert(plvl != NULL, "out of memory!"); + if (plvl == NULL) + return false; + if (!plvl->LoadLevelInfo(pszLevel)) { + delete plvl; + return false; + } + + for (int i = 0; i < kcSides; i++) { + SideInfo *psidi = plvl->GetSideInfo(i); + word wf = 0; + switch (psidi->nIntelligence) { + case knIntelligenceComputer: + case knIntelligenceComputerNeutral: + wf = kfPlrComputer; + break; + + case knIntelligenceComputerOvermind: + wf = kfPlrComputer | kfPlrComputerOvermind; + break; + } + + Player *pplr = AllocPlayer(wf); + + // The local player is the first human player allocated + + if (wf & kfPlrComputer) { + pplr->SetHandicap(kfHcapHard); // life is hard for computers because that's how the levels have been tuned + } else if (gpplrLocal == NULL) { + gpplrLocal = pplr; + gpplrLocal->SetHandicap(gwfHandicap); + } + + pplr->SetSide(i); + } + + delete plvl; + return true; +} + +#define knVerPlayerMgrState 1 +bool PlayerMgr::LoadState(Stream *pstm) +{ + byte nVer = pstm->ReadByte(); + if (nVer != knVerPlayerMgrState) + return false; + + Reset(); + + int cPlayers = pstm->ReadWord(); + Assert(cPlayers <= kcPlayersMax); + + while (cPlayers-- != 0) { + Player *pplr = gplrm.AllocPlayer(); + if (pplr == NULL || !pplr->LoadState(pstm)) + return false; + } + return true; +} + +void PlayerMgr::SetAllies(Player **applr, int cplrs, SideMask sidmAllies) +{ + // Set allies + + for (int n = 0; n < cplrs; n++) + applr[n]->SetAllies(sidmAllies); + + // Recalc enemies. Clear first + + Gob *pgobT; + for (pgobT = ggobm.GetFirstGob(); pgobT != NULL; pgobT = ggobm.GetNextGob(pgobT)) { + if (!(pgobT->GetFlags() & kfGobUnit)) + continue; + UnitGob *punt = (UnitGob *)pgobT; + punt->RecalcEnemyNearby(true); + } + + // Now recalc + + for (pgobT = ggobm.GetFirstGob(); pgobT != NULL; pgobT = ggobm.GetNextGob(pgobT)) { + if (!(pgobT->GetFlags() & kfGobUnit)) + continue; + UnitGob *punt = (UnitGob *)pgobT; + punt->RecalcEnemyNearby(false); + } +} + +bool PlayerMgr::SaveState(Stream *pstm) +{ + pstm->WriteByte(knVerPlayerMgrState); + pstm->WriteWord(gplrm.GetPlayerCount()); + for (Player *pplr = gplrm.GetNextPlayer(NULL); pplr != NULL; pplr = gplrm.GetNextPlayer(pplr)) + pplr->SaveState(pstm); + return true; +} + +#if 0 // not using anymore +bool PlayerMgr::IsSideInUse(Side side) +{ + Player *pplr = m_aplr; + for (int i = 0; i < kcPlayersMax; i++, pplr++) { + if (pplr->m_side == side) + return true; + } + + return false; +} +#endif + +const char *PlayerMgr::GetCreatorName() +{ + Player *pplr = m_aplr; + for (int i = 0; i < kcPlayersMax; i++, pplr++) { + if (pplr->m_wf & kfPlrCreator) { + return pplr->m_szName; + } + } + return NULL; +} + +void PlayerMgr::Update(long cUpdates) +{ + Player *pplr = m_aplr; + for (int i = 0; i < kcPlayersMax; i++, pplr++) { + if (pplr->m_wf & kfPlrInUse) + pplr->Update(cUpdates); + } +} + +int PlayerMgr::GetUnitInstanceCountFromMask(UnitMask um, word wfPlr) +{ + int cUnits = 0; + Player *pplr = m_aplr; + for (int i = 0; i < kcPlayersMax; i++, pplr++) { + // If kfPlrComputer matches in both... + if (((pplr->m_wf ^ wfPlr) & kfPlrComputer) == 0) + cUnits += pplr->GetUnitInstanceCountFromMask(um); + } + return cUnits; +} + +#ifdef TRACKSTATE +void PlayerMgr::TrackState(StateFrame *frame) +{ + Player *pplr = m_aplr; + for (int i = 0; i < kcPlayersMax; i++, pplr++) { + if (!(pplr->m_wf & kfPlrInUse)) { + continue; + } + pplr->TrackState(frame); + } +} +#endif + +void PlayerMgr::SendWinStats() +{ + if (!gfMultiplayer || gptra == NULL) { + return; + } + + // Find the allies of the winner, if any. They are all winners. + // Since kfPlrWinner only gets set by the human winner, this will + // only be != 0 by the winning player. + SideMask sidmWinners = 0; + Player *pplr = NULL; + while ((pplr = GetNextPlayer(pplr)) != NULL) { + if (pplr->GetFlags() & kfPlrWinner) { + sidmWinners = pplr->GetAllies(); + break; + } + } + + pplr = NULL; + while ((pplr = GetNextPlayer(pplr)) != NULL) { + // Collect and send stats for each player; let the server sort it out + WinStatsNetMessage wsnm; + wsnm.pid = pplr->m_pid; + wsnm.ws.sidm = GetSideMask(pplr->m_side); + wsnm.ws.sidmAllies = pplr->m_sidmAllies; + wsnm.ws.cCreditsAcquired = pplr->GetTotalCreditsAcquired(); + wsnm.ws.cCreditsConsumed = pplr->GetTotalCreditsConsumed(); + wsnm.ws.cEnemyMobileUnitsKilled = pplr->GetEnemyMobileUnitsKilled(); + wsnm.ws.cEnemyStructuresKilled = pplr->GetEnemyStructuresKilled(); + wsnm.ws.cMobileUnitsLost = pplr->GetMobileUnitsLost(); + wsnm.ws.cStructuresLost = pplr->GetStructuresLost(); + Assert(ARRAYSIZE(pplr->m_acut) == ARRAYSIZE(wsnm.ws.acut)); + for (int i = 0; i < ARRAYSIZE(wsnm.ws.acut); i++) { + wsnm.ws.acut[i] = pplr->m_acut[i]; + } + Assert(ARRAYSIZE(pplr->m_acutBuilt) == ARRAYSIZE(wsnm.ws.acutBuilt)); + for (int i = 0; i < ARRAYSIZE(wsnm.ws.acutBuilt); i++) { + wsnm.ws.acutBuilt[i] = pplr->m_acutBuilt[i]; + } + wsnm.ws.ff = 0; + + // If there is a winner team, then the rest are losers + if (sidmWinners != 0) { + if (GetSideMask(pplr->GetSide()) & sidmWinners) { + wsnm.ws.ff |= kfwsWinner; + } else { + wsnm.ws.ff |= kfwsLoser; + } + } else { + // This side isn't the winner. It doesn't know who the winners + // are, but it does know it is a loser. + if (pplr->m_wf & kfPlrLoser) { + wsnm.ws.ff |= kfwsLoser; + } + } + + // Note: Update wsnm.hash with whatever is helpful to validate these + // results on the server. See validating code in server/game.cpp. + + gptra->SendNetMessage(&wsnm); + } +} + +//--------------------------------------------------------------------------- +// Player Implementation + +Player::Player() +{ + Init(0); +} + +Player::~Player() +{ + if (m_szFormalObjectiveInfo != NULL) + gmmgr.FreePtr(m_szFormalObjectiveInfo); + + for (int i = 0; i < kcFormalObjectivesMax; i++) { + if (m_aszFormalObjectiveText[i] != NULL) + gmmgr.FreePtr(m_aszFormalObjectiveText[i]); + if (m_aszFormalObjectiveStatus[i] != NULL) + gmmgr.FreePtr(m_aszFormalObjectiveStatus[i]); + } +} + +#ifdef TRACKSTATE +void Player::TrackState(StateFrame *frame) { + int i = frame->AddCountedValue('PLYR'); + frame->AddValue('WFUP', (dword)m_wfUpgrades, i); + frame->AddValue('SIDE', (dword)m_side, i); + frame->AddValue('SALI', (dword)m_sidmAllies, i); + frame->AddValue('SDIS', (dword)m_sidmDiscovered, i); + frame->AddValue('CRED', (dword)m_nCredits, i); + frame->AddValue('POWR', (dword)m_nPowerSupply, i); + frame->AddValue('DMND', (dword)m_nPowerDemand, i); + frame->AddValue('UPGR', (dword)m_upgm, i); + frame->AddValue('CMUK', (dword)m_cmuntKilled, i); + frame->AddValue('CSTK', (dword)m_cstruKilled, i); + frame->AddValue('CMLS', (dword)m_cmuntLost, i); + frame->AddValue('CSLS', (dword)m_cstruLost, i); + frame->AddValue('TCAQ', (dword)m_nTotalCreditsAcquired, i); + frame->AddValue('TCCN', (dword)m_nTotalCreditsConsumed, i); + frame->AddValue('CURL', (dword)m_cUpdatesRepairLast, i); + frame->AddValue('CSNC', (dword)m_cStructsNeedCredits, i); + frame->AddValue('WFHN', (dword)m_wfHandicap, i); +} +#endif + +void Player::Init(Pid pid) +{ + m_wf = 0; + m_pid = pid; + m_nCredits = 0; + m_nCreditsConsumed = m_nCreditsAcquired = 0; + m_nDirCredits = 0; + m_nConsumerCredits = knConsumerMax; + m_side = ksideNeutral; + m_wfUpgrades = 0; + m_nPowerDemand = 0; + m_nPowerSupply = 0; + m_szName[0] = 0; + m_sidmAllies = 0; + m_umAllowed = kumAll; + m_szObjective[0] = 0; + m_tptDiscover.tx = m_tptDiscover.ty = 0; + m_upgm = 0; + m_upgmAllowed = kupgmAll; + m_szFormalObjectiveInfo = NULL; + m_cUpdatesRepairLast = 0; + m_cStructsNeedCredits = 0; + m_wfHandicap = kfHcapDefault; + m_cUpdates = -1; + m_nLagState = knLagNone; + m_fLeftGame = false; + + memset(m_acut, 0, ARRAYSIZE(m_acut)); + memset(m_acutBuilt, 0, ARRAYSIZE(m_acutBuilt)); + + for (int i = 0; i < kcFormalObjectivesMax; i++) { + m_aszFormalObjectiveText[i] = NULL; + m_aszFormalObjectiveStatus[i] = NULL; + } +} + +#define knVerPlayerState 11 +bool Player::LoadState(Stream *pstm) +{ + byte nVer = pstm->ReadByte(); + if (nVer != knVerPlayerState) + return false; + m_wfUpgrades = pstm->ReadWord(); + m_side = pstm->ReadWord(); + m_sidmAllies = pstm->ReadWord(); + m_sidmDiscovered = pstm->ReadWord(); + m_tptDiscover.tx = pstm->ReadWord(); + m_tptDiscover.ty = pstm->ReadWord(); + pstm->ReadString(m_szName, sizeof(m_szName)); + m_wf = pstm->ReadWord(); + m_pid = pstm->ReadWord(); + m_nCredits = (long)pstm->ReadDword(); + + m_nCreditsAcquired = 0; + m_nCreditsConsumed = 0; + m_nPowerSupply = (short)pstm->ReadWord(); + m_nPowerDemand = (short)pstm->ReadWord(); + pstm->Read(m_acut, sizeof(m_acut)); + m_umAllowed = pstm->ReadDword(); + pstm->ReadString(m_szObjective, sizeof(m_szObjective)); + m_upgm = pstm->ReadWord(); + m_upgmAllowed = pstm->ReadWord(); + m_cUpdatesRepairLast = pstm->ReadDword(); + + // Elaborate b.s. to save memory from dynamic heap on palm + + char szFormalObjectiveInfo[kcchObjectiveInfoMax]; + pstm->ReadString(szFormalObjectiveInfo, sizeof(szFormalObjectiveInfo)); + if (szFormalObjectiveInfo[0] == (char)0xff) { + m_szFormalObjectiveInfo = NULL; + } else { + m_szFormalObjectiveInfo = (char *)gmmgr.AllocPtr( + strlen(szFormalObjectiveInfo) + 1); + gmmgr.WritePtr(m_szFormalObjectiveInfo, 0, szFormalObjectiveInfo, + strlen(szFormalObjectiveInfo) + 1); + } + + for (int i = 0; i < kcFormalObjectivesMax; i++) { + char szFormalObjectiveText[kcchObjectiveMax]; + pstm->ReadString(szFormalObjectiveText, sizeof(szFormalObjectiveText)); + if (szFormalObjectiveText[0] == (char)0xff) { + m_aszFormalObjectiveText[i] = NULL; + } else { + m_aszFormalObjectiveText[i] = (char *)gmmgr.AllocPtr( + strlen(szFormalObjectiveText) + 1); + gmmgr.WritePtr(m_aszFormalObjectiveText[i], 0, + szFormalObjectiveText, strlen(szFormalObjectiveText) + 1); + } + + char szFormalObjectiveStatus[kcchObjectiveStatusMax]; + pstm->ReadString(szFormalObjectiveStatus, + sizeof(szFormalObjectiveStatus)); + if (szFormalObjectiveStatus[0] == (char)0xff) { + m_aszFormalObjectiveStatus[i] = NULL; + } else { + m_aszFormalObjectiveStatus[i] = (char *)gmmgr.AllocPtr( + strlen(szFormalObjectiveStatus) + 1); + gmmgr.WritePtr(m_aszFormalObjectiveStatus[i], 0, + szFormalObjectiveStatus, + strlen(szFormalObjectiveStatus) + 1); + } + } + + m_cmuntKilled = pstm->ReadWord(); + m_cstruKilled = pstm->ReadWord(); + m_cmuntLost = pstm->ReadWord(); + m_cstruLost = pstm->ReadWord(); + m_nTotalCreditsAcquired = pstm->ReadDword(); + m_nTotalCreditsConsumed = pstm->ReadDword(); + m_wfHandicap = pstm->ReadWord(); + + // The local player is the first human player allocated + + if (!(m_wf & kfPlrComputer) && gpplrLocal == NULL) { + gpplrLocal = this; + gwfHandicap = m_wfHandicap; + } + + return pstm->IsSuccess(); +} + +bool Player::SaveState(Stream *pstm) +{ + pstm->WriteByte(knVerPlayerState); + pstm->WriteWord(m_wfUpgrades); + pstm->WriteWord(m_side); + pstm->WriteWord(m_sidmAllies); + pstm->WriteWord(m_sidmDiscovered); + pstm->WriteWord(m_tptDiscover.tx); + pstm->WriteWord(m_tptDiscover.ty); + pstm->WriteString(m_szName); + pstm->WriteWord(m_wf); + pstm->WriteWord(m_pid); + pstm->WriteDword(m_nCredits); + pstm->WriteWord(m_nPowerSupply); + pstm->WriteWord(m_nPowerDemand); + pstm->Write(m_acut, sizeof(m_acut)); + pstm->WriteDword(m_umAllowed); + pstm->WriteString(m_szObjective); + pstm->WriteWord(m_upgm); + pstm->WriteWord(m_upgmAllowed); + pstm->WriteDword(m_cUpdatesRepairLast); + + if (m_szFormalObjectiveInfo == NULL) { + pstm->WriteByte(0xff); + pstm->WriteByte(0); + } else { + pstm->WriteString(m_szFormalObjectiveInfo); + } + + for (int i = 0; i < kcFormalObjectivesMax; i++) { + if (m_aszFormalObjectiveText[i] == NULL) { + pstm->WriteByte(0xff); + pstm->WriteByte(0); + } else { + pstm->WriteString(m_aszFormalObjectiveText[i]); + } + if (m_aszFormalObjectiveStatus[i] == NULL) { + pstm->WriteByte(0xff); + pstm->WriteByte(0); + } else { + pstm->WriteString(m_aszFormalObjectiveStatus[i]); + } + } + + pstm->WriteWord(m_cmuntKilled); + pstm->WriteWord(m_cstruKilled); + pstm->WriteWord(m_cmuntLost); + pstm->WriteWord(m_cstruLost); + pstm->WriteDword(m_nTotalCreditsAcquired); + pstm->WriteDword(m_nTotalCreditsConsumed); + pstm->WriteWord(m_wfHandicap); + + return pstm->IsSuccess(); +} + +// TUNE: +#define kctLagGrace 100 // lag time period to be declared laggy player +#define kctLagRedemption 0 // 300 // to become non-laggy, the player must be not laggy for this period +#define kctLagKill 800 // if still laggy after this period, recommend kill + +bool Player::IsBehind(long cUpdates) +{ + // If this player has already broadcasted a disconnect message, force it + // into a no-lag state + + bool fBehind = m_cUpdates < cUpdates; + if (m_wf & kfPlrDisconnectBroadcasted) { + SetLagState(knLagNone); + return fBehind; + } + + if (fBehind) { + // Player is behind + + switch (m_nLagState) { + case knLagNone: + // Remember that pplr is lagging and when this started + + m_tLagStart = HostGetTickCount(); + m_nLagState = knLagGrace; + break; + + case knLagGrace: + // This state is effectively a grace period. If the player remains + // laggy through this period, it becomes guilty of lag + + if (HostGetTickCount() - m_tLagStart >= kctLagGrace) { m_nLagState += knLagGuilty; m_tLastLag = HostGetTickCount(); } break; + + case knLagGuilty: + case knLagKill: + // This player has lagged long enough to be guilty as charged. Now + // the player needs to be *not* laggy over a fixed period to get + // out of this state + + m_tLastLag = HostGetTickCount(); + if (HostGetTickCount() - m_tLagStart >= kctLagKill) + m_nLagState = knLagKill; + break; + } + } else { + // Player is not behind + + switch (m_nLagState) { + case knLagNone: + // All is ok + + break; + + case knLagGrace: + // This player has "caught up" during the grace period. Assume there is no lag now. + + m_nLagState = knLagNone; + break; + + case knLagGuilty: + case knLagKill: + // Guilty of lag and yet the player has caught up. If the player can keep this state + // it will be declared not laggy + + if (HostGetTickCount() - m_tLastLag >= kctLagRedemption) { + m_nLagState = knLagNone; + break; + } + } + } + + return fBehind; +} + +int Player::GetLagState() +{ + return m_nLagState; +} + +void Player::SetLagState(int nLagState) +{ + m_nLagState = nLagState; + m_tLastLag = HostGetTickCount(); + m_tLagStart = m_tLastLag; +} + +int Player::GetLagTimeout() +{ + // Return seconds until this player gets to the kill state + + long ctElapsed = HostGetTickCount() - m_tLagStart; + if (ctElapsed > kctLagKill) + return 0; + return (kctLagKill - ctElapsed + 50) / 100; +} + +void Player::SetUpgrades(word wfUpgrades) +{ + word wfChange = wfUpgrades ^ m_wfUpgrades; + if (wfChange == 0) + return; + m_wfUpgrades = wfUpgrades; + + if (wfUpgrades & kfUpgradeHrc) + m_upgm |= kupgmAdvancedHRC; + else + m_upgm &= ~kupgmAdvancedHRC; + if (wfUpgrades & kfUpgradeVts) + m_upgm |= kupgmAdvancedVTS; + else + m_upgm &= ~kupgmAdvancedVTS; + + // Tell interested units that they are being upgraded; this gets them to wake up + + for (Gob *pgob = ggobm.GetFirstGob(); pgob != NULL; pgob = ggobm.GetNextGob(pgob)) { + if (pgob->GetOwner() != this) + continue; + if ((pgob->GetFlags() & (kfGobActive | kfGobUnit)) == (kfGobActive | kfGobUnit)) { + UnitGob *punt = (UnitGob *)pgob; + MobileUnitBuilderConsts *pmubc = (MobileUnitBuilderConsts *)punt->GetConsts(); + switch (punt->GetType()) { + case kgtVehicleTransportStation: + if (wfChange & kfUpgradeVtsInProgress) + gsmm.SendMsg((m_wfUpgrades & kfUpgradeVtsInProgress) ? kmidBeingUpgraded : kmidUpgradeComplete, punt->GetId()); + break; + + case kgtHumanResourceCenter: + if (wfChange & kfUpgradeHrcInProgress) + gsmm.SendMsg((m_wfUpgrades & kfUpgradeHrcInProgress) ? kmidBeingUpgraded : kmidUpgradeComplete, punt->GetId()); + break; + } + } + } +} + +void Player::ModifyNeedCreditsCount(int cDelta) +{ + // track how many units are waiting for credits so we can + // flash the UI if needed. Keep here so it can be re-updated + // when loading a save game which happens before the UI is initialized. + + m_cStructsNeedCredits += cDelta; + Assert((short)m_cStructsNeedCredits >= 0); +} + +void Player::AddPowerSupplyAndDemand(int nPowerSupply, int nPowerDemand) +{ + bool fLowBefore = IsPowerLow(); + m_nPowerSupply += nPowerSupply; + m_nPowerDemand += nPowerDemand; + bool fLowAfter = IsPowerLow(); + + // Notify this player's gobs that want to know that the power situation has changed + + if (fLowBefore != fLowAfter) { + for (Gob *pgob = ggobm.GetFirstGob(); pgob != NULL; pgob = ggobm.GetNextGob(pgob)) { + if (pgob->GetOwner() != this) + continue; + if ((pgob->GetFlags() & (kfGobActive | kfGobUnit)) == (kfGobActive | kfGobUnit)) { + UnitGob *punt = (UnitGob *)pgob; + if (punt->GetConsts()->wf & kfUntcNotifyPowerLowHigh) { + gsmm.SendMsg(kmidPowerLowHigh, punt->GetId()); + } + } + } + + // Notify the of a power change that may affect if the radar is powered + + if (this == gpplrLocal) { + gpmm->CalcPoweredRadar(); + } + } +} + +void Player::SetName(const char *pszName) +{ + strncpyz(m_szName, (char *)pszName, sizeof(m_szName)); +} + +UnitMask Player::GetUnitMask() +{ + UnitMask um = 0; + + for (int i = 0; i < kutMax; i++) { + if (m_acut[i] != 0) + um |= gapuntc[i]->um; + } + + return um; +} + +int Player::GetUnitActiveCountFromMask(UnitMask um) +{ + int c = 0; + for (int i = 0; i < kutMax; i++) { + if (gapuntc[i]->um & um) + c += m_acut[i]; + } + + return c; +} + +int Player::GetUnitInstanceCountFromMask(UnitMask um) +{ + if (!(m_wf & kfPlrInUse)) + return 0; + + int cUnits = 0; + for (Gob *pgob = ggobm.GetFirstGob(); pgob != NULL; pgob = ggobm.GetNextGob(pgob)) { + if (pgob->GetFlags() & kfGobUnit) { + UnitGob *punt = (UnitGob *)pgob; + if (punt->GetOwner() == this) { + if (punt->GetConsts()->um & um) + cUnits++; + } + } + } + return cUnits; +} + +int Player::GetCreditsDirection() +{ + int nDir = m_nDirCredits; + m_nDirCredits = 0; + return nDir; +} + +int Player::GetCreditsConsumer() +{ + int nConsumer = m_nConsumerCredits; + m_nConsumerCredits = knConsumerMax; + return nConsumer; +} + +void Player::SetCredits(long nCredits, bool fAffectTotals, int nConsumer) +{ + Assert(nCredits >= 0); + + if (nConsumer < m_nConsumerCredits) + m_nConsumerCredits = nConsumer; + + if (nCredits > m_nCredits && m_nCredits != 0) { + m_nDirCredits = 1; + } else if (nCredits < m_nCredits && nCredits != 0) { + if (m_nDirCredits == 0) + m_nDirCredits = -1; + } + + long dCredits = nCredits - m_nCredits; + if (dCredits > 0) + m_nTotalCreditsAcquired += dCredits; + else + m_nTotalCreditsConsumed -= dCredits; + + m_nCredits = nCredits; + + if (this == gpplrLocal && fAffectTotals) { + if (dCredits < 0) + m_nCreditsConsumed += -dCredits; + else if (dCredits > 0) + m_nCreditsAcquired += dCredits; + } +} + +void Player::SetObjective(char *psz) +{ + strncpyz(m_szObjective, psz, sizeof(m_szObjective)); +} + +void Player::Update(long cUpdates) +{ + // If auto repair is on, repair all damaged structures owned by this player. + // TUNE: + +#define kcupdRepair 6 + + if (m_cUpdatesRepairLast == 0 || abs(m_cUpdatesRepairLast - cUpdates) >= kcupdRepair) { + m_cUpdatesRepairLast = cUpdates; + if (m_wf & kfPlrAutoRepair) + Repair(true); + } + + // new theory. Play a credit noise every delta credits + + if (m_nCreditsConsumed >= knCreditDelta) { + m_nCreditsConsumed %= knCreditDelta; + gsndm.PlaySfx(ksfxGameCreditsDecreasing); + } + + if (m_nCreditsAcquired >= knCreditDelta) { + m_nCreditsAcquired %= knCreditDelta; + gsndm.PlaySfx(ksfxGameCreditsIncreasing); + } +} + +void Player::Repair(bool fOn) +{ + if (fOn) + m_wf |= kfPlrAutoRepair; + else + m_wf &= ~kfPlrAutoRepair; + + for (Gob *pgobT = ggobm.GetFirstGob(); pgobT != NULL; pgobT = ggobm.GetNextGob(pgobT)) { + if (pgobT->GetOwner() != this) + continue; + + if ((pgobT->GetFlags() & (kfGobStructure | kfGobActive)) != (kfGobStructure | kfGobActive)) + continue; + + StructGob *pstru = (StructGob *)pgobT; + + if (fOn) { + if (!(pstru->GetUnitFlags() & kfUnitRepairing)) { + if (pstru->GetHealth() < divfx(pstru->GetConsts()->GetArmorStrength(), itofx(3))) + pstru->Repair(true); + } + } else { + if (pstru->GetUnitFlags() & kfUnitRepairing) + pstru->Repair(false); + } + } +} + +void Player::SetFormalObjectiveText(int iObjective, char *pszText) +{ + if (m_aszFormalObjectiveText[iObjective] != NULL) + gmmgr.FreePtr(m_aszFormalObjectiveText[iObjective]); + m_aszFormalObjectiveText[iObjective] = (char *)gmmgr.AllocPtr(strlen(pszText) + 1); + gmmgr.WritePtr(m_aszFormalObjectiveText[iObjective], 0, pszText, strlen(pszText) + 1); + SetFormalObjectiveStatus(iObjective, "Incomplete"); +} + +void Player::SetFormalObjectiveStatus(int iObjective, char *pszText) +{ + if (m_aszFormalObjectiveStatus[iObjective] != NULL) + gmmgr.FreePtr(m_aszFormalObjectiveStatus[iObjective]); + m_aszFormalObjectiveStatus[iObjective] = (char *)gmmgr.AllocPtr(strlen(pszText) + 1); + gmmgr.WritePtr(m_aszFormalObjectiveStatus[iObjective], 0, pszText, strlen(pszText) + 1); +} + +void Player::SetFormalObjectiveInfo(char *pszText) +{ + if (m_szFormalObjectiveInfo != NULL) + gmmgr.FreePtr(m_szFormalObjectiveInfo); + m_szFormalObjectiveInfo = (char *)gmmgr.AllocPtr(strlen(pszText) + 1); + gmmgr.WritePtr(m_szFormalObjectiveInfo, 0, pszText, strlen(pszText) + 1); +} + +// +// Objectives / Win Summary / Lose Summary form +// + +class ObjectivesForm : public ShellForm { +public: + ObjectivesForm(Player *pplr, int so, bool fForceInfoDisplay) secPlayer; + void UpdateStatistics() secPlayer; + + // ShellForm overrides + + virtual bool Init(FormMgr *pfrmm, IniReader *pini, word idf) secPlayer; + virtual void OnControlSelected(word idc) secPlayer; + +private: + Player *m_pplr; + int m_so; + static bool s_fStatistics; +}; + +bool ObjectivesForm::s_fStatistics = false; + +ObjectivesForm::ObjectivesForm(Player *pplr, int so, bool fForceInfoDisplay) +{ + m_pplr = pplr; + m_so = so; + if (fForceInfoDisplay) + s_fStatistics = false; +} + +bool ObjectivesForm::Init(FormMgr *pfrmm, IniReader *pini, word idf) +{ + if (!ShellForm::Init(pfrmm, pini, idf)) + return false; + + LabelControl *plbl; + plbl = (LabelControl *)GetControlPtr(kidcMissionTitle); + char szT[kcbLevelTitle]; + strncpyz(szT, gsim.GetLevel()->GetTitle(), sizeof(szT)); + HtStrupr(szT); + plbl->SetText(szT); + + for (int i = 0; i < kcFormalObjectivesMax; i++) { + plbl = (LabelControl *)GetControlPtr(kidcObjectiveText1 + i); + plbl->SetText(m_pplr->GetFormalObjectiveText(i)); + plbl = (LabelControl *)GetControlPtr(kidcObjectiveStatus1 + i); + plbl->SetText(m_pplr->GetFormalObjectiveStatus(i)); + } + + UpdateStatistics(); + + // Stop the animation. Will anyone people notice? + + m_fAnimate = false; + + return true; +} + +void ObjectivesForm::UpdateStatistics() +{ + char szT[100]; + LabelControl *plbl; + if (m_so == ksoObjectives) { + plbl = (LabelControl *)GetControlPtr(kidcObjectiveInfo); + plbl->SetText(m_pplr->GetFormalObjectiveInfo()); + + for (int i = 0; i < m_cctl; i++) { + Control *pctl = m_apctl[i]; + int idc = pctl->GetId(); + if (idc >= kidcPage1 && idc < kidcPage2) + pctl->Show(!s_fStatistics); + else if (idc >= kidcPage2 && idc < kidcPage3) + pctl->Show(s_fStatistics); + } + } + + plbl = (LabelControl *)GetControlPtr(kidcMobileUnitsKilled); + itoa(m_pplr->GetEnemyMobileUnitsKilled(), szT, 10); + plbl->SetText(szT); + + plbl = (LabelControl *)GetControlPtr(kidcStructuresKilled); + itoa(m_pplr->GetEnemyStructuresKilled(), szT, 10); + plbl->SetText(szT); + + plbl = (LabelControl *)GetControlPtr(kidcMobileUnitsLost); + itoa(m_pplr->GetMobileUnitsLost(), szT, 10); + plbl->SetText(szT); + + plbl = (LabelControl *)GetControlPtr(kidcStructuresLost); + itoa(m_pplr->GetStructuresLost(), szT, 10); + plbl->SetText(szT); + + plbl = (LabelControl *)GetControlPtr(kidcCreditsAction); + sprintf(szT, "%ld / %ld", m_pplr->GetTotalCreditsAcquired(), m_pplr->GetTotalCreditsConsumed()); + plbl->SetText(szT); + + plbl = (LabelControl *)GetControlPtr(kidcGameTime); + long t = gsim.GetTickCount(); + int nHour = t / (100L * 60 * 60); + t -= nHour * (100L * 60 * 60); + int nMin = t / (100 * 60); + Assert(nMin < 60); + t -= nMin * (100L * 60); + int nSec = t / 100; + Assert(nSec < 60); + sprintf(szT, "%02d:%02d:%02d", nHour, nMin, nSec); + plbl->SetText(szT); +} + +void ObjectivesForm::OnControlSelected(word idc) +{ + if (idc == kidcStatistics || idc == kidcInfo) { + s_fStatistics = !s_fStatistics; + UpdateStatistics(); + } else { + ShellForm::OnControlSelected(idc); + } +} + +int Player::ShowObjectives(int so, bool fForceInfoDisplay, bool fAborting) +{ + // The default for losing is knGoAbortLevel. This way, if someone exits + // while the form is up, knGoAbortLevel will be returned and the game + // won't be saved. The default for winning, is knGoSuccess. This way, + // if someone exits while the form is up, the game will be saved, which + // will mean the mission re-loads and can auto-advance. + + int idf; + int nGo; + + switch (so) { + case ksoObjectives: + idf = gfMultiplayer ? kidfMultiplayerObjectives : kidfObjectives; + nGo = knGoSuccess; + break; + + case ksoWinSummary: + if (m_wf & kfPlrSummaryShown) + return knGoSuccess; + m_wf |= kfPlrSummaryShown | kfPlrWinner; + idf = gfMultiplayer ? kidfMultiplayerWinSummary : kidfWinSummary; + gplrm.SendWinStats(); + if (!gfMultiplayer && !fAborting) { + // Mark current game complete. + // Doing it here ensures it is done when the mission is + // completed. + gpcptm->MarkComplete(&ggame.GetLastMissionIdentifier()); + } + nGo = knGoSuccess; + break; + + case ksoLoseSummary: + if (m_wf & kfPlrSummaryShown) + return knGoSuccess; + m_wf |= kfPlrSummaryShown | kfPlrLoser; + idf = gfMultiplayer ? kidfMultiplayerLoseSummary : kidfLoseSummary; + gplrm.SendWinStats(); + nGo = knGoAbortLevel; + break; + + default: + Assert("Invalid objective screen type passed to ShowObjectives"); + return knGoInitFailure; + } + +lbTryAgain: + ObjectivesForm *pfrm = (ObjectivesForm *)gpmfrmm->LoadForm(gpiniForms, idf, + new ObjectivesForm(this, so, fForceInfoDisplay)); + if (pfrm == NULL) { + Assert("Objectives form failed to load!"); + return knGoInitFailure; + } + + if (fAborting) { + LabelControl *plbl; + plbl = (LabelControl *)pfrm->GetControlPtr(kidcMissionResult); + if (plbl != NULL) + plbl->SetText("MISSION ABORTED"); + } + + if (so != ksoObjectives) { + gsndm.PlaySfx((idf == kidfLoseSummary || idf == kidfMultiplayerLoseSummary || fAborting) ? ksfxGameLoseLevel : ksfxGameWinLevel); + } + + int idc; + pfrm->DoModal(&idc, true, so == ksoObjectives); + delete pfrm; + + if (!gevm.IsAppStopping()) { + switch (idc) { + case kidcLoadGame: + { + Stream *pstm = PickLoadGameStream(); + if (pstm == NULL) + goto lbTryAgain; + + gpstmSavedGame = pstm; + nGo = knGoLoadSavedGame; + } + break; + + case kidcAbortMission: + case kidcRestartMission: + nGo = idc == kidcAbortMission ? knGoAbortLevel : knGoRetryLevel; + break; + } + } else { + // The game is exiting. If the level was lost, then don't save + // the re-initialize game, otherwise the user will be able + // to exit while this form is up, restart the game, and advance + // to the next level. + + if (nGo != knGoSuccess) { + ggame.SkipSaveReinitializeGame(); + } + } + + return nGo; +} + +} // namespace wi diff --git a/game/Processor.cpp b/game/Processor.cpp new file mode 100644 index 0000000..4e3fbc0 --- /dev/null +++ b/game/Processor.cpp @@ -0,0 +1,628 @@ +#include "ht.h" +#include "strings.h" + +namespace wi { + +// +// ProcessorGob implementation +// + +static StructConsts gConsts; + +// +// Gob methods +// + +bool ProcessorGob::InitClass(IniReader *pini) +{ + gConsts.gt = kgtProcessor; + gConsts.ut = kutProcessor; + gConsts.umPrerequisites = kumReactor; + gConsts.wf |= kfUntcHasFullnessIndicator; + + // Sound effects + + gConsts.sfxAbortRepair = ksfxGalaxiteProcessorAbortRepair; + gConsts.sfxRepair = ksfxGalaxiteProcessorRepair; + gConsts.sfxDamaged = ksfxGalaxiteProcessorDamaged; + gConsts.sfxSelect = ksfxGalaxiteProcessorSelect; + gConsts.sfxDestroyed = ksfxGalaxiteProcessorDestroyed; + gConsts.sfxImpact = ksfxNothing; + + return StructGob::InitClass(&gConsts, pini); +} + +void ProcessorGob::ExitClass() +{ + StructGob::ExitClass(&gConsts); +} + +ProcessorGob::ProcessorGob() : StructGob(&gConsts) +{ + m_aniOverlay.Init(m_pstruc->panid); + StartAnimation(&m_aniOverlay, 3, 0, 0); + m_gidMiner = kgidNull; + m_wptFakeMiner.wx = kwxInvalid; + m_fProcessingAnimationInProgress = false; +} + +#define knVerProcessorGobState 2 +bool ProcessorGob::LoadState(Stream *pstm) +{ + byte nVer = pstm->ReadByte(); + if (nVer != knVerProcessorGobState) + return false; + m_gidMiner = pstm->ReadWord(); + m_wptFakeMiner.wx = pstm->ReadWord(); + m_wptFakeMiner.wy = pstm->ReadWord(); + m_fProcessingAnimationInProgress = pstm->ReadByte() != 0 ? true : false; + m_fDoorMoving = pstm->ReadByte() != 0 ? true : false; + m_aniOverlay.LoadState(pstm); + return StructGob::LoadState(pstm); +} + +bool ProcessorGob::SaveState(Stream *pstm) +{ + pstm->WriteByte(knVerProcessorGobState); + pstm->WriteWord(m_gidMiner); + pstm->WriteWord(m_wptFakeMiner.wx); + pstm->WriteWord(m_wptFakeMiner.wy); + pstm->WriteByte(m_fProcessingAnimationInProgress); + pstm->WriteByte(m_fDoorMoving); + m_aniOverlay.SaveState(pstm); + return StructGob::SaveState(pstm); +} + +long CalcCreditsShare(Player *pplr) +{ + int cWarehouses = pplr->GetUnitCount(kutWarehouse); + int cProcessors = pplr->GetUnitCount(kutProcessor); + + // Takeover the credits this Processor 'owns' + + return pplr->GetCredits() / (cWarehouses + cProcessors); +} + +void ProcessorGob::GetClippingBounds(Rect *prc) +{ + UnitGob::GetClippingBounds(prc); + + if (m_wptFakeMiner.wx != kwxInvalid) { + int ifrm = m_wptFakeMiner.wy < m_wy + kwcTile ? 1 : 0; + int xMiner = PcFromUwc(m_wptFakeMiner.wx); + int yMiner = PcFromWc(m_wptFakeMiner.wy); + Rect rcFakeMiner; + m_pstruc->panid->GetBounds(4, ifrm, &rcFakeMiner); + rcFakeMiner.Offset(xMiner, yMiner); + prc->Union(&rcFakeMiner); + } +} + +bool ProcessorGob::IsTakeoverable(Player *pplr) +{ + // Have to make sure there is limit space for the miner too + + if (m_gidMiner != kgidNull) { + MinerGob *pgobMiner = (MinerGob *)ggobm.GetGob(m_gidMiner, false); + if (pgobMiner != NULL) { + if (!ggobm.IsBelowLimit(knLimitMobileUnit, pplr)) { + if (pplr == gpplrLocal) + ShowAlert(kidsUnitLimitReached); + return false; + } + } + } + return StructGob::IsTakeoverable(pplr); +} + +// Override Takeover to funds to the new owner + +void ProcessorGob::Takeover(Player *pplr) +{ + // Takeover the credits this Processor 'owns' + + long cCreditsTaken = CalcCreditsShare(m_pplr); + m_pplr->SetCredits(m_pplr->GetCredits() - cCreditsTaken, true); + pplr->SetCredits(pplr->GetCredits() + cCreditsTaken, true); + + // if there's a miner parked inside, winner! + + if (m_gidMiner != kgidNull) { + + // take it over + + MinerGob *pgobMiner = (MinerGob *)ggobm.GetGob(m_gidMiner, false); + if (pgobMiner != NULL) { + pgobMiner->Deactivate(); + Assert(ggobm.IsBelowLimit(knLimitMobileUnit, pplr)); + ggobm.TrackGobCounts(pgobMiner, false); + pgobMiner->SetOwner(pplr); + ggobm.TrackGobCounts(pgobMiner, true); + pgobMiner->Activate(); + } else { + Assert(true); // there should be a miner! + } + } + + // Takeover the Processor itself + + StructGob::Takeover(pplr); +} + +void ProcessorGob::Draw(DibBitmap *pbm, int xViewOrigin, int yViewOrigin, int nLayer) +{ + // Draw base + + StructGob::Draw(pbm, xViewOrigin, yViewOrigin, nLayer); + + // Draw overlay + + if (nLayer == knLayerDepthSorted) { + + // Don't draw the overlay on top of the destroyed base + + if (m_ani.GetStrip() != 2) { + Side side = m_pplr->GetSide(); + + // Draw fake miner if it is meant to be + + if (m_wptFakeMiner.wx != kwxInvalid) { + int ifrm = m_wptFakeMiner.wy < m_wy + kwcTile ? 1 : 0; + int xMiner = PcFromUwc(m_wptFakeMiner.wx) - xViewOrigin; + int yMiner = PcFromWc(m_wptFakeMiner.wy) - yViewOrigin; + m_pstruc->panid->DrawFrame(4, ifrm, pbm, xMiner, yMiner, side); + } + + if (m_ff & kfGobDrawFlashed) + side = (Side)-1; + else if (m_ff & kfGobBeingBuilt) + side = ksideNeutral; + + int x = PcFromUwc(m_wx) - xViewOrigin; + int y = PcFromUwc(m_wy) - yViewOrigin; + m_aniOverlay.Draw(pbm, x, y, side); + } + } else if (nLayer == knLayerSelection && (m_ff & kfGobSelected)) { + WRect wrcT; + GetUIBounds(&wrcT); + Rect rcT; + rcT.FromWorldRect(&wrcT); + rcT.Offset(-xViewOrigin, -yViewOrigin); + + long nCapacity = m_pplr->GetCapacity(); + int nPips = 0; + if (nCapacity != 0) + nPips = ((m_pplr->GetCredits() * 10) + (nCapacity / 20)) / nCapacity; + DrawFullnessIndicator(pbm, &rcT, nPips, 10); + } +} + +dword ProcessorGob::GetAnimationHash() +{ + dword dw = StructGob::GetAnimationHash(); + int nFrame = m_aniOverlay.GetFrame(); + int nStrip = m_aniOverlay.GetStrip(); + dw ^= (m_wptFakeMiner.wx << 16) | m_wptFakeMiner.wy; + return dw ^ ((nFrame << 16) | nStrip); +} + +void ProcessorGob::GetAnimationBounds(Rect *prc, bool fBase) +{ + if (fBase) { + m_ani.GetAnimationData()->GetBounds(0, 0, prc); + return; + } + + StructGob::GetAnimationBounds(prc, fBase); + if (m_wptFakeMiner.wx != kwxInvalid) { + int ifrm = m_wptFakeMiner.wy < m_wy + kwcTile ? 1 : 0; + Rect rcBounds; + m_pstruc->panid->GetBounds(4, ifrm, &rcBounds); + int xOffsetMiner = PcFromUwc(m_wptFakeMiner.wx - m_wx); + int yOffsetMiner = PcFromWc(m_wptFakeMiner.wy - m_wy); + rcBounds.Offset(xOffsetMiner, yOffsetMiner); + prc->Union(&rcBounds); + } +} + +void ProcessorGob::DrawAnimation(DibBitmap *pbm, int x, int y) +{ + StructGob::DrawAnimation(pbm, x, y); + + // Don't go further if destroyed + + if (m_ani.GetStrip() == 2) { + return; + } + Side side = m_pplr->GetSide(); + if (m_wptFakeMiner.wx != kwxInvalid) { + int xOffsetMiner = PcFromUwc(m_wptFakeMiner.wx - m_wx); + int yOffsetMiner = PcFromWc(m_wptFakeMiner.wy - m_wy); + int ifrm = m_wptFakeMiner.wy < m_wy + kwcTile ? 1 : 0; + m_pstruc->panid->DrawFrame(4, ifrm, pbm, x + xOffsetMiner, + y + yOffsetMiner, side); + } + + if (m_ff & kfGobDrawFlashed) { + side = (Side)-1; + } else if (m_ff & kfGobBeingBuilt) { + side = ksideNeutral; + } + m_aniOverlay.Draw(pbm, x, y, side); +} + +void ProcessorGob::NotifyMinersAttemptingDelivery(bool fDying) +{ + // Notify miners that are stuck attempting delivery to this processor that the + // processor is available. + + for (Gob *pgobT = ggobm.GetFirstGob(); pgobT != NULL; pgobT = ggobm.GetNextGob(pgobT)) { + if (pgobT->GetType() != kgtGalaxMiner) + continue; + if (pgobT->GetOwner() != m_pplr) + continue; + if (!(pgobT->GetFlags() & kfGobActive)) + continue; + MinerGob *pmnr = (MinerGob *)pgobT; + if (pmnr->IsAttemptingToDeliver(m_gid)) { + if (fDying) { + pmnr->SendDeliverCommand(kgidNull); + } else { + pmnr->SendDeliverCommand(m_gid); + } + } + } +} + +// +// StateMachine methods +// + +#if defined(DEBUG_HELPERS) +char *ProcessorGob::GetName() +{ + return "Processor"; +} +#endif + +int ProcessorGob::ProcessStateMachineMessage(State st, Message *pmsg) +{ +BeginStateMachine + OnMsg(kmidGalaxiteDelivery) + // Remember the MinerGob and hide it (deselect it first) + // NOTE: It still occupies terrain and is counted in its owning + // Player's unit counts. + + m_gidMiner = pmsg->smidSender; + MinerGob *pgobMiner = (MinerGob *)ggobm.GetGob(m_gidMiner, false); + pgobMiner->Hide(true); + + // Set / clear reservation / occupation bits + + TPoint tpt; + pgobMiner->GetTilePosition(&tpt); + gsim.GetLevel()->GetTerrainMap()->SetFlags(tpt.tx, tpt.ty, 1, 1, kbfStructure); + gsim.GetLevel()->GetTerrainMap()->ClearFlags(tpt.tx, tpt.ty, 1, 1, kbfMobileUnit); + + SetState(kstProcessorGetMiner); + + OnMsg(kmidAnimationComplete) + m_fProcessingAnimationInProgress = false; + m_unvl.MinSkip(); + + //----------------------------------------------------------------------- + + State(kstBeingBuilt) + OnMsg(kmidBuildComplete) + // When a Processor is built at run time it brings a fresh Miner along with + // itself (included, free of charge!). + + if (ggobm.IsBelowLimit(knLimitMobileUnit, m_pplr)) { + MinerGob *pmnr = new MinerGob(); + Assert(pmnr != NULL, "out of memory!"); + if (pmnr == NULL) + goto lbError; + if (!pmnr->Init(m_wx + kwcTile, m_wy + (kwcTile * 2), m_pplr, 0, 0, NULL)) { + delete pmnr; + goto lbError; + } + + // Make sure the Miner prefers to return to this Processor + + pmnr->SetFavoriteProcessor(m_gid); + + // Hide the Miner so we can 'launch' it, same as it is launched + // after it completes a Galaxite delivery. + + m_gidMiner = pmnr->GetId(); + pmnr->GetCenter(&m_wptFakeMiner); + m_wptFakeMiner.wy = m_wy + WcFromTile16ths(15); + pmnr->Hide(true); + + // Set / clear reservation / occupation bits + + TPoint tpt; + pmnr->GetTilePosition(&tpt); + gsim.GetLevel()->GetTerrainMap()->SetFlags(tpt.tx, tpt.ty, 1, 1, kbfStructure); + gsim.GetLevel()->GetTerrainMap()->ClearFlags(tpt.tx, tpt.ty, 1, 1, kbfMobileUnit); + + StartAnimation(&m_aniOverlay, 3, -1, 0); + SetState(kstProcessorPutMiner); + } else { +lbError: + SetState(kstIdle); + } + Activate(); + + OnEnter + // init will mark the building location as occupied. + // additionally here we'll mark the spot in front of our front door + // as occupied so the miner can exit when we're done building. + // We twiddle these flags on kmidGalaxiteDelivery and kstPutMiner + + Assert(m_pstruc->ctx == 3 && m_pstruc->cty == 2); + gsim.GetLevel()->GetTerrainMap()->SetFlags(TcFromWc(m_wx) + 1, + TcFromWc(m_wy) + m_pstruc->cty, 1, 1, kbfStructure); + + return StructGob::ProcessStateMachineMessage(st, pmsg); + + //----------------------------------------------------------------------- + + State(kstProcessorGetMiner) + OnEnter + MinerGob *pgobMiner = (MinerGob *)ggobm.GetGob(m_gidMiner, false); + if (pgobMiner != NULL) { + pgobMiner->GetCenter(&m_wptFakeMiner); + pgobMiner->Select(false); + pgobMiner->Hilight(false); + StartAnimation(&m_aniOverlay, 3, 0, 0); + m_fDoorMoving = false; + } + + OnExit + // To keep StructGob from attempting to handle a state it doesn't understand + + OnUpdate + m_unvl.MinSkip(); + + // Open the door when the miner gets close + + if (m_wptFakeMiner.wy < m_wy + WcFromTile16ths(35)) { + AdvanceAnimation(&m_aniOverlay); + if (!m_fDoorMoving) { + m_fDoorMoving = true; + if (m_pplr == gpplrLocal) + gsndm.PlaySfx(ksfxGalaxiteProcessorDoorOpening); + } + } + + if (m_wptFakeMiner.wy > m_wy + WcFromTile16ths(15)) { + m_wptFakeMiner.wy -= kwcTile16th; + MarkRedraw(); + } else { + SetState(kstProcessorTakeGalaxite); + } + + DefUpdate(); + + State(kstProcessorTakeGalaxite) + OnEnter + OnExit + // To keep StructGob from attempting to handle a state it doesn't understand + + OnUpdate + m_unvl.MinSkip(); + + // Add credits while taking galaxite from miner + + MinerGob *pgobMiner = (MinerGob *)ggobm.GetGob(m_gidMiner, false); + Assert(pgobMiner != NULL); + int nAmount = pgobMiner->GetGalaxiteAmount(); + if (nAmount > 0) { + nAmount -= 2; + int nGalaxiteValue = m_pplr->GetHandicap() & kfHcapIncreasedMinerLoadValue ? + ((knGalaxiteValue * (100 + knIncreasedMinerLoadValuePercent)) + 50) / 100 : knGalaxiteValue; + long nCreditsNew = m_pplr->GetCredits() + 2 * nGalaxiteValue; + if (nCreditsNew <= m_pplr->GetCapacity()) { + MarkRedraw(); + m_pplr->SetCredits(nCreditsNew, true); + pgobMiner->SetGalaxiteAmount(nAmount); + + // Kick off the processing animation if it isn't already in progress + + if (!m_fProcessingAnimationInProgress) { + if (ggobm.IsBelowLimit(knLimitSupport)) { + PuffGob *pgob = new PuffGob(); + Assert(pgob != NULL, "out of memory!"); + if (pgob != NULL) + pgob->Init(m_wx, m_wy, m_gid); + } + m_fProcessingAnimationInProgress = true; + } + } else { + // Need more storage! + + gsim.GetLevel()->GetTriggerMgr()->SetConditionTrue(knGalaxiteCapacityReachedCondition, + GetSideMask(GetSide())); + +// TUNE: +#define kctIntervalStorageNotify (30 * 100) + if (m_pplr == gpplrLocal) { + static long s_tLastStorageNotify = 0; + long tCurrent = HostGetTickCount(); + if (s_tLastStorageNotify == 0 || (tCurrent - s_tLastStorageNotify) >= kctIntervalStorageNotify) { + s_tLastStorageNotify = tCurrent; + gsndm.PlaySfx(ksfxGalaxiteWarehouseTooFull); + ShowAlert(kidsWarehouseTooFull); + } + } + } + } else { + SetState(kstProcessorPutMiner); + } + DefUpdate(); + + State(kstProcessorPutMiner) + OnEnter + m_fDoorMoving = false; + + OnExit + // Restore the MinerGob. + + MinerGob *pgobMiner = (MinerGob *)ggobm.GetGob(m_gidMiner, false); + Assert(pgobMiner != NULL); + pgobMiner->Hide(false); + + // Set / clear reservation / occupation bits + + TPoint tpt; + pgobMiner->GetTilePosition(&tpt); + gsim.GetLevel()->GetTerrainMap()->ClearFlags(tpt.tx, tpt.ty, 1, 1, kbfStructure); + gsim.GetLevel()->GetTerrainMap()->SetFlags(tpt.tx, tpt.ty, 1, 1, kbfMobileUnit); + + // Have the Miner resume mining + // NOTE: We send a message rather than call MinerGob::Mine method directly + // so the state change will occur immediately. + + SendMineCommand(pgobMiner->GetId(), kwxInvalid, 0); + + m_gidMiner = kgidNull; + m_wptFakeMiner.wx = kwxInvalid; + + // Notify any miners attempting to deliver + + NotifyMinersAttemptingDelivery(false); + + OnUpdate + m_unvl.MinSkip(); + + WPoint wptDst; + ((MinerGob *)ggobm.GetGob(m_gidMiner, false))->GetCenter(&wptDst); + if (m_wptFakeMiner.wy > m_wy + WcFromTile16ths(27)) { + int ifrm = m_aniOverlay.GetFrame(); + if (ifrm > 0) { + SetAnimationFrame(&m_aniOverlay, --ifrm); + if (!m_fDoorMoving) { + m_fDoorMoving = true; + if (m_pplr == gpplrLocal) + gsndm.PlaySfx(ksfxGalaxiteProcessorDoorClosing); + } + } + } + + if (m_wptFakeMiner.wy < wptDst.wy) { + m_wptFakeMiner.wy += kwcTile16th; + MarkRedraw(); + } else { + SetState(kstIdle); + } + DefUpdate(); + + State(kstDying) + OnEnter +#if 0 // DWM: crazy last minute change. Players don't lose credits when they sell Processors + // Sorry, player must lose some Credits along with this Processor + + if (m_ff & kfGobActive) + m_pplr->SetCredits(m_pplr->GetCredits() - CalcCreditsShare(m_pplr), true); +#endif + + // Remove the Miner too if it's inside the Processor + + MinerGob *pgobMiner = (MinerGob *)ggobm.GetGob(m_gidMiner, false); + if (pgobMiner != NULL) { + // Clear the structure bit so the "miner tile" is free again + + TPoint tpt; + pgobMiner->GetTilePosition(&tpt); + gsim.GetLevel()->GetTerrainMap()->ClearFlags(tpt.tx, tpt.ty, 1, 1, kbfStructure); + + // Deactivate the miner first which will subtract + // it from its owning Player's unit counts. + + pgobMiner->Deactivate(); + ggobm.RemoveGob(pgobMiner); + delete pgobMiner; + m_gidMiner = kgidNull; + } + + // The miner spot is reserved while this gob is being built. Clear the bit just in case. + // We don't need to check anything, just clearing it is safe. + + Assert(m_pstruc->ctx == 3 && m_pstruc->cty == 2); + gsim.GetLevel()->GetTerrainMap()->ClearFlags(TcFromWc(m_wx) + 1, + TcFromWc(m_wy) + m_pstruc->cty, 1, 1, kbfStructure); + + int nHandled = StructGob::ProcessStateMachineMessage(st, pmsg); + + // Now that this gob is deactivated, notify any miners attempting + // deliver to find a different processor + + NotifyMinersAttemptingDelivery(true); + return nHandled; + +#if 0 +EndStateMachineInherit(StructGob) +#else + return knHandled; + } + } else { + return (int)StructGob::ProcessStateMachineMessage(st, pmsg); + } + return (int)StructGob::ProcessStateMachineMessage(st, pmsg); +#endif +} + +// +// PuffGob implementation +// UNDONE: Consider making AnimGob savable by passing in an index +// referring to a global array of AnimData's (then the index can be saved +// and we can get rid of PuffGob). +// + +PuffGob::PuffGob() +{ +} + +bool PuffGob::Init(WCoord wx, WCoord wy, StateMachineId smidNotify) +{ + UnitConsts *puntc = gapuntc[kutProcessor]; + return AnimGob::Init(wx, wy, kfAnmDeleteWhenDone | kfAnmSmokeFireLayer, NULL, puntc->panid, puntc->panid->GetStripIndex("smoke"), smidNotify, NULL); +} + +#define knVerPuffGobState 1 +bool PuffGob::LoadState(Stream *pstm) +{ + byte nVer = pstm->ReadByte(); + if (nVer != knVerPuffGobState) + return false; + UnitConsts *puntc = gapuntc[kutProcessor]; + m_ani.Init(puntc->panid); + m_ani.SetStrip(puntc->panid->GetStripIndex("smoke")); + m_ani.LoadState(pstm); + m_smidNotify = pstm->ReadWord(); + m_wfAnm = kfAnmDeleteWhenDone | kfAnmSmokeFireLayer; + return AnimGob::LoadState(pstm); +} + +bool PuffGob::SaveState(Stream *pstm) +{ + pstm->WriteByte(knVerPuffGobState); + m_ani.SaveState(pstm); + pstm->WriteWord(m_smidNotify); + return AnimGob::SaveState(pstm); +} + +bool PuffGob::IsSavable() +{ + // Because AnimGob is not savable, we need to override + + return true; +} + +GobType PuffGob::GetType() +{ + return kgtPuff; +} + +} // namespace wi diff --git a/game/README.txt b/game/README.txt new file mode 100644 index 0000000..3d02d76 --- /dev/null +++ b/game/README.txt @@ -0,0 +1,100 @@ +Game history and notes +---------------------- + +Originally this game was started in 2001 and targeted Palm based and +PocketPC based handheld devices. The main development environment was +Windows. The game was developed and run on Windows, which allowed for +fast iteration, and ports were simultaneously written and maintained +for Palm and PocketPC. + +Code in the "game" directory is cross-platform. Platform +specific code is in subdirectories. The platform code for the original +Palm and Windows CE based Pocket PC devices is currently not part of this +release. It was removed because these platforms are long gone, and +putting time and effort into maintaining them would not be a good use +of time. If there is interest in these platforms, we can release this code +(email Scott and Darrin). The first Palm and PocketPC releases were made in +2003. + +Later in 2007 the iPhone hit the scene and the iOS version was started. +The iOS platform specific code evolved while coding on a Mac using OS X. +The Windows platform code wasn't being maintained during this period, so it +is a bit out of sync with the game side. Since the iOS device dev environment +and simulator is reasonably good, there was also no effort put into an OS X +specific platform layer (for running on Macs). The first iOS release was made +in 2008. + +Fast forward to 2012/13. An SDL layer (Simple Direct Media Layer, see +libsdl.org) was started, but not (yet) completed. The idea with the SDL +version is to use it as the platform layer for potentially all platforms. +Rather than having N platform specific layers, have SDL address the bulk +platform requirements, and then have smaller platform specific code as +necessary. As of 6/2014 this hasn't been finished but this is the direction +to go in for the future. In other words, when an Android version is created, +the SDL layer should be completed first rather than creating an Android layer. +Once this is done, the iOS version can be moved to the SDL version as well (and, +Windows and Mac versions can be created easily). + +New artwork +----------- + +Currently the game uses 2D artwork that was created for 8 bit Palm devices in +both "low res" and "high res" formats. A decision was made to ship the +iOS version with the original artwork, rather than waiting for and +incorporating all new artwork. Because the iPhone res was so much higher than +the old Palms, the high res Palm artwork was used and autoscaled up by 1.33x. +Around 2008-09, we commissioned much higher res 32 bit artwork to be created, +with 2-4x the animation frames. This was never incorporated into the game, +but it can be. We are releasing this new artwork with the game. The artwork +is again 2D and rendered at a fixed size. A few issues with this new artwork: + +- Current high res devices have again made this new artwork small by + comparison. The proper path forward has to be evaluated (3D perhaps?). +- The render code is almost all handwritten, expects 8 bit artwork, and + is on the game side. Ideally, the platform specific side would handle + drawing and the game side wouldn't have format dependencies. +- The unit animations are currently tied into multiplayer simulation state. + Read below. + +The game's multiplayer model works by synchronized game state. In other words, +it assumes that if all clients get the same commands at the same time, they +will progress to the exact same state at time X. This makes maintaining game +state easy - just communicate the commands, not the game state. An issue that +currently exists is that the game simulation, and the unit animations are +joined at the hip, so to speak. If you have two clients, one +that has a Liberator with 16 frames of animation, the other with 24 frames +of animation, these clients won't be in sync with each other. Either this +design needs to change so animation isn't synchronized with game state this +way, or all clients need to animate the exact same way. + +Platform specific code directories +---------------------------------- + +palm (removed) +ce (removed) +iphone (working. should of been named "ios") +sdl (not too far from working. Expected to be future king). +win (not working. Left for reference.) + +There are more details about the sdl port to discuss in a more direct way +through email. Please contact Scott and Darrin. + +Why are we releasing this +------------------------- + +We have enjoyed creating and supporting the game community of users over the +years. We want to see the game continue to grow and flourish beyond what we +are capable of at this time. We believe releasing it with an open license +will make it an opportunity for others to build on and extend going forward. +For example we hope to see an Android port come out of this, and we're looking +forward to see what other innovations happen. + +We ask that developers contribute changes back for others to use and benefit +from. This game has lot of potential to grow and get better. RTS games continue +to evolve with innovating features and game play, and mobile devices these days +are very capable. At a minimum, there is a huge amount of opportunity to grow +this game in these directions to take advantage of the current state of the +art. + +Scott Ludwig (scottlu@gmail.com) +Darrin Massena (darrin@massena.com) diff --git a/game/Radar.cpp b/game/Radar.cpp new file mode 100644 index 0000000..8565c2a --- /dev/null +++ b/game/Radar.cpp @@ -0,0 +1,121 @@ +#include "ht.h" + +namespace wi { + +// +// RadarGob implementation +// + +static StructConsts gConsts; + +// +// Gob methods +// + +bool RadarGob::InitClass(IniReader *pini) +{ + gConsts.gt = kgtRadar; + gConsts.ut = kutRadar; + gConsts.umPrerequisites = kumReactor; + + // Sound effects + + gConsts.sfxAbortRepair = ksfxRadarAbortRepair; + gConsts.sfxRepair = ksfxRadarRepair; + gConsts.sfxDamaged = ksfxRadarDamaged; + gConsts.sfxSelect = ksfxRadarSelect; + gConsts.sfxDestroyed = ksfxRadarDestroyed; + gConsts.sfxImpact = ksfxNothing; + + // Wants power notification + + gConsts.wf |= kfUntcNotifyPowerLowHigh; + + return StructGob::InitClass(&gConsts, pini); +} + +void RadarGob::ExitClass() +{ + StructGob::ExitClass(&gConsts); +} + +RadarGob::RadarGob() : StructGob(&gConsts) +{ +} + +// Struct gob methods + +void RadarGob::Activate() +{ + StructGob::Activate(); + if (gpplrLocal == m_pplr && m_pplr->GetUnitCount(kutRadar) == 1) + gpmm->CalcPoweredRadar(); +} + +void RadarGob::Deactivate() +{ + StructGob::Deactivate(); + if (gpplrLocal == m_pplr && m_pplr->GetUnitCount(kutRadar) == 0) + gpmm->CalcPoweredRadar(); +} + +// +// StateMachine methods +// + +#if defined(DEBUG_HELPERS) +char *RadarGob::GetName() +{ + return "Radar"; +} +#endif + +int RadarGob::ProcessStateMachineMessage(State st, Message *pmsg) +{ +BeginStateMachine + OnMsg(kmidPlaySfx) + if (gpplrLocal == m_pplr) { + gsndm.PlaySfx((Sfx)pmsg->PlaySfx.sfx); + } + + State(kstBeingBuilt) + OnMsg(kmidBuildComplete) + + // wait a bit, then announce new structure options if + // this is our first radar + + if (m_pplr->GetUnitCount(kutRadar) == 0) { + Message msgT; + memset(&msgT, 0, sizeof(msgT)); + msgT.mid = kmidPlaySfx; + msgT.smidSender = m_gid; + msgT.smidReceiver = m_gid; + msgT.PlaySfx.sfx = ksfxGameNewStructureOptions; + gsmm.SendDelayedMsg(&msgT, 96); + } + + return StructGob::ProcessStateMachineMessage(st, pmsg); + + State(kstIdle) + OnUpdate + // Don't animate if power is too low + // This relies on kmidPowerLowHigh to wake up if power situation changes + + if (!m_pplr->IsPowerLow()) + AdvanceAnimation(&m_ani); + + DefUpdate(); + +#if 0 +EndStateMachineInherit(StructGob) +#else + return knHandled; + } + } else { + return (int)StructGob::ProcessStateMachineMessage(st, pmsg); + } + return (int)StructGob::ProcessStateMachineMessage(st, pmsg); +#endif +} + +} // namespace wi diff --git a/game/RawBitmap.cpp b/game/RawBitmap.cpp new file mode 100644 index 0000000..f6f7073 --- /dev/null +++ b/game/RawBitmap.cpp @@ -0,0 +1,257 @@ +#include "ht.h" + +namespace wi { + +// This exists soley for the purpose of being overridden + +HtBitmap::~HtBitmap() +{ +} + +RawBitmap *LoadRawBitmap(char *pszFn) +{ + RawBitmap *prbm = new RawBitmap(); + if (prbm == NULL) + return NULL; + if (!prbm->Init(pszFn)) { + delete prbm; + return NULL; + } + return prbm; +} + +RawBitmap::RawBitmap() +{ + m_pfil = NULL; + m_cx = 0; + m_cy = 0; +} + +RawBitmap::~RawBitmap() +{ + if (m_pfil != NULL) { + gpakr.fclose(m_pfil); + m_pfil = NULL; + } +} + +bool RawBitmap::Init(char *pszFn) +{ + File *pfil = gpakr.fopen(pszFn, "rb"); + if (pfil == NULL) + return false; + + short cx; + if (gpakr.fread(&cx, sizeof(cx), 1, pfil) != 1) { + gpakr.fclose(pfil); + return false; + } + + short cy; + if (gpakr.fread(&cy, sizeof(cy), 1, pfil) != 1) { + gpakr.fclose(pfil); + return false; + } + + m_pfil = pfil; + m_cx = BigWord(cx); + m_cy = BigWord(cy); + return true; +} + +void RawBitmap::GetSize(Size *psiz) +{ + psiz->cx = m_cx; + psiz->cy = m_cy; +} + +#if 0 +// fread based blt + +void RawBitmap::BltTo(DibBitmap *pbmDst, int xDst, int yDst, Rect *prcSrc) +{ + // Get dib dimensions + + Size sizDib; + pbmDst->GetSize(&sizDib); + + // Src rect to blt from + + if (prcSrc == NULL) { + Rect rcSrcT; + rcSrcT.Set(0, 0, sizDib.cx, sizDib.cy); + prcSrc = &rcSrcT; + } + + // Right and bottom edge clipping + + if (sizDib.cx - xDst < prcSrc->Width()) + prcSrc->right = prcSrc->left + sizDib.cx - xDst; + if (sizDib.cy - yDst < prcSrc->Height()) + prcSrc->bottom = prcSrc->top + sizDib.cy - yDst; + + // Left and top edge clipping + + int xDstT = xDst; + if (xDstT < 0) { + prcSrc->left -= xDstT; + xDstT = 0; + } + int yDstT = yDst; + if (yDstT < 0) { + prcSrc->top -= yDstT; + yDstT = 0; + } + + // Anything to blt? + + if (prcSrc->IsEmpty()) + return; + + // Get destination address and dest row bytes + + int cbRow = pbmDst->GetRowBytes(); + byte *pbDst = pbmDst->GetBits() + (long)yDstT * cbRow + xDstT; + + // Seek to the start of bits + + gpakr.fseek(m_pfil, sizeof(word) * 2 + (long)prcSrc->top * m_cx + prcSrc->left, SEEK_SET); + + // Can't use the scratch buffer to chunk because the decompressor + // uses it! Use this lame approach for now. + + int cbSrcReturn = m_cx - prcSrc->Width(); + int cy = prcSrc->Height(); + for (int y = 0; y < cy; y++) { + if (gpakr.fread(pbDst, prcSrc->Width(), 1, m_pfil) != 1) + return; + if (cbSrcReturn != 0) + gpakr.fseek(m_pfil, cbSrcReturn, SEEK_CUR); + pbDst += cbRow; + } +} +#else +// Record mapping based blt + +void RawBitmap::BltTo(class DibBitmap *pbmDst, int xDst, int yDst, Side side, Rect *prcSrc) +{ + BltTo(pbmDst, xDst, yDst, prcSrc); +} + +void RawBitmap::BltTo(DibBitmap *pbmDst, int xDst, int yDst, Rect *prcSrc) +{ + // Src rect to blt from + + if (prcSrc == NULL) { + Rect rcSrcT; + rcSrcT.Set(0, 0, m_cx, m_cy); + prcSrc = &rcSrcT; + } + + // Right and bottom edge clipping + + Size sizDib; + pbmDst->GetSize(&sizDib); + if (sizDib.cx - xDst < prcSrc->Width()) + prcSrc->right = prcSrc->left + sizDib.cx - xDst; + if (sizDib.cy - yDst < prcSrc->Height()) + prcSrc->bottom = prcSrc->top + sizDib.cy - yDst; + + // Left and top edge clipping + + int xDstT = xDst; + if (xDstT < 0) { + prcSrc->left -= xDstT; + xDstT = 0; + } + int yDstT = yDst; + if (yDstT < 0) { + prcSrc->top -= yDstT; + yDstT = 0; + } + + // Anything to blt? + + if (prcSrc->IsEmpty()) + return; + + // Get destination address and dest row bytes + + int cbRowDst = pbmDst->GetRowBytes(); + byte *pbDst = pbmDst->GetBits() + (long)yDstT * cbRowDst + xDstT; + + // Seek to the start of bits + + long offBits = sizeof(word) * 2 + (long)prcSrc->top * m_cx + prcSrc->left; + gpakr.fseek(m_pfil, offBits, SEEK_SET); + int nRecCurrent = BigWord(m_pfil->nRecFirst) + m_pfil->nRecOffStream; + dword offRecCurrent = m_pfil->offRecStart; + + // Try to blt chunks as large as possible. If a scan intersects a record boundary, handle it + // specially. + + int cxBlt = prcSrc->Width(); + int cyLeft = prcSrc->Height(); + byte *pbRec = NULL; + word cbRec; + dword dwCookie; + while (cyLeft > 0) { + // See how much we can blt from the current record + + if (pbRec == NULL) { + pbRec = m_pfil->prnfo->ppdbReader->MapRecord(nRecCurrent, &dwCookie, &cbRec); + if (pbRec == NULL) + break; + } + + dword cbRecLeft = offRecCurrent + cbRec - offBits; + int cyRecWholeScans = cbRecLeft / m_cx; + + // Blt as much as we can + + int cyBlt = _min(cyLeft, cyRecWholeScans); + LeftToRightBltThunk(&pbRec[offBits - offRecCurrent], m_cx, pbDst, cbRowDst, cxBlt, cyBlt); + cyLeft -= cyBlt; + if (cyLeft == 0) + break; + offBits += (long)cyBlt * m_cx; + pbDst += (long)cyBlt * cbRowDst; + + // Does the next scan intersect a record boundary? + + if (offRecCurrent + cbRec - offBits < (word)cxBlt) { + // Blt left half, unlock current record, lock next record, blt right half + + int cbLeftHalf = (int)(offRecCurrent + cbRec - offBits); + memcpy(pbDst, &pbRec[offBits - offRecCurrent], cbLeftHalf); + m_pfil->prnfo->ppdbReader->UnmapRecord(nRecCurrent, dwCookie); + offRecCurrent += cbRec; + nRecCurrent++; + pbRec = m_pfil->prnfo->ppdbReader->MapRecord(nRecCurrent, &dwCookie, &cbRec); + if (pbRec == NULL) + return; + memcpy(pbDst + cbLeftHalf, pbRec, cxBlt - cbLeftHalf); + pbDst += cbRowDst; + offBits += m_cx; + cyLeft--; + continue; + } + + // Next scan doesn't intersect a record boundary + + memcpy(pbDst, &pbRec[offBits - offRecCurrent], cxBlt); + m_pfil->prnfo->ppdbReader->UnmapRecord(nRecCurrent, dwCookie); + pbRec = NULL; + offRecCurrent += cbRec; + offBits += m_cx; + nRecCurrent++; + pbDst += cbRowDst; + cyLeft--; + } + + if (pbRec != NULL) + m_pfil->prnfo->ppdbReader->UnmapRecord(nRecCurrent, dwCookie); +} +#endif + +} // namespace wi diff --git a/game/Reactor.cpp b/game/Reactor.cpp new file mode 100644 index 0000000..691c417 --- /dev/null +++ b/game/Reactor.cpp @@ -0,0 +1,52 @@ +#include "ht.h" + +namespace wi { + +// +// ReactorGob implementation +// + +static StructConsts gConsts; + +// +// Gob methods +// + +bool ReactorGob::InitClass(IniReader *pini) +{ + gConsts.gt = kgtReactor; + gConsts.ut = kutReactor; + + // Sound effects + + gConsts.sfxAbortRepair = ksfxReactorAbortRepair; + gConsts.sfxRepair = ksfxReactorRepair; + gConsts.sfxDamaged = ksfxReactorDamaged; + gConsts.sfxSelect = ksfxReactorSelect; + gConsts.sfxDestroyed = ksfxReactorDestroyed; + gConsts.sfxImpact = ksfxNothing; + + return StructGob::InitClass(&gConsts, pini); +} + +void ReactorGob::ExitClass() +{ + StructGob::ExitClass(&gConsts); +} + +ReactorGob::ReactorGob() : StructGob(&gConsts) +{ +} + +// +// StateMachine methods +// + +#if defined(DEBUG_HELPERS) +char *ReactorGob::GetName() +{ + return "Reactor"; +} +#endif + +} // namespace wi \ No newline at end of file diff --git a/game/Replicator.cpp b/game/Replicator.cpp new file mode 100644 index 0000000..e791a16 --- /dev/null +++ b/game/Replicator.cpp @@ -0,0 +1,560 @@ +#include "ht.h" +#include "strings.h" + +namespace wi { + +// +// ReplicatorGob implementation +// + +static AnimationData *s_panidActivator = NULL; +static BuilderConsts gConsts; + +#if defined(DEBUG_HELPERS) +char *ReplicatorGob::GetName() +{ + return "Replicator"; +} +#endif + +bool ReplicatorGob::InitClass(IniReader *pini) +{ + gConsts.gt = kgtReplicator; + gConsts.ut = kutReplicator; + + // Sound effects + gConsts.sfxUnitReady = ksfxReplicatorBuild; + +#if 0 + gConsts.sfxPowerOn = ksfxReplicatorOn; + gConsts.sfxPowerOff = ksfxReplicatorOff; + gConsts.sfxUnitBuildAbort = ksfxReplicatorAbortRecruiting; + gConsts.sfxUnitReady = ksfxReplicatorUnitReady; + gConsts.sfxAbortRepair = ksfxReplicatorAbortRepair; + gConsts.sfxDamaged = ksfxReplicatorDamaged; + gConsts.sfxDestroyed = ksfxReplicatorDestroyed; + gConsts.sfxRepair = ksfxReplicatorRepair; + gConsts.sfxSelect = ksfxReplicatorSelect; +#endif + + // Preload the Activator animation data + + if (s_panidActivator == NULL) { + s_panidActivator = LoadAnimationData("activator.anir"); + if (s_panidActivator == NULL) + return false; + } + + return StructGob::InitClass(&gConsts, pini); +} + +void ReplicatorGob::ExitClass() +{ + delete s_panidActivator; + s_panidActivator = NULL; + + StructGob::ExitClass(&gConsts); +} + +ReplicatorGob::ReplicatorGob() : StructGob(&gConsts) +{ + m_fEnabled = false; + m_fReplicating = false; + m_pplrNeedCredits = NULL; +} + +bool ReplicatorGob::Init(WCoord wx, WCoord wy, Player *pplr, fix fxHealth, dword ff, const char *pszName) +{ + if (!StructGob::Init(wx, wy, pplr, fxHealth, ff, pszName)) + return false; + + // Clear out the Replicator's entrance and exits + + TerrainMap *ptrmap = gsim.GetLevel()->GetTerrainMap(); + ptrmap->ClearFlags(TcFromWc(m_wx) + kdtxReplicatorInput, TcFromWc(m_wy) + kdtyReplicatorInput, 1, 1, kbfStructure); + ptrmap->ClearFlags(TcFromWc(m_wx) + kdtxReplicatorOutput1 - 1, TcFromWc(m_wy) + kdtyReplicatorOutput1 + 1, 1, 2, kbfStructure); + ptrmap->ClearFlags(TcFromWc(m_wx) + kdtxReplicatorOutput2 + 1, TcFromWc(m_wy) + kdtyReplicatorOutput2 + 1, 1, 2, kbfStructure); + + return true; +} + +#define knVerReplicatorGobState 3 +bool ReplicatorGob::LoadState(Stream *pstm) +{ + byte nVer = pstm->ReadByte(); + if (nVer != knVerReplicatorGobState) + return false; + m_fEnabled = pstm->ReadByte() != 0; + m_fReplicating = pstm->ReadByte() != 0; + m_ifrmLights = pstm->ReadByte(); + s_cReplicators = pstm->ReadWord(); + pstm->Read(s_atptInput, sizeof(s_atptInput)); + Pid pid = pstm->ReadWord(); + if (pid != kpidNeutral) + m_pplrNeedCredits = gplrm.GetPlayerFromPid(pid); + + return StructGob::LoadState(pstm); +} + +bool ReplicatorGob::SaveState(Stream *pstm) +{ + pstm->WriteByte(knVerReplicatorGobState); + pstm->WriteByte(m_fEnabled); + pstm->WriteByte(m_fReplicating); + pstm->WriteByte(m_ifrmLights); + pstm->WriteWord(s_cReplicators); + pstm->Write(s_atptInput, sizeof(s_atptInput)); + Pid pid = m_pplrNeedCredits == NULL ? kpidNeutral : m_pplrNeedCredits->GetId(); + pstm->WriteWord(pid); + + return StructGob::SaveState(pstm); +} + +void ReplicatorGob::GetClippingBounds(Rect *prc) +{ + // Union of the two strips that combine to form the Replicator image + + m_pstruc->panid->GetBounds(0, 0, prc); + Rect rcT; + m_pstruc->panid->GetBounds(1, 0, &rcT); + prc->Union(&rcT); + + // If selected, union with that rect + + if (gwfPerfOptions & kfPerfSelectionBrackets) { + if (m_ff & kfGobSelected) { + Rect rcSel; + rcSel.FromWorldRect(&m_pstruc->wrcUIBounds); +// rcSel.top -= kcyHealthBar; + prc->Union(&rcSel); + } + } + + prc->Offset(PcFromUwc(m_wx), PcFromUwc(m_wy)); +} + +void ReplicatorGob::Draw(DibBitmap *pbm, int xViewOrigin, int yViewOrigin, int nLayer) +{ + if (nLayer == knLayerDepthSorted) { + int x = PcFromUwc(m_wx) - xViewOrigin; + int y = PcFromUwc(m_wy) - yViewOrigin; + + Side side = m_pplr->GetSide(); + if (m_ff & kfGobDrawFlashed) + side = (Side)-1; + + m_ani.SetFrame(0); + m_ani.SetStrip(m_fEnabled ? 2 : 0); + m_ani.Draw(pbm, x, y, side); + m_ani.SetStrip(m_fEnabled ? 3 : 1); +#ifdef IPHONE + // When this is scaled up by 1 1/3 (scaling 24 art to 32), + // the upper right quadrant piece is 55 high, which translates after + // rounding to 73, yet the lower right quad piece wants to go at + // location 56 (effectively) which scales to 75, so there is a gap. + + m_ani.Draw(pbm, x, y - 1, side); // ugly hack +#else + m_ani.Draw(pbm, x, y, side); +#endif + + if (m_fReplicating) { + m_ani.SetStrip(m_pmuntc->panid->GetStripIndex("l 0")); + m_ani.SetFrame(m_ifrmLights / 2); + m_ani.Draw(pbm, x, y, side); + } + + } else if (nLayer == knLayerSelection) { + if (m_ff & kfGobSelected) { + Rect rcT; + rcT.FromWorldRect(&m_puntc->wrcUIBounds); + rcT.Offset(-xViewOrigin + PcFromWc(m_wx), -yViewOrigin + PcFromWc(m_wy)); + DrawSelectionIndicator(pbm, &rcT, m_fxHealth, m_puntc->GetArmorStrength()); + } + + } else { + StructGob::Draw(pbm, xViewOrigin, yViewOrigin, nLayer); + } +} + +void ReplicatorGob::GetInputTilePosition(TPoint *ptpt) +{ + GetTilePosition(ptpt); + ptpt->tx += kdtxReplicatorInput; + ptpt->ty += kdtyReplicatorInput; +} + +void ReplicatorGob::Enable(bool fEnable) +{ + if (m_fEnabled != fEnable) { + gsndm.PlaySfx(fEnable ? ksfxReplicatorOn : ksfxReplicatorOff); + m_fEnabled = fEnable; + MarkRedraw(); + } +} + +void ReplicatorGob::Activate() +{ + TPoint tpt; + GetTilePosition(&tpt); + AddReplicatorInputPoint(tpt.tx + kdtxReplicatorInput, tpt.ty + kdtyReplicatorInput); + StructGob::Activate(); +} + +void ReplicatorGob::Deactivate() +{ + TPoint tpt; + GetTilePosition(&tpt); + RemoveReplicatorInputPoint(tpt.tx + kdtxReplicatorInput, tpt.ty + kdtyReplicatorInput); + StructGob::Deactivate(); +} + +bool ReplicatorGob::ClearOutputBay(TCoord txBay, TCoord tyBay, TCoord txOut, TCoord tyOut) +{ + for (Gid gid = ggobm.GetFirstGid(txBay, tyBay); gid != kgidNull; gid = ggobm.GetNextGid(gid)) { + MobileUnitGob *pmunt = (MobileUnitGob *)ggobm.GetGob(gid); + if (pmunt == NULL) + continue; + if (!(pmunt->GetFlags() & kfGobMobileUnit)) + continue; + + // Don't ask it to move if it is already trying to + + if (pmunt->IsReadyForCommand() && !pmunt->IsMobile()) { + Message msgT; + msgT.mid = kmidMoveCommand; + msgT.smidSender = m_gid; + msgT.smidReceiver = pmunt->GetId(); + msgT.MoveCommand.wptTarget.wx = WcFromTc(txOut) + kwcTileHalf; + msgT.MoveCommand.wptTarget.wy = WcFromTc(tyOut) + kwcTileHalf; + msgT.MoveCommand.gidTarget = kgidNull; + msgT.MoveCommand.wptTargetCenter.wx = msgT.MoveCommand.wptTarget.wx; + msgT.MoveCommand.wptTargetCenter.wy = msgT.MoveCommand.wptTarget.wy; + msgT.MoveCommand.tcTargetRadius = 0; + msgT.MoveCommand.wcMoveDistPerUpdate = ((MobileUnitConsts *)pmunt->GetConsts())->GetMoveDistPerUpdate(); + gsmm.SendMsg(&msgT); + } + + // Jammed + + return true; + } + + // Maybe a unit is moving into the output bay (i.e., it is jammed) + + if (!IsTileFree(txBay, tyBay, kbfMobileUnit)) + return true; // jammed + + // Open + + return false; +} + +int ReplicatorGob::ProcessStateMachineMessage(State st, Message *pmsg) +{ +BeginStateMachine + OnMsg(kmidPlaySfx) + gsndm.PlaySfx((Sfx)pmsg->PlaySfx.sfx); + + State(kstIdle) + OnUpdate + // If a mobile unit is at either output, ask it to move on + + TPoint tpt; + GetTilePosition(&tpt); + Gid gid; + + // Clear left and right output bays + Player *pplrNeedCredits = NULL; + bool fJammed = ClearOutputBay(tpt.tx + kdtxReplicatorOutput1, tpt.ty + kdtyReplicatorOutput1, tpt.tx + kdtxReplicatorOutput1 - 1, tpt.ty + kdtyReplicatorOutput1 + 1); + fJammed = ClearOutputBay(tpt.tx + kdtxReplicatorOutput2, tpt.ty + kdtyReplicatorOutput2, tpt.tx + kdtxReplicatorOutput2 + 1, tpt.ty + kdtyReplicatorOutput2 + 1) || fJammed; + + // If there's nothing clogging the outputs we can consider the input + if (m_fEnabled && !fJammed) { + // loop through all the gobs on this tile to find the mobile unit and ignore ourself, or + // shots flying by + for (gid = ggobm.GetFirstGid(tpt.tx + kdtxReplicatorInput, tpt.ty + kdtyReplicatorInput); gid != kgidNull; gid = ggobm.GetNextGid(gid)) { + MobileUnitGob *pmunt = (MobileUnitGob *)ggobm.GetGob(gid); + if (pmunt == NULL) + continue; + if (!(pmunt->GetFlags() & kfGobMobileUnit)) + continue; + + // Something there! If it's ready, clone it. + + if (!(pmunt->GetMobileUnitFlags() & kfMuntAtReplicatorInput)) + continue; + + // At the limit? +// TUNE: +#define kctIntervalLimitNotify 600 + + if (!ggobm.IsBelowLimit(knLimitMobileUnit, pmunt->GetOwner())) { + if (pmunt->GetOwner() == gpplrLocal) { + static long s_tLastNotify; + long tCurrent = gtimm.GetTickCount(); + if (abs(s_tLastNotify - tCurrent) >= kctIntervalLimitNotify) { + s_tLastNotify = tCurrent; + ShowAlert(kidsUnitLimitReached); + } + } + continue; + } + + // Does the player have enough credits to perform the replication? + + MobileUnitConsts *pmuntc = (MobileUnitConsts *)pmunt->GetConsts(); + Player *pplr = pmunt->GetOwner(); + long cCredits = pplr->GetCredits(); + + int nCost = pmuntc->GetCost(); + int cReplicationCost = GetReplicationCost(nCost); + if (cCredits < cReplicationCost) { + pplrNeedCredits = pplr; + break; + } + + // Take the money! + + pplr->SetCredits(cCredits - cReplicationCost, true); + + // Start the replicating animation + + m_fReplicating = true; + m_ifrmLights = -1; + + MobileUnitGob *pmuntClone = (MobileUnitGob *)CreateGob(pmunt->GetType()); + if (pmuntClone != NULL) { + pmuntClone->Init(WcFromTc(tpt.tx + kdtxReplicatorOutput2), WcFromTc(tpt.ty + kdtyReplicatorOutput2), pmunt->GetOwner(), pmunt->GetHealth(), 0, NULL); + + // Clone acquires the selection state of the original + + if (pmunt->GetFlags() & kfGobSelected) + pmuntClone->Select(true); + + // Replicated GalaxMiners lose their load of Galaxite + // UNDONE: if there is more of this special casing go with virtual UnitGob::Replicate() + + if (pmunt->GetType() == kgtGalaxMiner) + ((MinerGob *)pmunt)->SetGalaxiteAmount(0); + + // If this unit is a member of a group add its clone to the group too + + UnitGroup *pug = gsim.GetLevel()->GetUnitGroupMgr()->GetUnitGroup(pmunt->GetId()); + if (pug != NULL) + pug->AddUnit(pmuntClone, true); + } + + // Warp the original Unit to the left output port + + TerrainMap *ptrmap = gsim.GetLevel()->GetTerrainMap(); + ptrmap->ClearFlags(tpt.tx + kdtxReplicatorInput, tpt.ty + kdtyReplicatorInput, 1, 1, kbfMobileUnit); + ptrmap->SetFlags(tpt.tx + kdtxReplicatorOutput1, tpt.ty + kdtyReplicatorOutput1, 1, 1, kbfMobileUnit); + pmunt->SetPosition(WcFromTc(tpt.tx + kdtxReplicatorOutput1) + kwcTileHalf, WcFromTc(tpt.ty + kdtyReplicatorOutput1) + kwcTileHalf); + + // Clear the bit + + pmunt->SetMobileUnitFlags(pmunt->GetMobileUnitFlags() & ~kfMuntAtReplicatorInput); + + // Let player know the replication process has happened + // play cool replication sound effect + + gsndm.PlaySfx(ksfxReplicatorBuild); + + // wait a quarter second and let the new unit announce itself + + Sfx sfx = SfxFromCategory(pmuntc->sfxcSelect); + Message msgT; + memset(&msgT, 0, sizeof(msgT)); + msgT.mid = kmidPlaySfx; + msgT.smidSender = m_gid; + msgT.smidReceiver = m_gid; + msgT.PlaySfx.sfx = sfx; + gsmm.SendDelayedMsg(&msgT, 24); + + // Play it again in another quarter of a second to sound cool + + memset(&msgT, 0, sizeof(msgT)); + msgT.mid = kmidPlaySfx; + msgT.smidSender = m_gid; + msgT.smidReceiver = m_gid; + msgT.PlaySfx.sfx = sfx; + gsmm.SendDelayedMsg(&msgT, 48); + break; + } + } + + if (m_fReplicating) { + m_ifrmLights++; + int nStrip = m_pmuntc->panid->GetStripIndex("l 0"); + if (m_ifrmLights / 2 >= m_pmuntc->panid->GetFrameCount(nStrip)) { + m_ifrmLights = -1; + m_fReplicating = false; + } + MarkRedraw(); + } + WaitingForCredits(pplrNeedCredits != NULL, false, pplrNeedCredits); + m_unvl.MinSkip(); + DefUpdate(); + +#if 0 +EndStateMachineInherit(StructGob) +#else + return knHandled; + } + } else { + return (int)StructGob::ProcessStateMachineMessage(st, pmsg); + } + return (int)StructGob::ProcessStateMachineMessage(st, pmsg); +#endif +} + +void ReplicatorGob::WaitingForCredits(bool fNeed, bool fOverride, Player *pplr) +{ + // we need to be using the unit's player, not our own. Remember so when the unit + // disappears we can decrement the right player's count + + if (fNeed) { + Assert((m_pplrNeedCredits == NULL) || (m_pplrNeedCredits == pplr) || (fOverride)); + if (fOverride) + pplr = m_pplrNeedCredits; + else + m_pplrNeedCredits = pplr; + } else { + Assert(pplr == NULL); + pplr = m_pplrNeedCredits; + m_pplrNeedCredits = NULL; + } + + StructGob::WaitingForCredits(fNeed, fOverride, pplr); +} + + +// Replicator input point management + +int ReplicatorGob::s_cReplicators; +TPoint ReplicatorGob::s_atptInput[10]; + +void ReplicatorGob::AddReplicatorInputPoint(TCoord tx, TCoord ty) +{ + Assert(s_cReplicators < ARRAYSIZE(s_atptInput) - 1); + if (s_cReplicators >= ARRAYSIZE(s_atptInput) - 1) + return; + s_atptInput[s_cReplicators].tx = tx; + s_atptInput[s_cReplicators].ty = ty; + s_cReplicators++; +} + +void ReplicatorGob::RemoveReplicatorInputPoint(TCoord tx, TCoord ty) +{ + for (int n = 0; n < ARRAYSIZE(s_atptInput); n++) { + if (s_atptInput[n].tx == tx && s_atptInput[n].ty) { + memmove(&s_atptInput[n], &s_atptInput[n + 1], (s_cReplicators - n - 1) * ELEMENTSIZE(s_atptInput)); + s_cReplicators--; + return; + } + } + Assert(); +} + +// +// ActivatorGob implementation +// + +#if defined(DEBUG_HELPERS) +char *ActivatorGob::GetName() +{ + return "Activator"; +} +#endif + +bool ActivatorGob::Init(IniReader *pini, FindProp *pfind, const char *pszName) +{ + int side, tx, ty; + int cArgs = pini->GetPropertyValue(pfind, "%*d ,%d ,%d ,%d", &side, &tx, &ty); + if (cArgs < 3) { + Assert("ActivatorGob requires at least 3 valid initialization parameters"); + return false; + } + + // Keep players from building on top of Activators + + gsim.GetLevel()->GetTerrainMap()->SetFlags(tx, ty, 1, 1, kbfReserved); + + return AnimGob::Init(WcFromTc(tx), WcFromTc(ty), kfAnmLoop | kfAnmSurfaceDecalLayer, NULL, s_panidActivator, 0, ksmidNull, pszName); +} + +#define knVerActivatorGobState 1 +bool ActivatorGob::LoadState(Stream *pstm) +{ + byte nVer = pstm->ReadByte(); + if (nVer != knVerActivatorGobState) + return false; + m_fActivated = pstm->ReadByte() != 0; + m_ani.Init(s_panidActivator); + m_ani.LoadState(pstm); + m_wfAnm = kfAnmLoop | kfAnmSurfaceDecalLayer; + return AnimGob::LoadState(pstm); +} + +bool ActivatorGob::SaveState(Stream *pstm) +{ + pstm->WriteByte(knVerActivatorGobState); + pstm->WriteByte(m_fActivated); + m_ani.SaveState(pstm); + return AnimGob::SaveState(pstm); +} + +bool ActivatorGob::IsSavable() +{ + // Because AnimGob is not savable, we need to override + + return true; +} + +GobType ActivatorGob::GetType() +{ + return kgtActivator; +} + +int ActivatorGob::ProcessStateMachineMessage(State st, Message *pmsg) +{ +BeginStateMachine + OnUpdate + bool fActivated = false; + for (Gid gid = ggobm.GetFirstGid(TcFromWc(m_wx), TcFromWc(m_wy)); gid != kgidNull; gid = ggobm.GetNextGid(gid)) { + MobileUnitGob *pmunt = (MobileUnitGob *)ggobm.GetGob(gid); + if (pmunt == NULL) + continue; + if (!(pmunt->GetFlags() & kfGobMobileUnit)) + continue; + fActivated = true; + break; + } + + if (fActivated != m_fActivated) { + m_fActivated = fActivated; + StartAnimation(&m_ani, fActivated ? 1 : 0, 0, kfAniLoop | kfAnmSurfaceDecalLayer); + gsndm.PlaySfx(fActivated ? ksfxActivatorOn : ksfxActivatorOff); + + } + + // Check every 3 updates for the presence of a MobileUnit on top of the Activator + + m_unvl.MinSkip(3); + + return AnimGob::ProcessStateMachineMessage(st, pmsg); + +#if 0 +EndStateMachineInherit(AnimGob) +#else + return knHandled; + } + } else { + return (int)AnimGob::ProcessStateMachineMessage(st, pmsg); + } + return (int)AnimGob::ProcessStateMachineMessage(st, pmsg); +#endif +} + +} // namespace wi diff --git a/game/Research.cpp b/game/Research.cpp new file mode 100644 index 0000000..f3f180d --- /dev/null +++ b/game/Research.cpp @@ -0,0 +1,595 @@ +#include "ht.h" +#include "strings.h" + +namespace wi { + +const int kcUpdateCost = 3; //TUNE: - credits of upgrade per update + +class UpgradeForm : public Form +{ +public: + ResearchGob *GetOwner() { + return m_pgobOwner; + } + void SetOwner(ResearchGob *pgobOwner) secStructures; + void UpdateUpgradeInfo(ListItem *pli) secStructures; + + // Form overrides + + void EndForm(int nResult = kidcCancel) secStructures; + virtual void OnControlSelected(word idc) secStructures; + virtual void OnPaintBackground(DibBitmap *pbm, UpdateMap *pupd) secStructures; + +private: + ResearchGob *m_pgobOwner; +}; + +// UNDONE: migrate from wfUpgrade (kfUpgrade*) +// UNDONE: v2 upgrade infrastructure +// UNDONE: implement increase mining speed upgrade +// UNDONE: new action for setting upgrades and allowed upgrades +// UNDONE: initialize this from upgrades.ini (mirror gobtemplates.ini structure) +// UNDONE: tapping '?' icon hangs + +// Single player upgrades + +Upgrade gaupg[kupgtMax] = { + { + kupgmAdvancedHRC, + "Advanced HRC", + "hrc1", + 0, kumHumanResourceCenter, + 750, 4500, + "Upgrade the HRC to gain access to more advanced infantry.", + "This upgrade requires a HRC." + }, + { + kupgmAdvancedVTS, + "Advanced VTS", + "vts1", + 0, kumVehicleTransportStation, + 1150, 4500, + "Upgrade the VTS to gain access to more advanced vehicles.", + "This upgrade requires a VTS." + }, +}; + +// Multiplayer upgrades + +Upgrade gaupgMP[kupgtMax] = { + { + kupgmAdvancedHRC, + "Advanced HRC", + "hrc1", + 0, kumHumanResourceCenter, + 1500, 4500, + "Upgrade the HRC to gain access to more advanced infantry.", + "This upgrade requires a HRC." + }, + { + kupgmAdvancedVTS, + "Advanced VTS", + "vts1", + 0, kumVehicleTransportStation, + 3000, 4500, + "Upgrade the VTS to gain access to more advanced vehicles.", + "This upgrade requires a VTS." + }, +#if 0 + { + kupgmIncreasedBullpupSpeed, + "Increased Bullpup Speed", + "bullpup1", + 0, kumProcessor, + 3000, 4500, + "Increase Bullpup movement and mining speed by 10%. All existing and future Bullpups will " + "be retrofited with high efficiency triple-buffered fuel cores.", + "This upgrade requires a Processor." + } +#endif +}; + +// +// ResearchGob implementation +// + +static StructConsts gConsts; +static UpgradeForm *s_pfrmUpgrade = NULL; +static AnimationData *s_panidUpgradeIcons = NULL; + +// +// Gob methods +// + +bool ResearchGob::InitClass(IniReader *pini) +{ + gConsts.gt = kgtResearchCenter; + gConsts.ut = kutResearchCenter; + //TUNE: + gConsts.umPrerequisites = kumReactor; + + // Sound effects + + gConsts.sfxAbortRepair = ksfxResearchCenterAbortRepair; + gConsts.sfxRepair = ksfxResearchCenterRepair; + gConsts.sfxDamaged = ksfxResearchCenterDamaged; + gConsts.sfxSelect = ksfxResearchCenterSelect; + gConsts.sfxDestroyed = ksfxResearchCenterDestroyed; + gConsts.sfxImpact = ksfxNothing; + gConsts.sfxAbortUpgrade = ksfxResearchCenterAbortRepair; + gConsts.sfxUpgrade = ksfxResearchCenterRepair; + + // Wants power notification + + gConsts.wf |= kfUntcNotifyPowerLowHigh; + + // Preload the Upgrade form + + s_pfrmUpgrade = new UpgradeForm(); + if (s_pfrmUpgrade == NULL) { + Assert("fatal error"); + return false; + } + + if (!s_pfrmUpgrade->Init(gpmfrmm, gpiniForms, kidfUpgrade)) { + Assert("unable to init Upgrade form"); + return false; + } + gpmfrmm->RemoveForm(s_pfrmUpgrade); + + // Initialize animation that contains the upgrade icons + + s_panidUpgradeIcons = LoadAnimationData("upgrades.anir"); + Assert(s_panidUpgradeIcons != NULL, "Upgrade icons failed to load"); + if (s_panidUpgradeIcons == NULL) + return false; + + return StructGob::InitClass(&gConsts, pini); +} + +void ResearchGob::ExitClass() +{ + delete s_panidUpgradeIcons; + s_panidUpgradeIcons = NULL; + delete s_pfrmUpgrade; + s_pfrmUpgrade = NULL; + StructGob::ExitClass(&gConsts); +} + +ResearchGob::ResearchGob() : StructGob(&gConsts) +{ + m_utTarget = kutNone; + m_nCreditsSpentOnUpgrade = 0; +} + +#define knVerResearchGobState 3 +bool ResearchGob::LoadState(Stream *pstm) +{ + byte nVer = pstm->ReadByte(); + if (nVer != knVerResearchGobState) + return false; + m_utTarget = (UnitType)(char)pstm->ReadByte(); + + //temp fix me - 255 check is because CW compiler isn't sign extending + // m_utTarget in the up-cast from char to UnitType (which is short). + // Even forcing (UnitType)(short)(signed char) results in 255! + + if (m_utTarget == 255) + m_utTarget = kutNone; + + if (m_utTarget != kutNone) { + m_nCreditsSpentOnUpgrade = (int)pstm->ReadWord(); + } + return StructGob::LoadState(pstm); +} + +bool ResearchGob::SaveState(Stream *pstm) +{ + pstm->WriteByte(knVerResearchGobState); + pstm->WriteByte((byte)m_utTarget); + if (m_utTarget != kutNone) { + pstm->WriteWord(m_nCreditsSpentOnUpgrade); + } + return StructGob::SaveState(pstm); +} + +void ResearchGob::InitMenu(Form *pfrm) +{ + bool fUpgradeInProgress = (m_pplr->GetUpgrades() & (kfUpgradeHrcInProgress | kfUpgradeVtsInProgress)) > 0; + + Control *pctl = pfrm->GetControlPtr(kidcResearch); + pctl->Show(!fUpgradeInProgress); + + pctl = pfrm->GetControlPtr(kidcAbortUpgrade); + pctl->Show(fUpgradeInProgress); + + StructGob::InitMenu(pfrm); +} + +void ResearchGob::OnMenuItemSelected(int idc) +{ + switch (idc) { + case kidcResearch: + { + s_pfrmUpgrade->SetOwner(this); + gpmfrmm->AddForm(s_pfrmUpgrade); + int idc; + s_pfrmUpgrade->DoModal(&idc); + gpmfrmm->RemoveForm(s_pfrmUpgrade); + + if (idc == kidcCancel) + break; + + // this must be enqueued because it begins the consumption of credits + + Message msg; + memset(&msg, 0, sizeof(msg)); + msg.mid = kmidUpgradeCommand; + msg.smidReceiver = m_gid; + msg.smidSender = ksmidNull; + msg.UpgradeCommand.wfUpgrade = idc == kupgmAdvancedHRC ? kfUpgradeHrc : kfUpgradeVts; + gcmdq.Enqueue(&msg); + if (m_pplr == gpplrLocal) + gsndm.PlaySfx(gConsts.sfxUpgrade); + } + break; + + case kidcAbortUpgrade: + + // terminate the consumption of credits and restore + // we can only do one upgrade at a time so don't need to say which + + gcmdq.Enqueue(kmidAbortUpgradeCommand, m_gid); + if (m_pplr == gpplrLocal) + gsndm.PlaySfx(gConsts.sfxAbortUpgrade); + break; + + default: + StructGob::OnMenuItemSelected(idc); + break; + } +} + +// +// StateMachine methods +// + +#if defined(DEBUG_HELPERS) +char *ResearchGob::GetName() +{ + return "ResearchCenter"; +} +#endif + +int ResearchGob::ProcessStateMachineMessage(State st, Message *pmsg) +{ +BeginStateMachine + //----------------------------------------------------------------------- + + State(kstIdle) + OnMsg(kmidUpgradeCommand) + + // the upgrade basically applies to the player - they now build/have + // fancier versions of the same old structures in question. + // it's up to the structures themselves to check for the upgrade + // or its progress and behave appropriately + + if (pmsg->UpgradeCommand.wfUpgrade & kfUpgradeVts) { + if (!(m_pplr->GetUpgrades() & (kfUpgradeVts | kfUpgradeVtsInProgress))) { + m_pplr->SetUpgrades(m_pplr->GetUpgrades() | kfUpgradeVtsInProgress); + Assert(m_utTarget == kutNone); + m_utTarget = kutVehicleTransportStation; + + } + } + + if (pmsg->UpgradeCommand.wfUpgrade & kfUpgradeHrc) { + if (!(m_pplr->GetUpgrades() & (kfUpgradeHrc | kfUpgradeHrcInProgress))) { + m_pplr->SetUpgrades(m_pplr->GetUpgrades() | kfUpgradeHrcInProgress); + Assert(m_utTarget == kutNone); + m_utTarget = kutHumanResourceCenter; + } + } + + // Cause the current state to wake up and start costing the upgrade + + m_unvl.MinSkip(); + + OnMsg(kmidAbortUpgradeCommand) + AbortUpgrade(); + + OnMsg(kmidSelfDestructCommand) + AbortUpgrade(); + SelfDestruct(); + + OnUpdate + // Don't animate if power is too low + // This relies on kmidPowerLowHigh to wake up if power situation changes + + if (!m_pplr->IsPowerLow()) + AdvanceAnimation(&m_ani); + + if (m_utTarget != kutNone) { + UpgradeUpdate(); + MarkRedraw(); + m_unvl.MinSkip(); + } + DefUpdate(); + +#if 0 +EndStateMachineInherit(StructGob) +#else + return knHandled; + } + } else { + return (int)StructGob::ProcessStateMachineMessage(st, pmsg); + } + return (int)StructGob::ProcessStateMachineMessage(st, pmsg); +#endif +} + +void ResearchGob::UpgradeUpdate() +{ + int nCost = ((StructConsts *)gapuntc[m_utTarget])->nUpgradeCost; + + // God can upgrade things instantly and for free + + if (gfGodMode && !ggame.IsMultiplayer()) { + m_nCreditsSpentOnUpgrade = nCost; + WaitingForCredits(false); + } else { + + // calc update as either a default amount or what's left + + int nCostPerUpdate = nCost - m_nCreditsSpentOnUpgrade; + if (nCostPerUpdate > kcUpdateCost) + nCostPerUpdate = kcUpdateCost; //TUNE: + + // update if the player has enough credits to cover it + + long nCredits = m_pplr->GetCredits() - nCostPerUpdate; + if (nCredits >= 0) { + m_pplr->SetCredits(nCredits, true); + m_nCreditsSpentOnUpgrade += nCostPerUpdate; + Assert(m_nCreditsSpentOnUpgrade <= nCost); + } + WaitingForCredits(nCredits < 0); + } + if (m_nCreditsSpentOnUpgrade == nCost) { + + // fix up the player flags + // play the appropriate noises + // reset our state + + word wFlagOn, wFlagOff; + if (m_utTarget == kutHumanResourceCenter) { + wFlagOn = kfUpgradeHrc; + wFlagOff = kfUpgradeHrcInProgress; + } else { + wFlagOn = kfUpgradeVts; + wFlagOff = kfUpgradeVtsInProgress; + } + word wUpgrades = m_pplr->GetUpgrades(); + wUpgrades &= ~wFlagOff; + m_pplr->SetUpgrades(wUpgrades | wFlagOn); + + if (m_pplr == gpplrLocal) { + gsndm.PlaySfx(m_utTarget == kutHumanResourceCenter ? ksfxGameNewRecruitOptions : ksfxGameNewVehicleOptions); + ShowAlert(m_utTarget == kutHumanResourceCenter ? kidsNewRecruitOptions : kidsNewVehicleOptions); + } + + m_utTarget = kutNone; + m_nCreditsSpentOnUpgrade = 0; + WaitingForCredits(false); + } +} + +void ResearchGob::Draw(DibBitmap *pbm, int xViewOrigin, int yViewOrigin, int nLayer) +{ + StructGob::Draw(pbm, xViewOrigin, yViewOrigin, nLayer); + + if (nLayer == knLayerDepthSorted) { + if (m_utTarget != kutNone) { + Rect rcT; + rcT.FromWorldRect(&m_puntc->wrcUIBounds); + rcT.Offset(-xViewOrigin + PcFromWc(m_wx), -yViewOrigin + PcFromWc(m_wy)); + DrawBuildProgressIndicator(pbm, &rcT, m_nCreditsSpentOnUpgrade, ((StructConsts *)gapuntc[m_utTarget])->nUpgradeCost); // passing into fix params? + } + } +} + +void ResearchGob::AbortUpgrade() +{ + // abandon upgrade and restore spent credits + + if (m_utTarget != kutNone) { + m_pplr->SetCredits(m_pplr->GetCredits() + m_nCreditsSpentOnUpgrade, false); + m_pplr->ModifyTotalCreditsConsumed(-m_nCreditsSpentOnUpgrade); + m_pplr->SetUpgrades(m_pplr->GetUpgrades() & ~(kfUpgradeVtsInProgress | kfUpgradeHrcInProgress)); + m_utTarget = kutNone; + m_nCreditsSpentOnUpgrade = 0; + WaitingForCredits(false); + m_unvl.MinSkip(); + } +} + +void ResearchGob::Deactivate() +{ + // for both takeover and destruction the upgrade should be abandoned w/o restoring credits + + if (m_utTarget != kutNone) + { + m_pplr->SetUpgrades(m_pplr->GetUpgrades() & ~(kfUpgradeVtsInProgress | kfUpgradeHrcInProgress)); + m_utTarget = kutNone; + m_nCreditsSpentOnUpgrade = 0; + } + + StructGob::Deactivate(); +} + +//=========================================================================== +// UpgradeForm implementation + +void UpgradeForm::SetOwner(ResearchGob *pgobOwner) +{ + Size sizDib; + m_pfrmm->GetDib()->GetSize(&sizDib); + Rect rc; + rc.left = ((sizDib.cx - m_rc.Width()) / 2) & ~1; + rc.top = 0; // (sizDib.cy - m_rc.Height()) / 2; + rc.right = rc.left + m_rc.Width(); + rc.bottom = rc.top + m_rc.Height(); + SetRect(&rc); + + m_wf |= kfFrmAutoTakedown; + + m_pgobOwner = pgobOwner; + + BuildListControl *plstc = (BuildListControl *)GetControlPtr(kidcList); + plstc->Clear(); + + // Initialize list with available upgrade types. + // UNDONE: need to link list to dynamic build capability + + Player *pplr = pgobOwner->GetOwner(); + UpgradeMask upgmOwned = pplr->GetUpgradeMask(); + UpgradeMask upgmAllowed = pplr->GetAllowedUpgrades(); + + int cUpgrades = 0; + Upgrade *pupg = gfMultiplayer ? gaupgMP : gaupg; + for (int i = 0; i < kupgtMax; i++, pupg++) { + + // Has the player already acquired this upgrade? If so, don't add it to the list + + if (pplr->GetUpgradeMask() & pupg->upgm) + continue; + + // Does this player have what it takes to perform this upgrade? + + bool fDisabled = true; + if (gfGodMode || ((upgmOwned & pupg->upgmPrerequisites) == pupg->upgmPrerequisites && + (pplr->GetUnitMask() & pupg->umPrerequisites) == pupg->umPrerequisites)) + fDisabled = false; + + // Is this one of the upgrades this player is allowed? + + if (gfGodMode || (pupg->upgm & upgmAllowed)) { + + // Yep, add it to the list + + cUpgrades++; + int nStripIcon = s_panidUpgradeIcons->GetStripIndex(pupg->szIconName); + if (nStripIcon != -1) { + plstc->Add(s_panidUpgradeIcons, nStripIcon, 0, (void *)pupg, fDisabled); + } + } + } + + if (cUpgrades == 0) { + // Hide the "Research" button if no upgrades are available + + ButtonControl *pbtn = (ButtonControl *)GetControlPtr(kidcOk); + pbtn->Show(false); + LabelControl *plbl = (LabelControl *)GetControlPtr(kidcCost); + plbl->SetText(""); + PipMeterControl *pmtr = (PipMeterControl *)GetControlPtr(kidcCostMeter); + pmtr->SetValue(0); + plbl = (LabelControl *)GetControlPtr(kidcName); + plbl->SetText("NO UPGRADES AVAILABLE"); + plbl = (LabelControl *)GetControlPtr(kidcDescription); + plbl->SetText(""); + plbl = (LabelControl *)GetControlPtr(kidcPrerequisites); + plbl->SetText(""); + plbl = (LabelControl *)GetControlPtr(kidcCostLabel); + plbl->Show(false); + plbl = (LabelControl *)GetControlPtr(kidcPrerequisitesLabel); + plbl->Show(false); + } + + // Select the first upgrade by default + + plstc->Select(0); + plstc->SetQueueInfo(NULL, NULL); + +} + +void UpgradeForm::UpdateUpgradeInfo(ListItem *pli) +{ + Upgrade *pupg = (Upgrade *)pli->pvData; + + // Update Cost + + LabelControl *plbl = (LabelControl *)GetControlPtr(kidcCost); + char szT[20]; + itoa(pupg->nCost, szT, 10); + plbl->SetText(szT); + PipMeterControl *pmtr = (PipMeterControl *)GetControlPtr(kidcCostMeter); + pmtr->SetValue(((long)pupg->nCost * 100) / 3000); + plbl = (LabelControl *)GetControlPtr(kidcCostLabel); + plbl->Show(true); + + // Update Name + + plbl = (LabelControl *)GetControlPtr(kidcName); + plbl->SetText(pupg->szName); + + // Update Description + + plbl = (LabelControl *)GetControlPtr(kidcDescription); + plbl->SetText(pupg->szDescription); + + // Update prerequisites + + plbl = (LabelControl *)GetControlPtr(kidcPrerequisites); + plbl->SetText(pupg->szPrerequisites); + plbl = (LabelControl *)GetControlPtr(kidcPrerequisitesLabel); + plbl->Show(true); + + // Hide the "Research" button if this upgrade is disabled + + ButtonControl *pbtn = (ButtonControl *)GetControlPtr(kidcOk); + pbtn->Show(!pli->fDisabled); +} + +void UpgradeForm::EndForm(int nResult) +{ + m_pgobOwner = NULL; + Form::EndForm(nResult); +} + +void UpgradeForm::OnControlSelected(word idc) +{ + switch (idc) { + case kidcCancel: + EndForm(kidcCancel); + return; + + case kidcHelp: + // UNDONE: upgrade help text + Help("upgrades", !ggame.IsMultiplayer()); + break; + + case kidcList: + { + ListControl *plstc = (ListControl *)GetControlPtr(kidcList); + ListItem *pli = plstc->GetSelectedItem(); + if (pli != NULL) + UpdateUpgradeInfo(pli); + } + break; + + case kidcOk: + ListControl *plstc = (ListControl *)GetControlPtr(kidcList); + EndForm(((Upgrade *)plstc->GetSelectedItemData())->upgm); + return; + } +} + +void UpgradeForm::OnPaintBackground(DibBitmap *pbm, UpdateMap *pupd) +{ + RawBitmap *prbm = LoadRawBitmap("buildformbkgd.rbm"); + BltHelper(pbm, prbm, pupd, m_rc.left, m_rc.top); + delete prbm; +} + +} // namespace wi \ No newline at end of file diff --git a/game/SRInfantry.cpp b/game/SRInfantry.cpp new file mode 100644 index 0000000..9f7a642 --- /dev/null +++ b/game/SRInfantry.cpp @@ -0,0 +1,220 @@ +#include "ht.h" + +namespace wi { + +static MobileUnitConsts gConsts; + +#if defined(DEBUG_HELPERS) +char *SRInfantryGob::GetName() +{ + return "SRInfantry"; +} +#endif + +static int s_anFiringStripIndices[8] = { 1, 2, 3, 4, 5, 6, 7, 8 }; +static int s_anMovingStripIndices[8] = { 17, 18, 19, 20, 21, 22, 23, 24 }; +static int s_anIdleStripIndices[8] = { 9, 10, 11, 12, 13, 14, 15, 16 }; + +bool SRInfantryGob::InitClass(IniReader *pini) +{ + gConsts.gt = kgtShortRangeInfantry; + gConsts.ut = kutShortRangeInfantry; + gConsts.wf |= kfUntcNotifyEnemyNearby; + + // Initialize the frame indices arrays + + gConsts.anFiringStripIndices = s_anFiringStripIndices; + gConsts.anMovingStripIndices = s_anMovingStripIndices; + gConsts.anIdleStripIndices = s_anIdleStripIndices; + + // Sound effects + + gConsts.sfxFire = ksfxMachineGunInfantryFire; + gConsts.sfxImpact = ksfxNothing; + + gConsts.sfxcDestroyed = ksfxcInfantryDestroyed; + gConsts.sfxcSelect = ksfxcMale03Select; + gConsts.sfxcMove = ksfxcMale03Move; + gConsts.sfxcAttack = ksfxcMale03Attack; + + return MobileUnitGob::InitClass(&gConsts, pini); +} + +void SRInfantryGob::ExitClass() +{ + MobileUnitGob::ExitClass(&gConsts); +} + +SRInfantryGob::SRInfantryGob() : MobileUnitGob(&gConsts) +{ +} + +bool SRInfantryGob::Fire(UnitGob *puntTarget, WCoord wx, WCoord wy, WCoord wdx, WCoord wdy) +{ + // MobileUnitGob handles rotating towards the target, firing delay, + // and starting the fire animation + + if (!MobileUnitGob::Fire(puntTarget, wx, wy, wdx, wdy)) + return false; + + // Fire off the shot! + + WCoord wdxRnd = ((GetRandom() & 7) - 3) * kwcTile16th; + WCoord wdyRnd = ((GetRandom() & 7) - 3) * kwcTile16th; + Point ptSpecial; + m_ani.GetSpecialPoint(&ptSpecial, 1); // frame #1 (second frame) has the firing point + + CreateBulletGob(m_wx + WcFromPc(ptSpecial.x), m_wy + WcFromPc(ptSpecial.y), wx + wdxRnd, wy + wdyRnd, + GetDamageTo(puntTarget), m_gid, puntTarget->GetId()); + + // Play sound + + gsndm.PlaySfx(m_pmuntc->sfxFire); + + return true; +} + +void SRInfantryGob::Idle() +{ + // 1/4 of the time we pivot left, 1/4 we pivot right, and 1/2 we play the idle + + switch (GetRandom() & 3) { + case 0: + m_dir--; + if (m_dir < 0) + m_dir = 7; + StartAnimation(&m_ani, m_pmuntc->anIdleStripIndices[m_dir], 0, kfAniResetWhenDone); + break; + + case 1: + m_dir++; + if (m_dir > 7) + m_dir = 0; + StartAnimation(&m_ani, m_pmuntc->anIdleStripIndices[m_dir], 0, kfAniResetWhenDone); + break; + + default: + StartAnimation(&m_ani, m_pmuntc->anIdleStripIndices[m_dir], 0, kfAniResetWhenDone); + break; + } +} + +int SRInfantryGob::ProcessStateMachineMessage(State st, Message *pmsg) +{ +BeginStateMachine + State(kstDying) + OnEnter + TRect trc; + GetTileRect(&trc); + + Deactivate(); + + // Redraw this part of minimap. It will skip inactive munts + + gpmm->RedrawTRect(&trc); + + m_ff ^= kfGobLayerDepthSorted | kfGobLayerSurfaceDecal; + + gsndm.PlaySfx(SfxFromCategory(m_pmuntc->sfxcDestroyed)); + m_ani.Start("die 3", kfAniIgnoreFirstAdvance); + MarkRedraw(); + + // Remove corpse after 10 seconds + + gsmm.SendDelayedMsg(kmidDelete, 1000, m_gid, m_gid); + + OnUpdate + AdvanceAnimation(&m_ani); + +#if 0 +EndStateMachineInherit(MobileUnitGob) +#else + return knHandled; + } + } else { + return (int)MobileUnitGob::ProcessStateMachineMessage(st, pmsg); + } + return (int)MobileUnitGob::ProcessStateMachineMessage(st, pmsg); +#endif +} + +#if 0 // Not used anymore +// +// RicochetGob implementation +// + +bool RicochetGob::Init(WCoord wx, WCoord wy, word wfAnm, const char *pszAniName, AnimationData *panid, + StateMachineId smidNotify, const char *pszName) +{ + if (!AnimGob::Init(wx, wy, wfAnm, pszAniName, panid, GetRandom() % 3, smidNotify, pszName)) + return false; + + m_fNew = true; + return true; +} + +bool RicochetGob::OnStripDone() +{ + if (m_fNew) { + m_fNew = false; + SetAnimationStrip(&m_ani, GetRandom() % 3); + return false; + } else { + return true; + } +} +#endif + +// +// BulletGob implementation +// + +AnimationData *BulletGob::s_panidShot = NULL; + +// this will actually create & fire +BulletGob *CreateBulletGob(WCoord wx, WCoord wy, WCoord wxTarget, WCoord wyTarget, int nDamage, Gid gidOwner, Gid gidTarget, bool fEagle) +{ + if (!ggobm.IsBelowLimit(knLimitSupport)) + return NULL; + + BulletGob *pgob = new BulletGob(); + Assert(pgob != NULL, "out of memory!"); + if (pgob == NULL) + return NULL; + + if (!pgob->Init(BulletGob::s_panidShot, wx, wy, wxTarget, wyTarget, nDamage, gidOwner, gidTarget, kwcTile)) { + delete pgob; + return NULL; + } + + pgob->Launch(); + return pgob; +} + +bool BulletGob::InitClass(IniReader *pini) +{ + s_panidShot = LoadAnimationData("bullet.anir"); + if (s_panidShot == NULL) + return false; + return true; +} + +void BulletGob::ExitClass() +{ + delete s_panidShot; + s_panidShot = NULL; +} + +GobType BulletGob::GetType() +{ + return kgtBullet; +} + +#if defined(DEBUG_HELPERS) +char *BulletGob::GetName() +{ + return "Bullet"; +} +#endif + +} // namespace wi \ No newline at end of file diff --git a/game/Shell.cpp b/game/Shell.cpp new file mode 100644 index 0000000..a217098 --- /dev/null +++ b/game/Shell.cpp @@ -0,0 +1,1052 @@ +#include "game/ht.h" +#include "game/lobby.h" +#include "game/serviceurls.h" +#include "base/misc.h" + +namespace wi { + +// A simple form handler for the main menu, for buttons that want to +// be processed without ending the modal form loop. + +class MainMenuForm : public ShellForm +{ +public: + virtual void OnControlSelected(word idc) { + // Catch this here so the form doesn't get re-created (with associated + // sound effect). + if (idc == kidcLeaderboard) { + LoginHandler handler; + std::string d = base::StringEncoder::QueryEncode(gszDeviceId); + const char *url; + if (strlen(handler.StatsUsername()) == 0) { + url = base::Format::ToString("%s?d=%s", kszLeaderboardUrl, + d.c_str()); + } else { + std::string q = base::StringEncoder::QueryEncode( + handler.StatsUsername()); + url = base::Format::ToString("%s?p=%s&d=%s", kszLeaderboardUrl, + q.c_str(), d.c_str()); + } + HostInitiateWebView("Hostile Takeover Statistics", url); + return; + } + ShellForm::OnControlSelected(idc); + } +}; + +Shell gshl; +Shell::Shell() +{ + m_ppal = NULL; + m_mpiclriclrShadow = NULL; +} + +bool Shell::Init() +{ + m_ppal = (Palette *)gpakr.MapFile("shell.palbin", &m_fmapPalette); + Assert(m_ppal != NULL); + m_mpiclriclrShadow = (byte *)gpakr.MapFile("shell.palbin.shadowmap", &m_fmapShadowMap); + Assert(m_mpiclriclrShadow != NULL); + return true; +} + +void Shell::Exit() +{ + if (m_ppal != NULL) + gpakr.UnmapFile(&m_fmapPalette); + if (m_mpiclriclrShadow != NULL) + gpakr.UnmapFile(&m_fmapShadowMap); +} + +void Shell::SetPalette() +{ + gmpiclriclrShadow = m_mpiclriclrShadow; + SetHslAdjustedPalette(m_ppal, gnHueOffset, gnSatMultiplier, gnLumOffset); +} + +int Shell::PlayGame(PlayMode pm, MissionIdentifier *pmiid, Stream *pstm, + int nRank) +{ + // UNDONE: free up space-taking Shell resources + + int nGo; + + do { + nGo = knGoSuccess; + + switch (pm) { + case kpmNormal: + nGo = ggame.PlayLevel(pmiid, NULL, nRank); + break; + + case kpmSavedGame: + nGo = ggame.PlaySavedGame(pstm); + break; + } + + if (nGo == knGoLoadSavedGame) { + pm = kpmSavedGame; + pstm = gpstmSavedGame; + } + } while (nGo == knGoLoadSavedGame); + + // UNDONE: reload space-taking Shell resources + + gmpiclriclrShadow = m_mpiclriclrShadow; + + return nGo; +} + +void Shell::Launch(bool fLoadReinitializeSave, MissionIdentifier *pmiid) +{ +#ifdef STRESS + static char *s_aszStressLevels[] = { + "S_01.lvl", "S_02.lvl", "S_03.lvl", "S_04.lvl", "S_05.lvl", "S_06.lvl", "S_07.lvl", + "S_08.lvl", "S_09.lvl", "S_10.lvl", "S_11.lvl", "S_12.lvl", "S_13.lvl", "S_14.lvl" + }; + + if (gfStress) { + while (true) { + gtStressTimeout = 30 * 60 * 100L; // 30 minutes + int ilvl = GetAsyncRandom() % ARRAYSIZE(s_aszStressLevels); + strncpyz(pmiid->szLvlFilename, s_aszStressLevels[ilvl], + sizeof(pmiid->szLvlFilename)); + int nGo = PlayGame(kpmNormal, pmiid, NULL, 0); + if (nGo == knGoAppStop) + return; + } + } +#endif // def STRESS + +#if 0 +#if defined(DEBUG) && defined(CE) + WCHAR wszDeviceName[128]; + SystemParametersInfo(SPI_GETOEMINFO, sizeof(wszDeviceName), &wszDeviceName, 0); + char tst[500]; + int cch = wcslen(wszDeviceName); + char *pch = (char *)wszDeviceName; + char *pdst = tst; + while (cch > 0) { + if (*pch != 0) + *pdst++ = *pch++; + else + pch++; + cch--; + } + *pdst = 0; + HtMessageBox(kfMbClearDib, "Device ID String", tst); +#endif +#endif + +#ifdef BETA_TIMEOUT + HtMessageBox(kfMbWhiteBorder | kfMbClearDib, "HOSTILE TAKEOVER", + "Hostile Takeover\n\n" + "Copyright 2003-2008 Spiffcode, Inc.\n\n" + "http://www.spiffcode.com\n\n" + "Test Release. Please keep private until out of beta. Thank you :)"); +#endif + + // Handle DRM + + if (!DrmValidate()) + return; + + if (gevm.IsAppStopping()) + return; + + // Are we launching immediately into a saved game? + + Stream *pstm = HostOpenSaveGameStream(knGameReinitializeSave, true); + + if (pstm != NULL) { + + // Stream is closed/deleted upon return + + int nGo = PlayGame(kpmSavedGame, NULL, pstm, 0); + if (nGo == knGoAppStop) + return; + + //if (nGo == knGoInitFailure) + // silently fall into a normal game open + + } else if (pmiid != NULL) { + int nGo = PlayGame(kpmNormal, pmiid, NULL, 0); + if (nGo == knGoAppStop) + return; + + if (nGo == knGoInitFailure) + HtMessageBox(kfMbWhiteBorder, "Load Game", "Error launch level!"); + } + + while (true) { + if (gevm.IsAppStopping()) { + return; + } + + // Show startup form (new single player, new multi player, load saved + // game, etc) + + ShellForm *pfrm = (ShellForm *)gpmfrmm->LoadForm(gpiniForms, + kidfStartup, new MainMenuForm()); + Assert(pfrm != NULL); + if (pfrm == NULL) + return; + +#ifdef V100_MENUS + pfrm->GetControlPtr(kidcPlayMultiPlayer)->Show(false); + pfrm->GetControlPtr(kidcPlaySinglePlayer)->Show(false); + if (gfDemo) { + pfrm->GetControlPtr(kidcBeginNewGame)->Show(false); + pfrm->GetControlPtr(kidcPlayMission)->Show(false); + } else { + pfrm->GetControlPtr(kidcPlayDemo)->Show(false); + Control *pctl = pfrm->GetControlPtr(kidcBuyMe); + Rect rc; + pctl->GetRect(&rc); + pctl->Show(false); + pctl = pfrm->GetControlPtr(kidcLoadSavedGame); + pctl->SetRect(&rc); + } +#else + if (gfDemo) + pfrm->GetControlPtr(kidcLoadSavedGame)->Show(false); + else + pfrm->GetControlPtr(kidcBuyMe)->Show(false); +#endif + + // Make Shell palette and shadow map active + + gshl.SetPalette(); + + int idc; + pfrm->DoModal(&idc); + delete pfrm; + + // While the dialog was up the player might have exited the app + + if (gevm.IsAppStopping()) + return; + + // Before beta check + + switch (idc) { + case kidcPlay: + if (DoPlay()) { + return; + } + continue; + + case kidcBuyMe: + DrmValidate(); + continue; + + case kidcSetupGame: + DoModalGameOptionsForm(m_ppal, false); + continue; + + case kidcForums: + HostOpenUrl(""); + continue; + + case kidcHelp: + Help(); + continue; + + case kidcCredits: + Help("credits"); + continue; + } + +#ifdef BETA_TIMEOUT + if (!CheckBetaTimeout()) { + continue; + } +#endif + + switch (idc) { + case kidcPlayDemo: + case kidcBeginNewGame: + if (BeginNewGame()) + return; + break; + + case kidcPlayMission: + case kidcPlaySinglePlayer: + if (PlaySinglePlayer(NULL)) { + return; + } + break; + + case kidcPlayMultiPlayer: + if (PlayMultiplayer(NULL)) { + return; + } + break; + + case kidcLoadSavedGame: + { + Stream *pstm = PickLoadGameStream(); + if (pstm == NULL) + break; + + // Stream is closed/deleted upon return + + int nGo = PlayGame(kpmSavedGame, NULL, pstm, 0); + + if (nGo == knGoAppStop) + return; + + if (nGo == knGoInitFailure) + HtMessageBox(kfMbWhiteBorder | kfMbClearDib, "Load Game", "Error loading saved game!"); + } + break; + + case kidcDownloadMissions: + DownloadMissionPack(); + break; + + case kidcExitGame: + return; + } + } +} + +bool Shell::DoPlay() +{ + while (true) { + ShellForm *pfrm = (ShellForm *)gpmfrmm->LoadForm(gpiniForms, + kidfPlay, new ShellForm()); + if (pfrm == NULL) { + return kidcCancel; + } + int idc; + pfrm->DoModal(&idc); + delete pfrm; + + if (gevm.IsAppStopping()) { + return true; + } + + if (idc == kidcCancel) { + return false; + } + +#ifdef BETA_TIMEOUT + if (!CheckBetaTimeout()) { + continue; + } +#endif + + if (idc == kidcPlaySinglePlayer) { + if (PlaySinglePlayer(NULL)) { + return true; + } + } + + if (idc == kidcPlayMultiPlayer) { + if (PlayMultiplayer(NULL)) { + return true; + } + } + } +} + +void Shell::DownloadMissionPack() +{ + // Show the download form. True is returned if the user wants to + // play the first mission in the pack identified by packid. + + PackId packid; + if (!ShowDownloadMissionPackForm(&packid)) { + return; + } + + // The downloaded a pack identified by packid, and wants to play it. + // Find the first mission and determine if it's single or multi + // player. Then, launch the appropriate Play form with this info. + // It will show, with the appropriate mission highlighted. + + MissionList *pml = CreateMissionList(&packid, kmltAll); + if (pml == NULL) { + return; + } + if (pml->GetCount() == 0) { + delete pml; + return; + } + MissionDescription md; + pml->GetMissionDescription(0, &md); + + // Show the appropriate form. This will cause the first mission from + // this pack to be highlighted. + + if (pml->IsMultiplayerMissionType(md.mt)) { + PlayMultiplayer(&packid); + } else { + PlaySinglePlayer(&packid); + } + + delete pml; +} + +bool Shell::PlayMultiplayer(const PackId *ppackid) +{ + Lobby lobby; + dword result = lobby.Shell(ppackid); + return result == knShellResultAppStop; +} + +bool Shell::PlaySinglePlayer(const PackId *ppackid) +{ + MissionIdentifier miidFind; + memset(&miidFind, 0, sizeof(miidFind)); + if (ppackid != NULL) { + miidFind.packid = *ppackid; + } + + while (true) { + // First, create a mission list, which is an enumerator for the + // missions we want to show in this form. + + MissionList *pml = CreateMissionList(NULL, kmltSinglePlayer); + if (pml == NULL) { + return true; + } + SelectMissionForm *pfrm = (SelectMissionForm *)gpmfrmm->LoadForm( + gpiniForms, kidfSelectMissionWide, + new SelectMissionForm(pml, &miidFind)); + if (pfrm == NULL) { + delete pml; + return true; + } + + int idc; + pfrm->DoModal(&idc); + + MissionIdentifier miid; + bool fMissionSelected = false; + if (idc == kidcOk) { + if (pfrm->GetSelectedMission(&miid)) { + miidFind = miid; + fMissionSelected = true; + } + } + + delete pfrm; + delete pml; + + // While the dialog was up the player might have exited the app + + if (gevm.IsAppStopping()) + return true; + + if (!fMissionSelected) + return false; + + int nGo = PlayGame(kpmNormal, &miid, NULL, 0); + + // The next time the SelectMission form shows, highlight the last + // mission the user was playing. + + miidFind = ggame.GetLastMissionIdentifier(); + + if (nGo == knGoAppStop) { + return true; + } + + if (nGo == knGoInitFailure) { + HtMessageBox(kfMbWhiteBorder, "Error", "Unable to load and initialize the mission."); + continue; + } + } + + return false; +} + +#if 0 +// Returns true if the app is stopping + +bool Shell::PlaySinglePlayer() +{ + while (true) { + ShellForm *pfrm = (ShellForm *)gpmfrmm->LoadForm(gpiniForms, kidfPlaySolo, new ShellForm()); + if (pfrm == NULL) + return false; +#if !defined(DEV_BUILD) + pfrm->GetControlPtr(kidcLoadSavedGame)->Show(false); + pfrm->GetControlPtr(kidcPlaybackGame)->Show(false); +#endif + int idc; + pfrm->DoModal(&idc); + delete pfrm; + + // While the dialog was up the player might have exited the app + + if (gevm.IsAppStopping()) + return true; + + switch (idc) { + case kidcCancel: + return false; + + case kidcBeginNewGame: + return BeginNewGame(); + + case kidcPlayChallengeLevel: + if (PlayChallengeLevel(false)) + return true; + break; + + case kidcPlayStoryMission: + if (PlayChallengeLevel(true)) + return true; + break; + + case kidcPlaybackGame: + break; + + case kidcLoadSavedGame: + { + Stream *pstm = PickLoadGameStream(); + if (pstm == NULL) + break; + + // Stream is closed/deleted upon return + + int nGo = PlayGame(kpmSavedGame, pstm); + + if (nGo == knGoAppStop) + return true; + + if (nGo == knGoInitFailure) + HtMessageBox(kfMbWhiteBorder | kfMbClearDib, "Load Game", "Error loading saved game!"); + } + break; + } + } + + return false; +} +#endif + +bool Shell::PlayChallengeLevel(bool fStory) +{ +#if 0 + while (true) { + // Have the player select a level to play + + PickLevelForm *pfrm = (PickLevelForm *)gpmfrmm->LoadForm(gpiniForms, kidfPickLevel, + new PickLevelForm(fStory ? kltStory : kltChallenge)); + Assert(pfrm != NULL); + if (pfrm == NULL) + return true; + + if (gfDemo) { + Control *pctl = pfrm->GetControlPtr(kidcOk); + pctl->Show(false); + } else { +#if 0 + // UNDONE: + pctl = GetControlPtr(kidcPleaseRegister); + pctl->Show(false); +#endif + } + + int idc; + pfrm->DoModal(&idc); + + char szLevel[kcbFilename]; + if (idc != kidcCancel) + strcpy(szLevel, pfrm->m_szLevel); + delete pfrm; + + // While the dialog was up the player might have exited the app + + if (gevm.IsAppStopping()) + return true; + + if (idc == kidcCancel) + return false; + + int nGo = PlayGame(kpmNormal, szLevel); + if (nGo == knGoAppStop) + return true; + + if (nGo == knGoInitFailure) { + HtMessageBox(kfMbWhiteBorder, "Error", "Unable to load and initialize the mission."); + continue; + } + } +#endif + return false; +} + +bool Shell::BeginNewGame() +{ +#if 0 + bool fNewGame = true; + int nRank = 0; + if ((gnDemoRank != 0) && (!gfDemo)) { + + // would you like to replay missions? + // UNDONE: Expand HtMessageBox and use it here instead of all this. + + DialogForm *pfrm = (DialogForm *)gpmfrmm->LoadForm(gpiniForms, kidfContinueGame, new DialogForm()); + if (pfrm != NULL) { + pfrm->SetBorderColorIndex(kiclrWhite); + pfrm->SetTitleColor(GetColor(kiclrSide1)); + pfrm->SetBackgroundColorIndex(kiclrShadow2x); + pfrm->SetClearDibFlag(); + + // position the form + + Rect rcForm; + pfrm->GetRect(&rcForm); + DibBitmap *pbm = pfrm->GetFormMgr()->GetDib(); + Size siz; + pbm->GetSize(&siz); + int yNew = (siz.cy - rcForm.Height()) / 2; + int xNew = (siz.cx - rcForm.Width()) / 2; + rcForm.Offset(xNew - rcForm.left, yNew - rcForm.top); + pfrm->SetRect(&rcForm); + + int idc; + pfrm->DoModal(&idc); + gpmfrmm->RemoveForm(pfrm); + + if (gevm.IsAppStopping()) + return false; + + + fNewGame = (idc == kidcOk); + delete pfrm; + } + + nRank = fNewGame ? 0 : gnDemoRank; + gnDemoRank = 0; + ggame.SavePreferences(); + } + int nGo = PlayGame(kpmNormal, (void *)(fNewGame ? "S_00.lvl" : "S_03.lvl"), nRank); + if (nGo == knGoAppStop) + return true; + + if (nGo == knGoInitFailure) + HtMessageBox(kfMbWhiteBorder, "Error", "Unable to load and initialize the mission."); + +#endif + return false; +} + +#if 0 +// +// PickLevelForm implementation +// + +PickLevelForm::PickLevelForm(LevelType lt) +{ + m_lt = lt; + m_szLevel[0] = 0; +} + +int CreateLevelList(char **ppsz) +{ + // Get all the .lvl files + + char szFn[kcbFilename]; + int cFiles = 0; + + Enum enm; + while (gpakr.EnumFiles(&enm, szFn, sizeof(szFn))) { + int cch = strlen(szFn); + if (cch < 4) + continue; + if (szFn[cch - 4] == '.' && szFn[cch - 3] == 'l' && szFn[cch - 2] == 'v' && szFn[cch - 1] == 'l') { + strncpyz((char *)&gpbScratch[cFiles * kcbFilename], szFn, kcbFilename); + cFiles++; + } + } + + // Sort them based on filename! + + for (int i = cFiles - 1; i >= 0; i--) { + for (int j = 1; j <= i; j++) { + char *pszBack = (char *)&gpbScratch[(j - 1) * kcbFilename]; + char *pszAhead = (char *)&gpbScratch[j * kcbFilename]; + + if (strcmp(pszBack, pszAhead) > 0) { + char szT[kcbFilename]; + strcpy(szT, pszBack); + strcpy(pszBack, pszAhead); + strcpy(pszAhead, szT); + } + } + } + + // Alloc a chunk and copy them in + + char *pszT = new char[cFiles * kcbFilename]; + if (pszT == NULL) + return 0; + memcpy(pszT, gpbScratch, cFiles * kcbFilename); + *ppsz = pszT; + return cFiles; +} + +bool PickLevelForm::Init(FormMgr *pfrmm, IniReader *pini, word idf) +{ + if (!ShellForm::Init(pfrmm, pini, idf)) + return false; + + // Create a entry for each level + + char szLevel[kcbFilename]; + char szTitle[100]; + + ListControl *plstc = (ListControl *)GetControlPtr(kidcLevelList); + + char *aszLevels = NULL; + int cLevels = CreateLevelList(&aszLevels); + for (int nLevel = 0; nLevel < cLevels; nLevel++) { + strncpyz(szLevel, (char *)&aszLevels[nLevel * kcbFilename], sizeof(szLevel)); + + // Only show the requested level types + + LevelType lt; + if (strnicmp(szLevel, "m_", 2) == 0) + lt = kltMultiplayer; + else if (strnicmp(szLevel, "s_", 2) == 0) + lt = kltStory; + else + lt = kltChallenge; + + if (lt != m_lt) + continue; + + // Pull title from level. + +#if 1 +//faster + strcpy(szTitle, ""); + IniReader *pini = LoadIniFile(gpakr, szLevel); + if (pini != NULL) { + pini->GetPropertyValue("General", "Title", szTitle, sizeof(szTitle)); + delete pini; + } + plstc->Add(szTitle, (void *)nLevel); +#else + Level *plvl = new Level(); + plvl->LoadLevelInfo(szLevel); + plstc->Add(plvl->GetTitle(), (void *)nLevel); + delete plvl; +#endif + } + + delete aszLevels; + return true; +} + +void PickLevelForm::OnControlSelected(word idc) +{ + if (idc == kidcOk) { + ListControl *plstc = (ListControl *)GetControlPtr(kidcLevelList); + int nLevel = (int)plstc->GetSelectedItemData(); + char *aszLevels = NULL; + int cLevels = CreateLevelList(&aszLevels); + if (nLevel >= 0 && nLevel <= cLevels) { + strcpy(m_szLevel, (char *)&aszLevels[nLevel * kcbFilename]); + delete aszLevels; + } else { + HtMessageBox(kfMbWhiteBorder, "Error!", "First you must select a level to play."); + delete aszLevels; + return; + } + } else if (idc == kidcLevelList) { + return; + } + EndForm(idc); +} +#endif + +// +// ShellForm implementation +// + +ShellForm::ShellForm() +{ + m_fCached = false; + m_fAnimate = true; + m_fTimerEnabled = false; +} + +ShellForm::~ShellForm() +{ + if (m_fTimerEnabled) + gtimm.RemoveTimer(this); +} + +bool ShellForm::Init(FormMgr *pfrmm, IniReader *pini, word idf) +{ + if (!Form::Init(pfrmm, pini, idf)) + return false; + + // Keep invisible until the controls are at the right place + + Show(false); + + // Shell forms draw over the whole screen, so size the form to be full screen. + // This way it is a full screen opaquing form when it comes up, which means things + // won't try to draw behind it, slowing the game down + + DibBitmap *pbm = m_pfrmm->GetDib(); + Size siz; + pbm->GetSize(&siz); + + int xNew = (siz.cx - m_rc.Width()) / 2; + int yNew = (siz.cy - m_rc.Height()) / 2; + + // Reposition the controls + + for (int n = 0; n < m_cctl; n++) { + Control *pctl = m_apctl[n]; + Rect rcCtl; + pctl->GetRect(&rcCtl); + int xNewCtl = rcCtl.left + xNew; + int yNewCtl = rcCtl.top + yNew; + pctl->SetPosition(xNewCtl, yNewCtl); + } + Rect rcNew; + rcNew.Set(0, 0, siz.cx, siz.cy); + SetRect(&rcNew); + + // Set version string - hack + + if (m_idf == kidfStartup) { + LabelControl *pctl = (LabelControl *)GetControlPtr(kidcVersion); + if (pctl != NULL) { + // If no version string, exe+data are unmarked. Show date/time + // Use the version string verbatim so we show extra text at the end + + char szT[64]; + if (!ggame.GetFormattedVersionString(gszVersion, szT)) { + szT[0] = 0; +#ifdef DEV_BUILD + strcat(szT, "DEV BUILD "); +#endif + strcat(szT, __DATE__); + strcat(szT, ", "); + strcat(szT, __TIME__); + } else { + szT[0] = 'v'; + strncpyz(&szT[1], gszVersion, sizeof(szT)); + } + pctl->SetText(szT); + } + } + + return true; +} + +#define kctRate 2 +#define kcFcZip 18 +bool ShellForm::DoModal(int *pnResult, bool fAnimate, bool fShowSound) +{ + m_fAnimate = fAnimate; + if (!m_fAnimate) { + if (fShowSound) + gsndm.PlaySfx(ksfxGuiFormShow); + return Form::DoModal(pnResult, (Sfx)-1, (Sfx)-1); + } + + // Take over form show sound playing + + Size siz; + ggame.GetPlayfieldSize(&siz); + + // Reposition all the form's controls of the form so they can be zipped in + + for (int i = 0; i < m_cctl; i++) { + Control *pctl = m_apctl[i]; + + Rect rcCtl; + pctl->GetRect(&rcCtl); + + // Remember where the control is supposed to end up + + m_axDst[i] = (rcCtl.left & ~1); + + // Odd index controls fly in from the left, even from the right + + if (i & 1) { + pctl->SetPosition(rcCtl.left - siz.cx - (i * PcFromFc(kcFcZip)), rcCtl.top); + } else { + pctl->SetPosition(rcCtl.left + siz.cx + (i * PcFromFc(kcFcZip)), rcCtl.top); + } + } + + // Now that the controls are positioned right make the form visible + + Show(true); + + if (fShowSound) + m_wf |= kfFrmShowSound; + + // Start timer + + gtimm.AddTimer(this, kctRate); + m_fTimerEnabled = true; + m_fCached = false; + m_tLast = 0; + + // Some controls might be added on the fly after ShellForm::Init. Since we + // won't have their destination stashed away we'll just ignore them. + + m_cctlToZip = m_cctl; + + return Form::DoModal(pnResult, (Sfx)-1, (Sfx)-1); +} + +void ShellForm::OnTimer(long tCurrent) +{ + Assert(m_fAnimate); + + int ct = (int)(tCurrent - m_tLast); + m_tLast = tCurrent; + + // Waiting until all the pieces have been cached before doing absolute + // timing. This gives smoother movement. + + int pcMove = PcFromFc(kcFcZip); + if (m_fCached) + pcMove = ((ct + kctRate / 2) / kctRate) * PcFromFc(kcFcZip); + + bool fZipping = false; + + for (int i = 0; i < m_cctlToZip; i++) { + Control *pctl = m_apctl[i]; + + Rect rcCtl; + pctl->GetRect(&rcCtl); + + int xDst = m_axDst[i]; + if (rcCtl.left == xDst) + continue; + + fZipping = true; + + int x; + if (xDst > rcCtl.left) + x = _min(rcCtl.left + pcMove, xDst); + else + x = _max(rcCtl.left - pcMove, xDst); + + pctl->SetPosition(x & ~1, rcCtl.top); + } + + gevm.SetRedrawFlags(kfRedrawDirty | kfRedrawBeforeTimer); + + if (!fZipping) { + gtimm.RemoveTimer(this); + m_fTimerEnabled = false; + if (m_wf & kfFrmShowSound) + gsndm.PlaySfx(ksfxGuiFormShow); + OnZipDone(); + } +} + +void ShellForm::OnPaintBackground(DibBitmap *pbm, UpdateMap *pupd) +{ + // Draw the fancy background bitmap + + Size sizDib; + pbm->GetSize(&sizDib); + RawBitmap *prbm = LoadRawBitmap("titlescreenbkgd.rbm"); + Size sizBmp; + prbm->GetSize(&sizBmp); + + // Draw middle + Rect rcBmp; + rcBmp.left = ((sizDib.cx - sizBmp.cx) / 2) & ~1; + rcBmp.top = (sizDib.cy - sizBmp.cy) / 2; + rcBmp.right = rcBmp.left + sizBmp.cx; + rcBmp.bottom = rcBmp.top + sizBmp.cy; + BltHelper(pbm, prbm, pupd, rcBmp.left, rcBmp.top); + +#if 0 + // Draw left + if (rcBmp.left > 0) { + BltHelper(pbm, prbm, pupd, rcBmp.left - sizBmp.cx, rcBmp.top); + } + + // Draw right + if (rcBmp.right < sizDib.cx) { + BltHelper(pbm, prbm, pupd, rcBmp.right, rcBmp.top); + } +#endif + + delete prbm; + + // If the screen is wider than the form we clear those areas + // out first to the form's background color + + Size siz; + pbm->GetSize(&siz); + if (rcBmp.top > 0) { + Rect rc; + rc.Set(0, 0, siz.cx, rcBmp.top); + FillHelper(pbm, pupd, &rc, GetColor(kiclrBlack)); + } + if (rcBmp.bottom < siz.cy) { + Rect rc; + rc.Set(0, rcBmp.bottom, siz.cx, siz.cy); + FillHelper(pbm, pupd, &rc, GetColor(kiclrBlack)); + } + + if (rcBmp.left > 0) { + Rect rc; + rc.Set(0, rcBmp.top, rcBmp.left, rcBmp.bottom); + FillHelper(pbm, pupd, &rc, GetColor(kiclrBlack)); + } + if (rcBmp.right < siz.cx) { + Rect rc; + rc.Set(rcBmp.right, rcBmp.top, siz.cx, rcBmp.bottom); + FillHelper(pbm, pupd, &rc, GetColor(kiclrBlack)); + } + + // Don't start timing for absolute positioned animation until we've + // loaded the cache at least once. Gives smoother animation + + if (!m_fCached) { + m_fCached = true; + m_tLast = gtimm.GetTickCount(); + } +} + +// +// RegisterNowForm +// + +class RegisterNowForm : public ShellForm +{ +public: + virtual bool OnPenEvent(Event *pevt) secGameOptionsForm; +}; + +void DoRegisterNowForm() +{ + ShellForm *pfrm = (ShellForm *)gpmfrmm->LoadForm(gpiniForms, kidfRegisterNow, new RegisterNowForm()); + if (pfrm != NULL) { + pfrm->DoModal(); + delete pfrm; + } +} + +bool RegisterNowForm::OnPenEvent(Event *pevt) +{ + if (pevt->eType == penDownEvent) { + EndForm(kidcOk); + return true; + } + return false; +} + +} // namespace wi diff --git a/game/SimUI.cpp b/game/SimUI.cpp new file mode 100644 index 0000000..64c65c6 --- /dev/null +++ b/game/SimUI.cpp @@ -0,0 +1,2803 @@ +#include "game/ht.h" +#include "mpshared/netmessage.h" +#include "game/strings.h" +#include "game/statetracker.h" +#include "game/stateframe.h" +#include "game/uploader.h" +#include "game/serviceurls.h" +#include "game/chatter.h" +#include "game/gameform.h" +#include "base/sigslot.h" +#include + +namespace wi { + +// Global representing the contiguous rect opaquing the map, in map relative tile coords + +TRect *gptrcMapOpaque; +TRect gtrcMapOpaque; + +// Handy dandy global flags used for enabling various test features + +#ifdef STATS_DISPLAY +int gcBitmapsDrawn; +extern bool gfShowStats; +int gcbCommandsQueued; +#endif +int gcMessagesPerUpdate; + +extern bool gfShowFPS; +extern bool gfSuspendUpdates; +extern bool gfSingleStep; + +short gcPaint = 0; +#ifdef STATS_DISPLAY +static short s_cUpdateRects = 0; +#endif +static fix s_cfxFPS = 0; +static long s_tLastFPSUpdate = 0; + +// Misc SimUI management variables + +bool gfDragSelecting; +WPoint s_wptSelect1, s_wptSelect2; +WRect gwrcSelection; +WPoint s_awptSelection[250]; +int s_cwptSelection; +bool gfLassoSelection = false; +#ifdef STATS_DISPLAY +long gcPathScoresCalced = 0; +#endif + +bool PtInPolygon(WPoint *awpt, int cwpt, WCoord wx, WCoord wy) secSimUIForm; + +// +// Form used for waiting +// + +WaitForm::WaitForm(char *pszTitle, bool fFullScreen) +{ + m_pszTitle = pszTitle; + m_fFullScreen = fFullScreen; +} + +bool WaitForm::Init(FormMgr *pfrmm, IniReader *pini, word idf) +{ + if (!Form::Init(pfrmm, pini, idf)) + return false; + + // Size the form to be full screen + + DibBitmap *pbm = m_pfrmm->GetDib(); + Size siz; + pbm->GetSize(&siz); + + // Remember old bounds + + m_rcOrig = m_rc; + + // Set text if passed + + if (m_pszTitle != NULL) { + LabelControl *plbl = (LabelControl *)GetControlPtr(kidcTitle); + plbl->SetText(m_pszTitle); + } + + int xNew = (siz.cx - m_rc.Width()) / 2; + int yNew = (siz.cy - m_rc.Height()) / 2; + + if (m_fFullScreen) { + // Reposition the controls + + for (int n = 0; n < m_cctl; n++) { + Control *pctl = m_apctl[n]; + Rect rcCtl; + pctl->GetRect(&rcCtl); + int xNewCtl = rcCtl.left + xNew; + int yNewCtl = rcCtl.top + yNew; + pctl->SetPosition(xNewCtl, yNewCtl); + } + Rect rcNew; + rcNew.Set(0, 0, siz.cx, siz.cy); + SetRect(&rcNew); + } + + return true; +} + +void WaitForm::OnPaintBackground(DibBitmap *pbm, UpdateMap *pupd) +{ + Form::OnPaintBackground(pbm, pupd); + + DrawBorder(pbm, &m_rcOrig, gcxyBorder, GetColor(kiclrCyanSideFirst), pupd); +} + +// This is a hack so that sigslot.h doesn't need to be included from ht.h. + +class SlotHack : public base::has_slots<> { +public: + void Connect(SimUIForm *sui, Chatter *chatter) { + if (chatter != NULL) { + chatter->SignalOnBlink.connect(this, &SlotHack::OnChatButtonBlink); + chatter->SignalOnPlayers.connect(this, &SlotHack::OnPlayersButton); + } + sui_ = sui; + chatter_ = chatter; + } + void Disconnect() { + if (chatter_ != NULL) { + chatter_->SignalOnBlink.disconnect(this); + chatter_->SignalOnPlayers.disconnect(this); + } + } + void OnChatButtonBlink(bool fOn) { + if (sui_ != NULL) { + sui_->OnChatButtonBlink(fOn); + } + } + void OnPlayersButton() { + if (sui_ != NULL) { + sui_->OnPlayersButton(); + } + } + SimUIForm *sui_; + Chatter *chatter_; +}; +SlotHack g_slothack; + +// +// SimUIForm implementation +// + +bool SimUIForm::s_fReadyForPaint; + +SimUIForm::SimUIForm(word wfRole, dword gameid, Chatter *chatter) +{ + s_fReadyForPaint = false; + gfDragSelecting = false; + s_cwptSelection = 0; + + m_fTimerAdded = false; + + m_wfRole = wfRole; + m_pcmdqServer = NULL; + m_pfrmWaitingForAllPlayers = NULL; + m_tLastCommunication = 0; + + m_nStateMoveTarget = 0; + + m_ppenh = NULL; +#ifdef IPHONE + SetUIType(kuitFinger); +#else + SetUIType(kuitStylus); +#endif + m_punmrFirst = NULL; + + m_gameid = gameid; + m_pchatter = chatter; + if (m_pchatter != NULL) { + m_pchatter->SetChatTitle("Game Chat"); + } +#ifdef TRACKSTATE + m_ptracker = new StateTracker(m_gameid); + m_fSyncError = false; +#endif + + g_slothack.Connect(this, chatter); +} + +SimUIForm::~SimUIForm() +{ + delete m_pfrmWaitingForAllPlayers; + delete m_pfrmLag; + if (m_fTimerAdded) + gtimm.RemoveTimer(this); + if (m_wfRole & kfRoleMultiplayer) { + gptra->SetCallback(NULL); + gptra->SetGameCallback(NULL); + } + + if (m_pcmdqServer != NULL) { + m_pcmdqServer->Exit(); + delete m_pcmdqServer; + } + + delete m_ppenh; + +#ifdef TRACKSTATE + delete m_ptracker; +#endif + + g_slothack.Disconnect(); + if (m_pchatter != NULL) { + m_pchatter->HideChat(); + } +} + +void SimUIForm::SetUIType(UIType uit) +{ + switch (uit) { + case kuitFinger: + delete m_ppenh; + m_ppenh = new FingerHandler(this); + break; + + case kuitStylus: + delete m_ppenh; + m_ppenh = new StylusHandler(this); + break; + } +} + +void SimUIForm::OnChatButtonBlink(bool fOn) { +// Annoying since the button is visible during observe, and you can already +// see the chat on screen. +#if 0 + if (gpplrLocal->GetFlags() & kfPlrObserver) { + GetControlPtr(kidcChat)->Show(fOn); + } +#endif +} + +void SimUIForm::OnPlayersButton() { + std::string s = GameForm::GetPlayersString(); + if (m_pchatter != NULL) { + m_pchatter->AddChat("", s.c_str(), true); + } +} + +static SimUIForm *gpsui; + +const long kctStartTimeout = 1500; // 15 seconds + +bool SimUIForm::Init(FormMgr *pfrmm, IniReader *pini, word idf) +{ + gpsui = this; + + m_tStartTimeout = HostGetTickCount() + kctStartTimeout; + + // Init move target + + m_nStateMoveTarget = 0; + m_aniMoveTarget.Init(g_panidMoveTarget); + + if (m_wfRole & kfRoleMultiplayer) { + m_pcmdqServer = new CommandQueue(); + Assert(m_pcmdqServer != NULL, "out of memory!"); + if (m_pcmdqServer == NULL) + return false; + if (!m_pcmdqServer->Init(kcmsgCommandQueueMax)) { + return false; + } + + // Transport callback + + gptra->SetCallback(this); + gptra->SetGameCallback(this); + + // Create a big form to cover everything and show "Waiting for all + // players" + + m_pfrmWaitingForAllPlayers = gpmfrmm->LoadForm(gpiniForms, kidfWaiting, new WaitForm("WAITING FOR ALL PLAYERS...", true)); + if (m_pfrmWaitingForAllPlayers != NULL) { + gpmfrmm->DrawFrame(false); + } + } + + // New multiplayer design: client maintains its own update timer. + // Client messages are sent "just in time" by server, scheduled to be + // executed on a given update. Clients know what update to expect the + // next message from the server. Every so often the client sends feedback + // to the server on latency (time of actual update - time message received) + // so that the server can tune when client messages are sent, in real + // time. This approach works better over the internet. The previous + // approach worked fine over local networks. + + gtimm.AddTimer(this, gtGameSpeed); + m_fTimerAdded = true; + + while (m_punmrFirst != NULL) { + UpdateNetMessageRecord *punmrT = m_punmrFirst; + m_punmrFirst = punmrT->punmrNext; + delete punmrT->punm; + delete punmrT; + } + + // Let the base Form initialize the controls + + bool fSuccess = Form::Init(pfrmm, pini, idf); + + if (m_wfRole & kfRoleMultiplayer) { + m_cUpdatesBlock = 0; + m_msUpdatesBlock = 0; + SendUpdateResult(0, 0, 0); + } + + // Size the rect to the dib + + DibBitmap *pbmSim = m_pfrmm->GetDib(); + Size sizSim; + pbmSim->GetSize(&sizSim); + Rect rcSim; + rcSim.Set(0, 0, sizSim.cx, sizSim.cy); + SetRect(&rcSim); + + // Set control positions + + Control *pctlT; + Rect rcT; + + // Objective is at upper-left + + pctlT = GetControlPtr(kidcObjective); + Assert(pctlT != NULL); + pctlT->SetPosition(0, 0); + + // Countdown timer is at upper-right + + pctlT = GetControlPtr(kidcCountdown); + Assert(pctlT != NULL); + pctlT->GetRect(&rcT); + pctlT->SetPosition(sizSim.cx - rcT.Width(), 0); + + // FPS is at upper right, down 1 line to leave room for countdown timer + + pctlT = GetControlPtr(kidcFps); + Assert(pctlT != NULL); + pctlT->GetRect(&rcT); + pctlT->SetPosition(sizSim.cx - rcT.Width(), rcT.Height()); + + // Chat is at upper right. Hide chat button for now. It only appears + // in observer mode. + + pctlT = GetControlPtr(kidcChat); + pctlT->GetRect(&rcT); + pctlT->SetPosition(sizSim.cx - rcT.Width() - rcT.top, rcT.top); + pctlT->Show(false); + + // Alert is at bottom left + + pctlT = GetControlPtr(kidcAlert); + Assert(pctlT != NULL); + pctlT->GetRect(&rcT); + pctlT->SetPosition(0, sizSim.cy - rcT.Height()); + + // Reset lag state + + m_pfrmLag = NULL; + m_pidLagging = kpidNeutral; + + // Show / hide these controls + + GetControlPtr(kidcFps)->Show(gfShowFPS); + GetControlPtr(kidcAlert)->Show(false); + Assert(gsim.GetLevel() != NULL); + Assert(gsim.GetLevel()->GetTriggerMgr() != NULL); + GetControlPtr(kidcCountdown)->Show(gsim.GetLevel()->GetTriggerMgr()->GetCountdownTimer()->GetFlags() & kfCtVisibleAtStart); + + // This form wants pen2 events + m_wf |= kfFrmDemandPen2; + + return fSuccess; +} + +// +// IGameCallback implementation +// + +void SimUIForm::OnNetMessage(NetMessage **ppnm) +{ +#ifdef TRACKSTATE + // If there has been a sync error, do not respond to any messages + if (m_fSyncError) { + return; + } +#endif + + // Setting *ppnm = NULL allows the callee to keep the NetMessage + // rather than the caller deleting it + NetMessage *pnm = *ppnm; + + // Track the last time communication was received on this device + + m_tLastCommunication = HostGetTickCount(); + + switch (pnm->nmid) { + case knmidScPlayerDisconnect: + { + // This must run at the same point on all clients, since + // it sets the kfPlrComputer flag. + PlayerDisconnectNetMessage *pdm = (PlayerDisconnectNetMessage*)pnm; + OnPlayerDisconnectNotify(pdm->pid, pdm->nReason); + + // Asynchronously, check for game over + Event evt; + evt.eType = checkGameOverEvent; + evt.dw = pdm->pid; + gevm.PostEvent(&evt); + } + break; + + case knmidScLagNotify: + // This player is being notified by the server of another player + // that is lagging. + + OnLagNotify(((LagNotifyNetMessage *)pnm)->pidLagging, + ((LagNotifyNetMessage *)pnm)->cSeconds); + break; + + case knmidScUpdate: + QueueUpdateMessage((UpdateNetMessage *)*ppnm); + + // Tell the caller to not delete + *ppnm = NULL; + break; + + case knmidScCheckWin: + OnCheckWin(((CheckWinNetMessage *)pnm)->pid); + break; + +#ifdef TRACKSTATE + case knmidScSyncError: + ReportSyncError(); + break; +#endif + } +} + +void SimUIForm::OnReceiveChat(const char *player, const char *chat) { + ShowAlert(base::Format::ToString("%s: %s", player, chat)); + if (m_pchatter != NULL) { + m_pchatter->AddChat(player, chat, false); + } +} + +void SimUIForm::OnGameDisconnect() +{ + HtMessageBox(kfMbWhiteBorder, "Network", + "You've been disconnected from this game. Press OK to continue."); + + Event evt; + memset(&evt, 0, sizeof(evt)); + evt.eType = gameOverEvent; + evt.dw = knGoAbortLevel; + gevm.PostEvent(&evt); +} + +void SimUIForm::OnStatusUpdate(char *pszStatus) +{ + // nothing to do here +} + +void SimUIForm::OnConnectionClose() +{ + Event evt; + memset(&evt, 0, sizeof(evt)); + evt.idf = m_idf; + evt.eType = connectionCloseEvent; + gevm.PostEvent(&evt); +} + +std::string s_message; + +void SimUIForm::OnShowMessage(const char *message) +{ + s_message = message; + Event evt; + memset(&evt, 0, sizeof(evt)); + evt.idf = m_idf; + evt.eType = showMessageEvent; + gevm.PostEvent(&evt); +} + +bool SimUIForm::OnFilterEvent(Event *pevt) { + if (pevt->eType == connectionCloseEvent) { + // My connection to the server broke! + + if (m_pchatter != NULL) { + m_pchatter->HideChat(); + } + HtMessageBox(kfMbWhiteBorder, "Comm Problem", + "Your connection to the server dropped."); + + if ((gpplrLocal->GetFlags() & kfPlrObserver) == 0) { + gpplrLocal->ShowObjectives(ksoWinSummary, false, true); + } + + Event evt; + memset(&evt, 0, sizeof(evt)); + evt.eType = gameOverEvent; + evt.dw = knGoAbortLevel; + gevm.PostEvent(&evt); + return true; + } + + if (pevt->eType == showMessageEvent) { + if (m_pchatter != NULL) { + m_pchatter->HideChat(); + } + HtMessageBox(kfMbWhiteBorder, "Server Message", s_message.c_str()); + s_message = ""; + return true; + } + return false; +} + +#ifdef TRACKSTATE +void SimUIForm::ReportSyncError() +{ + // A sync error has occured, must remember this so no state changes + // from here forward + m_fSyncError = true; + + // Remove the game timer + if (m_fTimerAdded) { + gtimm.RemoveTimer(this); + m_fTimerAdded = false; + } + + // Alert the user to what is happening + HtMessageBox(kfMbWhiteBorder, "Sync Error", + "This mission has experienced a sync error and must end. " + "Please notify the mission author."); + gpmfrmm->DrawFrame(true); + + // Upload the state tracker state + base::ByteBuffer *pbb = m_ptracker->ToJson(); + const char *url = base::Format::ToString("%s?gameid=%d&pid=%d", + kszSyncErrorUploadUrl, m_gameid, gpplrLocal->GetId()); + UploadByteBuffer(gphttp, pbb, url); + + // End the game + Event evt; + memset(&evt, 0, sizeof(evt)); + evt.eType = gameOverEvent; + evt.dw = knGoAbortLevel; + gevm.PostEvent(&evt); +} +#endif + +void SimUIForm::QueueUpdateMessage(UpdateNetMessage *punm) { +#ifdef TRACKSTATE + m_ptracker->ExpireFrames(punm->cUpdatesSync); +#endif + + // Remember the UpdateNetMessage + + UpdateNetMessageRecord *punmr = new UpdateNetMessageRecord; + punmr->msReceived = HostGetMillisecondCount(); + punmr->punm = punm; + punmr->punmrNext = NULL; + + // Put it on the end of the list + + UpdateNetMessageRecord **ppunmr = &m_punmrFirst; + while ((*ppunmr) != NULL) { + ppunmr = &(*ppunmr)->punmrNext; + } + *ppunmr = punmr; + + // If the client is blocked waiting for this update, process it + // right away. + + bool fUpdateNow = false; + if (gsim.GetUpdateCount() + 1 == m_cUpdatesBlock) { + fUpdateNow = true; + } + + // It's possible the client gets behind. For example, if the client + // is blocked on network i/o and then sudden 5 updates come in, + // running the timer as normal would take too long for it to catch up with + // the server. So, process the commands here. + + if (m_punmrFirst != NULL && m_punmrFirst->punmrNext != NULL) { + fUpdateNow = true; + } + + // Running the simulation requires running from the event loop, + // because of the state set, and state checked, from within + // the event loop. + + if (fUpdateNow) { + Event evt; + evt.eType = runUpdatesNowEvent; + evt.idf = m_idf; + gevm.PostEvent(&evt); + } +} + +void SimUIForm::RunUpdatesNow() +{ + // If blocked waiting for an update and there is one, process it + // and reset the timer + + if (m_punmrFirst != NULL && gsim.GetUpdateCount() + 1 == m_cUpdatesBlock) { + gtimm.RemoveTimer(this); + OnTimer(0); + gtimm.AddTimer(this, gtGameSpeed); + ggame.UpdateTriggers(); + } + + // If there are more than one updates available, process them to + // catch up. + + while (m_punmrFirst != NULL && m_punmrFirst->punmrNext != NULL) { + gtimm.RemoveTimer(this); + OnTimer(0); + gtimm.AddTimer(this, gtGameSpeed); + ggame.UpdateTriggers(); + } +} + +bool SimUIForm::ProcessUpdateMessage(CommandQueue *pcmdq) +{ + // This should not happen + if (gsim.GetUpdateCount() + 1 > m_cUpdatesBlock) { + Trace("Broken!"); + return false; + } + + // Commands are processed at block points. If not at block point yet, + // continue the simulation. + + if (gsim.GetUpdateCount() + 1 < m_cUpdatesBlock) { + pcmdq->Clear(); + return true; + } + + // At cUpdatesBlock for the first time? If so remember the timestamp + // for latency calc purposes + + if (m_msUpdatesBlock == 0) { + m_msUpdatesBlock = HostGetMillisecondCount(); + } + + // No messages to process? Block until they come. Users don't like + // blocking since it results in on screen stutter, perceived as lag. + + if (m_punmrFirst == NULL) { + Trace("BLOCK!"); + return false; + } + + // Take the next set of commands and queue them up + + UpdateNetMessageRecord *punmr = m_punmrFirst; + m_punmrFirst = punmr->punmrNext; + UpdateNetMessage *punm = punmr->punm; + pcmdq->Clear(); + for (int i = 0; i < punm->cmsgCommands; i++) { + pcmdq->Enqueue(&punm->amsgCommands[i]); + } + + // Send the update result with latency info + // Don't send if cUpdatesBlock_ == 0. This is a special case + // that is handled elsewhere (search for calls to SendUpdatesResult). + + long cmsLatency = m_msUpdatesBlock - punmr->msReceived; + +#ifdef TRACKSTATE + dword hash = m_ptracker->GetHash(); + Trace("GetHash: end update %d", gsim.GetUpdateCount()); + m_ptracker->SetNextBlock(); +#else + dword hash = 0; +#endif + +#if 0 + // Adjust the timer trigger, without changing the rate. If updates are + // coming a little too soon, wait a bit before triggering next. If updates + // are a bit old, trigger sooner next time. This maintains "just in time" + // update delivery. + if (cmsLatency <= 0) { + gtimm.BoostTimer(this, 1); + } + if (cmsLatency >= 200) { + gtimm.BoostTimer(this, -1); + } +#endif + + if (m_cUpdatesBlock != 0) { + SendUpdateResult(m_cUpdatesBlock, cmsLatency, hash); + } + + // This is the next point to block. Ideally the server sends another + // UpdateNetMessage before the clients get to this point. This is how + // stuttering is reduced, which users perceive as lag. + + m_cUpdatesBlock = punm->cUpdatesBlock; + m_msUpdatesBlock = 0; + + delete punm; + delete punmr; + + // Ok to update the simulation + return true; +} + +void SimUIForm::SendUpdateResult(long cUpdatesBlock, long cmsLatency, + dword hash) +{ + UpdateResultNetMessage urnm; + urnm.ur.cUpdatesBlock = cUpdatesBlock; + urnm.ur.hash = hash; + urnm.ur.cmsLatency = cmsLatency; + gptra->SendNetMessage(&urnm); +} + +void SimUIForm::OnTimer(long tCurrent) { +#ifdef TRACKSTATE + // If there has been a sync error, do nothing + if (m_fSyncError) { + return; + } +#endif + + if (gfSuspendUpdates && !gfSingleStep) { + gevm.SetRedrawFlags(kfRedrawDirty); + return; + } + gfSingleStep = false; + if (gsim.IsPaused()) { + return; + } + +// TUNE: +#define kctLocalLag 200 + if ((m_wfRole & (kfRoleMultiplayer | kfRoleServer)) == kfRoleMultiplayer) { + if (m_tLastCommunication != 0) { + long tCurrent = HostGetTickCount(); + if (tCurrent - m_tLastCommunication >= kctLocalLag) { + ShowAlert("Network lag"); + + // Request redraw. When the client is waiting on the server it + // won't redraw unless asked to. + + gevm.SetRedrawFlags(kfRedrawDirty); + } + } + } + + // Send queued commands to server whenever there are any + + if ((m_wfRole & (kfRoleMultiplayer | kfRoleServer)) == kfRoleMultiplayer) { + + int cmsg = gcmdq.GetCount(); + if (cmsg != 0) { + int cbcc = sizeof(ClientCommandsNetMessage) + + ((cmsg - 1) * sizeof(Message)); + ClientCommandsNetMessage *pcc = + (ClientCommandsNetMessage *)new byte[cbcc]; + pcc->nmid = knmidCsClientCommands; + pcc->cb = cbcc; + pcc->cmsgCommands = cmsg; + memcpy(&pcc->amsgCommands, gcmdq.GetFirst(), + cmsg * sizeof(Message)); + gptra->SendNetMessage(pcc); + delete pcc; + } + gcmdq.Clear(); + } + + if ((m_wfRole & (kfRoleMultiplayer | kfRoleServer)) == kfRoleMultiplayer) { + // Process the next update message + if (ProcessUpdateMessage(m_pcmdqServer)) { +#ifdef TRACKSTATE + BeginTrackState(); +#endif + gsim.Update(m_pcmdqServer); +#ifdef TRACKSTATE + EndTrackState(); +#endif + } + } else { + // Update (i.e., 'step') the Simulation + + gsim.Update(&gcmdq); + } + + // Update UI + + Update(); + +#ifdef DEBUG_HELPERS + extern void UpdateLog(); + UpdateLog(); +#endif + + // Check to see if something needs to be done with the lag form + + CheckLagForm(); +} + +#ifdef TRACKSTATE +StateFrame *gpframeCurrent; + +void SimUIForm::BeginTrackState() +{ + long cUpdates = gsim.GetUpdateCount() + 1; + StateFrame *frame = m_ptracker->AddFrame(cUpdates); + int i = frame->AddCountedValue('SMUI'); + frame->AddValue('BLCK', (dword)m_cUpdatesBlock, i); + + // Set this so it can be used during gsim.Update() + gpframeCurrent = frame; +} + +void SimUIForm::EndTrackState() +{ + gsim.TrackState(gpframeCurrent); + gpframeCurrent = NULL; +} +#endif + +bool SimUIForm::EventProc(Event *pevt) +{ + switch (pevt->eType) { + case runUpdatesNowEvent: + RunUpdatesNow(); + break; + } + + return Form::EventProc(pevt); +} + +void SimUIForm::CheckMultiplayerGameOver(Pid pidLeft) { + if (!gfMultiplayer) { + return; + } + + // This should be handled by triggers, but for now it is + // hamfistedly hardwired here. + // + // If transitioning to a single player (or team) then assume this player + // has won. This isn't always the right choice, that is why it should + // be handled by triggers. But there are a lot of maps made that + // don't handle this case, and there is not enough conditions + // available currently to handle resigning / leaving the game with + // triggers. + + if (gplrm.DetectTransitionToSingleHumanTeam(pidLeft)) { + + // If this player isn't an observer it means they haven't + // officially won/lost yet. Let them know the game is over and set + // them to be an Observer. + + if ((gpplrLocal->GetFlags() & + (kfPlrObserver | kfPlrSummaryShown)) == 0) { + gpplrLocal->ShowObjectives(ksoWinSummary, false, false); + + if (!ggame.AskObserveGame()) { + Event evt; + memset(&evt, 0, sizeof(evt)); + evt.eType = gameOverEvent; + evt.dw = knGoAbortLevel; + gevm.PostEvent(&evt); + } + } else { + // Some other player is victorious. Show alert. + // There is only one non-observer, non-computer player + // left. + + Player *pplr = gplrm.GetNextHumanPlayer(NULL); + if (pplr != NULL) { + char szT[100]; + sprintf(szT, "%s is victorious!", pplr->GetName()); + ShowAlert(szT); + if (m_pchatter != NULL) { + m_pchatter->AddChat("", szT, true); + } + } + } + } +} + +void SimUIForm::SetObserving() +{ + gpplrLocal->SetObjective("< Observing >"); + gsim.GetLevel()->GetFogMap()->RevealAll(gpupdSim); + GetControlPtr(kidcFps)->Show(false); + GetControlPtr(kidcCountdown)->Show(false); + GetControlPtr(kidcChat)->Show(true); +} + +void SimUIForm::OnCheckWin(Pid pid) { + if (gpplrLocal == NULL || gptra == NULL) { + return; + } + +#if 0 +// Disable challenges for now. It needs to be tested against mission +// scripting, and it needs to be updated to handle allies. Because challenge +// is disabled, it means the server believes the first client that says it +// won. So some clever hacker can cause mayhem. + + // A client has claimed win. In order to be valid, the local player + // must be marked either winner (if == this pid) or a loser. + + bool fValid = true; + if (gpplrLocal->GetId() == pid) { + if ((gpplrLocal->GetFlags() & (kfPlrWinner | kfPlrLoser)) != + kfPlrWinner) { + fValid = false; + } + } else { + if ((gpplrLocal->GetFlags() & (kfPlrWinner | kfPlrLoser)) != + kfPlrLoser) { + fValid = false; + } + } + if (!fValid) { + ChallengeWinNetMessage cwnm; + gptra->SendNetMessage(&cwnm); + } +#endif + + // Assume this player won for purposes of chat system message + Player *pplr = gplrm.GetPlayerFromPid(pid); + if (pplr != NULL && m_pchatter != NULL) { + m_pchatter->AddChat(NULL, base::Format::ToString( + "%s has won this game.", pplr->GetName()), true); + } +} + +void SimUIForm::OnPlayerDisconnectNotify(Pid pid, int nReason) +{ + // We've been notified by the server of a player disconnecting. Display + // alert + + Player *pplr = gplrm.GetPlayerFromPid(pid); + if (pplr != NULL) { + pplr->SetFlags(pplr->GetFlags() | kfPlrComputer); + + // Set observer flag so that it is possible to identify the observing + // players later. Don't set it for the local player, since this + // flag is heavily overloaded and used to determine whether to show + // win/lose summaries and AskObserve form, at which point it will be + // set there. Clearly the win/lose paths need re-writing rather than + // patching. + if (pplr != gpplrLocal) { + pplr->SetFlags(pplr->GetFlags() | kfPlrObserver); + } + + char szT[128]; + char *pszReason = NULL; + char *pszChat = NULL; + switch (nReason) { + case knDisconnectReasonLeftGame: + if (!pplr->GetLeftGame()) { + pplr->SetLeftGame(); + pszReason = "left the game"; + pszChat = "."; + } + break; + + case knDisconnectReasonResign: + pszReason = "resigned"; + pszChat = ", and is now observing."; + break; + + case knDisconnectReasonKilled: + pszReason = "kicked by another player"; + pszChat = ", and left the game."; + break; + + case knDisconnectReasonAbnormal: + pszReason = "dropped out"; + pszChat = ", and left the game."; + break; + + case knDisconnectReasonNotResponding: + pszReason = "disconnected - not responding"; + pszChat = ", and left the game."; + break; + } + + if (pszReason != NULL) { + sprintf(szT, "%s %s", pplr->GetName(), pszReason); + ShowAlert(szT); + } + if (pszChat != NULL && m_pchatter != NULL) { + m_pchatter->AddChat(NULL, base::Format::ToString("%s%s", szT, + pszChat), true); + } + } +} + +void SimUIForm::OnLagNotify(Pid pidLagging, int cSeconds) +{ + // First thing's first, acknowledge this to the server, + // so the server knows this client is alive. + + LagAcknowledgeNetMessage lanm; + gptra->SendNetMessage(&lanm); + + // pidLagging is kpidNeutral if there are no laggards, + // which causes GetPlayerFromPid() to return NULL. + + Player *pplr = gplrm.GetPlayerFromPid(pidLagging); + if (pplr == NULL) { + ShowAlert(""); + if (m_pfrmLag != NULL) { + m_pfrmLag->Show(false); + delete m_pfrmLag; + m_pfrmLag = NULL; + } + return; + } + + // We've been notified by the server of this player lagging. Display alert + + ShowAlert(base::Format::ToString("%s lagging (%ds)", pplr->GetName(), + cSeconds)); + + // If cSeconds is not 0, hide the form. It only comes up at 0. + + if (cSeconds != 0) { + if (m_pfrmLag != NULL) { + m_pfrmLag->Show(false); + delete m_pfrmLag; + m_pfrmLag = NULL; + } + return; + } + + // cSeconds is 0, then show the lag form. First see if it is for a + // different player. If so, hide it first. + + if (m_pfrmLag != NULL) { + // If same pid is lagging, nothing to do; keep the form up + + if (m_pidLagging == pidLagging) { + return; + } + + // A different pid is lagging, yet the form is up. Delete the current + // one first. + + m_pfrmLag->Show(false); + delete m_pfrmLag; + m_pfrmLag = NULL; + } + + // Show the lagging form, modeless, so that it can be hidden and reshown + // at will. Check during update if in EndForm state, and handle + // appropriately. + + m_pidLagging = pidLagging; + m_pfrmLag = CreateHtMessageBox(kidfMessageBoxQuery, kfMbWhiteBorder, + "LAG ALERT", + base::Format::ToString( +"%s is communicating erratically and causing the game to lag. Disconnect %s?", + pplr->GetName(), pplr->GetName())); + m_pfrmLag->SetFlags(m_pfrmLag->GetFlags() | kfFrmDoModal); + gsndm.PlaySfx(ksfxGuiFormShow); + m_pfrmLag->Show(true); +} + +void SimUIForm::CheckLagForm() +{ + if (m_pfrmLag == NULL || (m_pfrmLag->GetFlags() & kfFrmDoModal) != 0) { + return; + } + + int nResult = m_pfrmLag->GetResult(); + m_pfrmLag->Show(false); + delete m_pfrmLag; + m_pfrmLag = NULL; + + // Send a KillLaggingPlayerNetMessage with the result + + KillLaggingPlayerNetMessage klpnm; + klpnm.pid = m_pidLagging; + klpnm.fYes = (nResult == kidcOk) ? 1 : 0; + gptra->SendNetMessage(&klpnm); +} + +void SimUIForm::Update() +{ +#ifdef STRESS + if (gfStress) { + if (gsim.GetTickCount() > gtStressTimeout) { + Event evt; + memset(&evt, 0, sizeof(evt)); + evt.eType = gameOverEvent; + evt.dw = knGoAbortLevel; + gevm.PostEvent(&evt); + } + } +#endif + + // Advance the move target while it's visible + + if (m_nStateMoveTarget == 1) { + m_aniMoveTarget.Advance(1); + if (m_aniMoveTarget.GetFlags() & kfAniDone) + m_nStateMoveTarget = 2; + } + + if (m_pfrmWaitingForAllPlayers != NULL) { + delete m_pfrmWaitingForAllPlayers; + m_pfrmWaitingForAllPlayers = NULL; + } + + // Have the InputUI update its displays (e.g., credits, power) + + InputUIForm *pfrmInputUI = ggame.GetInputUIForm(); + pfrmInputUI->Update(); + + // Update the objective display + + LabelControl *plbl = (LabelControl *)GetControlPtr(kidcObjective); + plbl->SetText(gpplrLocal->GetObjective()); + + // Calc stats + + long tCurrent = HostGetTickCount(); + if (s_tLastFPSUpdate == 0) + s_tLastFPSUpdate = tCurrent; + if (tCurrent - s_tLastFPSUpdate >= 100) { + fix32 fx1 = itofx32(gcPaint * 100); + fix32 fx2 = itofx32(tCurrent - s_tLastFPSUpdate); + if (fx2 == 0) + s_cfxFPS = 0; + else + s_cfxFPS = (fix)divfx32(fx1, fx2); + int cFpsWhole = fx32toi(s_cfxFPS); + int cFpsFraction = (int)fxtoi(mulfx(fracfx(s_cfxFPS), itofx(10))); + + if (gfShowFPS) { + char szFPS[80]; + sprintf(szFPS, "%d.%d", cFpsWhole, cFpsFraction); + ((LabelControl *)GetControlPtr(kidcFps))->SetText(szFPS); + } + + s_tLastFPSUpdate = tCurrent; + gcPaint = 0; + + // Set the # of pen events to accept per second. We don't need anything faster + // than the frame rate. + + static long s_tLastPaint; + if (s_tLastPaint == 0) + s_tLastPaint = tCurrent; + gevm.SetPenEventInterval((word)(tCurrent - s_tLastPaint)); + s_tLastPaint = tCurrent; + } + + // Let the minimap decide if it should invalidate. + + gpmm->Update(); + + // Everything has been updated for (at least) the first time + // so we're ready to display it. + + if (!s_fReadyForPaint) { + s_fReadyForPaint = true; + InvalidateRect(NULL); + } + + // Set redraw + + gevm.SetRedrawFlags(kfRedrawDirty); +} + +void AddPointToLassoSelection(WPoint wpt) +{ + // TUNE: + if (abs(s_awptSelection[s_cwptSelection - 1].wx - wpt.wx) >= WcFromTile16ths(4) || + abs(s_awptSelection[s_cwptSelection - 1].wy - wpt.wy) >= WcFromTile16ths(4)) { + s_awptSelection[s_cwptSelection] = wpt; + if (s_cwptSelection < (sizeof(s_awptSelection) / sizeof(WPoint)) - 1) + s_cwptSelection++; + } +} + +void SimUIForm::OnUpdateMapInvalidate(UpdateMap *pupd, Rect *prcOpaque) +{ +#ifdef DRAW_LINES + if (gfDrawLines) + pupd->InvalidateRect(); +#endif + +#ifdef DRAW_PATHS + if (gfDrawPaths) + pupd->InvalidateRect(); +#endif + +#if defined(DRAW_OCCUPIED_TILE_INDICATOR) || defined(MARK_TILE_BOUNDARIES) || defined(MARK_OCCUPIED_TILES) + pupd->InvalidateRect(); +#endif + + // Invalidate move target + + if (m_nStateMoveTarget != 0) { + pupd->InvalidateTile(TcFromWc(m_wxMoveTarget), TcFromWc(m_wyMoveTarget)); + if (m_nStateMoveTarget == 2) + m_nStateMoveTarget = 0; + } + + // Handle selection invalidation + + InvalidateDragSelection(); + + // Calculate the current opaquing rect for the map. + // HACK: Store in a global since various places need it, such as when a gob gets removed or when + // galaxite gets invalidated. + + Rect rcT; + bool fEmpty = prcOpaque->IsEmpty(); + if (fEmpty) { +// Probably not worth it +#if 0 + // Use the minimap since it covers the map + + Control *pctl = GetControlPtr(kidcMiniMap); + if (pctl->GetFlags() & kfCtlVisible) { + pctl->GetRect(&rcT); + fEmpty = false; + } +#endif + } else { + // Use the opaque rect passed + + rcT = *prcOpaque; + } + + // Map it map relative in tile coords + + if (!fEmpty) { + WCoord wxView, wyView; + gsim.GetViewPos(&wxView, &wyView); + Rect rcMapRelative; + rcMapRelative = rcT; + rcMapRelative.Offset(PcFromWc(wxView), PcFromWc(wyView)); + gtrcMapOpaque.left = TcFromPc(rcMapRelative.left + gcxTile - 1); + gtrcMapOpaque.top = TcFromPc(rcMapRelative.top + gcyTile - 1); + gtrcMapOpaque.right = TcFromPc(rcMapRelative.right); + gtrcMapOpaque.bottom = TcFromPc(rcMapRelative.bottom); + + // Expand the tile rect if the opaque rect is on the screen + // edge. This way gobs will get opaqued when on screen edge even + + Size sizDib; + m_pfrmm->GetDib()->GetSize(&sizDib); + if (rcT.left <= 0) + gtrcMapOpaque.left = 0; + if (rcT.top <= 0) + gtrcMapOpaque.top = 0; + if (rcT.right >= sizDib.cx) + gtrcMapOpaque.right = ktcMax; + if (rcT.bottom >= sizDib.cy) + gtrcMapOpaque.bottom = ktcMax; + + // Turn this opaque rect on by setting this global + + gptrcMapOpaque = >rcMapOpaque; + } else { + gptrcMapOpaque = NULL; + } + + // Get visible gobs + + Gob **ppgobVisible; + int cgobVisible; + gsim.FindVisibleGobs(&ppgobVisible, &cgobVisible); + + // Add known invalid gobs to the update map + + Gob **ppgobStop = &ppgobVisible[cgobVisible]; + Gob **ppgobT; + for (ppgobT = ppgobVisible; ppgobT < ppgobStop; ppgobT++) { + Gob *pgob = *ppgobT; + + // Invalidate if asked to redraw + + if (pgob->GetFlags() & kfGobRedraw) + pgob->Invalidate(); + } + + // Mark gobs not already marked for redraw that are touching invalid areas + // Add this "overflow" to the damage map; this is what is used to create + // a valid back buffer when scrolling + + bool fNewInvalid = false; + do { + fNewInvalid = false; + for (ppgobT = ppgobVisible; ppgobT < ppgobStop; ppgobT++) { + Gob *pgob = *ppgobT; + dword ff = pgob->GetFlags(); + if (!(ff & kfGobRedraw)) { + if (pupd->IsMapTileRectInvalidAndTrackDamage(&pgob->m_trcBoundingLast, &fNewInvalid)) + pgob->SetFlags(ff | kfGobRedraw); + } + } + } while (fNewInvalid); + + // Let the base invalidate controls as necessary + + Form::OnUpdateMapInvalidate(pupd, prcOpaque); +} + +void SimUIForm::InvalidateDragSelection() +{ + if (gfDragSelecting) { + WCoord wxView, wyView; + gsim.GetViewPos(&wxView, &wyView); + + if (gfLassoSelection) { + // The slow redraw this causes is unavoidable unless we remove lasso as an option. + + WPoint wptLast; + wptLast.wx = s_awptSelection[0].wx - wxView; + wptLast.wy = s_awptSelection[0].wy - wyView; + for (int i = 1; i < s_cwptSelection; i++) { + WPoint wptNext; + wptNext.wx = s_awptSelection[i].wx - wxView; + wptNext.wy = s_awptSelection[i].wy - wyView; + int x1 = PcFromWc(wptLast.wx); + int y1 = PcFromWc(wptLast.wy); + int x2 = PcFromWc(wptNext.wx); + int y2 = PcFromWc(wptNext.wy); + Rect rcT; + rcT.left = _min(x1, x2); + rcT.top = _min(y1, y2); + rcT.right = rcT.left + abs(x2 - x1) + 1; + rcT.bottom = rcT.top + abs(y2 - y1) + 1; + gpupdSim->InvalidateRect(&rcT); + wptLast = wptNext; + } + } else { + // PERF: there is a way to make selection drawing faster by only invalidating edges + // that move, and redrawing dirty cells only on the control layer. Can do if this isn't fast enough. + + int xLeft = PcFromWc(gwrcSelection.left - wxView); + int yTop = PcFromWc(gwrcSelection.top - wyView); + int xRight = PcFromWc(gwrcSelection.right - wxView); + int yBottom = PcFromWc(gwrcSelection.bottom - wyView); + + Rect rcT; + rcT.Set(xLeft, yTop, xRight, yTop + 1); + gpupdSim->InvalidateRect(&rcT); + rcT.Set(xRight - 1, yTop, xRight, yBottom); + gpupdSim->InvalidateRect(&rcT); + rcT.Set(xLeft, yBottom - 1, xRight, yBottom); + gpupdSim->InvalidateRect(&rcT); + rcT.Set(xLeft, yTop, xLeft + 1, yBottom); + gpupdSim->InvalidateRect(&rcT); + } + } +} + +void SimUIForm::ScrollInvalidate(UpdateMap *pupd) +{ + Form::ScrollInvalidate(pupd); + InvalidateDragSelection(); + gpmm->Invalidate(); +} + +void SimUIForm::OnPaintBackground(DibBitmap *pbm, UpdateMap *pupd) +{ + if (!s_fReadyForPaint) + return; + + gsim.DrawBackground(pupd, pbm); +} + +void SimUIForm::OnPaint(DibBitmap *pbm) +{ + // We're not ready to paint until the first Update has executed because + // Update initializes things like the Credits and Power controls. + + if (!s_fReadyForPaint) + return; + + // Draw everything managed by the Simulation (map, Gobs, Gob UI) + + gsim.Draw(m_pfrmm->GetUpdateMap(), pbm); + + // Draw the selection rectangle or lasso lines, if any + + if (gfDragSelecting) { + WCoord wxView, wyView; + gsim.GetViewPos(&wxView, &wyView); + + if (gfLassoSelection) { + Color clr = GetColor(kiclrWhite); + WPoint wptLast; + wptLast.wx = s_awptSelection[0].wx - wxView; + wptLast.wy = s_awptSelection[0].wy - wyView; + for (int i = 1; i < s_cwptSelection; i++) { + WPoint wptNext; + wptNext.wx = s_awptSelection[i].wx - wxView; + wptNext.wy = s_awptSelection[i].wy - wyView; + pbm->DrawLine(PcFromWc(wptLast.wx), PcFromWc(wptLast.wy), PcFromWc(wptNext.wx), PcFromWc(wptNext.wy), clr); + wptLast = wptNext; + } + } else { + Rect rcT; + rcT.left = PcFromWc(gwrcSelection.left - wxView); + rcT.top = PcFromWc(gwrcSelection.top - wyView); + rcT.right = PcFromWc(gwrcSelection.right - wxView); + rcT.bottom = PcFromWc(gwrcSelection.bottom - wyView); + DrawBorder(pbm, &rcT, 1, GetColor(kiclrWhite)); + } + } + + // Handle placement form + + gpfrmPlace->OnPaintSimUI(pbm); + + // Let the input handler draw what it needs + + m_ppenh->OnPaint(pbm); + + // Call the default Form handling + + Form::OnPaint(pbm); +} + +void SimUIForm::OnPaintControls(DibBitmap *pbm, UpdateMap *pupd) +{ + // We're not ready to paint until the first Update has executed because + // Update initializes things like the Credits and Power controls. + + if (!s_fReadyForPaint) + return; + + // Give some time to sound servicing + + HostSoundServiceProc(); + + // Draw fog + + gsim.DrawFog(pupd, pbm); + + // Draw move indicator + + if (m_nStateMoveTarget == 1) { + WCoord wxView, wyView; + gsim.GetViewPos(&wxView, &wyView); + m_aniMoveTarget.Draw(pbm, PcFromUwc(m_wxMoveTarget) - (PcFromWc(wxView) & 0xfffe), PcFromUwc(m_wyMoveTarget) - (PcFromWc(wyView) & 0xfffe)); + } + + // Give some time to sound servicing + + HostSoundServiceProc(); + + // Draw controls + + Form::OnPaintControls(pbm, pupd); + + // Handle Structure Placement controls here + + gpfrmPlace->OnPaintControlsSimUI(pbm, pupd); + + // Draw stats + +#ifdef STATS_DISPLAY + if (gfShowStats) { + Font *pfnt = gapfnt[kifntDefault]; + int cy = pfnt->GetHeight(); + int y = cy; + char szT[80]; + + // Display useful info + + // Count of active Gobs + + int cpgob = 0; + for (Gob *pgobT = ggobm.GetFirstGob(); pgobT != NULL; pgobT = ggobm.GetNextGob(pgobT), cpgob++); + sprintf(szT, "active Gobs: %d", cpgob); + pfnt->DrawText(pbm, szT, 1, y); + y += cy; + + // Count of visible sprites + + sprintf(szT, "bitmaps drawn: %d", gcBitmapsDrawn); + pfnt->DrawText(pbm, szT, 1, y); + y += cy; + + // Count of messages per update + + sprintf(szT, "msgs/update: %d", gcMessagesPerUpdate); + pfnt->DrawText(pbm, szT, 1, y); + y += cy; + + // Count of updates + + sprintf(szT, "update count: %ld", gsim.GetUpdateCount()); + pfnt->DrawText(pbm, szT, 1, y); + y += cy; + + // Invalidate + + Rect rcT = m_rc; + rcT.top = cy; + rcT.bottom = y; + InvalidateRect(&rcT); + } +#endif + +#ifdef STATS_DISPLAY + s_cUpdateRects += gpmfrmm->GetUpdateRectCount(); +#endif +} + +void SimUIForm::FrameComplete() +{ + Form::FrameComplete(); +} + +void SimUIForm::OnControlSelected(word idc) { + switch (idc) { + case kidcChat: + if (m_pchatter != NULL) { + m_pchatter->ShowChat(); + } + break; + } +} + +// SimUI handles input events which may effect the whole playfield and/or +// multiple Gobs. It does as much as it can independent of the Gobs but +// passes context-specific messages to the Gobs when specialized responses +// are desired. +// +// SimUI's OnPenEvent handler is responsible for +// +// - individual selection/multiple selection +// - input routing, i.e., cooking and passing pertinent events to the +// appropriate Gobs. For example, passing selected Gobs SetTarget and +// invoking Gob::PopupMenu. +// + +bool SimUIForm::OnPenEvent(Event *pevt) +{ + // Give the form controls first crack at handling the event. Form + // controls include: soft menu button, mode cancel button, status + // label) + + if (Form::OnPenEvent(pevt)) { + return true; + } + + // Debug gob identification + + if (pevt->eType == penHoverEvent) { +#if defined(WIN) && defined(DEBUG) + WCoord wxTarget, wyTarget; + gsim.GetViewPos(&wxTarget, &wyTarget); + TCoord tx = TcFromWc(wxTarget + WcFromPc(pevt->x)); + TCoord ty = TcFromWc(wyTarget + WcFromPc(pevt->y)); + for (Gid gid = ggobm.GetFirstGid(tx, ty); gid != kgidNull; + gid = ggobm.GetNextGid(gid)) { + Gob *pgob = ggobm.GetGob(gid); + if (pgob != NULL) { + char sz[512]; + pgob->ToString(sz); + Trace(sz); + } + } +#endif + return true; + } + + bool fScrollOnly = false; + if (gpfrmPlace != NULL && gpfrmPlace->IsBusy()) { + fScrollOnly = true; + } + if (gpplrLocal->GetFlags() & kfPlrObserver) { + fScrollOnly = true; + } + + return m_ppenh->OnPenEvent(pevt, fScrollOnly); +} + +/* +var ar = new Array(64) +for (r = 0; r < 64; r++) { + c = 0; + for (y = 50; y >= -50; y--) { + for (x = 50; x >= -50; x--) { + d = Math.sqrt(x * x + y * y); + if (d <= r) + c++; + } + } + + ar[r] = c; +} + +for (r = 0; r < 64; r++) { + WScript.Echo("Range " + r + " fits " + ar[r]); +} + +var str = ""; +n = 0; +for (r = 0; r < 64; r++) { + while (n <= ar[r]) { + if (n % 16 == 0) { + WScript.Echo(str); + str = ""; + } + str += r + ", "; + n++; + } +} +*/ + +// Map unit count to target range, i.e. what circle radius will fit a given count of units + +byte gmpcUnitsToRadius[] = +{ + 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, +}; + +int RadiusFromUnitCount(int cUnits) +{ + TCoord tcRadius; + if (cUnits >= ARRAYSIZE(gmpcUnitsToRadius)) { + tcRadius = gmpcUnitsToRadius[ARRAYSIZE(gmpcUnitsToRadius) - 1]; + } else { + tcRadius = gmpcUnitsToRadius[cUnits]; + } + return tcRadius; +} + +MobileUnitGob *SimUIForm::SetSelectionTargets(Gid gid, WCoord wxTarget, WCoord wyTarget) +{ + // Find a target that is free and close + + WPoint wptT; + FindNearestFreeTile(TcFromWc(wxTarget), TcFromWc(wyTarget), &wptT, 0); + wxTarget = wptT.wx; + wyTarget = wptT.wy; + + // Find targets for all MobileUnits. Targets are a "closely packed" version + // of the current selection, centered around the midpoint of the selection, + // placed at the target. + + // First figure out which units are mobile and attempt to retain their formation + + WCoord wxMin = kwcMax; + WCoord wxMax = 0; + WCoord wyMin = kwcMax; + WCoord wyMax = 0; + + WCoord wcMoveDistPerUpdate = (WCoord)kwcMax; + int cgobsSel = 0; + Gob *pgobT; + for (pgobT = ggobm.GetFirstGob(); pgobT != NULL; pgobT = ggobm.GetNextGob(pgobT)) { + if ((pgobT->GetFlags() & (kfGobSelected | kfGobMobileUnit)) == (kfGobSelected | kfGobMobileUnit) && + ((gfGodMode && !ggame.IsMultiplayer()) || pgobT->GetSide() == gpplrLocal->GetSide())) { + + // Get selection dimensions + + WPoint wpt; + pgobT->GetCenter(&wpt); + if (wpt.wx < wxMin) + wxMin = wpt.wx; + if (wpt.wx > wxMax) + wxMax = wpt.wx; + if (wpt.wy < wyMin) + wyMin = wpt.wy; + if (wpt.wy > wyMax) + wyMax = wpt.wy; + cgobsSel++; + + // Get min speed per update + + if (pgobT->GetFlags() & kfGobMobileUnit) { + MobileUnitGob *pmunt = (MobileUnitGob *)pgobT; + MobileUnitConsts *pmuntc = (MobileUnitConsts *)pmunt->GetConsts(); + if (pmuntc->GetMoveDistPerUpdate() < wcMoveDistPerUpdate) + wcMoveDistPerUpdate = pmuntc->GetMoveDistPerUpdate(); + } + } + } + WCoord cwxSel = wxMax - wxMin; + if (cwxSel <= 0) + cwxSel = 1; + WCoord cwySel = wyMax - wyMin; + if (cwySel <= 0) + cwySel = 1; + + // The dest scaling rect is different if moving vs. attacking. + + int ctxSel = TcFromWc(wxMax) - TcFromWc(wxMin) + 1; + int ctySel = TcFromWc(wyMax) - TcFromWc(wyMin) + 1; + int ctxTarg; + int ctyTarg; + if (gid == kgidNull) { + // Pick a destination scaling rect for movement + + ctxTarg = ctxSel; + ctyTarg = ctySel; + + // Pack the rect if it is <= 50% full + + if (cgobsSel <= (ctxSel * ctySel + 1) / 2) { + int ctxT = ctxSel; + int ctyT = ctySel; + while (true) { + // Remember last + + ctxTarg = ctxT; + ctyTarg = ctyT; + + // Try smaller size, decrease whichever size is greater + + if (ctxT > ctyT) { + ctxT--; + } else { + ctyT--; + } + + if (ctxT * ctyT < cgobsSel + cgobsSel / 3) + break; + } + } + + } else { + // Pick a destination scaling rect for attacking + + Gob *pgob = ggobm.GetGob(gid); + ctxTarg = 1; + ctyTarg = 1; + if (pgob != NULL) { + TRect trc; + pgob->GetTileRect(&trc); + ctxTarg = trc.right - trc.left; + ctyTarg = trc.bottom - trc.top; + } + + // Make the outside edge away from the gob; this hardwires some accounting + // for some firing distance, gives a larger edge for gobs to move to + + ctxTarg += 2; + ctyTarg += 2; + } + + WCoord cwxTarg = WcFromTc(ctxTarg - 1); + if (cwxTarg == 0) + cwxTarg = 1; + WCoord cwyTarg = WcFromTc(ctyTarg - 1); + if (cwyTarg == 0) + cwyTarg = 1; + + // Figure out the range of this target + + TCoord tcTargetRadius = RadiusFromUnitCount(cgobsSel); + + // Find src center + + WCoord wxCenterSrc = wxMin + (wxMax - wxMin) / 2; + WCoord wyCenterSrc = wyMin + (wyMax - wyMin) / 2; + + // Find map size + + Size sizMapTiles; + gsim.GetLevel()->GetTileMap()->GetTCoordMapSize(&sizMapTiles); + WCoord cwxMap = WcFromTc(sizMapTiles.cx); + WCoord cwyMap = WcFromTc(sizMapTiles.cy); + + // Move & keep formation, or target destination if not a mobile unit + + Gob *pgobTarget = ggobm.GetGob(gid); + + MobileUnitGob *pmuntSetTarget = NULL; + for (pgobT = ggobm.GetFirstGob(); pgobT != NULL; pgobT = ggobm.GetNextGob(pgobT)) { + dword ff = pgobT->GetFlags(); + if ((ff & kfGobSelected) && + ((gfGodMode && !ggame.IsMultiplayer()) || pgobT->GetSide() == gpplrLocal->GetSide() || (pgobTarget != NULL && pgobTarget->GetType() == kgtReplicator))) { + + // If it's a mobile gob, give it a move command to a scaled coordinate + + if (ff & kfGobMobileUnit) { + // Calc dst + + MobileUnitGob *pmunt = (MobileUnitGob *)pgobT; + WPoint wptSrc; + pmunt->GetCenter(&wptSrc); + WCoord wxDst; + if (wptSrc.wx - wxCenterSrc >= 0) { + wxDst = (WCoord)(((long)(wptSrc.wx - wxCenterSrc) * cwxTarg * 8 + (cwxSel * 4)) / (cwxSel * 8) + wxTarget); + } else { + wxDst = (WCoord)(((long)(wptSrc.wx - wxCenterSrc) * cwxTarg * 8 - (cwxSel * 4)) / (cwxSel * 8) + wxTarget); + } + if (wxDst < 0) + wxDst = 0; + if (wxDst >= cwxMap) + wxDst = cwxMap - 1; + WCoord wyDst; + if (wptSrc.wy - wyCenterSrc >= 0) { + wyDst = (WCoord)(((long)(wptSrc.wy - wyCenterSrc) * cwyTarg * 8 + (cwySel * 4)) / (cwySel * 8) + wyTarget); + } else { + wyDst = (WCoord)(((long)(wptSrc.wy - wyCenterSrc) * cwyTarg * 8 - (cwySel * 4)) / (cwySel * 8) + wyTarget); + } + if (wyDst < 0) + wyDst = 0; + if (wyDst >= cwyMap) + wyDst = cwyMap - 1; + if (gfGodMode && !ggame.IsMultiplayer()) + pmunt->ClearAction(); + + // Need to clean up the targets. + // 1. Targets can be either in open terrain or on the unit being targetted, + // not on other structures (other munts is ok) + // 2. For any targets on the munt being targetted, if it's a structure it needs + // to be checked for accessibility + // 3. Adjust the target radius to be on the same side of any blockage + + // Clean up dest if attacking + + if (pgobTarget != NULL) { + // Find the closest tile to this that is unblocked by terrain + // Ok if on a structure... which structure is important, that is checked next + + WPoint wptT; + FindNearestFreeTile(TcFromWc(wxDst), TcFromWc(wyDst), &wptT, 0); + wxDst = wptT.wx; + wyDst = wptT.wy; + + // Calc these now as wxDst, wyDst may have changed + + TCoord txT = TcFromWc(wxDst); + TCoord tyT = TcFromWc(wyDst); + + // If dest is on top of a structure make sure that is the target and dest + // is accessible + + Assert(pgobTarget->GetFlags() & kfGobUnit); + UnitGob *puntTarget = (UnitGob *)pgobTarget; + Gob *pgobT = ggobm.GetShadowGob(txT, tyT); + if (pgobT != NULL) { + // The destination is on top of a structure. Need to + // check if this is ok If it isn't ok, get a new point + // that is accessible. If it is ok, make sure the + // position is accessible. + + Assert(pgobT->GetFlags() & kfGobStructure); + StructGob *pstruT = (StructGob *)pgobT; + if (pgobT != pgobTarget) { + // Targetting something but wxDst, wyDst on a + // structure we're not targetting. Get a valid + // attack point from the real target + + WPoint wptT; + puntTarget->GetAttackPoint(&wptT); + wxDst = wptT.wx; + wyDst = wptT.wy; + } else { + // wxDst, wyDst is on top of the structure we are + // targetting which is fine. Get an accessible + // position if the one we have isn't accessible + + if (!pstruT->IsAccessible(txT, tyT)) { + WPoint wptT; + puntTarget->GetAttackPoint(&wptT); + wxDst = wptT.wx; + wyDst = wptT.wy; + } + } + } + } else { + // Not attacking, just moving. + // Find a clear terrain spot that isn't terrain blocked and + // not on a structure + + WPoint wptT; + FindNearestFreeTile(TcFromWc(wxDst), TcFromWc(wyDst), &wptT, kbfStructure); + wxDst = wptT.wx; + wyDst = wptT.wy; + } + + // Adjust the target radius so we end up on the same side of + // any blockage + + TCoord tcTargetRadiusT = tcTargetRadius; + TerrainMap *ptrmap = gsim.GetLevel()->GetTerrainMap(); + TCoord txTo = TcFromWc(wxTarget); + TCoord tyTo = TcFromWc(wyTarget); + TCoord txFrom = TcFromWc(wxDst); + TCoord tyFrom = TcFromWc(wyDst); + TCoord txFree, tyFree; + if (ptrmap->FindLastUnoccupied(txTo, tyTo, txFrom, tyFrom, &txFree, &tyFree)) { + if (txFree != txFrom || tyFree != tyFrom) { + wxDst = WcFromTc(txFree) + kwcTileHalf; + wyDst = WcFromTc(tyFree) + kwcTileHalf; + int dtx = abs(txFree - txTo); + if (dtx < ARRAYSIZE(gmpDistFromDxy)) { + int dty = abs(tyFree - tyTo); + if (dty < ARRAYSIZE(gmpDistFromDxy)) { + tcTargetRadiusT = gmpDistFromDxy[dtx][dty]; + } + } + } + } + + // Send command + + pmunt->SetTarget(gid, wxDst, wyDst, wxTarget, wyTarget, tcTargetRadiusT, wcMoveDistPerUpdate); + pmuntSetTarget = pmunt; + } + + // Else give it a set target command with the original target + // coordinate + + else if (ff & kfGobUnit) { + UnitGob *punt = (UnitGob *)pgobT; + punt->SetTarget(gid, wxTarget, wyTarget, 0, wcMoveDistPerUpdate); + } + } + } + + return pmuntSetTarget; +} + +void SimUIForm::ClearSelection() +{ + s_wptSelect1.wx = 0; + s_wptSelect1.wy = 0; + s_wptSelect2 = s_wptSelect1; + s_cwptSelection = 0; + gwrcSelection.Set(s_wptSelect1, s_wptSelect2); + gsim.ClearGobSelection(); +} + +Gob *SimUIForm::HitTestGob(int x, int y, bool fFinger, WCoord *pwx, + WCoord *pwy, bool *pfHitSurrounding) +{ + WCoord wxTarget, wyTarget; + gsim.GetViewPos(&wxTarget, &wyTarget); + wxTarget += WcFromPc(x); + wyTarget += WcFromPc(y); + + // Pin to edges. Gobs created off the map will cause corruption + + TCoord ctx, cty; + ggobm.GetMapSize(&ctx, &cty); + if (TcFromWc(wxTarget) >= ctx) + wxTarget = WcFromTc(ctx - 1); + if (TcFromWc(wyTarget) >= cty) + wyTarget = WcFromTc(cty - 1); + + *pwx = wxTarget; + *pwy = wyTarget; + + if (fFinger) { + return gsim.FingerHitTest(wxTarget, wyTarget, kfGobActive | kfGobUnit, + pfHitSurrounding); + } else { + Gob *pgobHit = NULL; + Enum enm; + gsim.HitTest(&enm, wxTarget, wyTarget, kfGobActive | kfGobUnit, + &pgobHit); + *pfHitSurrounding = false; + return pgobHit; + } +} + +void SimUIForm::MoveOrAttackOrSelect(int x, int y, dword ff) +{ + WCoord wxTarget, wyTarget; + bool fHitSurrounding; + Gob *pgobHit = HitTestGob(x, y, false, &wxTarget, &wyTarget, + &fHitSurrounding); + MoveOrAttackOrSelect(pgobHit, wxTarget, wyTarget, ff); +} + +// This routine is called in two situations. +// 1. On pen down to select the unit tapped on (fSelectOnly == true) +// 2. On pen up to attack the unit tapped on or to move to the location tapped on (fSelectOnly == false) + +const int kctDoubleTapDelay = 100; // TUNE: + +void SimUIForm::MoveOrAttackOrSelect(Gob *pgobHit, WCoord wxTarget, + WCoord wyTarget, dword ff) +{ + // TODO: break up this giant routine + + if (pgobHit == NULL) { + if (ff & kfMasMove) { + MobileUnitGob *pmunt = SetSelectionTargets(kgidNull, wxTarget, wyTarget); + if (pmunt != NULL) { + // BUGBUG: fix this multiplayer problem (gob lists out of sync) +#if 0 + if (!ggame.IsMultiplayer()) { + WCoord wxT = WcTrunc(wxTarget) + kwcTileHalf; + WCoord wyT = WcTrunc(wyTarget) + kwcTileHalf; + CreateAnimGob(wxT, wyT, kfAnmDeleteWhenDone | kfAnmSmokeFireLayer, NULL, g_panidMoveTarget); + } +#else + // Single and multi-player friendly move target + + if (m_nStateMoveTarget != 0) + m_pfrmm->GetUpdateMap()->InvalidateTile(TcFromWc(m_wxMoveTarget), TcFromWc(m_wyMoveTarget)); + m_aniMoveTarget.Start(0, 0, 0); + m_wxMoveTarget = WcTrunc(wxTarget) + kwcTileHalf; + m_wyMoveTarget = WcTrunc(wyTarget) + kwcTileHalf; + m_nStateMoveTarget = 1; +#endif + gsndm.PlaySfx(SfxFromCategory(((MobileUnitConsts *)gapuntc[pmunt->GetUnitType()])->sfxcMove)); + } + +#ifdef RALLY_POINTS + // TOTAL HACK: + // If the player has HRC or VTS selected and they've tapped the + // ground set that point as the structure's rally point. Of course + // this should be visualized, etc. Ah, if only we had time for such + // things. + + for (Gob *pgobT = ggobm.GetFirstGob(); pgobT != NULL; pgobT = ggobm.GetNextGob(pgobT)) { + if ((pgobT->GetFlags() & (kfGobStructure | kfGobSelected)) != (kfGobStructure | kfGobSelected)) + continue; + BuilderGob *pbldr = (BuilderGob *)pgobT; + + // We know that if an enemy structure is selected then a local player's + // structure can't also be selected. + + if (pgobT->GetOwner() != gpplrLocal) + break; + + // VTS or HRC? + + if (!((1UL << pbldr->GetUnitType()) & (kumVehicleTransportStation | kumHumanResourceCenter))) + break; + + pbldr->SetRallyPoint(TcFromWc(wxTarget), TcFromWc(wyTarget)); + WCoord wxT = WcTrunc(wxTarget) + kwcTileHalf; + WCoord wyT = WcTrunc(wyTarget) + kwcTileHalf; + CreateAnimGob(wxT, wyT, kfAnmDeleteWhenDone | kfAnmSmokeFireLayer, NULL, g_panidMoveTarget); + ShowAlert("Rally point set"); + break; + } +#endif + } + return; + } + + // Replicator is a special case. When it is specified as a target the + // selected units are directed to move to its input. + + if (pgobHit->GetType() == kgtReplicator) { + if ((ff & (kfMasMove | kfMasAttack)) == 0) { + return; + } + + MobileUnitGob *pmunt = SetSelectionTargets(pgobHit->GetId(), wxTarget, wyTarget); + if (pmunt != NULL) + gsndm.PlaySfx(SfxFromCategory(((MobileUnitConsts *)gapuntc[pmunt->GetUnitType()])->sfxcMove)); + return; + } + + + // God mode is allowed only if gfGodMode is on when playing single player + + bool fGodMode = false; + if (gfGodMode && !ggame.IsMultiplayer()) + fGodMode = true; + + // Selection? + + if (IsSelectionCommand(pgobHit)) { + static long s_tLastSelected = 0; + static Gob *s_pgobLastSelected = NULL; + + if (ff & kfMasSelect) { + bool fAlreadySelected = (pgobHit->GetFlags() & kfGobSelected) != 0; + + // Selection. Cancel selection for units elsewhere, set selection + // for this unit + + // If the unit is already selected and the last selection occured + // in within the double-tap window then select all units of the + // same type on-screen. UNDONE: this may not be the best place to + // do this. + + if (fAlreadySelected && HostGetTickCount() - s_tLastSelected < kctDoubleTapDelay && s_pgobLastSelected == pgobHit) { + SelectSameUnitTypes(pgobHit, false); + } else { + gsim.SetGobSelected(pgobHit); + } + s_tLastSelected = HostGetTickCount(); + s_pgobLastSelected = pgobHit; + + // Play sfx + + if (!gfGodMode) { + if (pgobHit->GetFlags() & kfGobStructure) { + gsndm.PlaySfx(((StructConsts *)gapuntc[((StructGob *)pgobHit)->GetUnitType()])->sfxSelect); + } else { + if (gfGodMode || pgobHit->GetSide() == gpplrLocal->GetSide()) + gsndm.PlaySfx(SfxFromCategory(((MobileUnitConsts *)gapuntc[((UnitGob *)pgobHit)->GetUnitType()])->sfxcSelect)); + } + } + } + + if (ff & kfMasShowMenu) { + if (pgobHit->GetFlags() & kfGobStructure) { + ShowUnitMenu(pgobHit); + } + } + } else if (ff & kfMasAttack) { + // An attack. Reset the wxTarget, wyTarget to be the middle of the gob + // and issue targets + + UnitGob *puntHit = (UnitGob *)pgobHit; + WPoint wptTarget; + puntHit->GetAttackPoint(&wptTarget); + wxTarget = wptTarget.wx; + wyTarget = wptTarget.wy; + MobileUnitGob *pmunt = SetSelectionTargets(puntHit->GetId(), wxTarget, wyTarget); + if (pmunt != NULL) + gsndm.PlaySfx(SfxFromCategory(((MobileUnitConsts *)gapuntc[((UnitGob *)pmunt)->GetUnitType()])->sfxcAttack)); + } +} + +bool SimUIForm::IsSelectionCommand(Gob *pgobHit) +{ + // God mode is allowed only if gfGodMode is on when playing single player + + bool fGodMode = false; + if (gfGodMode && !ggame.IsMultiplayer()) + fGodMode = true; + + // Figure out if this is a selection or an attack command: + // + // Player can select same side units + // Player can select enemy units if no friendly units are selected + // Player can attack enemy units + // GodMode can select any unit if that doesn't imply an attack (from + // either side) + // GodMode can attack any side if there are selected enemy units + + // If there are any selected units that can attack pgobHit, it may be an + // attack. + + bool fOwnSelection = false; + bool fSelection = true; + Side sideHit = pgobHit->GetSide(); + for (Gob *pgobT = ggobm.GetFirstGob(); pgobT != NULL; pgobT = ggobm.GetNextGob(pgobT)) { + if ((pgobT->GetFlags() & (kfGobUnit | kfGobSelected)) != (kfGobUnit | kfGobSelected)) + continue; + UnitGob *puntT = (UnitGob *)pgobT; + if (puntT->GetOwner() == gpplrLocal) + fOwnSelection = true; + if (puntT->IsValidTarget(pgobHit)) { + fSelection = false; + break; + } + } + + // If not in god mode and the local player does not own any of the already + // selected units then just select the new unit + + if ((!fGodMode) && (!fOwnSelection)) { + fSelection = true; + } + + return fSelection; +} + +bool SimUIForm::HasSelectedUnits() +{ + Gob *pgobT = ggobm.GetFirstGob(); + for (; pgobT != NULL; pgobT = ggobm.GetNextGob(pgobT)) { + if ((pgobT->GetFlags() & (kfGobUnit | kfGobSelected | kfGobStructure)) + != (kfGobUnit | kfGobSelected)) { + continue; + } + if (((UnitGob *)pgobT)->GetOwner() == gpplrLocal) { + return true; + } + } + return false; +} + +void SimUIForm::ShowUnitMenu(Gob *pgob) { + if (pgob != NULL && (pgob->GetFlags() & kfGobUnit) != 0) { + // Don't popup menu if the player doesn't own the Gob (unless in + // God mode). God mode is limited for multiplayer games. + if ((!gfGodMode || ggame.IsMultiplayer()) && + pgob->GetSide() != gpplrLocal->GetSide()) { + return; + } + pgob->PopupMenu(); + } +} + +void SimUIForm::SelectSameUnitTypes(Gob *pgob, bool fSfx) +{ + if ((pgob->GetFlags() & (kfGobUnit | kfGobStructure)) != kfGobUnit) { + return; + } + + WCoord wxViewT, wyViewT; + gsim.GetViewPos(&wxViewT, &wyViewT); + TCoord txView = TcFromWc(wxViewT); + TCoord tyView = TcFromWc(wyViewT); + + Size sizT; + ggame.GetPlayfieldSize(&sizT); + TCoord txRight = TcFromWc(wxViewT + WcFromPc(sizT.cx + gcxTile - 1)); + TCoord tyBottom = TcFromWc(wyViewT + WcFromPc(sizT.cy + gcyTile - 1)); + + TRect trc; + trc.Set(txView, tyView, txRight, tyBottom); + gsim.SelectSameUnitTypes((UnitGob *)pgob, &trc); + + if (gfGodMode || pgob->GetSide() == gpplrLocal->GetSide()) { + gsndm.PlaySfx(SfxFromCategory(((MobileUnitConsts *) + gapuntc[((UnitGob *)pgob)->GetUnitType()])->sfxcSelect)); + } +} + +void SimUIForm::CalcLevelSpecificConstants() +{ +} + +// OPT: this isn't hideous but could be made faster + +bool PtInPolygon(WPoint *awpt, int cwpt, WCoord wx, WCoord wy) +{ + bool fInside = false; + WPoint *pwptI = &awpt[0]; + WPoint *pwptJ = &awpt[cwpt - 1]; + WPoint *pwptEnd = pwptJ + 1; + + for (int i = 0; pwptI < pwptEnd; pwptI++) { + if ((((pwptI->wy <= wy) && (wy < pwptJ->wy)) || ((pwptJ->wy <= wy) && (wy < pwptI->wy))) && + (wx < (pwptJ->wx - pwptI->wx) * (wy - pwptI->wy) / (pwptJ->wy - pwptI->wy) + pwptI->wx)) + fInside = !fInside; + pwptJ = pwptI; + } + + return fInside; +} + +// +// MiniMapControl +// + +MiniMapControl *gpmm; + +MiniMapControl::MiniMapControl() +{ + m_pbm = NULL; + m_cxyBorder = 0; + m_wfMm = 0; + m_xOff = 0; + m_yOff = 0; + m_tInvalidateLast = 0; +} + +MiniMapControl::~MiniMapControl() +{ + gpmm = NULL; + delete m_pbm; +} + +int MiniMapControl::CalcWidth() +{ + // Figure out scale based on playfield size + + Size siz; + ggame.GetPlayfieldSize(&siz); + int nScale = 1; + if (siz.cx >= 240) + nScale = 2; + + // Width + + Size sizMap; + gsim.GetLevel()->GetTileMap()->GetMapSize(&sizMap); + return sizMap.cx / (gcxTile / nScale) + 1; +} + +bool MiniMapControl::Init(Form *pfrm, IniReader *pini, FindProp *pfind) +{ + // Base initialization + + if (!Control::Init(pfrm, pini, pfind)) + return false; + + // idc (x y cx cy) border + + char szBorder[32]; + int cArgs = pini->GetPropertyValue(pfind, "%*d (%*d %*d %*d %*d) %s", szBorder); + + // Remember the size of the input UI area since our border drawing needs it + + Size sizDib; + ggame.GetInputUIForm()->GetFormMgr()->GetDib()->GetSize(&sizDib); + m_cyInputUI = sizDib.cy; + + // Our even aligned blts depend on having a border for odd width maps + + m_cxyBorder = 1; + + // Figure out scale based on playfield size + + Size siz; + ggame.GetPlayfieldSize(&siz); + m_nScale = 1; + if (siz.cx >= 240 && siz.cx < 320) { + if (siz.cy > siz.cx) + m_nScale = 2; + } + if (siz.cx >= 320) + m_nScale = 2; + gsim.SetMiniMapScale(m_nScale); + + // Size + + Size sizMap; + gsim.GetLevel()->GetTileMap()->GetMapSize(&sizMap); + m_rc.left = m_rc.right - ((sizMap.cx / (gcxTile / m_nScale)) + m_cxyBorder); + m_rc.bottom = m_rc.top + (sizMap.cy / (gcyTile / m_nScale)) + m_cxyBorder; + + // The map is aligned to an edge. The edge is even, and the dib starts on + // an even address, so we want the width to be even so our blts are aligned. + // If the map is odd, draw the border inside the dib to make it even. If the + // map is even, draw the border outside the dib. + + int cx = m_rc.Width(); + if (cx & 1) { + m_wfMm &= ~kfMmVertBorderInside; + cx -= m_cxyBorder; + m_xOff = 0; + m_yOff = m_cxyBorder; + } else { + m_wfMm |= kfMmVertBorderInside; + m_xOff = m_cxyBorder; + m_yOff = m_cxyBorder; + } + + // Alloc a dib to hold it + + m_pbm = CreateDibBitmap(NULL, cx, m_rc.Height()); + if (m_pbm == NULL) + return false; + + // Draw internal borders + + int x = 0; + int y = 0; + if (m_cxyBorder != 0) { + int iclr = gfMultiplayer ? gaiclrSide[gpplrLocal->GetSide()] : kiclr0CyanSide; + Color clrBorder = GetColor(iclr); + if (m_wfMm & kfMmVertBorderInside) { + m_pbm->Fill(x + m_cxyBorder, y, m_rc.Width() - m_cxyBorder, m_cxyBorder, clrBorder); + m_pbm->Fill(x, y, m_cxyBorder, m_rc.Height() - m_cyInputUI + 1, clrBorder); + m_pbm->Fill(x, y + m_rc.Height() - m_cyInputUI + 1, m_cxyBorder, m_rc.Height(), GetColor(kiclrBlack)); + } else { + m_pbm->Fill(x, y, m_rc.Width(), m_cxyBorder, clrBorder); + } + } + + // Stuff constants + + TileMap *ptmap = gsim.GetLevel()->GetTileMap(); + MiniTileSetHeader *pmtseth = ptmap->GetMiniTileSetHeader(m_nScale); + m_pbTileData = (byte *)(pmtseth + 1); + m_pwTileMap = ptmap->m_pwMapData; + m_pbFogMap = gsim.GetLevel()->GetFogMap()->GetMapPtr(); + ggobm.GetMapSize(&m_ctx, &m_cty); + m_cbRowBytes = m_pbm->GetRowBytes(); + m_clrBlack = (byte)GetColor(kiclrBlack); + m_clrWhite = (byte)GetColor(kiclrWhite); + m_clrGalaxite = (byte)GetColor(kiclrGalaxite); + for (Side sideT = ksideNeutral; sideT < kcSides; sideT++) + m_aclrSide[sideT] = (byte)GetSideColor(sideT); + + // Calc powered radar flag + + if (!CalcPoweredRadar()) + Redraw(); + + // Remember this object globally + + gpmm = this; + return true; +} + +void MiniMapControl::OnPaint(DibBitmap *pbm) +{ + Rect rcForm; + m_pfrm->GetRect(&rcForm); + int x = m_rc.left + rcForm.left; + int y = m_rc.top + rcForm.top; + + // Lay down bits + + Size siz; + m_pbm->GetSize(&siz); + Rect rcSrc; + rcSrc.Set(0, 0, siz.cx, siz.cy); + + if (m_wfMm & kfMmVertBorderInside) { + pbm->Blt(m_pbm, &rcSrc, x, y); + } else { + pbm->Blt(m_pbm, &rcSrc, x + m_cxyBorder, y); + int iclr = gfMultiplayer ? gaiclrSide[gpplrLocal->GetSide()] : kiclr0CyanSide; + pbm->Fill(x, y, m_cxyBorder, m_rc.Height() - m_cyInputUI + 1, GetColor(iclr)); + pbm->Fill(x, y + m_rc.Height() - m_cyInputUI + 1, m_cxyBorder, m_rc.Height(), GetColor(kiclrBlack)); + } + + // Draw box showing the full-screen visible area of the map + + WCoord wxViewT, wyViewT; + gsim.GetViewPos(&wxViewT, &wyViewT); + short xView = PcFromUwc(wxViewT) & 0xfffe; + short yView = PcFromUwc(wyViewT) & 0xfffe; + WCoord wxView = WcFromUpc(xView); + WCoord wyView = WcFromUpc(yView); + + int cxTile = gcxTile / m_nScale; + int cyTile = gcyTile / m_nScale; + + Rect rc; + rc.left = x + m_cxyBorder + PcFromUwc(wxView) / cxTile; + rc.top = y + m_cxyBorder + PcFromUwc(wyView) / cyTile; + ggame.GetPlayfieldSize(&siz); + rc.right = rc.left + (siz.cx + cxTile - 1) / cxTile; + rc.bottom = rc.top + (siz.cy + cyTile - 1) / cyTile; + DrawBorder(pbm, &rc, 1, GetColor(kiclrWhite)); +} + +int MiniMapControl::OnHitTest(Event *pevt) +{ + // Don't use finger hit testing on the minimap control; this way + // all input over the map goes to the map. + + if (pevt->ff & kfEvtFinger) { + pevt->ff = ~kfEvtFinger; + int result = Control::OnHitTest(pevt); + pevt->ff |= kfEvtFinger; + return result; + } + return Control::OnHitTest(pevt); +} + +//TUNE: +#define kctMiniMapInvalidateRate 25 + +void MiniMapControl::Update() +{ + // Gets called at Update time. If the minimap is dirty and it's been at + // least kctMiniMapUpdateRate ticks since the last invalidate, invalidate + // it now. This gives us immediate invalidates in many cases, but ensures + // there is at least kctMiniMapUpdateRate ticks between invalidates. + + long tCurrent = gtimm.GetTickCount(); + if (m_wfMm & kfMmRedraw) { + if (m_tInvalidateLast == 0 || tCurrent - m_tInvalidateLast >= kctMiniMapInvalidateRate) { + m_tInvalidateLast = tCurrent; + m_wfMm &= ~kfMmRedraw; + Invalidate(); + } + } + +// Enough time to give the second finger time to come down, and not long +// enough to make the minimap jerky + +#define kctPenDownTimeout 25 + + if (m_wfMm & kfMmPenDownTimeout) { + if (tCurrent - m_tPenDown >= kctPenDownTimeout) { + m_wfMm &= ~kfMmPenDownTimeout; + OnPenEvent2(&m_evtPenDown); + } + } +} + +void MiniMapControl::OnBreakCapture() +{ + // Forget about the pen down, if there is one + + m_wfMm &= ~kfMmPenDownTimeout; + Trace("Breaking capture"); +} + +void MiniMapControl::Redraw() +{ + TRect trc; + trc.Set(0, 0, m_ctx, m_cty); + RedrawTRect(&trc); +} + +void MiniMapControl::OnPenEvent(Event *pevt) +{ + // Process the pen down in waiting if there is a pen up + + if (pevt->eType == penUpEvent) { + if (m_wfMm & kfMmPenDownTimeout) { + m_wfMm &= ~kfMmPenDownTimeout; + OnPenEvent2(&m_evtPenDown); + } + } + + if (pevt->eType != penDownEvent && pevt->eType != penMoveEvent) { + return; + } + +#ifdef IPHONE + // If already waiting for pen down timeout, then just update the x,y + + if (m_wfMm & kfMmPenDownTimeout) { + m_evtPenDown.x = pevt->x; + m_evtPenDown.y = pevt->y; + return; + } + + // If pen down event, remember it and don't process it for a little bit. + // This gives enough time to forget about the pen down if the second + // finger goes down and the control is forced to give up capture. + + if (pevt->eType == penDownEvent) { + m_tPenDown = gtimm.GetTickCount(); + m_evtPenDown = *pevt; + m_wfMm |= kfMmPenDownTimeout; + return; + } +#endif + + OnPenEvent2(pevt); +} + +void MiniMapControl::OnPenEvent2(Event *pevt) +{ + static int s_xPenDown; + static int s_yPenDown; + static WCoord s_wxViewDown; + static WCoord s_wyViewDown; + + Rect rcForm; + m_pfrm->GetRect(&rcForm); + + int xPen = pevt->x - (rcForm.left + m_rc.left + m_cxyBorder); + int yPen = pevt->y - (rcForm.top + m_rc.top + m_cxyBorder); + + WCoord wxView, wyView; + gsim.GetViewPos(&wxView, &wyView); + + Size sizPlayfield; + ggame.GetPlayfieldSize(&sizPlayfield); + + if (pevt->eType == penDownEvent) { + // Calc the rectangle (in minimap pixel coordinates) bounding the map + // area currently being viewed. + + Rect rcView; + rcView.left = TcFromWc(wxView) * m_nScale; + rcView.top = TcFromWc(wyView) * m_nScale; + rcView.right = rcView.left + (((sizPlayfield.cx + gcxTile - 1) / gcxTile) * m_nScale); + rcView.bottom = rcView.top + (((sizPlayfield.cy + gcyTile - 1) / gcyTile) * m_nScale); + +#if 0 +// This feels super sloppy, plus it isn't neeed for fine movement anymore, +// since the map scrolls nicely with a finger directly + // If this is finger UI, make the rect larger + + if (pevt->ff & kfEvtFinger) { + int cxInflate = (gcxTile * 2 - rcView.Width()) / 2; + if (cxInflate < 0) { + cxInflate = 0; + } + int cyInflate = (gcyTile * 2 - rcView.Height()) / 2; + if (cyInflate < 0) { + cyInflate = 0; + } + rcView.Inflate(cxInflate, cyInflate); + } +#endif + + // Pen down inside the view rect? + + if (!rcView.PtIn(xPen, yPen)) { + + // No, set the view to be centered around the point tapped + + TCoord tx = xPen / m_nScale; + TCoord ty = yPen / m_nScale; + + wxView = WcFromTc(tx - (sizPlayfield.cx / gcxTile) / 2); + wyView = WcFromTc(ty - ((sizPlayfield.cy + gcyTile - 1) / gcyTile) / 2); + } + + s_xPenDown = xPen; + s_yPenDown = yPen; + s_wxViewDown = wxView; + s_wyViewDown = wyView; + } else { + int dx = xPen - s_xPenDown; + wxView = s_wxViewDown + WcFromTc(dx / m_nScale); + wxView += (dx % m_nScale) * kwcTile / m_nScale; + int dy = yPen - s_yPenDown; + wyView = s_wyViewDown + WcFromTc(dy / m_nScale); + wyView += (dy % m_nScale) * kwcTile / m_nScale; + } + + gsim.SetViewPos(wxView, wyView); + Invalidate(); +} + +bool MiniMapControl::CalcPoweredRadar() +{ + if (this == NULL) + return false; + + // Remember if the local player has fully powered Radar. This determines whether + // enemy units show on the mini-map. + + bool fHasPoweredRadar = gpplrLocal->GetUnitCount(kutRadar) > 0 && gpplrLocal->GetPowerDemand() <= gpplrLocal->GetPowerSupply(); + word wfMm = fHasPoweredRadar ? kfMmHasPoweredRadar : 0; + if ((wfMm ^ m_wfMm) != 0) { + m_wfMm &= ~kfMmHasPoweredRadar; + m_wfMm |= wfMm; + Redraw(); + return true; + } + return false; +} + +void MiniMapControl::RedrawTile(TCoord tx, TCoord ty) +{ + TRect trc; + trc.left = tx; + trc.top = ty; + trc.right = tx + 1; + trc.bottom = ty + 1; + RedrawTRect(&trc); +} + +void MiniMapControl::RedrawTRect(TRect *ptrc) +{ + // gpmm uninitialized? + + if (this == NULL) + return; + + // Redraw this rect + + byte *pbDst = m_pbm->GetBits() + (long)ptrc->top * m_cbRowBytes * m_nScale + + ptrc->left * m_nScale + (long)m_yOff * m_cbRowBytes + m_xOff; + int cbDstReturn = m_cbRowBytes - ptrc->Width() * m_nScale; + long offset = (long)ptrc->top * m_ctx + ptrc->left; + byte *pbFogMap = m_pbFogMap + offset; + word *pwTileMap = m_pwTileMap + offset; + int ctReturn = m_ctx - ptrc->Width(); + + if (m_nScale == 1) { + for (TCoord ty = ptrc->top; ty < ptrc->bottom; ty++) { + for (TCoord tx = ptrc->left; tx < ptrc->right; tx++, pbFogMap++, pwTileMap++) { + // Fogged? + + if (IsFogOpaque(*pbFogMap)) { + *pbDst++ = m_clrBlack; + continue; + } + + // Not fogged; remember to redraw the minimap to the screen next timer + + m_wfMm |= kfMmRedraw; + + // Unit gob? + + UnitGob *punt = ggobm.GetUnitGob(tx, ty); + + // don't show inactive munts + + if (punt != NULL) { + dword wf = punt->GetFlags(); + if ((wf & (kfGobMobileUnit | kfGobActive)) != (kfGobMobileUnit)) { + if (wf & kfGobSelected) { + *pbDst++ = m_clrWhite; + } else { + *pbDst++ = m_aclrSide[punt->GetSide()]; + } + continue; + } + } + + // Galaxite? + + if (HasGalaxite(*pbFogMap)) { + *pbDst++ = m_clrGalaxite; + continue; + } + + // Tile + + int nTile = (BigWord(*pwTileMap) & 0x7fc); + *pbDst++ = m_pbTileData[nTile >> 2]; + continue; + } + pwTileMap += ctReturn; + pbFogMap += ctReturn; + pbDst += cbDstReturn; + } + } else if (m_nScale == 2) { + for (TCoord ty = ptrc->top; ty < ptrc->bottom; ty++) { + for (TCoord tx = ptrc->left; tx < ptrc->right; tx++, pbFogMap++, pwTileMap++) { + // Fogged? + + if (IsFogOpaque(*pbFogMap)) { + *pbDst++ = m_clrBlack; + *pbDst++ = m_clrBlack; + *(pbDst + m_cbRowBytes - 2) = m_clrBlack; + *(pbDst + m_cbRowBytes - 1) = m_clrBlack; + continue; + } + + // Not fogged; remember to redraw the minimap to the screen next timer + + m_wfMm |= kfMmRedraw; + + // Unit gob? + + UnitGob *punt = ggobm.GetUnitGob(tx, ty); + if (punt != NULL) { + dword wf = punt->GetFlags(); + if ((wf & (kfGobMobileUnit | kfGobActive)) != (kfGobMobileUnit)) { + byte clr; + if (wf & kfGobSelected) { + clr = m_clrWhite; + } else { + clr = m_aclrSide[punt->GetSide()]; + } + + *pbDst++ = clr; + *pbDst++ = clr; + *(pbDst + m_cbRowBytes - 2) = clr; + *(pbDst + m_cbRowBytes - 1) = clr; + continue; + } + } + + // Galaxite? + + if (HasGalaxite(*pbFogMap)) { + *pbDst++ = m_clrGalaxite; + *pbDst++ = m_clrGalaxite; + *(pbDst + m_cbRowBytes - 2) = m_clrGalaxite; + *(pbDst + m_cbRowBytes - 1) = m_clrGalaxite; + continue; + } + + // Tile + + int nTile = (BigWord(*pwTileMap) & 0x7fc); + byte *pbSrc = &m_pbTileData[nTile]; + *pbDst++ = *pbSrc++; + *pbDst++ = *pbSrc++; + *(pbDst + m_cbRowBytes - 2) = *pbSrc++; + *(pbDst + m_cbRowBytes - 1) = *pbSrc++; + continue; + } + pwTileMap += ctReturn; + pbFogMap += ctReturn; + pbDst += cbDstReturn + m_cbRowBytes; + } + } +} + +} // namespace wi diff --git a/game/Simulation.cpp b/game/Simulation.cpp new file mode 100644 index 0000000..ef1321d --- /dev/null +++ b/game/Simulation.cpp @@ -0,0 +1,1204 @@ +#include "game/ht.h" +#include "game/strings.h" +#include "game/stateframe.h" + +namespace wi { + +CommandQueue gcmdq; + +#ifdef STATS_DISPLAY +extern int gcBitmapsDrawn; +#endif +extern int gcMessagesPerUpdate; + +extern bool gfDragSelecting; + +//--------------------------------------------------------------------------- +// Simulation implementation + +Simulation::Simulation() +{ + m_plvl = NULL; + m_fPaused = false; + m_nMiniMapScale = 1; + m_cgobVisible = 0; + m_wxViewSave = 0; + m_wyViewSave = 0; + m_cUpdatesSave = -1; +} + +Simulation::~Simulation() +{ +} + +bool Simulation::OneTimeInit() +{ + // Initialize all the Gob classes + + Status("Parse GobTemplates.ini..."); + IniReader *piniGobTemplates = LoadIniFile(gpakr, "GobTemplates.ini"); + if (piniGobTemplates == NULL) + return false; + + Status("Init SurfaceDecalGob..."); + bool fSuccess = SurfaceDecalGob::InitClass(piniGobTemplates); + Assert(fSuccess); + Status("Init SceneryGob..."); + fSuccess = fSuccess && SceneryGob::InitClass(piniGobTemplates); + Assert(fSuccess); + Status("Init SRInfantryGob..."); + fSuccess = fSuccess && SRInfantryGob::InitClass(piniGobTemplates); + Assert(fSuccess); + Status("Init HrcGob..."); + fSuccess = fSuccess && HrcGob::InitClass(piniGobTemplates); + Assert(fSuccess); + Status("Init ProcessorGob..."); + fSuccess = fSuccess && ProcessorGob::InitClass(piniGobTemplates); + Assert(fSuccess); + Status("Init ReactorGob..."); + fSuccess = fSuccess && ReactorGob::InitClass(piniGobTemplates); + Assert(fSuccess); + Status("Init MinerGob..."); + fSuccess = fSuccess && MinerGob::InitClass(piniGobTemplates); + Assert(fSuccess); + Status("Init VtsGob..."); + fSuccess = fSuccess && VtsGob::InitClass(piniGobTemplates); + Assert(fSuccess); + Status("Init RadarGob..."); + fSuccess = fSuccess && RadarGob::InitClass(piniGobTemplates); + Assert(fSuccess); + Status("Init HqGob..."); + fSuccess = fSuccess && HqGob::InitClass(piniGobTemplates); + Assert(fSuccess); + Status("Init ResearchGob..."); + fSuccess = fSuccess && ResearchGob::InitClass(piniGobTemplates); + Assert(fSuccess); + Status("Init LRInfantryGob..."); + fSuccess = fSuccess && LRInfantryGob::InitClass(piniGobTemplates); + Assert(fSuccess); + Status("Init LTankGob..."); + fSuccess = fSuccess && LTankGob::InitClass(piniGobTemplates); + Assert(fSuccess); + Status("Init MTankGob..."); + fSuccess = fSuccess && MTankGob::InitClass(piniGobTemplates); + Assert(fSuccess); + Status("Init RTankGob..."); + fSuccess = fSuccess && RTankGob::InitClass(piniGobTemplates); + Assert(fSuccess); + Status("Init GTankGob..."); + fSuccess = fSuccess && GTankGob::InitClass(piniGobTemplates); + Assert(fSuccess); + Status("Init SpInfantryGob..."); + fSuccess = fSuccess && SpInfantryGob::InitClass(piniGobTemplates); + Status("Init OvermindGob..."); + fSuccess = fSuccess && OvermindGob::InitClass(piniGobTemplates); + Status("Init TankShotGob..."); + fSuccess = fSuccess && TankShotGob::InitClass(piniGobTemplates); + Status("Init RocketGob..."); + fSuccess = fSuccess && RocketGob::InitClass(piniGobTemplates); + Status("Init BulletGob..."); + fSuccess = fSuccess && BulletGob::InitClass(piniGobTemplates); + Assert(fSuccess); + Status("Init AndyShotGob..."); + fSuccess = fSuccess && AndyShotGob::InitClass(piniGobTemplates); + Assert(fSuccess); + Status("Init WarehouseGob..."); + fSuccess = fSuccess && WarehouseGob::InitClass(piniGobTemplates); + Assert(fSuccess); + Status("Init GunTowerGob..."); + fSuccess = fSuccess && GunTowerGob::InitClass(piniGobTemplates); + Assert(fSuccess); + Status("Init RocketTowerGob..."); + fSuccess = fSuccess && RocketTowerGob::InitClass(piniGobTemplates); + Assert(fSuccess); + Status("Init MobileHqGob..."); + fSuccess = fSuccess && MobileHqGob::InitClass(piniGobTemplates); + Assert(fSuccess); + Status("Init ArtilleryGob..."); + fSuccess = fSuccess && ArtilleryGob::InitClass(piniGobTemplates); + Assert(fSuccess); + Status("Init ArtilleryShotGob..."); + fSuccess = fSuccess && ArtilleryShotGob::InitClass(piniGobTemplates); + Assert(fSuccess); + Status("Init ReplicatorGob..."); + fSuccess = fSuccess && ReplicatorGob::InitClass(piniGobTemplates); + Assert(fSuccess); + Status("Init AndyGob..."); + fSuccess = fSuccess && AndyGob::InitClass(piniGobTemplates); + Assert(fSuccess); + Status("Init FoxGob..."); + fSuccess = fSuccess && FoxGob::InitClass(piniGobTemplates); + Assert(fSuccess); + + delete piniGobTemplates; + + // Do some other one-time initialization that we didn't want to + // do at game startup because we want to startup snappily. + +#ifdef DRAW_PATHS + // Load the direction arrow bitmaps + + Status("Load path arrow bitmaps..."); + LoadArrows(); +#endif + + return fSuccess; +} + +void Simulation::OneTimeExit() +{ + SRInfantryGob::ExitClass(); + HrcGob::ExitClass(); + ProcessorGob::ExitClass(); + ReactorGob::ExitClass(); + MinerGob::ExitClass(); + VtsGob::ExitClass(); + RadarGob::ExitClass(); + HqGob::ExitClass(); + ResearchGob::ExitClass(); + LRInfantryGob::ExitClass(); + LTankGob::ExitClass(); + MTankGob::ExitClass(); + RTankGob::ExitClass(); + GTankGob::ExitClass(); + SpInfantryGob::ExitClass(); + TankShotGob::ExitClass(); + RocketGob::ExitClass(); + BulletGob::ExitClass(); + AndyShotGob::ExitClass(); + WarehouseGob::ExitClass(); + GunTowerGob::ExitClass(); + RocketTowerGob::ExitClass(); + MobileHqGob::ExitClass(); + ArtilleryGob::ExitClass(); + ArtilleryShotGob::ExitClass(); + ReplicatorGob::ExitClass(); + AndyGob::ExitClass(); + FoxGob::ExitClass(); + +#ifdef DRAW_PATHS + FreeArrows(); +#endif + + delete m_plvl; + m_plvl = NULL; +} + +bool Simulation::PerLevelInit() +{ +#ifdef DEBUG_HELPERS + void ClearLog(); + ClearLog(); +#endif + + if (!gcmdq.Init()) + return false; + + m_wxView = m_wyView = 0; + m_cUpdates = -1; + m_tCurrent = 0; + m_fGameOver = false; + m_pbldm = new BuildMgr(); + if (m_pbldm == NULL) + return false; + + m_cupdTriggerMgrUpdateLast = 0; + + // So visible gobs get recalced + + m_wxViewSave = (WCoord)-1; + m_wyViewSave = (WCoord)-1; + m_cUpdatesSave = -2; + + return true; +} + +void Simulation::PerLevelExit() +{ + gcmdq.Exit(); + + delete m_plvl; + m_plvl = NULL; + delete m_pbldm; + m_pbldm = NULL; +} + +bool Simulation::LoadLevel(const char *pszLevelName) +{ + Assert(m_plvl == NULL); + + m_plvl = new Level(); + if (!m_plvl->Init(pszLevelName)) { + delete m_plvl; + m_plvl = NULL; + return false; + } + + // Initialize level-specific player info for each player + + Player *pplr = gplrm.GetNextPlayer(NULL); + for (; pplr != NULL; pplr = gplrm.GetNextPlayer(pplr)) { + SideInfo *psidi = m_plvl->GetSideInfo(pplr->GetSide()); + pplr->SetCredits(psidi->nInitialCredits, false); + + // UNDONE: Level could (should?) provide this state + pplr->SetUpgrades(0); + + if (pplr == gpplrLocal) { + + // Center the view around the level-specified initial view position + + Size siz; + gpupdSim->GetMapSize(&siz); + SetViewPos(psidi->wptInitialView.wx - (WcFromTc(siz.cx) / 2), + psidi->wptInitialView.wy - (WcFromTc(siz.cy) / 2), true); + } + + // In a multiplayer game supporting a range of players (e.g. 2-4) dummy + // players are allocated for any 'human' sides that aren't in use by + // human players. The dummy players must be present so the level, which + // contains units for all sides, can load properly. Here as we load the + // level we remove all units owned by the unfulfilled players. + + word wf = pplr->GetFlags(); + if (wf & kfPlrUnfulfilled) { + Gob *pgobT; + for (pgobT = ggobm.GetFirstGob(); pgobT != NULL; ) { + + // Get the next Gob BEFORE possibly deleting the one it points + // to + + Gob *pgobNext = ggobm.GetNextGob(pgobT); + if (pgobT->GetOwner() == pplr) { + if (pgobT->GetFlags() & kfGobUnit) { + UnitGob *punt = (UnitGob *)pgobT; + punt->Deactivate(); + punt->Delete(); + + // Don't count these units as ever having been built + pplr->DecUnitBuiltCount(punt->GetUnitType()); + } else { + // Not a unit gob, but still owned by this player + // (for example, a tank shot). Remove / delete it. + ggobm.RemoveGob(pgobT); + delete pgobT; + } + } + pgobT = pgobNext; + } + } + } + + return true; +} + +#define knVerSimState 3 +bool Simulation::LoadState(Stream *pstm) +{ + // Do version handling + + byte nVer = pstm->ReadByte(); + if (nVer != knVerSimState) + return false; + + // Load update count! + + m_cUpdates = (long)pstm->ReadDword(); + + // Load view + + WCoord wxView = pstm->ReadWord(); + WCoord wyView = pstm->ReadWord(); + + // Load Simulation tick count + + m_tCurrent = pstm->ReadDword(); + m_cupdTriggerMgrUpdateLast = pstm->ReadDword(); + + // Load level + + Assert(m_plvl == NULL); + m_plvl = new Level(); + if (m_plvl == NULL) + return false; + if (!m_plvl->LoadState(pstm)) { + delete m_plvl; + m_plvl = NULL; + return false; + } + + // Load state machine mgr (delayed messages) + + gsmm.LoadState(pstm); + + // Save command queue (usually empty) + + gcmdq.LoadState(pstm); + + // Set view pos + + SetViewPos(wxView, wyView, true); + + // Done + + return pstm->IsSuccess(); +} + +bool Simulation::SaveState(Stream *pstm) +{ + // Save version + + pstm->WriteByte(knVerSimState); + + // Save update count + + pstm->WriteDword(m_cUpdates); + + // Save view x,y + + pstm->WriteWord(m_wxView); + pstm->WriteWord(m_wyView); + + // Save Simulation tick count + + pstm->WriteDword(m_tCurrent); + pstm->WriteDword(m_cupdTriggerMgrUpdateLast); + + // Save level + + m_plvl->SaveState(pstm); + + // Save state machine mgr (delayed messages...) + + gsmm.SaveState(pstm); + + // Save command queue (usually empty, but just in case...) + + gcmdq.SaveState(pstm); + + return pstm->IsSuccess(); +} + +long Simulation::GetTickCount() +{ + return m_tCurrent; +} + +void Simulation::AddTimer(Timer *ptmr, long ct) +{ + // fyi you are the first user of a timer based on simulation time. + // you'll need to add code to LoadState & SaveState to preserve + // your timer times because simulation time is preserved across load/save + Assert(false); + AddTimer(ptmr, ct); +} + +// The tCurrent increment can't really ever be anything other than 8 (80 ms) +// because Gobs assume it as their update rate and increment animations, etc +// every time they're Updated. + +void Simulation::Update(CommandQueue *pcmdq) +{ + if (m_fPaused) + return; + + // Give some time to sound servicing + + HostSoundServiceProc(); + + // Advance Simulation time + + m_tCurrent += kcmsUpdate / 10; + m_cUpdates++; + + // Update triggers. This is done here so the triggers act on what the + // player is currently seeing (e.g., Credits will show >= NNNN when + // >= NNNN Credits condition is satisfied). + // PostEvent so that modal actions can occur + + // Note - CountdownTimer trigger depends on this being better than once a second. + // TUNE: + #define kcupdTriggerMgrUpdate 6 + + if (m_cupdTriggerMgrUpdateLast == 0 || abs(m_cUpdates - m_cupdTriggerMgrUpdateLast) >= kcupdTriggerMgrUpdate) { + m_cupdTriggerMgrUpdateLast = m_cUpdates; + ggame.ScheduleUpdateTriggers(); + } + + // Update Players + + gplrm.Update(m_cUpdates); + + // Update unit groups. + + m_plvl->GetUnitGroupMgr()->Update(); + + // Update Build Manager + // OPT: this doesn't have to be done every update + + m_pbldm->Update(); + + // Process all queued commands +#ifdef DEBUG_HELPERS + extern void UpdateCommandQueueViewer(); + UpdateCommandQueueViewer(); +#endif + + int cmsg = pcmdq->GetCount(); +#ifdef STATS_DISPLAY + extern int gcbCommandsQueued; + gcbCommandsQueued += cmsg * sizeof(Message); +#endif + int i = 0; + Message *pmsg = pcmdq->GetFirst(); + for (; i < cmsg; i++, pmsg++) { + // Disconnect is processed in order, by all clients, at the same time. + // To do this, it is turned into a message, since messages have a + // synchronization mechanism. + if (pmsg->mid == kmidPlayerDisconnect) { + HandlePlayerDisconnect(pmsg); + continue; + } + gsmm.RouteMessage(pmsg); + } + pcmdq->Clear(); + + gcMessagesPerUpdate = 0; + + ScanDispatch(m_tCurrent); + + // Send Update messages to all Gobs with state machines + + Message msgUpdate; + memset(&msgUpdate, 0, sizeof(msgUpdate)); + msgUpdate.mid = kmidReservedUpdate; + msgUpdate.smidSender = ksmidNull; + + // Snapshot the gids of the Gobs we care about so we aren't vulnerable + // to the insertion or deletion of elements on the Gob list during the + // kmidReserveUpdate callbacks. + + Assert(kcpgobMax * sizeof(Gid) <= 1536); + Gid agid[kcpgobMax]; + Gid *pgidT = agid; + int cgid = 0; + Gob *pgobT; + for (pgobT = ggobm.GetFirstGob(); pgobT != NULL; pgobT = ggobm.GetNextGob(pgobT)) { + if (pgobT->GetFlags() & kfGobStateMachine) { + *pgidT++ = pgobT->GetId(); + cgid++; + } + } + + pgidT = agid; + for (i = 0; i < cgid; i++, pgidT++) { + + // Gob might have been destroyed as a consequence of an earlier + // Gob's Update. If so, skip it. + + pgobT = ggobm.GetGob(*pgidT, false); + if (pgobT == NULL) + continue; + +#ifdef MP_DEBUG_SHAREDMEM + gsideCurrent = pgobT->GetOwner()->GetSide(); + ggidCurrent = pgobT->GetId(); + gcupdCurrent = m_cUpdates; + ggtCurrent = pgobT->GetType(); +#endif + + UpdateInterval *punvl = pgobT->GetUpdateInterval(); + if (punvl->Decrement()) { + msgUpdate.smidReceiver = *pgidT; + gsmm.SendMsg(&msgUpdate); + +#ifdef PIL + if (gfOS5Pa1Device) { + if ((i & 15) == 0) + HostSoundServiceProc(); + } +#endif + } + } + + // Delayed messages are dispatched AFTER Update messages are sent so if + // respondants create new Gobs they won't receive an immediate Update + // before their first paint. This is important for cases like delayed shots. + +#ifdef DEBUG_HELPERS + extern void UpdateDelayedMessageViewer(); + UpdateDelayedMessageViewer(); +#endif + gsmm.DispatchDelayedMessages(); + + long tCurrent = HostGetTickCount(); + + // Sound effects + + // Credits increasing or decreasing? +#if 0 + switch (gpplrLocal->GetCreditsDirection()) { + case 0: + break; + + case 1: + gsndm.PlaySfx(ksfxGameCreditsIncreasing); + break; + + case -1: + // If the credits user is for repair, only play decrease sound over a longer interval + // TODO: figure out ultimate approach for managing interval for credit spending +// TUNE: +#define kctIntervalRepairNotify (75) + if (true) { // gpplrLocal->GetCreditsConsumer() == knConsumerRepair) { + static long s_tLastRepairNotify = 0; + if (s_tLastRepairNotify == 0 || abs(tCurrent - s_tLastRepairNotify) >= kctIntervalRepairNotify) { + s_tLastRepairNotify = tCurrent; + gsndm.PlaySfx(ksfxGameCreditsDecreasing); + } + } else { + gsndm.PlaySfx(ksfxGameCreditsDecreasing); + } + break; + } +#endif + + // Base under attack? + +// TUNE: +#define kctIntervalAttackNotify (30 * 100) + if (gpplrLocal->GetFlags() & kfPlrStructureAttacked) { + gpplrLocal->SetFlags(gpplrLocal->GetFlags() & ~kfPlrStructureAttacked); + static long s_tLastAttackNotify = 0; + if (s_tLastAttackNotify == 0 || abs(tCurrent - s_tLastAttackNotify) >= kctIntervalAttackNotify) { + s_tLastAttackNotify = tCurrent; + gsndm.PlaySfx(ksfxGameBaseUnderAttack); + ShowAlert(kidsBaseUnderAttack); + } + } + + // Power too low? + +// TUNE: +#define kctIntervalPowerLowNotify (30 * 100) + if (gpplrLocal->GetPowerDemand() != 0 && gpplrLocal->GetPowerDemand() > gpplrLocal->GetPowerSupply()) { + static long s_tLastPowerLowNotify = 0; + if (s_tLastPowerLowNotify == 0 || (tCurrent - s_tLastPowerLowNotify) >= kctIntervalPowerLowNotify) { + s_tLastPowerLowNotify = tCurrent; + gsndm.PlaySfx(ksfxReactorPowerTooLow); + ShowAlert(kidsLowPower); + } + } + + +#ifdef MP_STRESS + // Has to be at the end of Update() because gcmdq serves the needs of both + // collecting client commands and collecting multiplayer commands. This + // role switching occurs in in knmidScUpdate handling in + // SimUIForm::OnReceive. + + extern void MPStressUpdate(); + MPStressUpdate(); +#endif + +} + +void Simulation::HandlePlayerDisconnect(Message *pmsg) { + // Re-route this as a NetMessage to get proper handling + if (gptra == NULL) { + return; + } + IGameCallback *pgcb = gptra->GetGameCallback(); + if (pgcb == NULL) { + return; + } + + // This must not be modal + PlayerDisconnectNetMessage pdnm; + pdnm.pid = pmsg->PlayerDisconnect.pid; + pdnm.nReason = pmsg->PlayerDisconnect.nReason; + NetMessage *pnm = &pdnm; + pgcb->OnNetMessage(&pnm); +} + +#ifdef TRACKSTATE +void Simulation::TrackState(StateFrame *frame) { + // Sim stuff + + int i = frame->AddCountedValue('SIMU'); + frame->AddValue('SEED', (dword)GetRandomSeed(), i); + frame->AddValue('CMPU', (dword)gcMessagesPerUpdate, i); + frame->AddValue('CUPD', (dword)m_cUpdates, i); + + // gobs + + Gob *pgobT; + for (pgobT = ggobm.GetFirstGob(); pgobT != NULL; + pgobT = ggobm.GetNextGob(pgobT)) { + if (!(pgobT->GetFlags() & kfGobStateMachine)) { + continue; + } + pgobT->TrackState(frame); + } + + // players + + gplrm.TrackState(frame); +} +#endif + +void Simulation::SetSelection(Rect *prc) +{ + if (gfLassoSelection || !gfDragSelecting) { + return; + } + + for (Gob *pgobT = ggobm.GetFirstGob(); pgobT != NULL; + pgobT = ggobm.GetNextGob(pgobT)) { + dword ff = pgobT->GetFlags(); + + // If were in select mode then only Gobs inside the selection rectangle + // get to be selected. + + if ((ff & kfGobUnit) == 0) { + continue; + } + bool fSelect = false; + UnitGob *punt = (UnitGob *)pgobT; + if ((gfGodMode || punt->GetOwner() == gpplrLocal) && + ((ff & (kfGobActive | kfGobUnit)) == (kfGobActive | kfGobUnit))) { + if ((ff & kfGobStructure) == 0 || + (punt->GetConsts()->um & kumTowers) != 0) { + WPoint wptGobCenter; + punt->GetCenter(&wptGobCenter); + if (prc->PtIn(wptGobCenter.wx, wptGobCenter.wy)) { + fSelect = true; + } + } + } + punt->Select(fSelect); + } +} + +void Simulation::ClearGobSelection() +{ + // Deselect any selected Gobs + + for (Gob *pgobT = ggobm.GetFirstGob(); pgobT != NULL; pgobT = ggobm.GetNextGob(pgobT)) { + dword ff = pgobT->GetFlags(); + if ((ff & (kfGobSelected | kfGobUnit)) == (kfGobSelected | kfGobUnit)) + ((UnitGob *)pgobT)->Select(false); + } +} + +void Simulation::SetGobSelected(Gob *pgob) +{ + ClearGobSelection(); + if ((pgob->GetFlags() & (kfGobSelected | kfGobUnit)) == kfGobUnit) + ((UnitGob *)pgob)->Select(true); +} + +void Simulation::SelectSameUnitTypes(UnitGob *punt, TRect *ptrc) +{ + Player *pplr = punt->GetOwner(); + if (pplr != gpplrLocal) + return; + + UnitType ut = punt->GetUnitType(); + + // Store the gobs on the stack; we've tested this with 1K of stack; assert if it + // changes + + Assert(kcpgobMax / 2 * sizeof(Gob *) <= 1536); + Gob *apgob[kcpgobMax / 2]; + + int cpgob = ggobm.FindGobs(ptrc, apgob, ARRAYSIZE(apgob)); + for (int i = 0; i < cpgob; i++) { + MobileUnitGob *pmuntT = (MobileUnitGob *)apgob[i]; + dword ff = pmuntT->GetFlags(); + if (!(ff & kfGobMobileUnit)) + continue; + if (pmuntT->GetOwner() != pplr) + continue; + if (pmuntT->GetUnitType() != ut) + continue; + + pmuntT->Select(true); + } +} + +void Simulation::FindVisibleGobs(Gob ***pppgobVisible, int *pcgobVisible) +{ + // Reuse the gob list if it's the same update count and view pos + + if (m_wxView != m_wxViewSave || m_wyView != m_wyViewSave || m_cUpdates != m_cUpdatesSave) { + // Get visible gobs / refresh cache info + + Size siz; + ggame.GetPlayfieldSize(&siz); + short xView = PcFromUwc(m_wxView) & 0xfffe; + short yView = PcFromUwc(m_wyView) & 0xfffe; + Rect rcVisible; + rcVisible.left = xView; + rcVisible.top = yView; + rcVisible.right = xView + siz.cx; + rcVisible.bottom = yView + siz.cy; + m_cgobVisible = ggobm.FindGobs(&rcVisible, m_apgobVisible, ARRAYSIZE(m_apgobVisible), m_plvl->GetFogMap()->GetMapPtr()); + m_wxViewSave = m_wxView; + m_wyViewSave = m_wyView; + m_cUpdatesSave = m_cUpdates; + } + + // Return if asked + + if (pppgobVisible != NULL) + *pppgobVisible = m_apgobVisible; + if (pcgobVisible != NULL) + *pcgobVisible = m_cgobVisible; +} + +void Simulation::DrawBackground(UpdateMap *pupd, DibBitmap *pbm) +{ + // Draw tiles where the updatemap is invalid + + short xView = PcFromUwc(m_wxView) & 0xfffe; + short yView = PcFromUwc(m_wyView) & 0xfffe; + FogMap *pfogm = m_plvl->GetFogMap(); + TileMap *ptmap = m_plvl->GetTileMap(); + Size sizDib; + pbm->GetSize(&sizDib); + ptmap->Draw(pbm, 0, 0, sizDib.cx, sizDib.cy, xView, yView, pfogm->GetMapPtr(), pupd); + + HostSoundServiceProc(); + + // Draw galaxite where the updatemap is invalid + + pfogm->DrawGalaxite(pbm, xView, yView, pupd, m_plvl->GetTerrainMap()->GetMapPtr()); + HostSoundServiceProc(); +} + +void Simulation::Draw(UpdateMap *pupd, DibBitmap *pbm) +{ + // Maps only draw on even coords + // OPT: can this be streamlined? + + short xView = PcFromUwc(m_wxView) & 0xfffe; + short yView = PcFromUwc(m_wyView) & 0xfffe; + WCoord wxView = WcFromUpc(xView); + WCoord wyView = WcFromUpc(yView); + + // + // Normal full-size map (not mini-map) + // + + // Set the view origin so that blt-style map scrolling is possible + + pupd->SetViewOrigin(xView, yView); + + // Give some time to sound servicing + + HostSoundServiceProc(); + + // Find visible Gobs + + FindVisibleGobs(); + +#ifdef STATS_DISPLAY + gcBitmapsDrawn = 0; +#endif + +#ifdef DRAW_PATHS + if (gfDrawPaths) { + + // Draw paths + + for (Gob *pgob = ggobm.GetFirstGob(); pgob != NULL; pgob = ggobm.GetNextGob(pgob)) { + if ((pgob->GetFlags() & kfGobMobileUnit) && ((MobileUnitGob *)pgob)->m_ppathUnit != NULL) + ((MobileUnitGob *)pgob)->DrawPath(pbm, wxView, wyView); + } + } +#endif + + HostSoundServiceProc(); + + // Figure out what layers are in use. Much of the time only 1 or 2 layers are in use + + dword ffLayers = 0; + Gob **ppgobStop = &m_apgobVisible[m_cgobVisible]; + Gob **ppgobT; + for (ppgobT = m_apgobVisible; ppgobT < ppgobStop; ppgobT++) + ffLayers |= (*ppgobT)->GetFlags(); + ffLayers &= kfGobLayerMask; + + // Turn off symbols layers if off + + if (!(gwfPerfOptions & kfPerfSymbolFlashing)) + ffLayers &= ~kfGobLayerSymbols; + + // Draw gobs. Skip layers that aren't in use; only call a gob if it wants to draw on + // this layer + + dword ffLayerT = kfGobLayerSurfaceDecal; + for (int nLayer = knLayerSurfaceDecal; nLayer <= knLayerEnd; nLayer++, ffLayerT <<= 1) { + if (!(ffLayerT & ffLayers)) + continue; + int cT = 0; + for (ppgobT = m_apgobVisible; ppgobT < ppgobStop; ppgobT++) { + Gob *pgobT = *ppgobT; + dword ff = pgobT->GetFlags(); + + // If gob is marked for redraw... + + if (ff & kfGobRedraw) { + // If gob is painting on this layer then paint + + if (ff & ffLayerT) { + pgobT->Draw(pbm, xView, yView, nLayer); + +#ifdef PIL + if (gfOS5Pa1Device) { + cT++; + if ((cT & 15) == 0) + HostSoundServiceProc(); + } +#endif + } + } + } + } + + // Give some time to sound servicing + + HostSoundServiceProc(); + + // Clear kfGobRedraw since the gobs are now "valid". + // Do this after painting because some painting code ends up setting this bit + + for (ppgobT = m_apgobVisible; ppgobT < ppgobStop; ppgobT++) { + Gob *pgobT = *ppgobT; + pgobT->SetFlags(pgobT->GetFlags() & ~kfGobRedraw); + } + +#ifdef DRAW_LINES + if (gfDrawLines) { + // Draw target lines + + for (Gob *pgob = ggobm.GetFirstGob(); pgob != NULL; pgob = ggobm.GetNextGob(pgob)) { + // Total hack-o-rama + if ((pgob->GetFlags() & kfGobMobileUnit) && ((MobileUnitGob *)pgob)->m_wptTarget.wx != kwxInvalid) + ((MobileUnitGob *)pgob)->DrawTargetLine(pbm, xView, yView); + if ((pgob->GetType() == kgtRocketTower || pgob->GetType() == kgtMachineGunTower) && + ((TowerGob *)pgob)->m_wptTarget.wx != kwxInvalid) + ((TowerGob *)pgob)->DrawTargetLine(pbm, xView, yView); + } + } +#endif + + +#ifdef MARK_TILE_BOUNDARIES + { + Color clr = GetColor(kiclrWhite); + Size sizT; + ggame.GetPlayfieldSize(&sizT); + for (int y = -PcFromUwc(WcFrac(wyView)); y < sizT.cy; y += gcyTile) { + for (int x = -PcFromUwc(WcFrac(wxView)); x < sizT.cx; x += gcxTile) { + pbm->Fill(x, y, 2, 2, clr); + } + } + } +#endif + +#ifdef MARK_OCCUPIED_TILES + { + Color clr = GetColor(kiclrWhite); + Size sizT; + ggame.GetPlayfieldSize(&sizT); + int ctxView = sizT.cx / gcxTile + 1; + int ctyView = sizT.cy / gcyTile + 1; + TerrainMap *ptrmap = m_plvl->GetTerrainMap(); + + int xView = PcFromWc(wxView); + int yView = PcFromWc(wyView); + TCoord txView = TcFromWc(wxView); + TCoord tyView = TcFromWc(wyView); + for (TCoord ty = tyView; ty < tyView + ctyView; ty++) { + for (TCoord tx = txView; tx < txView + ctxView; tx++) { + if (ptrmap->IsOccupied(tx, ty, 1, 1)) { + int x = PcFromTc(tx) - xView; + int y = PcFromTc(ty) - yView; + pbm->Fill(x + gcxTile / 2 - 1, y + gcyTile / 2 - 1, 2, 2, clr); + } + } + } + } +#endif + + pupd->SetViewOrigin(0, 0); + HostSoundServiceProc(); +} + +void Simulation::DrawFog(UpdateMap *pupd, DibBitmap *pbm) +{ + // Draw fog map + + short xView = PcFromUwc(m_wxView) & 0xfffe; + short yView = PcFromUwc(m_wyView) & 0xfffe; + m_plvl->GetFogMap()->Draw(pbm, xView, yView, pupd); + HostSoundServiceProc(); +} + +bool Simulation::SetViewPos(WCoord wx, WCoord wy, bool fInit) +{ + // Pin the passed position to the bounds of the map + + Size sizMap; + m_plvl->GetTileMap()->GetMapSize(&sizMap); + + Size sizPlayfield; + ggame.GetPlayfieldSize(&sizPlayfield); + + WCoord wcxMax = WcFromUpc(sizMap.cx - sizPlayfield.cx); + WCoord wcyMax = WcFromUpc(sizMap.cy - sizPlayfield.cy); + + if (wx < 0) + wx = 0; + else if (wx > wcxMax) + wx = wcxMax; + + if (wy < 0) + wy = 0; + else if (wy > wcyMax) + wy = wcyMax; + + // If we're initing, we're loading a new level. Invalidate and + // set the map offset + + if (fInit) { + gpupdSim->Reset(); + gpdisp->ResetScrollOffset(); + m_wxView = WcFromPc(0); + m_wyView = WcFromPc(0); + } + + if (m_wxView == wx && m_wyView == wy) + return false; + + // Otherwise scroll the map + + int dx = (PcFromWc(m_wxView) & 0xfffe) - (PcFromWc(wx) & 0xfffe); + int dy = (PcFromWc(m_wyView) & 0xfffe) - (PcFromWc(wy) & 0xfffe); + gpfrmmSim->Scroll(dx, dy); + + // Set the new view pos + + m_wxView = wx; + m_wyView = wy; + + // Force redraw before next update + + gevm.SetRedrawFlags(kfRedrawDirty | kfRedrawBeforeTimer); + + // Debug + +// Trace("View: %d,%d", PcFromWc(m_wxView) & 0xfffe, PcFromWc(m_wyView) & 0xfffe); + + return true; +} + +void Simulation::GetViewPos(WCoord *pwx, WCoord *pwy) +{ + Assert(pwx != NULL); + Assert(pwy != NULL); + + *pwx = m_wxView; + *pwy = m_wyView; +} + +// Passed coordinates are in world coordinates + +bool Simulation::HitTest(Enum *penm, WCoord wx, WCoord wy, word wf, Gob **ppgob) +{ + if (penm->m_pvNext == (void *)kEnmFirst) { + FindVisibleGobs(); + penm->m_dwUser = 0; + } + + for (; (int)penm->m_dwUser < m_cgobVisible; penm->m_dwUser++) { + Gob *pgob = m_apgobVisible[penm->m_dwUser]; + if ((pgob->GetFlags() & wf) != wf) + continue; + + WRect wrcT; + pgob->GetUIBounds(&wrcT); + if (!wrcT.PtIn(wx, wy)) + continue; + + *ppgob = pgob; + penm->m_dwUser++; + return true; + } + + return false; +} + +// Passed coordinates are in world coordinates + +Gob *Simulation::FingerHitTest(WCoord wx, WCoord wy, word wf, + bool *pfHitSurrounding) +{ + // Finger hit testing is tricky, because currently at least, + // a given tile is smaller than a finger. It's difficult to pick + // a cell on a grid, when the cells are smaller than a finger. One + // solution is to make the tiles larger. Instead, the gobs will be + // hit-tested specially, so that close hits register, while taking + // special precaution for open terrain between closely packed + // gobs. + + // Calc the world rect of the tile being hit + + WRect wrcTileHit; + wrcTileHit.left = WcTrunc(wx); + wrcTileHit.top = WcTrunc(wy); + wrcTileHit.right = wrcTileHit.left + kwcTile; + wrcTileHit.bottom = wrcTileHit.top + kwcTile; + WRect wrcTileHitAccum; + wrcTileHitAccum.SetEmpty(); + + // Find the "closest" gob being hit, and keep track of hit area overlap + // with the tile being hit. + + Gob *pgobHitBest = NULL; + WCoord wcDistanceBest = kwcMax; + + FindVisibleGobs(); + for (int i = 0; i < m_cgobVisible; i++) { + Gob *pgobHit = m_apgobVisible[i]; + if ((pgobHit->GetFlags() & wf) != wf) { + continue; + } + + // Calc the inside rect - at least a tile's width and height + // The inside rect is a direct hit on the gob. Return the + // first direct hit, if there is one. + + WRect wrcInside; + pgobHit->GetUIBounds(&wrcInside); + WCoord cwxInflate = 0; + if (wrcInside.Width() < kwcTile) { + cwxInflate += (kwcTile - wrcInside.Width()) / 2; + } + WCoord cwyInflate = 0; + if (wrcInside.Height() < kwcTile) { + cwyInflate += (kwcTile - wrcInside.Height()) / 2; + } + wrcInside.Inflate(cwxInflate, cwyInflate); + + // Calc distance to this. If 0, we're inside and we're done + // Remember "closest" gob. + + WCoord wcDistance = wrcInside.GetDistance(wx, wy); + if (wcDistance == 0) { + *pfHitSurrounding = false; + return pgobHit; + } + if (wcDistance < wcDistanceBest) { + wcDistanceBest = wcDistance; + pgobHitBest = pgobHit; + } + + // Calc the outside rect - accumulate overlap with the tile rect. + + WRect wrcOutside = wrcInside; + cwxInflate = 0; + if (wrcOutside.Width() < kwcTile * 2) { + cwxInflate += (kwcTile * 2 - wrcOutside.Width()) / 2; + } + cwyInflate = 0; + if (wrcOutside.Height() < kwcTile * 2) { + cwyInflate += (kwcTile * 2 - wrcOutside.Height()) / 2; + } + wrcOutside.Inflate(cwxInflate, cwyInflate); + + // Intersect and accumlate with tile rect. + + WRect wrcT; + if (wrcT.Intersect(&wrcTileHit, &wrcOutside)) { + if (wrcTileHitAccum.IsEmpty()) { + wrcTileHitAccum = wrcT; + } else { + wrcTileHitAccum.Union(&wrcT); + } + } + } + + // If no gob or hit too far away, no hit. + + if (pgobHitBest == NULL) { + return NULL; + } + + // Structures get no slop since they are big already + if (pgobHitBest->GetFlags() & kfGobStructure) { + if (wcDistanceBest != 0) { + return NULL; + } + } + + // Check if this is a mobile headquarters that is moving. In this case, + // selection is prioritized over local targetting, so that transforming + // is easier. Local targetting is higher priority if the mobile HQ + // is not moving. + + if (pgobHitBest->GetType() == kgtMobileHeadquarters) { + MobileUnitGob *pmunt = (MobileUnitGob *)pgobHitBest; + if (pmunt->IsMobile()) { + if (wcDistanceBest <= kwcTile) { + *pfHitSurrounding = true; + return pgobHitBest; + } + } + } + + // See if the unit is close enough to qualify for being hit on. + + if (wcDistanceBest > kwcTileHalf) { + return NULL; + } + + // Check to see if the tile hit is completely obscured by overlapping + // hit regions. If yes, then leave as open terrain (so that terrain can + // be hit between closely packed gobs). + + if (wrcTileHitAccum.Equal(&wrcTileHit)) { + return NULL; + } + + // Independent of hit thumbprint, if a gob is already selected, allow + // the terrain next to it to be selected. + + if (pgobHitBest->GetFlags() & kfGobSelected) { + return NULL; + } + + // Structures are big enough to hit the regular bounds. + if (pgobHitBest->GetFlags() & kfGobStructure) { + return NULL; + } + + // This gob has been hit, in the "surrounding" area. + + *pfHitSurrounding = true; + return pgobHitBest; +} + +void Simulation::Pause(bool fPause) +{ + m_fPaused = fPause; +} + +bool Simulation::IsPaused() +{ + return m_fPaused; +} + +} // namespace wi diff --git a/game/SocTransport.cpp b/game/SocTransport.cpp new file mode 100644 index 0000000..f4c6ae8 --- /dev/null +++ b/game/SocTransport.cpp @@ -0,0 +1,819 @@ +#ifdef PNO +#define USE_PALM_UNIX_HEADERS +#endif + +#include "ht.h" +#include "mpshared/netmessage.h" +#include "SocTransport.h" + +namespace wi { + +#ifdef DEBUG +const TCHAR *PszFromSocError() secComm; +const TCHAR *PszFromSocError(int nError) secComm; +#endif + +//--------------------------------------------------------------------------- +// SocTransport implementation + +SocTransport::SocTransport(dword dwIpAddress) +{ + m_socBroadcast = INVALID_SOCKET; + m_socBroadcastListen = INVALID_SOCKET; + m_socAcceptListen = INVALID_SOCKET; + m_dwIpAddress = dwIpAddress; + m_pconAsyncConnect = NULL; +} + +bool SocTransport::GetLocalNetAddress(NetAddress *pnad) +{ + memset(pnad, 0, sizeof(*pnad)); + sockaddr_in *psoca = (sockaddr_in *)pnad; + psoca->sin_family = AF_INET; + psoca->sin_port = 0; + +#ifdef IPHONE + psoca->sin_addr.s_addr = htonl(m_dwIpAddress); +#else + psoca->sin_addr.S_un.S_addr = htonl(m_dwIpAddress); +#endif + return true; +} + +// Stub so we can force the code section this destructor goes in + +SocTransport::~SocTransport() +{ +} + +void SocTransport::GetAddressString(char *pszAddress, int cbMax) +{ + in_addr ina; +#ifdef IPHONE + ina.s_addr = htonl(m_dwIpAddress); +#else + ina.S_un.S_addr = htonl(m_dwIpAddress); +#endif + strncpyz(pszAddress, inet_ntoa(ina), cbMax); +} + +// UNDONE: this blocks in connect() (not Async!) + +Connection *SocTransport::AsyncConnect(NetAddress *pnad) +{ + MpTrace("AsyncConnect(%s)", pnad != NULL ? inet_ntoa(((sockaddr_in *)pnad)->sin_addr) : "loopback"); + + Connection *pcon; + + // A NULL pnad means this device is the server and is now trying to + // connect to itself as a client. Use the LoopbackConnection to satisfy + // this. + + if (pnad == NULL) { + pcon = new LoopbackConnection(this); + Assert(pcon != NULL, "out of memory!"); + if (pcon == NULL) + return NULL; + m_fAsyncConnectLoopback = true; + + } else { + // Create the socket for connecting to the server + + SOCKET soc = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (soc == INVALID_SOCKET) { +#ifdef DEBUG + HostMessageBox(TEXT("socket err: 0x%lx"), PszFromSocError()); +#endif + return NULL; + } + + // Disable nagle algorithm + + int nTrue = 1; + setsockopt(soc, IPPROTO_TCP, TCP_NODELAY, (NetBuff)&nTrue, sizeof(int)); + + // Disable excessive 'lingering' (particularly a problem on PalmOS <=5 which has only 16 sockets) + + linger lngr; + lngr.l_onoff = 1; + lngr.l_linger = 0; + setsockopt(soc, SOL_SOCKET, SO_LINGER, (NetBuff)&lngr, sizeof(lngr)); + + // Connect to the server + // UNDONE: On PalmOS <=5 this occasionally times out after 2 secs. Longer would be better + + if (connect(soc, (const sockaddr *)pnad, sizeof(sockaddr_in)) != 0) { + switch (WSAGetLastError()) { + case WSAEHOSTUNREACH: + HtMessageBox(kfMbWhiteBorder, "Comm Problem", "Host unreachable."); + break; + + case WSAECONNREFUSED: + HtMessageBox(kfMbWhiteBorder, "Comm Problem", "Connection refused."); + break; + + case WSAETIMEDOUT: + HtMessageBox(kfMbWhiteBorder, "Comm Problem", "Timed out trying to connect to host."); + break; + + default: + HtMessageBox(kfMbWhiteBorder, "Comm Problem", "Unable to connect. Error %d", WSAGetLastError()); + break; + } + closesocket(soc); + return NULL; + } + + pcon = NewConnection(soc); + if (pcon == NULL) { + closesocket(soc); + return NULL; + } + m_fAsyncConnectLoopback = false; + } + + AddConnection(pcon); + + Assert(m_pconAsyncConnect == NULL, "Can only have one pending AsyncConnect at a time"); + m_pconAsyncConnect = pcon; + + return pcon; +} + +SocConnection *SocTransport::NewConnection(SOCKET soc) +{ + return new SocConnection(soc); +} + +void SocTransport::Poll() +{ + if (!IsOpen()) + return; + + // Users of Transport::AsyncConnect rely on it not completing the connection + // immediately so they have an opportunity to hook the OnConnectComplete + // callback. + + if (m_pconAsyncConnect != NULL) { + if (m_fAsyncConnectLoopback) + ((LoopbackConnection *)m_pconAsyncConnect)->Connect(); + IConnectionCallback *pccb = m_pconAsyncConnect->GetCallback(); + if (pccb != NULL) { + pccb->OnConnectComplete(m_pconAsyncConnect); + } + m_pconAsyncConnect = NULL; + } + + // While a game is being advertised the server accepts client connections + + if (m_socAcceptListen != INVALID_SOCKET) { + + // Test if any client connection requests are pending + + fd_set fds; + FD_ZERO(&fds); + FD_SET(m_socAcceptListen, &fds); + TIMEVAL tvTimeout; + tvTimeout.tv_sec = 0; + tvTimeout.tv_usec = 0; + int nSelected = select(((int)m_socAcceptListen) + 1, &fds, NULL, NULL, &tvTimeout); + if (nSelected == SOCKET_ERROR) { +#ifdef DEBUG + HostMessageBox(TEXT("select err: %s"), PszFromSocError()); +#endif + if (m_ptcb != NULL) + m_ptcb->OnTransportError(ktraeAdvertiseGameFailed); + return; + } + + // Accept a client connection and produce a new socket for communicating with that client + + if (nSelected == 1) { + NetAddress nad; + memset(&nad, 0, sizeof(nad)); +#ifdef IPHONE + socklen_t cbAddr = sizeof(nad); +#else + int cbAddr = sizeof(nad); +#endif + SOCKET socConn = accept(m_socAcceptListen, (sockaddr *)&nad, &cbAddr); + if (socConn == INVALID_SOCKET) { + // This error is seen on a Tungsten C hosting a game when a ux50 + // times out while trying to connect. The best thing to do is just + // ignore the connection attempt. Hopefully it will try again and + // get it right this time + + if (WSAGetLastError() == 0) + return; +#ifdef DEBUG + HostMessageBox(TEXT("accept err: %s"), PszFromSocError()); +#endif + return; + } +// HostMessageBox("cbAddr = %d, sizeof(sockaddr_in) = %d", cbAddr, sizeof(sockaddr_in)); +// Assert(cbAddr == sizeof(sockaddr_in)); + + Connection *pcon = new SocConnection(socConn); + Assert(pcon != NULL, "out of memory!"); + if (pcon == NULL) { + closesocket(socConn); + return; + } + AddConnection(pcon); + + if (m_ptcb != NULL) { + if (!m_ptcb->OnClientConnect(pcon)) + delete pcon; + } + } + } + + Transport::Poll(); +} + +// BeginGameSearch is called when the JoinOrHostMultiplayer form enters its +// modal loop. EndGameSearch is called when the form is destructed. + +// Winsock GameSearch +// - create a non-blocking datagram socket to listen for SERVERINFO broadcasts +// - poll the socket to check for SERVERINFO broadcasts every 1/10th of a second +// - for each SERVERINFO broadcast received call the Transport's registered +// ITransportCallback::OnReceive method + +bool SocTransport::BeginGameSearch() +{ + // Already searching? + + if (m_socBroadcastListen != INVALID_SOCKET) + return false; + + m_fBlockNextGameHost = false; + + m_socBroadcastListen = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + + if (m_socBroadcastListen == INVALID_SOCKET) { +#ifdef DEBUG + HostMessageBox(TEXT("socket err: %s"), PszFromSocError()); +#endif + HtMessageBox(kfMbWhiteBorder, "Comm Problem", "Failed to open network."); + return false; + } + + // Set the socket to non-blocking because we're going to poll it + // every 1/10th of a second. + + unsigned long ulT = 1; +#if defined(PIL) + // I think the NetSocket.c fcntl simulator routine (NetFCntl) is screwed + // up, using the wrong constants and not conforming to its own documentation. + // But for now I'll go with the flow... + + if (fcntl(m_socBroadcastListen, F_SETFL, FNDELAY) == SOCKET_ERROR) { +#elif defined(IPHONE) + if (fcntl(m_socBroadcastListen, F_SETFL, fcntl(m_socBroadcastListen, F_GETFL, 0) | O_NONBLOCK) == -1) { +#else + if (ioctlsocket(m_socBroadcastListen, FIONBIO, &ulT) == SOCKET_ERROR) { +#endif +#ifdef DEBUG + HostMessageBox(TEXT("socket err: %s"), PszFromSocError()); +#endif + HtMessageBox(kfMbWhiteBorder, "Comm Problem", "Network error."); + return false; + } + + sockaddr_in soca; + soca.sin_family = AF_INET; +// soca.sin_addr.S_un.S_addr = htonl(m_dwIpAddress); + +#ifdef IPHONE + soca.sin_addr.s_addr = htonl(INADDR_ANY); +#else + soca.sin_addr.S_un.S_addr = htonl(INADDR_ANY); +#endif + + // The server broadcasts across 4 ports so we can run up to 4 clients on + // the same machine without conflict. Loop through all the ports until + // we find a free one (one not taken already by another running client). + + int i; + for (i = 0; i < 4; i++) { + soca.sin_port = htons(SERVERINFO_BROADCAST_PORT + i); + if (bind(m_socBroadcastListen, (sockaddr *)&soca, sizeof(soca)) != SOCKET_ERROR) + break; + } + +#ifdef DEBUG + if (i == 4) + HostMessageBox(TEXT("bind err: %s"), PszFromSocError()); +#endif + + // Start timer + + gtimm.AddTimer(this, 10); // Every 100ms, i.e., 1/10 second + return true; +} + +void SocTransport::EndGameSearch() +{ + if (m_socBroadcastListen == INVALID_SOCKET) + return; + + // Stop timer + + gtimm.RemoveTimer(this); + + closesocket(m_socBroadcastListen); + m_socBroadcastListen = INVALID_SOCKET; +} + +void SocTransport::NextGameHost() +{ + m_fBlockNextGameHost = false; +} + +// AdvertiseGame must clean up after itself if it fails, i.e., +// do not assume that UnadvertiseGame will be called. + +bool SocTransport::AdvertiseGame(const char *pszGameName) +{ + int err; + + // Remember the game name we'll be broadcasting + + strncpyz(m_szGameName, (char *)pszGameName, sizeof(m_szGameName)); + + // Create a socket to broadcast through + + m_socBroadcast = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (m_socBroadcast == INVALID_SOCKET) { +#ifdef DEBUG + HostMessageBox(TEXT("socket err: %s"), PszFromSocError()); +#endif + return false; + } + int broadcast = 1; + setsockopt(m_socBroadcast, SOL_SOCKET, SO_BROADCAST, (char *)&broadcast, sizeof(broadcast)); + + sockaddr_in soca; + memset(&soca, 0, sizeof(soca)); + soca.sin_family = AF_INET; +// soca.sin_addr.S_un.S_addr = htonl(m_dwIpAddress); + +#ifdef IPHONE + soca.sin_addr.s_addr = htonl(INADDR_ANY); +#else + soca.sin_addr.S_un.S_addr = htonl(INADDR_ANY); +#endif + err = bind(m_socBroadcast, (sockaddr *)&soca, sizeof(soca)); + if (err == SOCKET_ERROR) { +#ifdef DEBUG + HostMessageBox(TEXT("socket err: %s"), PszFromSocError()); +#endif + closesocket(m_socBroadcast); + m_socBroadcast = INVALID_SOCKET; + return false; + } + + // Create the socket for listening to client connection requests + + m_socAcceptListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (m_socAcceptListen == INVALID_SOCKET) { +#ifdef DEBUG + HostMessageBox(TEXT("socket err: %s"), PszFromSocError()); +#endif + if (m_socBroadcast != INVALID_SOCKET) { + closesocket(m_socBroadcast); + m_socBroadcast = INVALID_SOCKET; + } + return false; + } + + // Disable nagle algorithm -- should be inherited by all accepted sockets + + int nTrue = 1; + setsockopt(m_socAcceptListen, IPPROTO_TCP, TCP_NODELAY, (NetBuff)&nTrue, sizeof(int)); + + // Bind the listening socket to the local address and the connection port + + NetAddress nad; + GetLocalNetAddress(&nad); + ((sockaddr_in *)&nad)->sin_port = htons(SERVER_LISTEN_PORT); + +#ifdef IPHONE + ((sockaddr_in *)&nad)->sin_addr.s_addr = htons(INADDR_ANY); +#else + ((sockaddr_in *)&nad)->sin_addr.S_un.S_addr = htons(INADDR_ANY); +#endif + +// HostMessageBox("addr %s, fam %d, port %d", inet_ntoa(((sockaddr_in *)&nad)->sin_addr), ((sockaddr_in *)&nad)->sin_family, ntohs(((sockaddr_in *)&nad)->sin_port)); + err = bind(m_socAcceptListen, (const sockaddr *)&nad, sizeof(sockaddr_in)); + if (err == SOCKET_ERROR) { +#ifdef DEBUG + HostMessageBox(TEXT("bind err: %s"), PszFromSocError()); +#endif + if (m_socBroadcast != INVALID_SOCKET) { + closesocket(m_socBroadcast); + m_socBroadcast = INVALID_SOCKET; + } + closesocket(m_socAcceptListen); + m_socAcceptListen = INVALID_SOCKET; + return false; + } + + // Prepare for client connections + + err = listen(m_socAcceptListen, SOMAXCONN); + if (err == SOCKET_ERROR) { +#ifdef DEBUG + HostMessageBox(TEXT("listen err: %s"), PszFromSocError()); +#endif + if (m_socBroadcast != INVALID_SOCKET) { + closesocket(m_socBroadcast); + m_socBroadcast = INVALID_SOCKET; + } + closesocket(m_socAcceptListen); + m_socAcceptListen = INVALID_SOCKET; + return false; + } + + if (m_socBroadcast != INVALID_SOCKET) { + // Start timer + + gtimm.AddTimer(this, 50); // Every 500ms, i.e., 1/2 second + + // Broadcast game availability immediately + + OnTimer(0); + } + return true; +} + +void SocTransport::UnadvertiseGame() +{ + // Close connection accepting socket + + closesocket(m_socAcceptListen); + m_socAcceptListen = INVALID_SOCKET; + + // Close broadcast socket + + if (m_socBroadcast != INVALID_SOCKET) { + closesocket(m_socBroadcast); + m_socBroadcast = INVALID_SOCKET; + + // Stop timer + + gtimm.RemoveTimer(this); + } +} + +void SocTransport::OnTimer(long tCurrent) +{ + // If we're Advertising broadcast a SERVERINFO message + + if (m_socBroadcast != INVALID_SOCKET) { + sockaddr_in soca; + soca.sin_family = AF_INET; + +#ifdef IPHONE + soca.sin_addr.s_addr = INADDR_BROADCAST; +#else + soca.sin_addr.S_un.S_addr = INADDR_BROADCAST; +#endif + + ServerInfoNetMessage nm(m_szGameName); + MpTrace("> %s", PszFromNetMessage(&nm)); + + // Broadcast to 4 ports to support running 4 clients on the same machine + + for (int i = 0; i < 4; i++) { + soca.sin_port = htons(SERVERINFO_BROADCAST_PORT + i); + if (sendto(m_socBroadcast, (char *)&nm, sizeof(nm), 0, (sockaddr *)&soca, sizeof(soca)) == SOCKET_ERROR) { +#ifdef DEBUG + HostMessageBox(TEXT("sendto err: %s"), PszFromSocError()); +#endif + } + } + } + + // If we're searching for hosts check for receipt of a SERVERINFO message + + if (m_socBroadcastListen != INVALID_SOCKET) { + NetAddress nad; + +#ifdef IPHONE + socklen_t cbSocaServer = sizeof(nad); +#else + int cbSocaServer = sizeof(nad); +#endif + ServerInfoNetMessage sinm; + if (recvfrom(m_socBroadcastListen, (char *)&sinm, sizeof(sinm), 0, (sockaddr *)&nad, &cbSocaServer) == SOCKET_ERROR) { + int err = WSAGetLastError(); + if (err != WSAEWOULDBLOCK && err != WSAETIMEDOUT) { +#ifdef DEBUG + HostMessageBox(TEXT("recvfrom err: %s"), PszFromSocError(err)); + + // UNDONE: If we discover any errors that would cause us to want to abort the + // game search operation... + +// if (m_ptcb != NULL) +// m_ptcb->OnTransportError(ktraeGameSearchFailed); + +#endif + return; + } + } else { + MpTrace("< GAMEHOSTFOUND"); + + if (m_fBlockNextGameHost) + return; + + // Fill in a NetAddress ourselves so we can be sure no random numbers are present + + NetAddress nadT; + memset(&nadT, 0, sizeof(nadT)); + // HACK: this is the port the game is going to want to connect to + ((sockaddr_in *)&nadT)->sin_port = htons(SERVER_LISTEN_PORT); + ((sockaddr_in *)&nadT)->sin_family = ((sockaddr_in *)&nad)->sin_family; + ((sockaddr_in *)&nadT)->sin_addr = ((sockaddr_in *)&nad)->sin_addr; + + if (m_ptcb != NULL) { + m_fBlockNextGameHost = true; + m_ptcb->OnGameHostFound(&nadT); + } + } + } +} + +bool SocTransport::CanSpecifyAddress() +{ + return true; +} + +bool ValidateIpAddress(const char *psz) secComm; +bool ValidateIpAddress(const char *psz) +{ + dword dwIpAddress = inet_addr(psz); + if (dwIpAddress == INADDR_NONE) { + HtMessageBox(kfMbWhiteBorder, "Input Error", "Invalid Internet address."); + return false; + } + + return true; +} + +bool SocTransport::SpecifyAddress(NetAddress *pnad) +{ +#if 0 + static char *s_apszChars[] = { + "ABCDEFGHIJKLM", + "NOPQRSTUVWXYZ", + "0123456789.-", + }; +#else + static char *s_apszChars[] = { + "123", + "456", + "789", + ".0" + }; +#endif + + char szIpAddress[32]; + if (!DoInputPanelForm(s_apszChars, ARRAYSIZE(s_apszChars), "Address:", "", szIpAddress, sizeof(szIpAddress), ValidateIpAddress)) + return false; + + dword nlIpAddress = inet_addr(szIpAddress); + + sockaddr_in *psoca = (sockaddr_in *)pnad; + psoca->sin_family = AF_INET; + psoca->sin_port = htons(SERVER_LISTEN_PORT); + +#ifdef IPHONE + psoca->sin_addr.s_addr = nlIpAddress; +#else + psoca->sin_addr.S_un.S_addr = nlIpAddress; +#endif + + return true; +} + +//--------------------------------------------------------------------------- +// SocConnection implementation + +SocConnection::SocConnection() +{ + m_soc = INVALID_SOCKET; + + m_fListening = false; + m_fDisconnecting = false; +} + +SocConnection::SocConnection(SOCKET soc) +{ + m_soc = soc; + + m_fListening = false; + m_fDisconnecting = false; +} + +SocConnection::~SocConnection() +{ + if (m_soc != INVALID_SOCKET) + closesocket(m_soc); + gptra->RemoveConnection(this); +} + +bool SocConnection::Poll() +{ + if (m_fDisconnecting) { + m_fDisconnecting = false; + if (m_pccb != NULL) + m_pccb->OnDisconnect(this); + return false; + } + + if (m_soc == INVALID_SOCKET) + return false; + + // Test if any incoming data is pending + + fd_set fds; + FD_ZERO(&fds); + FD_SET(m_soc, &fds); + TIMEVAL tvTimeout; + tvTimeout.tv_sec = 0; + tvTimeout.tv_usec = 0; + fd_set fdsDummy; + FD_ZERO(&fdsDummy); +// MpTrace("m_soc = %d, fds = %ld", (UInt16)m_soc, (UInt32)fds); + int nSelected = select(((int)m_soc) + 1, &fds, &fdsDummy, &fdsDummy, &tvTimeout); + + if (nSelected == SOCKET_ERROR) { +#ifdef DEBUG + HostMessageBox(TEXT("select err: %s"), PszFromSocError()); +#endif + return false; + } + + if (nSelected == 1) { + NetMessage nm; + int cb = recv(m_soc, (char *)&nm, sizeof(NetMessage), 0); +// MpTrace("recv: cb %d", cb); + if (cb != sizeof(NetMessage)) { + HandleRecvError(); + return false; + } + int cbT = BigWord(nm.cb); + NetMessage *pnm = (NetMessage *)new byte[cbT]; + if (pnm == NULL) { + // Data is still pending but we can't do anything about it. + // Follow the usual course of action (HandleRecvError will close the Connection) + + HandleRecvError(); + return false; + } + memcpy(pnm, &nm, sizeof(NetMessage)); + int cbRemaining = cbT - sizeof(NetMessage); + + // UNDONE: this now is a blocking operation until all of the message is + // received. Better would be to accumulate the pieces of the message in + // a temp buffer (SocConnection member) until it is complete, in a non- + // blocking fashion. + + byte *pbT = (byte *)(pnm + 1); + while (cbRemaining != 0) { + cbT = recv(m_soc, (char *)pbT, cbRemaining, 0); + if (cbT == SOCKET_ERROR) { + int err = WSAGetLastError(); + if (err != WSAEWOULDBLOCK) { + delete pnm; + HandleRecvError(); + return false; + } + } + pbT += cbT; + cbRemaining -= cbT; + } + + if (m_pccb != NULL) { + // Before calling OnReceive, order in native byte order + + NetMessageByteOrderSwap(pnm, false); + + MpTrace("< %s", PszFromNetMessage(pnm)); + m_pccb->OnReceive(this, pnm); + } + delete pnm; + } + + return true; +} + +void SocConnection::HandleRecvError() +{ + // Pretty much any error is fatal to a Connection but we'll start + // by casing them out one by one so everything is considered + + int err = WSAGetLastError(); + + // On CE.NET when a peer terminates ungracefully recv will return partial + // messages. We detect this and treat it like any other fatal error. + + if (err < WSABASEERR) + err = 0; + + switch (err) { + case 0: // When the peer gracefully closes its socket + case WSAECONNRESET: // When the peer crashes, etc (ungraceful termination) + case WSAECONNABORTED: + case netErrSocketClosedByRemote: // Couldn't map this to WSACONRESET or WSACONABORTED because they're already mapped to other Palm errors + AsyncDisconnect(); + break; + + // Treat all errors as fatal to the Connection and Disconnect + + default: +#ifdef DEBUG + HostMessageBox(TEXT("recv err: %s"), PszFromSocError(err)); +#endif + AsyncDisconnect(); + break; + } +} + +void SocConnection::HandleSendError() +{ + // Pretty much any error is fatal to a Connection but we'll start + // by casing them out one by one so everything is considered + + int err = WSAGetLastError(); + switch (err) { + case WSAECONNRESET: + AsyncDisconnect(); + break; + + // Treat all errors as fatal to the Connection and Disconnect + + default: +#ifdef DEBUG + HostMessageBox(TEXT("send err: %s"), PszFromSocError(err)); +#endif + AsyncDisconnect(); + break; + } +} + +bool SocConnection::AsyncSend(NetMessage *pnm) +{ + if (m_soc == INVALID_SOCKET) + return false; + + MpTrace("> %s", PszFromNetMessage(pnm)); + + // Before sending, order in network byte order + + int cb = pnm->cb; // nab this before byte-swapping it! + + NetMessageByteOrderSwap(pnm, true); + + // UNDONE: this is synchronous + int cbActual = send(m_soc, (NetBuff)pnm, cb, 0); + + // Swap it back in case of reuse! + + NetMessageByteOrderSwap(pnm, false); + + if (cbActual != cb) { + HandleSendError(); + return false; + } + + return true; +} + +void SocConnection::AsyncDisconnect() +{ + if (m_soc == INVALID_SOCKET) + return; + + shutdown(m_soc, SD_SEND); + closesocket(m_soc); + m_soc = INVALID_SOCKET; + + // Callers assume that the disconnect notification will be async + // so we do a little extra work to satisfy this. + + m_fDisconnecting = true; +} + +// Error strings for Socket errors + +#ifdef DEBUG +const TCHAR *PszFromSocError() +{ + return PszFromSocError(WSAGetLastError()); +} +#endif + +} // namespace wi diff --git a/game/SocTransport.h b/game/SocTransport.h new file mode 100644 index 0000000..6cfe3af --- /dev/null +++ b/game/SocTransport.h @@ -0,0 +1,160 @@ +// It would be better if there were a socket abstraction, that Transport used. +// This way, these platform differences would be kept in the socket +// implementations. + +#ifdef PIL +// On Palm we try to make things look like they do on Windows so we +// can share code. Conveniently Palm provides mappings to Berkeley +// Sockets which gets us most the way there. + +// Map non-Berkeley stuff + +extern "C" { +#include +} + +#define SOCKET NetSocketRef +#define HOSTENT hostent +#define INVALID_SOCKET -1 +#define SOCKET_ERROR -1 + +#define WSAGetLastError() errno +#define WSASetLastError(err) (errno = (err)) +#define closesocket(soc) close(soc) +#define TIMEVAL timeval +#define WSAEWOULDBLOCK EWOULDBLOCK +#define WSAEMSGSIZE EMSGSIZE +#define WSABASEERR 0 +#define WSAECONNRESET ECONNRESET +#define WSAECONNABORTED ECONNABORTED +#define WSAETIMEDOUT netErrTimeout +#define WSAEHOSTUNREACH netErrUnreachableDest +#define WSAECONNREFUSED ECONNREFUSED +#define TRUE 1 + +namespace wi { +typedef void *NetBuff; +} +#endif + +#if defined(IPHONE) +#include +#include +#include +#include +#include +#include +#define SOCKET int +#define HOSTENT hostent +#define INVALID_SOCKET -1 +#define SOCKET_ERROR -1 +#define WSAGetLastError() errno +#define WSASetLastError(err) (errno = (err)) +#define closesocket(soc) close(soc) +#define TIMEVAL timeval +#define WSAEWOULDBLOCK EWOULDBLOCK +#define WSAEMSGSIZE EMSGSIZE +#define WSABASEERR 0 +#define WSAECONNRESET ECONNRESET +#define WSAECONNABORTED ECONNABORTED +#define WSAETIMEDOUT ETIMEDOUT +#define WSAEHOSTUNREACH EHOSTUNREACH +#define WSAECONNREFUSED ECONNREFUSED +#define TRUE 1 +#define SD_SEND SHUT_WR +#define netErrSocketClosedByRemote -1 // this happens when read() returns 0 +namespace wi { +typedef const char *NetBuff; +} +#endif + +#if defined(WIN) || defined(CE) +#include +#define netErrSocketClosedByRemote (WSABASEERR - 1) // won't happen + +namespace wi { +typedef const char *NetBuff; +} +#endif + +namespace wi { + +#define SERVER_LISTEN_PORT 22222 // 0x56ce +#define SERVERINFO_BROADCAST_PORT 22223 // 22223-22226 +#define kcbSendBufferMax 8192 + +class SocConnection; + +class SocTransport : public Transport, Timer // tra +{ +public: + SocTransport(dword dwIpAddress) secComm; + virtual ~SocTransport() secComm; + + // Transport implementation + +// virtual bool Open(); +// virtual void Close(); + virtual Connection *AsyncConnect(NetAddress *pnad) secComm; + virtual void Poll() secComm; + + virtual bool BeginGameSearch() secComm; + virtual void NextGameHost() secComm; + virtual void EndGameSearch() secComm; + virtual bool AdvertiseGame(const char *pszGameName) secComm; + virtual void UnadvertiseGame() secComm; + + virtual bool CanSpecifyAddress() secComm; + virtual bool SpecifyAddress(NetAddress *pnad) secComm; + virtual void GetAddressString(char *pszAddress, int cbMax) secComm; + + // Timer method + + virtual void OnTimer(long tCurrent) secComm; + + // + +protected: + virtual SocConnection *NewConnection(SOCKET soc) secComm; + +protected: + dword m_dwIpAddress; + +private: + bool GetLocalNetAddress(NetAddress *pnad) secComm; + + char m_szGameName[kcbGameName]; + SOCKET m_socBroadcast; + SOCKET m_socBroadcastListen; + SOCKET m_socAcceptListen; + + Connection *m_pconAsyncConnect; + bool m_fAsyncConnectLoopback; + bool m_fBlockNextGameHost; +}; + +class SocConnection : public Connection +{ +public: + SocConnection() secComm; + SocConnection(SOCKET soc) secComm; + ~SocConnection() secComm; + + // Connection implementation + + virtual bool Poll() secComm; + virtual bool AsyncSend(NetMessage *pnm) secComm; + virtual void AsyncDisconnect() secComm; + +private: + void HandleRecvError() secComm; + void HandleSendError() secComm; + + bool m_fListening; + bool m_fDisconnecting; + +protected: + SOCKET m_soc; +}; + +} // namespace wi \ No newline at end of file diff --git a/game/SpInfantry.cpp b/game/SpInfantry.cpp new file mode 100644 index 0000000..867d8fb --- /dev/null +++ b/game/SpInfantry.cpp @@ -0,0 +1,156 @@ +#include "ht.h" + +namespace wi { + +static SpConsts gConsts; + +#if defined(DEBUG_HELPERS) +char *SpInfantryGob::GetName() +{ + return "SpInfantry"; +} +#endif + +static int s_anIdleStripIndices[8] = { 1, 2, 3, 4, 5, 6, 7, 8 }; +static int s_anMovingStripIndices[8] = { 9, 10, 11, 12, 13, 14, 15, 16 }; + +bool SpInfantryGob::InitClass(IniReader *pini) +{ + gConsts.gt = kgtTakeoverSpecialist; + gConsts.ut = kutTakeoverSpecialist; + gConsts.umPrerequisites = kumResearchCenter; + gConsts.upgmPrerequisites = kupgmAdvancedHRC; + + // Initialize the frame indices arrays + + gConsts.anFiringStripIndices = s_anIdleStripIndices; + gConsts.anMovingStripIndices = s_anMovingStripIndices; + gConsts.anIdleStripIndices = s_anIdleStripIndices; + + // Sound effects + + gConsts.sfxImpact = ksfxNothing; + gConsts.sfxFire = ksfxNothing; + gConsts.sfxStructureCaptured = ksfxTakeoverSpecialistStructureCaptured; + + gConsts.sfxcDestroyed = ksfxcInfantryDestroyed; + gConsts.sfxcSelect = ksfxcMajor01Select; + gConsts.sfxcMove = ksfxcMajor01Move; + gConsts.sfxcAttack = ksfxcMajor01Attack; + + return MobileUnitGob::InitClass(&gConsts, pini); +} + +void SpInfantryGob::ExitClass() +{ + MobileUnitGob::ExitClass(&gConsts); +} + +SpInfantryGob::SpInfantryGob() : MobileUnitGob(&gConsts) +{ +} + +bool SpInfantryGob::IsValidTarget(Gob *pgobTarget) +{ + // only active, takeoverable structures that don't belong to us already + + if ((pgobTarget->GetFlags() & (kfGobStructure | kfGobActive)) != (kfGobStructure | kfGobActive) + || IsAlly(pgobTarget->GetSide())) + return false; + + // Takeover returns false if the structure isn't takeoverable + + StructGob *pstru = (StructGob *)pgobTarget; + return pstru->IsTakeoverable(GetOwner()); +} + +bool SpInfantryGob::Fire(UnitGob *puntTarget, WCoord wx, WCoord wy, WCoord wdx, WCoord wdy) +{ + // can takeover if within firing range and structure + // meets the takeover criteria + + if (!IsValidTarget(puntTarget)) + return false; + + // MobileUnitGob handles rotating towards the target and firing delay + + if (!MobileUnitGob::Fire(puntTarget, wx, wy, wdx, wdy)) + return false; + + StructGob *pstru = (StructGob *)puntTarget; + pstru->Takeover(m_pplr); + if (m_pplr == gpplrLocal) + gsndm.PlaySfx(m_pspc->sfxStructureCaptured); + + // SpI only gets to takeover once, destroy ourselves w/o dying + + Deactivate(); + gsmm.PostMsg(kmidDelete, m_gid, m_gid); + + return false; +} + +void SpInfantryGob::Idle() +{ + // 1/4 of the time we pivot left, 1/4 we pivot right, and 1/2 we play the idle + + switch (GetRandom() & 3) { + case 0: + m_dir--; + if (m_dir < 0) + m_dir = 7; + StartAnimation(&m_ani, m_pmuntc->anIdleStripIndices[m_dir], 0, kfAniResetWhenDone); + break; + + case 1: + m_dir++; + if (m_dir > 7) + m_dir = 0; + StartAnimation(&m_ani, m_pmuntc->anIdleStripIndices[m_dir], 0, kfAniResetWhenDone); + break; + + default: + StartAnimation(&m_ani, m_pmuntc->anIdleStripIndices[m_dir], 0, kfAniResetWhenDone); + break; + } +} + +int SpInfantryGob::ProcessStateMachineMessage(State st, Message *pmsg) +{ +BeginStateMachine + // This is needed at global scope to handle the kmidDelete sent by SpInfantryGob::Fire + + OnMsg(kmidDelete) + Delete(); + return knDeleted; + + State(kstDying) + OnEnter + Deactivate(); + m_ff ^= kfGobLayerDepthSorted | kfGobLayerSurfaceDecal; + + gsndm.PlaySfx(SfxFromCategory(m_pmuntc->sfxcDestroyed)); + m_ani.Start("die 3", kfAniIgnoreFirstAdvance); + MarkRedraw(); + + // remove corpse after 10 seconds + + gsmm.SendDelayedMsg(kmidDelete, 1000, m_gid, m_gid); + + OnUpdate + AdvanceAnimation(&m_ani); + + +#if 0 +EndStateMachineInherit(MobileUnitGob) +#else + return knHandled; + } + } else { + return (int)MobileUnitGob::ProcessStateMachineMessage(st, pmsg); + } + return (int)MobileUnitGob::ProcessStateMachineMessage(st, pmsg); +#endif +} + +} // namespace wi \ No newline at end of file diff --git a/game/StateMachine.cpp b/game/StateMachine.cpp new file mode 100644 index 0000000..1130e7a --- /dev/null +++ b/game/StateMachine.cpp @@ -0,0 +1,554 @@ +#include "game/ht.h" +#include "game/stateframe.h" + +namespace wi { + +// Portions Copyright (C) Steve Rabin, 2000 + +// StateMachine methods + +StateMachine::StateMachine() +{ + m_fForceStateChange = false; + m_st = m_stNext = kstReservedGlobal; +#ifdef DEBUG_HELPERS + m_fDebug = false; +#endif +} + +int StateMachine::ProcessStateMachineMessage(State st, Message *pmsg) +{ + return knHandled; +} + +// StateMachineMgr methods + +StateMachineMgr::StateMachineMgr() +{ + m_pdmsgHead = NULL; +} + +StateMachineMgr::~StateMachineMgr() +{ + ClearDelayedMessages(); +} + +bool StateMachineMgr::Init(TimerMgr *ptimm) +{ + m_ptimm = ptimm; + ClearDelayedMessages(); + return true; +} + +#define knVerStateMachineMgrState 1 +bool StateMachineMgr::LoadState(Stream *pstm) +{ + // Version check + + byte nVer = pstm->ReadByte(); + if (nVer != knVerStateMachineMgrState) + return false; + + // Read msg count + + int cmsgs = pstm->ReadWord(); + + // Read in messages, restore delivery time + + long tCurrent = m_ptimm->GetTickCount(); + while (cmsgs-- != 0) { + Message msg; + if (pstm->Read(&msg, sizeof(msg)) == 0) + return false; + dword ctDelivery = pstm->ReadDword(); + msg.tDelivery = tCurrent + ctDelivery; + StoreDelayedMessage(&msg); + } + + return pstm->IsSuccess(); +} + +bool StateMachineMgr::SaveState(Stream *pstm) +{ + // Save version + + pstm->WriteByte(knVerStateMachineMgrState); + + // Save count of messages + + int cmsgs = 0; + DelayedMessage *pdmsg; + for (pdmsg = m_pdmsgHead; pdmsg != NULL; pdmsg = pdmsg->pdmsgNext) + cmsgs++; + pstm->WriteWord(cmsgs); + + // Save messages and time delta remaining for each message. + + long tCurrent = m_ptimm->GetTickCount(); + for (pdmsg = m_pdmsgHead; pdmsg != NULL; pdmsg = pdmsg->pdmsgNext) { + pstm->Write(&pdmsg->msg, sizeof(pdmsg->msg)); + pstm->WriteDword(pdmsg->msg.tDelivery - tCurrent); + } + + return pstm->IsSuccess(); +} + +void StateMachineMgr::ClearDelayedMessages() +{ + DelayedMessage *pdmsg = m_pdmsgHead; + while (pdmsg != NULL) { + DelayedMessage *pdmsgNext = pdmsg->pdmsgNext; + delete pdmsg; + pdmsg = pdmsgNext; + } + m_pdmsgHead = NULL; +} + +void StateMachineMgr::SendMsg(Message *pmsg) +{ + pmsg->tDelivery = 0; + RouteMessage(pmsg); +} + +void StateMachineMgr::SendMsg(MessageId mid, StateMachineId smidReceiver) +{ + Message msg; + memset(&msg, 0, sizeof(msg)); + + msg.mid = mid; + msg.smidReceiver = smidReceiver; + msg.smidSender = ksmidNull; + + RouteMessage(&msg); +} + +void StateMachineMgr::SendMsg(MessageId mid, StateMachineId smidSender, StateMachineId smidReceiver) +{ + Message msg; + memset(&msg, 0, sizeof(msg)); + + msg.mid = mid; + msg.smidSender = smidSender; + msg.smidReceiver = smidReceiver; + + RouteMessage(&msg); +} + +void StateMachineMgr::PostMsg(Message *pmsg) +{ + pmsg->tDelivery = 0; + StoreDelayedMessage(pmsg); +} + +void StateMachineMgr::PostMsg(MessageId mid, StateMachineId smidSender, StateMachineId smidReceiver) +{ + Message msg; + memset(&msg, 0, sizeof(msg)); + + msg.mid = mid; + msg.smidSender = smidSender; + msg.smidReceiver = smidReceiver; + msg.tDelivery = 0; + + StoreDelayedMessage(&msg); +} + +void StateMachineMgr::SendDelayedMsg(Message *pmsg, long tDelay) +{ + pmsg->tDelivery = m_ptimm->GetTickCount() + tDelay; + RouteMessage(pmsg); +} + +void StateMachineMgr::SendDelayedMsg(MessageId mid, long tDelay, StateMachineId smidSender, StateMachineId smidReceiver) +{ + Message msg; + memset(&msg, 0, sizeof(msg)); + + msg.mid = mid; + msg.smidSender = smidSender; + msg.smidReceiver = smidReceiver; + msg.tDelivery = m_ptimm->GetTickCount() + tDelay; + + RouteMessage(&msg); +} + +void StateMachineMgr::RouteMessage(Message *pmsg) +{ + extern int gcMessagesPerUpdate; + gcMessagesPerUpdate++; + +#ifdef TRACKSTATE + // Track that this message has been sent + extern StateFrame *gpframeCurrent; + if (gpframeCurrent != NULL) { + TrackState(gpframeCurrent, pmsg); + } +#endif + + StateMachine *psm = GetStateMachine(pmsg->smidReceiver); + + // If receiver doesn't exist anymore, discard the message + + if (psm == NULL) + return; + + if (pmsg->tDelivery > m_ptimm->GetTickCount()) { + + // This message needs to be stored until it's time to send it + // UNDONE: this can fail due to out of memory + + StoreDelayedMessage(pmsg); + return; + } + + // If SetState() was called outside of ProcessStateMachineMessage() + // handling, change the state before processing this message so that the + // message is processed in the correct state + + if (psm->m_fForceStateChange) + ProcessStateChange(psm); + + // See if this specific state handles this message + +#if 0 // def DEBUG_HELPERS + if (psm->m_fDebug && pmsg->mid != kmidReservedUpdate) + DebugBreak(); +#endif + + int nRet = psm->ProcessMessage(psm->m_st, pmsg); + if (nRet == knDeleted) + return; + + // If not, try the "global" handler + + if (nRet == knNotHandled) + nRet = psm->ProcessMessage(kstReservedGlobal, pmsg); + + // If this state machine has been deleted, no further processing + + if (nRet == knDeleted) + return; + + // Process state change if a state change occured as part of the previous + // message handling. + + if (psm->m_fForceStateChange) + ProcessStateChange(psm); +} + +void StateMachineMgr::ProcessStateChange(StateMachine *psm) +{ +#ifdef DEBUG + int cStateChanges = 0; +#endif + + // Check for a state change + + while (psm->m_fForceStateChange) { + + // NOTE: Circular logic (state changes causing state changes) could cause + // an infinite loop here. We don't attempt to protect against this. + +#ifdef DEBUG + Assert(cStateChanges < 3); + cStateChanges++; +#endif + + // Create a general msg for initializing and cleaning up the state change + + Message msgT; + msgT.smidSender = msgT.smidReceiver = GetId(psm); + msgT.tDelivery = 0; + + psm->m_fForceStateChange = false; + + // Let the last state clean-up. + // NOTE: this purposefully does NOT bubble up to the Global state handler + + msgT.mid = kmidReservedExit; + int nRet = psm->ProcessMessage(psm->m_st, &msgT); + if (nRet == knDeleted) + return; + + // We don't allow Exit handlers to change the state. +// Assert(!psm->m_fForceStateChange); + psm->m_fForceStateChange = false; + + // Set the new state + + psm->m_st = psm->m_stNext; + + // Let the new state initialize + // NOTE: this purposefully does NOT bubble up to the Global state + // handler + + msgT.mid = kmidReservedEnter; + nRet = psm->ProcessMessage(psm->m_st, &msgT); + if (nRet == knDeleted) + return; + } +} + +// This function is called every game tick + +void StateMachineMgr::DispatchDelayedMessages() +{ + DelayedMessage **ppdmsg = &m_pdmsgHead; + + while (*ppdmsg != NULL) { + DelayedMessage *pdmsg = *ppdmsg; + if (pdmsg->msg.tDelivery <= m_ptimm->GetTickCount()) { + *ppdmsg = pdmsg->pdmsgNext; + RouteMessage(&pdmsg->msg); + delete pdmsg; + } else { + ppdmsg = &(*ppdmsg)->pdmsgNext; + } + } +} + +bool StateMachineMgr::StoreDelayedMessage(Message *pmsg) +{ + // Store this message (in some data structure) for later routing + + // A priority queue would be the ideal data structure (but not required) + // to store the delayed messages - Check out Mark Nelson's article + // "Priority Queues and the STL" in the January 1996 Dr. Dobb's Journal + // http://www.dogma.net/markn/articles/pq_stl/priority.htm + + // Note: In main game loop call SendDelayedMessages() every game + // tick to check if it's time to send the stored messages + + DelayedMessage *pdmsg = new DelayedMessage; + Assert(pdmsg != NULL, "out of memory!"); + if (pdmsg == NULL) + return false; + + pdmsg->msg = *pmsg; + pdmsg->pdmsgNext = NULL; + + DelayedMessage **ppdmsg = &m_pdmsgHead; + + while (*ppdmsg != NULL) + ppdmsg = &(*ppdmsg)->pdmsgNext; + *ppdmsg = pdmsg; + + return true; +} + +#ifdef TRACKSTATE + +dword gmpmidQuad[] = { + 'MRNL', // kmidReservedNull, + 'MREN', // kmidReservedEnter, + 'MREX', // kmidReservedExit, + 'MRUP', // kmidReservedUpdate, + 'MHIT', // kmidHit, + 'MNAH', // kmidNearbyAllyHit, + 'MDEL', // kmidDelete, + 'MENB', // kmidEnemyNearby, + 'MPLH', // kmidPowerLowHigh, + 'MMWN', // kmidMoveWaitingNearby, + 'MBUP', // kmidBeingUpgraded, + 'MUPC', // kmidUpgradeComplete, + 'MMVC', // kmidMoveCommand, + 'MATC', // kmidAttackCommand, + 'MANC', // kmidAnimationComplete, + 'MBOC', // kmidBuildOtherCommand, + 'MABO', // kmidAbortBuildOtherCommand, + 'MBDC', // kmidBuildComplete, + 'MFRC', // kmidFireComplete, + 'MSSM', // kmidSpawnSmoke, + 'MSDC', // kmidSelfDestructCommand, + 'MREC', // kmidRepairCommand, + 'MUPC', // kmidUpgradeCommand, + 'MAUC', // kmidAbortUpgradeCommand, + 'MTFC', // kmidTransformCommand, + 'MDLC', // kmidDeliverCommand, + 'MGXD', // kmidGalaxiteDelivery, + 'MMIC', // kmidMineCommand, + 'MMAC', // kmidMoveAction, + 'MAAC', // kmidAttackAction, + 'MGUA', // kmidGuardAction, + 'MGVA', // kmidGuardVicinityAction, + 'MGAA', // kmidGuardAreaAction, + 'MHEA', // kmidHuntEnemiesAction, + 'MFIR', // kmidFire, + 'MHET', // kmidHeal, + 'MSFX', // kmidPlaySfx, + 'PDIS', // kmidPlayerDisconnect, +}; + +void StateMachineMgr::TrackState(StateFrame *frame, Message *pmsg) { + int i = frame->AddCountedValue(gmpmidQuad[pmsg->mid]); + frame->AddValue('SMIS', (dword)pmsg->smidSender, i); + frame->AddValue('SMIR', (dword)pmsg->smidReceiver, i); + + switch (pmsg->mid) { + case kmidHit: + frame->AddValue('GIDA', (dword)pmsg->Hit.gidAssailant, i); + frame->AddValue('SIDA', (dword)pmsg->Hit.sideAssailant, i); + frame->AddValue('NDAM', (dword)pmsg->Hit.nDamage, i); + break; + + case kmidPlaySfx: + // Output 0 so it is always the same, because some units that + // send kmidPlaySfx select the sfx using SfxFromCategory, which + // uses a random # generator that is not in sync with MP. + // bug causing this. + //frame->AddValue('SFX ', (dword)pmsg->PlaySfx.sfx, i); + frame->AddValue('SFX ', 0, i); + break; + + case kmidEnemyNearby: + frame->AddValue('ENNB', (dword)pmsg->EnemyNearby.gidEnemy, i); + break; + + case kmidMoveCommand: + frame->AddValue('GIDT', (dword)pmsg->MoveCommand.gidTarget, i); + frame->AddValue('TRWX', (dword)pmsg->MoveCommand.wptTarget.wx, i); + frame->AddValue('TRWY', (dword)pmsg->MoveCommand.wptTarget.wy, i); + frame->AddValue('TCWX', (dword)pmsg->MoveCommand.wptTargetCenter.wx, i); + frame->AddValue('TCWY', (dword)pmsg->MoveCommand.wptTargetCenter.wy, i); + frame->AddValue('TCRA', (dword)pmsg->MoveCommand.tcTargetRadius, i); + frame->AddValue('MDPU', (dword)pmsg->MoveCommand.wcMoveDistPerUpdate, + i); + break; + + case kmidAttackCommand: + frame->AddValue('GIDT', (dword)pmsg->AttackCommand.gidTarget, i); + frame->AddValue('TRWX', (dword)pmsg->AttackCommand.wptTarget.wx, i); + frame->AddValue('TRWY', (dword)pmsg->AttackCommand.wptTarget.wy, i); + frame->AddValue('TCWX', (dword)pmsg->AttackCommand.wptTargetCenter.wx, + i); + frame->AddValue('TCWY', (dword)pmsg->AttackCommand.wptTargetCenter.wy, + i); + frame->AddValue('TCRA', (dword)pmsg->AttackCommand.tcTargetRadius, i); + frame->AddValue('MDPU', (dword)pmsg->AttackCommand.wcMoveDistPerUpdate, + i); + break; + + case kmidUpgradeCommand: + frame->AddValue('WFUP', (dword)pmsg->UpgradeCommand.wfUpgrade, i); + break; + + case kmidBuildOtherCommand: + frame->AddValue('UNTT', (dword)pmsg->BuildOtherCommand.ut, i); + frame->AddValue('WPWX', (dword)pmsg->BuildOtherCommand.wpt.wx, i); + frame->AddValue('WPWY', (dword)pmsg->BuildOtherCommand.wpt.wy, i); + break; + + case kmidAbortBuildOtherCommand: + frame->AddValue('UNTT', (dword)pmsg->AbortBuildOtherCommand.ut, i); + break; + + case kmidDeliverCommand: + frame->AddValue('GTAR', (dword)pmsg->DeliverCommand.gidTarget, i); + break; + + case kmidMineCommand: + frame->AddValue('GTAR', (dword)pmsg->MineCommand.gidTarget, i); + frame->AddValue('TRWX', (dword)pmsg->MineCommand.wptTarget.wx, i); + frame->AddValue('TRWY', (dword)pmsg->MineCommand.wptTarget.wy, i); + break; + + case kmidGuardAreaAction: + frame->AddValue('AREA', (dword)pmsg->GuardAreaCommand.nArea, i); + break; + + case kmidHuntEnemiesAction: + frame->AddValue('UMSK', (dword)pmsg->HuntEnemiesCommand.um, i); + break; + + case kmidPlayerDisconnect: + frame->AddValue('PID ', (dword)pmsg->PlayerDisconnect.pid, i); + frame->AddValue('REAS', (dword)pmsg->PlayerDisconnect.nReason, i); + break; + } +} +#endif + +#if defined(DEBUG_HELPERS) + +// NOTE: these must be in the same order as the MessageId enum +// Note: Use LABEL macros instead of this + +char *gaszMessageNames[] = { + "Null", + "Enter", + "Exit", + "Update", + + "Hit", + "NearbyAllyHit", + "Delete", + "EnemyNearby", + "PowerLowHigh", + "MoveWaitingNearby", + "BeginUpgraded", + "UpgradeComplete", + "MoveCommand", + "AttackCommand", + "AnimationComplete", + "BuildOtherCommand", + "AbortBuildOtherCommand", + "BuildComplete", + "FireComplete", + "SpawnSmoke", + "SelfDestructCommand", + "RepairCommand", + "UpgradeCommand", + "AbortUpgradeCommand", + "TransformCommand", + "DeliverCommand", + "GalaxiteDelivery", + "MineCommand", + + "MoveAction", + "AttackAction", + "GuardAction", + "GuardVicinityAction", + "GuardAreaAction", + "HuntEnemiesAction", + + "Fire", + "Heal", +}; + +// NOTE: these must be in the same order as the State enum +// Note: Use LABEL macros instead of this + +char *gaszStateNames[] = { + "Null", + "Zombie", + "Global", + + "Guard", + "Move", + "Attack", + "Chase", + "Idle", + "Dying", + "BuildOtherCompleting", + "BeingBuilt", + "HuntEnemies", + + "ProcessorGetMiner", + "ProcessorPutMiner", + "ProcessorTakeGalaxite", + + "MinerMoveToProcessor", + "MinerRotateForEntry", + "MinerMine", + "MinerFindGalaxite", + "MinerFaceGalaxite", + "MinerApproachGalaxite", + "MinerSuck", + "MinerStepAside", + + "ChangeStatePendingFireComplete", + "ContinueActionPendingFireComplete", +}; +#endif + +} // namespace wi diff --git a/game/Struct.cpp b/game/Struct.cpp new file mode 100644 index 0000000..c98bd5f --- /dev/null +++ b/game/Struct.cpp @@ -0,0 +1,1188 @@ +#include "ht.h" +#include "strings.h" + +namespace wi { + +static AnimationData *s_panidStructureExplosion = NULL; +static AnimationData *s_panidBigSmoke = NULL; +static DialogForm *s_pfrmConfirmation = NULL; +TBitmap *StructGob::s_ptbmRepairing = NULL; +TBitmap *StructGob::s_ptbmNeedsPower = NULL; +TBitmap *StructGob::s_ptbmNeedCredits = NULL; + +//=========================================================================== +// StructGob implementation + +bool StructGob::InitClass(StructConsts *pstruc, IniReader *pini) +{ + if (!UnitGob::InitClass(pstruc, pini)) + return false; + + char szTemplate[10]; + itoa(pstruc->gt, szTemplate, 10); + + // Required properties + + if (pini->GetPropertyValue(szTemplate, "TileDimensions", "%d,%d", + &pstruc->ctx, &pstruc->cty) != 2) + return false; + + int nArmorStrength; + if (pini->GetPropertyValue(szTemplate, "ArmorStrength", "%d", &nArmorStrength) != 1) + return false; + Assert(nArmorStrength < 1 << 10); + pstruc->fxArmorStrength = itofx(nArmorStrength); + + // Don't include Headquarters in min/max since they aren't player-buildable + + if (pstruc->gt != kgtHeadquarters) { + gfxStructureArmorStrengthMin = _min(gfxStructureArmorStrengthMin, pstruc->fxArmorStrength); + gfxStructureArmorStrengthMax = _max(gfxStructureArmorStrengthMax, pstruc->fxArmorStrength); + } + + // Optional properties + + if (pini->GetPropertyValue(szTemplate, "ArmorStrengthMP", "%d", &nArmorStrength) != 1) { + pstruc->fxArmorStrengthMP = pstruc->fxArmorStrength; + } else { + Assert(nArmorStrength < 1 << 10); + pstruc->fxArmorStrengthMP = itofx(nArmorStrength); + } + + if (pini->GetPropertyValue(szTemplate, "ReserveDimensions", "%d,%d", + &pstruc->ctxReserve, &pstruc->ctyReserve) != 2) { + pstruc->ctxReserve = pstruc->ctx; + pstruc->ctyReserve = pstruc->cty; + } + + if (pini->GetPropertyValue(szTemplate, "PowerSupply", "%d", &pstruc->nPowerSupply) != 1) + pstruc->nPowerSupply = 0; + gnPowerSupplyMin = _min(gnPowerSupplyMin, pstruc->nPowerSupply); + gnPowerSupplyMax = _max(gnPowerSupplyMax, pstruc->nPowerSupply); + + if (pini->GetPropertyValue(szTemplate, "PowerDemand", "%d", &pstruc->nPowerDemand) != 1) + pstruc->nPowerDemand = 0; + gnPowerDemandMin = _min(gnPowerDemandMin, pstruc->nPowerDemand); + gnPowerDemandMax = _max(gnPowerDemandMax, pstruc->nPowerDemand); + + if (pini->GetPropertyValue(szTemplate, "UpgradeCost", "%d", &pstruc->nUpgradeCost) != 1) + pstruc->nUpgradeCost = 0; + + // Preload the structure's menu form + + if (!LoadMenu(pstruc, pini, szTemplate, kidfStructMenu)) + return false; + + // Preload the confirmation "Are you sure?" dialog + + if (!LoadConfirmation()) { + return false; + } + + // Preload the smoke animation data + + if (s_panidBigSmoke == NULL) { + s_panidBigSmoke = LoadAnimationData("smoke.anir"); + if (s_panidBigSmoke == NULL) + return false; + } + + // Preload the structure explosion animation data + + if (s_panidStructureExplosion == NULL) { + s_panidStructureExplosion = LoadAnimationData("sexplosion.anir"); + if (s_panidStructureExplosion == NULL) + return false; + } + + // Preload the repair symbol bitmap + + if (s_ptbmRepairing == NULL) { + s_ptbmRepairing = LoadTBitmap("repairing_symbol.tbm"); + if (s_ptbmRepairing == NULL) { + Assert("Failed to load repairing_symbol.tbm"); + return false; + } + } + + // Preload the needs power symbol bitmap + + if (s_ptbmNeedsPower == NULL) { + s_ptbmNeedsPower = LoadTBitmap("needs_power_symbol.tbm"); + if (s_ptbmNeedsPower == NULL) { + Assert("Failed to load needs_power_symbol.tbm"); + return false; + } + } + + // Preload the need credits symbol bitmap + + if (s_ptbmNeedCredits == NULL) { + s_ptbmNeedCredits = LoadTBitmap("needs_credits_symbol.tbm"); + if (s_ptbmNeedCredits == NULL) { + Assert("Failed to load need_credits_symbol.tbm"); + return false; + } + } + + // All structures defog a larger area + + pstruc->wf |= kfUntcLargeDefog; + + return true; +} + +void StructGob::ExitClass(StructConsts *pstruc) +{ + delete s_ptbmNeedCredits; + s_ptbmNeedCredits = NULL; + delete s_ptbmNeedsPower; + s_ptbmNeedsPower = NULL; + delete s_ptbmRepairing; + s_ptbmRepairing = NULL; + delete s_panidStructureExplosion; + s_panidStructureExplosion = NULL; + delete s_panidBigSmoke; + s_panidBigSmoke = NULL; + delete s_pfrmConfirmation; + s_pfrmConfirmation = NULL; + + UnitGob::ExitClass(pstruc); +} + +StructGob::StructGob(StructConsts *pstruc) : UnitGob(pstruc) +{ + m_ff |= kfGobStructure; + m_tLastSmoke = 0; + m_nSeqLastVisible = 0; +} + +StructGob::~StructGob() +{ +} + +bool StructGob::Init(WCoord wx, WCoord wy, Player *pplr, fix fxHealth, dword ff, const char *pszName) +{ + if (!UnitGob::Init(wx, wy, pplr, fxHealth, ff, pszName)) + return false; + + // Mark this structure's position as occupied on the terrain map + + TerrainMap *ptrmap = gsim.GetLevel()->GetTerrainMap(); + TCoord tx = TcFromWc(m_wx); + TCoord ty = TcFromWc(m_wy); + Assert(tx >= 0 && ty >= 0 && tx + m_pstruc->ctx <= ptrmap->GetWidth() && ty + m_pstruc->cty <= ptrmap->GetHeight(), + "%s out of map bounds", m_pstruc->szName); + ptrmap->SetFlags(tx, ty, m_pstruc->ctx, m_pstruc->cty, kbfStructure); + Assert(tx + m_pstruc->ctxReserve <= ptrmap->GetWidth() && ty + m_pstruc->ctyReserve <= ptrmap->GetHeight(), + "%s reserve out of map bounds", m_pstruc->szName); + ptrmap->SetFlags(tx, ty, m_pstruc->ctxReserve, m_pstruc->ctyReserve, kbfReserved); + + // Shadow this structure's occupation in the gid map + + ggobm.ShadowGob(this, TcFromWc(m_wx), TcFromWc(m_wy), m_pstruc->ctx, m_pstruc->cty); + + // Redraw this part of the minimap + + TRect trc; + GetTileRect(&trc); + gpmm->RedrawTRect(&trc); + + if (ff & kfGobBeingBuilt) { + SetState(kstBeingBuilt); + } else { + Activate(); + SetState(kstIdle); + } + + return true; +} + +void StructGob::Delete() +{ + // Mark this structure's position as unoccupied on the terrain map + + gsim.GetLevel()->GetTerrainMap()->ClearFlags(TcFromWc(m_wx), TcFromWc(m_wy), + m_pstruc->ctx, m_pstruc->cty, kbfStructure); + gsim.GetLevel()->GetTerrainMap()->ClearFlags(TcFromWc(m_wx), TcFromWc(m_wy), + m_pstruc->ctxReserve, m_pstruc->ctyReserve, kbfReserved); + + // Unshadow this structure's occupation in the gid map + + ggobm.UnshadowGob(this, TcFromWc(m_wx), TcFromWc(m_wy), m_pstruc->ctx, m_pstruc->cty); + + // Remove from Gid map and force minimap redraw + + UnitGob::Delete(); +} + +#define knVerStructGobState 3 +bool StructGob::LoadState(Stream *pstm) +{ + byte nVer = pstm->ReadByte(); + if (nVer != knVerStructGobState) + return false; + dword ctLastSmoke = pstm->ReadDword(); + m_tLastSmoke = gsim.GetTickCount() - ctLastSmoke; + + if (UnitGob::LoadState(pstm)) { + ggobm.ShadowGob(this, TcFromWc(m_wx), TcFromWc(m_wy), m_pstruc->ctx, m_pstruc->cty); + if (m_wfUnit & kfUnitNeedCredits) + WaitingForCredits(true, true); + return true; + } + return false; +} + +bool StructGob::SaveState(Stream *pstm) +{ + pstm->WriteByte(knVerStructGobState); + pstm->WriteDword(gsim.GetTickCount() - m_tLastSmoke); + return UnitGob::SaveState(pstm); +} + +// are you sure dialog used to confirm structure recycling + +bool StructGob::LoadConfirmation() +{ + if (s_pfrmConfirmation != NULL) + return true; + + s_pfrmConfirmation = new DialogForm(); + if (s_pfrmConfirmation == NULL) + return false; + + if (!s_pfrmConfirmation->Init(gpmfrmm, gpiniForms, kidfAreYouSure)) + return false; + + s_pfrmConfirmation->SetFlags(s_pfrmConfirmation->GetFlags() | kfFrmAutoTakedown); + + gpmfrmm->RemoveForm(s_pfrmConfirmation); + return true; +} + +bool StructGob::PopupConfirmation(char *pszTitle) +{ + Assert(s_pfrmConfirmation != NULL); + + // set the title color here so it will be correct for the gob we're + // popping off of + + s_pfrmConfirmation->SetTitleColor(GetSideColor(GetSide())); + + // set the position to be near the gob like the menu is + + Rect rc; + s_pfrmConfirmation->GetRect(&rc); + int cx = rc.Width(); + int cy = rc.Height(); + Size sizPlayfield; + ggame.GetPlayfieldSize(&sizPlayfield); + + WPoint wpt; + GetCenter(&wpt); + WCoord wxViewOrigin, wyViewOrigin; + gsim.GetViewPos(&wxViewOrigin, &wyViewOrigin); + + int xDialog = PcFromWc(wpt.wx - wxViewOrigin) - (cx / 2); + int yDialog = PcFromWc(wpt.wy - wyViewOrigin) - (cy / 2); + if (xDialog < 0) + xDialog = 0; + else if (xDialog >= sizPlayfield.cx - cx) + xDialog = sizPlayfield.cx - cx; + if (yDialog < 0) + yDialog = 0; + else if (yDialog >= sizPlayfield.cy - cy) + yDialog = sizPlayfield.cy - cy; + rc.Offset(xDialog - rc.left, yDialog - rc.top); + s_pfrmConfirmation->SetRect(&rc); + + // set the title + + LabelControl *pctl = (LabelControl *)s_pfrmConfirmation->GetControlPtr(kidcTitle); + pctl->SetText(pszTitle); + + // Show the dialog and get the user's selection + + s_pfrmConfirmation->SetUserDataPtr((void*) this); + gpmfrmm->AddForm(s_pfrmConfirmation); + int idc; + s_pfrmConfirmation->DoModal(&idc); + gpmfrmm->RemoveForm(s_pfrmConfirmation); + return idc == kidcOk; +} + +void StructGob::TakedownConfirmation() +{ + Assert(s_pfrmConfirmation != NULL); + if ((StructGob *)s_pfrmConfirmation->GetUserDataPtr() == this) + s_pfrmConfirmation->EndForm(kidcCancel); +} + +void StructGob::Activate() +{ + // Now this structure can influence global power + + m_pplr->AddPowerSupplyAndDemand(m_pstruc->nPowerSupply, m_pstruc->nPowerDemand); + + // Add to area lists + + AreaMask amNew = ggobm.CalcAreaMask(TcFromWc(m_wx), TcFromWc(m_wy), m_pstruc->ctx, m_pstruc->cty); + ggobm.MoveGobBetweenAreas(m_gid, 0, amNew); + + // Call base + + UnitGob::Activate(); +} + +void StructGob::Deactivate() +{ + WaitingForCredits(false); + m_wfUnit &= ~(kfUnitRepairing | kfUnitDrawRepairingSymbol | kfUnitDrawNeedsPowerSymbol); + EnableOrDisableSymbolLayer(); + + // Remove this structure's global power influence + + m_pplr->AddPowerSupplyAndDemand(-m_pstruc->nPowerSupply, -m_pstruc->nPowerDemand); + + Assert(s_pfrmConfirmation != NULL); + if ((StructGob *)s_pfrmConfirmation->GetUserDataPtr() == this) + s_pfrmConfirmation->EndForm(kidcCancel); + + // Remove from area lists + + AreaMask amOld = ggobm.CalcAreaMask(TcFromWc(m_wx), TcFromWc(m_wy), m_pstruc->ctx, m_pstruc->cty); + ggobm.MoveGobBetweenAreas(m_gid, amOld, 0); + + // Call base + + UnitGob::Deactivate(); +} + +void StructGob::GetTileRect(TRect *ptrc) +{ + ptrc->left = TcFromWc(m_wx); + ptrc->top = TcFromWc(m_wy); + ptrc->right = ptrc->left + m_pstruc->ctx; + ptrc->bottom = ptrc->top + m_pstruc->cty; +} + +void StructGob::GetTilePaddedWRect(WRect *pwrc) +{ + pwrc->left = WcTrunc(m_wx); + pwrc->top = WcTrunc(m_wy); + pwrc->right = pwrc->left + WcFromTc(m_pstruc->ctx); + pwrc->bottom = pwrc->top + WcFromTc(m_pstruc->cty); +} + +bool StructGob::IsAccessible(TCoord tx, TCoord ty) +{ + // tx, ty is in the structure "somewhere". See if it is accessible (a tile somewhere around it that is free + // of terrain and free of structures + + TerrainMap *ptrmap = gsim.GetLevel()->GetTerrainMap(); + TCoord ctx, cty; + ggobm.GetMapSize(&ctx, &cty); + for (Direction dir = 0; dir < 8; dir++) { + TCoord txT = tx + g_mpDirToDx[dir]; + TCoord tyT = ty + g_mpDirToDy[dir]; + if (txT < 0 || txT >= ctx || tyT < 0 || tyT >= cty) + continue; + if (!ptrmap->IsBlocked(txT, tyT, kbfStructure)) + return true; + } + return false; +} + +void StructGob::GetAttackPoint(WPoint *pwpt) +{ + // Need to get a terrain accessible point on this structure that can be attacked. + // This tile needs to be free of blocking terrain and blocking structures. + // If there is none at least get the closest to the enemy. + + // Don't use GetCenter() as that uses the UI rect. We want the structure rect which gaurantees + // a coordinate whose tile is occupied by this structure + + WPoint wptExtent; + wptExtent.wx = WcFromTc(TcFromWc(m_wx) + m_pstruc->ctx); + wptExtent.wy = WcFromTc(TcFromWc(m_wy) + m_pstruc->cty); + + pwpt->wx = WcTrunc(m_wx + (wptExtent.wx - m_wx) / 2) + kwcTileHalf; + pwpt->wy = WcTrunc(m_wy + (wptExtent.wy - m_wy) / 2) + kwcTileHalf; + + TCoord txTest = TcFromWc(pwpt->wx); + TCoord tyTest = TcFromWc(pwpt->wy); + if (IsAccessible(txTest, tyTest)) + return; + + // Otherwise find a spot that is accessible + + TCoord tx = TcFromWc(m_wx); + TCoord ty = TcFromWc(m_wy); + for (TCoord tyT = ty; tyT < ty + m_pstruc->cty; tyT++) { + for (TCoord txT = tx; txT < tx + m_pstruc->ctx; txT++) { + if (txT != txTest && tyT != tyTest) { + if (IsAccessible(txT, tyT)) { + pwpt->wx = WcFromTc(txT) + kwcTileHalf; + pwpt->wy = WcFromTc(tyT) + kwcTileHalf; + return; + } + } + } + } + + // Couldn't find anything that is "accessible" unfortunately. Try to find something close by + // that is accessible + + FindNearestFreeTile(TcFromWc(pwpt->wx), TcFromWc(pwpt->wy), pwpt, kbfStructure); +} + +void StructGob::DrawSymbol(TBitmap *ptbm, DibBitmap *pbm, int xViewOrigin, int yViewOrigin) +{ + int x = -xViewOrigin + PcFromWc(m_wx); + int y = -yViewOrigin + PcFromWc(m_wy); + + Rect rcT; + rcT.FromWorldRect(&m_puntc->wrcUIBounds); + Size sizT; + ptbm->GetSize(&sizT); + ptbm->BltTo(pbm, x + (rcT.Width() - sizT.cx) / 2, y + (rcT.Height() - sizT.cy) / 2); +} + +void StructGob::Draw(DibBitmap *pbm, int xViewOrigin, int yViewOrigin, int nLayer) +{ + switch (nLayer) { + case knLayerMiniMap: + { + int iclr; + if (m_ff & kfGobSelected) + iclr = kiclrWhite; + else + iclr = GetSideColor(m_pplr->GetSide()); + + int cxy = gsim.GetMiniMapScale(); + pbm->Fill(xViewOrigin + MmcFromWc(m_wx), yViewOrigin + MmcFromWc(m_wy), m_pstruc->ctx * cxy, m_pstruc->cty * cxy, + GetColor(iclr)); + } + return; + + case knLayerSymbols: + + // only one symbol shows at a time. Low Power can alternate with repairing + // or with need credits, but repairing and need credits are mutually exclusive + + UnitGob::Draw(pbm, xViewOrigin, yViewOrigin, nLayer); + if (m_wfUnit & kfUnitDrawNeedsPowerSymbol) { + DrawSymbol(s_ptbmNeedsPower, pbm, xViewOrigin, yViewOrigin); + } else { + if (m_wfUnit & kfUnitDrawRepairingSymbol) { + DrawSymbol(s_ptbmRepairing, pbm, xViewOrigin, yViewOrigin); + } else if (m_wfUnit & kfUnitDrawNeedCreditsSymbol) { + DrawSymbol(s_ptbmNeedCredits, pbm, xViewOrigin, yViewOrigin); + } + } + break; + + case knLayerSelection: + if ((m_ff & (kfGobSelected | kfGobBeingBuilt)) == kfGobBeingBuilt) { + Rect rcT; + rcT.FromWorldRect(&m_puntc->wrcUIBounds); + rcT.Offset(-xViewOrigin + PcFromWc(m_wx), -yViewOrigin + PcFromWc(m_wy)); + DrawHealthIndicator(pbm, &rcT, m_fxHealth, m_puntc->GetArmorStrength()); + return; + } + + // fall through + + default: + UnitGob::Draw(pbm, xViewOrigin, yViewOrigin, nLayer); + break; + } +} + +void StructGob::DefUpdate() +{ + // If we're repairing we'll skip no updates, even if blocked on credits. If we're blocked + // on low power then we'll skip till we get to the next symbol flash. However, most of the time + // we will be none of these, so GetUpdateCount and do the math for cupdUntilFlash inside the tests + // for symbol situations rather than on every single update. + + long cUpdates; + long cupdUntilFlash; + + if ((m_puntc->wf & kfUntcNotifyPowerLowHigh) && m_pplr->IsPowerLow() && (m_ff & kfGobActive)) { + cUpdates = gsim.GetUpdateCount(); + cupdUntilFlash = kcupdSymbolFlashRate - (cUpdates % kcupdSymbolFlashRate); + + if (cupdUntilFlash == kcupdSymbolFlashRate) { + + // Low Power will flash ON for odd intervals + + if ((short)(cUpdates / kcupdSymbolFlashRate) & 1) + m_wfUnit |= kfUnitDrawNeedsPowerSymbol; + else + m_wfUnit &= ~kfUnitDrawNeedsPowerSymbol; + + EnableOrDisableSymbolLayer(); + if (gwfPerfOptions & kfPerfSymbolFlashing) + MarkRedraw(); + } + m_unvl.MinSkip(cupdUntilFlash - 1); + } + + // If repairing and sufficient credits are available + + if (m_wfUnit & kfUnitRepairing) { + cUpdates = gsim.GetUpdateCount(); + cupdUntilFlash = kcupdSymbolFlashRate - (cUpdates % kcupdSymbolFlashRate); + + long nCreditsHave = m_pplr->GetCredits(); + + if (cupdUntilFlash == kcupdSymbolFlashRate) { + if (nCreditsHave > 0) { + + // Repair will flash OFF for odd intervals + + if ((short)(cUpdates / kcupdSymbolFlashRate) & 1) + m_wfUnit &= ~kfUnitDrawRepairingSymbol; + else + m_wfUnit |= kfUnitDrawRepairingSymbol; + + } else { + m_wfUnit &= ~kfUnitDrawRepairingSymbol; + } + EnableOrDisableSymbolLayer(); + if (gwfPerfOptions & kfPerfSymbolFlashing) + MarkRedraw(); + } + + fix fxArmorStrength = m_pstruc->GetArmorStrength(); + if (m_fxHealth < fxArmorStrength) { + + // n health unit costs m credits to repair + + int nCreditsNeeded = knCreditsPerRepairUpdate; + + // Take whatever is left (repair on a discount!) + + if (nCreditsHave != 0 && nCreditsHave < nCreditsNeeded) + nCreditsNeeded = nCreditsHave; + + if (nCreditsHave >= nCreditsNeeded) { + + fix fxHealth = addfx(m_fxHealth, kfxHealthUnitsRepairedPerUpdate); + m_pplr->SetCredits(nCreditsHave - nCreditsNeeded, true, knConsumerRepair); + + // make sure we're not in need credits mode + + WaitingForCredits(false); + + // At full strength? If so, stop repairing + + if (fxHealth >= fxArmorStrength) { + fxHealth = fxArmorStrength; + m_wfUnit &= ~(kfUnitRepairing | kfUnitDrawRepairingSymbol); + Assert(!(m_wfUnit & kfUnitNeedCredits), "finished a repair without credits?"); + EnableOrDisableSymbolLayer(); + MarkRedraw(); + } + SetHealth(fxHealth); + } else { + WaitingForCredits(true); + } + } else { + m_wfUnit &= ~(kfUnitRepairing | kfUnitDrawRepairingSymbol); + Assert(!(m_wfUnit & kfUnitNeedCredits), "finished a repair without credits?"); + EnableOrDisableSymbolLayer(); + MarkRedraw(); + } + + // when repairing we need every update to do our repair increments or + // to check for a fresh cash infusion (there is no credit change + // notification and because building can block w/o credits at zero it's + // too complex for now) + + m_unvl.MinSkip(); + } + + if (m_wfUnit & kfUnitNeedCredits) { + cUpdates = gsim.GetUpdateCount(); + cupdUntilFlash = kcupdSymbolFlashRate - (cUpdates % kcupdSymbolFlashRate); + + if (cupdUntilFlash == kcupdSymbolFlashRate) { + + // NeedCredits will flash OFF for odd intervals + + if ((short)(cUpdates / kcupdSymbolFlashRate) & 1) + m_wfUnit &= ~kfUnitDrawNeedCreditsSymbol; + else + m_wfUnit |= kfUnitDrawNeedCreditsSymbol; + + EnableOrDisableSymbolLayer(); + if (gwfPerfOptions & kfPerfSymbolFlashing) + MarkRedraw(); + } + + // again, we have no credit infusion update, and so far all things that + // can block on credits get every update while credit consuming + + m_unvl.MinSkip(); + } + + UnitGob::DefUpdate(); +} + +void StructGob::EnableOrDisableSymbolLayer() +{ + dword ffOld = m_ff; + + if (!IsAlly(gpplrLocal->GetSide()) && ((gpplrLocal->GetHandicap() & kfHcapShowEnemyResourceStatus) == 0)) + m_wfUnit &= ~(kfUnitDrawNeedsPowerSymbol | kfUnitDrawNeedCreditsSymbol); + + if (m_wfUnit & (kfUnitDrawRepairingSymbol | kfUnitDrawNeedsPowerSymbol | kfUnitDrawNeedCreditsSymbol)) { + m_ff |= kfGobLayerSymbols; + } else { + m_ff &= ~kfGobLayerSymbols; + } + + // Changed? + + if ((ffOld ^ m_ff) & kfGobLayerSymbols) { + if (gwfPerfOptions & kfPerfSymbolFlashing) + MarkRedraw(); + } +} + +bool StructGob::NeedsRepair() +{ + return m_fxHealth < m_pstruc->GetArmorStrength(); +} + +bool StructGob::IsTakeoverable(Player *pplr) +{ + // Can only takeover if the player has limit space + + if (!ggobm.IsBelowLimit(knLimitStruct, pplr)) { + if (pplr == gpplrLocal) + ShowAlert(kidsBuildingLimitReached); + return false; + } + + if (m_ff & kfGobActive) + return true; + return false; +} + +void StructGob::Takeover(Player *pplr) +{ + // Removes any player-specific influence this Structure has + + Deactivate(); + + // Change owners + + Assert(ggobm.IsBelowLimit(knLimitStruct, pplr)); + ggobm.TrackGobCounts(this, false); + m_pplr->IncStructuresLost(); + SetOwner(pplr); + m_pplr->IncEnemyStructuresKilled(); + ggobm.TrackGobCounts(this, true); + + // Applies any player-specific influence this Structure has + + Activate(); + + // Reveal fog around this newly owned structure + + if (pplr == gpplrLocal) { + WCoord wxView, wyView; + gsim.GetViewPos(&wxView, &wyView); + FogMap *pfogm = gsim.GetLevel()->GetFogMap(); + WPoint wpt; + GetCenter(&wpt); + RevealPattern *prvlp = (RevealPattern *)(m_pstruc->wf & kfUntcLargeDefog ? grvlpLarge : grvlp); + pfogm->Reveal(TcFromWc(wpt.wx), TcFromWc(wpt.wy), prvlp, gpupdSim, wxView, wyView); + } + + // force an update so it gets in sync on lowpower/credits etc + + m_unvl.MinSkip(); + + // Redraw this part of the minimap + + TRect trc; + GetTileRect(&trc); + gpmm->RedrawTRect(&trc); +} + +// pplr is used for the replicator do do a custom override + +void StructGob::WaitingForCredits(bool fNeed, bool fOverride, Player *pplr) +{ + // override is for LoadState + + if ((fNeed == ((m_wfUnit & kfUnitNeedCredits) == kfUnitNeedCredits)) && !fOverride) + return; + + int cDelta; + if (fNeed) { + m_wfUnit |= kfUnitNeedCredits; + cDelta = 1; + } else { + m_wfUnit &= ~( kfUnitDrawNeedCreditsSymbol | kfUnitNeedCredits ); + EnableOrDisableSymbolLayer(); + if (gwfPerfOptions & kfPerfSymbolFlashing) + MarkRedraw(); + cDelta = -1; + } + + // track how many buildings need credits in the player so the local player can reflect + // that need in the UI + + if (pplr == NULL) + pplr = m_pplr; + pplr->ModifyNeedCreditsCount(cDelta); + + return; +} + +void StructGob::OnPowerLowHigh() +{ + if (!GetOwner()->IsPowerLow()) { + m_wfUnit &= ~kfUnitDrawNeedsPowerSymbol; + EnableOrDisableSymbolLayer(); + MarkRedraw(); + } else { + + // force an update to start symbol flashing + + m_unvl.MinSkip(); + } +} + +void StructGob::Repair(bool fOn) +{ + if (fOn) { + // If already at max health, don't waste any more time + + if (m_fxHealth == m_pstruc->GetArmorStrength()) + return; + + // toggle on repair state. Icon will be shown in update + + m_wfUnit |= kfUnitRepairing; + + if (m_pplr == gpplrLocal) { + if (m_pplr->GetCredits() > 0) + gsndm.PlaySfx(m_pstruc->sfxRepair); + } + + } else { + m_wfUnit &= ~(kfUnitRepairing | kfUnitDrawRepairingSymbol | kfUnitDrawNeedCreditsSymbol); + WaitingForCredits(false); + if (m_pplr == gpplrLocal) + gsndm.PlaySfx(m_pstruc->sfxAbortRepair); + } + + m_unvl.MinSkip(); +} + +bool StructGob::IsValidTarget(Gob *pgobTarget) +{ + // most structures cannot attack. Towers can so they override this. + + return false; +} + +void StructGob::InitMenu(Form *pfrm) +{ + ButtonControl *pbtn = (ButtonControl *)pfrm->GetControlPtr(kidcRepair); + pbtn->Enable(NeedsRepair()); + pbtn->Show((m_wfUnit & kfUnitRepairing) == 0); + + pbtn = (ButtonControl *)pfrm->GetControlPtr(kidcAbortRepair); + pbtn->Show((m_wfUnit & kfUnitRepairing) != 0); +} + +void StructGob::OnMenuItemSelected(int idc) +{ + switch (idc) { + + // RepairCommand acts as a toggle + + case kidcRepair: + case kidcAbortRepair: + gcmdq.Enqueue(kmidRepairCommand, m_gid); + break; + + case kidcSelfDestruct: + if (PopupConfirmation("SELL BUILDING")) + gcmdq.Enqueue(kmidSelfDestructCommand, m_gid); + break; + } +} + +void StructGob::SelfDestruct(bool fRecycleValue) +{ + // Refund half the value of the structure if recycling + + if (fRecycleValue) { + m_pplr->SetCredits(m_pplr->GetCredits() + (m_pstruc->GetCost() / 2), true); + + // Only do this for 'sold' structures. Structures lost while being built don't count as 'lost' + + m_pplr->IncStructuresLost(); + } + SetState(kstDying); +} + +void StructGob::SetHealth(fix fxHealth) +{ + UnitGob::SetHealth(fxHealth); + + if (m_ff & kfGobBeingBuilt) + return; + + if (m_fxHealth * 2 < m_pstruc->GetArmorStrength()) { + if (m_ani.GetStrip() != 1) + StartAnimation(&m_ani, 1, 0, m_ani.GetFlags()); + } else { + if (m_ani.GetStrip() != 0) + StartAnimation(&m_ani, 0, 0, m_ani.GetFlags()); + } +} + +ScorchGob *StructGob::GetScorchGob() +{ + if (ggobm.IsBelowLimit(knLimitScorch)) + return new ScorchGob; + + // Find the oldest scorch and delete it + + int nSequenceOldest = 0x7fff; + ScorchGob *pgobScorchOldest = NULL; + for (Gob *pgobT = ggobm.GetFirstGob(); pgobT != NULL; pgobT = ggobm.GetNextGob(pgobT)) { + if (pgobT->GetType() != kgtScorch) + continue; + ScorchGob *pgobScorch = (ScorchGob *)pgobT; + int nSequence = pgobScorch->GetSequence(); + if (nSequence < nSequenceOldest) { + nSequenceOldest = nSequence; + pgobScorchOldest = pgobScorch; + } + } + + Assert(pgobScorchOldest != NULL); + if (pgobScorchOldest != NULL) { + ggobm.RemoveGob(pgobScorchOldest); + delete pgobScorchOldest; + } + + return new ScorchGob; +} + +int StructGob::ProcessStateMachineMessage(State st, Message *pmsg) +{ +BeginStateMachine + OnMsg(kmidPowerLowHigh) + OnPowerLowHigh(); + + OnMsg(kmidHit) + int fxHealthPrev = m_fxHealth; + + // apply damage + + fix fxDamage = itofx(pmsg->Hit.nDamage); + if (m_pplr->GetHandicap() & kfHcapIncreasedArmor) + fxDamage = (fix)mulfx(fxDamage, (itofx(knDecreasedDamagePercent) / 100)); + SetHealth(subfx(m_fxHealth, fxDamage)); + if (m_fxHealth <= 0) { + Player *pplr = gplrm.GetPlayer(pmsg->Hit.sideAssailant); + pplr->IncEnemyStructuresKilled(); + m_pplr->IncStructuresLost(); + + SetState(kstDying); + + } else { + ShowDamageIndicator(); + + // If health is dipping into the severely damaged zone, fire off + // a couple smoke animations. + + if (fxHealthPrev * 2 >= m_pstruc->GetArmorStrength() && m_fxHealth * 2 < m_pstruc->GetArmorStrength()) { + + // Play damaged sound + + gsndm.PlaySfx(m_pstruc->sfxDamaged); + + if (gsim.GetTickCount() - m_tLastSmoke > 200) { + int cSmokes = (GetRandom() % 3) + 2; + if (m_pstruc->ctx == 1 || m_pstruc->cty == 1) + cSmokes /= 2; + for (int i = 0; i < cSmokes; i++) + gsmm.SendDelayedMsg(kmidSpawnSmoke, GetRandom() & 63, m_gid, m_gid); + + m_tLastSmoke = gsim.GetTickCount(); + } + } + } + + // Remember that a structure has been attacked + + m_pplr->SetFlags(m_pplr->GetFlags() | kfPlrStructureAttacked); + + // Notify nearby allies that we've been hit! CONSIDER: pass in an expanded range? + + NotifyNearbyAlliesOfHit(pmsg->Hit.gidAssailant); + + OnMsg(kmidSpawnSmoke) + if (gwfPerfOptions & kfPerfSmoke) { + if (ggobm.IsBelowLimit(knLimitSupport)) { + SmokeGob *pgob = new SmokeGob((GetRandom() & 3) + 4); + Assert(pgob != NULL, "out of memory!"); + if (pgob != NULL) { + WCoord wcx = WcFromTc(m_pstruc->ctx); + WCoord wcy = WcFromTc(m_pstruc->cty) - kwcTileHalf; + WCoord wdx = GetRandom() % wcx; + WCoord wdy = (GetRandom() % wcy) + WcFromTile16ths(12); + WCoord wxSmoke = m_wx + wdx; + WCoord wySmoke = m_wy + wdy; + + Size wsizMap; + gsim.GetLevel()->GetTileMap()->GetTCoordMapSize(&wsizMap); + wsizMap.cx = WcFromTc(wsizMap.cx); + wsizMap.cy = WcFromTc(wsizMap.cy); + + if (wxSmoke < 0) + wxSmoke = 0; + if (wxSmoke >= wsizMap.cx) + wxSmoke = wsizMap.cx - 1; + if (wySmoke >= wsizMap.cy) + wySmoke = wsizMap.cy - 1; + + if (!pgob->Init(wxSmoke, wySmoke, m_gid)) + delete pgob; + } + } + } + + OnMsg(kmidSelfDestructCommand) + + // assume we only get this message when user has + // initiated a recycle. Game can call the method. + SelfDestruct(); + + OnMsg(kmidRepairCommand) + + // Toggle repairing + + Repair(!(m_wfUnit & kfUnitRepairing)); + + OnMsg(kmidDelete) + Assert("Shouldn't receive kmidDelete when not in kstDying state"); + + //----------------------------------------------------------------------- + + State(kstBeingBuilt) + OnEnter + m_ff |= kfGobLayerSelection; + + OnExit + m_ff &= ~kfGobLayerSelection; + + OnMsg(kmidBuildComplete) + Activate(); + SetState(kstIdle); + + OnUpdate + // Don't advance animation + + DefUpdate(); + + //----------------------------------------------------------------------- + + State(kstIdle) + OnEnter + // Play idle animation, chosen based on the health of the Structure + + int nStrip; + if (m_fxHealth == 0) + nStrip = 2; // destroyed + else if (m_fxHealth * 2 < m_pstruc->GetArmorStrength()) + nStrip = 1; // damaged + else + nStrip = 0; // healthy + StartAnimation(&m_ani, nStrip, 0, kfAniIgnoreFirstAdvance | kfAniLoop); + + OnUpdate + AdvanceAnimation(&m_ani); + DefUpdate(); + + //----------------------------------------------------------------------- + + State(kstDying) + OnEnter + // if you're destroyed during building it's possible to get here unactivated + + if (m_ff & kfGobActive) + Deactivate(); + m_ff ^= kfGobLayerDepthSorted | kfGobLayerSurfaceDecal; + + gsndm.PlaySfx(m_pstruc->sfxDestroyed); + StartAnimation(&m_ani, 2, 0, kfAniIgnoreFirstAdvance); // UNDONE: hardcoded + + // dont remove ourselves from the "occupied" map until it's all over + // so troops can't run across this freshly destroyed area while it's hot (they have cheap boots) + + gsmm.SendDelayedMsg(kmidDelete, 800, m_gid, m_gid); + + // Show a huge explosion! + + WCoord wcx = WcFromTc(m_pstruc->ctx); + WCoord wcy = WcFromTc(m_pstruc->cty) + kwcTileHalf; + Gob *pgobExpl = CreateAnimGob(m_wx + (wcx / 2), m_wy + (wcy / 2), kfAnmDeleteWhenDone | kfAnmSmokeFireLayer, NULL, + s_panidStructureExplosion); + if (pgobExpl != NULL) + pgobExpl->SetOwner(m_pplr); + + // Show some smoke (in the near future) + + int cSmokes = (GetRandom() % 3) + 1; + int i; + for (i = 0; i < cSmokes; i++) + gsmm.SendDelayedMsg(kmidSpawnSmoke, GetRandom() & 63, m_gid, m_gid); + + // Lay down a random smattering of scorch marks + + if (gwfPerfOptions & kfPerfScorchMarks) { + int cScorches = (GetRandom() & 3) + 1; + WCoord wcxScorchable = WcFromTc(m_pstruc->ctx); + WCoord wcyScorchable = WcFromTc(m_pstruc->cty); + + Size wsizMap; + gsim.GetLevel()->GetTileMap()->GetTCoordMapSize(&wsizMap); + wsizMap.cx = WcFromTc(wsizMap.cx); + wsizMap.cy = WcFromTc(wsizMap.cy); + + for (i = 0; i < cScorches; i++) { + + // Pick a random scorch type to lay down but make sure it is + // one that is smaller than the structure leaving it. + + Size sizScorch; + WCoord wcxScorch, wcyScorch; + int nScorch; + do { + nScorch = GetRandom() % (sizeof(gaptbmScorches) / sizeof(TBitmap *)); + gaptbmScorches[nScorch]->GetSize(&sizScorch); + wcxScorch = WcFromUpc(sizScorch.cx); + wcyScorch = WcFromUpc(sizScorch.cy); + } while (wcxScorch > wcxScorchable || wcyScorch > wcyScorchable); + + WCoord wxoff = GetRandom() % (wcxScorchable - wcxScorch + 1); + WCoord wyoff = GetRandom() % (wcyScorchable - wcyScorch + 1); + + ScorchGob *pgobScorch = GetScorchGob(); + if (pgobScorch != NULL) { + // Make sure the scorch stays within the map bounds + + WCoord wxScorch = m_wx + wxoff + (wcxScorch / 2); + WCoord wyScorch = m_wy + wyoff + wcyScorch; + if (wxScorch < 0) + wxScorch = 0; + if (wxScorch >= wsizMap.cx) + wxScorch = wsizMap.cx - 1; + if (wyScorch >= wsizMap.cy) + wyScorch = wsizMap.cy - 1; + + pgobScorch->Init(wxScorch, wyScorch, nScorch); + } + } + } + + OnUpdate + AdvanceAnimation(&m_ani); + + OnMsg(kmidDelete) + Delete(); + return knDeleted; + + // Do this so the kmidSpawnSmoke message will bubble up to the 'global' handler above + + OnMsg(kmidSpawnSmoke) + return knNotHandled; + + // Eat all other messages (e.g., kmidHit, kmidNearbyAllyHit) + + DiscardMsgs + + //----------------------------------------------------------------------- + +EndStateMachine +} + +// +// SmokeGob implementation +// + +SmokeGob::SmokeGob(int cLoops) +{ + m_cLoops = cLoops; +} + +bool SmokeGob::Init(WCoord wx, WCoord wy, StateMachineId smidNotify) +{ + return AnimGob::Init(wx, wy, kfAnmLoop | kfAnmSmokeFireLayer, NULL, s_panidBigSmoke, 0, smidNotify, NULL); +} + +#define knVerSmokeGobState 1 +bool SmokeGob::LoadState(Stream *pstm) +{ + byte nVer = pstm->ReadByte(); + if (nVer != knVerSmokeGobState) + return false; + m_cLoops = pstm->ReadWord(); + m_ani.Init(s_panidBigSmoke); + m_ani.LoadState(pstm); + m_smidNotify = pstm->ReadWord(); + m_wfAnm = kfAnmLoop | kfAnmSmokeFireLayer; + return AnimGob::LoadState(pstm); +} + +bool SmokeGob::SaveState(Stream *pstm) +{ + pstm->WriteByte(knVerSmokeGobState); + pstm->WriteWord(m_cLoops); + m_ani.SaveState(pstm); + pstm->WriteWord(m_smidNotify); + return AnimGob::SaveState(pstm); +} + +bool SmokeGob::IsSavable() +{ + // Because AnimGob is not savable, we need to override + + return true; +} + +GobType SmokeGob::GetType() +{ + return kgtSmoke; +} + +// After the initial set (smoke growing) completes, draw the +// cycle a few times then destroy self. + +bool SmokeGob::OnStripDone() +{ + if (m_ani.GetStrip() == 0) { + SetAnimationStrip(&m_ani, 1); + } else { + m_cLoops--; + if (m_cLoops < 0) + return true; + } + return false; +} + +} // namespace wi diff --git a/game/Tank.cpp b/game/Tank.cpp new file mode 100644 index 0000000..d2d5cbc --- /dev/null +++ b/game/Tank.cpp @@ -0,0 +1,843 @@ +#include "ht.h" + +namespace wi { + +const int kcMTankSecondShot = 2; // Frame count of first shot (ignoring 1st frame) - MTank Anims need to match! +const int kifrmTankAction = 1; // frame where the shot really starts + +// +// Abstract Tank +// + +bool TankGob::InitClass(MobileUnitConsts *pmuntc, IniReader *pini) +{ + pmuntc->wf |= kfUntcNotifyEnemyNearby; + return MobileUnitGob::InitClass(pmuntc, pini); +} + +void TankGob::ExitClass(MobileUnitConsts *pmuntc) +{ + MobileUnitGob::ExitClass(pmuntc); +} + +TankGob::TankGob(MobileUnitConsts *pmuntc) : MobileUnitGob(pmuntc) +{ + m_dir = kdirS; + + // Turret stuff + + m_dir16Turret = m_dir * 2; +} + +bool TankGob::Init(WCoord wx, WCoord wy, Player *pplr, fix fxHealth, dword ff, const char *pszName) +{ + if (!MobileUnitGob::Init(wx, wy, pplr, fxHealth, ff, pszName)) + return false; + + StartAnimation(&m_ani, m_dir, 0, 0); + m_aniTurret.Init(m_pmuntc->panid); + SetAnimationStrip(&m_aniTurret, m_pmuntc->anFiringStripIndices[m_dir16Turret]); + return true; +} + +#define knVerTankGobState 1 +bool TankGob::LoadState(Stream *pstm) +{ + byte nVer = pstm->ReadByte(); + if (nVer != knVerTankGobState) + return false; + m_dir16Turret = pstm->ReadByte(); + m_aniTurret.Init(m_pmuntc->panid); + SetAnimationStrip(&m_aniTurret, m_pmuntc->anFiringStripIndices[m_dir16Turret]); + return MobileUnitGob::LoadState(pstm); +} + +bool TankGob::SaveState(Stream *pstm) +{ + pstm->WriteByte(knVerTankGobState); + pstm->WriteByte(m_dir16Turret); + return MobileUnitGob::SaveState(pstm); +} + +void TankGob::Draw(DibBitmap *pbm, int xViewOrigin, int yViewOrigin, int nLayer) +{ + if (nLayer == knLayerDepthSorted) { + +#ifdef DRAW_OCCUPIED_TILE_INDICATOR + { + WRect wrcT; + GetTilePaddedWRect(&wrcT); + Rect rcT; + rcT.FromWorldRect(&wrcT); + rcT.Offset(-xViewOrigin, -yViewOrigin); + DrawBorder(pbm, &rcT, 1, GetColor(kiclrWhite)); + } +#endif + Side side = m_pplr->GetSide(); + if (m_ff & kfGobDrawFlashed) + side = (Side)-1; + + // Draw base + + int x = PcFromUwc(m_wx) - xViewOrigin; + int y = PcFromUwc(m_wy) - yViewOrigin; + m_ani.Draw(pbm, x, y, side); + + // Draw turret + // The turret is aligned with the base's special point + + Point ptBaseSpecial; + m_ani.GetSpecialPoint(&ptBaseSpecial); + m_aniTurret.Draw(pbm, x + ptBaseSpecial.x, y + ptBaseSpecial.y, side); + } else { + MobileUnitGob::Draw(pbm, xViewOrigin, yViewOrigin, nLayer); + } +} + +// UNDONE: MobileUnitGob can handle some of this (e.g., timing) + +bool TankGob::Fire(UnitGob *puntTarget, WCoord wx, WCoord wy, WCoord wdx, WCoord wdy) +{ + Direction16 dir16Fire = CalcDir16(wdx, wdy); + if (m_dir16Turret != dir16Fire) { + m_unvl.MinSkip(); + return false; + } + + // Firing rate is limited by ctFiringRate + + long t = gsim.GetTickCount(); + long ctWait = m_pmuntc->ctFiringRate; + long ctRemaining = ctWait - (t - m_tLastFire); + if (ctRemaining > 0) { + m_unvl.MinSkip((ctRemaining + (kctUpdate / 2)) / kctUpdate - 1); + return false; + } + + m_tLastFire = t; + + // Play firing animation (start on frame 1 where the action is) + + StartAnimation(&m_aniTurret, m_pmuntc->anFiringStripIndices[dir16Fire], kifrmTankAction, kfAniIgnoreFirstAdvance | kfAniResetWhenDone); + m_wfMunt |= kfMuntFiring; + gsmm.SendDelayedMsg(kmidFireComplete, m_aniTurret.GetRemainingStripTime(), m_gid, m_gid); + + // Fire off the shot! + + WCoord wdxRnd = ((GetRandom() & 7) - 3) * kwcTile16th; + WCoord wdyRnd = ((GetRandom() & 7) - 3) * kwcTile16th; + Point ptSpecial; + m_aniTurret.GetSpecialPoint(&ptSpecial, kifrmTankAction); + + LaunchProjectile(m_wx + WcFromPc(ptSpecial.x), m_wy + WcFromPc(ptSpecial.y), wx + wdxRnd, wy + wdyRnd, + GetDamageTo(puntTarget), m_gid, puntTarget->GetId()); + + // Play sound + + gsndm.PlaySfx(m_pmuntc->sfxFire); + + return true; +} + +// default. Overridden by several tanks +void TankGob::LaunchProjectile(WCoord wx, WCoord wy, WCoord wxTarget, WCoord wyTarget, int nDamage, Gid gidOwner, Gid gidTarget) +{ + CreateTankShotGob(wx, wy, wxTarget, wyTarget, nDamage, gidOwner, gidTarget); +} + +void TankGob::Idle() +{ + // 1/2 of the time we pivot the turret left, 1/2 we pivot it right + + if (GetRandom() & 1) { + m_dir16Turret--; + if (m_dir16Turret < 0) + m_dir16Turret = 15; + } else { + m_dir16Turret++; + if (m_dir16Turret > 15) + m_dir16Turret = 0; + } + SetAnimationStrip(&m_aniTurret, m_pmuntc->anFiringStripIndices[m_dir16Turret]); +} + +void TankGob::DefUpdate() +{ + // Try to make the turrent point toward the target (unless we're already firing) + + if (m_wptTarget.wx != kwxInvalid && !(m_wfMunt & kfMuntFiring)) { + // Get the aim point. If we have a unit target, it is the unit's center + // If we don't have a unit target, it is whatever is in m_wptTarget + + WPoint wptAim = m_wptTarget; + Gob *pgobTarget = ggobm.GetGob(m_gidTarget); + if (pgobTarget != NULL) + pgobTarget->GetCenter(&wptAim); + + // If the target is in the tile we're in stop trying to look exactly at it + + if (WcTrunc(m_wx) != WcTrunc(wptAim.wx) || WcTrunc(m_wy) != WcTrunc(wptAim.wy)) { + + Direction16 dir16 = CalcDir16(wptAim.wx - m_wx, wptAim.wy - m_wy); + + // Make sure we're facing the way we want to go + + int d = dir16 - m_dir16Turret; + if (d != 0) { + if (d < -8) + d = 1; + else if (d > 8) + d = -1; + if (d < 0) + m_dir16Turret--; + else + m_dir16Turret++; + m_dir16Turret = ((unsigned int)m_dir16Turret) % 16; + SetAnimationStrip(&m_aniTurret, m_pmuntc->anFiringStripIndices[m_dir16Turret]); + m_unvl.MinSkip(); + } + } + } + + AdvanceAnimation(&m_aniTurret); + + MobileUnitGob::DefUpdate(); +} + +dword TankGob::GetAnimationHash() { + dword dw = MobileUnitGob::GetAnimationHash(); + return dw ^ (m_dir16Turret << 8); +} + +void TankGob::GetAnimationBounds(Rect *prc, bool fBase) { + MobileUnitGob::GetAnimationBounds(prc, fBase); + + if (!fBase) { + Rect rcTurret; + m_aniTurret.GetBounds(&rcTurret); + Point ptBaseSpecial; + m_ani.GetSpecialPoint(&ptBaseSpecial); + rcTurret.Offset(ptBaseSpecial.x, ptBaseSpecial.y); + prc->Union(&rcTurret); + } +} + +void TankGob::DrawAnimation(DibBitmap *pbm, int x, int y) +{ + Side side = m_pplr->GetSide(); + MobileUnitGob::DrawAnimation(pbm, x, y); + Point ptBaseSpecial; + m_ani.GetSpecialPoint(&ptBaseSpecial); + m_aniTurret.Draw(pbm, x + ptBaseSpecial.x, y + ptBaseSpecial.y, side); +} + +void TankGob::GetClippingBounds(Rect *prc) +{ + MobileUnitGob::GetClippingBounds(prc); + Rect rcTurret; + m_aniTurret.GetBounds(&rcTurret); + Point ptBaseSpecial; + m_ani.GetSpecialPoint(&ptBaseSpecial); + rcTurret.Offset(ptBaseSpecial.x + PcFromUwc(m_wx), ptBaseSpecial.y + PcFromUwc(m_wy)); + prc->Union(&rcTurret); +} + +// +// Light Tank +// + +static MobileUnitConsts gLTankConsts; + +#if defined(DEBUG_HELPERS) +char *LTankGob::GetName() +{ + return "LTank"; +} +#endif + +static int s_anTurretStripIndices[16] = { 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 }; +static int s_anBaseStripIndices[8] = { 0, 1, 2, 3, 4, 5, 6, 7 }; +static int s_anIdleStripIndices[8] = { 0, 1, 2, 3, 4, 5, 6, 7 }; + +bool LTankGob::InitClass(IniReader *pini) +{ + gLTankConsts.gt = kgtLightTank; + gLTankConsts.ut = kutLightTank; + + // Initialize the frame indices arrays + + gLTankConsts.anFiringStripIndices = s_anTurretStripIndices; + gLTankConsts.anMovingStripIndices = s_anBaseStripIndices; + gLTankConsts.anIdleStripIndices = s_anIdleStripIndices; + + // Sound effects + + gLTankConsts.sfxFire = ksfxLightTankFire; + gLTankConsts.sfxImpact = ksfxLightTankImpact; + + gLTankConsts.sfxcDestroyed = ksfxcVehicleDestroyed; + gLTankConsts.sfxcSelect = ksfxcMajor02Select; + gLTankConsts.sfxcMove = ksfxcMajor02Move; + gLTankConsts.sfxcAttack = ksfxcMajor02Attack; + + return TankGob::InitClass(&gLTankConsts, pini); +} + +void LTankGob::ExitClass() +{ + TankGob::ExitClass(&gLTankConsts); +} + +LTankGob::LTankGob() : TankGob(&gLTankConsts) +{ +} + +// +// Medium Tank +// + +static MobileUnitConsts gMTankConsts; + +#if defined(DEBUG_HELPERS) +char *MTankGob::GetName() +{ + return "MTank"; +} +#endif + +bool MTankGob::InitClass(IniReader *pini) +{ + gMTankConsts.gt = kgtMediumTank; + gMTankConsts.ut = kutMediumTank; + gMTankConsts.upgmPrerequisites = kupgmAdvancedVTS; + + // Initialize the frame indices arrays + + gMTankConsts.anFiringStripIndices = s_anTurretStripIndices; + gMTankConsts.anMovingStripIndices = s_anBaseStripIndices; + gMTankConsts.anIdleStripIndices = s_anIdleStripIndices; + + // Sound effects + + gMTankConsts.sfxFire = ksfxMediumTankFire; + gMTankConsts.sfxImpact = ksfxMediumTankImpact; + + gMTankConsts.sfxcDestroyed = ksfxcVehicleDestroyed; + gMTankConsts.sfxcSelect = ksfxcMale06Select; + gMTankConsts.sfxcMove = ksfxcMale06Move; + gMTankConsts.sfxcAttack = ksfxcMale06Attack; + + return TankGob::InitClass(&gMTankConsts, pini); +} + +void MTankGob::ExitClass() +{ + TankGob::ExitClass(&gMTankConsts); +} + +// need to load state & save state + +MTankGob::MTankGob() : TankGob(&gMTankConsts) +{ + m_pgobSecondShot = NULL; +} + +MTankGob::~MTankGob() +{ + // don't be left holding a shot. This does mean + // that exiting and restarting will lose this shot. + + delete m_pgobSecondShot; +} + +// Make sure the second shot doesn't fire after this tank has been +// destroyed. + +void MTankGob::Deactivate() +{ + delete m_pgobSecondShot; + m_pgobSecondShot = NULL; + TankGob::Deactivate(); +} + +void MTankGob::LaunchProjectile(WCoord wx, WCoord wy, WCoord wxTarget, WCoord wyTarget, int nDamage, Gid gidOwner, Gid gidTarget) +{ + CreateTankShotGob(wx, wy, wxTarget, wyTarget, nDamage / 2, gidOwner, gidTarget); + + // set up for the 2nd shot, kcMTankSecondShot updates (frames) later + //kcMTankSecondShot is defined in code, anims need to match! + + m_cShotcountdown = kcMTankSecondShot; + + // Add the current update interval count since m_cShotcountdown gets decremented in DefUpdate + // right after this call + + m_cShotcountdown += m_unvl.GetUpdateCount(); + + m_unvl.MinSkip(m_cShotcountdown); + + if (ggobm.IsBelowLimit(knLimitSupport)) { + m_pgobSecondShot = new TankShotGob(); + Assert(m_pgobSecondShot != NULL, "out of memory!"); + if (m_pgobSecondShot != NULL) { + // we need to find the 2nd special point for the 2nd shot, modify the endpoint as well + + Point ptSpecial1, ptSpecial2; + m_aniTurret.GetSpecialPoint(&ptSpecial1, kifrmTankAction); + m_aniTurret.GetSpecialPoint(&ptSpecial2, kifrmTankAction + kcMTankSecondShot); + + if (!m_pgobSecondShot->Init(TankShotGob::s_panidShot, wx + WcFromPc(ptSpecial2.x - ptSpecial1.x), wy + WcFromPc(ptSpecial2.y-ptSpecial1.y), + wxTarget + WcFromPc(ptSpecial2.x - ptSpecial1.x), wyTarget + WcFromPc(ptSpecial2.y-ptSpecial1.y), + nDamage / 2, gidOwner, gidTarget, kwcTankShotRate)) { + delete m_pgobSecondShot; + m_pgobSecondShot = NULL; + return; + } + } + } +} + +void MTankGob::DefUpdate() +{ + // if we have a 2nd shot pending, countdown to fire and make it so! + + if (m_pgobSecondShot != NULL) { + m_cShotcountdown -= m_unvl.GetUpdateCount(); + if (m_cShotcountdown < 0) { + // The unit limit check was made when this gob was created, however now that it is time to fire, + // the limit may have been reached (since it is Launch that adds the gob). Check again. + + if (ggobm.IsBelowLimit(knLimitSupport)) { + m_pgobSecondShot->Launch(); + } else { + delete m_pgobSecondShot; + } + m_pgobSecondShot = NULL; + gsndm.PlaySfx(m_pmuntc->sfxFire); + } + } + TankGob::DefUpdate(); +} + +// +// Rocket Tank +// + +static MobileUnitConsts gRTankConsts; + +#if defined(DEBUG_HELPERS) +char *RTankGob::GetName() +{ + return "RTank"; +} +#endif + +bool RTankGob::InitClass(IniReader *pini) +{ + gRTankConsts.gt = kgtRocketVehicle; + gRTankConsts.ut = kutRocketVehicle; + gRTankConsts.upgmPrerequisites = kupgmAdvancedVTS; + + // Initialize the frame indices arrays + + gRTankConsts.anFiringStripIndices = s_anTurretStripIndices; + gRTankConsts.anMovingStripIndices = s_anBaseStripIndices; + gRTankConsts.anIdleStripIndices = s_anIdleStripIndices; + + // Sound effects + + gRTankConsts.sfxFire = ksfxRocketVehicleFire; + gRTankConsts.sfxImpact = ksfxRocketVehicleImpact; + + gRTankConsts.sfxcDestroyed = ksfxcVehicleDestroyed; + gRTankConsts.sfxcSelect = ksfxcMajor01Select; + gRTankConsts.sfxcMove = ksfxcMajor01Move; + gRTankConsts.sfxcAttack = ksfxcMajor01Attack; + + return TankGob::InitClass(&gRTankConsts, pini); +} + +void RTankGob::ExitClass() +{ + TankGob::ExitClass(&gRTankConsts); +} + +RTankGob::RTankGob() : TankGob(&gRTankConsts) +{ +} + +void RTankGob::LaunchProjectile(WCoord wx, WCoord wy, WCoord wxTarget, WCoord wyTarget, int nDamage, Gid gidOwner, Gid gidTarget) +{ + CreateRocketGob(wx, wy, wxTarget, wyTarget, nDamage, gidOwner, gidTarget); +} + + +// +// Machine Gun Tank +// + +static MobileUnitConsts gGTankConsts; + +#if defined(DEBUG_HELPERS) +char *GTankGob::GetName() +{ + return "GTank"; +} +#endif + +bool GTankGob::InitClass(IniReader *pini) +{ + gGTankConsts.gt = kgtMachineGunVehicle; + gGTankConsts.ut = kutMachineGunVehicle; + + // Initialize the frame indices arrays + + gGTankConsts.anFiringStripIndices = s_anTurretStripIndices; + gGTankConsts.anMovingStripIndices = s_anBaseStripIndices; + gGTankConsts.anIdleStripIndices = s_anIdleStripIndices; + + // Sound effects + + gGTankConsts.sfxFire = ksfxMachineGunVehicleFire; + gGTankConsts.sfxImpact = ksfxNothing; + + gGTankConsts.sfxcDestroyed = ksfxcVehicleDestroyed; + gGTankConsts.sfxcSelect = ksfxcMale01Select; + gGTankConsts.sfxcMove = ksfxcMale01Move; + gGTankConsts.sfxcAttack = ksfxcMale01Attack; + + return TankGob::InitClass(&gGTankConsts, pini); +} + +void GTankGob::ExitClass() +{ + TankGob::ExitClass(&gGTankConsts); +} + +GTankGob::GTankGob() : TankGob(&gGTankConsts) +{ +} + +// UNDONE: MobileUnitGob can handle some of this (e.g., timing) + +bool GTankGob::Fire(UnitGob *puntTarget, WCoord wx, WCoord wy, WCoord wdx, WCoord wdy) +{ + // Firing rate is limited by ctFiringRate + + long t = gsim.GetTickCount(); + long ctWait = m_pmuntc->ctFiringRate; + long ctRemaining = ctWait - (t - m_tLastFire); + if (ctRemaining > 0) { + m_unvl.MinSkip((ctRemaining + (kctUpdate / 2)) / kctUpdate - 1); + return false; + } + + Direction16 dir16Fire = CalcDir16(wdx, wdy); + if (m_dir16Turret != dir16Fire) + return false; + + m_tLastFire = t; + + // Play firing animation + + StartAnimation(&m_aniTurret, m_pmuntc->anFiringStripIndices[dir16Fire], 0, kfAniResetWhenDone); + m_wfMunt |= kfMuntFiring; + gsmm.SendDelayedMsg(kmidFireComplete, m_aniTurret.GetRemainingStripTime(), m_gid, m_gid); + + // Fire off those shots! + + WCoord wdxRnd = ((GetRandom() & 7) - 3) * kwcTile16th; + WCoord wdyRnd = ((GetRandom() & 7) - 3) * kwcTile16th; + Point ptSpecial; + m_aniTurret.GetSpecialPoint(&ptSpecial, kifrmTankAction); + + CreateBulletGob(m_wx + WcFromPc(ptSpecial.x), m_wy + WcFromPc(ptSpecial.y), wx + wdxRnd, wy + wdyRnd, + GetDamageTo(puntTarget) / 3, m_gid, puntTarget->GetId()); + + // Two more shots at slight delays + + gsmm.SendDelayedMsg(kmidFire, kctUpdate * 3, m_gid, m_gid); + gsmm.SendDelayedMsg(kmidFire, kctUpdate * 6, m_gid, m_gid); + + // Play sound + + gsndm.PlaySfx(m_pmuntc->sfxFire); + + return true; +} + +int GTankGob::ProcessStateMachineMessage(State st, Message *pmsg) +{ +BeginStateMachine + OnMsg(kmidFire) + UnitGob *puntTarget = (UnitGob *)ggobm.GetGob(m_gidTarget); + if (puntTarget != NULL) { + // Fire off the shot! + + WCoord wdxRnd = ((GetRandom() & 7) - 3) * kwcTile16th; + WCoord wdyRnd = ((GetRandom() & 7) - 3) * kwcTile16th; + + WPoint wpt; + puntTarget->GetCenter(&wpt); + Point ptSpecial; + m_aniTurret.GetSpecialPoint(&ptSpecial, kifrmTankAction); + CreateBulletGob(m_wx + WcFromPc(ptSpecial.x), m_wy + WcFromPc(ptSpecial.y), wpt.wx + wdxRnd, wpt.wy + wdyRnd, + GetDamageTo(puntTarget) / 3, m_gid, puntTarget->GetId()); + } + +#if 0 +EndStateMachineInherit(MobileUnitGob) +#else + return knHandled; + } + } else { + return (int)MobileUnitGob::ProcessStateMachineMessage(st, pmsg); + } + return (int)MobileUnitGob::ProcessStateMachineMessage(st, pmsg); +#endif +} + +// +// TankShotGob implementation +// + +AnimationData *TankShotGob::s_panidShot = NULL; + +// this will actually create & fire +TankShotGob *CreateTankShotGob(WCoord wx, WCoord wy, WCoord wxTarget, WCoord wyTarget, int nDamage, Gid gidOwner, Gid gidTarget) +{ + if (!ggobm.IsBelowLimit(knLimitSupport)) + return NULL; + + TankShotGob *pgob = new TankShotGob(); + Assert(pgob != NULL, "out of memory!"); + if (pgob == NULL) + return NULL; + + if (!pgob->Init(TankShotGob::s_panidShot, wx, wy, wxTarget, wyTarget, nDamage, gidOwner, gidTarget, kwcTankShotRate)) { + delete pgob; + return NULL; + } + + pgob->Launch(); + return pgob; +} + +bool TankShotGob::InitClass(IniReader *pini) +{ + s_panidShot = LoadAnimationData("tankshot.anir"); + if (s_panidShot == NULL) + return false; + return true; +} + +void TankShotGob::ExitClass() +{ + delete s_panidShot; + s_panidShot = NULL; +} + +TankShotGob::TankShotGob() +{ + m_ff |= kfGobStateMachine | kfGobLayerSmokeFire; +} + +bool TankShotGob::Init(AnimationData *panid, WCoord wx, WCoord wy, WCoord wxTarget, WCoord wyTarget, int nDamage, Gid gidOwner, Gid gidTarget, WCoord wcMoveRate) +{ + // Units fire from their special point which may be off the edge of the map. + // We cannot allow Gobs to be off the map so here we bring it on. + + BringInBounds(&wx, &wy); + BringInBounds(&wxTarget, &wyTarget); + +#ifdef DEBUG + TileMap *ptmap = gsim.GetLevel()->GetTileMap(); + Size sizT; + ptmap->GetTCoordMapSize(&sizT); + Assert(wx < WcFromTc(sizT.cx) && wy < WcFromTc(sizT.cy)); + Assert(wxTarget < WcFromTc(sizT.cx) && wyTarget < WcFromTc(sizT.cy)); +#endif + + // initialize our state but don't actually fire, save that for Launch + + m_gidOwner = gidOwner; + m_gidTarget = gidTarget; + m_nDamage = nDamage; + + m_li.Init(wx, wy, wxTarget, wyTarget, wcMoveRate); + + // LineIterator initializes x,y to the first step-integral point on the line, + // presuming that the final step should be at the target x,y + + m_wx = m_li.GetWX(); + m_wy = m_li.GetWY(); + + m_ani.Init(panid); + if (m_ani.GetAnimationData()->GetFrameCount(0) == 8) + StartAnimation(&m_ani, 0, CalcDir(wxTarget - wx, wyTarget - wy), kfAniDone); + + return true; +} + +void TankShotGob::Launch() +{ + // HACK: If this animation has 8 frames we assume that's one for each direction + + if (m_ani.GetAnimationData()->GetFrameCount(0) != 8) + StartAnimation(&m_ani, 0, 0, kfAniLoop); + + // Add the fresh Gob to the GobMgr. GobMgr::AddGob assigns this Gob a gid + + ggobm.AddGob(this); + + // Let the target know when it will be hit. Doing it this way + // means the hit will arrive in the same number of updates for all + // players (the number of updates calc'd by the shooter). Depending + // on screen resolution, the animated travel time may be off by an update + + Message msgT; + msgT.mid = kmidHit; + msgT.smidSender = m_gidOwner; + msgT.smidReceiver = m_gidTarget; + msgT.Hit.gidAssailant = m_gidOwner; + msgT.Hit.sideAssailant = ggobm.GetGob(m_gidOwner)->GetSide(); + msgT.Hit.nDamage = m_nDamage; + + // BUGBUG: m_li.GetStepsRemaining is influenced by the firing point which is resolution dependent. + // Instead, this message's delay should be derived using the distance from the world coordinate + // centers of the source and target + + gsmm.SendDelayedMsg(&msgT, (m_li.GetStepsRemaining() + 1) * (kcmsUpdate / 10)); +} + +// TankShotGobs don't get loaded + +bool TankShotGob::Init(IniReader *pini, FindProp *pfind, const char *pszName) +{ + return true; +} + +bool TankShotGob::IsSavable() +{ + return false; +} + +GobType TankShotGob::GetType() +{ + return kgtTankShot; +} + +void TankShotGob::GetClippingBounds(Rect *prc) +{ + // hardcoded that the travel is strip 0 and the impact is strip 1 + + if (m_ani.GetStrip() == 0) { + if (!(gwfPerfOptions & kfPerfShots)) { + prc->SetEmpty(); + return; + } + } else { + if (!(gwfPerfOptions & kfPerfShotImpacts)) { + prc->SetEmpty(); + return; + } + } + + m_ani.GetBounds(prc); + prc->Offset(PcFromUwc(m_wx), PcFromUwc(m_wy)); +} + +void TankShotGob::Draw(DibBitmap *pbm, int xViewOrigin, int yViewOrigin, int nLayer) +{ + if (nLayer == knLayerSmokeFire) { + // hardcoded that the travel is strip 0 and the impact is strip 1 + + if (m_ani.GetStrip() == 0) { + if (!(gwfPerfOptions & kfPerfShots)) + return; + } else { + if (!(gwfPerfOptions & kfPerfShotImpacts)) + return; + } + m_ani.Draw(pbm, PcFromUwc(m_wx) - xViewOrigin, PcFromUwc(m_wy) - yViewOrigin, m_pplr->GetSide()); + } +} + +#if defined(DEBUG_HELPERS) +char *TankShotGob::GetName() +{ + return "TankShot"; +} +#endif + +int TankShotGob::ProcessStateMachineMessage(State st, Message *pmsg) +{ +BeginStateMachine + OnUpdate + // Advance the shot animation + // hardcoded that the travel is strip 0 and the impact is strip 1 + + if (m_ani.GetStrip() == 0) { + AdvanceAnimation(&m_ani); + MarkRedraw(); + + // Advance the shot position + + if (m_li.Step()) { + WCoord wxOld = m_wx; + WCoord wyOld = m_wy; + m_wx = m_li.GetWX(); + m_wy = m_li.GetWY(); + + // Keep GobMgr in the loop so it can maintain proper depth sorting + + if (m_wx != wxOld || m_wy != wyOld) + ggobm.MoveGob(this, wxOld, wyOld, m_wx, m_wy); + + // Assumes getting called each update + + m_unvl.MinSkip(); + + } else { + + // Play impact sound + + UnitGob *punt = (UnitGob *)ggobm.GetGob(m_gidOwner); + if (punt != NULL) { + UnitConsts *puntc = (UnitConsts *)punt->GetConsts(); + gsndm.PlaySfx(puntc->sfxImpact); + } + + // Start the impact animation + + StartAnimation(&m_ani, 1, 0, 0); + } + + // Assume valid if we're not drawing shots + + if (!(gwfPerfOptions & kfPerfShots)) + m_ff &= ~kfGobRedraw; + + } else { + + // Advance the impact animation + + if (!AdvanceAnimation(&m_ani)) { + + // Kill this shot + + ggobm.RemoveGob(this); + delete this; + return knDeleted; + } + + // Assume valid if we're not drawing shot impacts + + if (!(gwfPerfOptions & kfPerfShotImpacts)) + m_ff &= ~kfGobRedraw; + } + +EndStateMachine +} + +} // namespace wi diff --git a/game/Tower.cpp b/game/Tower.cpp new file mode 100644 index 0000000..02b958e --- /dev/null +++ b/game/Tower.cpp @@ -0,0 +1,614 @@ +#include "ht.h" + +namespace wi { + +static int s_anGunTowerTurretStripIndices[16] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 }; + +// +// Abstract base class for Towers +// + +bool TowerGob::InitClass(TowerConsts *ptwrc, IniReader *pini) +{ + if (!StructGob::InitClass(ptwrc, pini)) + return false; + + ptwrc->anFiringStripIndices = s_anGunTowerTurretStripIndices; + ptwrc->wf |= kfUntcNotifyEnemyNearby | kfUntcNotifyPowerLowHigh; + + char szTemplate[10]; + itoa(ptwrc->gt, szTemplate, 10); + return true; +} + +void TowerGob::ExitClass(TowerConsts *ptwrc) +{ + StructGob::ExitClass(ptwrc); +} + +TowerGob::TowerGob(TowerConsts *ptwrc) : StructGob(ptwrc) +{ + m_tLastFire = 0; + m_gidTargetPrimary = kgidNull; + m_gidTargetSecondary = kgidNull; + m_wptTarget.wx = kwxInvalid; // kxInvalid = no target location + + // Turret stuff + + m_dir16Turret = kdirS * 2; + m_aniTurret.Init(m_ptwrc->panid); + SetAnimationStrip(&m_aniTurret, m_ptwrc->anFiringStripIndices[m_dir16Turret]); +} + +#define knVerTowerGobState 1 +bool TowerGob::LoadState(Stream *pstm) +{ + byte nVer = pstm->ReadByte(); + if (nVer != knVerTowerGobState) + return false; + m_dir16Turret = pstm->ReadByte(); + m_aniTurret.Init(m_ptwrc->panid); + SetAnimationStrip(&m_aniTurret, m_ptwrc->anFiringStripIndices[m_dir16Turret]); + m_gidTargetPrimary = pstm->ReadWord(); + m_gidTargetSecondary = pstm->ReadWord(); + m_tLastFire = gsim.GetTickCount() - pstm->ReadDword(); + m_wptTarget.wx = pstm->ReadWord(); + m_wptTarget.wy = pstm->ReadWord(); + return StructGob::LoadState(pstm); +} + +bool TowerGob::SaveState(Stream *pstm) +{ + pstm->WriteByte(knVerTowerGobState); + pstm->WriteByte(m_dir16Turret); + pstm->WriteWord(m_gidTargetPrimary); + pstm->WriteWord(m_gidTargetSecondary); + pstm->WriteDword(gsim.GetTickCount() - m_tLastFire); + pstm->WriteWord(m_wptTarget.wx); + pstm->WriteWord(m_wptTarget.wy); + return StructGob::SaveState(pstm); +} + +void TowerGob::Activate() +{ + NotifyEnemyNearby(); + StructGob::Activate(); +} + +#ifdef DRAW_LINES +void TowerGob::DrawTargetLine(DibBitmap *pbm, int xViewOrigin, int yViewOrigin) +{ + if (m_wptTarget.wx == kwxInvalid) + return; + + WPoint wptCenter; + GetCenter(&wptCenter); + pbm->DrawLine(PcFromUwc(wptCenter.wx) - xViewOrigin, PcFromUwc(wptCenter.wy) - yViewOrigin, + PcFromUwc(m_wptTarget.wx) - xViewOrigin, PcFromUwc(m_wptTarget.wy) - yViewOrigin, + GetSideColor(m_pplr->GetSide())); +} +#endif + +void TowerGob::Draw(DibBitmap *pbm, int xViewOrigin, int yViewOrigin, int nLayer) +{ + // Draw base + + StructGob::Draw(pbm, xViewOrigin, yViewOrigin, nLayer); + + // Draw turret + + if (nLayer == knLayerDepthSorted) { + + // Don't draw the turret on top of the destroyed base + + if (m_ani.GetStrip() != 2) { + Side side = m_pplr->GetSide(); + if (m_ff & kfGobDrawFlashed) + side = (Side)-1; + else if (m_ff & kfGobBeingBuilt) + side = ksideNeutral; + + // The turret is aligned with the base's special point + + Point ptBaseSpecial; + m_ani.GetSpecialPoint(&ptBaseSpecial); + m_aniTurret.Draw(pbm, PcFromUwc(m_wx) - xViewOrigin + ptBaseSpecial.x, + PcFromUwc(m_wy) - yViewOrigin + ptBaseSpecial.y, side); + } + } +} + +bool TowerGob::IsTakeoverable(Player *pplr) +{ + // towers are not Takeoverable + return false; +} + +bool TowerGob::IsValidTarget(Gob *pgobTarget) +{ + // most structures cannot attack, so bypass the struct class on this one + // TUNE: + // however, since we can't move, let's make sure the target is in range ? + //if IsGobWithinRange(pgobTarget, m_ptwrc->tcFiringRange) + + return UnitGob::IsValidTarget(pgobTarget); +} + +void TowerGob::SetTarget(Gid gid, WCoord wx, WCoord wy, WCoord wxCenter, WCoord wyCenter, TCoord tcRadius, WCoord wcMoveDistPerUpdate) +{ + // If target doesn't exist anymore, ignore + + Gob *pgobTarget = ggobm.GetGob(gid); + if (pgobTarget == NULL) + return; + + // If target is an ally, ignore + + if (IsAlly(pgobTarget->GetSide())) + return; + + // Attack it! + // Flash the target Gob + + if (!((UnitGob *)pgobTarget)->IsAlly(gpplrLocal->GetSide())) + pgobTarget->Flash(); + + // Queue message + + Message msgT; + msgT.mid = kmidAttackCommand; + msgT.smidSender = m_gid; + msgT.smidReceiver = m_gid; + msgT.AttackCommand.wptTarget.wx = 0; + msgT.AttackCommand.wptTarget.wy = 0; + msgT.AttackCommand.gidTarget = gid; + gcmdq.Enqueue(&msgT); +} + +void TowerGob::DefUpdate() +{ + // Try to make the turret point toward the target + // UNDONE: unless we're already firing + + // BUGBUG: this usage of the special point effectively makes the turret's direction + // resolution dependent which will break multiplayer games between devices of + // differing resolution. + + Point ptSpecial; + m_ani.GetSpecialPoint(&ptSpecial); + WCoord wx = m_wx + WcFromPc(ptSpecial.x); + WCoord wy = m_wy + WcFromPc(ptSpecial.y); + + if (m_wptTarget.wx != kwxInvalid) { + + // If the target is in the tile we're in stop trying to look exactly at it + + if (WcTrunc(wx) != WcTrunc(m_wptTarget.wx) || WcTrunc(wy) != WcTrunc(m_wptTarget.wy)) { + + Direction16 dir16 = CalcDir16(m_wptTarget.wx - wx, m_wptTarget.wy - wy); + + // Make sure we're facing the way we want to go + + int d = dir16 - m_dir16Turret; + if (d != 0) { + if (d < -8) + d = 1; + else if (d > 8) + d = -1; + if (d < 0) + m_dir16Turret--; + else + m_dir16Turret++; + m_dir16Turret = ((unsigned int)m_dir16Turret) % 16; + SetAnimationStrip(&m_aniTurret, m_ptwrc->anFiringStripIndices[m_dir16Turret]); + m_unvl.MinSkip(); + } + } + } + + AdvanceAnimation(&m_aniTurret); + + StructGob::DefUpdate(); +} + +UnitGob *TowerGob::FindEnemyNearby(TCoord tcRange) +{ + // smart targeting requires a surveillance center and power + + if (!m_pplr->IsPowerLow() && (m_pplr->GetUnitCount(kutRadar) > 0)) { + return UnitGob::FindEnemyNearby(tcRange); + } else { + return NULL; + } +} + +int TowerGob::ProcessStateMachineMessage(State st, Message *pmsg) +{ +BeginStateMachine + OnMsg(kmidEnemyNearby) + // Notification that an enemy is nearby + + RememberEnemyNearby(pmsg->EnemyNearby.gidEnemy); + m_unvl.MinSkip(); + + OnMsg(kmidAttackCommand) + // stash attack parameters to be picked up by the attack state + + m_gidTargetPrimary = pmsg->AttackCommand.gidTarget; + SetState(kstAttack); + + OnMsg(kmidFire) + UnitGob *puntTarget = (UnitGob *)ggobm.GetGob(m_gidTargetPrimary); + if (puntTarget == NULL || !IsGobWithinRange(puntTarget, m_ptwrc->tcFiringRange)) + puntTarget = (UnitGob *)ggobm.GetGob(m_gidTargetSecondary); + if (puntTarget != NULL) { + // Fire off the shot! + + WCoord wdxRnd = ((GetRandom() & 7) - 3) * kwcTile16th; + WCoord wdyRnd = ((GetRandom() & 7) - 3) * kwcTile16th; + + Point ptTurretSpecial; + m_aniTurret.GetSpecialPoint(&ptTurretSpecial, 1); + Point ptBaseSpecial; + m_ani.GetSpecialPoint(&ptBaseSpecial); + + WPoint wpt; + puntTarget->GetCenter(&wpt); + CreateBulletGob(m_wx + WcFromPc(ptBaseSpecial.x + ptTurretSpecial.x), + m_wy + WcFromPc(ptBaseSpecial.y + ptTurretSpecial.y), + wpt.wx + wdxRnd, wpt.wy + wdyRnd, + GetDamageTo(puntTarget) / 3, m_gid, puntTarget->GetId()); + } + + OnMsg(kmidHit) + // It is possible to be hit by someone without having a chance to target + // it first. The worse case of this is when an enemy is lost track of + // due to the short m_agidEnemyNearby. In any case, if we aren't already + // busy with something else attack the assailant. + + if (m_gidTargetSecondary == kgidNull && !m_pplr->IsPowerLow() && m_pplr->GetUnitCount(kutRadar) > 0) { + m_gidTargetSecondary = pmsg->Hit.gidAssailant; + SetState(kstAttack); + } + + return StructGob::ProcessStateMachineMessage(st, pmsg); + + //----------------------------------------------------------------------- + + State(kstIdle) + OnEnter + m_wptTarget.wx = kwxInvalid; // no target + + //OnMsg(kmidHit) - if power is low then we'll end up taking hits that + // we don't respond to. Sometimes we'll take a first hit before we respond too. C'est le guerre + + OnUpdate + // any enemy in range? + + UnitGob *puntTarget = FindEnemyNearby(m_ptwrc->tcFiringRange); + if (puntTarget != NULL) { + + // let's have a little focus + +// DWM: this can occur if the tower loses power while it has a secondary target +// It's OK because it just means the tower picks a new secondary when it wakes up +// Assert(m_gidTargetSecondary == kgidNull); + m_gidTargetSecondary = puntTarget->GetId(); + SetState(kstAttack); + } + + // Handle flashing, turret pointing, firing animation + + DefUpdate(); + + //----------------------------------------------------------------------- + + State(kstAttack) + OnUpdate + + // if power is low then we're just not going to do anything + + UnitGob* puntTarget = NULL; + if (!GetOwner()->IsPowerLow()) { + + // look for our primary (player-set) enemy first + + if (m_gidTargetPrimary != kgidNull) { + puntTarget = (UnitGob *)ggobm.GetGob(m_gidTargetPrimary); + if (puntTarget == NULL || !IsValidTarget(puntTarget)) { + + // primary enemy is dead/gone or taken over, we can forget it. + + m_gidTargetPrimary = kgidNull; + puntTarget = NULL; + } else if (!IsGobWithinRange(puntTarget, m_ptwrc->tcFiringRange)) { + + // primary target not in range + + puntTarget = NULL; + } + } + + if (puntTarget == NULL) { + + // primary is not available, who can we shoot at in the meantime? + + puntTarget = (UnitGob *)ggobm.GetGob(m_gidTargetSecondary); + if (puntTarget == NULL || !IsValidTarget(puntTarget) || + !IsGobWithinRange(puntTarget, m_ptwrc->tcFiringRange)) { + + // secondary target not available, new secondary? + + puntTarget = FindEnemyNearby(m_ptwrc->tcFiringRange); + if (puntTarget != NULL) + m_gidTargetSecondary = puntTarget->GetId(); + else + m_gidTargetSecondary = kgidNull; + } + } + } + + if (puntTarget != NULL) { + + // we've got one, fire at the enemy + + Point ptSpecial; + m_ani.GetSpecialPoint(&ptSpecial); + puntTarget->GetAttackPoint(&m_wptTarget); + Fire(puntTarget, m_wptTarget.wx, m_wptTarget.wy, m_wptTarget.wx - (m_wx + WcFromPc(ptSpecial.x)), + m_wptTarget.wy - (m_wy + WcFromPc(ptSpecial.y))); + } else { + + // no valid targets in range + + SetState(kstIdle); + } + + // Handle flashing + + DefUpdate(); + + //----------------------------------------------------------------------- + + State(kstDying) + // Override to avoid animating through the Galaxite level frames + OnUpdate + + //----------------------------------------------------------------------- + +#if 0 +EndStateMachineInherit(StructGob) +#else + return knHandled; + } + } else { + return (int)StructGob::ProcessStateMachineMessage(st, pmsg); + } + return (int)StructGob::ProcessStateMachineMessage(st, pmsg); +#endif +} + +dword TowerGob::GetAnimationHash() { + dword dw = StructGob::GetAnimationHash(); + return dw ^ (m_dir16Turret << 8); +} + +void TowerGob::GetAnimationBounds(Rect *prc, bool fBase) { + StructGob::GetAnimationBounds(prc, fBase); + + if (!fBase) { + Rect rcTurret; + m_aniTurret.GetBounds(&rcTurret); + Point ptBaseSpecial; + m_ani.GetSpecialPoint(&ptBaseSpecial); + rcTurret.Offset(ptBaseSpecial.x, ptBaseSpecial.y); + prc->Union(&rcTurret); + } +} + +void TowerGob::DrawAnimation(DibBitmap *pbm, int x, int y) +{ + Side side = m_pplr->GetSide(); + StructGob::DrawAnimation(pbm, x, y); + Point ptBaseSpecial; + m_ani.GetSpecialPoint(&ptBaseSpecial); + m_aniTurret.Draw(pbm, x + ptBaseSpecial.x, y + ptBaseSpecial.y, side); +} + +void TowerGob::GetClippingBounds(Rect *prc) +{ + StructGob::GetClippingBounds(prc); + Rect rcTurret; + m_aniTurret.GetBounds(&rcTurret); + Point ptBaseSpecial; + m_ani.GetSpecialPoint(&ptBaseSpecial); + rcTurret.Offset(ptBaseSpecial.x + PcFromUwc(m_wx), ptBaseSpecial.y + PcFromUwc(m_wy)); + prc->Union(&rcTurret); +} + +// +// GunTowerGob implementation +// + +static TowerConsts gtwrcGun; + +bool GunTowerGob::InitClass(IniReader *pini) +{ + gtwrcGun.gt = kgtMachineGunTower; + gtwrcGun.ut = kutMachineGunTower; + gtwrcGun.umPrerequisites = kumReactor | kumRadar; + + // Sound effects + + gtwrcGun.sfxAbortRepair = ksfxMachineGunTowerAbortRepair; + gtwrcGun.sfxAttack = ksfxMachineGunTowerAttack; + gtwrcGun.sfxDamaged = ksfxMachineGunTowerDamaged; + gtwrcGun.sfxDestroyed = ksfxMachineGunTowerDestroyed; + gtwrcGun.sfxFire = ksfxMachineGunTowerFire; + gtwrcGun.sfxImpact = ksfxNothing; + gtwrcGun.sfxRepair = ksfxMachineGunTowerRepair; + gtwrcGun.sfxSelect = ksfxMachineGunTowerSelect; + + return TowerGob::InitClass(>wrcGun, pini); +} + +void GunTowerGob::ExitClass() +{ + TowerGob::ExitClass(>wrcGun); +} + +GunTowerGob::GunTowerGob() : TowerGob(>wrcGun) +{ +} + +// UNDONE: UnitGob can handle some of this (e.g., timing) + +bool GunTowerGob::Fire(UnitGob *puntTarget, WCoord wx, WCoord wy, WCoord wdx, WCoord wdy) +{ + // Make sure we're facing the way we want to fire before we try to fire + + Direction16 dir16Fire = CalcDir16(wdx, wdy); + if (m_dir16Turret != dir16Fire) { + m_unvl.MinSkip(); + return false; + } + + // Firing rate is limited by ctFiringRate + + long t = gsim.GetTickCount(); + long ctWait = m_ptwrc->ctFiringRate; + long ctRemaining = ctWait - (t - m_tLastFire); + if (ctRemaining > 0) { + m_unvl.MinSkip((ctRemaining + (kctUpdate / 2)) / kctUpdate - 1); + return false; + } + + m_tLastFire = t; + + // Play firing animation + + StartAnimation(&m_aniTurret, m_ptwrc->anFiringStripIndices[dir16Fire], 0, kfAniResetWhenDone); + gsmm.SendDelayedMsg(kmidFireComplete, m_aniTurret.GetRemainingStripTime(), m_gid, m_gid); + + WCoord wdxRnd = ((GetRandom() & 7) - 3) * kwcTile16th; + WCoord wdyRnd = ((GetRandom() & 7) - 3) * kwcTile16th; + + Point ptTurretSpecial; + m_aniTurret.GetSpecialPoint(&ptTurretSpecial, 1); + Point ptBaseSpecial; + m_ani.GetSpecialPoint(&ptBaseSpecial); + + CreateBulletGob(m_wx + WcFromPc(ptBaseSpecial.x + ptTurretSpecial.x), + m_wy + WcFromPc(ptBaseSpecial.y + ptTurretSpecial.y), + wx + wdxRnd, wy + wdyRnd, + GetDamageTo(puntTarget) / 3, m_gid, puntTarget->GetId()); + + // Two more shots at slight delays + + gsmm.SendDelayedMsg(kmidFire, kctUpdate * 2, m_gid, m_gid); + gsmm.SendDelayedMsg(kmidFire, kctUpdate * 4, m_gid, m_gid); + + // Play sound + + gsndm.PlaySfx(m_ptwrc->sfxFire); + + return true; +} + +#if defined(DEBUG_HELPERS) +char *GunTowerGob::GetName() +{ + return "GunTower"; +} +#endif + +// +// RocketTowerGob implementation +// + +static TowerConsts gtwrcRocket; + +bool RocketTowerGob::InitClass(IniReader *pini) +{ + gtwrcRocket.gt = kgtRocketTower; + gtwrcRocket.ut = kutRocketTower; + gtwrcRocket.umPrerequisites = kumReactor | kumRadar; + + // Sound effects + + gtwrcRocket.sfxAbortRepair = ksfxRocketTowerAbortRepair; + gtwrcRocket.sfxAttack = ksfxRocketTowerAttack; + gtwrcRocket.sfxDamaged = ksfxRocketTowerDamaged; + gtwrcRocket.sfxDestroyed = ksfxRocketTowerDestroyed; + gtwrcRocket.sfxFire = ksfxRocketTowerFire; + gtwrcRocket.sfxImpact = ksfxRocketTowerImpact; + gtwrcRocket.sfxRepair = ksfxRocketTowerRepair; + gtwrcRocket.sfxSelect = ksfxRocketTowerSelect; + return TowerGob::InitClass(>wrcRocket, pini); +} + +void RocketTowerGob::ExitClass() +{ + TowerGob::ExitClass(>wrcRocket); +} + +RocketTowerGob::RocketTowerGob() : TowerGob(>wrcRocket) +{ +} + +#if defined(DEBUG_HELPERS) +char *RocketTowerGob::GetName() +{ + return "RocketTower"; +} +#endif + +// UNDONE: UnitGob can handle some of this (e.g., timing) + +bool RocketTowerGob::Fire(UnitGob *puntTarget, WCoord wx, WCoord wy, WCoord wdx, WCoord wdy) +{ + // Firing rate is limited by ctFiringRate + + long t = gsim.GetTickCount(); + long ctWait = m_ptwrc->ctFiringRate; + long ctRemaining = ctWait - (t - m_tLastFire); + if (ctRemaining > 0) { + m_unvl.MinSkip((ctRemaining + (kctUpdate / 2)) / kctUpdate - 1); + return false; + } + + Direction16 dir16Fire = CalcDir16(wdx, wdy); + if (m_dir16Turret != dir16Fire) + return false; + + m_tLastFire = t; + + // Play firing animation (start on frame 1 where the action is) + + StartAnimation(&m_aniTurret, m_ptwrc->anFiringStripIndices[dir16Fire], 1, kfAniIgnoreFirstAdvance | kfAniResetWhenDone); + gsmm.SendDelayedMsg(kmidFireComplete, m_aniTurret.GetRemainingStripTime(), m_gid, m_gid); + + // Fire off the shot! + + WCoord wdxRnd = ((GetRandom() & 7) - 3) * kwcTile16th; + WCoord wdyRnd = ((GetRandom() & 7) - 3) * kwcTile16th; + Point ptTurretSpecial; + m_aniTurret.GetSpecialPoint(&ptTurretSpecial); + Point ptBaseSpecial; + m_ani.GetSpecialPoint(&ptBaseSpecial); + + // This little hack to handle rocket firing is easier/cheaper than having + // the RocketTankGob override TankGob::Fire + + CreateRocketGob(m_wx + WcFromPc(ptBaseSpecial.x + ptTurretSpecial.x), + m_wy + WcFromPc(ptBaseSpecial.y + ptTurretSpecial.y), + wx + wdxRnd, wy + wdyRnd, GetDamageTo(puntTarget), m_gid, puntTarget->GetId()); + + // Play sound + + gsndm.PlaySfx(m_ptwrc->sfxFire); + + return true; +} + +} // namespace wi diff --git a/game/TriggerActions.cpp b/game/TriggerActions.cpp new file mode 100644 index 0000000..5cbb22c --- /dev/null +++ b/game/TriggerActions.cpp @@ -0,0 +1,1379 @@ +#include "ht.h" + +namespace wi { + +bool Trigger::LoadAction(IniReader *pini, FindProp *pfind) +{ + char sz[1000]; // Must be big to hold large Ecom texts + sz[0] = 0; + int nAction; + int cArgs = pini->GetPropertyValue(pfind, "%d,%s", &nAction, sz); + if (cArgs == 0) + return false; + Assert(strlen(sz) + 1 < sizeof(sz)); + + TriggerAction *pactn; + switch (nAction) { + case knPreserveTriggerTriggerAction: + pactn = new PreserveTriggerAction(); + break; + + case knWaitTriggerAction: + pactn = new WaitAction(); + break; + + case knCenterViewTriggerAction: + pactn = new CenterViewAction(); + break; + + case knSetNextMissionTriggerAction: + pactn = new SetNextMissionAction(); + break; + + case knEndMissionTriggerAction: + pactn = new EndMissionAction(); + break; + + case knEcomTriggerAction: + pactn = new EcomAction(); + break; + + case knSetAllowedUnitsTriggerAction: + pactn = new SetAllowedUnitsAction(); + break; + + case knAlliesTriggerAction: + pactn = new AlliesAction(); + break; + + case knSetObjectiveTriggerAction: + pactn = new SetObjectiveAction(); + break; + + case knSetSwitchTriggerAction: + pactn = new SetSwitchAction(); + break; + + case knDefogAreaTriggerAction: + pactn = new DefogAreaAction(); + break; + + case knCreateUnitGroupTriggerAction: + pactn = new CreateUnitGroupAction(); + break; + + case knHuntTriggerAction: + pactn = new HuntAction(); + break; + + case knCreateRandomUnitGroupTriggerAction: + pactn = new CreateRandomUnitGroupAction(); + break; + + case knStartCountdownTimerTriggerAction: + pactn = new StartCountdownAction(); + break; + + case knModifyCountdownTimerTriggerAction: + pactn = new ModifyCountdownAction(); + break; + + case knRepairTriggerAction: + pactn = new RepairAction(); + break; + + case knEnableReplicatorTriggerAction: + pactn = new EnableReplicatorAction(); + break; + + case knModifyCreditsTriggerAction: + pactn = new ModifyCreditsAction(); + break; + + case knMoveUnitsInAreaTriggerAction: + pactn = new MoveUnitsInAreaAction(); + break; + + case knSetFormalObjectiveTextTriggerAction: + pactn = new SetFormalObjectiveTextAction(); + break; + + case knSetFormalObjectiveStatusTriggerAction: + pactn = new SetFormalObjectiveStatusAction(); + break; + + case knSetFormalObjectiveInfoTriggerAction: + pactn = new SetFormalObjectiveInfoAction(); + break; + + case knShowObjectivesTriggerAction: + pactn = new ShowObjectivesAction(); + break; + + case knCutSceneTriggerAction: + pactn = new CutSceneAction(); + break; + + case knJumpToMissionTriggerAction: + pactn = new JumpToMissionAction(); + break; + + case knModifyPvarTriggerAction: + pactn = new ModifyPvarAction(); + break; + + case knSetPvarTextTriggerAction: + pactn = new SetPvarTextAction(); + break; + + case knShowAlertTriggerAction: + pactn = new ShowAlertAction(); + break; + + case knSetAllowedUpgradesTriggerAction: + pactn = new SetAllowedUpgradesAction(); + break; + + case knSetUpgradesTriggerAction: + pactn = new SetUpgradesAction(); + break; + +#ifdef UNDONE + case knMoveUnitTriggerAction: + pactn = new MoveUnitAction(); + break; + + case knSetPlayerControlsTriggerAction: + pactn = new SetPlayerControlsAction(); + break; + + case knPanViewAction: + pactn = new PanViewTriggerAction(); + break; + + case knTargetUnitTriggerAction: + pactn = new TargetUnitAction(); + break; +#endif + + default: + Assert(false); + break; + } + + // Init it, error if that failed + + Assert(pactn != NULL, "out of memory!"); + if (!pactn->Init(sz)) { + delete pactn; + return false; + } + + // Link it in last + + TriggerAction **ppactn = &m_pactn; + while ((*ppactn) != NULL) + ppactn = &((*ppactn)->m_pactnNext); + *ppactn = pactn; + + return true; +} + +// Action base class implementation + +TriggerAction::TriggerAction() +{ + m_pactnNext = NULL; +} + +// Just here to be overridden by derived classes + +TriggerAction::~TriggerAction() +{ +} + +bool TriggerAction::Init(char *psz) +{ + return true; +} + +bool TriggerAction::LoadState(Stream *pstm) +{ + return true; +} + +bool TriggerAction::SaveState(Stream *pstm) +{ + return true; +} + +// PreserveTriggerAction + +bool PreserveTriggerAction::Perform(Trigger *ptgr, Side side) +{ + ptgr->Arm(side); + return true; +} + +// WaitAction + +WaitAction::WaitAction() +{ + memset(m_afWaitingSide, 0, sizeof(m_afWaitingSide)); +} + +#define knVerWaitActionState 2 +bool WaitAction::LoadState(Stream *pstm) +{ + byte nVer = pstm->ReadByte(); + if (nVer != knVerWaitActionState) + return false; + + pstm->ReadBytesRLE((byte *)m_atStartSide, sizeof(m_atStartSide)); + pstm->ReadBytesRLE((byte *)m_afWaitingSide, sizeof(m_afWaitingSide)); + + return pstm->IsSuccess(); +} + +bool WaitAction::SaveState(Stream *pstm) +{ + pstm->WriteByte(knVerWaitActionState); + + pstm->WriteBytesRLE((byte *)m_atStartSide, sizeof(m_atStartSide)); + pstm->WriteBytesRLE((byte *)m_afWaitingSide, sizeof(m_afWaitingSide)); + + return pstm->IsSuccess(); +} + +bool WaitAction::Init(char *psz) +{ + int cSecs; + if (!ParseNumber(&psz, &cSecs)) + return false; + m_ctWait = cSecs * 100; + return true; +} + +bool WaitAction::Perform(Trigger *ptgr, Side side) +{ + // If in the middle of waiting, check if the wait is over. + + long t = gsim.GetTickCount(); + if (m_afWaitingSide[side]) { + // Wait over? If so, return true + + if (t - m_atStartSide[side] >= m_ctWait) { + m_afWaitingSide[side] = false; + return true; + } + + // Wait not over, return false + + return false; + } + + // Starting the wait, assume wait not over + + m_afWaitingSide[side] = true; + m_atStartSide[side] = t; + return false; +} + +#ifdef DEBUG_HELPERS +char *WaitAction::ToString() +{ + // UNDONE: remove hardcode kside1 + sprintf(s_szDebugHelpers, "Wait %d [%d]", m_ctWait / 100, + (gsim.GetTickCount() - m_atStartSide[kside1]) / 100); + return s_szDebugHelpers; +} +#endif + +// CenterViewAction +// Immediately center the View on the specified Area + +bool CenterViewAction::Init(char *psz) +{ + return ParseArea(&psz, &m_nArea); +} + +bool CenterViewAction::Perform(Trigger *ptgr, Side side) +{ + // Only bother for the local player + + if (side != gpplrLocal->GetSide()) + return true; + + TRect trc; + ggobm.GetAreaRect(m_nArea, &trc, side); + WRect wrc; + wrc.FromTileRect(&trc); + Rect rcSimUI; + ggame.GetSimUIForm()->GetRect(&rcSimUI); + gsim.SetViewPos((wrc.left + wrc.Width() / 2) - (WcFromPc(rcSimUI.Width()) / 2), + (wrc.top + wrc.Height() / 2) - (WcFromPc(rcSimUI.Height()) / 2), true); + return true; +} + +// SetNextMissionAction + +bool SetNextMissionAction::Init(char *psz) +{ + strncpyz(m_szLevel, psz, sizeof(m_szLevel)); + return true; +} + +bool SetNextMissionAction::Perform(Trigger *ptgr, Side side) +{ + // Only bother for the local player + + if (side != gpplrLocal->GetSide()) + return true; + + if (strcmp(m_szLevel, "[none]") == 0) + ggame.SetNextLevel(""); + else + ggame.SetNextLevel(m_szLevel); + return true; +} + +// EndMissionAction + +bool EndMissionAction::Init(char *psz) +{ + return ParseNumber(&psz, &m_nWinLose); +} + +bool EndMissionAction::Perform(Trigger *ptgr, Side side) +{ + Assert(m_pactnNext == NULL, "Action attempted after End Mission"); +#ifdef DEBUG + if (m_nWinLose == knWinLoseTypeWin) + Assert(strcmp(ggame.GetNextLevel(), gsim.GetLevel()->GetFilename()) != 0, "No Next Mission specified"); +#endif + + // Treat local and remote players differently. In a single player game we + // don't allow non-local players to perform EndMission actions (bad + // scripting). + + if (side != gpplrLocal->GetSide() && !gfMultiplayer) + return true; + + // In a multiplayer game we want to notify the local player of the remote + // player's demise. We also want to mark the 'EndMission'ed player as an + // Observer and put them into observing 'mode'. + + if (gfMultiplayer) { + + Player *pplr = gplrm.GetPlayer(side); + if ((pplr->GetFlags() & (kfPlrUnfulfilled | kfPlrObserver)) == 0) { + // Show alert + char szT[100]; + sprintf(szT, m_nWinLose == knWinLoseTypeWin ? "%s is victorious!" : "%s has been defeated!", pplr->GetName()); + ShowAlert(szT); + + // This player is now out, and an observer + pplr->SetFlags(pplr->GetFlags() | kfPlrObserver | kfPlrComputer); + + if (pplr == gpplrLocal) { + // In MP, TriggerMgr::Update() is called synchronously in the + // simulation, so it is not possible to execute modal loops + // such the end mission forms. For this reason, this part is + // done asynchronously. + + Event evt; + memset(&evt, 0, sizeof(evt)); + if (m_nWinLose == knWinLoseTypeWin) { + evt.eType = mpEndMissionWinEvent; + } else { + evt.eType = mpEndMissionLoseEvent; + } + evt.dw = side; + gevm.PostEvent(&evt); + } else { + // A non-local player lost. Have the local player check to + // see if it won. + if (m_nWinLose == knWinLoseTypeLose) { + Event evt; + memset(&evt, 0, sizeof(evt)); + evt.eType = checkGameOverEvent; + evt.dw = pplr->GetId(); + gevm.PostEvent(&evt); + } + } + } + + return true; + } + + // Keep any more triggers from executing + // NOTE: doesn't keep subsequent actions in the same trigger from executing + + gsim.GetLevel()->GetTriggerMgr()->Enable(false); + + // Bring up the Objectives screen + + Event evt; + memset(&evt, 0, sizeof(evt)); + evt.eType = gameOverEvent; + +#ifdef STRESS + if (gfStress) { + evt.dw = knGoAbortLevel; + } else { +#endif + gsim.Pause(true); + evt.dw = gplrm.GetPlayer(side)->ShowObjectives(m_nWinLose == knWinLoseTypeWin ? ksoWinSummary : ksoLoseSummary); + gsim.Pause(false); +#ifdef STRESS + } +#endif + + // UNDONE: post this event even if the app is stopping? + gevm.PostEvent(&evt); + + // if the app was exited while the mission summary was up, return to it. + + return !gevm.IsAppStopping(); +} + +void EndMissionAction::OnMPEndMissionActionEvent(int nWinLose, Side side) { + + Player *pplr = gplrm.GetPlayer(side); + if (pplr != gpplrLocal) { + return; + } + + pplr->ShowObjectives(nWinLose == knWinLoseTypeWin ? + ksoWinSummary : ksoLoseSummary); + + if (ggame.AskObserveGame()) { + + // Cause modal forms to cancel such as build forms, help, options, + // etc. + + gevm.PostEvent(cancelModeEvent); + } else { + Event evt; + memset(&evt, 0, sizeof(evt)); + evt.eType = gameOverEvent; + evt.dw = knGoAbortLevel; + gevm.PostEvent(&evt); + } +} + +// EcomAction + +EcomAction::~EcomAction() +{ + delete m_pszMessage; +} + +bool EcomAction::Init(char *psz) +{ + m_pszMessage = NULL; + int nParsed; + if (!ParseNumber(&psz, &m_nBackground)) + return false; + if (!ParseNumber(&psz, &nParsed)) + return false; + m_fMore = (nParsed == knMoreCloseTypeMore); + if (!ParseNumber(&psz, &m_nCharFrom)) { + Assert(false); // most likely don't have new ecoms in data file + return false; + } + if (!ParseNumber(&psz, &m_nCharTo)) + return false; + m_pszMessage = new char[strlen(psz) + 1]; + if (m_pszMessage != NULL) + strcpy(m_pszMessage, psz); + return true; +} + +bool EcomAction::Perform(Trigger *ptgr, Side side) +{ + // Does nothing in multiplayer + if (gfMultiplayer) { + return true; + } + +// Let any side bring up an ecom. Mission Authors request +#if 0 + // Only bother for the local player + + if (side != gpplrLocal->GetSide()) + return true; +#endif + + // if there's a build form up, pass and try again later + if (gpmfrmm->EcomSuppressed()) + return false; + + // Bring up the Ecom + + gsim.Pause(true); + //fixme + if (!gfMultiplayer) + Ecom(m_nCharFrom, m_nCharTo, m_pszMessage, m_nBackground, m_fMore); + gsim.Pause(false); + + // if the app was exited while the ecom was up, return to this ecom. + + return !gevm.IsAppStopping(); +} + +// SetAllowedUnitsAction + +bool SetAllowedUnitsAction::Init(char *psz) +{ + int nCaSideMask; + if (!ParseNumber(&psz, &nCaSideMask)) + return false; + m_wfCaSideMask = nCaSideMask; + return ParseUnitMask(&psz, &m_um); +} + +bool SetAllowedUnitsAction::Perform(Trigger *ptgr, Side side) +{ + Player *applr[kcSides]; + int cplrs = GetPlayersListFromCaSideMask(side, m_wfCaSideMask, applr); + for (int n = 0; n < cplrs; n++) + applr[n]->SetAllowedUnits(m_um); + return true; +} + +// SetAllowedUpgradesAction + +bool SetAllowedUpgradesAction::Init(char *psz) +{ + int nCaSideMask; + if (!ParseNumber(&psz, &nCaSideMask)) + return false; + m_wfCaSideMask = nCaSideMask; + return ParseUpgradeMask(&psz, &m_upgm); +} + +bool SetAllowedUpgradesAction::Perform(Trigger *ptgr, Side side) +{ + Player *applr[kcSides]; + int cplrs = GetPlayersListFromCaSideMask(side, m_wfCaSideMask, applr); + for (int n = 0; n < cplrs; n++) + applr[n]->SetAllowedUpgrades(m_upgm); + return true; +} + +// SetUpgradesAction + +bool SetUpgradesAction::Init(char *psz) +{ + int nCaSideMask; + if (!ParseNumber(&psz, &nCaSideMask)) + return false; + m_wfCaSideMask = nCaSideMask; + return ParseUpgradeMask(&psz, &m_upgm); +} + +bool SetUpgradesAction::Perform(Trigger *ptgr, Side side) +{ + Player *applr[kcSides]; + int cplrs = GetPlayersListFromCaSideMask(side, m_wfCaSideMask, applr); + word wfUpgrade = 0; + if (m_upgm & kupgmAdvancedHRC) + wfUpgrade |= kfUpgradeHrc; + if (m_upgm & kupgmAdvancedVTS) + wfUpgrade |= kfUpgradeVts; + for (int n = 0; n < cplrs; n++) + applr[n]->SetUpgrades(wfUpgrade); + return true; +} + +// AlliesAction + +bool AlliesAction::Init(char *psz) +{ + int nCaSideMask; + if (!ParseNumber(&psz, &nCaSideMask)) + return false; + m_wfCaSideMaskA = nCaSideMask; + if (!ParseNumber(&psz, &nCaSideMask)) + return false; + m_wfCaSideMaskB = nCaSideMask; + return true; +} + +bool AlliesAction::Perform(Trigger *ptgr, Side side) +{ + SideMask sidmAllies = GetSideMaskFromCaSideMask(side, m_wfCaSideMaskB); + Player *applr[kcSides]; + int cplrs = GetPlayersListFromCaSideMask(side, m_wfCaSideMaskA, applr); + gplrm.SetAllies(applr, cplrs, sidmAllies); + return true; +} + +// SetObjectiveAction + +SetObjectiveAction::SetObjectiveAction() +{ + m_szObjective = NULL; +} + +SetObjectiveAction::~SetObjectiveAction() +{ + if (m_szObjective != NULL) + gmmgr.FreePtr(m_szObjective); +} + +bool SetObjectiveAction::Init(char *psz) +{ + int nCaSideMask; + if (!ParseNumber(&psz, &nCaSideMask)) + return false; + m_wfCaSideMask = nCaSideMask; + + m_szObjective = (char *)gmmgr.AllocPtr(strlen(psz) + 1); + gmmgr.WritePtr(m_szObjective, 0, psz, strlen(psz) + 1); + + return true; +} + +bool SetObjectiveAction::Perform(Trigger *ptgr, Side side) +{ + Player *applr[kcSides]; + int cplrs = GetPlayersListFromCaSideMask(side, m_wfCaSideMask, applr); + for (int n = 0; n < cplrs; n++) + applr[n]->SetObjective(m_szObjective); + return true; +} + +// SetSwitchAction + +bool SetSwitchAction::Init(char *psz) +{ + if (!ParseNumber(&psz, &m_iSwitch)) + return false; + Assert(m_iSwitch < kcSwitchMax); + + int nOnOff; + if (!ParseNumber(&psz, &nOnOff)) + return false; + m_fOn = nOnOff == 1; + return true; +} + +bool SetSwitchAction::Perform(Trigger *ptgr, Side side) +{ + gsim.GetLevel()->GetTriggerMgr()->SetSwitch(m_iSwitch, m_fOn); + return true; +} + +// DefogAreaAction + +bool DefogAreaAction::Init(char *psz) +{ + return ParseArea(&psz, &m_nArea); +} + +bool DefogAreaAction::Perform(Trigger *ptgr, Side side) +{ +#if 0 + // Only bother for the local player + + if (side != gpplrLocal->GetSide()) + return true; +#endif + + TRect trc; + Level *plvl = gsim.GetLevel(); + ggobm.GetAreaRect(m_nArea, &trc, side); + WCoord wxView, wyView; + gsim.GetViewPos(&wxView, &wyView); + plvl->GetFogMap()->Reveal(&trc, gpupdSim, wxView, wyView); + return true; +} + +// CreateUnitGroupAction + +bool CreateUnitGroupAction::Init(char *psz) +{ + return ParseNumber(&psz, &m_nUnitGroup); +} + +bool CreateUnitGroupAction::Perform(Trigger *ptgr, Side side) +{ + gsim.GetLevel()->GetUnitGroupMgr()->ActivateUnitGroup(m_nUnitGroup); + return true; +} + +// CreateRandomUnitGroupAction + +bool CreateRandomUnitGroupAction::Init(char *psz) +{ + return true; +} + +bool CreateRandomUnitGroupAction::Perform(Trigger *ptgr, Side side) +{ + gsim.GetLevel()->GetUnitGroupMgr()->ActivateRandomUnitGroup(); + return true; +} + +// HuntAction + +bool HuntAction::Init(char *psz) +{ + if (!ParseUnitMask(&psz, &m_um1)) + return false; + int nCaSideMask; + if (!ParseNumber(&psz, &nCaSideMask)) + return false; + m_wfCaSideMask1 = nCaSideMask; + + if (!ParseUnitMask(&psz, &m_um2)) + return false; + if (!ParseNumber(&psz, &nCaSideMask)) + return false; + m_wfCaSideMask2 = nCaSideMask; + return true; +} + +// UNDONE: since hunting units start by producing a path to the target, +// path creation is expensive, and there may be a lot of hunting units +// set things up so that the hunt actions are performed over time. +// E.g., have 5 units hunt per update. + +bool HuntAction::Perform(Trigger *ptgr, Side side) +{ + // UNDONE: enumerate all units that match SideMask1 & UnitMask1 and + // direct them to hunt units that match SideMask2 & UnitMask2 + + // Make a list of valid targets + + // Loop through all + return true; +} + +// StartCountdownAction + +StartCountdownAction::StartCountdownAction() +{ + m_szCountdown = NULL; +} + +StartCountdownAction::~StartCountdownAction() +{ + if (m_szCountdown != NULL) + gmmgr.FreePtr(m_szCountdown); +} + +bool StartCountdownAction::Init(char *psz) +{ + if (!ParseNumber(&psz, &m_nSecs)) + return false; + + m_szCountdown = (char *)gmmgr.AllocPtr(strlen(psz) + 1); + gmmgr.WritePtr(m_szCountdown, 0, psz, strlen(psz) + 1); + + return true; +} + +bool StartCountdownAction::Perform(Trigger *ptgr, Side side) +{ + CountdownTimer *pcdt = gsim.GetLevel()->GetTriggerMgr()->GetCountdownTimer(); + Assert(pcdt != 0); + pcdt->SetTimer(m_nSecs, m_szCountdown); + pcdt->StartTimer(true); + + return true; +} + +// ModifyCountdownAction + +bool ModifyCountdownAction::Init(char *psz) +{ + if (!ParseNumber(&psz, &m_nAction)) + return false; + + return true; +} + +bool ModifyCountdownAction::Perform(Trigger *ptgr, Side side) +{ + CountdownTimer *pct = gsim.GetLevel()->GetTriggerMgr()->GetCountdownTimer(); + switch (m_nAction) { + case knModifyCountdownTypeStop: + pct->StartTimer(false); + break; + case knModifyCountdownTypeResume: + pct->StartTimer(true); + break; + case knModifyCountdownTypeHide: + pct->ShowTimer(false); + break; + case knModifyCountdownTypeShow: + pct->ShowTimer(true); + break; + default: + Assert(false); + } + return true; +} + +#ifdef DEBUG_HELPERS +char *ModifyCountdownAction::ToString() +{ + switch (m_nAction) { + case knModifyCountdownTypeStop: + sprintf(s_szDebugHelpers, "Stop Countdown"); + break; + case knModifyCountdownTypeResume: + sprintf(s_szDebugHelpers, "Resume Countdown"); + break; + case knModifyCountdownTypeHide: + sprintf(s_szDebugHelpers, "Hide Countdown"); + break; + case knModifyCountdownTypeShow: + sprintf(s_szDebugHelpers, "Show Countdown"); + break; + default: + Assert(false); + } + return s_szDebugHelpers; +} +#endif + +// RepairAction + +bool RepairAction::Init(char *psz) +{ + int nCaSideMask; + if (!ParseNumber(&psz, &nCaSideMask)) + return false; + m_wfCaSideMask = nCaSideMask; + + int nOnOff; + if (!ParseNumber(&psz, &nOnOff)) + return false; + m_fOn = nOnOff == 1; + return true; +} + +bool RepairAction::Perform(Trigger *ptgr, Side side) +{ + Player *applr[kcSides]; + int cplrs = GetPlayersListFromCaSideMask(side, m_wfCaSideMask, applr); + for (int n = 0; n < cplrs; n++) + applr[n]->Repair(m_fOn); + + return true; +} + +// EnableReplicatorAction + +bool EnableReplicatorAction::Init(char *psz) +{ + int nCaSideMask; + if (!ParseNumber(&psz, &nCaSideMask)) + return false; + m_wfCaSideMask = nCaSideMask; + + int nOnOff; + if (!ParseNumber(&psz, &nOnOff)) + return false; + m_fOn = nOnOff == 1; + return true; +} + +bool EnableReplicatorAction::Perform(Trigger *ptgr, Side side) +{ + SideMask sidm = GetSideMaskFromCaSideMask(side, m_wfCaSideMask); + + for (Gob *pgob = ggobm.GetFirstGob(); pgob != NULL; pgob = ggobm.GetNextGob(pgob)) { + if (pgob->GetType() == kgtReplicator && (sidm & GetSideMask(pgob->GetSide())) != 0) { + ((ReplicatorGob *)pgob)->Enable(m_fOn); + } + } + + return true; +} + +// ModifyCreditsAction + +bool ModifyCreditsAction::Init(char *psz) +{ + if (!ParseNumber(&psz, &m_nAction)) + return false; + + int nCaSideMask; + if (!ParseNumber(&psz, &nCaSideMask)) + return false; + m_wfCaSideMask = nCaSideMask; + + if (!ParseLong(&psz, &m_nAmount)) + return false; + + return true; +} + +bool ModifyCreditsAction::Perform(Trigger *ptgr, Side side) +{ + Player *applr[kcSides]; + int cplrs = GetPlayersListFromCaSideMask(side, m_wfCaSideMask, applr); + for (int n = 0; n < cplrs; n++) { + Player *pplr = applr[n]; + + switch (m_nAction) { + case knModifyNumberTypeSet: + pplr->SetCredits(m_nAmount, true); + break; + + case knModifyNumberTypeAdd: + { + long nCredits = pplr->GetCredits() + m_nAmount; + if (nCredits < 0) + nCredits = 0; + pplr->SetCredits(nCredits, true); + } + break; + + case knModifyNumberTypeSubtract: + { + long nCredits = pplr->GetCredits() - m_nAmount; + if (nCredits < 0) + nCredits = 0; + pplr->SetCredits(nCredits, true); + } + break; + + default: + Assert(false); + } + } + + return true; +} + +#ifdef DEBUG_HELPERS +char *ModifyCreditsAction::ToString() +{ + switch (m_nAction) { + case knModifyNumberTypeSet: + sprintf(s_szDebugHelpers, "Set Credits"); + break; + + case knModifyNumberTypeAdd: + sprintf(s_szDebugHelpers, "Add Credits"); + break; + + case knModifyNumberTypeSubtract: + sprintf(s_szDebugHelpers, "Subtract Credits"); + break; + + default: + Assert(false); + } + return s_szDebugHelpers; +} +#endif + +// MoveUnitsInAreaAction + +bool MoveUnitsInAreaAction::Init(char *psz) +{ + int nCaSideMask; + if (!ParseNumber(&psz, &nCaSideMask)) + return false; + m_wfCaSideMask = nCaSideMask; + + if (!ParseUnitMask(&psz, &m_um)) + return false; + + if (!ParseArea(&psz, &m_nAreaSrc)) + return false; + + return ParseArea(&psz, &m_nAreaDst); +} + +bool MoveUnitsInAreaAction::Perform(Trigger *ptgr, Side side) +{ + // Store the gobs on the stack + + Assert(kcpgobMax / 2 * sizeof(Gob *) <= 1536); + Gob *apgob[kcpgobMax / 2]; + + // Find all the relevent units in AreaSrc + + Enum enm; + UnitMask umFind = m_um & kumMobileUnits; + SideMask sidmFind = GetSideMaskFromCaSideMask(side, m_wfCaSideMask); + Gob **ppgobT = apgob; + Gob *pgob; + while ((pgob = ggobm.EnumGobsInArea(&enm, m_nAreaSrc, sidmFind, umFind)) != NULL) { + *ppgobT++ = pgob; + if (ppgobT - apgob >= ARRAYSIZE(apgob)) + break; + } + + // Send them off to AreaDst + + int cpmunt = ppgobT - apgob; + if (cpmunt > 0) { + TRect trc; + ggobm.GetAreaRect(m_nAreaDst, &trc, side); + MoveUnitsToArea((MobileUnitGob **)apgob, cpmunt, &trc); + } + + return true; +} + +void MoveUnitsToArea(MobileUnitGob **apmunt, int cpmunt, TRect *ptrc) +{ + // Figure out min speed + + MobileUnitGob **ppmunt = apmunt; + WCoord wcMoveDistPerUpdate = kwcMax; + int ipmunt; + for (ipmunt = 0; ipmunt < cpmunt; ipmunt++, ppmunt++) { + MobileUnitGob *pmunt = *ppmunt; + Assert((pmunt->GetFlags() & kfGobMobileUnit) != 0); + + // Get min speed per update + + MobileUnitConsts *pmuntc = (MobileUnitConsts *)pmunt->GetConsts(); + if (pmuntc->GetMoveDistPerUpdate() < wcMoveDistPerUpdate) + wcMoveDistPerUpdate = pmuntc->GetMoveDistPerUpdate(); + } + + // Send commands + + TCoord tcRadius = RadiusFromUnitCount(cpmunt) + 1; + ppmunt = apmunt; + for (ipmunt = 0; ipmunt < cpmunt; ipmunt++, ppmunt++) { + MobileUnitGob *pmunt = *ppmunt; + + // Tell it where to go. + + Point ptCenter; + ptrc->GetCenter(&ptCenter); + SendMoveAction(pmunt->GetId(), WcFromTc(ptCenter.x), WcFromTc(ptCenter.y), tcRadius, wcMoveDistPerUpdate); + } +} + +// SetFormalObjectiveTextAction + +SetFormalObjectiveTextAction::SetFormalObjectiveTextAction() +{ + m_szObjective = NULL; +} + +SetFormalObjectiveTextAction::~SetFormalObjectiveTextAction() +{ + if (m_szObjective != NULL) + gmmgr.FreePtr(m_szObjective); +} + +bool SetFormalObjectiveTextAction::Init(char *psz) +{ + if (!ParseNumber(&psz, &m_iObjective)) + return false; + Assert(m_iObjective < kcFormalObjectivesMax, "Objective number (%d) exceeds range", m_iObjective); + + m_szObjective = (char *)gmmgr.AllocPtr(strlen(psz) + 1); + gmmgr.WritePtr(m_szObjective, 0, psz, strlen(psz) + 1); + + return true; +} + +bool SetFormalObjectiveTextAction::Perform(Trigger *ptgr, Side side) +{ + Player *pplr = gplrm.GetPlayer(side); + pplr->SetFormalObjectiveText(m_iObjective, m_szObjective); + return true; +} + +// SetFormalObjectiveStatusAction + +SetFormalObjectiveStatusAction::SetFormalObjectiveStatusAction() +{ + m_szStatus = NULL; +} + +SetFormalObjectiveStatusAction::~SetFormalObjectiveStatusAction() +{ + if (m_szStatus != NULL) + gmmgr.FreePtr(m_szStatus); +} + +bool SetFormalObjectiveStatusAction::Init(char *psz) +{ + if (!ParseNumber(&psz, &m_iObjective)) + return false; + Assert(m_iObjective < kcFormalObjectivesMax, "Objective number (%d) exceeds range", m_iObjective); + + m_szStatus = (char *)gmmgr.AllocPtr(strlen(psz) + 1); + gmmgr.WritePtr(m_szStatus, 0, psz, strlen(psz) + 1); + + return true; +} + +bool SetFormalObjectiveStatusAction::Perform(Trigger *ptgr, Side side) +{ + Player *pplr = gplrm.GetPlayer(side); + pplr->SetFormalObjectiveStatus(m_iObjective, m_szStatus); + return true; +} + +// ShowObjectivesAction + +bool ShowObjectivesAction::Perform(Trigger *ptgr, Side side) +{ + // Only bother for the local player + + if (side != gpplrLocal->GetSide()) + return true; + + // Bring up the Objectives form + + if (!gfMultiplayer) { + gsim.Pause(true); + gplrm.GetPlayer(side)->ShowObjectives(ksoObjectives, true); + gsim.Pause(false); + } else { + // Make sure this the action won't be executed again while + // nested inside of Player::ShowObjective's input loop. + + ptgr->SetCurrentActionComplete(side); + + // Show asynchronously so TriggerMgr::Update() doesn't block + Event evt; + memset(&evt, 0, sizeof(evt)); + evt.eType = mpShowObjectivesEvent; + evt.dw = side; + gevm.PostEvent(&evt); + } + + // if the app was exited while the objectives screen was up, return to it. + + return !gevm.IsAppStopping(); +} + +void ShowObjectivesAction::OnMPShowObjectivesEvent(Side side) { + // Don't pause the simulation + gplrm.GetPlayer(side)->ShowObjectives(ksoObjectives, true); +} + +// SetFormalObjectiveInfoAction + +SetFormalObjectiveInfoAction::SetFormalObjectiveInfoAction() +{ + m_szInfo = NULL; +} + +SetFormalObjectiveInfoAction::~SetFormalObjectiveInfoAction() +{ + if (m_szInfo != NULL) + gmmgr.FreePtr(m_szInfo); +} + +bool SetFormalObjectiveInfoAction::Init(char *psz) +{ + m_szInfo = (char *)gmmgr.AllocPtr(strlen(psz) + 1); + gmmgr.WritePtr(m_szInfo, 0, psz, strlen(psz) + 1); + + return true; +} + +bool SetFormalObjectiveInfoAction::Perform(Trigger *ptgr, Side side) +{ + Player *pplr = gplrm.GetPlayer(side); + pplr->SetFormalObjectiveInfo(m_szInfo); + return true; +} + +// CutSceneAction + +CutSceneAction::CutSceneAction() +{ + m_pszMessage = NULL; +} + +CutSceneAction::~CutSceneAction() +{ + if (m_pszMessage != NULL) + gmmgr.FreePtr(m_pszMessage); +} + +bool CutSceneAction::Init(char *psz) +{ + m_pszMessage = (char *)gmmgr.AllocPtr(strlen(psz) + 1); + gmmgr.WritePtr(m_pszMessage, 0, psz, strlen(psz) + 1); + + return true; +} + +bool CutSceneAction::Perform(Trigger *ptgr, Side side) +{ + // Does nothing in multiplayer + if (gfMultiplayer) { + return true; + } + + // UNDONE: clear screen for cleaner palette transition? + + CutScene(m_pszMessage, true); + + // if the app was exited while the cut scene was up, return to it + + return !gevm.IsAppStopping(); +} + +// JumpToMissionAction + +bool JumpToMissionAction::Init(char *psz) +{ + strncpyz(m_szLevel, psz, sizeof(m_szLevel)); + return true; +} + +bool JumpToMissionAction::Perform(Trigger *ptgr, Side side) +{ + // Does nothing in multiplayer + if (gfMultiplayer) { + return true; + } + + Assert(m_pactnNext == NULL, "Action attempted after End Mission"); + + ggame.SetNextLevel(m_szLevel); + + // Keep any more triggers from executing + // NOTE: doesn't keep subsequent actions in the same trigger from executing + + gsim.GetLevel()->GetTriggerMgr()->Enable(false); + + Event evt; + memset(&evt, 0, sizeof(evt)); + evt.eType = gameOverEvent; + evt.dw = knGoSuccess; + + gevm.PostEvent(&evt); + + return true; +} + +// ModifyPvarAction + +bool ModifyPvarAction::Init(char *psz) +{ + if (!ParseString(&psz, m_szName)) + return false; + Assert(strlen(m_szName) < kcbPvarNameMax); + + if (!ParseNumber(&psz, &m_nAction)) + return false; + + if (!ParseLong(&psz, &m_nAmount)) + return false; + + return true; +} + +bool ModifyPvarAction::Perform(Trigger *ptgr, Side side) +{ + int nAmount = m_nAmount; + + switch (m_nAction) { + case knModifyNumberTypeAdd: + { + char szT[kcbPvarValueMax]; + if (ggame.GetVar(m_szName, szT, sizeof(szT))) + nAmount = atoi(szT) + m_nAmount; + } + break; + + case knModifyNumberTypeSubtract: + { + char szT[kcbPvarValueMax]; + if (ggame.GetVar(m_szName, szT, sizeof(szT))) + nAmount = atoi(szT) - m_nAmount; + } + break; + + + case knModifyNumberTypeSet: +// Not in v1.1 by accident +#if 0 + { + char szT[kcbPvarValueMax]; + if (ggame.GetVar(m_szName, szT, sizeof(szT))) + nAmount = atoi(szT); + } +#endif + break; + + default: + Assert(false); + break; + } + + char szT[20]; + itoa(nAmount, szT, 10); + ggame.SetVar(m_szName, szT); + return true; +} + +#ifdef DEBUG_HELPERS +char *ModifyPvarAction::ToString() +{ + switch (m_nAction) { + case knModifyNumberTypeSet: + sprintf(s_szDebugHelpers, "Set Pvar"); + break; + + case knModifyNumberTypeAdd: + sprintf(s_szDebugHelpers, "Add to Pvar"); + break; + + case knModifyNumberTypeSubtract: + sprintf(s_szDebugHelpers, "Subtract from Pvar"); + break; + + default: + Assert(false); + } + return s_szDebugHelpers; +} +#endif + +// SetPvarTextAction + +bool SetPvarTextAction::Init(char *psz) +{ + if (!ParseString(&psz, m_szName)) + return false; + Assert(strlen(m_szName) < kcbPvarNameMax); + + strncpyz(m_szValue, psz, sizeof(m_szValue)); + return true; +} + +bool SetPvarTextAction::Perform(Trigger *ptgr, Side side) +{ + ggame.SetVar(m_szName, m_szValue); + return true; +} + +// ShowAlertAction + +bool ShowAlertAction::Init(char *psz) +{ + strncpyz(m_szAlert, psz, sizeof(m_szAlert)); + return true; +} + +bool ShowAlertAction::Perform(Trigger *ptgr, Side side) +{ + ShowAlert(m_szAlert); + return true; +} + +} // namespace wi diff --git a/game/TriggerConditions.cpp b/game/TriggerConditions.cpp new file mode 100644 index 0000000..11017a1 --- /dev/null +++ b/game/TriggerConditions.cpp @@ -0,0 +1,591 @@ +#include "ht.h" + +namespace wi { + +bool Trigger::LoadCondition(IniReader *pini, FindProp *pfind) +{ + char sz[128]; + sz[0] = 0; + int nCondition; + int cArgs = pini->GetPropertyValue(pfind, "%d,%s", &nCondition, sz); + if (cArgs == 0) + return false; + + Condition *pcdn = NULL; + switch (nCondition) { + case knMissionLoadedCondition: + pcdn = new MissionLoadedCondition(); + break; + + case knCreditsCondition: + pcdn = new CreditsCondition(); + break; + + case knOwnsUnitsCondition: + pcdn = new OwnsUnitsCondition(); + break; + + case knAreaContainsUnitsCondition: + pcdn = new AreaContainsUnitsCondition(); + break; + + case knPlaceStructureModeCondition: + pcdn = new PlaceStructureModeCondition(); + break; + + case knMinerCantFindGalaxiteCondition: + pcdn = new MinerCantFindGalaxiteCondition(); + break; + + case knGalaxiteCapacityReachedCondition: + pcdn = new GalaxiteCapacityReachedCondition(); + break; + + case knElapsedTimeCondition: + pcdn = new ElapsedTimeCondition(); + break; + + case knSwitchCondition: + pcdn = new SwitchCondition(); + break; + + case knPeriodicTimerCondition: + pcdn = new PeriodicTimerCondition(); + break; + + case knDiscoversSideCondition: + pcdn = new DiscoversSideCondition(); + break; + + case knCountdownTimerCondition: + pcdn = new CountdownTimerCondition(); + break; + + case knTestPvarCondition: + pcdn = new TestPvarCondition(); + break; + + case knHasUpgradesCondition: + pcdn = new HasUpgradesCondition(); + break; + +#ifdef UNDONE + case knUnitDestroyedCondition: + pcdn = new UnitDestroyedCondition(); + break; + + case knDeathsCondition: + pcdn = new DeathsCondition(); + break; + + case knMobileHQDeployableCondition: + pcdn = new MobileHQDeployableCondition(); + break; + + case knMobileHQDeployedCondition: + pcdn = new MobileHQDeployedCondition(); + break; + + case knUnitSeesUnitCondition: + pcdn = new UnitSeesUnitCondition(); + break; + + case knUnitDestroyedCondition: + pcdn = new UnitDestroyedCondition(); + break; +#endif + + default: + Assert(false); + break; + } + + // Init it, error if that failed + + Assert(pcdn != NULL, "out of memory!"); + if (!pcdn->Init(sz)) { + delete pcdn; + return false; + } + + // Link it in last + + Condition **ppcdn = &m_pcdn; + while ((*ppcdn) != NULL) + ppcdn = &((*ppcdn)->m_pcdnNext); + *ppcdn = pcdn; + + return true; +} + +// Condition base class implementation + +Condition::Condition() +{ + m_pcdnNext = NULL; +} + +bool Condition::Init(char *psz) +{ + return true; +} + +#ifdef DEBUG_HELPERS +bool Condition::SafeIsTrue(Side side) +{ + return IsTrue(side); +} +#endif + +// MissionLoadedCondition + +// REMOVE_SOMEDAY: this is here to keep m68k-gcc from generating a default +// constructor in the .text section +MissionLoadedCondition::MissionLoadedCondition() +{ +} + +bool MissionLoadedCondition::IsTrue(Side side) +{ + return true; +} + +// CreditsCondition + +// REMOVE_SOMEDAY: this is here to keep m68k-gcc from generating a default +// constructor in the .text section +CreditsCondition::CreditsCondition() +{ +} + +bool CreditsCondition::Init(char *psz) +{ + int nCaSideMask; + if (!ParseNumber(&psz, &nCaSideMask)) + return false; + m_wfCaSideMask = nCaSideMask; + return m_qnum.Parse(&psz); +} + +bool CreditsCondition::IsTrue(Side side) +{ + Player *applr[kcSides]; + int cplrs = GetPlayersListFromCaSideMask(side, m_wfCaSideMask, applr); + + // Add up a total credits for all the sides in question + + long cCredits = 0; + for (int n = 0; n < cplrs; n++) + cCredits += applr[n]->GetCredits(); + + // Does the total credit count meet our threshold? + + return m_qnum.Compare(cCredits); +} + +// OwnsUnitsCondition + +// REMOVE_SOMEDAY: this is here to keep m68k-gcc from generating a default +// constructor in the .text section +OwnsUnitsCondition::OwnsUnitsCondition() +{ +} + +bool OwnsUnitsCondition::Init(char *psz) +{ + int nCaSideMask; + if (!ParseNumber(&psz, &nCaSideMask)) + return false; + m_wfCaSideMask = nCaSideMask; + if (!m_qnum.Parse(&psz)) + return false; + return ParseUnitMask(&psz, &m_um); +} + +bool OwnsUnitsCondition::IsTrue(Side side) +{ + Player *applr[kcSides]; + int cplrs = GetPlayersListFromCaSideMask(side, m_wfCaSideMask, applr); + + // Add up the number of Units owned by these players + + int cunt = 0; + for (int n = 0; n < cplrs; n++) + cunt += applr[n]->GetUnitActiveCountFromMask(m_um); + + // Does the total Unit count meet our threshold? + + return m_qnum.Compare(cunt); +} + +// AreaContainsUnitsCondition + +// REMOVE_SOMEDAY: this is here to keep m68k-gcc from generating a default +// constructor in the .text section +AreaContainsUnitsCondition::AreaContainsUnitsCondition() +{ + m_nVersionLevel = gsim.GetLevel()->GetVersion(); +} + +bool AreaContainsUnitsCondition::Init(char *psz) +{ + if (!ParseNumber(&psz, &m_nArea)) + return false; + if (!m_qnum.Parse(&psz)) + return false; + if (!ParseUnitMask(&psz, &m_um)) + return false; + int nCaSideMask; + if (!ParseNumber(&psz, &nCaSideMask)) + return false; + m_wfCaSideMask = nCaSideMask; + return true; +} + +bool AreaContainsUnitsCondition::IsTrue(Side side) +{ + // Find the Gobs in the Area. + + SideMask sidm = GetSideMaskFromCaSideMask(side, m_wfCaSideMask); + + // This logic has an error. It is here for backwards compatibility. + // CheckUnitsInArea returns true if a desired unit type and a desired + // side are in an area, but not if desired unit type is of desired side. + + if (m_nVersionLevel == 0) { + switch (m_qnum.m_nNumber) { + case 0: + if (m_qnum.m_nQualifier == knQualifierExactly) + return !ggobm.CheckUnitsInArea(m_nArea, sidm, m_um); + break; + + case 1: + if (m_qnum.m_nQualifier == knQualifierAtLeast) + return ggobm.CheckUnitsInArea(m_nArea, sidm, m_um); + break; + } + } + + // Count gobs in area + + Enum enm; + int cgobsMatch = 0; + while (ggobm.EnumGobsInArea(&enm, m_nArea, sidm, m_um) != NULL) + cgobsMatch++; + + // Does the total Unit count meet our threshold? + + return m_qnum.Compare(cgobsMatch); +} + +#ifdef DEBUG_HELPERS +char *AreaContainsUnitsCondition::ToString() +{ + sprintf(s_szDebugHelpers, "area \"%s\" contains %s %s owned by %s", ggobm.GetAreaName(m_nArea), m_qnum.ToString(), PszFromUnitMask(m_um), PszFromCaSideMask(m_wfCaSideMask)); + return s_szDebugHelpers; +} +#endif + +// PlaceStructureModeCondition + +// REMOVE_SOMEDAY: this is here to keep m68k-gcc from generating a default +// constructor in the .text section +PlaceStructureModeCondition::PlaceStructureModeCondition() +{ +} + +bool PlaceStructureModeCondition::IsTrue(Side side) +{ + return gpfrmPlace->GetOwner() != NULL; +} + +// MinerCantFindGalaxiteCondition + +// REMOVE_SOMEDAY: this is here to keep m68k-gcc from generating a default +// constructor in the .text section +MinerCantFindGalaxiteCondition::MinerCantFindGalaxiteCondition() +{ +} + +bool MinerCantFindGalaxiteCondition::Init(char *psz) +{ + int nCaSideMask; + if (!ParseNumber(&psz, &nCaSideMask)) + return false; + m_wfCaSideMask = nCaSideMask; + return true; +} + +bool MinerCantFindGalaxiteCondition::IsTrue(Side side) +{ + // Determine the Sides we will be testing against + + SideMask sidmTest = GetSideMaskFromCaSideMask(side, m_wfCaSideMask); + + return gsim.GetLevel()->GetTriggerMgr()->IsConditionTrue(knMinerCantFindGalaxiteCondition, sidmTest); +} + +// GalaxiteCapacityReachedCondition + +// REMOVE_SOMEDAY: this is here to keep m68k-gcc from generating a default +// constructor in the .text section +GalaxiteCapacityReachedCondition::GalaxiteCapacityReachedCondition() +{ +} + +bool GalaxiteCapacityReachedCondition::Init(char *psz) +{ + int nCaSideMask; + if (!ParseNumber(&psz, &nCaSideMask)) + return false; + m_wfCaSideMask = nCaSideMask; + return true; +} + +bool GalaxiteCapacityReachedCondition::IsTrue(Side side) +{ + // Determine the Sides we will be testing against + + SideMask sidmTest = GetSideMaskFromCaSideMask(side, m_wfCaSideMask); + + return gsim.GetLevel()->GetTriggerMgr()->IsConditionTrue(knGalaxiteCapacityReachedCondition, sidmTest); +} + +// ElapsedTimeCondition + +// REMOVE_SOMEDAY: this is here to keep m68k-gcc from generating a default +// constructor in the .text section +ElapsedTimeCondition::ElapsedTimeCondition() +{ +} + +bool ElapsedTimeCondition::Init(char *psz) +{ + return m_qnum.Parse(&psz); +} + +bool ElapsedTimeCondition::IsTrue(Side side) +{ + return m_qnum.Compare(gsim.GetTickCount() / 100); +} + +#ifdef DEBUG_HELPERS +char *ElapsedTimeCondition::ToString() +{ + sprintf(s_szDebugHelpers, "elapsed time [%d] is %s", gsim.GetTickCount() / 100, m_qnum.ToString()); + return s_szDebugHelpers; +} +#endif + +// SwitchCondition + +// REMOVE_SOMEDAY: this is here to keep m68k-gcc from generating a default +// constructor in the .text section +SwitchCondition::SwitchCondition() +{ +} + +bool SwitchCondition::Init(char *psz) +{ + if (!ParseNumber(&psz, &m_iSwitch)) + return false; + Assert(m_iSwitch < kcSwitchMax); + + int nOnOff; + if (!ParseNumber(&psz, &nOnOff)) + return false; + m_fOn = nOnOff == 1; + return true; +} + +bool SwitchCondition::IsTrue(Side side) +{ + return gsim.GetLevel()->GetTriggerMgr()->GetSwitch(m_iSwitch) == m_fOn; +} + +#ifdef DEBUG_HELPERS +char *SwitchCondition::ToString() +{ + sprintf(s_szDebugHelpers, "switch \"%s\" is %s", gsim.GetLevel()->GetTriggerMgr()->GetSwitchName(m_iSwitch), + m_fOn ? "on" : "off"); + return s_szDebugHelpers; +} +#endif + +// PeriodicTimerCondition + +// REMOVE_SOMEDAY: this is here to keep m68k-gcc from generating a default +// constructor in the .text section +PeriodicTimerCondition::PeriodicTimerCondition() +{ +} + +bool PeriodicTimerCondition::Init(char *psz) +{ + int csec; + if (!ParseNumber(&psz, &csec)) + return false; + + m_iTimer = gsim.GetLevel()->GetTriggerMgr()->AddPeriodicTimer(csec * 100); + return true; +} + +// Periodic timers only start counting the first time they are tested. +// This way they can sit 'below' other conditions and have deterministic +// firing times. + +bool PeriodicTimerCondition::IsTrue(Side side) +{ + TriggerMgr *ptgrm = gsim.GetLevel()->GetTriggerMgr(); + ptgrm->StartPeriodicTimer(m_iTimer); + return ptgrm->IsPeriodicTimerTriggered(m_iTimer); +} + +#ifdef DEBUG_HELPERS +bool PeriodicTimerCondition::SafeIsTrue(Side side) +{ + return gsim.GetLevel()->GetTriggerMgr()->IsPeriodicTimerTriggered(m_iTimer); +} + +char *PeriodicTimerCondition::ToString() +{ + TriggerMgr *ptgrm = gsim.GetLevel()->GetTriggerMgr(); + long ctPeriod = ptgrm->GetTimerPeriod(m_iTimer); + long ctCountdown = ptgrm->GetTimerCountdown(m_iTimer); + if (ctCountdown == kctTimerNotStarted) + ctCountdown = ctPeriod + 100; + sprintf(s_szDebugHelpers, "every %d [%d] seconds", ctPeriod / 100, (ctPeriod - ctCountdown) / 100); + return s_szDebugHelpers; +} +#endif + +// CountdownTimerCondition + +bool CountdownTimerCondition::Init(char *psz) +{ + return m_qnum.Parse(&psz); +} + +bool CountdownTimerCondition::IsTrue(Side side) +{ + int csecs; + bool fStarted; + fStarted = gsim.GetLevel()->GetTriggerMgr()->GetCountdownTimer()->GetTimer(&csecs); + if (fStarted) + return m_qnum.Compare(csecs); + return false; +} + + +#if 0 +// UnitDestroyedCondition + +bool UnitDestroyedCondition::Init(char *psz) +{ + if (!ParseNumber(&psz, &m_iUnitDestroyed)) + return false; + Assert(m_iUnitDestroyed < kcUnitDestroyedMax); + + int nOnOff; + if (!ParseNumber(&psz, &nOnOff)) + return false; + m_fOn = nOnOff == 1; + return true; +} + +bool UnitDestroyedCondition::IsTrue(Side side) +{ + return gsim.GetLevel()->GetTriggerMgr()->GetUnitDestroyed(m_iUnitDestroyed) == m_fOn; +} +#endif + +// DiscoversSideCondition + +// REMOVE_SOMEDAY: this is here to keep m68k-gcc from generating a default +// constructor in the .text section +DiscoversSideCondition::DiscoversSideCondition() +{ +} + +bool DiscoversSideCondition::Init(char *psz) +{ + int nCaSideMask; + if (!ParseNumber(&psz, &nCaSideMask)) + return false; + m_wfCaSideMaskA = nCaSideMask; + + if (!ParseNumber(&psz, &nCaSideMask)) + return false; + m_wfCaSideMaskB = nCaSideMask; + return true; +} + +bool DiscoversSideCondition::IsTrue(Side side) +{ + SideMask sidmTotalDiscovered = 0; + Player *applr[kcSides]; + int cplrs = GetPlayersListFromCaSideMask(side, m_wfCaSideMaskA, applr); + for (int n = 0; n < cplrs; n++) + sidmTotalDiscovered |= applr[n]->GetDiscoveredSides(); + + return (sidmTotalDiscovered & GetSideMaskFromCaSideMask(side, m_wfCaSideMaskB)) != 0; +} + +// TestPvarCondition + +// REMOVE_SOMEDAY: this is here to keep m68k-gcc from generating a default +// constructor in the .text section +TestPvarCondition::TestPvarCondition() +{ +} + +bool TestPvarCondition::Init(char *psz) +{ + if (!ParseString(&psz, m_szName)) + return false; + Assert(strlen(m_szName) < kcbPvarNameMax); + return m_qnum.Parse(&psz); +} + +bool TestPvarCondition::IsTrue(Side side) +{ + int nValue = 0; + char szT[kcbPvarValueMax]; + if (ggame.GetVar(m_szName, szT, sizeof(szT))) + nValue = atoi(szT); + + return m_qnum.Compare(nValue); +} + +// HasUpgradesCondition + +// REMOVE_SOMEDAY: this is here to keep m68k-gcc from generating a default +// constructor in the .text section +HasUpgradesCondition::HasUpgradesCondition() +{ +} + +bool HasUpgradesCondition::Init(char *psz) +{ + int nCaSideMask; + if (!ParseNumber(&psz, &nCaSideMask)) + return false; + m_wfCaSideMask = nCaSideMask; + + if (!ParseUpgradeMask(&psz, &m_upgm)) + return false; + return true; +} + +bool HasUpgradesCondition::IsTrue(Side side) +{ + UpgradeMask upgm = 0; + Player *applr[kcSides]; + int cplrs = GetPlayersListFromCaSideMask(side, m_wfCaSideMask, applr); + for (int n = 0; n < cplrs; n++) + upgm |= applr[n]->GetUpgradeMask(); + + return (upgm & m_upgm) != 0; +} + +} // namespace wi \ No newline at end of file diff --git a/game/Unit.cpp b/game/Unit.cpp new file mode 100644 index 0000000..9051013 --- /dev/null +++ b/game/Unit.cpp @@ -0,0 +1,1368 @@ +#include "ht.h" + +namespace wi { + +TBitmap *gaptbmScorches[4]; +UnitConsts *gapuntc[kutMax]; +int gnUnitCostMin = 32767; +int gnUnitCostMax = -1; +int gnUnitCostMPMin = 32767; +int gnUnitCostMPMax = -1; +fix gfxMobileUnitArmorStrengthMin = itofx(511); +fix gfxMobileUnitArmorStrengthMax = itofx(-1); +fix gfxStructureArmorStrengthMin = itofx(511); +fix gfxStructureArmorStrengthMax = itofx(-1); +fix gfxInfantryDamageMin = itofx(511); +fix gfxInfantryDamageMax = itofx(-1); +fix gfxVehicleDamageMin = itofx(511); +fix gfxVehicleDamageMax = itofx(-1); +fix gfxStructureDamageMin = itofx(511); +fix gfxStructureDamageMax = itofx(-1); +TCoord gtcFiringRangeMin = 127; +TCoord gtcFiringRangeMax = -1; +WCoord gwcMoveDistPerUpdateMin = 32767; +WCoord gwcMoveDistPerUpdateMax = -1; +int gnPowerSupplyMin = 32767; +int gnPowerSupplyMax = -1; +int gnPowerDemandMin = 32767; +int gnPowerDemandMax = -1; + +UnitConsts *GetUnitConsts(GobType gt) +{ + for (int i = 0; i < kutMax; i++) { + if (gapuntc[i]->gt == gt) + return gapuntc[i]; + } + + return NULL; +} + +void GetPrerequisiteString(char *psz, UnitConsts *puntc) +{ + *psz = 0; + int i; + for (i = 0; i < kutMax; i++) { + if (puntc->umPrerequisites & (1UL << i)) { + if (*psz != 0) + strcat(psz, ", "); + strcat(psz, gapuntc[i]->szLongName); + } + } + + for (i = 0; i < kupgtMax; i++) { + if (puntc->upgmPrerequisites & (1UL << i)) { + if (*psz != 0) + strcat(psz, ", "); + strcat(psz, gaupg[i].szName); + strcat(psz, " upgrade"); + } + } +} + +//=========================================================================== +// UnitGob implementation + +bool UnitGob::InitClass(UnitConsts *puntc, IniReader *pini) +{ + gapuntc[puntc->ut] = puntc; + puntc->um = 1L << puntc->ut; + + char szTemplate[10]; + itoa(puntc->gt, szTemplate, 10); + + puntc->nInfantryDamage = 0; + puntc->nVehicleDamage = 0; + puntc->nStructureDamage = 0; + puntc->nInfantryDamageMP = 0; + puntc->nVehicleDamageMP = 0; + puntc->nStructureDamageMP = 0; + puntc->ctFiringRate = 0; + puntc->tcFiringRange = 0; + + // Required properties + + if (pini->GetPropertyValue(szTemplate, "Cost", "%d", &puntc->nCost) != 1) + return false; + gnUnitCostMin = _min(gnUnitCostMin, puntc->nCost); + gnUnitCostMax = _max(gnUnitCostMax, puntc->nCost); + + int csecTimeToBuild; + if (pini->GetPropertyValue(szTemplate, "TimeToBuild", "%d", &csecTimeToBuild) != 1) + return false; + puntc->cupdTimeToBuild = UpdFromSec(csecTimeToBuild); + if (pini->GetPropertyValue(szTemplate, "TimeToBuildMP", "%d", &csecTimeToBuild) != 1) + puntc->cupdTimeToBuildMP = puntc->cupdTimeToBuild; + else + puntc->cupdTimeToBuildMP = UpdFromSec(csecTimeToBuild); + int pcT; + if (pini->GetPropertyValue(szTemplate, "UIBoundsLeft", "%d", &pcT) != 1) + return false; + puntc->wrcUIBounds.left = pcT * kwcTile16th; // HACK: so we can keep existing GobTemplate UIBounds + if (pini->GetPropertyValue(szTemplate, "UIBoundsTop", "%d", &pcT) != 1) + return false; + puntc->wrcUIBounds.top = pcT * kwcTile16th; // HACK: so we can keep existing GobTemplate UIBounds + if (pini->GetPropertyValue(szTemplate, "UIBoundsRight", "%d", &pcT) != 1) + return false; + puntc->wrcUIBounds.right = pcT * kwcTile16th; // HACK: so we can keep existing GobTemplate UIBounds + if (pini->GetPropertyValue(szTemplate, "UIBoundsBottom", "%d", &pcT) != 1) + return false; + puntc->wrcUIBounds.bottom = pcT * kwcTile16th; // HACK: so we can keep existing GobTemplate UIBounds + if (pini->GetPropertyValue(szTemplate, "SortOffset", "%d", &puntc->wdySortOffset) != 1) + return false; + puntc->wdySortOffset *= 16; // HACK: so we can keep existing GobTemplate sort offsets + if (pini->GetPropertyValue(szTemplate, "Animation", "%s", &puntc->szAniName) != 1) + return false; + if (pini->GetPropertyValue(szTemplate, "Name", "%s", puntc->szName) != 1) { + Assert("Struct/Unit must have 'Name' property"); + return false; + } + Assert(strlen(puntc->szName) < kcbStructUnitName); + + // Optional properties + + int cmsFiringRate; + if (pini->GetPropertyValue(szTemplate, "FiringRate", "%d", &cmsFiringRate) == 1) + puntc->ctFiringRate = cmsFiringRate / 10; + + fix fxDamagePerSec; + if (pini->GetPropertyValue(szTemplate, "InfantryDamage", "%d", &puntc->nInfantryDamage)) { + if (puntc->nInfantryDamage != 0) { + fxDamagePerSec = (fix)mulfx(itofx32(puntc->nInfantryDamage), divfx(itofx(1000), itofx(cmsFiringRate))); + gfxInfantryDamageMin = _min(gfxInfantryDamageMin, fxDamagePerSec); + gfxInfantryDamageMax = _max(gfxInfantryDamageMax, fxDamagePerSec); + } + } + if (pini->GetPropertyValue(szTemplate, "InfantryDamageMP", "%d", &puntc->nInfantryDamageMP) != 1) + puntc->nInfantryDamageMP = puntc->nInfantryDamage; + if (pini->GetPropertyValue(szTemplate, "VehicleDamage", "%d", &puntc->nVehicleDamage)) { + if (puntc->nVehicleDamage != 0) { + fxDamagePerSec = (fix)mulfx(itofx32(puntc->nVehicleDamage), divfx(itofx(1000), itofx(cmsFiringRate))); + gfxVehicleDamageMin = _min(gfxVehicleDamageMin, fxDamagePerSec); + gfxVehicleDamageMax = _max(gfxVehicleDamageMax, fxDamagePerSec); + } + } + if (pini->GetPropertyValue(szTemplate, "VehicleDamageMP", "%d", &puntc->nVehicleDamageMP) != 1) + puntc->nVehicleDamageMP = puntc->nVehicleDamage; + if (pini->GetPropertyValue(szTemplate, "StructureDamage", "%d", &puntc->nStructureDamage)) { + if (puntc->nStructureDamage != 0) { + fxDamagePerSec = (fix)mulfx(itofx32(puntc->nStructureDamage), divfx(itofx(1000), itofx(cmsFiringRate))); + gfxStructureDamageMin = _min(gfxStructureDamageMin, fxDamagePerSec); + gfxStructureDamageMax = _max(gfxStructureDamageMax, fxDamagePerSec); + } + } + if (pini->GetPropertyValue(szTemplate, "StructureDamageMP", "%d", &puntc->nStructureDamageMP) != 1) + puntc->nStructureDamageMP = puntc->nStructureDamage; + + pini->GetPropertyValue(szTemplate, "FiringRange", "%d", &puntc->tcFiringRange); + gtcFiringRangeMin = _min((int)gtcFiringRangeMin, puntc->tcFiringRange); + gtcFiringRangeMax = _max((int)gtcFiringRangeMax, puntc->tcFiringRange); + + char szT[300]; +#ifdef DEBUG + szT[sizeof(szT) - 1] = 0; // guard +#endif + if (pini->GetPropertyValue(szTemplate, "Description", szT, sizeof(szT))) { + + // We dynamically allocate szDescription to save .bss space + + Assert(szT[sizeof(szT) - 1] == 0); + puntc->szDescription = new char[strlen(szT) + 1]; + if (puntc->szDescription != NULL) + strcpy(puntc->szDescription, szT); + } + + if (!pini->GetPropertyValue(szTemplate, "LongName", puntc->szLongName, sizeof(puntc->szLongName))) + strcpy(puntc->szLongName, puntc->szName); + + if (pini->GetPropertyValue(szTemplate, "CostMP", "%d", &puntc->nCostMP) != 1) + puntc->nCostMP = puntc->nCost; + gnUnitCostMPMin = _min(gnUnitCostMPMin, puntc->nCostMP); + gnUnitCostMPMax = _max(gnUnitCostMPMax, puntc->nCostMP); + + puntc->panid = LoadAnimationData(puntc->szAniName); + Assert(puntc->panid != NULL); + if (puntc->panid == NULL) + return false; + + InitFingerUIBounds(puntc); + + // If they aren't already loaded, load the scorch marks + + if (gaptbmScorches[0] == NULL) { + gaptbmScorches[0] = LoadTBitmap("scorch_8x8.tbm"); + gaptbmScorches[1] = LoadTBitmap("scorch_16x16.tbm"); + gaptbmScorches[2] = LoadTBitmap("scorch_32x16.tbm"); + gaptbmScorches[3] = LoadTBitmap("scorch_48x48.tbm"); + } + + return true; +} + +void UnitGob::InitFingerUIBounds(UnitConsts *puntc) +{ + // Finger UI Bounds inflates the UI bounds for finger hit testing + + WRect wrc = puntc->wrcUIBounds; + + // Make sure height is kwcTile * 4, then adjust width to keep aspect + // ratio the same + + int cy = wrc.Height(); + int cx = wrc.Width(); + if (cy < kwcTile * 4) { + cy = kwcTile * 4; + cx = cx * cy * 64 / wrc.Height() / 64; + } + wrc.Inflate((cx - wrc.Width()) / 2, (cy - wrc.Height()) / 2); + puntc->wrcUIBoundsFinger = wrc; +} + +void UnitGob::ExitClass(UnitConsts *puntc) +{ + // If they aren't already deleted, delete the scorch marks + + if (gaptbmScorches[0] != NULL) { + for (int i = 0; i < sizeof(gaptbmScorches) / sizeof(TBitmap *); i++) { + delete gaptbmScorches[i]; + gaptbmScorches[i] = NULL; + } + } + + delete puntc->panid; + puntc->panid = NULL; + + delete puntc->pfrmMenu; + puntc->pfrmMenu = NULL; + + delete puntc->szDescription; + puntc->szDescription = NULL; +} + +UnitGob::UnitGob(UnitConsts *puntc) +{ + m_ff |= kfGobUnit | kfGobStateMachine | kfGobLayerDepthSorted; + m_puntc = puntc; + m_wfUnit = 0; + for (int n = 0; n < ARRAYSIZE(m_agidEnemyNearby); n++) + m_agidEnemyNearby[n] = kgidNull; + + // Just to be clear this Unit hasn't notified anyone about being hit for awhile + + m_cupdLastHitNotify = -1000000; + m_panispr = NULL; +} + +UnitGob::~UnitGob() +{ + delete m_panispr; + m_panispr = NULL; +} + +bool UnitGob::Init(IniReader *pini, FindProp *pfind, const char *pszName) +{ + int wf = 0; + int side, tx, ty; + int nHealth; + int cArgs = pini->GetPropertyValue(pfind, "%*d ,%d ,%d ,%d ,%d ,%d", &side, &tx, &ty, &wf, &nHealth); + if (cArgs < 5) { + Assert("UnitGob requires at least 5 valid initialization parameters"); + return false; + } + + fix fxHealth = (fix)((m_puntc->GetArmorStrength() * (long)nHealth) / 100L); + return Init(WcFromTc(tx), WcFromTc(ty), gplrm.GetPlayer(side), fxHealth, wf, pszName); +} + +bool UnitGob::Init(WCoord wx, WCoord wy, Player *pplr, fix fxHealth, dword ff, const char *pszName) +{ + // UNDONE: stash Name away somewhere + + m_pplr = pplr; + m_wx = WcTrunc(wx); + m_wy = WcTrunc(wy); + + m_ff |= ff; + + m_ani.Init(m_puntc->panid); + StartAnimation(&m_ani, 0, 0, kfAniLoop | kfAniIgnoreFirstAdvance); + + // Initial health is derived from nArmorStrength + + if (fxHealth == 0) + m_fxHealth = m_puntc->GetArmorStrength(); + else + m_fxHealth = fxHealth; + + // Reveal this part of the map + + if (m_pplr == gpplrLocal) { + WCoord wxView, wyView; + gsim.GetViewPos(&wxView, &wyView); + FogMap *pfogm = gsim.GetLevel()->GetFogMap(); + WPoint wpt; + GetCenter(&wpt); + RevealPattern *prvlp = (RevealPattern *)(m_puntc->wf & kfUntcLargeDefog ? grvlpLarge : grvlp); + pfogm->Reveal(TcFromWc(wpt.wx), TcFromWc(wpt.wy), prvlp, gpupdSim, wxView, wyView); + } + + // Add the fresh Gob to the GobMgr. + + ggobm.AddGob(this); + + // Redraw this part of minimap + + TRect trc; + GetTileRect(&trc); + gpmm->RedrawTRect(&trc); + + // Initialize the state machine by sending the first Initialize msg + + gsmm.SendMsg(kmidReservedEnter, m_gid, m_gid); + + return true; +} + +void UnitGob::Delete() +{ + // Get trect for minimap redrawing + + TRect trc; + GetTileRect(&trc); + + // Remove from gid map + + ggobm.RemoveGob(this); + delete this; + + // Redraw this part of minimap after the gob is gone from gobmgr. + + gpmm->RedrawTRect(&trc); +} + +#define knVerUnitGobState 4 +bool UnitGob::LoadState(Stream *pstm) +{ + byte nVer = pstm->ReadByte(); + if (nVer != knVerUnitGobState) + return false; + m_fxHealth = (fix)pstm->ReadWord(); + m_wfUnit = pstm->ReadWord(); + pstm->Read(m_agidEnemyNearby, sizeof(m_agidEnemyNearby)); + m_ani.Init(m_puntc->panid); + m_ani.LoadState(pstm); + m_cupdLastHitNotify = (long)pstm->ReadDword(); + m_cDamageCountdown = pstm->ReadWord(); + + return Gob::LoadState(pstm); +} + +bool UnitGob::SaveState(Stream *pstm) +{ + pstm->WriteByte(knVerUnitGobState); + pstm->WriteWord(m_fxHealth); + pstm->WriteWord(m_wfUnit); + pstm->Write(m_agidEnemyNearby, sizeof(m_agidEnemyNearby)); + m_ani.SaveState(pstm); + pstm->WriteDword(m_cupdLastHitNotify); + pstm->WriteWord(m_cDamageCountdown); + + return Gob::SaveState(pstm); +} + +GobType UnitGob::GetType() +{ + return m_puntc->gt; +} + +bool UnitGob::IsAlly(Side side) +{ + if (m_pplr->GetAllies() & GetSideMask(side)) + return true; + + return false; +} + +void UnitGob::Activate() +{ + m_ff |= kfGobActive; + m_pplr->IncUnitCount(m_puntc->ut); + + // Best place to do this is here, after the unit has been + // fully built, it is counted as having been built. + m_pplr->IncUnitBuiltCount(m_puntc->ut); +} + +void UnitGob::Deactivate() +{ + Assert(m_ff & kfGobActive); + + // make sure this unit's menu is not left up + + UnitConsts *puntc = GetUnitConsts(GetType()); + Assert(puntc->pfrmMenu != NULL); + if (puntc->pfrmMenu->GetOwner() == (UnitGob *)this) + puntc->pfrmMenu->EndForm(kidcCancel); + + m_ff &= ~(kfGobActive | kfGobDrawFlashed); + Select(false); + m_pplr->DecUnitCount(m_puntc->ut); + ClearDamageIndicator(); + + // Delete the highlight sprite + + delete m_panispr; + m_panispr = NULL; +} + +bool UnitGob::IsIdle() +{ + return true; +} + +bool UnitGob::IsValidTarget(Gob *pgobTarget) +{ + return (pgobTarget->GetFlags() & (kfGobUnit | kfGobActive)) == (kfGobUnit | kfGobActive) + && !IsAlly(pgobTarget->GetSide()); +} + +void UnitGob::SetTarget(Gid gid, WCoord wxTarget, WCoord wyTarget, WCoord wxCenter, WCoord wyCenter, TCoord tcRange, WCoord wcMoveDistPerUpdate) +{ +} + +int UnitGob::GetDamageTo(UnitGob *puntTarget) +{ + UnitMask umTarget = puntTarget->GetConsts()->um; + int nDamage = 0; + + if (umTarget & kumInfantry) { + nDamage = m_puntc->GetInfantryDamage(); + } else if (umTarget & kumVehicles) { + nDamage = m_puntc->GetVehicleDamage(); + } else if (umTarget & kumStructures) { + nDamage = m_puntc->GetStructureDamage(); + } + + if (m_pplr->GetHandicap() & kfHcapIncreasedFirePower) + nDamage = ((nDamage * (100 + knIncreasedFirePowerPercent)) + 50) / 100; // +50 for rounding + + return nDamage; +} + +void UnitGob::Draw(DibBitmap *pbm, int xViewOrigin, int yViewOrigin, int nLayer) +{ + // When a unit is displayed as a corpse we want other units draw on top of + // it UNDONE: this affects all unit types and structures that don't + // override ::Draw. It may be better to do this is a more localized way + // (e.g., specific units spawn a corpse/SurfaceDecalGob upon death) + + if ((nLayer == knLayerDepthSorted && m_st != kstDying) || + (nLayer == knLayerSurfaceDecal && m_st == kstDying)) { + +#ifdef DRAW_OCCUPIED_TILE_INDICATOR + { + WRect wrcT; + GetTilePaddedWRect(&wrcT); + Rect rcT; + rcT.FromWorldRect(&wrcT); + rcT.Offset(-xViewOrigin, -yViewOrigin); + DrawBorder(pbm, &rcT, 1, GetColor(kiclrWhite)); + } +#endif + Side side = m_pplr->GetSide(); + if (m_ff & kfGobDrawFlashed) + side = (Side)-1; + else if (m_ff & kfGobBeingBuilt) + side = ksideNeutral; + + int xT = PcFromUwc(m_wx) - xViewOrigin; + int yT = PcFromUwc(m_wy) - yViewOrigin; + m_ani.Draw(pbm, xT, yT, side); + + } else if (nLayer == knLayerSelection) { + if (m_ff & kfGobSelected) { + Rect rcT; + rcT.FromWorldRect(&m_puntc->wrcUIBounds); + rcT.Offset(-xViewOrigin + PcFromWc(m_wx), -yViewOrigin + PcFromWc(m_wy)); + DrawSelectionIndicator(pbm, &rcT, m_fxHealth, m_puntc->GetArmorStrength()); + + // If this Gob was hit recently show its health + + } else if (m_wfUnit & kfUnitDrawHealthIndicator) { + Rect rcT; + rcT.FromWorldRect(&m_puntc->wrcUIBounds); + rcT.Offset(-xViewOrigin + PcFromWc(m_wx), + -yViewOrigin + PcFromWc(m_wy)); + DrawHealthIndicator(pbm, &rcT, m_fxHealth, + m_puntc->GetArmorStrength()); + } + + // If this gob is hilighted, draw the hilight indicator + if (m_wfUnit & kfUnitHilighted) { + if (m_panispr != NULL) { + int xT = PcFromUwc(m_wx) - xViewOrigin; + int yT = PcFromUwc(m_wy) - yViewOrigin; + m_panispr->SetPosition(xT, yT); + m_panispr->SetScale((float)(kwcTile * 4) / (float)m_puntc->wrcUIBounds.Height()); + m_panispr->CaptureFrame(this); + } + } + } +} + +dword UnitGob::GetSortKey() +{ + return MakeSortKey(m_wy + m_puntc->wdySortOffset, m_gid); +} + +// See Gob::GetClippingBounds for a description of what the clipping +// bounds is and how it is used. +// NOTE: the values returned are in pixel coordinates + +void UnitGob::GetClippingBounds(Rect *prc) +{ + // Union of animation and selection + + m_ani.GetBounds(prc); + + if (m_ff & kfGobSelected) { + if (gwfPerfOptions & kfPerfSelectionBrackets) { + Rect rcSel; + rcSel.FromWorldRect(&m_puntc->wrcUIBounds); + rcSel.top -= kcyHealthBar; + if (m_puntc->wf & kfUntcHasFullnessIndicator) + rcSel.bottom += kcyFullnessBar; + prc->Union(&rcSel); + } else { + Rect rcSel; + rcSel.FromWorldRect(&m_puntc->wrcUIBounds); + prc->top = rcSel.top - kcyHealthBar; + prc->left = _min(prc->left, rcSel.left); + prc->right = _max(prc->right, rcSel.right); + if (m_puntc->wf & kfUntcHasFullnessIndicator) + prc->bottom = _max(prc->bottom, rcSel.bottom + kcyFullnessBar); + } + } else if (m_ff & (kfGobBeingUpgraded | kfGobBeingBuilt) || (m_wfUnit & kfUnitDrawHealthIndicator)) { + + // Health is drawn above gob when being upgraded or built + // or if it has been recently damaged + + Rect rcSel; + rcSel.FromWorldRect(&m_puntc->wrcUIBounds); + prc->top = rcSel.top - kcyHealthBar; + prc->left = _min(prc->left, rcSel.left); + prc->right = _max(prc->right, rcSel.right); + } + + prc->Offset(PcFromUwc(m_wx), PcFromUwc(m_wy)); +} + +// See Gob::GetUIBounds for a description of what the UI bounds +// is and how it is used. +// NOTE: the values returned are in world coordinates + +void UnitGob::GetUIBounds(WRect *pwrc) +{ + // Fudging to make it look/feel good. + // These fudge-factors are read from GobTemplates.ini + + pwrc->left = m_wx + m_puntc->wrcUIBounds.left; + pwrc->top = m_wy + m_puntc->wrcUIBounds.top; + pwrc->right = m_wx + m_puntc->wrcUIBounds.right; + pwrc->bottom = m_wy + m_puntc->wrcUIBounds.bottom; +} + +// UNDONE: OPT: not necessarily the fastest way to return the center + +void UnitGob::GetCenter(WPoint *pwpt) +{ + pwpt->wx = m_wx + m_puntc->wrcUIBounds.left + (m_puntc->wrcUIBounds.right - m_puntc->wrcUIBounds.left) / 2; + pwpt->wy = m_wy + m_puntc->wrcUIBounds.top + (m_puntc->wrcUIBounds.bottom - m_puntc->wrcUIBounds.top) / 2; +} + +void UnitGob::GetAttackPoint(WPoint *pwpt) +{ + GetCenter(pwpt); +} + +dword UnitGob::GetAnimationHash() +{ + int nFrame = m_ani.GetFrame(); + int nStrip = m_ani.GetStrip(); + return ((nFrame << 16) | nStrip) ^ (dword)this ^ (GetId() << 16); +} + +void UnitGob::GetAnimationBounds(Rect *prc, bool fBase) +{ + m_ani.GetBounds(prc); +} + +void UnitGob::DrawAnimation(DibBitmap *pbm, int x, int y) +{ + m_ani.Draw(pbm, x, y, m_pplr->GetSide()); +} + +void UnitGob::Select(bool fSelect) +{ + // Set bits + + if (fSelect) { + if (m_ff & kfGobSelected) { + return; + } + m_ff |= kfGobSelected | kfGobLayerSelection; + } else { + if (!(m_ff & kfGobSelected)) { + return; + } + m_ff &= ~kfGobSelected; + if ((m_wfUnit & kfUnitHilighted) == 0) { + m_ff &= ~kfGobLayerSelection; + } + } + MarkRedraw(); + + // Redraw this part of minimap + + TRect trc; + GetTileRect(&trc); + gpmm->RedrawTRect(&trc); +} + +void UnitGob::Hilight(bool fHilight) +{ + if (fHilight) { + if (m_wfUnit & kfUnitHilighted) { + return; + } + m_wfUnit |= kfUnitHilighted; + m_ff |= kfGobLayerSelection; + m_panispr = CreateHilightSprite(); + + // Only need to do this so the sprite captures the current frame + MarkRedraw(); + } else { + if (!(m_wfUnit & kfUnitHilighted)) { + return; + } + m_wfUnit &= ~kfUnitHilighted; + if ((m_ff & kfGobSelected) == 0) { + m_ff &= ~kfGobLayerSelection; + } + delete m_panispr; + m_panispr = NULL; + } +} + +AnimSprite *UnitGob::CreateHilightSprite() +{ + AnimSprite *panispr = gpsprm->CreateAnimSprite(); + if (panispr != NULL) { + panispr->SetPalette(gsim.GetLevel()->GetPalette()); + } + return panispr; +} + +void UnitGob::SetHealth(fix fxHealth) +{ + if (m_wfUnit & kfUnitInvulnerable) + return; + + if (m_fxHealth != fxHealth) { + m_fxHealth = fxHealth; + if ((m_ff & (kfGobBeingBuilt | kfGobSelected)) || (m_wfUnit & kfUnitDrawHealthIndicator)) + MarkRedraw(); + } +} + +void UnitGob::DefUpdate() +{ + // Handle flashing + + if (m_ff & kfGobFlashing) { + m_ff &= ~kfGobFlashing; + m_ff |= kfGobDrawFlashed | kfGobRedraw; + m_unvl.MinSkip(); + gevm.SetRedrawFlags(kfRedrawBeforeTimer); + } else if (m_ff & kfGobDrawFlashed) { + m_ff &= ~kfGobDrawFlashed; + m_ff |= kfGobRedraw; + m_unvl.MinSkip(); + } + + // Handle damage countdown + + if (m_cDamageCountdown != 0 && !(m_ff & kfGobBeingBuilt)) { + m_cDamageCountdown -= m_unvl.GetUpdateCount(); + if (m_cDamageCountdown <= 0) { + m_cDamageCountdown = 0; + + // Time to redraw w/ no damage indicator + + ClearDamageIndicator(); + } else { + m_unvl.MinSkip(m_cDamageCountdown); + } + } +} + +void UnitGob::PopupMenu() +{ + if (m_puntc->pfrmMenu == NULL) + return; + + // UNDONE: show a special being-built menu (Abort only) + if (m_ff & kfGobBeingBuilt) + return; + + // Show the menu and get the user's selection + + Assert(!(m_puntc->pfrmMenu->GetFlags() & kfFrmDoModal)); + + m_puntc->pfrmMenu->SetOwner(this); + gpmfrmm->AddForm(m_puntc->pfrmMenu); + int idc; + m_puntc->pfrmMenu->DoModal(&idc); + gpmfrmm->RemoveForm(m_puntc->pfrmMenu); + + // Act on the user's selection + + OnMenuItemSelected(idc); +} + +bool UnitGob::LoadMenu(UnitConsts *puntc, IniReader *pini, char *pszTemplate, int idfDefault) +{ + int idf; + if (pini->GetPropertyValue(pszTemplate, "Menu", "%d", &idf) != 1) + idf = idfDefault; + + puntc->pfrmMenu = new UnitMenu(); + if (puntc->pfrmMenu == NULL) + return false; + + if (!puntc->pfrmMenu->Init(gpmfrmm, gpiniForms, idf)) + return false; + + // UNDONE: override title label + + gpmfrmm->RemoveForm(puntc->pfrmMenu); + return true; +} + +TCoord UnitGob::CalcRange(TCoord tx, TCoord ty, Gob *pgob) +{ + TPoint tpt; + pgob->GetTilePosition(&tpt); + TCoord tcx = abs(tx - tpt.tx); + TCoord tcy = abs(ty - tpt.ty); + + if (tcx >= 10 || tcy >= 10) + // Lame rectangular range calculator good enough for stuff that + // is almost certainly out of interesting range anyway and it + // allows us to keep the size of the Euclidean distance table down. + + return tcx > tcy ? tcx : tcy; + else + // Nice accurate euclidean distance calculator + + return gmpDistFromDxy[tcx][tcy]; +} + +void UnitGob::RememberEnemyNearby(Gid gidEnemy) +{ + // If this gob is already in our list, nothing to do + + int n; + for (n = 0; n < ARRAYSIZE(m_agidEnemyNearby); n++) { + if (m_agidEnemyNearby[n] == gidEnemy) + return; + } + + // We remember a limited # of gobs, so replace the first one + // we find that is further away than the new enemy. Sometimes + // the gid ids can be recycled into gobs on the same side, so check for that. + + UnitGob *puntEnemy = (UnitGob *)ggobm.GetGob(gidEnemy); + if (puntEnemy == NULL) + return; + TCoord txThis = TcFromWc(m_wx); + TCoord tyThis = TcFromWc(m_wy); + Side sideThis = GetSide(); + + TCoord tcEnemy = CalcRange(txThis, tyThis, puntEnemy); + for (n = 0; n < ARRAYSIZE(m_agidEnemyNearby); n++) { + Gob *puntEnemyNearby = ggobm.GetGob(m_agidEnemyNearby[n]); + if (puntEnemyNearby == NULL || puntEnemyNearby->GetSide() == sideThis) { + m_agidEnemyNearby[n] = gidEnemy; + m_ff &= ~kfGobNoEnemiesNearby; + return; + } + TCoord tc = CalcRange(txThis, tyThis, puntEnemyNearby); + if (tc > tcEnemy) { + m_agidEnemyNearby[n] = gidEnemy; + m_ff &= ~kfGobNoEnemiesNearby; + return; + } + } + + // NOTE: might want to set a "rescan" bit if an enemy doesn't fit in the + // list, the issue being we don't want that bit set most of the time. +} + +UnitGob *UnitGob::FindEnemyNearby(TCoord tcRange) +{ + if (m_ff & kfGobNoEnemiesNearby) + return NULL; + + TCoord txThis = TcFromWc(m_wx); + TCoord tyThis = TcFromWc(m_wy); + Side sideThis = GetSide(); + + int cEmpty = 0; + TCoord tcClosest = tcRange; + UnitGob *puntEnemyClosest = NULL; + + for (int n = 0; n < ARRAYSIZE(m_agidEnemyNearby); n++) { + Gid gidEnemyNearby = m_agidEnemyNearby[n]; + if (gidEnemyNearby == kgidNull) { + cEmpty++; + continue; + } + UnitGob *puntEnemyNearby = (UnitGob *)ggobm.GetGob(gidEnemyNearby); + if (puntEnemyNearby == NULL || puntEnemyNearby->GetSide() == sideThis) { + m_agidEnemyNearby[n] = kgidNull; + cEmpty++; + continue; + } + TCoord tc = CalcRange(txThis, tyThis, puntEnemyNearby); + if (tc <= tcClosest) { + tcClosest = tc; + puntEnemyClosest = puntEnemyNearby; + } + } + + if (puntEnemyClosest != NULL) + return puntEnemyClosest; + + if (cEmpty == ARRAYSIZE(m_agidEnemyNearby)) { +#if defined(DEBUG) && defined(WIN) +// Assert(ggobm.FindEnemyWithinRange(this, tcRange) == NULL, "Lost track of a nearby enemy!"); +#endif + m_ff |= kfGobNoEnemiesNearby; + } + + return NULL; +} + +// NOTE: Max sight distance assumption + +#define ktcSightRangeMax 5 + +void UnitGob::NotifyEnemyNearby() +{ + // OPT: Could scan leading edges only (when moving), as long as + // the edge is 2 tiles thick. Would need to break out cases where the whole + // rect needs to be scanned. + + TCoord ctx, cty; + ggobm.GetMapSize(&ctx, &cty); + TCoord tx = TcFromWc(m_wx); + TCoord ty = TcFromWc(m_wy); + + TCoord txLeft = tx - ktcSightRangeMax; + if (txLeft < 0) + txLeft = 0; + TCoord txRight = tx + ktcSightRangeMax + 1; + if (txRight > ctx) + txRight = ctx; + TCoord tyTop = ty - ktcSightRangeMax; + if (tyTop < 0) + tyTop = 0; + TCoord tyBottom = ty + ktcSightRangeMax + 1; + if (tyBottom > cty) + tyBottom = cty; + + // Let's check for 'discovery' while we're at it + + SideMask sidmAlreadyDiscovered = m_pplr->GetDiscoveredSides(); + + for (TCoord tyT = tyTop; tyT < tyBottom; tyT++) { + for (TCoord txT = txLeft; txT < txRight; txT++) { + // Any enemies here that want to know about mobile units? + + Side side = m_pplr->GetSide(); + bool fPreped = false; + for (Gid gid = ggobm.GetFirstGid(txT, tyT); gid != kgidNull; gid = ggobm.GetNextGid(gid)) { + UnitGob *punt = (UnitGob *)ggobm.GetGob(gid); + if (punt == NULL) + continue; + dword ffUnit = punt->GetFlags(); + if (!(ffUnit & kfGobUnit)) + continue; + + // Has side A already discovered side B? + + SideMask sidm = GetSideMask(punt->GetSide()); + if (!(sidmAlreadyDiscovered & sidm)) { + + // No + + sidmAlreadyDiscovered |= sidm; + m_pplr->SetDiscoveredSides(sidmAlreadyDiscovered); + TPoint tpt; + punt->GetTilePosition(&tpt); + m_pplr->SetDiscoverPoint(&tpt); + + // Then side B has just discovered side A too. + + Player *pplrB = punt->m_pplr; + pplrB->SetDiscoveredSides(pplrB->GetDiscoveredSides() | GetSideMask(side)); + pplrB->SetDiscoverPoint(&tpt); + } + + // Any enemies in this tile that want to know about mobile units? + + if (punt->IsAlly(side)) + continue; + + // Notify this enemy + + Gid gidFound = punt->GetId(); + Message msg; + if (!fPreped) { + fPreped = true; + memset(&msg, 0, sizeof(msg)); + msg.mid = kmidEnemyNearby; + msg.smidSender = m_gid; + } + + // Notify this enemy of the current gob if it wants to know + + if (gapuntc[punt->GetUnitType()]->wf & kfUntcNotifyEnemyNearby) { + msg.EnemyNearby.gidEnemy = m_gid; + msg.smidReceiver = gidFound; + gsmm.SendMsg(&msg); + } + + // Notify the current gob of this enemy, only if this enemy is a mobile unit + // (so that the current gob doesn't get notified of towers). + + if (ffUnit & kfGobMobileUnit) { + if (m_puntc->wf & kfUntcNotifyEnemyNearby) { + msg.EnemyNearby.gidEnemy = gidFound; + msg.smidReceiver = m_gid; + gsmm.SendMsg(&msg); + } + } + } + } + } +} + +void UnitGob::RecalcEnemyNearby(bool fClearOnly) +{ + // NOTE: Could set a lazy rescan bit instead of forcing a rescan. + // Only if perf is an issue. + + if (fClearOnly) { + // Wipe the enemies list clean and recalc + + m_ff &= ~kfGobNoEnemiesNearby; + for (int n = 0; n < ARRAYSIZE(m_agidEnemyNearby); n++) + m_agidEnemyNearby[n] = kgidNull; + return; + } + + // Calc enemy nearby + + NotifyEnemyNearby(); +} + +// TUNE: + +const TCoord ktcNotifyAlliesOfHitRange = 4; + +void UnitGob::NotifyNearbyAlliesOfHit(Gid gidAssailant) +{ + // Only report getting nailed once every other update + + long cupd = gsim.GetUpdateCount(); + if (cupd - m_cupdLastHitNotify < 2) + return; + m_cupdLastHitNotify = cupd; + + TCoord ctx, cty; + ggobm.GetMapSize(&ctx, &cty); + TCoord tx = TcFromWc(m_wx); + TCoord ty = TcFromWc(m_wy); + + TCoord txLeft = tx - ktcNotifyAlliesOfHitRange; + if (txLeft < 0) + txLeft = 0; + TCoord txRight = tx + ktcNotifyAlliesOfHitRange + 1; + if (txRight > ctx) + txRight = ctx; + TCoord tyTop = ty - ktcNotifyAlliesOfHitRange; + if (tyTop < 0) + tyTop = 0; + TCoord tyBottom = ty + ktcNotifyAlliesOfHitRange + 1; + if (tyBottom > cty) + tyBottom = cty; + + Message msg; + memset(&msg, 0, sizeof(msg)); + msg.mid = kmidNearbyAllyHit; + msg.smidSender = m_gid; + msg.Hit.gidAssailant = gidAssailant; + + for (TCoord tyT = tyTop; tyT < tyBottom; tyT++) { + for (TCoord txT = txLeft; txT < txRight; txT++) { + // Any MobileGob allies around? + + Side side = m_pplr->GetSide(); + bool fPreped = false; + for (Gid gid = ggobm.GetFirstGid(txT, tyT); gid != kgidNull; gid = ggobm.GetNextGid(gid)) { + + // Don't notify self! + + if (gid == m_gid) + continue; + + UnitGob *punt = (UnitGob *)ggobm.GetGob(gid); + if (punt == NULL) + continue; + dword ffUnit = punt->GetFlags(); + if (!(ffUnit & kfGobMobileUnit)) + continue; + if (!punt->IsAlly(side)) + continue; + + // OPT: don't notify MobileUnits that are already attacking a target? + + // Notify this ally + + msg.smidReceiver = punt->GetId(); + gsmm.SendMsg(&msg); + } + } + } +} + +void UnitGob::ShowDamageIndicator() +{ + // Only show enemy units' health + + if (m_pplr == gpplrLocal) + return; + + // Only if turned on + + if (!(gwfPerfOptions & kfPerfEnemyDamageIndicator)) + return; + + // Enable health indicator drawing + + m_wfUnit |= kfUnitDrawHealthIndicator; + m_ff |= kfGobLayerSelection; + MarkRedraw(); + + m_cDamageCountdown = UpdFromMsec(kcmsDamageIndicatorTimeout); + m_unvl.MinSkip(m_cDamageCountdown); +} + +void UnitGob::ClearDamageIndicator() +{ + if ((m_ff & kfGobSelected) == 0) + m_ff &= ~kfGobLayerSelection; + m_wfUnit &= ~kfUnitDrawHealthIndicator; + MarkRedraw(); +} + +//=========================================================================== +// UnitMenu implementation + +UnitGob *UnitMenu::GetOwner() +{ + return m_punt; +} + +bool UnitMenu::DoModal(int *pnResult, Sfx sfxShow, Sfx sfxHide) +{ + gtimm.AddTimer(this, 25); + bool f = Form::DoModal(pnResult, sfxShow, sfxHide); + gtimm.RemoveTimer(this); + return f; +} + +void UnitMenu::OnTimer(long tCurrent) +{ + // Initialize on a timer so that options show / hide while the form + // is visible + + m_punt->InitMenu(this); +} + +const int kcxButtonSpace = 0; +const int kcyTitleMargin = 3; +const int kcxTitleMargin = 5; + +void UnitMenu::SetOwner(UnitGob *punt, bool fPerUnitInit) +{ + m_punt = punt; + + // Let the owning Gob initialize the menu state (hide/show/disable buttons) + + if (fPerUnitInit) { + m_punt->InitMenu(this); + } + + char *pszTitle = m_punt->GetConsts()->szName; + + // Calc the width/height of the 'button bar' (all visible buttons) + + int cButtons = 0; + int cxButton = 0, cyButton = 0; + int i = 0; + for (i = 0; i < m_cctl; i++) { + Control *pctl = m_apctl[i]; + word id = pctl->GetId(); + if (id < kidcRelocButtonMin || id >= kidcRelocButtonMax) + continue; + + // While we're at it force each button to draw using the 'side1' colors + // This is because some of them use side1's dark blues. + + word wf = pctl->GetFlags(); + pctl->SetFlags(wf | kfCtlUseSide1Colors); + + if (!(wf & kfCtlVisible)) + continue; + + if (cButtons == 0) { + Rect rcT; + pctl->GetRect(&rcT); + cxButton = rcT.Width(); + cyButton = rcT.Height(); + } + cButtons++; + } + int cxButtons = ((cxButton + kcxButtonSpace) * cButtons) - kcxButtonSpace; + + // Calc the width/height of the title bar + + int cxTitleText = gapfnt[kifntTitle]->GetTextExtent(pszTitle); + int cxTitleWhole = cxTitleText + kcxTitleMargin * 2; + int cyFont = gapfnt[kifntTitle]->GetHeight(); + int cyTitle = cyFont + kcyTitleMargin * 2; + + // Get Gob's UIBounds in screen coordinates which becomes the interior + // rect around which the title and button bar are formatted. + // This somewhat elaborate means of producing the screen-coord rcUIBounds + // is designed to perfectly match the coordinate conversion pipeline + // used to draw the selection rect. This way the tile/button bar are + // locked right with the selection rect it regardless of resolution. + + WCoord wxViewOrigin, wyViewOrigin; + gsim.GetViewPos(&wxViewOrigin, &wyViewOrigin); + int xViewOrigin = PcFromUwc(wxViewOrigin) & 0xfffe; + int yViewOrigin = PcFromUwc(wyViewOrigin) & 0xfffe; + + Rect rcUIBounds; + rcUIBounds.FromWorldRect(&m_punt->GetConsts()->wrcUIBounds); + WPoint wpt; + m_punt->GetPosition(&wpt); + int xPos = PcFromWc(wpt.wx) - xViewOrigin; + int yPos = PcFromWc(wpt.wy) - yViewOrigin; + rcUIBounds.Offset(xPos, yPos); + + // Set form size/position + // Put title on top of hilight sprite, if there is one. + + Rect rcForm; + AnimSprite *panispr = punt->GetAnimSprite(); + if (panispr != NULL) { + Rect rcSpriteBounds; + panispr->GetBounds(&rcSpriteBounds); + rcSpriteBounds.Offset(xPos, yPos); + rcForm.top = rcSpriteBounds.top - cyTitle - kcyHealthBar; + } else { + rcForm.top = rcUIBounds.top - cyTitle - kcyHealthBar; + } + rcForm.bottom = rcUIBounds.bottom + cyButton; + int cxForm = _max(rcUIBounds.Width(), cxTitleWhole); + if (cxForm < cxButtons) + cxForm = cxButtons; + rcForm.left = rcUIBounds.left + (rcUIBounds.Width() / 2) - (cxForm / 2); + rcForm.right = rcForm.left + cxForm; + int cyForm = rcForm.Height(); + + // Keep the form on screen + + Size sizPlayfield; + ggame.GetPlayfieldSize(&sizPlayfield); + int dx = 0, dy = 0; + if (rcForm.left < 0) + dx = -rcForm.left; + else if (rcForm.right > sizPlayfield.cx) + dx = sizPlayfield.cx - rcForm.right; + if (rcForm.top < 0) + dy = -rcForm.top; + else if (rcForm.bottom > sizPlayfield.cy) + dy = sizPlayfield.cy - rcForm.bottom; + rcForm.Offset(dx, dy); + SetRect(&rcForm); + + // Set title size/position. dx/dy (amount of form that would have + // been off-screen) are cleverly used to keep the buttons as close + // to the partially off-screen unit as possible. + + LabelControl *plblTitle = (LabelControl *)GetControlPtr(kidcTitle); + Rect rcTitle; + rcTitle.left = (cxForm - cxTitleText) / 2 - dx; + if (rcTitle.left < 0) + rcTitle.left = 0; + else if (rcTitle.left + cxTitleText > cxForm) + rcTitle.left += cxForm - (rcTitle.left + cxTitleText); + rcTitle.top = kcyTitleMargin - dy; + if (rcTitle.top < kcyTitleMargin) + rcTitle.top = kcyTitleMargin; + else if (rcTitle.top - kcyTitleMargin + cyTitle > cyForm - cyButton) + rcTitle.top += (cyForm - cyButton) - (rcTitle.top - kcyTitleMargin + cyTitle); + rcTitle.right = rcTitle.left + cxTitleText; + rcTitle.bottom = rcTitle.top + cyFont; + plblTitle->SetRect(&rcTitle); + + // Set the titlebar text + + plblTitle->SetText(pszTitle); + + // Set the button positions. dx/dy are used as above for the title. + + Rect rcButton; + rcButton.left = (cxForm - cxButtons) / 2 - dx; + if (rcButton.left < 0) + rcButton.left = 0; + else if (rcButton.left + cxButtons > cxForm) + rcButton.left += cxForm - (rcButton.left + cxButtons); + rcButton.top = cyTitle + kcyHealthBar + rcUIBounds.Height() - dy; + if (rcButton.top < cyTitle) + rcButton.top = cyTitle; + else if (rcButton.top + cyButton > cyForm) + rcButton.top = cyForm - cyButton; + rcButton.bottom = rcButton.top + cyButton; + + // Stash away for painting later + + m_rcButtons = rcButton; + m_rcButtons.right = m_rcButtons.left + cxButtons; + m_rcButtons.Offset(m_rc.left, m_rc.top); + + // Position the buttons + + for (i = 0; i < m_cctl; i++) { + Control *pctl = m_apctl[i]; + word id = pctl->GetId(); + if (id < kidcRelocButtonMin || id >= kidcRelocButtonMax) + continue; + + if (!(pctl->GetFlags() & kfCtlVisible)) + continue; + + rcButton.right = rcButton.left + cxButton; + pctl->SetRect(&rcButton); + rcButton.left += cxButton + kcxButtonSpace; + } + + // HACK: if there is only one button it's a Transform or Deliver button + // and we don't want to draw a border around it. + + if (cButtons == 1) + m_rcButtons.SetEmpty(); + + // menus are AutoTakedown + + m_wf |= kfFrmAutoTakedown | kfFrmTranslucent; +} + +void UnitMenu::OnPaintBackground(DibBitmap *pbm, UpdateMap *pupd) +{ + // Draw title background + + LabelControl *plblTitle = (LabelControl *)GetControlPtr(kidcTitle); + Rect rc; + plblTitle->GetRect(&rc); + rc.Offset(m_rc.left, m_rc.top); + rc.Inflate(kcxTitleMargin - 1, kcyTitleMargin - 1); // leave 1 pixel space for black border + ShadowHelper(pbm, pupd, &rc); + + // Draw border around title + + rc.Inflate(1, 1); + DrawBorder(pbm, &rc, 1, GetColor(kiclrBlack), pupd); + + // Draw border around buttons + + if (!m_rcButtons.IsEmpty()) + DrawBorder(pbm, &m_rcButtons, 1, GetColor(kiclrBlack), pupd); + + // Draw border around whole thing for testing +// DrawBorder(pbm, &m_rc, 1, GetColor(kiclrRed), pupd); +} + +void UnitMenu::OnPaint(DibBitmap *pbm) +{ + Form::OnPaint(pbm); +} + +// Treat everything except a tap on one of the button controls as a 'miss' +// and auto-takedown. + +bool UnitMenu::OnHitTest(Event *pevt) +{ + if (!(m_wf & kfFrmVisible)) + return false; + + for (int n = m_cctl - 1; n >= 0; n--) { + // Is it on this control? + + Control *pctl = m_apctl[n]; + if (pctl->OnHitTest(pevt) < 0) + continue; + + // Yes, keep it + + return true; + } + + // we're AutoTakedown and hit not on one of the controls + +// Assert(pevt->eType != penHoldEvent && pevt->eType != penUpEvent); + if (pevt->eType == penDownEvent) { + + // Take down and let the event pass through + + EndForm(kidcCancel); + return false; + } + + // As long as this form stays up it should swallow any other + // events to avoid surprising the forms/objects underneath. + + return true; +} + +#ifdef MP_DEBUG_SHAREDMEM +void UnitGob::MPValidate() +{ +// Warehouse sets animation state during draw +// m_ani.MPValidate((Animation *)(((byte *)MPGetGobPtr(m_gid)) + OFFSETOF(UnitGob, m_ani))); + MPValidateGobMember(UnitGob, m_agidEnemyNearby); + MPValidateGobMember(UnitGob, m_fxHealth); + MPValidateGobMember(UnitGob, m_cupdLastHitNotify); +// gpplrLocal specific +// MPValidateGobMember(UnitGob, m_cDamageCountdown); +// MPValidateGobMember(UnitGob, m_wfUnit); + MPValidateGobMember(UnitGob, m_unvl); +} +#endif + +} // namespace wi diff --git a/game/UnitGroupMgr.cpp b/game/UnitGroupMgr.cpp new file mode 100644 index 0000000..c5d16a7 --- /dev/null +++ b/game/UnitGroupMgr.cpp @@ -0,0 +1,1445 @@ +#include "ht.h" + +namespace wi { + +// +// UnitGroupMgr +// + +UnitGroupMgr::UnitGroupMgr() +{ + m_cug = 0; + m_aug = NULL; +} + +UnitGroupMgr::~UnitGroupMgr() +{ + delete[] m_aug; + m_aug = NULL; + m_cug = 0; +} + +bool UnitGroupMgr::Init(IniReader *pini) +{ + // Get trigger count + + int cArgs = pini->GetPropertyValue("General", "UnitGroupCount", "%d", &m_cug); + if (cArgs != 1) + return false; + + // Allocate UnitGroup storage, one chunk + + Assert(m_aug == NULL); + m_aug = new UnitGroup[m_cug]; + Assert(m_aug != NULL, "out of memory!"); + if (m_aug == NULL) + return false; + + for (int iug = 0; iug < m_cug; iug++) { + if (!m_aug[iug].Init(pini, iug)) { + delete m_aug; + m_aug = NULL; + return false; + } + } + + return true; +} + +#define knVerUnitGroupMgrState 1 +bool UnitGroupMgr::LoadState(Stream *pstm) +{ + byte nVer = pstm->ReadByte(); + if (nVer != knVerUnitGroupMgrState) + return false; + + for (int i = 0; i < m_cug; i++) + m_aug[i].LoadState(pstm); + + return pstm->IsSuccess(); +} + +bool UnitGroupMgr::SaveState(Stream *pstm) +{ + pstm->WriteByte(knVerUnitGroupMgrState); + + for (int i = 0; i < m_cug; i++) + m_aug[i].SaveState(pstm); + + return pstm->IsSuccess(); +} + +void UnitGroupMgr::ActivateUnitGroup(int iug) +{ + // If the group is already active leave it alone + + if ((m_aug[iug].GetFlags() & kfUgActive) != 0) + return; + + m_aug[iug].Activate(); +} + +// Rob expects random groups to be activated in a fashion that is +// not truly random. The idea is that groups should activate randomly +// but not repeat until all have been activated (aka shuffle) + +void UnitGroupMgr::ActivateRandomUnitGroup() +{ + // Figure out how many inactive random groups there are + + int cug = 0, iug, cugNotRecentlyActive = 0; + UnitGroup *pug = m_aug; + for (iug = 0; iug < m_cug; iug++, pug++) { + word wf = pug->GetFlags(); + if ((wf & (kfUgActive | kfUgRandomGroup)) == kfUgRandomGroup) { + if (wf & kfUgNotRecentlyActivated) + cugNotRecentlyActive++; + cug++; + } + } + + if (cug == 0) + return; // nothing to activate + + // Everyone has been activated recently, start afresh + + if (cugNotRecentlyActive == 0) { + pug = m_aug; + for (iug = 0; iug < m_cug; iug++, pug++) { + word wf = pug->GetFlags(); + if ((wf & (kfUgActive | kfUgRandomGroup)) == kfUgRandomGroup) + pug->SetFlags(wf | kfUgNotRecentlyActivated); + } + } else { + cug = cugNotRecentlyActive; + } + + // Pick one of the random groups + + cug = (GetRandom() % cug); + + // Search back through again to find the picked group + + pug = m_aug; + for (iug = 0; true; pug++) { + if ((pug->GetFlags() & (kfUgActive | kfUgRandomGroup | kfUgNotRecentlyActivated)) == (kfUgRandomGroup | kfUgNotRecentlyActivated)) { + if (iug == cug) { + pug->Activate(); + return; + } + iug++; + } + } + + // Activate it + + pug->Activate(); +} + +// OPT: this doesn't need to be called every Simulation::Update + +void UnitGroupMgr::Update() +{ + UnitGroup *pug = m_aug; + for (int iug = 0; iug < m_cug; iug++, pug++) + if (pug->GetFlags() & kfUgActive) + pug->Update(); + +#ifdef DEBUG_HELPERS + extern void UpdateUnitGroupViewer(); + UpdateUnitGroupViewer(); +#endif +} + +bool UnitGroupMgr::CreateAtLevelLoadGroups() +{ + UnitGroup *pug = m_aug; + for (int iug = 0; iug < m_cug; iug++, pug++) + if (pug->GetFlags() & kfUgCreateAtLevelLoad) + pug->Activate(); + return true; +} + +UnitGroup *UnitGroupMgr::GetUnitGroup(Gid gid) +{ + UnitGroup *pug = m_aug; + for (int iug = 0; iug < m_cug; iug++, pug++) { + if (!(pug->GetFlags() & kfUgActive)) + continue; + + int cule = pug->GetUnitCount(); + UnitListEntry *pule = pug->GetUnitList(); + for (int iule = 0; iule < cule; iule++, pule++) { + if (pule->gid == gid && (pule->bf & kfUleBuilt)) + return pug; + } + } + + return NULL; +} + +// +// UnitGroup +// + +UnitGroup::UnitGroup() +{ +#ifdef DEBUG + m_szName[0] = 0; +#endif + m_pactn = NULL; + m_pactnLast = NULL; + m_wf = kfUgNotRecentlyActivated; + m_cule = 0; + m_aule = NULL; + m_pplr = NULL; + m_wfMunt = 0; + m_nSpawnArea = -1; +} + +UnitGroup::~UnitGroup() +{ + delete m_aule; + + // Delete Actions + + UnitGroupAction *pactn = m_pactn; + while (pactn != NULL) { + UnitGroupAction *pactnNext = pactn->m_pactnNext; + delete pactn; + pactn = pactnNext; + } +} + +#define knVerUnitGroupState 5 +bool UnitGroup::LoadState(Stream *pstm) +{ + byte nVer = pstm->ReadByte(); + if (nVer != knVerUnitGroupState) + return false; + + // Read the last (in-progress) action (if any) + + char nAction = pstm->ReadByte(); + if (nAction != -1) { + UnitGroupAction *pactn = m_pactn; + for (int j = 0; j < nAction; j++, pactn = pactn->m_pactnNext); + m_pactnLast = pactn; + } + + // Read the UnitGroup flags + + m_wf = pstm->ReadWord(); + + // Read the UnitListEntries + + int cule = pstm->ReadWord(); + if (cule != m_cule) { + UnitListEntry *aule = new UnitListEntry[cule]; + Assert(aule != NULL, "out of memory!"); + if (aule != NULL) { + delete m_aule; + m_aule = aule; + m_cule = cule; + } + } + + for (int i = 0; i < m_cule; i++) { + m_aule[i].ut = pstm->ReadWord(); + m_aule[i].gid = pstm->ReadWord(); + m_aule[i].bf = pstm->ReadByte(); + } + + // Give each Action a chance to Read its state + + for (UnitGroupAction *pactn = m_pactn; pactn != NULL; pactn = pactn->m_pactnNext) + pactn->LoadState(pstm); + + m_wfMunt = pstm->ReadWord(); + + return pstm->IsSuccess(); +} + +bool UnitGroup::SaveState(Stream *pstm) +{ + pstm->WriteByte(knVerUnitGroupState); + + // Map m_pactnLast into an index or -1 for no last action + + if (m_pactnLast == NULL) { + pstm->WriteByte((byte)-1); + } else { + char nAction = 0; + for (UnitGroupAction *pactn = m_pactn; pactn != NULL; pactn = pactn->m_pactnNext, nAction++) + if (pactn == m_pactnLast) + break; + pstm->WriteByte(nAction); + } + + // Write the UnitGroup flags + + pstm->WriteWord(m_wf); + + // Write the UnitListEntries because some may have been added dynamically + + pstm->WriteWord(m_cule); + for (int i = 0; i < m_cule; i++) { + pstm->WriteWord(m_aule[i].ut); + pstm->WriteWord(m_aule[i].gid); + pstm->WriteByte(m_aule[i].bf); + } + + // Give each Action a chance to write its state + + for (UnitGroupAction *pactn = m_pactn; pactn != NULL; pactn = pactn->m_pactnNext) + pactn->SaveState(pstm); + + pstm->WriteWord(m_wfMunt); + + return pstm->IsSuccess(); +} + +bool UnitGroup::Init(IniReader *pini, int iug) +{ + char szUnitGroup[50]; + sprintf(szUnitGroup, "UnitGroup %d", iug); + + char szProp[32]; + FindProp find; + while (pini->FindNextProperty(&find, szUnitGroup, szProp, sizeof(szProp))) { + switch (szProp[0]) { + case 'S': // Side or Spawn or SpawnArea + { + int n; + pini->GetPropertyValue(&find, "%d", &n); + + switch (szProp[1]) { + case 'i': // Side + // Read the UnitGroup's side and map it to its owning Player + + Assert(strcmp(szProp, "Side") == 0); + m_pplr = gplrm.GetPlayer(n); + break; + + case 'p': // Spawn or SpawnArea + if (strcmp(szProp, "SpawnArea") == 0) + m_nSpawnArea = n; // SpawnArea + else + m_wf |= n != 0 ? kfUgSpawn : 0; // Spawn + break; + } + } + break; + + case 'L': // LoopForever + { + Assert(strcmp(szProp, "LoopForever") == 0); + int n; + pini->GetPropertyValue(&find, "%d", &n); + if (n != 0) + m_wf |= kfUgLoopForever; + } + break; + + case 'R': // RandomGroup or ReplaceUnits + { + int n; + pini->GetPropertyValue(&find, "%d", &n); + + switch (szProp[1]) { + case 'a': // RandomGroup + Assert(strcmp(szProp, "RandomGroup") == 0); + if (n != 0) + m_wf |= kfUgRandomGroup; + break; + + case 'e': // ReplaceUnits (aka "Recreate if destroyed") + Assert(strcmp(szProp, "ReplaceGroup") == 0); + if (n != 0) + m_wf |= kfUgReplaceGroup; + break; + } + } + break; + + case 'C': // CreateAtLevelLoad + { + Assert(strcmp(szProp, "CreateAtLevelLoad") == 0); + int n; + pini->GetPropertyValue(&find, "%d", &n); + if (n != 0) + m_wf |= kfUgCreateAtLevelLoad; + } + break; + + case 'U': + { + // Expand Units string to an array of UnitListEntries + + Assert(strcmp(szProp, "Units") == 0); + char szT[256]; + pini->GetPropertyValue(&find, "%s", szT); + + char *psz = szT; + if (!ParseNumber(&psz, &m_cule)) + return false; + + m_aule = new UnitListEntry[m_cule]; + Assert(m_aule != NULL, "out of memory!"); + if (m_aule == NULL) + break; + + UnitListEntry *pule = m_aule; + int nUnitType; + while (ParseNumber(&psz, &nUnitType)) { + int cUnits; + if (!ParseNumber(&psz, &cUnits)) + return false; + + for (int i = 0; i < cUnits; i++, pule++) { + pule->ut = (UnitType)nUnitType; + pule->gid = kgidNull; + pule->bf = 0; + } + } + Assert(pule - m_aule == m_cule); + } + break; + + case 'A': + if (szProp[1] == 'g') { + int nAggressiveness; + pini->GetPropertyValue(&find, "%d", &nAggressiveness); + m_wfMunt = AggBitsFromAgg(nAggressiveness); + } else { + if (!LoadAction(pini, &find)) + return false; + } + break; + + case 'H': + Assert(strcmp(szProp, "Health") == 0); + pini->GetPropertyValue(&find, "%d", &m_nHealth); + break; + +#ifdef DEBUG + case 'N': // Name + Assert(strcmp(szProp, "Name") == 0); + pini->GetPropertyValue(&find, "%s", m_szName); + break; +#endif + + default: + Assert(false); + break; + } + } + + // If this group is to be spawned it better have a spawn area + + Assert((m_wf & kfUgSpawn) == 0 || m_nSpawnArea != -1); + + return true; +} + +void UnitGroup::OnBuilt(UnitGob *punt) +{ + // Scan the list of desired Units and slot in this new one. + // As we scan keep track of whether this group has all the + // units it needs yet. + + bool fNeedsUnit = false; + UnitType ut = punt->GetUnitType(); + UnitListEntry *pule = m_aule; + for (int iule = 0; iule < m_cule; iule++, pule++) { + if (pule->bf & kfUleBuilt) + continue; + if (pule->ut != ut) { + fNeedsUnit = true; + continue; + } + + // This UnitGroup wants this unit type. Retain it and set any special + // flags it requires. + + pule->gid = punt->GetId(); + pule->bf |= kfUleBuilt; + if (punt->GetFlags() & kfGobMobileUnit) { + MobileUnitGob *pmunt = (MobileUnitGob *)punt; + word wf = (pmunt->GetMobileUnitFlags() & ~kfMuntAggressivenessBits) | m_wfMunt; + + // Human-controlled units don't attack while following move orders + + if (!(m_pplr->GetFlags() & kfPlrComputer)) + wf &= ~kfMuntAttackEnemiesWhenMoving; + pmunt->SetMobileUnitFlags(wf); + } + punt->SetHealth((fix)(((long)m_nHealth * punt->GetConsts()->GetArmorStrength()) / 100)); + + // Keep scanning so fNeedsUnit will be determined but set ut = kutNone + // so no more matches will be found. + + ut = kutNone; + } + + if (!fNeedsUnit) + m_wf &= ~kfUgNeedsUnit; +} + +void UnitGroup::AddUnit(UnitGob *punt, bool fReplicant) +{ + UnitListEntry *auleNew = new UnitListEntry[m_cule + 1]; + Assert(auleNew != NULL, "out of memory!"); + if (auleNew == NULL) + return; + + memcpy(auleNew, m_aule, sizeof(UnitListEntry) * m_cule); + delete m_aule; + m_aule = auleNew; + m_aule[m_cule].gid = punt->GetId(); + m_aule[m_cule].ut = punt->GetConsts()->ut; + m_aule[m_cule].bf = fReplicant ? kfUleBuilt | kfUleReplicant : kfUleBuilt; + m_cule++; +} + +void UnitGroup::RemoveReplicants() +{ + UnitListEntry *pule = m_aule; + for (int iule = 0; iule < m_cule; iule++, pule++) { + + // We require that constant unit list entries and Replicant unit list + // entries are not intermingled + + if (pule->bf & kfUleReplicant) { +#ifdef DEBUG + for (int i = iule; i < m_cule; i++, pule++) + Assert(pule->bf & kfUleReplicant); +#endif + m_cule = iule; + return; + } + } +} + +void UnitGroup::Activate() +{ + // Can't let an active group be reactivated + Assert((m_wf & kfUgActive) == 0); + + m_wf = (m_wf | kfUgNeedsUnit | kfUgActive | kfUgActivatedBefore) & ~(kfUgWaitingToReplace | kfUgNotRecentlyActivated); + m_pactnLast = NULL; + + // Make sure human control players don't auto-attack while following move orders + + word wfMuntFilter; + if (!(m_pplr->GetFlags() & kfPlrComputer)) + wfMuntFilter = ~kfMuntAttackEnemiesWhenMoving; + else + wfMuntFilter = 0xffff; + + if (m_wf & kfUgSpawn) { + + // Spawn MobileUnits in the center of the Area, Stuctures at the area's upper-left + + TRect trc; + ggobm.GetAreaRect(m_nSpawnArea, &trc); + Point pt; + trc.GetCenter(&pt); + + UnitListEntry *pule = m_aule; + for (int iule = 0; iule < m_cule; iule++, pule++) { + UnitGob *punt = NULL; + UnitConsts *puntc = gapuntc[pule->ut]; + fix fxHealth = (fix)(((long)m_nHealth * puntc->GetArmorStrength()) / 100); + if ((1UL << pule->ut) & kumStructures) { + // Check to see if limits have been reached + + if (!ggobm.IsBelowLimit(knLimitStruct, m_pplr)) + continue; + + // Is the space required by the structure free? + + StructConsts *pstruc = (StructConsts *)puntc; + if (gsim.GetLevel()->GetTerrainMap()->IsOccupied(trc.left, trc.top, pstruc->ctxReserve, pstruc->ctyReserve, kbfStructure | kbfMobileUnit)) + continue; // no + punt = (UnitGob *)CreateGob(pstruc->gt); + Assert(punt->GetFlags() & kfGobStructure); + if (punt == NULL) + continue; + punt->Init(WcFromTc(trc.left), WcFromTc(trc.top), m_pplr, fxHealth, 0, NULL); + } else { + // Check to see if limits have been reached + + if (!ggobm.IsBelowLimit(knLimitMobileUnit, m_pplr)) + continue; + punt = (UnitGob *)CreateGob(puntc->gt); + if (punt == NULL) + continue; + WPoint wpt; + FindNearestFreeTile(pt.x, pt.y, &wpt); + punt->Init(wpt.wx, wpt.wy, m_pplr, fxHealth, 0, NULL); + Assert(punt->GetFlags() & kfGobMobileUnit); + MobileUnitGob *pmunt = (MobileUnitGob *)punt; + pmunt->SetMobileUnitFlags((pmunt->GetMobileUnitFlags() & ~kfMuntAggressivenessBits) | (m_wfMunt & wfMuntFilter)); + } + + pule->gid = punt->GetId(); + pule->bf |= kfUleBuilt; + } + m_wf &= ~kfUgNeedsUnit; + + } else { + BuildMgr *pbldm = gsim.GetBuildMgr(); + UnitListEntry *pule = m_aule; + for (int iule = 0; iule < m_cule; iule++, pule++) { + pule->gid = kgidNull; + pule->bf &= ~kfUleBuilt; + pbldm->BuildUnit(pule->ut, this, m_nSpawnArea); + } + } + + Update(); +} + +void UnitGroup::Update() +{ + // Clear out dead units and if they're all dead deactivate or recreate the group. + // We only count active units except with miners, we'll count inactive ones + // because they're probably in the processor. + + bool fAllGone = true; + UnitListEntry *pule = m_aule; + for (int iule = 0; iule < m_cule; iule++, pule++) { + if (pule->gid != kgidNull) { + if (ggobm.GetGob(pule->gid, pule->ut != kutGalaxMiner) != NULL) + fAllGone = false; + else + pule->gid = kgidNull; + } + } + + // Still needing a unit? + + if (m_wf & kfUgNeedsUnit) + return; + + // If all Units are gone deactivate the group + + if (fAllGone) { + m_wf &= ~kfUgActive; + + // Reset all actions to prepare for reexecution if/when this + // UnitGroup is recreated. + + UnitGroupAction *pactn = m_pactn; + while (pactn != NULL) { + pactn->Reset(); + pactn = pactn->m_pactnNext; + } + + // Clean out Replicants + + RemoveReplicants(); + + // Reactivate groups that want it + + if (m_wf & kfUgReplaceGroup) + Activate(); + return; + } + + if (m_wf & kfUgWaitingToReplace) { + // kfUgReplaceGroups are kept around after they finish executing + // all their actions so they can be recreated when all their members + // are destroyed. + + return; + } + + // Is this UnitGroup still executing an action? + + UnitGroupAction *pactnNext = m_pactnLast; + + do { + // Start executing actions. If pactnNext != NULL, start with that action + + if (pactnNext == NULL) + pactnNext = m_pactn; + + while (pactnNext != NULL) { + // If this action doesn't complete, remember that and return + + if (!pactnNext->Perform(this)) { + m_pactnLast = pactnNext; + return; + } + + // Action complete, continue to next action + + m_pactnLast = NULL; // clear out 'in-progress' action + pactnNext = pactnNext->m_pactnNext; + } + } while (m_wf & kfUgLoopForever); + + // All actions done, deactivate the group unless we have to keep it around + // for "Recreate if destroyed", i.e., it is a kfUgReplaceGroup + + if (m_wf & kfUgReplaceGroup) + m_wf |= kfUgWaitingToReplace; + else { + RemoveReplicants(); + m_wf &= ~kfUgActive; + } +} + +bool UnitGroup::LoadAction(IniReader *pini, FindProp *pfind) +{ + char sz[128]; + sz[0] = 0; + int nAction; + int cArgs = pini->GetPropertyValue(pfind, "%d,%s", &nAction, sz); + if (cArgs == 0) + return false; + Assert(strlen(sz) + 1 < sizeof(sz)); + + UnitGroupAction *pactn; + switch (nAction) { + case knWaitUnitGroupAction: + pactn = new WaitUnitGroupAction(); + break; + + case knSetSwitchUnitGroupAction: + pactn = new SetSwitchUnitGroupAction(); + break; + + case knMoveUnitGroupAction: + pactn = new MoveUnitGroupAction(); + break; + + case knAttackUnitGroupAction: + pactn = new AttackUnitGroupAction(); + break; + + case knGuardUnitGroupAction: + pactn = new GuardUnitGroupAction(); + break; + + case knGuardVicinityUnitGroupAction: + pactn = new GuardVicinityUnitGroupAction(); + break; + + case knMineUnitGroupAction: + pactn = new MineUnitGroupAction(); + break; + + default: + Assert(false); + break; + } + + // Init it, error if that failed + + Assert(pactn != NULL, "out of memory!"); + if (!pactn->Init(sz)) { + delete pactn; + return false; + } + + // Link it in last + + UnitGroupAction **ppactn = &m_pactn; + while ((*ppactn) != NULL) + ppactn = &((*ppactn)->m_pactnNext); + *ppactn = pactn; + + return true; +} + +UnitGroupAction::UnitGroupAction() +{ + m_pactnNext = NULL; +} + +bool UnitGroupAction::LoadState(Stream *pstm) +{ + return true; +} + +bool UnitGroupAction::SaveState(Stream *pstm) +{ + return true; +} + +// Placeholder to be overridden + +void UnitGroupAction::Reset() +{ +} + +// WaitUnitGroupAction + +WaitUnitGroupAction::WaitUnitGroupAction() +{ + Reset(); +} + +#define knVerWaitUnitGroupActionState 1 +bool WaitUnitGroupAction::LoadState(Stream *pstm) +{ + byte nVer = pstm->ReadByte(); + if (nVer != knVerWaitUnitGroupActionState) + return false; + + m_fWaiting = pstm->ReadByte() != 0; + m_tStart = pstm->ReadDword(); + + return pstm->IsSuccess(); +} + +bool WaitUnitGroupAction::SaveState(Stream *pstm) +{ + pstm->WriteByte(knVerWaitUnitGroupActionState); + pstm->WriteByte(m_fWaiting); + pstm->WriteDword(m_tStart); + + return pstm->IsSuccess(); +} + +bool WaitUnitGroupAction::Init(char *psz) +{ + int cSecs; + if (!ParseNumber(&psz, &cSecs)) + return false; + m_ctWait = cSecs * 100; + return true; +} + +void WaitUnitGroupAction::Reset() +{ + m_fWaiting = false; +} + +bool WaitUnitGroupAction::Perform(UnitGroup *pug) +{ + // If in the middle of waiting, check if the wait is over. + + long t = gsim.GetTickCount(); + if (m_fWaiting) { + // Wait over? If so, return true + + if (t - m_tStart >= m_ctWait) { + m_fWaiting = false; + return true; + } + + // Wait not over, return false + + return false; + } + + // Starting the wait, assume wait not over + + m_fWaiting = true; + m_tStart = t; + return false; +} + +#ifdef DEBUG_HELPERS +char *WaitUnitGroupAction::ToString() +{ + sprintf(s_szDebugHelpers, "Wait %d [%d]", m_ctWait / 100, (gsim.GetTickCount() - m_tStart) / 100); + return s_szDebugHelpers; +} +#endif + +// SetSwitchUnitGroupAction + +bool SetSwitchUnitGroupAction::Init(char *psz) +{ + if (!ParseNumber(&psz, &m_iSwitch)) + return false; + Assert(m_iSwitch < kcSwitchMax); + + int nOnOff; + if (!ParseNumber(&psz, &nOnOff)) + return false; + m_fOn = nOnOff == 1; + return true; +} + +bool SetSwitchUnitGroupAction::Perform(UnitGroup *pug) +{ + gsim.GetLevel()->GetTriggerMgr()->SetSwitch(m_iSwitch, m_fOn); + return true; +} + +// MoveUnitGroupAction +// +// UnitGroup Move action: +// All members of the group head to the same point. +// +// 1. Send each member of the group to the center of the specified Area +// UNDONE: 1a. if a member decides to go on attack, all members should attack the same target +// 2. Wait until all (living) members of the group are idle (no commands pending and in kstGuard) +// 3. Action is complete +// +// How units behave while moving (e.g., movement rate, whether they attack +// nearby enemies) is up to them but influenced by the group's aggressiveness +// level. + +#define knVerMoveUnitGroupActionState 1 +bool MoveUnitGroupAction::LoadState(Stream *pstm) +{ + byte nVer = pstm->ReadByte(); + if (nVer != knVerMoveUnitGroupActionState) + return false; + + m_fWaiting = pstm->ReadByte() != 0; + + return pstm->IsSuccess(); +} + +bool MoveUnitGroupAction::SaveState(Stream *pstm) +{ + pstm->WriteByte(knVerMoveUnitGroupActionState); + pstm->WriteByte(m_fWaiting); + + return pstm->IsSuccess(); +} + +bool MoveUnitGroupAction::Init(char *psz) +{ + Reset(); + return ParseArea(&psz, &m_nArea); +} + +void MoveUnitGroupAction::Reset() +{ + m_fWaiting = false; +} + +bool MoveUnitGroupAction::Perform(UnitGroup *pug) +{ + // First time through + + if (!m_fWaiting) { + TRect trc; + Level *plvl = gsim.GetLevel(); + ggobm.GetAreaRect(m_nArea, &trc, pug->GetOwner()->GetSide()); + + // Figure out min speed + + UnitListEntry *pule = pug->GetUnitList(); + WCoord wcMoveDistPerUpdate = kwcMax; + int iule; + for (iule = 0; iule < pug->GetUnitCount(); iule++, pule++) { + // Ignore dead units + + UnitGob *punt = (UnitGob *)ggobm.GetGob(pule->gid); + if (punt == NULL) + continue; + Assert((punt->GetFlags() & kfGobMobileUnit) != 0); + MobileUnitGob *pmunt = (MobileUnitGob *)punt; + + // Get min speed per update + + MobileUnitConsts *pmuntc = (MobileUnitConsts *)pmunt->GetConsts(); + if (pmuntc->GetMoveDistPerUpdate() < wcMoveDistPerUpdate) + wcMoveDistPerUpdate = pmuntc->GetMoveDistPerUpdate(); + } + + // Send commands + + TCoord tcRadius = RadiusFromUnitCount(pug->GetUnitCount()) + 1; + pule = pug->GetUnitList(); + for (iule = 0; iule < pug->GetUnitCount(); iule++, pule++) { + + // Ignore dead units + + UnitGob *punt = (UnitGob *)ggobm.GetGob(pule->gid); + if (punt == NULL) + continue; + Assert((punt->GetFlags() & kfGobMobileUnit) != 0); + MobileUnitGob *pmunt = (MobileUnitGob *)punt; + + // Tell it where to go. + + Point ptCenter; + trc.GetCenter(&ptCenter); + SendMoveAction(pmunt->GetId(), WcFromTc(ptCenter.x), WcFromTc(ptCenter.y), tcRadius, wcMoveDistPerUpdate); + } + + m_fWaiting = true; + return false; + + } else { + m_fWaiting = false; + UnitListEntry *pule = pug->GetUnitList(); + for (int iule = 0; iule < pug->GetUnitCount(); iule++, pule++) { + + // Ignore dead units + + UnitGob *punt = (UnitGob *)ggobm.GetGob(pule->gid); + if (punt == NULL) + continue; + + // Is it trying? + + if (!punt->IsIdle()) { + m_fWaiting = true; + break; + } + } + } + + return !m_fWaiting; +} + +#ifdef DEBUG_HELPERS +char *MoveUnitGroupAction::ToString() +{ + sprintf(s_szDebugHelpers, "Move to \"%s\"", ggobm.GetAreaName(m_nArea)); + return s_szDebugHelpers; +} +#endif + +// UnitGroup Attack for action: +// Each member of the group attacks the nearest enemy Unit that matches +// the UnitMask. When the enemy is destroyed a new target is selected and +// attacked. The action is complete when have elapsed. +// +// 1. Select a target +// 2. Send each Unit an Attack Action against the target +// 3. When target is destroyed go to step #1 +// UNDONE: 3a. What if there are no targets? Incorporate some sort of a pause to keep from scanning each update. +// 4. Action is complete when have elapsed + +#define knVerAttackUnitGroupActionState 1 +bool AttackUnitGroupAction::LoadState(Stream *pstm) +{ + byte nVer = pstm->ReadByte(); + if (nVer != knVerAttackUnitGroupActionState) + return false; + + m_fWaiting = pstm->ReadByte() != 0; + m_tStart = pstm->ReadDword(); + m_gidTarget = pstm->ReadWord(); + + return pstm->IsSuccess(); +} + +bool AttackUnitGroupAction::SaveState(Stream *pstm) +{ + pstm->WriteByte(knVerAttackUnitGroupActionState); + pstm->WriteByte(m_fWaiting); + pstm->WriteDword(m_tStart); + pstm->WriteWord(m_gidTarget); + + return pstm->IsSuccess(); +} + +bool AttackUnitGroupAction::Init(char *psz) +{ + Reset(); + + if (!ParseUnitMask(&psz, &m_um)) + return false; + int nCaSideMask; + if (!ParseNumber(&psz, &nCaSideMask)) + return false; + m_wfCaSideMask = nCaSideMask; + int cSecs; + if (!ParseNumber(&psz, &cSecs)) + return false; + m_ctWait = cSecs * 100; + return true; +} + +void AttackUnitGroupAction::Reset() +{ + m_fWaiting = false; +} + +bool AttackUnitGroupAction::Perform(UnitGroup *pug) +{ + // If in the middle of waiting, check if the wait is over. + + long t = gsim.GetTickCount(); + if (m_fWaiting) { + // Wait over? If so, return true + + if (t - m_tStart >= m_ctWait) { + m_fWaiting = false; + return true; + } + } else { + + // Starting the action. Reset the wait start. + + m_fWaiting = true; + m_tStart = t; + m_gidTarget = kgidNull; + } + + // Is a target needed? If so, find one and direct everyone to attack it. + + SideMask sidmTarget = GetSideMaskFromCaSideMask(pug->GetOwner()->GetSide(), m_wfCaSideMask); + UnitGob *puntTarget = (UnitGob *)ggobm.GetGob(m_gidTarget); + if (puntTarget != NULL) { + + // Possibly the Unit has been taken over and is not owned by a side + // we are attacking any more. + + if (!(GetSideMask(puntTarget->GetSide()) & sidmTarget)) + puntTarget = NULL; + } + + // We have a target and everyone should already be attacking it so keep waiting. + + if (puntTarget != NULL) + return false; + + // Find a new target + + m_gidTarget = kgidNull; + + // Find the nearest Unit that matches the target SideMask and UnitMask + + // Calc group centroid + + UnitListEntry *pule = pug->GetUnitList(); + TCoord txGroupCenter = 0, tyGroupCenter = 0; + int cpunt = 0; + int iule; + for (iule = 0; iule < pug->GetUnitCount(); iule++, pule++) { + + // Ignore dead units + + UnitGob *punt = (UnitGob *)ggobm.GetGob(pule->gid); + if (punt == NULL) + continue; + + TPoint tpt; + punt->GetTilePosition(&tpt); + txGroupCenter += tpt.tx; + tyGroupCenter += tpt.ty; + cpunt++; + } + txGroupCenter /= cpunt; + tyGroupCenter /= cpunt; + + word nDist = 0x7fff; + puntTarget = (UnitGob *)ggobm.GetFirstGob(); + for (; puntTarget != NULL; puntTarget = (UnitGob *)ggobm.GetNextGob(puntTarget)) { + dword ff = puntTarget->GetFlags(); + if ((ff & (kfGobUnit | kfGobActive)) != (kfGobUnit | kfGobActive)) + continue; + + if (!(puntTarget->GetConsts()->um & m_um)) + continue; + + if (!(GetSideMask(puntTarget->GetSide()) & sidmTarget)) + continue; + + // Compute distance to this potential target. + + TPoint tpt; + puntTarget->GetTilePosition(&tpt); + TCoord dtx = tpt.tx - txGroupCenter; + TCoord dty = tpt.ty - tyGroupCenter; + word nDistT = dtx * dtx + dty * dty; // should not exceed 8192 for 64x64 maps + Assert(nDistT < 8192); + + // Keep track of the nearest target. + + if (nDistT < nDist) { + m_gidTarget = puntTarget->GetId(); + nDist = nDistT; + } + } + + // No valid targets found? + + if (m_gidTarget == kgidNull) { + // UNDONE: wait for awhile + return false; + } + + puntTarget = (UnitGob *)ggobm.GetGob(m_gidTarget); + + // Have everyone attack it + + Message msgT; + msgT.smidSender = ksmidNull; + puntTarget->GetAttackPoint(&msgT.AttackCommand.wptTarget); + msgT.AttackCommand.gidTarget = m_gidTarget; + msgT.AttackCommand.wptTargetCenter = msgT.AttackCommand.wptTarget; + msgT.AttackCommand.tcTargetRadius = 0; + msgT.AttackCommand.wcMoveDistPerUpdate = 0; + + pule = pug->GetUnitList(); + for (iule = 0; iule < pug->GetUnitCount(); iule++, pule++) { + + // Ignore dead units + + MobileUnitGob *pmunt = (MobileUnitGob *)ggobm.GetGob(pule->gid); + if (pmunt == NULL) + continue; + Assert((pmunt->GetFlags() & kfGobMobileUnit) != 0); + + // Tell it to attack the fresh target + + // The MobileUnitGob's state machine slams this member of the message + // so we must reinitialize it. + + msgT.mid = kmidAttackAction; + + msgT.smidReceiver = pmunt->GetId(); + gsmm.SendMsg(&msgT); + } + + // Keep 'waiting' (until all group members are dead or the attack time runs out). + + return false; +} + +#ifdef DEBUG_HELPERS +char *AttackUnitGroupAction::ToString() +{ + sprintf(s_szDebugHelpers, "Attack %s for %d [%d]", PszFromUnitMask(m_um), m_ctWait / 100, (gsim.GetTickCount() - m_tStart) / 100); + return s_szDebugHelpers; +} +#endif + +// UnitGroup Guard action: +// All members of the group stand still and guard as per the group's +// aggressiveness level. +// +// 1. Send a Guard command to each member of the group +// 2. Action is complete + +#define knVerGuardUnitGroupActionState 1 +bool GuardUnitGroupAction::LoadState(Stream *pstm) +{ + byte nVer = pstm->ReadByte(); + if (nVer != knVerGuardUnitGroupActionState) + return false; + + m_fWaiting = pstm->ReadByte() != 0; + m_tStart = pstm->ReadDword(); + + return pstm->IsSuccess(); +} + +bool GuardUnitGroupAction::SaveState(Stream *pstm) +{ + pstm->WriteByte(knVerGuardUnitGroupActionState); + pstm->WriteByte(m_fWaiting); + pstm->WriteDword(m_tStart); + + return pstm->IsSuccess(); +} + +bool GuardUnitGroupAction::Init(char *psz) +{ + Reset(); + + int cSecs; + if (!ParseNumber(&psz, &cSecs)) + return false; + m_ctWait = cSecs * 100; + return true; +} + +void GuardUnitGroupAction::Reset() +{ + m_fWaiting = false; +} + +bool GuardUnitGroupAction::Perform(UnitGroup *pug) +{ + // If in the middle of waiting, check if the wait is over. + + long t = gsim.GetTickCount(); + if (m_fWaiting) { + // Wait over? If so, return true + + if (t - m_tStart >= m_ctWait) { + m_fWaiting = false; + return true; + } + } else { + + // Starting the action. Reset the wait start. + + m_fWaiting = true; + m_tStart = t; + + // Have everyone Guard + + Message msgT; + msgT.smidSender = ksmidNull; + + UnitListEntry *pule = pug->GetUnitList(); + for (int iule = 0; iule < pug->GetUnitCount(); iule++, pule++) { + + // Ignore dead units + + MobileUnitGob *pmunt = (MobileUnitGob *)ggobm.GetGob(pule->gid); + if (pmunt == NULL) + continue; + Assert((pmunt->GetFlags() & kfGobMobileUnit) != 0); + + // Tell it to Guard + + // The MobileUnitGob's state machine slams this member of the message + // so we must reinitialize it. + + msgT.mid = kmidGuardAction; + + msgT.smidReceiver = pmunt->GetId(); + gsmm.SendMsg(&msgT); + } + } + + // Keep 'waiting' (until all group members are dead or the Guard time runs out). + + return false; +} + +// UnitGroup GuardArea action: +// All members of the group stand still and guard the area as per the group's +// aggressiveness level. If an enemy unit enters the area the group will go +// after it. All members of the group go after the same target. +// +// 1. Send a GuardArea command to each member of the group +// 2. Action is complete + +// UNDONE: + + +// MineUnitGroupAction + +bool MineUnitGroupAction::Init(char *psz) +{ + return true; +} + +bool MineUnitGroupAction::Perform(UnitGroup *pug) +{ + UnitListEntry *pule = pug->GetUnitList(); + for (int iule = 0; iule < pug->GetUnitCount(); iule++, pule++) { + + // Ignore dead units + + MinerGob *pmnr = (MinerGob *)ggobm.GetGob(pule->gid); + if (pmnr == NULL) + continue; + if (pmnr->GetType() != kgtGalaxMiner) + continue; + + SendMineCommand(pmnr->GetId(), kwxInvalid, kwxInvalid); + } + + // No waiting + + return true; +} + +// UnitGroup GuardViciniyt action: +// All members of the group stand still and guard as per the group's +// aggressiveness level. +// +// 1. Send a Guard command to each member of the group +// 2. Action is complete + +#define knVerGuardVicinityUnitGroupActionState 1 +bool GuardVicinityUnitGroupAction::LoadState(Stream *pstm) +{ + byte nVer = pstm->ReadByte(); + if (nVer != knVerGuardVicinityUnitGroupActionState) + return false; + + m_fWaiting = pstm->ReadByte() != 0; + m_tStart = pstm->ReadDword(); + + return pstm->IsSuccess(); +} + +bool GuardVicinityUnitGroupAction::SaveState(Stream *pstm) +{ + pstm->WriteByte(knVerGuardVicinityUnitGroupActionState); + pstm->WriteByte(m_fWaiting); + pstm->WriteDword(m_tStart); + + return pstm->IsSuccess(); +} + +bool GuardVicinityUnitGroupAction::Init(char *psz) +{ + Reset(); + + int cSecs; + if (!ParseNumber(&psz, &cSecs)) + return false; + m_ctWait = cSecs * 100; + return true; +} + +void GuardVicinityUnitGroupAction::Reset() +{ + m_fWaiting = false; +} + +bool GuardVicinityUnitGroupAction::Perform(UnitGroup *pug) +{ + // If in the middle of waiting, check if the wait is over. + + long t = gsim.GetTickCount(); + if (m_fWaiting) { + // Wait over? If so, return true + + if (t - m_tStart >= m_ctWait) { + m_fWaiting = false; + return true; + } + } else { + + // Starting the action. Reset the wait start. + + m_fWaiting = true; + m_tStart = t; + + // Have everyone Guard + + Message msgT; + msgT.smidSender = ksmidNull; + + UnitListEntry *pule = pug->GetUnitList(); + for (int iule = 0; iule < pug->GetUnitCount(); iule++, pule++) { + + // Ignore dead units + + MobileUnitGob *pmunt = (MobileUnitGob *)ggobm.GetGob(pule->gid); + if (pmunt == NULL) + continue; + Assert((pmunt->GetFlags() & kfGobMobileUnit) != 0); + + // Tell it to Guard + + // The MobileUnitGob's state machine slams this member of the message + // so we must reinitialize it. + + msgT.mid = kmidGuardVicinityAction; + + msgT.smidReceiver = pmunt->GetId(); + gsmm.SendMsg(&msgT); + } + } + + // Keep 'waiting' (until all group members are dead or the Guard time runs out). + + return false; +} + +} // namespace wi \ No newline at end of file diff --git a/game/VTS.cpp b/game/VTS.cpp new file mode 100644 index 0000000..42968e8 --- /dev/null +++ b/game/VTS.cpp @@ -0,0 +1,66 @@ +#include "ht.h" + +namespace wi { + +// +// VtsGob implementation +// + +static MobileUnitBuilderConsts gConsts; +MobileUnitBuildForm *VtsGob::s_pfrmBuild = NULL; + + +// +// Gob methods +// + +bool VtsGob::InitClass(IniReader *pini) +{ + gConsts.gt = kgtVehicleTransportStation; + gConsts.ut = kutVehicleTransportStation; + gConsts.umPrerequisites = kumReactor; + gConsts.umCanBuild = kumVehicles; + gConsts.wf |= kfUntcMobileUnitBuilder; + + // Preload the Vts's Build form + + s_pfrmBuild = new MobileUnitBuildForm(); + if (s_pfrmBuild == NULL) + return false; + + if (!s_pfrmBuild->Init(gpmfrmm, gpiniForms, kidfBuildVehicle)) + return false; + gpmfrmm->RemoveForm(s_pfrmBuild); + + // Sound effects + + gConsts.sfxUnitBuildAbort = ksfxVehicleTransportStationAbortManufacture; + gConsts.sfxUnitBuild = ksfxVehicleTransportStationManufacture; + gConsts.sfxUnitReady = ksfxVehicleTransportStationVehicleReady; + gConsts.sfxAbortRepair = ksfxVehicleTransportStationAbortRepair; + gConsts.sfxDamaged = ksfxVehicleTransportStationDamaged; + gConsts.sfxDestroyed = ksfxVehicleTransportStationDestroyed; + gConsts.sfxRepair = ksfxVehicleTransportStationRepair; + gConsts.sfxSelect = ksfxVehicleTransportStationSelect; + + // MobileUnitBuilderConsts + + gConsts.fUpgrade = kfUpgradeVts; + gConsts.fUpgradeInProgress = kfUpgradeVtsInProgress; + gConsts.pfrmBuild = s_pfrmBuild; + + return BuilderGob::InitClass(&gConsts, pini); +} + +void VtsGob::ExitClass() +{ + BuilderGob::ExitClass(&gConsts); + delete s_pfrmBuild; + s_pfrmBuild = NULL; +} + +VtsGob::VtsGob() : MobileUnitBuilderGob(&gConsts) +{ +} + +} // namespace wi \ No newline at end of file diff --git a/game/Warehouse.cpp b/game/Warehouse.cpp new file mode 100644 index 0000000..5f64ded --- /dev/null +++ b/game/Warehouse.cpp @@ -0,0 +1,148 @@ +#include "ht.h" + +namespace wi { + +// +// WarehouseGob implementation +// + +static StructConsts gConsts; + +// +// Gob methods +// + +bool WarehouseGob::InitClass(IniReader *pini) +{ + gConsts.gt = kgtWarehouse; + gConsts.ut = kutWarehouse; + gConsts.umPrerequisites = kumReactor | kumProcessor; + gConsts.wf |= kfUntcHasFullnessIndicator; + + // Sound effects + + gConsts.sfxAbortRepair = ksfxGalaxiteWarehouseAbortRepair; + gConsts.sfxRepair = ksfxGalaxiteWarehouseRepair; + gConsts.sfxDamaged = ksfxGalaxiteWarehouseDamaged; + gConsts.sfxSelect = ksfxGalaxiteWarehouseSelect; + gConsts.sfxDestroyed = ksfxGalaxiteWarehouseDestroyed; + gConsts.sfxImpact = ksfxNothing; + + return StructGob::InitClass(&gConsts, pini); +} + +void WarehouseGob::ExitClass() +{ + StructGob::ExitClass(&gConsts); +} + +WarehouseGob::WarehouseGob() : StructGob(&gConsts) +{ +} + +// Override Takeover to transfer funds to the new owner + +void WarehouseGob::Takeover(Player *pplr) +{ + // Takeover the credits this Warehouse 'owns' + + long cCreditsTaken = CalcCreditsShare(m_pplr); + m_pplr->SetCredits(m_pplr->GetCredits() - cCreditsTaken, true); + pplr->SetCredits(pplr->GetCredits() + cCreditsTaken, true); + + // Takeover the Warehouse itself + + StructGob::Takeover(pplr); +} + +void WarehouseGob::Draw(DibBitmap *pbm, int xViewOrigin, int yViewOrigin, int nLayer) +{ + // Don't waste time drawing a dead Warehouse's fullness indicator, etc + + if (!(m_ff & kfGobActive)) { + StructGob::Draw(pbm, xViewOrigin, yViewOrigin, nLayer); + } else { + + long nCapacity = m_pplr->GetCapacity(); + + // OPT: add a Player::GetFullnessPips to cache this calculation and have + // SetCapacity/SetCredits set a recalc bit. + + int nPips = 0; + if (nCapacity != 0) { + nPips = ((m_pplr->GetCredits() * 10) + (nCapacity / 20)) / nCapacity; + if (nPips > 10) // over capacity! + nPips = 10; + } + + // Select the appropriate frame depending on how close the player is to capacity + + SetAnimationFrame(&m_ani, nPips / 3); // 0,1,2=empty, 3,4,5=half full, 6,7,8=mostly full, 9,10=full + + StructGob::Draw(pbm, xViewOrigin, yViewOrigin, nLayer); + + if (nLayer == knLayerSelection && (m_ff & kfGobSelected)) { + WRect wrcT; + GetUIBounds(&wrcT); + Rect rcT; + rcT.FromWorldRect(&wrcT); + rcT.Offset(-xViewOrigin, -yViewOrigin); + DrawFullnessIndicator(pbm, &rcT, nPips, 10); + } + } +} + +// +// StateMachine methods +// + +#if defined(DEBUG_HELPERS) +char *WarehouseGob::GetName() +{ + return "Warehouse"; +} +#endif + +// UNDONE: when a Warehouse is destroyed some amount of the Galaxite +// is carrying should be lost + +int WarehouseGob::ProcessStateMachineMessage(State st, Message *pmsg) +{ +BeginStateMachine + //----------------------------------------------------------------------- + + State(kstIdle) + // Override to avoid animating through the Galaxite level frames + OnUpdate + DefUpdate(); + + //----------------------------------------------------------------------- + + + State(kstDying) + OnEnter + // Sorry, player must lose some Credits along with this Warehouse + + if (m_ff & kfGobActive) + m_pplr->SetCredits(m_pplr->GetCredits() - CalcCreditsShare(m_pplr), true); + + return StructGob::ProcessStateMachineMessage(st, pmsg); + + // Override to avoid animating through the Galaxite level frames + OnUpdate + + //----------------------------------------------------------------------- + +#if 0 +EndStateMachineInherit(StructGob) +#else + return knHandled; + } + } else { + return (int)StructGob::ProcessStateMachineMessage(st, pmsg); + } + return (int)StructGob::ProcessStateMachineMessage(st, pmsg); +#endif +} + +} // namespace wi \ No newline at end of file diff --git a/game/alertcontrol.cpp b/game/alertcontrol.cpp new file mode 100644 index 0000000..bddf1bf --- /dev/null +++ b/game/alertcontrol.cpp @@ -0,0 +1,79 @@ +#include "ht.h" + +namespace wi { + +// +// Alerts +// + +void ShowAlert(int id) +{ + char sz[kcchAlertText]; + if (gpstrtbl == NULL) + return; + gpstrtbl->GetString(id, sz, sizeof(sz)); + ShowAlert(sz); +} + +void ShowAlert(const char *psz) +{ + SimUIForm *pfrm = ggame.GetSimUIForm(); + if (pfrm == NULL) + return; + ((AlertControl *)pfrm->GetControlPtr(kidcAlert))->AddText(psz); +} + +// +// AlertControl +// + +AlertControl::AlertControl() +{ + m_wf = 0; +} + +#define kfTimerSet 1 +AlertControl::~AlertControl() +{ + if (m_wf & kfTimerSet) + gtimm.RemoveTimer(this); +} + +bool AlertControl::Init(Form *pfrm, IniReader *pini, FindProp *pfind) +{ + // Base initialization + + if (!Control::Init(pfrm, pini, pfind)) + return false; + + char szT[1]; + szT[0] = 0; + return LabelControl::Init(kifntShadow, szT, szT, szT); +} + +#define kctInterval 400 // 400 ticks, or 4 seconds +void AlertControl::AddText(const char *psz) +{ + // Add new Alert + + SetText(psz); + if (m_wf & kfTimerSet) { + gtimm.SetTimerRate(this, kctInterval); + } else { + gtimm.AddTimer(this, kctInterval); + m_wf |= kfTimerSet; + } + Show(true); +} + +void AlertControl::OnTimer(long tCurrent) +{ + // on timer firing the status should no longer be displayed + + Assert((m_wf & kfTimerSet) != 0); + Show(false); + gtimm.RemoveTimer(this); + m_wf &= ~kfTimerSet; +} + +} // namespace wi diff --git a/game/bitmap.cpp b/game/bitmap.cpp new file mode 100644 index 0000000..be3c253 --- /dev/null +++ b/game/bitmap.cpp @@ -0,0 +1,546 @@ +#include "ht.h" + +namespace wi { + +// Dib bitmaps + +DibBitmap *CreateDibBitmap(byte *pb, int cx, int cy) +{ + DibBitmap *pbm = new DibBitmap; + Assert(pbm != NULL, "out of memory!"); + if (pbm == NULL) + return NULL; + if (!pbm->Init(pb, cx, cy)) { + delete pbm; + return NULL; + } + return pbm; +} + +DibBitmap::DibBitmap() +{ + m_cbRow = 0; + m_cb = 0; + m_pb = NULL; + m_siz.cx = 0; + m_siz.cy = 0; + m_wf = 0; +} + +DibBitmap::~DibBitmap() +{ + if (m_wf & kfDibFreeMem) + delete m_pb; + m_pb = NULL; +} + +bool DibBitmap::Init(byte *pb, int cx, int cy) +{ + m_cbRow = (cx + 1) & ~1; + m_cb = (long)cy * m_cbRow; + if (pb == NULL) { + m_pb = new byte[m_cb]; + Assert(m_pb != NULL, "out of memory!"); + m_wf |= kfDibFreeMem; + } else { + m_pb = pb; + } + m_siz.cx = cx; + m_siz.cy = cy; + return m_pb != NULL; +} + +byte *DibBitmap::GetBits() +{ + return m_pb; +} + +int DibBitmap::GetRowBytes() +{ + return m_cbRow; +} + +#define TopToBottomBltThunk LeftToRightBltThunk +#define FastestBltThunk LeftToRightBltThunk +#define BottomToTopBltThunk RightToLeftBltThunk + +void DibBitmap::Blt(DibBitmap *pbmSrc, Rect *prcSrc, int xDst, int yDst) +{ + // Get src dib dimensions + + Size sizSrc; + pbmSrc->GetSize(&sizSrc); + + // Clip to source rect + + if (prcSrc->left < 0) + prcSrc->left = 0; + if (prcSrc->top < 0) + prcSrc->top = 0; + if (prcSrc->right > sizSrc.cx) + prcSrc->right = sizSrc.cx; + if (prcSrc->bottom > sizSrc.cy) + prcSrc->bottom = sizSrc.cy; + + // Clip to dest + + if (xDst < 0) { + prcSrc->left -= xDst; + xDst = 0; + } + if (yDst < 0) { + prcSrc->top -= yDst; + yDst = 0; + } + + int xRightDst = xDst + prcSrc->Width(); + if (xRightDst > m_siz.cx) + prcSrc->right -= xRightDst - m_siz.cx; + int yBottomDst = yDst + prcSrc->Height(); + if (yBottomDst > m_siz.cy) + prcSrc->bottom -= yBottomDst - m_siz.cy; + + // Anything to blt? + + if (prcSrc->IsEmpty()) + return; + + // Figure out direction to blt if we're doing a blt in the same dib + + if (this == pbmSrc) { + // Calc addresses + + byte *pbSrc = m_pb + (long)prcSrc->top * m_cbRow + prcSrc->left; + byte *pbDst = m_pb + (long)yDst * m_cbRow + xDst; + + // If same y, ... + + if (yDst == prcSrc->top) { + // Overlap? + + int cxInterval = prcSrc->right - xDst; + if (cxInterval > 0 && cxInterval < prcSrc->Width() * 2) { + // Overlap. If bltting to the left, copy from left to right + + if (xDst < prcSrc->left) { + LeftToRightBltThunk(pbSrc, m_cbRow, pbDst, m_cbRow, prcSrc->Width(), prcSrc->Height()); + } else { + RightToLeftBltThunk(pbSrc, m_cbRow, pbDst, m_cbRow, prcSrc->Width(), prcSrc->Height()); + } + } else { + // No overlap. Do the fastest blt + + FastestBltThunk(pbSrc, m_cbRow, pbDst, m_cbRow, prcSrc->Width(), prcSrc->Height()); + } + } else { + int cyInterval = prcSrc->bottom - yDst; + if (cyInterval > 0 && cyInterval < prcSrc->Height() * 2) { + // Overlap. If bltting upwards, copy from top to bottom + + if (yDst < prcSrc->top) { + TopToBottomBltThunk(pbSrc, m_cbRow, pbDst, m_cbRow, prcSrc->Width(), prcSrc->Height()); + } else { + BottomToTopBltThunk(pbSrc, m_cbRow, pbDst, m_cbRow, prcSrc->Width(), prcSrc->Height()); + } + } else { + // No overlap. Do the fastest blt + + FastestBltThunk(pbSrc, m_cbRow, pbDst, m_cbRow, prcSrc->Width(), prcSrc->Height()); + } + } + } else { + // No overlap. Do the fastest blt + + int cbRowBytesSrc = pbmSrc->GetRowBytes(); + byte *pbSrc = pbmSrc->GetBits() + (long)prcSrc->top * cbRowBytesSrc + prcSrc->left; + byte *pbDst = m_pb + (long)yDst * m_cbRow + xDst; + + FastestBltThunk(pbSrc, cbRowBytesSrc, pbDst, m_cbRow, prcSrc->Width(), prcSrc->Height()); + } +} + +void DibBitmap::Clear(Color clr) +{ +#ifdef __CPU_68K + if (gfArmPresent) { + memsetArm(m_pb, (byte)clr, m_cb); + return; + } +#endif + memset(m_pb, (byte)clr, m_cb); +} + +void DibBitmap::Fill(int x, int y, int cx, int cy, Color clr) +{ + // Destination clipping + + if (x < 0) { + cx += x; + x = 0; + } + if (y < 0) { + cy += y; + y = 0; + } + if (x + cx > m_siz.cx) + cx = m_siz.cx - x; + if (y + cy > m_siz.cy) + cy = m_siz.cy - y; + + if (cx <= 0 || cy <= 0) + return; + + // Drawing + + byte *pbDst = m_pb + (long)y * m_cbRow + x; + FillThunk(pbDst, cx, cy, m_cbRow, (byte)clr); +} + +void DibBitmap::Shadow(int x, int y, int cx, int cy) +{ + // Destination clipping + + if (x < 0) { + cx += x; + x = 0; + } + if (y < 0) { + cy += y; + y = 0; + } + if (x + cx > m_siz.cx) + cx = m_siz.cx - x; + if (y + cy > m_siz.cy) + cy = m_siz.cy - y; + + if (cx <= 0 || cy <= 0) + return; + + byte *pbRow = m_pb + (long)y * m_cbRow + x; + FillShadowThunk(pbRow, m_cbRow, cx, cy, gmpiclriclrShadow); +} + +void DibBitmap::GetSize(Size *psiz) +{ + *psiz = m_siz; +} + +#ifdef DRAW_LINES + +#define LEFT 1 +#define RIGHT 2 +#define BOTTOM 4 +#define TOP 8 + +#define SWAP(x, y) { int _t = x; x = y; y = _t; } + +#define OUTCODE(x, y, outcode, type) \ +{ \ + if (x < xl) outcode = LEFT, type = 1; \ + else if (x > xr) outcode = RIGHT, type = 1; \ + else outcode = type = 0; \ + if (y < yb) outcode |= BOTTOM, type++; \ + else if (y > yt) outcode |= TOP, type++; \ +} + +#define CLIP(a1, a2, b1, da, da2, db2, as, bs, sa, sb, \ + amin, AMIN, amax, AMAX, bmin, BMIN, bmax, BMAX) \ +{ \ + if (out1) { \ + if (out1 & AMIN) { ca = db2 * (amin - a1); as = amin; } \ + else if (out1 & AMAX) { ca = db2 * (a1 - amax); as = amax; } \ + if (out1 & BMIN) { cb = da2 * (bmin - b1); bs = bmin; } \ + else if (out1 & BMAX) { cb = da2 * (b1 - bmax); bs = bmax; } \ + if (type1 == 2) \ + out1 &= (ca + da < cb) ? ~(AMIN | AMAX) : ~(BMAX | BMIN); \ + if (out1 & (AMIN | AMAX)) { \ + cb = (ca + da) / da2; \ + if (sb >= 0) { if ((bs = b1 + cb) > bmax) return; } \ + else { if ((bs = b1 - cb) < bmin) return; } \ + r += ca - da2 * cb; \ + } \ + else { \ + ca = (cb - da + db2 - 1) / db2; \ + if (sa >= 0) { if ((as = a1 + ca) > amax) return; } \ + else { if ((as = a1 - ca) < amin) return; } \ + r += db2 * ca - cb; \ + } \ + } \ + else { as = a1; bs = b1; } \ + alt = 0; \ + if (out2) { \ + if (type2 == 2) { \ + ca = db2 * ((out2 & AMIN) ? a1 - amin : amax - a1); \ + cb = da2 * ((out2 & BMIN) ? b1 - bmin : bmax - b1); \ + out2 &= (cb + da < ca + 1) ? ~(AMIN | AMAX) : ~(BMIN | BMAX); \ + } \ + if (out2 & (AMIN | AMAX)) n = (out2 & AMIN) ? as - amin : amax - as; \ + else { n = (out2 & BMIN) ? bs - bmin : bmax - bs; alt = 1; } \ + } \ + else n = (a2 >= as) ? a2 - as : as - a2; \ +} + +#define plot(x, y) (*(m_pb + (y * m_cbRow) + x) = (byte)clr) + +void DibBitmap::DrawLine(short x1, short y1, short x2, short y2, Color clr) +{ + long xl = 0, yb = 0; + long xr = m_siz.cx - 1; + long yt = m_siz.cy - 1; + + long adx, ady, adx2, ady2, sx, sy; + long out1, out2, type1, type2; + long ca, cb, r, diff, xs, ys, n, alt; + + OUTCODE(x1, y1, out1, type1); + OUTCODE(x2, y2, out2, type2); + if (out1 & out2) return; + if ((type1 != 0 && type2 == 0) || (type1 == 2 && type2 == 1)){ + SWAP(out1, out2); + SWAP(type1, type2); + SWAP(x1, x2); + SWAP(y1, y2); + } + xs = x1; + ys = y1; + sx = 1; + adx = x2 - x1; + if (adx < 0) { adx = -adx; sx = -1; } + sy = 1; + ady = y2 - y1; + if (ady < 0) { ady = -ady; sy = -1; } + adx2 = adx + adx; + ady2 = ady + ady; + if (adx >= ady) { + /* + * line is semi-horizontal + */ + r = ady2 - adx; + CLIP(x1, x2, y1, adx, adx2, ady2, xs, ys, sx, sy, + xl, LEFT, xr, RIGHT, yb, BOTTOM, yt, TOP); + diff = ady2 - adx2; + if (alt) { + for (;; xs += sx) { /* alternate Bresenham */ + plot(xs, ys); + if (r >= 0 ) { + if (--n < 0) break; + r += diff; + ys += sy; + } + else r += ady2; + } + } + else{ + for (;; xs += sx) { /* standard Bresenham */ + plot(xs, ys); + if (--n < 0) break; + if (r >= 0 ) { r += diff; ys += sy; } + else r += ady2; + } + } + } + else { + /* + * line is semi-vertical + */ + r = adx2 - ady; + CLIP(y1, y2, x1, ady, ady2, adx2, ys, xs, sy, sx, + yb, BOTTOM, yt, TOP, xl, LEFT, xr, RIGHT); + diff = adx2 - ady2; + if (alt) { + for (;; ys += sy) { /* alternate Bresenham */ + plot(xs, ys); + if (r >= 0 ) { + if (--n < 0) break; + r += diff; + xs += sx; + } + else r += adx2; + } + } + else { + for (;; ys += sy) { /* standard Bresenham */ + plot(xs, ys); + if (--n < 0) break; + if (r >= 0 ) { r += diff; xs += sx; } + else r += adx2; + } + } + } +} +#endif // DRAW_LINES + +DibBitmap *DibBitmap::Suballoc(int yTop, int cy) +{ + Assert(yTop < m_siz.cy && yTop + cy <= m_siz.cy); + byte *pb = m_pb + (long)m_siz.cx * yTop; + return CreateDibBitmap(pb, m_siz.cx, cy); +} + +void DibBitmap::Scroll(Rect *prcSrc, int xDst, int yDst) +{ + // Some implementations do blts differently (such as rotated dibs). + // That is why this method - essentially a blt from and to the + // destination - exists. + Blt(this, prcSrc, xDst, yDst); +} + +#ifdef __CPU_68K +void DibBitmap::BltTiles(DibBitmap *pbmSrc, UpdateMap *pupd, int yTopDst) +{ + // OS5 path usually not executed + + if (IsOS50Compat()) { + bool fFirst = true; + Rect rc; + while (pupd->EnumUpdateRects(fFirst, NULL, &rc)) { + fFirst = false; + Blt(pbmSrc, &rc, rc.left, rc.top + yTopDst); + } + return; + } + + // Init + + bool *pfMap = pupd->GetInvalidMap(); + MapInfo *pmnfo = pupd->GetMapInfo(); + Size sizMap; + pupd->GetMapSize(&sizMap); + + dword cbOffset = yTopDst * (dword)m_siz.cx; + byte *pbSrc = pbmSrc->GetBits(); + byte *pbDst = m_pb + cbOffset; + + switch (gcxTile) { + case 16: + UpdateScreen816(pfMap, sizMap.cx, sizMap.cy, pbSrc, pbDst, m_siz.cx, + pmnfo->cxLeftTile, pmnfo->cyTopTile, pmnfo->cxRightTile, pmnfo->cyBottomTile, pmnfo->ctxInside, pmnfo->ctyInside); + break; + + case 24: + UpdateScreen824(pfMap, sizMap.cx, sizMap.cy, pbSrc, pbDst, m_siz.cx, + pmnfo->cxLeftTile, pmnfo->cyTopTile, pmnfo->cxRightTile, pmnfo->cyBottomTile, pmnfo->ctxInside, pmnfo->ctyInside); + break; + } +} + +#else +void DibBitmap::BltTiles(DibBitmap *pbmSrc, UpdateMap *pupd, int yTopDst) +{ + bool fFirst = true; + Rect rc; + while (pupd->EnumUpdateRects(fFirst, NULL, &rc)) { + fFirst = false; + Blt(pbmSrc, &rc, rc.left, rc.top + yTopDst); + } +} + +#if 0 +void DibBitmap::BltTiles(DibBitmap *pbmSrc, UpdateMap *pupd, int yTopDst) +{ + MapInfo *pmnfo = pupd->GetMapInfo(); + bool *pfInvalid = pupd->GetInvalidMap(); + Size sizMap; + pupd->GetMapSize(&sizMap); + int cbReturn = sizMap.cx - ((pmnfo->cxLeftTile != 0 ? 1 : 0) + pmnfo->ctxInside + (pmnfo->cxRightTile != 0 ? 1 : 0)); + + Rect rcSrc; + int xT = 0; + int yDst = yTopDst; + int ySrc = 0; + if (pmnfo->cyTopTile != 0) { + // Upper left corner + + if (pmnfo->cxLeftTile != 0 && *pfInvalid++) { + rcSrc.Set(xT, ySrc, xT + pmnfo->cxLeftTile, ySrc + pmnfo->cyTopTile); + Blt(pbmSrc, &rcSrc, xT, yDst); + } + xT += pmnfo->cxLeftTile; + + // Upper edge + + for (int tx = 0; tx < pmnfo->ctxInside; tx++) { + if (*pfInvalid++) { + rcSrc.Set(xT, ySrc, xT + gcxTile, ySrc + pmnfo->cyTopTile); + Blt(pbmSrc, &rcSrc, xT, yDst); + } + xT += gcxTile; + } + + // Upper right corner + + if (pmnfo->cxRightTile != 0 && *pfInvalid++) { + rcSrc.Set(xT, ySrc, xT + pmnfo->cxRightTile, ySrc + pmnfo->cyTopTile); + Blt(pbmSrc, &rcSrc, xT, yDst); + } + xT = 0; + yDst += pmnfo->cyTopTile; + ySrc += pmnfo->cyTopTile; + pfInvalid += cbReturn; + } + + // Inside + + for (int ty = 0; ty < pmnfo->ctyInside; ty++) { + + // Inside left + + if (pmnfo->cxLeftTile != 0 && *pfInvalid++) { + rcSrc.Set(xT, ySrc, xT + pmnfo->cxLeftTile, ySrc + gcyTile); + Blt(pbmSrc, &rcSrc, xT, yDst); + } + xT += pmnfo->cxLeftTile; + + // Inside + + for (int tx = 0; tx < pmnfo->ctxInside; tx++) { + if (*pfInvalid++) { + rcSrc.Set(xT, ySrc, xT + gcxTile, ySrc + gcyTile); + Blt(pbmSrc, &rcSrc, xT, yDst); + } + xT += gcxTile; + } + + // Inside right + + if (pmnfo->cxRightTile != 0 && *pfInvalid++) { + rcSrc.Set(xT, ySrc, xT + pmnfo->cxRightTile, ySrc + gcyTile); + Blt(pbmSrc, &rcSrc, xT, yDst); + } + xT = 0; + yDst += gcyTile; + ySrc += gcyTile; + pfInvalid += cbReturn; + } + + if (pmnfo->cyBottomTile != 0) { + // Bottom left tile + + if (pmnfo->cxLeftTile != 0 && *pfInvalid++) { + rcSrc.Set(xT, ySrc, xT + pmnfo->cxLeftTile, ySrc + pmnfo->cyBottomTile); + Blt(pbmSrc, &rcSrc, xT, yDst); + } + xT += pmnfo->cxLeftTile; + + // Bottom edge + + for (int tx = 0; tx < pmnfo->ctxInside; tx++) { + if (*pfInvalid++) { + rcSrc.Set(xT, ySrc, xT + gcxTile, ySrc + pmnfo->cyBottomTile); + Blt(pbmSrc, &rcSrc, xT, yDst); + } + xT += gcxTile; + } + + // Bottom right corner + + if (pmnfo->cxRightTile != 0 && *pfInvalid++) { + rcSrc.Set(xT, ySrc, xT + pmnfo->cxRightTile, ySrc + pmnfo->cyBottomTile); + Blt(pbmSrc, &rcSrc, xT, yDst); + } + } +} +#endif +#endif + +} // namespace wi \ No newline at end of file diff --git a/game/cachemgr.cpp b/game/cachemgr.cpp new file mode 100644 index 0000000..6b2aff5 --- /dev/null +++ b/game/cachemgr.cpp @@ -0,0 +1,228 @@ +#include "ht.h" + +namespace wi { + +CacheMgr gcam; + +CacheMgr::CacheMgr() +{ + m_pceList = NULL; + m_pceFirst = NULL; + m_pceFree = NULL; + m_cbTotalSize = 0; + m_cbLimit = 0; +} + +CacheMgr::~CacheMgr() +{ + Assert(m_pceList == NULL); +} + +void CacheMgr::Exit() +{ + // Free cache entries + + while (m_pceFirst != NULL) + Discard(m_pceFirst); + m_pceFirst = NULL; + + // Free CacheEntry list + + delete m_pceList; + m_pceList = NULL; + m_pceFree = NULL; +} + +bool CacheMgr::Init() +{ + // Alloc cache entries + + m_pceList = new CacheEntry[kcCacheEntries]; + Assert(m_pceList != NULL, "out of memory!"); + if (m_pceList == NULL) + return false; + memset(m_pceList, 0, sizeof(CacheEntry) * kcCacheEntries); + + // Add all entries to free list + + for (CacheEntry *pce = m_pceList; pce < &m_pceList[kcCacheEntries]; pce++) { + pce->wUniqueLock = kwIncUnique; + AddToFreeList(pce); + } + + return true; +} + +dword CacheMgr::GetLimit() +{ + return m_cbLimit; +} + +void CacheMgr::SetLimit(dword cbLimit) +{ + if (cbLimit == 0) { + m_cbLimit = 0; + return; + } + gcam.MakeSpace((dword)-1); + dword cbMin = m_cbTotalSize + ((dword)32 * 1024); + if (cbLimit < cbMin) + cbLimit = cbMin; + m_cbLimit = cbLimit; +} + +dword CacheMgr::GetTotalSize() +{ + return m_cbTotalSize; +} + +void *CacheMgr::GetPtr(CacheHandle hc) +{ + CacheEntry *pce = ValidateHandle(hc); + if (pce == NULL) + return NULL; + Assert(pce->hmem != NULL); + Remove(pce); + Add(pce); + return gmmgr.GetPtr(pce->hmem); +} + +void *CacheMgr::Lock(CacheHandle hc) +{ + CacheEntry *pce = ValidateHandle(hc); + if (pce == NULL) + return NULL; + Assert(pce->hmem != NULL); + Remove(pce); + Add(pce); + Assert(((pce->wUniqueLock + kwIncLock) & kwLockMask) != 0); + pce->wUniqueLock += kwIncLock; + if ((pce->wUniqueLock & kwLockMask) == kwIncLock) + gmmgr.SetLocked(pce->hmem); + return gmmgr.GetPtr(pce->hmem); +} + +void CacheMgr::Unlock(CacheHandle hc) +{ + CacheEntry *pce = ValidateHandle(hc); + if (pce == NULL) + return; + Assert(pce->hmem != NULL); + Assert((pce->wUniqueLock & kwLockMask) != 0); + pce->wUniqueLock -= kwIncLock; + if ((pce->wUniqueLock & kwLockMask) == 0) + gmmgr.ClearLocked(pce->hmem); +} + +bool CacheMgr::IsValid(CacheHandle hc) +{ + CacheEntry *pce = ValidateHandle(hc); + return pce != NULL; +} + +CacheHandle CacheMgr::NewObject(void *pv, word cb, word wfHints) +{ + // Apply limits if asked + + if (m_cbLimit != 0) { + while (m_cbTotalSize + cb > m_cbLimit) { + if (!MakeSpace(m_cbTotalSize + cb - m_cbLimit)) + return NULL; + } + } + + // Free up an entry if we need to + + if (m_pceFree == NULL) { + // No free slots available. Discard the oldest entry for reuse. + + for (CacheEntry *pceT = m_pceFirst->pcePrev; pceT != NULL; pceT = pceT->pcePrev) { + // If we loop back to m_pceFirst, then all CacheEntries are locked. No way! + + Assert(pceT != m_pceFirst); + if ((pceT->wUniqueLock & kwLockMask) == 0) { + Discard(pceT); + break; + } + } + } + CacheEntry *pce = m_pceFree->pcePrev; + Assert(pce != NULL); + if (pce == NULL) + return NULL; + + // Alloc the object + + pce->hmem = gmmgr.AllocHandle(cb, wfHints); + Assert(pce->hmem != NULL); + if (pce->hmem == NULL) + return NULL; + pce->cbSize = cb; + + // Write in data + + if (pv != NULL) + gmmgr.WriteHandle(pce->hmem, 0, pv, cb); + + // Take off free list, put at start of the alloced list + + RemoveFromFreeList(pce); + Add(pce); + m_cbTotalSize += cb; + return MakeHandle(pce); +} + +void CacheMgr::Write(CacheHandle hc, word ib, void *pvSrc, word cb) +{ + CacheEntry *pce = ValidateHandle(hc); + Assert(pce != NULL); + if (pce == NULL) + return; + gmmgr.WriteHandle(pce->hmem, ib, pvSrc, cb); +} + +word CacheMgr::GetSize(CacheHandle hc) +{ + CacheEntry *pce = ValidateHandle(hc); + Assert(pce != NULL); + if (pce == NULL) + return 0; + return pce->cbSize; +} + +void CacheMgr::Discard(CacheEntry *pce) +{ + Assert((pce->wUniqueLock & kwLockMask) == 0); + Assert(pce->hmem != NULL); + Remove(pce); + AddToFreeList(pce); + gmmgr.FreeHandle(pce->hmem); + pce->hmem = NULL; + pce->wUniqueLock += kwIncUnique; + if ((pce->wUniqueLock & kwUniqueMask) == 0) + pce->wUniqueLock += kwIncUnique; + m_cbTotalSize -= pce->cbSize; +} + +bool CacheMgr::MakeSpace(dword cb) +{ + if (m_pceFirst == NULL) + return false; + CacheEntry *pceT = m_pceFirst->pcePrev; + while (pceT != m_pceFirst) { + if ((pceT->wUniqueLock & kwLockMask) != 0) { + pceT = pceT->pcePrev; + continue; + } + CacheEntry *pcePrev = pceT->pcePrev; + Discard(pceT); + word cbLargestFree; + gmmgr.GetFreeSize(&cbLargestFree); + if (cbLargestFree >= cb) + return true; + pceT = pcePrev; + } + return false; +} + +} // namespace wi \ No newline at end of file diff --git a/game/calcdiagdist.js b/game/calcdiagdist.js new file mode 100644 index 0000000..9f89aa3 --- /dev/null +++ b/game/calcdiagdist.js @@ -0,0 +1,9 @@ +for (j = 0; j < 4; j++) { + var str = ""; + for (i = 0; i < 16; i++) { +// str += Math.floor((Math.cos(Math.PI / 4) * ((j * 16) + i) + .5)) + ", "; + var wcMoveDist = ((j * 16) + i); + str += Math.floor((256 + (wcMoveDist / 2)) / wcMoveDist) + ", "; + } + WScript.Echo(str); +} \ No newline at end of file diff --git a/game/chatcontroller.h b/game/chatcontroller.h new file mode 100644 index 0000000..02a6063 --- /dev/null +++ b/game/chatcontroller.h @@ -0,0 +1,26 @@ +#ifndef __CHATCONTROLLER_H__ +#define __CHATCONTROLLER_H__ + +namespace wi { + +class IChatControllerCallback { +public: + virtual void OnChatDismissed() = 0; + virtual void OnChatSend(const char *chat) = 0; + virtual void OnPlayers() = 0; +}; + +class IChatController { +public: + virtual void Clear() = 0; + virtual void AddChat(const char *player, const char *chat) = 0; + virtual void Show(bool fShow) = 0; + virtual void SetTitle(const char *title) = 0; + virtual const char *GetTitle() = 0; + virtual IChatControllerCallback *SetCallback( + IChatControllerCallback *pcccb) = 0; +}; + +} // namespace wi + +#endif // __CHATCONTROLLER_H__ diff --git a/game/chatter.cpp b/game/chatter.cpp new file mode 100644 index 0000000..f46242f --- /dev/null +++ b/game/chatter.cpp @@ -0,0 +1,100 @@ +#include "game/chatter.h" + +namespace wi { + +Chatter::Chatter(LoginHandler& handler) : handler_(handler), chatting_(false), + on_(true) { + chatc_ = HostGetChatController(); + old_ = chatc_->SetCallback(this); + ClearChat(); +} + +Chatter::~Chatter() { + chatc_->SetCallback(old_); + chatc_->Show(false); +} + +void Chatter::AddChat(const char *player, const char *chat, bool system) { + // Received chat from server, add it to the chat window + if (system) { + chatc_->AddChat("", chat); + } else { + chatc_->AddChat(player, chat); + if (!chatting_) { + char name[kcbPlayerName]; + handler_.GetPlayerName(name, sizeof(name)); + if (strcmp(name, player) != 0) { + gsndm.PlaySfx(ksfxGuiCheckBoxTap); + StartBlinking(); + } + } + } +} + +void Chatter::ClearChat() { + chatc_->Clear(); + StopBlinking(); +} + +void Chatter::ShowChat() { + if (!chatting_) { + chatc_->Show(true); + chatting_ = true; + StopBlinking(); + } +} + +void Chatter::SetChatTitle(const char *title) { + if (strcmp(title, title_.c_str()) != 0) { + title_ = title; + ClearChat(); + StopBlinking(); + } + chatc_->SetTitle(title); +} + +void Chatter::HideChat() { + if (chatting_) { + chatc_->Show(false); + chatting_ = false; + } +} + +void Chatter::OnChatDismissed() { + // Chat window was dismissed; remember this so next time chat comes in + // the blinker will start. + chatting_ = false; +} + +void Chatter::OnChatSend(const char *chat) { + // User pressed the Send button; send chat to the server + if (gptra != NULL) { + gptra->SendChat(chat); + } +} + +void Chatter::OnPlayers() { + SignalOnPlayers(); +} + +void Chatter::OnTimeout(int id) { + ShowChatButton(!on_); +} + +void Chatter::ShowChatButton(bool on) { + if (on != on_) { + on_ = on; + SignalOnBlink(on); + } +} + +void Chatter::StartBlinking() { + blinker_.Start(this, 1000, 0, false); +} + +void Chatter::StopBlinking() { + blinker_.Stop(); + ShowChatButton(true); +} + +} // namespace wi diff --git a/game/chatter.h b/game/chatter.h new file mode 100644 index 0000000..113e564 --- /dev/null +++ b/game/chatter.h @@ -0,0 +1,50 @@ +#ifndef __CHATTER_H__ +#define __CHATTER_H__ + +#include "game/ht.h" +#include "game/loginhandler.h" +#include "game/chatcontroller.h" +#include "base/sigslot.h" +#include + +namespace wi { + +class Chatter : private IChatControllerCallback, private ITimeout { +public: + Chatter(LoginHandler& handler); + ~Chatter(); + + void AddChat(const char *player, const char *chat, bool system); + void ClearChat(); + void HideChat(); + void ShowChat(); + void SetChatTitle(const char *title); + + base::signal1 SignalOnBlink; + base::signal0<> SignalOnPlayers; + +protected: + void ShowChatButton(bool on); + void StartBlinking(); + void StopBlinking(); + + // IChatControllerCallback + virtual void OnChatDismissed(); + virtual void OnChatSend(const char *chat); + virtual void OnPlayers(); + + // ITimeout + virtual void OnTimeout(int id); + + std::string title_; + LoginHandler& handler_; + TimeoutTimer blinker_; + IChatController *chatc_; + IChatControllerCallback *old_; + bool chatting_; + bool on_; +}; + +} // namespace wi + +#endif // __CHATTER_H__ diff --git a/game/chooseserverform.cpp b/game/chooseserverform.cpp new file mode 100644 index 0000000..042443f --- /dev/null +++ b/game/chooseserverform.cpp @@ -0,0 +1,370 @@ +#include "game/chooseserverform.h" +#include "game/simplerequest.h" +#include "game/serviceurls.h" +#include "game/loginhandler.h" +#include "mpshared/constants.h" +#include "base/format.h" +#include "base/thread.h" +#include "base/misc.h" +#include "yajl/wrapper/jsonbuilder.h" +#include "game/xtransport.h" +#include + +namespace wi { + +bool ServerInfoSort(const ServerInfo& info1, const ServerInfo& info2) { + return info1.sort_key < info2.sort_key; +} + +dword ChooseServerForm::DoForm(Transport **pptra, std::string *server_name) { + ChooseServerForm *pfrm = (ChooseServerForm *)gpmfrmm->LoadForm(gpiniForms, + kidfChooseServer, new ChooseServerForm()); + if (pfrm == NULL) { + return knChooseServerResultCancel; + } + + int result = 0; + pfrm->DoModal(&result); + Transport *ptra = pfrm->transport(); + std::string name = pfrm->server_name(); + delete pfrm; + + if (gevm.IsAppStopping()) { + return knChooseServerResultAppStop; + } + + if (result == kidcCancel || ptra == NULL) { + return knChooseServerResultCancel; + } + + *pptra = ptra; + *server_name = name; + return knChooseServerResultConnect; +} + +bool ChooseServerForm::DoModal(int *pnResult) { + // Hide Connect button until there is something to connect to + LabelControl *plbl = (LabelControl *)GetControlPtr(kidcOk); + plbl->Show(false); + + // Position the columns + PositionColumns(); + Refresh(false); + + // If there is a single entry this client can connect to, use it without + // presenting UI + if (infos_.size() == 1) { + Control *pctl = GetControlPtr(kidcOk); + if (pctl->GetFlags() & kfCtlVisible) { + const ServerInfo& info = infos_[0]; + if (Connect(info) == knTransportOpenResultSuccess) { + *pnResult = kidcOk; + return true; + } + } + } + + return ShellForm::DoModal(pnResult); +} + +void ChooseServerForm::OnZipDone() { + if (errors_) { + HtMessageBox(kfMbWhiteBorder, "Service Message", errorstr_.c_str()); + } +} + +void ChooseServerForm::PositionColumns() { + ListControl *plstc = (ListControl *)GetControlPtr(kidcServerList); + Rect rcList; + plstc->GetRect(&rcList); + Font *pfnt = plstc->GetFont(); + + LabelControl *plblPlayers = (LabelControl *)GetControlPtr(kidcNumPlayers); + int cxNumPlayers = pfnt->GetTextExtent(plblPlayers->GetText()); + int xNumPlayers = rcList.right - cxNumPlayers; + LabelControl *plblStatus = (LabelControl *)GetControlPtr(kidcServerStatus); + int cxStatus = pfnt->GetTextExtent(plblStatus->GetText()); + int xStatus = xNumPlayers - 40 - cxStatus; + int xLocation = rcList.left + (xStatus - rcList.left) / 2; + int xName = rcList.left; + + plstc->SetTabStops(xName - rcList.left, xLocation - rcList.left, + xStatus + cxStatus / 2 - rcList.left, + xNumPlayers + cxNumPlayers / 2 - rcList.left); + plstc->SetTabFlags(0, 0, kfLstTabCenterOn, kfLstTabCenterOn); + + word ids[] = { kidcServerName, kidcServerLocation, kidcServerStatus, + kidcNumPlayers }; + int ax[] = { xName, xLocation, xStatus, xNumPlayers }; + + for (int i = 0; i < ARRAYSIZE(ids); i++) { + LabelControl *plbl = (LabelControl *)GetControlPtr(ids[i]); + Rect rcCtl; + plbl->GetRect(&rcCtl); + plbl->SetPosition(ax[i], rcCtl.top); + } +} + +void ChooseServerForm::Refresh(bool show_errors) { + bool success = RequestInfos(); + + ListControl *plstc = (ListControl *)GetControlPtr(kidcServerList); + plstc->Clear(); + + if (infos_.size() == 0) { + if (success) { + errorstr_ = "The multiplayer service is down for maintenance. Try again later."; + } + if (show_errors) { + HtMessageBox(kfMbWhiteBorder, "Service Message", + errorstr_.c_str()); + } else { + errors_ = true; + } + } else { + for (int i = 0; i < infos_.size(); i++) { + const ServerInfo& info = infos_[i]; + const char *s = base::Format::ToString("%s\t%s\t%s\t%d", + info.name.c_str(), info.location.c_str(), info.status.c_str(), + info.player_count); + plstc->Add(s); + } + plstc->Select(0, true); + } + ShowHide(); +} + +void ChooseServerForm::OnControlSelected(word idc) { + switch (idc) { + case kidcOk: + { + ListControl *plstc = (ListControl *)GetControlPtr(kidcServerList); + int index = plstc->GetSelectedItemIndex(); + if (index >= 0 && index < infos_.size()) { + dword result = Connect(infos_[index]); + if (result == knTransportOpenResultSuccess) { + EndForm(kidcOk); + break; + } + ShowTransportError(result); + } + } + break; + + case kidcRefresh: + Refresh(); + break; + + case kidcCancel: + EndForm(kidcCancel); + break; + } +} + +dword ChooseServerForm::Connect(const ServerInfo& info) { + // Note for the moment, this only supports XTransport. This is done + // this way for the best user experience: so that a Connect can be + // attempted while this form is up, and if it fails the form stays up + // so the list can be refreshed, or another server tried. Bringing up + // this form is expensive because of the ServerInfo query. + + TransportWaitingUI twui("Contacting Service"); + Transport *ptra = (Transport *)new XTransport(info.address); + dword result = ptra->Open(); + if (result != knTransportOpenResultSuccess) { + delete ptra; + return result; + } + connected_ = info; + transport_ = ptra; + return result; +} + +void ChooseServerForm::ShowTransportError(dword result) { + const char *message = NULL; + switch(result) { + case knTransportOpenResultFail: + message = "Failure accessing network."; + break; + + case knTransportOpenResultNoNetwork: + message = "Please check for network connectivity."; + break; + + case knTransportOpenResultCantConnect: + message = "Could not connect to game server."; + break; + + case knTransportOpenResultNotResponding: + message = "Timed out waiting for Game server to respond."; + break; + + case knTransportOpenResultProtocolMismatch: + message = "This server requires that you upgrade to the latest Hostile Takeover before continuing."; + break; + + case knTransportOpenResultServerFull: + message = "This server is full. Please try another server."; + break; + } + + if (message != NULL) { + HtMessageBox(kfMbWhiteBorder, "Service Message", message); + } +} + +void ChooseServerForm::ShowHide() { + ListControl *plstc = (ListControl *)GetControlPtr(kidcServerList); + ButtonControl *pbtn = (ButtonControl *)GetControlPtr(kidcOk); + int index = plstc->GetSelectedItemIndex(); + if (index < 0 || index >= infos_.size()) { + pbtn->Show(false); + return; + } + ServerInfo& info = infos_[index]; + if (strcmp(info.status.c_str(), "ok") != 0) { + pbtn->Show(false); + return; + } + pbtn->Show(true); +} + +void ChooseServerForm::OnControlNotify(word idc, int nNotify) { + if (idc == kidcServerList && nNotify == knNotifySelectionChange) { + ShowHide(); + } + ShellForm::OnControlNotify(idc, nNotify); +} + +bool ChooseServerForm::RequestInfos() { + // Default error string + errorstr_ = "Invalid Response From Service"; + + TransportWaitingUI twui("Contacting Service"); + std::string url = GetServiceUrl(); + SimpleRequest req; + char errorstr[1024]; + if (!req.Get(url.c_str(), NULL, 0, errorstr, sizeof(errorstr))) { + errorstr_ = errorstr; + return false; + } + + // Parse json + json::JsonBuilder builder; + builder.Start(); + if (!builder.Update((const char *)req.body()->Data(), + req.body()->Length())) { + return false; + } + json::JsonObject *obj = builder.End(); + if (obj == NULL) { + return false; + } + + // Pull out infos and weed out the ones not appropriate for this client + json::JsonMap *map = (json::JsonMap *)obj; + json::JsonArray *infos = (json::JsonArray *)map->GetObject("infos"); + if (infos == NULL) { + delete obj; + return false; + } + std::map infomap; + for (int i = 0; i < infos->GetCount(); i++) { + json::JsonMap *json_map = (json::JsonMap *)infos->GetObject(i); + ServerInfo info; + info.sort_key = GetIntegerFromMap(json_map, "sort_key"); + info.name = GetStringFromMap(json_map, "name"); + info.location = GetStringFromMap(json_map, "location"); + info.address = base::SocketAddress(GetStringFromMap(json_map, + "address").c_str()); + info.protocol = GetIntegerFromMap(json_map, "protocol"); + info.status = GetStringFromMap(json_map, "status"); + info.player_count = GetIntegerFromMap(json_map, "player_count"); + info.type = GetStringFromMap(json_map, "type"); + + // If production client, only show production servers. +#if !defined(DEBUG) && !defined(BETA_TIMEOUT) + if (strcmp(info.type.c_str(), "production") != 0) { + continue; + } +#endif + + // Don't show old protocol servers to newer clients + if (info.protocol < kdwProtocolCurrent) { + continue; + } + + // If there is an info with this name that matches the protocol, + // keep it and discard the new info + std::map::iterator it = + infomap.find(info.name); + if (it != infomap.end()) { + if (it->second.protocol == kdwProtocolCurrent) { + continue; + } + + // Otherwise delete this so the new info can be added. This + // makes upgrading seamless. + infomap.erase(it); + } + + // Add new info into the map + infomap.insert(std::map::value_type( + info.name, info)); + } + delete obj; + + // Now read out the infos from the map, and sort them by sort_key + infos_.clear(); + std::map::iterator it = infomap.begin(); + while (it != infomap.end()) { + infos_.push_back(it->second); + it++; + } + std::stable_sort(infos_.begin(), infos_.end(), ServerInfoSort); + + return true; +} + +int ChooseServerForm::GetIntegerFromMap(json::JsonMap *map, const char *key) { + json::JsonString *s = (json::JsonString *)map->GetObject(key); + if (s == NULL) { + return 0; + } + int n = 0; + base::Format::ToInteger(s->GetString(), 10, &n); + return n; +} + +std::string ChooseServerForm::GetStringFromMap(json::JsonMap *map, + const char *key) { + json::JsonString *s = (json::JsonString *)map->GetObject(key); + if (s == NULL) { + return ""; + } + return s->GetString(); +} + +std::string ChooseServerForm::GetServiceUrl() { + // player, if exists + // device id, hashed + // protocol version + + std::string deviceid(base::StringEncoder::QueryEncode(gszDeviceId)); + + const char *url; + LoginHandler handler; + if (handler.anonymous()) { + url = base::Format::ToString("%s?x=%d&d=%s", kszServerInfoUrl, + kdwProtocolCurrent, deviceid.c_str()); + } else { + std::string player(base::StringEncoder::QueryEncode( + handler.username())); + url = base::Format::ToString("%s?x=%d&p=%s&d=%s", kszServerInfoUrl, + kdwProtocolCurrent, player.c_str(), deviceid.c_str()); + } + + return url; +} + +} diff --git a/game/chooseserverform.h b/game/chooseserverform.h new file mode 100644 index 0000000..d81eca2 --- /dev/null +++ b/game/chooseserverform.h @@ -0,0 +1,67 @@ +#ifndef __CHOOSESERVERFORM_H__ +#define __CHOOSESERVERFORM_H__ + +#include "game/ht.h" +#include "base/socketaddress.h" +#include "yajl/wrapper/jsontypes.h" +#include +#include + +namespace wi { + +struct ServerInfo { + int sort_key; + std::string name; + std::string location; + base::SocketAddress address; + int protocol; + std::string status; + int player_count; + std::string type; +}; + +const dword knChooseServerResultConnect = 0; +const dword knChooseServerResultCancel = 1; +const dword knChooseServerResultAppStop = 2; + +class ChooseServerForm : public ShellForm { +public: + ChooseServerForm() : transport_(NULL), errors_(false) {} + + Transport *transport() { return transport_; } + const std::string &server_name() { return connected_.name; } + + static dword DoForm(Transport **pptra, std::string *server_name); + + // Form overrides + virtual bool DoModal(int *pnResult); + virtual void OnControlSelected(word idc); + virtual void OnControlNotify(word idc, int nNotify); + + // ShellForm overrides + virtual void OnZipDone(); + +private: + void ShowHide(); + bool RequestInfos(); + void PositionColumns(); + void Refresh(bool show_errors = true); + std::string GetServiceUrl(); + dword Connect(const ServerInfo& info); + void ShowTransportError(dword error); + int GetIntegerFromMap(json::JsonMap *map, const char *key); + std::string GetStringFromMap(json::JsonMap *map, const char *key); + + std::vector infos_; + ServerInfo connected_; + std::string errorstr_; + Transport *transport_; + bool errors_; +}; + +} // namespace wi + +#endif // __CHOOSESERVERFORM_H__ + + + diff --git a/game/comm.cpp b/game/comm.cpp new file mode 100644 index 0000000..4d6b3e6 --- /dev/null +++ b/game/comm.cpp @@ -0,0 +1,32 @@ +#include "game/ht.h" + +namespace wi { + +Transport::Transport() { + m_ptcb = NULL; + m_plcb = NULL; + m_prcb = NULL; + m_pgcb = NULL; +} + +ITransportCallback *Transport::SetCallback(ITransportCallback *ptcb) { + ITransportCallback *old = m_ptcb; + m_ptcb = ptcb; + return old; +} + +ITransportCallback *Transport::GetCallback() { + return m_ptcb; +} + +IGameCallback *Transport::SetGameCallback(IGameCallback *pgcb) { + IGameCallback *old = m_pgcb; + m_pgcb = pgcb; + return old; +} + +IGameCallback *Transport::GetGameCallback() { + return m_pgcb; +} + +} // namespace wi diff --git a/game/completemanager.cpp b/game/completemanager.cpp new file mode 100644 index 0000000..7e4dec5 --- /dev/null +++ b/game/completemanager.cpp @@ -0,0 +1,130 @@ +#include "game/completemanager.h" +#include "mpshared/misc.h" +#include "base/format.h" +#include "game/httppackmanager.h" +#include +#include +#include + +namespace wi { + +void CompleteManager::Init() { + map_.clear(); + DIR *pdir = opendir(completedir_.c_str()); + dirent *pdent; + while ((pdent = readdir(pdir)) != NULL) { + dword key; + MissionHash mh; + if (!ParseFilename(pdent->d_name, &key, mh.hash)) { + continue; + } + map_.insert(CompleteMap::value_type(key, mh)); + } + closedir(pdir); +} + +bool CompleteManager::ParseFilename(const char *filename, dword *pkey, + byte *hash) { + + LOG() << filename; + + // key -hash + // kkkkkkkk-hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh + if (strlen(filename) != 8 + 1 + 32) { + return false; + } + char szId[9]; + strncpyz(szId, filename, 9); + if (!base::Format::ToDword(szId, 16, pkey)) { + return false; + } + return base::Format::FromHex(filename + 8 + 1, hash, 16); +} + +const char *CompleteManager::GetFilename(dword key, MissionHash *pmh) { + char szHash[33]; + strncpyz(szHash, base::Format::ToHex(pmh->hash, 16), sizeof(szHash)); + return base::Format::ToString("%08x-%s", key, szHash); +} + +bool CompleteManager::IsComplete(const MissionIdentifier *pmiid) { + // Quickly fail + dword key = GetKey(pmiid); + if (map_.count(key) == 0) { + return false; + } + + // Slow verification + MissionHash mh; + if (!GetMissionHash(pmiid, &mh)) { + return false; + } + + // See if this hash is known. + // Using a multimap for backwards compat reasons. See BadHashString. + std::pair range; + range = map_.equal_range(key); + for (CompleteMap::iterator it = range.first; it != range.second; it++) { + if (memcmp(mh.hash, it->second.hash, sizeof(mh.hash)) == 0) { + return true; + } + } + + return false; +} + +bool CompleteManager::GetMissionHash(const MissionIdentifier *pmiid, + MissionHash *pmh) { + gppackm->Mount(gpakr, &pmiid->packid); + if (!gpakr.HashFile(pmiid->szLvlFilename, pmh->hash)) { + gppackm->Unmount(gpakr, &pmiid->packid); + return false; + } + gppackm->Unmount(gpakr, &pmiid->packid); + return true; +} + +dword CompleteManager::GetKey(const MissionIdentifier *pmiid) { + const char *s = base::Format::ToString("%08x", pmiid->packid.id); + return BadHashString(s) ^ BadHashString(pmiid->szLvlFilename); +} + +void CompleteManager::MarkComplete(const MissionIdentifier *pmiid) { + MissionHash mh; + if (!GetMissionHash(pmiid, &mh)) { + return; + } + dword key = GetKey(pmiid); + const char *filename = GetFilename(key, &mh); + char szFilename[8 + 1 + 32 + 1]; + strncpyz(szFilename, filename, sizeof(szFilename)); + const char *filepath = base::Format::ToString("%s/%s", + completedir_.c_str(), szFilename); + + // This will create a zero length file. Perhaps in the future stats can + // be stored in this file, and this will become StatManager instead. + + FILE *pf = fopen(filepath, "w"); + if (pf != NULL) { + fclose(pf); + map_.insert(CompleteMap::value_type(key, mh)); + } +} + +dword CompleteManager::BadHashString(const char *s) { + // This string hash is here for a few reasons: + // 1. WI v1.5 CompleteManager uses this hash algorithm, and has + // committed its values to storage (oops) + // 2. This used to be the default string hash algorithm. It is no longer, + // because it is broken. For example, s_05.lvl hashes to the same value + // as s_10.lvl (as well as 6 to 11, 7 to 12, 8 to 13, and 9 to 14). + // 3. The easiest thing to do is to continue using it, and use a multimap + // for lookup, instead of a map (which is now the case). + dword h = 0; + for (; *s != 0; s++) { + h = 5 * h + *s; + } + return h; +} + +} // namespace wi diff --git a/game/completemanager.h b/game/completemanager.h new file mode 100644 index 0000000..3980ac2 --- /dev/null +++ b/game/completemanager.h @@ -0,0 +1,38 @@ +#ifndef __COMPETEMANAGER_H__ +#define __COMPETEMANAGER_H__ + +#include "game/ht.h" +#include +#include + +namespace wi { + +struct MissionHash { + byte hash[16]; +}; + +class CompleteManager { +public: + CompleteManager(const char *completedir) : completedir_(completedir) {} + ~CompleteManager() {} + + void Init(); + bool IsComplete(const MissionIdentifier *pmiid); + void MarkComplete(const MissionIdentifier *pmiid); + +private: + dword BadHashString(const char *s); + bool ParseFilename(const char *filename, dword *pkey, byte *hash); + const char *GetFilename(dword key, MissionHash *pmh); + bool GetMissionHash(const MissionIdentifier *pmiid, MissionHash *pmh); + dword GetKey(const MissionIdentifier *pmiid); + + typedef std::multimap CompleteMap; + CompleteMap map_; + std::string completedir_; +}; +extern CompleteManager *gpcptm; + +} // namespace wi + +#endif // __COMPETEMANAGER_H__ diff --git a/game/compression.cpp b/game/compression.cpp new file mode 100644 index 0000000..72a38c8 --- /dev/null +++ b/game/compression.cpp @@ -0,0 +1,24 @@ +#include "ht.h" +#include "mpshared/decompress.h" + +namespace wi { + +void DecompressToCache(byte *pbCompressed, CacheHandle hc, CompressionHeader *pcoh) +{ + // Writing to the cache requires use of gcam.Write() + + byte *pbCache = (byte *)gcam.GetPtr(hc); + byte *pbCompressedT = pbCompressed; + word cbDecompressed = 0; + word cb; + while ((cb = DecompressChunk(&pbCompressedT, gpbScratch, pbCache + cbDecompressed, gcbScratch / 2)) != 0) { + gcam.Write(hc, cbDecompressed, gpbScratch, cb); + cbDecompressed += cb; + if (cbDecompressed == BigWord(pcoh->cbUncompressed)) + break; + Assert(cbDecompressed <= BigWord(pcoh->cbUncompressed)); + Assert(pbCompressedT <= pbCompressed + BigWord(pcoh->cbCompressed)); + } +} + +} // namespace wi diff --git a/game/creategameform.cpp b/game/creategameform.cpp new file mode 100644 index 0000000..c48b9f5 --- /dev/null +++ b/game/creategameform.cpp @@ -0,0 +1,459 @@ +#include "game/ht.h" +#include "game/creategameform.h" +#include "game/completemanager.h" +#include "game/httppackmanager.h" +#include "game/httppackinfomanager.h" +#include "yajl/wrapper/jsontypes.h" + +namespace wi { + +const char *GetString(const json::JsonMap *map, const char *key); + +CreateGameForm::CreateGameForm(LoginHandler& handler, const PackId *ppackidFind, + Chatter& chatter, GameParams *prams) : handler_(handler), + chatter_(chatter), m_prams(prams), m_pml(NULL), + m_mt(kmtMultiplayerChallenge) { + memset(&m_miidFind, 0, sizeof(m_miidFind)); + if (ppackidFind != NULL) { + m_miidFind.packid = *ppackidFind; + } + chatter_.SetChatTitle("Room Chat"); + chatter_.SignalOnBlink.connect(this, &CreateGameForm::OnChatBlink); +} + +CreateGameForm::~CreateGameForm() { + delete m_pml; + chatter_.SignalOnBlink.disconnect(this); + chatter_.HideChat(); +} + +bool CreateGameForm::DoForm(LoginHandler& handler, const PackId *ppackidFind, + Chatter& chatter, GameParams *params) { + CreateGameForm *pfrm = (CreateGameForm *)gpmfrmm->LoadForm(gpiniForms, + kidfCreateGameWide, new CreateGameForm(handler, ppackidFind, + chatter, params)); + if (pfrm == NULL) { + return false; + } + int idc = kidcCancel; + pfrm->DoModal(&idc); + delete pfrm; + return idc == kidcOk; +} + +bool CreateGameForm::DoModal(int *pnResult) { + // Format the lists. + + int aidcList[] = { kidcChallengeList, kidcAddOnList }; + for (int i = 0; i < ARRAYSIZE(aidcList); i++) { + ListControl *plstc = (ListControl *)GetControlPtr(aidcList[i]); + m_aplstc[i] = plstc; + Rect rc; + plstc->GetRect(&rc); + Font *pfnt = gapfnt[kifntShadow]; + int cxComplete = pfnt->GetTextExtent("Complete"); + int xTitle = rc.Width() / 2 - cxComplete * 3 / 2; + plstc->SetTabStops(xTitle); + plstc->SetTabFlags(kfLstTabEllipsis); + plstc->SetFlags(plstc->GetFlags() | kfLstcKeepInteriorPositioning); + plstc->Clear(); + } + + if (m_pml == NULL) { + m_pml = CreateMissionList(NULL, kmltMultiplayer); + } + if (m_pml == NULL) { + return false; + } + + // If asked to find a certain mission, find it first to + // see what type it is, and switch the radio button bar to + // that type. + + int iPack = -1; + int iMission = -1; + int cLevels = m_pml->GetCount(); + for (int nLevel = 0; nLevel < cLevels; nLevel++) { + MissionIdentifier miid; + m_pml->GetMissionIdentifier(nLevel, &miid); + if (memcmp(&miid.packid, &m_miidFind.packid, + sizeof(miid.packid)) == 0) { + if (iPack == -1) { + iPack = nLevel; + } + if (strcmp(miid.szLvlFilename, + m_miidFind.szLvlFilename) == 0) { + iMission = nLevel; + break; + } + } + } + if (iMission == -1) { + iMission = iPack; + } + int iMissionSelect = iMission; + + // Init the lists + + MissionType mt = InitLists(iMissionSelect); + SwitchToMissionType(mt); + + // Game Speed + + m_tGameSpeed = gtGameSpeed; + if (m_tGameSpeed < 4) + m_tGameSpeed = 4; + SliderControl *psldr = (SliderControl *)GetControlPtr(kidcGameSpeed); + psldr->SetRange(2, ARRAYSIZE(gatGameSpeeds) - 1 - 3); // bring the extremes in a bit for multiplayer + psldr->SetValue(8); + for (int i = 0; i < ARRAYSIZE(gatGameSpeeds); i++) { + if (gatGameSpeeds[i] == m_tGameSpeed) { + psldr->SetValue(i); + break; + } + } + + // Hide this label. If we're finding an Add-On mission, then + // there are add-on missions and this label isn't meant to be visible. + + GetControlPtr(kidcAddOnMessage)->Show(false); + + UpdateLabels(); + + gptra->SetCallback(this); + bool fSuccess = ShellForm::DoModal(pnResult); + gptra->SetCallback(NULL); + delete m_pml; + m_pml = NULL; + return fSuccess; +} + +void CreateGameForm::OnChatBlink(bool on) { + GetControlPtr(kidcChat)->Show(on); +} + +void CreateGameForm::OnConnectionClose() { + Event evt; + memset(&evt, 0, sizeof(evt)); + evt.idf = m_idf; + evt.eType = connectionCloseEvent; + gevm.PostEvent(&evt); +} + +void CreateGameForm::OnShowMessage(const char *message) { + message_ = message; + Event evt; + memset(&evt, 0, sizeof(evt)); + evt.idf = m_idf; + evt.eType = showMessageEvent; + gevm.PostEvent(&evt); +} + +bool CreateGameForm::OnFilterEvent(Event *pevt) { + if (pevt->eType == connectionCloseEvent) { + chatter_.HideChat(); + HtMessageBox(kfMbWhiteBorder, "Comm Problem", "The server has closed your connection."); + EndForm(kidcCancel); + return true; + } + + if (pevt->eType == showMessageEvent) { + chatter_.HideChat(); + HtMessageBox(kfMbWhiteBorder, "Server Message", message_.c_str()); + message_ = ""; + return true; + } + return false; +} + +int CreateGameForm::IndexFromMissionType(MissionType mt) { + switch (mt) { + case kmtMultiplayerChallenge: + return 0; + case kmtMultiplayerAddOn: + return 1; + default: + return -1; + } +} + +MissionType CreateGameForm::MissionTypeFromIndex(int i) { + switch (i) { + case 0: + return kmtMultiplayerChallenge; + case 1: + return kmtMultiplayerAddOn; + default: + return kmtUnknown; + } +} + +void CreateGameForm::SwitchToMissionType(MissionType mt) { + m_mt = mt; + RadioButtonBarControl *prbbc = + (RadioButtonBarControl *)GetControlPtr(kidcCategories); + prbbc->SetSelectionIndex(IndexFromMissionType(mt)); + for (int i = 0; i < ARRAYSIZE(m_aplstc); i++) { + bool fShow = false; + if (i == IndexFromMissionType(mt)) { + fShow = true; + } + m_aplstc[i]->Show(fShow); + } + UpdateLabels(); +} + +MissionType CreateGameForm::InitLists(int iMissionSelect) { + // Fill in the lists, and along the way keep track of useful selection + // indexes. + + int ailiFirstIncomplete[kmtUnknown + 1]; + memset(ailiFirstIncomplete, 0xff, sizeof(ailiFirstIncomplete)); + int iliSelectSpecial = -1; + MissionType mtSelectSpecial = kmtUnknown; + + int cMissions = m_pml->GetCount(); + for (int i = 0; i < cMissions; i++) { + MissionDescription md; + if (!m_pml->GetMissionDescription(i, &md)) { + continue; + } + if (md.mt != kmtMultiplayerChallenge && + md.mt != kmtMultiplayerAddOn) { + continue; + } + + // Add the item. + + const char *pszT; + if (md.cPlayersMin == md.cPlayersMax) { + pszT = base::Format::ToString("%s (%d players)", + md.szLvlTitle, md.cPlayersMin); + } else { + pszT = base::Format::ToString("%s (%d-%d players)", + md.szLvlTitle, md.cPlayersMin, md.cPlayersMax); + } + ListControl *plstc = m_aplstc[IndexFromMissionType(md.mt)]; + plstc->Add(pszT, (void *)i); + + // Track the first incomplete, for later selection + + if (ailiFirstIncomplete[md.mt] == -1) { + MissionIdentifier miid; + m_pml->GetMissionIdentifier(i, &miid); + if (!gpcptm->IsComplete(&miid)) { + ailiFirstIncomplete[md.mt] = plstc->GetCount() - 1; + } + } + + // This is passed in when the form needs to select a certain + // mission when it first shows. + + if (i == iMissionSelect) { + iliSelectSpecial = plstc->GetCount() - 1; + mtSelectSpecial = md.mt; + } + } + + // The initially selected missions are the first incomplete missions + // for each mission type. + + MissionType mtSelect = kmtMultiplayerChallenge; + for (int i = 0; i < ARRAYSIZE(m_aplstc); i++) { + ListControl *plstc = m_aplstc[i]; + + // Is this the list that is awarded the special selection? + + if (i == IndexFromMissionType(mtSelectSpecial)) { + plstc->Select(iliSelectSpecial, true, true); + mtSelect = mtSelectSpecial; + continue; + } + + int iliSelect = ailiFirstIncomplete[MissionTypeFromIndex(i)]; + if (iliSelect < 0) { + iliSelect = 0; + } + plstc->Select(iliSelect, true, true); + } + return mtSelect; +} + +void CreateGameForm::OnControlSelected(word idc) { + switch (idc) { + case kidcPasswordPanel: + DoInputPanelForm(this, kidcPasswordLabel, kidcPassword); + break; + + case kidcGameSpeed: + { + SliderControl *psldr = (SliderControl *)GetControlPtr( + kidcGameSpeed); + m_tGameSpeed = gatGameSpeeds[psldr->GetValue()]; + UpdateLabels(); + } + break; + + case kidcChat: + chatter_.ShowChat(); + break; + + case kidcCancel: + EndForm(idc); + break; + + case kidcOk: + // Fill in GameParams + + ListControl *plstc = m_aplstc[IndexFromMissionType(m_mt)]; + int nLevel = (int)plstc->GetSelectedItemData(); + + MissionIdentifier miid; + if (!m_pml->GetMissionIdentifier(nLevel, &miid)) { + HtMessageBox(kfMbWhiteBorder, "Error!", + "First you must select a map to play on."); + return; + } + + // Mount the pdb + if (!gppackm->Mount(gpakr, &miid.packid)) { + HtMessageBox(kfMbWhiteBorder, "Error!", + "Cannot load mission pack."); + return; + } + + // Remember for later selecting from the list + + m_miidFind = miid; + + // Read the level info + + Level *plvl = new Level(); + if (plvl != NULL) { + if (plvl->LoadLevelInfo(miid.szLvlFilename)) { + m_prams->packid = miid.packid; + m_prams->dwVersionSimulation = knVersionSimulation; + m_prams->tGameSpeed = m_tGameSpeed; + strncpyz(m_prams->szLvlFilename, miid.szLvlFilename, + sizeof(m_prams->szLvlFilename)); + } + delete plvl; + } else { + HtMessageBox(kfMbWhiteBorder, "Error", "Out of memory!"); + idc = kidcCancel; + } + + gppackm->Unmount(gpakr, &miid.packid); + + // Keep this game speed around for next time + + gtGameSpeed = m_tGameSpeed; + ggame.SavePreferences(); + + EndForm(idc); + break; + } +} + +void CreateGameForm::OnControlNotify(word idc, int nNotify) { + if (idc == kidcCategories) { + RadioButtonBarControl *prbbc = + (RadioButtonBarControl *)GetControlPtr(kidcCategories); + int iButtonSelected = prbbc->GetSelectionIndex(); + if (iButtonSelected < 0) { + iButtonSelected = 0; + } + MissionType mtNew = MissionTypeFromIndex(iButtonSelected); + if (mtNew == m_mt) { + return; + } + SwitchToMissionType(mtNew); + + // If in Add-On category, and there is nothing there, show this + // label, otherwise hide it + + bool fShowLabel = false; + ListControl *plstc = + m_aplstc[IndexFromMissionType(kmtMultiplayerAddOn)]; + if (m_mt == kmtMultiplayerAddOn) { + if (plstc->GetCount() == 0) { + fShowLabel = true; + } + } + + LabelControl *plbl = + (LabelControl *)GetControlPtr(kidcAddOnMessage); + if (fShowLabel) { + plbl->Show(true); + if (m_mt == kmtMultiplayerAddOn) { + plstc->Show(false); + } + } else { + plbl->Show(false); + if (m_mt == kmtMultiplayerAddOn) { + plstc->Show(true); + } + } + } + + if (idc == kidcChallengeList || idc == kidcAddOnList) { + UpdateLabels(); + } + + // Handle button hiding + + bool fShow = true; + ListControl *plstc = m_aplstc[IndexFromMissionType(m_mt)]; + if (plstc->GetSelectedItemIndex() == -1) { + fShow = false; + } + GetControlPtr(kidcOk)->Show(fShow); +} + +void CreateGameForm::UpdateLabels() { + // Update Game Speed label + + char szT[80]; + GetSpeedMultiplierString(szT, m_tGameSpeed); + LabelControl *plbl = (LabelControl *)GetControlPtr(kidcGameSpeedLabel); + plbl->SetText(szT); + + // Update Mission Pack Info label + + plbl = (LabelControl *)GetControlPtr(kidcMissionPackInfo); + ListControl *plstc = m_aplstc[IndexFromMissionType(m_mt)]; + if (plstc->GetSelectedItemIndex() == -1) { + plbl->SetText(""); + return; + } + int i = (int)plstc->GetSelectedItemData(); + MissionIdentifier miid; + if (!m_pml->GetMissionIdentifier(i, &miid)) { + plbl->SetText(""); + return; + } + json::JsonMap *map = gppim->GetInfoMap(&miid.packid); + if (map == NULL) { + MissionDescription md; + if (!m_pml->GetMissionDescription(i, &md)) { + plbl->SetText(""); + return; + } + const char *s; + if (miid.packid.id == PACKID_MAIN) { + s = base::Format::ToString("%s by Spiffcode, Inc.", + md.szPackName); + } else { + s = md.szPackName; + } + plbl->SetText((char *)s); + return; + } + const char *szAuthor = GetString(map, "a"); + const char *szTitle = GetString(map, "t"); + const char *s = base::Format::ToString("%s by %s", szTitle, szAuthor); + plbl->SetText((char *)s); + delete map; +} + +} // namespace wi diff --git a/game/creategameform.h b/game/creategameform.h new file mode 100644 index 0000000..7dfeaae --- /dev/null +++ b/game/creategameform.h @@ -0,0 +1,52 @@ +#ifndef __CREATEGAMEFORM_H__ +#define __CREATEGAMEFORM_H__ + +#include "game/ht.h" +#include "game/chatter.h" +#include "game/loginhandler.h" +#include "base/sigslot.h" +#include + +namespace wi { + +class CreateGameForm : public ShellForm, ITransportCallback, + public base::has_slots<> { +public: + CreateGameForm(LoginHandler& handler, const PackId *ppackidFind, + Chatter& chatter, GameParams *prams); + virtual ~CreateGameForm(); + + static bool DoForm(LoginHandler& handler, const PackId *ppackidFind, + Chatter& chatter, GameParams *params); + virtual bool DoModal(int *pnResult = NULL); + virtual bool OnFilterEvent(Event *pevt); + +private: + void OnChatBlink(bool on); + int IndexFromMissionType(MissionType mt); + MissionType MissionTypeFromIndex(int i); + void SwitchToMissionType(MissionType mt); + MissionType InitLists(int iMissionSelect); + void OnControlSelected(word idc); + void OnControlNotify(word idc, int nNotify); + void UpdateLabels(); + + // TransportCallback + void OnStatusUpdate(char *pszStatus) { } + void OnConnectionClose(); + void OnShowMessage(const char *message); + + LoginHandler& handler_; + Chatter& chatter_; + MissionType m_mt; + GameParams *m_prams; + MissionIdentifier m_miidFind; + long m_tGameSpeed; + MissionList *m_pml; + ListControl *m_aplstc[2]; + std::string message_; +}; + +} // namespace wi + +#endif // __CREATEGAMEFORM_H__ diff --git a/game/createroomform.cpp b/game/createroomform.cpp new file mode 100644 index 0000000..819e392 --- /dev/null +++ b/game/createroomform.cpp @@ -0,0 +1,117 @@ +#include "game/createroomform.h" + +namespace wi { + +CreateRoomForm::CreateRoomForm() : ask_(knCreateRoomAskNone) { +} + +CreateRoomForm::~CreateRoomForm() { +} + +bool CreateRoomForm::DoForm(char *roomname, int cbRoomname, char *password, int cbPassword) { + CreateRoomForm *pfrm = (CreateRoomForm *)gpmfrmm->LoadForm(gpiniForms, + kidfCreateRoom, new CreateRoomForm()); + if (pfrm == NULL) { + return false; + } + if (!pfrm->DoModal()) { + delete pfrm; + return false; + } + pfrm->GetRoomname(roomname, cbRoomname); + pfrm->GetPassword(password, cbPassword); + delete pfrm; + return true; +} + +bool CreateRoomForm::DoModal(int *pnResult, Sfx sfxShow, Sfx sfxHide) { + HideShowPasswordFields(); + return ShellForm::DoModal(pnResult, sfxShow, sfxHide); +} + +void CreateRoomForm::HideShowPasswordFields() { + CheckBoxControl *pcbc = (CheckBoxControl *)GetControlPtr(kidcPrivate); + Control *pctl = GetControlPtr(kidcPasswordLabel); + pctl->Show(pcbc->IsChecked()); + pctl = GetControlPtr(kidcPassword); + pctl->Show(pcbc->IsChecked()); + pctl = GetControlPtr(kidcPasswordPanel); + pctl->Show(pcbc->IsChecked()); +} + +void CreateRoomForm::OnControlSelected(word idc) { + switch (idc) { + case kidcPrivate: + HideShowPasswordFields(); + break; + + case kidcRoomNamePanel: + case kidcRoomName: + { + ask_ = knCreateRoomAskRoomname; + char roomname[kcbRoomname]; + GetRoomname(roomname, sizeof(roomname)); + HostInitiateAsk("Enter Room Name", kcbRoomname - 1, roomname); + } + break; + + case kidcPasswordPanel: + case kidcPassword: + { + ask_ = knCreateRoomAskPassword; + char password[kcbPassword]; + GetPassword(password, sizeof(password)); + HostInitiateAsk("Enter Password", kcbPassword - 1, password, + knKeyboardAskDefault, true); + } + break; + + default: + ShellForm::OnControlSelected(idc); + break; + } +} + +bool CreateRoomForm::OnFilterEvent(Event *pevt) { + if (pevt->eType != askStringEvent) { + return false; + } + + char s[512]; + HostGetAskString(s, sizeof(s)); + + switch (ask_) { + case knCreateRoomAskRoomname: + { + roomname_ = s; + EditControl *pedc = (EditControl *)GetControlPtr(kidcRoomName); + pedc->SetText(s); + } + break; + + case knCreateRoomAskPassword: + { + password_ = s; + for (int i = 0; i < sizeof(s); i++) { + if (s[i] == 0) { + break; + } + s[i] = '*'; + } + EditControl *pedc = (EditControl *)GetControlPtr(kidcPassword); + pedc->SetText(s); + } + break; + } + return true; +} + +void CreateRoomForm::GetRoomname(char *roomname, int cb) { + strncpyz(roomname, roomname_.c_str(), cb); +} + +void CreateRoomForm::GetPassword(char *password, int cb) { + strncpyz(password, password_.c_str(), cb); +} + +} // namespace wi diff --git a/game/createroomform.h b/game/createroomform.h new file mode 100644 index 0000000..baaed83 --- /dev/null +++ b/game/createroomform.h @@ -0,0 +1,38 @@ +#ifndef __CREATEROOMFORM_H__ +#define __CREATEROOMFORM_H__ + +#include "game/ht.h" +#include + +namespace wi { + +const int knCreateRoomAskNone = 0; +const int knCreateRoomAskRoomname = 1; +const int knCreateRoomAskPassword = 2; + +class CreateRoomForm : public ShellForm { +public: + CreateRoomForm(); + ~CreateRoomForm(); + + static bool DoForm(char *roomname, int cbRoomname, char *password, + int cbPassword); + void GetRoomname(char *roomname, int cb); + void GetPassword(char *password, int cb); + + // Form callbacks + virtual bool DoModal(int *pnResult = NULL, Sfx sfxShow = ksfxGuiFormShow, + Sfx sfxHide = ksfxGuiFormHide); + virtual void OnControlSelected(word idc); + virtual bool OnFilterEvent(Event *pevt); + +private: + void HideShowPasswordFields(); + + int ask_; + std::string roomname_; + std::string password_; +}; + +} // namespace wi +#endif // __CREATEROOMFORM_H__ diff --git a/game/dist.plist b/game/dist.plist new file mode 100644 index 0000000..ce373e1 --- /dev/null +++ b/game/dist.plist @@ -0,0 +1,8 @@ + + + + + get-task-allow + + + diff --git a/game/dlmissionpack.cpp b/game/dlmissionpack.cpp new file mode 100644 index 0000000..2d90d47 --- /dev/null +++ b/game/dlmissionpack.cpp @@ -0,0 +1,704 @@ +#include "game/ht.h" +#include "game/httpindexloader.h" +#include "game/httppackinfomanager.h" +#include "game/httppackmanager.h" +#include "base/format.h" +#include "base/thread.h" +#include "yajl/wrapper/jsontypes.h" +#include + +namespace wi { + +#define CTX_INDEX ((void *)0) +#define CTX_INFO ((void *)1) + +#define PACKID_CUSTOMURL 0xFEFEFEFE + +class DownloadMissionPackForm : public ShellForm, ProgressCallback { +public: + DownloadMissionPackForm(bool fMultiplayer) : fMultiplayer_(fMultiplayer), + idxl_(gphttp, gppackm), fWantsPlay_(false) {} + ~DownloadMissionPackForm(); + + bool WantsPlay(PackId *ppackid) { + if (fWantsPlay_) { + *ppackid = packidPlay_; + return true; + } + return false; + } + + // Form overrides + virtual bool Init(FormMgr *pfrmm, IniReader *pini, word idf); + virtual void OnControlSelected(word idc); + virtual void OnControlNotify(word idc, int nNotify); + virtual bool OnPenEvent(Event *pevt); + virtual bool OnFilterEvent(Event *pevt); + +private: + void HandleCustomURL(); + void Sort(word id); + void PositionColumns(); + void HideShow(); + void ShowStatus(void *ctx, const char *psz); + void ShowIndex(const PackId *ppackidSelect = NULL); + bool ShowInfo(bool fRequest); + const char *GetItemString(const IndexEntry *pentry); + const char *GetString(const json::JsonMap *map, const char *key, + const char *missing); + void UpdateLabelRects(LabelControl *plbl); + + // ProgressCallback + virtual void OnBegin(void *ctx, int cbLength); + virtual void OnProgress(void *ctx, int cbTotal, int cbLength); + virtual void OnFinished(void *ctx); + virtual void OnError(void *ctx, const char *pszError); + + HttpIndexLoader idxl_; + int x0_, x1_, x2_, x3_; + word idSortIndicator_; + HttpIndexLoader::SortType sort_; + Rect rcStatus_; + Rect rcTitle_; + Rect rcNumPlayers_; + Rect rcNumMissions_; + bool fWantsPlay_; + PackId packidPlay_; + bool fMultiplayer_; +}; + +bool ShowDownloadMissionPackForm(PackId *ppackid) { + bool fMultiplayer = false; +#ifdef MULTIPLAYER + // First, choose single, or multiplayer. This UI added due to feedback. + ShellForm *pfrmT = (ShellForm *)gpmfrmm->LoadForm(gpiniForms, + kidfAddOnSingleMulti, new ShellForm()); + if (pfrmT == NULL) { + return false; + } + int idc; + pfrmT->DoModal(&idc); + delete pfrmT; + if (idc == kidcCancel || gevm.IsAppStopping()) { + return false; + } + if (idc == kidcPlayMultiPlayer) { + fMultiplayer = true; + } +#endif + + // Bring up the download form + DownloadMissionPackForm *pfrm; + pfrm = (DownloadMissionPackForm *)gpmfrmm->LoadForm(gpiniForms, + kidfDownloadMissionPackWide, new DownloadMissionPackForm( + fMultiplayer)); + if (pfrm != NULL) { + pfrm->DoModal(); + if (pfrm->WantsPlay(ppackid)) { + delete pfrm; + return true; + } + delete pfrm; + } + return false; +} + +DownloadMissionPackForm::~DownloadMissionPackForm() { + gppim->Reset(); +} + +bool DownloadMissionPackForm::Init(FormMgr *pfrmm, IniReader *pini, word idf) { + if (!ShellForm::Init(pfrmm, pini, idf)) { + return false; + } + + // Start downloading the mission pack index + + if (!idxl_.Start(CTX_INDEX, this)) { + return false; + } + + // No operation is available until a mission is selected + + GetControlPtr(kidcOk)->Show(false); + GetControlPtr(kidcDiscuss)->Show(false); + + // Show status + + ShowStatus(CTX_INDEX, "Contacting Server..."); + + // Position Column labels + + PositionColumns(); + + // Set info label to use ellipsis + + LabelControl *plbl = (LabelControl *)GetControlPtr(kidcMissionPackInfo); + plbl->SetFlags(plbl->GetFlags() | kfLblEllipsis); + + // No sorting yet + + idSortIndicator_ = 0; + sort_ = HttpIndexLoader::SORT_UNSORTED; + + return true; +} + +void DownloadMissionPackForm::PositionColumns() { + // Calc tab stops in the list + // S Title # Players Downloads + // S is + for installed, or ! for upgrade + // Title is the title. Make this as wide as possible + // # Players is as close to Downloads as is reasonable + // # Missions is right aligned + + ListControl *plstc = (ListControl *)GetControlPtr(kidcMissionPackList); + Font *pfnt = plstc->GetFont(); + LabelControl *plbl = (LabelControl *)GetControlPtr(kidcStatus); +#if 1 + plbl->SetText(""); + int cxPlus = pfnt->GetTextExtent("+"); +#else + int cxPlus = pfnt->GetTextExtent(plbl->GetText()); +#endif + plbl = (LabelControl *)GetControlPtr(kidcTitle); + int cxTitle = pfnt->GetTextExtent(plbl->GetText()); + plbl = (LabelControl *)GetControlPtr(kidcNumPlayers); + int cxNumPlayers = pfnt->GetTextExtent(plbl->GetText()); + int cxAsterisk = pfnt->GetTextExtent("*"); + plbl = (LabelControl *)GetControlPtr(kidcNumMissions); + int cxNumMissions = pfnt->GetTextExtent(plbl->GetText()); + + Rect rcList; + plstc->GetRect(&rcList); + + int xStatus = rcList.left + 2; + int xTitle = rcList.left + (rcList.Width() / 4 - cxTitle) / 2; + int xNumMissions = m_rc.Width() - cxAsterisk - cxNumMissions; + int xNumPlayers = xNumMissions - 10 - cxNumPlayers; + + // Calc the top of the list (past the arrow) for better column + // label hittesting + Size sizArrow; + ListControl::s_ptbmScrollUpUp->GetSize(&sizArrow); + int yListTop = rcList.top + sizArrow.cy; + + // Set the label positions + // Enable the labels for hittesting + + Rect rc; + plbl = (LabelControl *)GetControlPtr(kidcStatus); + plbl->GetRect(&rc); + rc.left = xStatus; + rc.right = xTitle; + rc.bottom += (yListTop - rc.bottom) / 2; + plbl->SetRect(&rc); + plbl->SetFlags(plbl->GetFlags() | kfLblHitTest); + rcStatus_ = rc; + + plbl = (LabelControl *)GetControlPtr(kidcTitle); + plbl->GetRect(&rc); + rc.Offset(xTitle - rc.left, 0); + rc.right += rc.Width(); + rc.bottom += (yListTop - rc.bottom) / 2; + plbl->SetRect(&rc); + plbl->SetFlags(plbl->GetFlags() | kfLblHitTest); + rcTitle_ = rc; + + plbl = (LabelControl *)GetControlPtr(kidcNumPlayers); + plbl->GetRect(&rc); + rc.Offset(xNumPlayers - rc.left, 0); + rc.bottom += (yListTop - rc.bottom) / 2; + plbl->SetRect(&rc); + plbl->SetFlags(plbl->GetFlags() | kfLblHitTest); + rcNumPlayers_ = rc; + + plbl = (LabelControl *)GetControlPtr(kidcNumMissions); + plbl->GetRect(&rc); + rc.Offset(xNumMissions - rc.left, 0); + rc.bottom += (yListTop - rc.bottom) / 2; + plbl->SetRect(&rc); + plbl->SetFlags(plbl->GetFlags() | kfLblHitTest); + rcNumMissions_ = rc; + + // Remember the tab settings for later use + + x0_ = 0; + x1_ = cxPlus + 4; + x2_ = xNumPlayers + cxNumPlayers / 2; + x3_ = xNumMissions + cxNumMissions / 2; +} + +bool DownloadMissionPackForm::OnPenEvent(Event *pevt) { + if (pevt->eType != penDownEvent) { + return ShellForm::OnPenEvent(pevt); + } + + Control *pctlCaptureBefore = GetControlCapture(); + bool f = ShellForm::OnPenEvent(pevt); + Control *pctlCaptureAfter = GetControlCapture(); + + if (pctlCaptureBefore == NULL && pctlCaptureAfter != NULL) { + Sort(pctlCaptureAfter->GetId()); + } + return f; +} + +void DownloadMissionPackForm::Sort(word id) { + word aidColumns[] = { + kidcStatus, kidcTitle, kidcNumPlayers, kidcNumMissions + }; + + int iCol; + for (iCol = 0; iCol < ARRAYSIZE(aidColumns); iCol++) { + if (id == aidColumns[iCol]) { + break; + } + } + if (iCol >= ARRAYSIZE(aidColumns)) { + return; + } + + HttpIndexLoader::SortType asortFlip[] = { + HttpIndexLoader::SORT_UNSORTED, + HttpIndexLoader::SORT_INSTALLEDDESCENDING, + HttpIndexLoader::SORT_INSTALLEDASCENDING, + HttpIndexLoader::SORT_TITLEDESCENDING, + HttpIndexLoader::SORT_TITLEASCENDING, + HttpIndexLoader::SORT_PLAYERSDESCENDING, + HttpIndexLoader::SORT_PLAYERSASCENDING, + HttpIndexLoader::SORT_MISSIONSDESCENDING, + HttpIndexLoader::SORT_MISSIONSASCENDING + }; + + // If this is already the sort column, flip the sort + if (id == idSortIndicator_) { + sort_ = asortFlip[sort_]; + idxl_.Sort(sort_); + ShowIndex(); + return; + } + + // Take sort indicator off + char szT[32]; + if (idSortIndicator_ != 0) { + LabelControl *plbl = (LabelControl *)GetControlPtr(idSortIndicator_); + strncpyz(szT, plbl->GetText(), sizeof(szT)); + int cch = strlen(szT); + if (szT[cch - 1] == '*') { + szT[cch - 1] = 0; + plbl->SetText(szT); + } + UpdateLabelRects(plbl); + } + + // Put on new column + idSortIndicator_ = id; + LabelControl *plbl = (LabelControl *)GetControlPtr(idSortIndicator_); + strncpyz(szT, plbl->GetText(), sizeof(szT)); + int cch = strlen(szT); + if (szT[cch - 1] != '*') { + szT[cch] = '*'; + szT[cch + 1] = 0; + plbl->SetText(szT); + UpdateLabelRects(plbl); + } + + // Force this before the sort occurs, for immediate visual feedback + + gpmfrmm->DrawFrame(false); + + // Perform default sort for this column + HttpIndexLoader::SortType asortDefault[] = { + HttpIndexLoader::SORT_INSTALLEDDESCENDING, + HttpIndexLoader::SORT_TITLEASCENDING, + HttpIndexLoader::SORT_PLAYERSASCENDING, + HttpIndexLoader::SORT_MISSIONSDESCENDING + }; + + sort_ = asortDefault[iCol]; + idxl_.Sort(sort_); + ShowIndex(); +} + +void DownloadMissionPackForm::UpdateLabelRects(LabelControl *plbl) { + switch (plbl->GetId()) { + case kidcStatus: + plbl->SetRect(&rcStatus_); + break; + + case kidcTitle: + plbl->SetRect(&rcTitle_); + break; + + case kidcNumPlayers: + plbl->SetRect(&rcNumPlayers_); + break; + + case kidcNumMissions: + plbl->SetRect(&rcNumMissions_); + break; + } +} + +void DownloadMissionPackForm::ShowStatus(void *ctx, const char *psz) { + if (ctx == CTX_INDEX) { + ListControl *plstc = (ListControl *)GetControlPtr(kidcMissionPackList); + plstc->Clear(); + plstc->SetTabStops(0); + plstc->SetTabFlags(0); + plstc->Add(psz, NULL); + } + + if (ctx == CTX_INFO) { + LabelControl *plbl = + (LabelControl *)GetControlPtr(kidcMissionPackInfo); + plbl->SetText((char *)psz); + } + + // Force the frame draw, since this gets called while the main loop + // is "asleep". + gpmfrmm->DrawFrame(false); +} + +void DownloadMissionPackForm::OnBegin(void *ctx, int cbLength) { + OnProgress(ctx, 0, cbLength); +} + +void DownloadMissionPackForm::OnProgress(void *ctx, int cbTotal, int cbLength) { + ShowStatus(ctx, base::Format::ToString("Downloaded %d of %d bytes...", + cbTotal, cbLength)); +} + +void DownloadMissionPackForm::OnError(void *ctx, const char *pszError) { + // Don't show info errors since it's ok to not have info, and the error + // is confusing + if (ctx == CTX_INFO) { + ShowStatus(ctx, ""); + } else { + ShowStatus(ctx, base::Format::ToString("Download error: %s", + pszError)); + } +} + +void DownloadMissionPackForm::OnFinished(void *ctx) { + if (ctx == CTX_INDEX) { + ShowStatus(ctx, "Download Complete! Sorting results..."); + + // Merge with installed packs so they are all listed + + idxl_.MergeInstalled(gppackm); + + // Add the fake entry for custom URLs + + PackId packid; + memset(&packid, 0, sizeof(packid)); + packid.id = PACKID_CUSTOMURL; + idxl_.AddFakeEntry(&packid, " Custom URL...", 999, 999, 0); + + // Sort the single list + + Sort(kidcNumMissions); + } + + if (ctx == CTX_INFO) { + ShowInfo(false); + } + + // Wake up to process the paint + + base::Thread::current().Post(base::kidmNullEvent, NULL); +} + +const char *DownloadMissionPackForm::GetItemString(const IndexEntry *pentry) { + // Figure out installed / upgrade state + + char s; + switch (gppackm->IsInstalled(&pentry->packid)) { + default: + case 0: + // Not installed + s = ' '; + break; + + case 1: + // Installed, current version is up to date + s = '+'; + break; + + case 2: + // Installed, but needs upgrade + s = '!'; + break; + } + + char szPlayers[8]; + if (pentry->cPlayersMin == pentry->cPlayersMax) { + strncpyz(szPlayers, base::Format::ToString("%d", pentry->cPlayersMin), + sizeof(szPlayers)); + } else { + strncpyz(szPlayers, base::Format::ToString("%d-%d", + pentry->cPlayersMin, pentry->cPlayersMax), sizeof(szPlayers)); + } + + if (pentry->packid.id == PACKID_CUSTOMURL) { + return base::Format::ToString(" \t%s\t \t ", pentry->title.c_str()); + } else { + return base::Format::ToString("%c\t%s\t%s\t%d", + s, pentry->title.c_str(), szPlayers, pentry->cMissions); + } +} + +void DownloadMissionPackForm::ShowIndex(const PackId *ppackidSelect) { + // Add entries to the list + + ListControl *plstc = (ListControl *)GetControlPtr(kidcMissionPackList); + plstc->SetTabStops(x0_, x1_, x2_, x3_); + plstc->SetTabFlags(kfLstTabCenter, kfLstTabEllipsis, kfLstTabCenterOn, + kfLstTabCenterOn); + plstc->Clear(); + int iSelect = 0; + int cAdded = 0; + int c = idxl_.GetCount(); + for (int i = 0; i < c; i++) { + const IndexEntry *pentry = idxl_.GetEntry(i); + + // Show either single player packs, or multiplayer packs. + // If a pack has both single and multiplayer missions, show + // these in both cases. + + if (pentry->packid.id != PACKID_CUSTOMURL) { + if (fMultiplayer_) { + if (pentry->cPlayersMax == 1) { + continue; + } + } else { + if (pentry->cPlayersMin != 1) { + continue; + } + } + } + + // Remember which entry to select + + if (iSelect == 0 && ppackidSelect != NULL) { + if (memcmp(ppackidSelect, &pentry->packid, + sizeof(*ppackidSelect)) == 0) { + iSelect = cAdded; + } + } + + plstc->Add(GetItemString(pentry), (void *)pentry); + cAdded++; + } + + if (cAdded > 0) { + plstc->Select(iSelect, true, true); + } +} + +bool DownloadMissionPackForm::ShowInfo(bool fRequest) { + ListControl *plstc = (ListControl *)GetControlPtr(kidcMissionPackList); + IndexEntry *entry = (IndexEntry *)plstc->GetSelectedItemData(); + if (entry == NULL) { + GetControlPtr(kidcDiscuss)->Show(false); + return false; + } + const json::JsonMap *map = gppim->GetInfoMap(&entry->packid); + if (map == NULL) { + if (fRequest) { + if (entry->packid.id == PACKID_CUSTOMURL) { + ShowStatus(CTX_INFO, "Press Download to download from a custom URL"); + } else { + gppim->Start(&entry->packid, CTX_INFO, this); + ShowStatus(CTX_INFO, "Loading Mission Pack info..."); + } + } + GetControlPtr(kidcDiscuss)->Show(false); + return false; + } + + const char *author = GetString(map, "a", "unknown"); + const char *desc = GetString(map, "d", "no description"); + const char *s = base::Format::ToString("By %s: %s", author, desc); + delete map; + + LabelControl *plbl = (LabelControl *)GetControlPtr(kidcMissionPackInfo); + plbl->SetText((char *)s); + GetControlPtr(kidcDiscuss)->Show(true); + return true; +} + +const char *DownloadMissionPackForm::GetString(const json::JsonMap *map, + const char *key, const char *missing) { + const json::JsonObject *obj = map->GetObject(key); + if (obj == NULL || obj->type() != json::JSONTYPE_STRING) { + return missing; + } + const json::JsonString *s = (json::JsonString *)obj; + + bool fWhitespace = true; + char ch; + const char *psz = s->GetString(); + while ((ch = *psz++) != 0) { + if (!isspace(ch)) { + fWhitespace = false; + break; + } + } + if (fWhitespace) { + return missing; + } + return s->GetString(); +} + +void DownloadMissionPackForm::OnControlNotify(word idc, int nNotify) { + if (idc == kidcMissionPackList && nNotify == knNotifySelectionChange) { + HideShow(); + } + Form::OnControlNotify(idc, nNotify); +} + +void DownloadMissionPackForm::HideShow() { + ListControl *plstc = (ListControl *)GetControlPtr(kidcMissionPackList); + LabelControl *plbl = (LabelControl *)GetControlPtr(kidcMissionPackInfo); + ButtonControl *pbtn = (ButtonControl *)GetControlPtr(kidcOk); + int selected = plstc->GetSelectedItemIndex(); + if (selected < 0) { + gppim->Reset(); + plbl->SetText(""); + pbtn->Show(false); + GetControlPtr(kidcDiscuss)->Show(false); + return; + } + + // Get the pack info for this pack + + IndexEntry *entry = (IndexEntry *)plstc->GetSelectedItemData(); + char *psz = NULL; + switch (gppackm->IsInstalled(&entry->packid)) { + case 0: + psz = "DOWNLOAD"; + break; + + case 1: + psz = "REMOVE"; + break; + + case 2: + psz = "UPGRADE"; + break; + } + if (psz != NULL) { + pbtn->SetText(psz); + } + + pbtn->Show(true); + ShowInfo(true); +} + +void DownloadMissionPackForm::OnControlSelected(word idc) { + if (idc == kidcCancel) { + EndForm(idc); + return; + } + + ListControl *plstc = (ListControl *)GetControlPtr(kidcMissionPackList); + IndexEntry *entry = (IndexEntry *)plstc->GetSelectedItemData(); + if (entry == NULL) { + return; + } + + switch (idc) { + case kidcOk: + switch (gppackm->IsInstalled(&entry->packid)) { + case 0: + // If this is a custom URL entry handle specially + if (entry->packid.id == PACKID_CUSTOMURL) { + HandleCustomURL(); + break; + } + // fall through + + case 2: + // Pack not install or needs upgrade. In either case, download + if (DownloadMissionPack(&entry->packid, NULL, true)) { + fWantsPlay_ = true; + packidPlay_ = entry->packid; + EndForm(kidcOk); + } + plstc->SetSelectedItemText(GetItemString(entry)); + HideShow(); + break; + + case 1: + // Pack is installed, so this action means remove + if (HtMessageBox(kidfMessageBoxQuery, + kfMbWhiteBorder | kfMbKeepTimersEnabled, + "Remove Mission Pack", "Are you sure?")) { + gppackm->Remove(&entry->packid); + if (idxl_.OnRemoved(&entry->packid)) { + ShowIndex(); + } else { + plstc->SetSelectedItemText(GetItemString(entry)); + } + HideShow(); + } + break; + } + break; + + case kidcDiscuss: + { + json::JsonMap *map = gppim->GetInfoMap(&entry->packid); + if (map != NULL) { + const json::JsonObject *obj = map->GetObject("di"); + if (obj != NULL && obj->type() == json::JSONTYPE_STRING) { + HostOpenUrl(((const json::JsonString *)obj)->GetString()); + } + delete map; + } + } + break; + } +} + +bool DownloadMissionPackForm::OnFilterEvent(Event *pevt) { + if (pevt->eType != askStringEvent) { + return false; + } + + // Get the URL and Save preferences so this url is remembered + HostGetAskString(gszAskURL, sizeof(gszAskURL)); + ggame.SavePreferences(); + + // Download this custom pack + PackId packid; + bool play = false; + if (!DownloadMissionPackFromURL(gszAskURL, &packid, &play)) { + // Some kind of error occured + return true; + } + + // User wants to play now? + if (play) { + // The user wants to play the mission + fWantsPlay_ = true; + packidPlay_ = packid; + EndForm(kidcOk); + return true; + } + + // Add this mission pack to the index, and re-fill the list control, + // and select it. + idxl_.AddIndexEntry(&packid); + idxl_.Sort(sort_); + ShowIndex(&packid); + return true; +} + +void DownloadMissionPackForm::HandleCustomURL() { + HostInitiateAsk("Custom URL", -1, gszAskURL, knKeyboardAskURL); +} + +} // namespace wi diff --git a/game/downloadbox.cpp b/game/downloadbox.cpp new file mode 100644 index 0000000..e5d0d7d --- /dev/null +++ b/game/downloadbox.cpp @@ -0,0 +1,257 @@ +#include "game/ht.h" +#include "game/httppackmanager.h" +#include "game/httppackinfomanager.h" +#include "game/progresscallback.h" +#include "base/messagequeue.h" +#include "base/format.h" +#include "base/thread.h" +#include "yajl/wrapper/jsontypes.h" + +namespace wi { + +class DownloadBox : public DialogForm, ProgressCallback { +public: + DownloadBox(const PackId *ppackid, const char *title, bool fPlayButton); + DownloadBox(const char *pszURL); + virtual ~DownloadBox(); + + virtual bool DoModal(int *pnResult = NULL, Sfx sfxShow = ksfxGuiFormShow, + Sfx sfxHide = ksfxGuiFormHide); + const PackId& packid() { return packid_; } + bool error() { return error_; } + +private: + void ShowStatus(const char *pszStatus); + + // ProgressCallback + virtual void OnBegin(void *ctx, int cbLength); + virtual void OnProgress(void *ctx, int cbTotal, int cbLength); + virtual void OnFinished(void *ctx); + virtual void OnError(void *ctx, const char *pszError); + + PackId packid_; + const char *pszTitle_; + int xOk_; + bool fPlayButton_; + const char *pszURL_; + bool error_; +}; + +bool DownloadMissionPack(const PackId *ppackid, const char *pszTitle, + bool fPlayButton) { + DownloadBox *pfrm = (DownloadBox *)gpmfrmm->LoadForm(gpiniForms, + kidfDownloadBox, new DownloadBox(ppackid, pszTitle, fPlayButton)); + int nResult = 0; + if (pfrm != NULL) { + pfrm->DoModal(&nResult, (Sfx)-1, (Sfx)-1); + delete pfrm; + } + return nResult == kidcPlay; +} + +bool DownloadMissionPackFromURL(const char *pszURL, PackId *ppackid, + bool *play) { + DownloadBox *pfrm = (DownloadBox *)gpmfrmm->LoadForm(gpiniForms, + kidfDownloadBox, new DownloadBox(pszURL)); + int nResult = 0; + bool error = false; + if (pfrm != NULL) { + pfrm->DoModal(&nResult, (Sfx)-1, (Sfx)-1); + *ppackid = pfrm->packid(); + error = pfrm->error(); + delete pfrm; + } + if (error) { + return false; + } + *play = (nResult == kidcPlay); + return true; +} + +bool AskInstallMissionPack(const PackId *ppackid, const char *pszUITitle, + const char *pszPackTitle) { + char *pszT = NULL; + switch (gppackm->IsInstalled(ppackid)) { + case 0: + // Not installed + pszT = "You must install the mission pack first. Download now?"; + break; + + case 1: + // Installed already + return true; + + case 2: + // needs upgrade + pszT = "You must upgrade the mission pack first. Download now?"; + break; + } + + // Ask the user + if (HtMessageBox(kidfMessageBoxQuery, kfMbWhiteBorder | + kfMbKeepTimersEnabled, pszUITitle, pszT)) { + + // Load the pack info so it's available when needed + gppim->Start(ppackid, NULL, NULL); + + // Load the pack itself with a supplied title + DownloadMissionPack(ppackid, pszPackTitle, false); + + // If installed now, return success + if (gppackm->IsInstalled(ppackid) == 1) { + return true; + } + } + return false; +} + +DownloadBox::DownloadBox(const PackId *ppackid, const char *pszTitle, + bool fPlayButton) : packid_(*ppackid), pszTitle_(pszTitle), + fPlayButton_(fPlayButton), pszURL_(NULL), error_(false) { +} + +DownloadBox::DownloadBox(const char *pszURL) : + pszTitle_(pszURL), pszURL_(pszURL), fPlayButton_(true), error_(false) { + memset(&packid_, 0, sizeof(packid_)); +} + +DownloadBox::~DownloadBox() { + gppackm->Cancel(); +} + +bool DownloadBox::DoModal(int *pnResult, Sfx sfxShow, Sfx sfxHide) { + // Set colors + SetTitleColor(GetColor(kiclrBlueSideFirst)); + SetBorderColorIndex(kiclrWhite); + SetBackgroundColorIndex(kiclrShadow2x); + + // Set title + + LabelControl *plbl = (LabelControl *)GetControlPtr(kidcMessage); + if (pszTitle_ != NULL) { + plbl->SetText(base::Format::ToString("Downloading %s", pszTitle_)); + } else { + json::JsonMap *map = gppim->GetInfoMap(&packid_); + if (map != NULL) { + const json::JsonObject *obj = map->GetObject("t"); + if (obj != NULL && obj->type() == json::JSONTYPE_STRING) { + plbl->SetText(base::Format::ToString("Downloading %s", + ((json::JsonString *)obj)->GetString())); + } + delete map; + } else { + plbl->SetText("Downloading..."); + } + } + + // Start the download + ShowStatus("Starting download..."); + if (pszURL_ != NULL) { + gppackm->Install(pszURL_, &packid_, NULL, this); + } else { + gppackm->Install(&packid_, NULL, this); + } + + // Remember the button positions + // Center OK, change to "CANCEL", hide Play + + Control *pctl = GetControlPtr(kidcOk); + Rect rcT; + pctl->GetRect(&rcT); + xOk_ = rcT.left; + rcT.Offset((m_rc.Width() - rcT.Width()) / 2 - rcT.left, 0); + pctl->SetRect(&rcT); + ButtonControl *pbtn = (ButtonControl *)pctl; + pbtn->SetText("CANCEL"); + + pctl = GetControlPtr(kidcPlay); + pctl->Show(false); + + // Show the form + return DialogForm::DoModal(pnResult, sfxShow, sfxHide); +} + +void DownloadBox::OnBegin(void *ctx, int cbLength) { + OnProgress(ctx, 0, cbLength); +} + +void DownloadBox::OnProgress(void *ctx, int cbTotal, int cbLength) { + ShowStatus(base::Format::ToString("Downloaded %d of %d bytes", + cbTotal, cbLength)); +} + +void DownloadBox::OnFinished(void *ctx) { + // Reposition and show both buttons, change kidcOk back to OK + + Control *pctl = GetControlPtr(kidcOk); + ButtonControl *pbtn = (ButtonControl *)pctl; + pbtn->SetText("OK"); + if (fPlayButton_) { + Rect rcT; + pctl->GetRect(&rcT); + rcT.Offset(xOk_ - rcT.left, 0); + pctl->SetRect(&rcT); + pctl = GetControlPtr(kidcPlay); + pctl->Show(true); + } + + // Create a message about where the missions went, so it is easy(ier) + // to understand what is going on + + MissionList *pml = CreateMissionList(&packid_, kmltAll); + if (pml == NULL || pml->GetCount() == 0) { + ShowStatus("Download complete, but mission pack has no missions!"); + delete pml; + return; + } + + int cSingle = 0; + int cMulti = 0; + int cTotal = pml->GetCount(); + for (int i = 0; i < cTotal; i++) { + MissionDescription md; + if (pml->GetMissionDescription(i, &md)) { + if (pml->IsMultiplayerMissionType(md.mt)) { + cMulti++; + } else { + cSingle++; + } + } + } + delete pml; + + std::string single; + if (cSingle != 0) { + single = base::Format::ToString( + "%d single player mission%s installed.", + cSingle, cSingle == 1 ? "" : "s"); + } + + std::string multi; + if (cMulti != 0) { + multi = base::Format::ToString( + "%d multiplayer mission%s installed.", + cMulti, cMulti == 1 ? "" : "s"); + } + + ShowStatus(base::Format::ToString("Download Complete! %s %s", + single.c_str(), multi.c_str())); +} + +void DownloadBox::OnError(void *ctx, const char *pszError) { + error_ = true; + ButtonControl *pbtn = (ButtonControl *)GetControlPtr(kidcOk); + pbtn->SetText("OK"); + ShowStatus(base::Format::ToString("Download error: %s", pszError)); +} + +void DownloadBox::ShowStatus(const char *pszStatus) { + LabelControl *plbl = (LabelControl *)GetControlPtr(kidcStatus); + plbl->SetText(pszStatus); + + // Wake up to process the paint + + base::Thread::current().Post(base::kidmNullEvent, NULL); +} + +} // namespace wi diff --git a/game/dragrect.cpp b/game/dragrect.cpp new file mode 100644 index 0000000..c6ef344 --- /dev/null +++ b/game/dragrect.cpp @@ -0,0 +1,327 @@ +#include "game/dragrect.h" +#include "game/ht.h" + +namespace wi { + +void DragRect::Init(const DPoint& pt0, const DPoint& pt1, const DPoint& pt2) { + pt0_ = pt0; + v01_ = Vec2d(pt0, pt1); + v02_ = Vec2d(pt0, pt2); +} + +void DragRect::TrackPoints01(const DPoint& pt0, const DPoint& pt1) { + // width variable, height constant, theta variable + double dy = v01_.flip90().project(v02_); + pt0_ = pt0; + v01_ = Vec2d(pt0, pt1); + v02_ = Vec2d(pt0, v01_.flip90().unit().scale(dy).add(pt1)); +} + +void DragRect::TrackPoints02(const DPoint& pt0, const DPoint& pt2) { + // width variable, height variable, theta constant + double fdy = v02_.project(v01_) / v02_.mag(); + double fdx = Vec2d(v02_.unit().scale(v02_.mag() * fdy).add(pt0_), + v01_.add(pt0_)).mag() / v02_.mag(); + v02_ = Vec2d(pt0, pt2); + DPoint ptT = v02_.unit().scale(v02_.mag() * fdy).add(pt0); + DPoint pt1 = v02_.flip270().unit().scale(v02_.mag() * fdx).add(ptT); + v01_ = Vec2d(pt0, pt1); + pt0_ = pt0; +} + +void DragRect::TrackPoints03(const DPoint& pt0, const DPoint& pt3) { + // width constant, height variable, theta variable + Vec2d v03(pt0, pt3); + DPoint pt1 = v03.flip270().unit().scale(v01_.mag()).add(pt0); + pt0_ = pt0; + v01_ = Vec2d(pt0, pt1); + v02_ = Vec2d(pt0, v03.add(pt1)); +} + +void DragRect::TrackPoints12(const DPoint& pt1, const DPoint& pt2) { + // width constant, height variable, theta variable + Vec2d v12(pt1, pt2); + DPoint pt0 = v12.flip90().unit().scale(v01_.mag()).add(pt1); + pt0_ = pt0; + v01_ = Vec2d(pt0, pt1); + v02_ = Vec2d(pt0, pt2); +} + +void DragRect::TrackPoints13(const DPoint& pt1, const DPoint& pt3) { + // width variable, height variable, theta constant + double fdy = v02_.project(v01_) / v02_.mag(); + double fdx = Vec2d(v02_.unit().scale(v02_.mag() * fdy).add(pt0_), + v01_.add(pt0_)).mag() / v02_.mag(); + Vec2d v13(pt1, pt3); + DPoint ptT = v13.unit().scale(v13.mag() * fdy).add(pt1); + DPoint pt0 = v13.flip90().unit().scale(v13.mag() * fdx).add(ptT); + pt0_ = pt0; + v01_ = Vec2d(pt0, pt1); + v02_ = Vec2d(pt0, v01_.add(pt3)); +} + +void DragRect::TrackPoints23(const DPoint& pt2, const DPoint& pt3) { + // width variable, height constant, theta variable + double dy = v01_.flip90().project(v02_); + Vec2d v32(pt3, pt2); + pt0_ = v32.flip270().unit().scale(dy).add(pt3); + v01_ = Vec2d(pt0_, v32.add(pt0_)); + v02_ = Vec2d(pt0_, pt2); +} + +void DragRect::TrackPoint0(const DPoint& pt0) { + // pt2 fixed, v01 direction locked + v02_ = Vec2d(pt0, v02_.add(pt0_)); + v01_ = v01_.unit().scale(v01_.project(v02_)); + pt0_ = pt0; +} + +void DragRect::TrackPoint1(const DPoint& pt1) { + // pt3 fixed, v01 direction locked + DPoint pt3 = v01_.flip180().add(v02_.add(pt0_)); + v01_ = v01_.unit().scale(-v01_.project(Vec2d(pt1, pt3))); + pt0_ = v01_.flip180().add(pt1); + v02_ = Vec2d(pt0_, v01_.add(pt3)); +} + +void DragRect::TrackPoint2(const DPoint& pt2) { + // pt0 fixed, v01 direction locked + v02_ = Vec2d(pt0_, pt2); + v01_ = v01_.unit().scale(v01_.project(v02_)); +} + +void DragRect::TrackPoint3(const DPoint& pt3) { + // pt1 fixed, v01 direction locked + DPoint pt1 = v01_.add(pt0_); + Vec2d v13(pt1, pt3); + v01_ = v01_.unit().scale(-v01_.project(v13)); + pt0_ = v01_.flip180().add(pt1); + v02_ = Vec2d(pt0_, v01_.add(pt3)); +} + +void DragRect::GetPoints(DPoint *apt) const { + apt[0] = pt0_; + apt[1] = v01_.add(pt0_); + apt[2] = v02_.add(pt0_); + apt[3] = v01_.flip180().add(apt[2]); +} + +dword DragRect::HitTest(const DPoint& pt, Vec2d *pvOffset) const { + DPoint apt[4]; + GetPoints(apt); + + int iBest = -1; + double magBest = MAXFLOAT; + for (int i = 0; i < ARRAYSIZE(apt); i++) { + double mag = Vec2d(pt, apt[i]).mag(); + if (mag < magBest) { + magBest = mag; + iBest = i; + } + } + + if (pvOffset != NULL) { + *pvOffset = Vec2d(pt, apt[iBest]); + } + + // Use a mask in case this will reflect edge and corner hittesting + // in the future. + return 1 << iBest; +} + +void DragRect::GetBoundingRect(Rect *prc) const { + prc->left = 9999; + prc->right = -1; + prc->top = 9999; + prc->bottom = -1; + + DPoint apt[4]; + GetPoints(apt); + + for (int i = 0; i < ARRAYSIZE(apt); i++) { + if (apt[i].x < prc->left) { + prc->left = apt[i].x; + } + if (apt[i].x > prc->right) { + prc->right = apt[i].x; + } + if (apt[i].y < prc->top) { + prc->top = apt[i].y; + } + if (apt[i].y > prc->bottom) { + prc->bottom = apt[i].y; + } + } +} + +bool DragRect::PtIn(const DPoint& pt) const { + // Look at the z portion of the cross product on each side. The result + // should be all negative or positive to be inside. + + DPoint apt[4]; + GetPoints(apt); + + double d0 = (pt.x - apt[0].x) * (apt[1].y - apt[0].y) - + (pt.y - apt[0].y) * (apt[1].x - apt[0].x); + double d1 = (pt.x - apt[1].x) * (apt[2].y - apt[1].y) - + (pt.y - apt[1].y) * (apt[2].x - apt[1].x); + double d2 = (pt.x - apt[2].x) * (apt[3].y - apt[2].y) - + (pt.y - apt[2].y) * (apt[3].x - apt[2].x); + double d3 = (pt.x - apt[3].x) * (apt[0].y - apt[3].y) - + (pt.y - apt[3].y) * (apt[0].x - apt[3].x); + + if (d0 < 0.0 && d1 < 0.0 && d2 < 0.0 && d3 < 0.0) { + return true; + } + if (d0 >= 0.0 && d1 >= 0.0 && d2 >= 0.0 && d3 >= 0.0) { + return true; + } + + return false; +} + +void DragRect::ScrollExpand(dword maskA, dword maskB, double dx, double dy) { + // This only applies when one finger is down. Imagine the map being + // scrolled by dx, dy, and the scroll rect expanding. Basically, the + // fixed point of the rect scrolls in the dx, dy direction. + + if (maskA != 0 && maskB != 0) { + return; + } + + // Simply "scroll" the fixed point + + DPoint pt; + DPoint apt[4]; + GetPoints(apt); + switch (maskA | maskB) { + case 1: + pt = apt[2]; + pt.x += dx; + pt.y += dy; + TrackPoint2(pt); + break; + case 2: + pt = apt[3]; + pt.x += dx; + pt.y += dy; + TrackPoint3(pt); + break; + case 4: + pt = apt[0]; + pt.x += dx; + pt.y += dy; + TrackPoint0(pt); + break; + case 8: + pt = apt[1]; + pt.x += dx; + pt.y += dy; + TrackPoint1(pt); + break; + } +} + +void DragRect::TrackPoints(dword maskA, const DPoint& ptA, + dword maskB, const DPoint& ptB) { + if (maskA == 0) { + switch (maskB) { + case 1: + TrackPoint0(ptB); + break; + case 2: + TrackPoint1(ptB); + break; + case 4: + TrackPoint2(ptB); + break; + case 8: + TrackPoint3(ptB); + break; + default: + break; + } + } else if (maskA == 1) { + switch (maskB) { + case 0: + TrackPoint0(ptA); + break; + case 1: + TrackPoint0(ptA); + break; + case 2: + TrackPoints01(ptA, ptB); + break; + case 4: + TrackPoints02(ptA, ptB); + break; + case 8: + TrackPoints03(ptA, ptB); + break; + default: + break; + } + } else if (maskA == 2) { + switch (maskB) { + case 0: + TrackPoint1(ptA); + break; + case 1: + TrackPoints01(ptB, ptA); + break; + case 2: + TrackPoint2(ptA); + break; + case 4: + TrackPoints12(ptA, ptB); + break; + case 8: + TrackPoints13(ptA, ptB); + break; + default: + break; + } + } else if (maskA == 4) { + switch (maskB) { + case 0: + TrackPoint2(ptA); + break; + case 1: + TrackPoints02(ptB, ptA); + break; + case 2: + TrackPoints12(ptB, ptA); + break; + case 4: + TrackPoint2(ptA); + break; + case 8: + TrackPoints23(ptA, ptB); + break; + default: + break; + } + } else if (maskA == 8) { + switch (maskB) { + case 0: + TrackPoint3(ptA); + break; + case 1: + TrackPoints03(ptB, ptA); + break; + case 2: + TrackPoints13(ptB, ptA); + break; + case 4: + TrackPoints23(ptB, ptA); + break; + case 8: + TrackPoint3(ptA); + break; + default: + break; + } + } +} + +} // namespace wi diff --git a/game/dragrect.h b/game/dragrect.h new file mode 100644 index 0000000..82a1913 --- /dev/null +++ b/game/dragrect.h @@ -0,0 +1,42 @@ +#ifndef __DRAGRECT_H__ +#define __DRAGRECT_H__ + +#include "inc/basictypes.h" +#include "game/vec2d.h" + +namespace wi { + +// pt0 is lower left, and points go ccw + +class Rect; +class DragRect { // drc +public: + void Init(const DPoint& pt0, const DPoint& pt1, const DPoint& pt2); + void GetPoints(DPoint *apt) const; + dword HitTest(const DPoint& pt, Vec2d *pvOffset = NULL) const; + void TrackPoints(dword maskA, const DPoint& ptA, dword maskB, + const DPoint& ptB); + void GetBoundingRect(Rect *prc) const; + bool PtIn(const DPoint& pt) const; + void ScrollExpand(dword maskA, dword maskB, double dx, double dy); + +private: + void TrackPoint0(const DPoint& pt0); + void TrackPoint1(const DPoint& pt1); + void TrackPoint2(const DPoint& pt2); + void TrackPoint3(const DPoint& pt3); + void TrackPoints01(const DPoint& pt0, const DPoint& pt1); + void TrackPoints02(const DPoint& pt0, const DPoint& pt2); + void TrackPoints03(const DPoint& pt0, const DPoint& pt3); + void TrackPoints12(const DPoint& pt1, const DPoint& pt2); + void TrackPoints13(const DPoint& pt1, const DPoint& pt3); + void TrackPoints23(const DPoint& pt2, const DPoint& pt3); + + DPoint pt0_; + Vec2d v01_; + Vec2d v02_; +}; + +} // namespace wi + +#endif // __DRAGRECT_H__ diff --git a/game/drawscan.cpp b/game/drawscan.cpp new file mode 100644 index 0000000..0b0f9ae --- /dev/null +++ b/game/drawscan.cpp @@ -0,0 +1,171 @@ +#include "game/ht.h" +#include "game/ops.h" + +namespace wi { + +int YClipToScan(int yClip, int cx, byte*& pop, byte*& pbSrc, word*& psc) { + if (yClip == 0) { + return 0; + } + int offset = 0; + int y = 0; + while (true) { + int op = (int)(word)*pop++; + if (op == kopAlign) { + pbSrc++; + continue; + } + if (op >= kopEvenData1 && op <= kopEvenData48) { + int cb = op - kopEvenData1 + 1; + offset += cb; + pbSrc += cb; + continue; + } + if (op >= kopOddData1 && op <= kopOddData48) { + int cb = op - kopOddData1 + 1; + offset += cb; + pbSrc += cb; + continue; + } + if (op >= kopEvenSide1 && op <= kopEvenSide16) { + int cb = op - kopEvenSide1 + 1; + offset += cb; + psc += (cb >> 2); + psc += ((cb & 2) >> 1); + psc += (cb & 1); + continue; + } + if (op >= kopOddSide1 && op <= kopOddSide16) { + int cb = op - kopOddSide1 + 1; + offset += cb; + psc++; + cb--; + psc += (cb >> 2); + psc += ((cb & 2) >> 1); + psc += (cb & 1); + continue; + } + if (op >= kopShadow1 && op <= kopShadow24) { + offset += (op - kopShadow1 + 1); + continue; + } + if (op >= kopTransparent1 && op <= kopTransparent32) { + offset += (op - kopTransparent1 + 1); + continue; + } + if (op >= kopNextScan0 && op <= kopNextScan48) { + offset += ((op - kopNextScan0) - cx); + y++; + if (y == yClip) { + break; + } + continue; + } + if (op == kopEnd) { + break; + } + } + return offset; +} + +int DrawScan(byte *pbDst, int cx, byte*& pbSrc, byte*& pop, word*& psc, + dword *mpscaiclr, byte *mpiclriclrShadow) { + + byte *pbDstStart = pbDst; + while (true) { + int op = (int)(word)*pop++; + if (op == kopAlign) { + pbSrc++; + continue; + } + if (op >= kopEvenData1 && op <= kopEvenData48) { + int cb = op - kopEvenData1 + 1; + for (; cb >= 2; cb -= 2) { + *((word *)pbDst) = *((word *)pbSrc); + pbDst += 2; + pbSrc += 2; + } + if (cb != 0) + *pbDst++ = *pbSrc++; + continue; + } + if (op >= kopOddData1 && op <= kopOddData48) { + int cb = op - kopOddData1 + 1; + *pbDst++ = *pbSrc++; + cb--; + for (; cb >= 2; cb -= 2) { + *((word *)pbDst) = *((word *)pbSrc); + pbDst += 2; + pbSrc += 2; + } + if (cb != 0) + *pbDst++ = *pbSrc++; + continue; + } + if (op >= kopEvenSide1 && op <= kopEvenSide16) { + int cb = op - kopEvenSide1 + 1; + for (; cb >= 4; cb -= 4) { + word *pwSrc = (word *)(((byte *)mpscaiclr) + (*psc)); + *((word *)pbDst) = *pwSrc++; + pbDst += 2; + *((word *)pbDst) = *pwSrc++; + pbDst += 2; + psc++; + } + for (; cb >= 2; cb -= 2) { + *((word *)pbDst) = *((word *)(((byte *)mpscaiclr) + (*psc))); + psc++; + pbDst += 2; + } + if (cb != 0) { + *pbDst++ = *((byte *)(((byte *)mpscaiclr) + (*psc))); + psc++; + } + continue; + } + if (op >= kopOddSide1 && op <= kopOddSide16) { + int cb = op - kopOddSide1 + 1; + *pbDst++ = *((byte *)(((byte *)mpscaiclr) + (*psc))); + psc++; + cb--; + for (; cb >= 4; cb -= 4) { + word *pwSrc = (word *)(((byte *)mpscaiclr) + (*psc)); + *((word *)pbDst) = *pwSrc++; + pbDst += 2; + *((word *)pbDst) = *pwSrc++; + pbDst += 2; + psc++; + } + for (; cb >= 2; cb -= 2) { + *((word *)pbDst) = *((word *)(((byte *)mpscaiclr) + (*psc))); + psc++; + pbDst += 2; + } + if (cb != 0) { + *pbDst++ = *((byte *)(((byte *)mpscaiclr) + (*psc))); + psc++; + } + continue; + } + if (op >= kopShadow1 && op <= kopShadow24) { + int cb = op - kopShadow1 + 1; + while (cb-- != 0) { + *pbDst = mpiclriclrShadow[*pbDst]; + pbDst++; + } + continue; + } + if (op >= kopTransparent1 && op <= kopTransparent32) { + int cb = op - kopTransparent1 + 1; + pbDst += cb; + continue; + } + if (op >= kopNextScan0 && op <= kopNextScan48) { + return &pbDst[op - kopNextScan0] - pbDstStart; + } + if (op == kopEnd) + return 0; + } +} + +} // namespace wi diff --git a/game/drm.cpp b/game/drm.cpp new file mode 100644 index 0000000..8f97643 --- /dev/null +++ b/game/drm.cpp @@ -0,0 +1,605 @@ +#include "ht.h" + +namespace wi { + +// The code is derived from the Owner name. The Owner name is backed up and is the same +// after a cold boot; that's why we use it. The Code is simply a non-obvious binary +// translation of the name into a form that can be validated. + +struct Code // code +{ + byte ab[4]; + byte bXor; + byte bSum; + byte abHash[2]; +}; + +// DRM key that gets saved to / loaded from prefs + +Key gkey; +bool gfDemo = false; + +bool GetCode(char *szName, Code *pcode, bool fShowError = true) secDrm; +bool ValidateCode(Code *pcode) secDrm; +void GetKeyFromCode(Code *pcode, Key *pkey) secDrm; +bool CompareKeys(Key *pkeyA, Key *pkeyB) secDrm; +dword HashBytes(byte *pb, int cb) secDrm; +void FillTemplate(char *psz, char *pszFormat, byte *pb) secDrm; +bool ValidateRetailKey(Key *pkey) secDrm; + +dword HashBytes(byte *pb, int cb) +{ + dword dwHash = 0; + while (cb-- != 0) + dwHash = (dwHash << 5) + dwHash + *pb++; + return dwHash; +} + +int DrmGetRandom(long *plSeed) secDrm; +int DrmGetRandom(long *plSeed) +{ + return (((*plSeed) = (*plSeed) * 214013L + 2531011L) >> 16) & 0x7fff; +} + +bool GetCode(char *szName, Code *pcode, bool fShowError) +{ + if (szName == NULL) { + char szNameT[64]; + if (!HostGetOwnerName(szNameT, sizeof(szNameT), fShowError)) + return false; + szName = szNameT; + } + + long lSeed = (long)HashBytes((byte *)szName, strlen(szName)); + + // Serialize in endian independent way + + Code code; + memset(&code, 0, sizeof(code)); + + int n; + for (n = 0; n < 4; n++) { + code.ab[n] = (byte)DrmGetRandom(&lSeed); + code.bXor ^= code.ab[n]; + code.bSum += code.ab[n]; + } + + // Hash what we have so far - more validation + + word wHash = (word)(HashBytes(code.ab, 6) >> 16); + code.abHash[0] = (byte)wHash; + code.abHash[1] = (byte)(wHash >> 8); + + // Now xor the first 6 with abHash[0] + + byte *pb = (byte *)&code; + for (n = 0; n < 6; n++) + *pb++ ^= code.abHash[0]; + + // Now xor the first 7 with abHash[1] + + pb = (byte *)&code; + for (n = 0; n < 7; n++) + *pb++ ^= code.abHash[1]; + + // Done + + *pcode = code; + Assert(ValidateCode(&code)); + return true; +} + +bool ValidateCode(Code *pcode) +{ + Code code = *pcode; + + // Now xor the first 7 with abHash[1] + + byte *pb = (byte *)&code; + int n; + for (n = 0; n < 7; n++) + *pb++ ^= code.abHash[1]; + + // Now xor the first 6 with abHash[0] + + pb = (byte *)&code; + for (n = 0; n < 6; n++) + *pb++ ^= code.abHash[0]; + + // Hash the first 6 and compare to our hash + + word wHash = (word)(HashBytes(code.ab, 6) >> 16); + if (code.abHash[0] != (byte)wHash) + return false; + if (code.abHash[1] != (byte)(wHash >> 8)) + return false; + + // Xor and add the first 4 + + byte bXor = 0; + byte bSum = 0; + for (n = 0; n < 4; n++) { + bXor ^= code.ab[n]; + bSum += code.ab[n]; + } + if (bXor != code.bXor) + return false; + if (bSum != code.bSum) + return false; + + return true; +} + +void GetKeyFromCode(Code *pcode, Key *pkey) +{ + byte *abCode = (byte *)pcode; + int cbCode = sizeof(*pcode); + + // Generate a seed + + long lSeed = 0x29a; + int n; + for (n = 0; n < cbCode; n++) + lSeed = lSeed * abCode[n] + n; + + // Initialize rotors + + byte abR1[256]; + byte abR2[256]; + byte abR3[256]; + + for (n = 0; n < 256; n++) + abR1[n] = n; + memset(abR2, 0, sizeof(abR2)); + memset(abR3, 0, sizeof(abR3)); + + for (n = 0; n < 256; n++) { + lSeed = 3 * lSeed + abCode[n & 7]; + long lRandom = lSeed % 65521; + int ibR1 = (lRandom & 255) % (256 - n); + lRandom >>= 8; + byte bTemp = abR1[255 - n]; + abR1[255 - n] = abR1[ibR1]; + abR1[ibR1] = bTemp; + if (abR3[255 - n] == 0) { + int nT = (lRandom & 255) % (255 - n); + while (abR3[nT] != 0) + nT = (nT + 1) % (255 - n); + abR3[255 - n] = nT & 255; + abR3[nT] = 255 - n; + } + } + for (n = 0; n < 256; n++) + abR2[abR1[n]] = n; + + // Encrypt the code into a key + + int nT = 0; + byte *abKey = (byte *)pkey; + int cbKey = sizeof(*pkey); + for (n = 0; n < cbKey; n++) { + int nSlotR1 = abR1[(n + nT) & 255]; + int nSlotR3 = abR3[nSlotR1 & 255]; + int nSlotR2 = abR2[nSlotR3 & 255]; + abKey[n] = abR2[nSlotR2 & 255] - nT; + nT = (nT + 1) & 255; + } +} + +bool CompareKeys(Key *pkeyA, Key *pkeyB) +{ + byte *pbA = (byte *)pkeyA; + byte *pbB = (byte *)pkeyB; + int cb = sizeof(*pkeyA); + while (cb-- != 0) { + if (*pbA++ != *pbB++) + return false; + } + return true; +} + +bool ValidateRetailKey(Key *pkey) +{ +#ifdef DRM_KEYONLY + char szT[4]; + + // Derive the "name" from the key + + szT[0] = pkey->ab[0]; + long lSeed = (long)szT[0]; + szT[1] = pkey->ab[1] ^ (byte)DrmGetRandom(&lSeed); + szT[2] = pkey->ab[2] ^ (byte)DrmGetRandom(&lSeed); + szT[3] = 0; + + // Fill in zeros + + int n; + for (n = 0; n < sizeof(szT) - 1; n++) { + while (szT[n] == 0) + szT[n] = (byte)DrmGetRandom(&lSeed); + } + + // Get code + + Code code; + GetCode(szT, &code); + + // Gen full key from this + + Key keyT; + GetKeyFromCode(&code, &keyT); + + // Compare keys - compares the last 6 bytes because we copy over the first 3 + + Key keyReconstructed = *pkey; + for (n = 0; n < sizeof(szT) - 1; n++) + keyReconstructed.ab[n] = keyT.ab[n]; + + // Now compare these two keys + + return CompareKeys(&keyT, &keyReconstructed); +#else + return false; +#endif +} + +// +// UI +// + +class DrmCodeForm : public ShellForm +{ +public: + virtual bool Init(FormMgr *pfrmm, IniReader *pini, word idf) secDrm; + virtual void OnControlSelected(word idc) secDrm; +}; + +class DrmKeyForm : public ShellForm +{ +public: + virtual bool Init(FormMgr *pfrmm, IniReader *pini, word idf) secDrm; + virtual void OnControlSelected(word idc) secDrm; + +private: + void OnBackspace() secDrm; + void OnEnterNumber(int nNumber) secDrm; + void ParseKey(Key *pkey) secDrm; +}; + +bool DrmValidate() +{ +#ifndef DRM + return true; +#else +#ifdef DRM_KEYONLY + // Validate key + + if (ValidateRetailKey(&gkey)) + return true; + + // Key Invalid, bring up UI + + ShellForm *pfrm = (ShellForm *)gpmfrmm->LoadForm(gpiniForms, kidfDrmKey, new DrmKeyForm()); + if (pfrm == NULL) + return false; + bool fSuccess = pfrm->DoModal(); + delete pfrm; + return fSuccess; + +#else + +#ifdef PIL + // Special Palm codepath to check for FILR resource + + DmOpenRef pdb; + int nRec = (short)DmSearchResource('FILR', 0, NULL, &pdb); + bool fHasFILR = (nRec >= 0); + + // If our key is valid, return success + + Code code; + if (!GetCode(NULL, &code, !fHasFILR)) { + // No code, which means not hotsynced yet. If we have a FILR resource, let copy protect succeed. + // We'll mark this binary later after the device has been hotsynced. Note there is a window where by + // someone could receive WI before hotsyncing, run it, and then beam it to someone else who could also + // run it. + + if (fHasFILR) + return true; + + // Go into demo mode + + HtMessageBox(kfMbClearDib, "Hostile Takeover", "License key not entered. Entering demo mode."); + gfDemo = true; + return true; + } + + Key keyT; + GetKeyFromCode(&code, &keyT); + + // Check for special resource. If present, mark the resource, copy a working key into gkey, save prefs + // and return. This way if someone beams this game to another device without the key or resource, the + // DRM form will come up + + if (fHasFILR) { + // Found resource; make sure it is on our .prc + + LocalID lidRes; + DmOpenDatabaseInfo(pdb, &lidRes, NULL, NULL, NULL, NULL); + LocalID lidApp; + UInt16 nCard; + SysCurAppDatabase (&nCard, &lidApp); + if (lidApp == lidRes) { + // If it starts with a 0, it's been poked already + // Otherwise it is first time; copy over a valid retail key + +#define memHeapFlagReadOnly 0x01 + + MemHandle hMem = DmGetResourceIndex(pdb, nRec); + UInt16 idHeap = MemHandleHeapID(hMem); + if (!(MemHeapFlags(idHeap) & memHeapFlagReadOnly)) { + char *psz = (char *)MemHandleLock(hMem); + if (*psz != 0) { + char ch = 0; + DmWrite(psz, 0, &ch, 1); + gkey = keyT; + ggame.SavePreferences(); + } + MemHandleUnlock(hMem); + } + } + } +#else + // If our key is valid, return success + + Code code; + if (!GetCode(NULL, &code)) + return false; + + Key keyT; + GetKeyFromCode(&code, &keyT); + +#endif + + // Compare keys + + if (CompareKeys(&gkey, &keyT)) + return true; + + // No valid key; bring up UI + + ShellForm *pfrm = (ShellForm *)gpmfrmm->LoadForm(gpiniForms, kidfDrmCode, new DrmCodeForm()); + if (pfrm == NULL) + return false; + gfDemo = !pfrm->DoModal(); + delete pfrm; + return true; +#endif +#endif +} + +void FillTemplate(char *psz, char *pszFormat, byte *pb) +{ + char *pchSrc = pszFormat; + char *pchDst = psz; + + bool fMSB = true; + byte bT = 0; + int cb = strlen(pszFormat) + 1; + while (cb-- != 0) { + if (*pchSrc != '?') { + *pchDst++ = *pchSrc++; + continue; + } + + int nNibble = 0; + if (fMSB) { + fMSB = false; + bT = *pb++; + nNibble = (bT >> 4) & 0x0f; + } else { + fMSB = true; + nNibble = (bT & 0x0f); + } + + char chT; + if (nNibble < 0x0a) { + chT = '0' + nNibble; + } else { + chT = 'A' + nNibble - 0x0a; + } + + *pchDst++ = chT; + pchSrc++; + } +} + +// +// DrmCodeForm implementation +// + +bool DrmCodeForm::Init(FormMgr *pfrmm, IniReader *pini, word idf) +{ + if (!ShellForm::Init(pfrmm, pini, idf)) + return false; + + Code code; + if (!GetCode(NULL, &code)) + return false; + + char szT[32]; + FillTemplate(szT, "C-\?\?\?\?-\?\?\?\?-\?\?\?\?-\?\?\?\?", (byte *)&code); + + LabelControl *plbl = (LabelControl *)GetControlPtr(kidcCode); + plbl->SetText(szT); + + return true; +} + +void DrmCodeForm::OnControlSelected(word idc) +{ + switch (idc) { + case kidcPlayDemo: + break; + + case kidcEnterKey: + { + ShellForm *pfrm = (ShellForm *)gpmfrmm->LoadForm(gpiniForms, kidfDrmKey, new DrmKeyForm()); + if (pfrm == NULL) + break; + bool f = pfrm->DoModal(); + delete pfrm; + if (f) { + m_idcLast = kidcOk; + break; + } + return; + } + break; + } + + ShellForm::OnControlSelected(idc); +} + +// +// DrmKeyForm implementation +// + +bool DrmKeyForm::Init(FormMgr *pfrmm, IniReader *pini, word idf) +{ + return ShellForm::Init(pfrmm, pini, idf); +} + +void DrmKeyForm::OnEnterNumber(int nNumber) +{ + char szT[32]; + LabelControl *plbl = (LabelControl *)GetControlPtr(kidcKey); + strncpyz(szT, plbl->GetText(), sizeof(szT)); + + // Find first '?' + + int cch = strlen(szT); + int n; + for (n = 0; n < cch; n++) { + if (szT[n] == '?') + break; + } + if (n == cch) + return; + + // Put new number there + + char ch = nNumber < 0xa ? '0' + nNumber : 'A' + nNumber - 0xa; + szT[n] = ch; + plbl->SetText(szT); +} + +void DrmKeyForm::OnBackspace() +{ + char szT[32]; + LabelControl *plbl = (LabelControl *)GetControlPtr(kidcKey); + strncpyz(szT, plbl->GetText(), sizeof(szT)); + + // Find last number + + int cch = strlen(szT); + int n; + for (n = cch - 1; n >= 0; n--) { + if ((szT[n] >= '0' && szT[n] <= '9') || (szT[n] >= 'A' && szT[n] <= 'F')) + break; + } + if (n < 0) + return; + + // Put '?' there + + szT[n] = '?'; + plbl->SetText(szT); +} + +void DrmKeyForm::ParseKey(Key *pkey) +{ + // Init key to 0 + + byte *pb = (byte *)pkey; + int cb = sizeof(*pkey); + memset(pb, 0, cb); + + // Parse out key + + LabelControl *plbl = (LabelControl *)GetControlPtr(kidcKey); + const char *psz = plbl->GetText(); + + // Find last number + + int cch = strlen(psz); + int n; + bool fMSB = true; + for (n = 0; n < cch; n++) { + if ((psz[n] >= '0' && psz[n] <= '9') || (psz[n] >= 'A' && psz[n] <= 'F')) { + int nNibble; + if (psz[n] >= 'A') { + nNibble = psz[n] - 'A' + 0xa; + } else { + nNibble = psz[n] - '0'; + } + if (fMSB) { + fMSB = false; + *pb = nNibble << 4; + } else { + fMSB = true; + *pb |= nNibble; + pb++; + } + } + } +} + +void DrmKeyForm::OnControlSelected(word idc) +{ + switch (idc) { + case kidcOk: + { + // Valid? + + Key key; + ParseKey(&key); + +#ifdef DRM_KEYONLY + bool fSuccess = ValidateRetailKey(&key); +#else + Code code; + GetCode(NULL, &code); + Key keyT; + GetKeyFromCode(&code, &keyT); + bool fSuccess = CompareKeys(&key, &keyT); +#endif + if (!fSuccess) { + HtMessageBox(kfMbWhiteBorder, "License Key", "The key you have entered is invalid!"); + return; + } + + // Good key. Save prefs. + + HtMessageBox(kfMbWhiteBorder, "License Key", "Thank you."); + gkey = key; + ggame.SavePreferences(); + } + + // fall through + + case kidcCancel: + ShellForm::OnControlSelected(idc); + break; + + case kidcBackspace: + OnBackspace(); + break; + + default: + OnEnterNumber(idc - kidc0); + break; + } +} + +} // namespace wi diff --git a/game/event.cpp b/game/event.cpp new file mode 100644 index 0000000..2e70b4e --- /dev/null +++ b/game/event.cpp @@ -0,0 +1,480 @@ +#include "ht.h" + +namespace wi { + +EventMgr gevm; + +EventMgr::EventMgr() +{ + Init(); +} + +EventMgr::~EventMgr() +{ +} + +bool EventMgr::PeekEvent(Event *pevt, long ctWait) +{ + if (m_fPeekKeep) { + *pevt = m_evtPeek; + return true; + } + if (!GetEvent(pevt, ctWait)) + return false; + + m_fPeekKeep = true; + m_evtPeek = *pevt; + return true; +} + +// TUNE: +const long kctPenHoldDelay = 50; + +bool EventMgr::GetEvent(Event *pevt, long ctWait, bool fCheckPaints) +{ +#if defined(WIN) && !defined(CE) && defined(DEBUG) + Assert(_CrtCheckMemory()); +#endif + + // Only needed for Tapwave bluetooth + +#ifdef PIL + if (gfTapwave && gptra != NULL) { + extern void HostTrackServiceCallsPerGetEvent(); + HostTrackServiceCallsPerGetEvent(); + } +#endif + + // Force stopping once we've started. Retain modifiers to the appStopEvent if there are any + + if (m_fAppStopping) { + if ((m_fPeekKeep) && (m_evtPeek.eType == appStopEvent)) { + *pevt = m_evtPeek; + } else { + pevt->eType = appStopEvent; + } + return true; + } + + // Service sound right away + + HostSoundServiceProc(); + long tStart = gtimm.GetTickCount(); + long ctTimerNext = 0; + + // Check the peek keep - if one exists, return it + + if (m_fPeekKeep) { + *pevt = m_evtPeek; + m_fPeekKeep = false; + return true; + } + + // GetEvent w/wait + timer scan and dispatch loop + + long ctTotal = ctWait; + long ctElapsed = 0; + long tCurrent = tStart; + while (true) { + // Check for posted events + + if (m_cevt != 0) { + *pevt = m_aevtQ[0]; + m_cevt--; + memmove(m_aevtQ, m_aevtQ + 1, m_cevt * ELEMENTSIZE(m_aevtQ)); + if (pevt->eType == appStopEvent) + m_fAppStopping = true; + return true; + } + + // Which is shorter, the timer wait or the asked wait? + + long ctNext; + if (ctWait == -1) { + ctNext = ctTimerNext; + } else { + ctNext = ctTotal - ctElapsed; + if (ctNext < 0) + ctNext = 0; + if (ctTimerNext != -1 && ctTimerNext < ctNext) { + ctNext = ctTimerNext; + } + } + + // Don't wait any longer than it takes for a penHoldEvent to be produced + + if (m_fTimingPenHold) { + int ctPenHoldRemaining = kctPenHoldDelay - (tCurrent - m_tPenDown); + if (ctPenHoldRemaining < 0) + ctPenHoldRemaining = 0; + + if (ctNext == -1 || ctPenHoldRemaining < ctNext) + ctNext = ctPenHoldRemaining; + } + + // This sleep might be too long for sound needs. Ask the sound manager + + if (ctNext != 0) + ctNext = gsndm.FilterSleepTicks(ctNext); + + // Get an event / wait this long (or don't wait at all if a redraw is + // pending) + + bool fHaveEvent = false; + +//#define PAINT_BEFORE_INPUT +#ifdef PAINT_BEFORE_INPUT + if ((m_wfRedraw & (kfRedrawDirty | kfRedrawBeforeInput)) != (kfRedrawDirty | kfRedrawBeforeInput)) { +#endif + +#ifdef __CPU_68K + // In general we like to call HostGetEvent so it can sleep, + // do I/O processing, whatever. However, on 68K Palms SysGetEvent + // is very slow and feeds us a lot of pen events the processing of + // which also slows the game so on 68K devices we throttle our + // calls to GetEvent some. + + if (abs(tCurrent - m_tLastMoveEvent) > + (long)m_ctMoveEventInterval) { +#else + { +#endif + // Get the event from the host. The host doesn't coalesce + // messages, but does mark messages has being coalesce + // candidates, based on what was in the queue when the post + // occured. On the game side, all pen events go into a history + // buffer for calculating flick vectors. Then, if the message + // was marked as a coalesced message, it is ignored. + + while (true) { + fHaveEvent = HostGetEvent(pevt, (m_wfRedraw & kfRedrawDirty) ? 0 : ctNext); + if (!fHaveEvent) + break; + UpdatePenHistory(pevt); + if (!(pevt->ff & kfEvtCoalesce)) + break; + + // There should always be a non-coalesced pen event + // following a coalesced one, but just in case... + ctNext = 0; + } + } +#ifdef PAINT_BEFORE_INPUT + } +#endif + + // Manage the production of penHoldEvents + + if (fHaveEvent) { + switch (pevt->eType) { + case penDownEvent: + // Hold events are generated when the pen is pressed down and + // held for a specified amount of time before being released. + + m_fTimingPenHold = true; + m_tPenDown = gtimm.GetTickCount(); + m_xHold = pevt->x; + m_yHold = pevt->y; + break; + + case penMoveEvent: + // If the pen moves by more than N pixels in any dimension then + // we'll assume the player isn't trying to tap-hold and cancel + // it. + + if (m_fTimingPenHold) { + // TUNE: penhold distance threshold + if (abs(pevt->x - m_xHold) > 7 || + abs(pevt->y - m_yHold) > 7) { + m_fTimingPenHold = false; + } + } + break; + + case penUpEvent: + // When the pen is released the timer is halted. + + m_fTimingPenHold = false; + break; + } + } else { + // penHoldEvents are only produced when no other events are queued. + // This seems OK. + + if (m_fTimingPenHold && tCurrent - m_tPenDown >= kctPenHoldDelay) { + m_fTimingPenHold = false; + fHaveEvent = true; + pevt->eType = penHoldEvent; + pevt->idf = 0; // UNDONE: could have post-CookEvent/penDown below stash idf for use here + pevt->x = m_xHold; + pevt->y = m_yHold; + } + } + + // Check for timers to dispatch + + HostSoundServiceProc(); + tCurrent = gtimm.GetTickCount(); + if ((m_wfRedraw & (kfRedrawDirty | kfRedrawBeforeTimer)) != + (kfRedrawDirty | kfRedrawBeforeTimer)) { + ctTimerNext = gtimm.ScanDispatch(tCurrent); + } + + // If we have an event, cook it and return it + + if (fHaveEvent) { + // Cook and return + + gpmfrmm->CookEvent(pevt); + switch (pevt->eType) { + case appStopEvent: + m_fAppStopping = true; + break; + + case penMoveEvent: + m_tLastMoveEvent = tCurrent; + + // fall through + case penDownEvent: + // Raise redraw priority while the pen is down + + m_wfRedraw |= kfRedrawBeforeInput; + break; + } + return true; + } + + // If asked to check for paints, do it here + // Sometimes this is not done from here, like during game simulation + + if (fCheckPaints) + gpmfrmm->CheckSetRedrawDirty(); + + // If redraw is needed, now is the time + + if (m_wfRedraw & (kfRedrawDirty | kfRedrawMax)) { + if (m_wfRedraw & kfRedrawMax) { + m_wfRedraw &= ~(kfRedrawBeforeTimer | kfRedrawBeforeInput); + } else { + m_wfRedraw &= ~(kfRedrawDirty | kfRedrawBeforeTimer | kfRedrawBeforeInput); + } + pevt->eType = gamePaintEvent; + return true; + } + + // If the user timeout expired, return timeout + + tCurrent = gtimm.GetTickCount(); + ctElapsed = tCurrent - tStart; + if (ctWait != -1) { + if (ctElapsed >= ctWait) + return false; + } + } +} + +void EventMgr::UpdatePenHistory(Event *pevt) +{ + bool fPenEvent = false; + + switch (pevt->eType) { + case penDownEvent: + case penMoveEvent: + case penUpEvent: + fPenEvent = true; + if (m_fPenHistoryInitialized) { + m_aevtPen1History[m_ievtPen1Next] = *pevt; + m_ievtPen1Next = (m_ievtPen1Next + 1) & (kcevtPenHistory - 1); + } + break; + + case penDownEvent2: + case penMoveEvent2: + case penUpEvent2: + fPenEvent = true; + if (m_fPenHistoryInitialized) { + m_aevtPen2History[m_ievtPen2Next] = *pevt; + m_ievtPen2Next = (m_ievtPen2Next + 1) & (kcevtPenHistory - 1); + } + break; + } + + if (fPenEvent && !m_fPenHistoryInitialized) { + for (int n = 0; n < ARRAYSIZE(m_aevtPen1History); n++) { + m_aevtPen1History[n] = *pevt; + m_aevtPen2History[n] = *pevt; + } + m_ievtPen1Next = 0; + m_ievtPen2Next = 0; + m_fPenHistoryInitialized = true; + } +} + +bool EventMgr::GetFlickVector(int nPen, FlickVector *pfliv) +{ + memset(pfliv, 0, sizeof(*pfliv)); + long msNow = HostGetMillisecondCount(); + Point ptStart; + if (!QueryPenHistory(nPen, msNow - kcmsFlickQuantum, &ptStart)) + return false; + Point ptEnd; + if (!QueryPenHistory(nPen, msNow, &ptEnd)) + return false; + pfliv->dx = ptEnd.x - ptStart.x; + pfliv->dy = ptEnd.y - ptStart.y; + pfliv->cms = kcmsFlickQuantum; + return true; +} + +bool EventMgr::QueryPenHistory(int nPen, long ms, Point *ppt) +{ + if (!m_fPenHistoryInitialized) { + return false; + } + + int ievtFirst, ievtLast; + Event *aevt; + if (nPen == 1) { + aevt = m_aevtPen1History; + ievtFirst = m_ievtPen1Next; + ievtLast = (m_ievtPen1Next - 1) & (kcevtPenHistory - 1); + } else if (nPen == 2) { + aevt = m_aevtPen2History; + ievtFirst = m_ievtPen2Next; + ievtLast = (m_ievtPen2Next - 1) & (kcevtPenHistory - 1); + } else { + return false; + } + + // Find event at time t + + Event *pevtOnOrBefore = NULL; + Event *pevtOnOrAfter = NULL; + for (int ievt = ievtFirst; true; ievt = (ievt + 1) & (kcevtPenHistory - 1)) { + Event *pevt = &aevt[ievt]; + if (pevt->ms == ms) { + pevtOnOrBefore = pevt; + pevtOnOrAfter = NULL; + break; + } + if (pevt->ms < ms) { + pevtOnOrBefore = pevt; + } + if (pevt->ms > ms) { + pevtOnOrAfter = pevt; + break; + } + if (ievt == ievtLast) + break; + } + + if (pevtOnOrBefore == NULL && pevtOnOrAfter != NULL) { + ppt->x = pevtOnOrAfter->x; + ppt->y = pevtOnOrAfter->y; + return true; + } + + if (pevtOnOrBefore != NULL && pevtOnOrAfter == NULL) { + ppt->x = pevtOnOrBefore->x; + ppt->y = pevtOnOrBefore->y; + return true; + } + + // Calculate a point between these two events + + float flPercent = (float)(ms - pevtOnOrBefore->ms) / (float)(pevtOnOrAfter->ms - pevtOnOrBefore->ms); + ppt->x = pevtOnOrBefore->x + (int)(flPercent * (pevtOnOrAfter->x - pevtOnOrBefore->x) + 0.5f); + ppt->y = pevtOnOrBefore->y + (int)(flPercent * (pevtOnOrAfter->y - pevtOnOrBefore->y) + 0.5f); + + return true; +} + +void EventMgr::Init() +{ + m_fPeekKeep = false; + m_cevt = 0; + m_fAppStopping = false; + m_wfRedraw = 0; + m_fTimingPenHold = false; + m_ctMoveEventInterval = 0; + m_nctMoveEventFraction = 0; + m_fPenHistoryInitialized = false; +} + +bool EventMgr::DispatchEvent(Event *pevt) +{ + Form *pfrm = gpmfrmm->GetFormPtr(pevt->idf); +// Trace("EventMgr::DispatchEvent: evt 0x%0lx, eType %d, idf %d", pevt, pevt->eType, pevt->idf); + if (pfrm != NULL) + return pfrm->EventProc(pevt); + return false; +} + +void EventMgr::PostEvent(Event *pevt, bool fCoalesce) +{ + if (fCoalesce) { + for (int nT = m_cevt - 1; nT >= 0; nT--) { + if (m_aevtQ[nT].eType == pevt->eType) { + m_aevtQ[nT] = *pevt; + return; + } + } + if (m_fPeekKeep && m_evtPeek.eType == pevt->eType) { + m_evtPeek = *pevt; + return; + } + } + + Assert(m_cevt < kcevtPostMax); + m_aevtQ[m_cevt] = *pevt; + m_cevt++; +} + +void EventMgr::PostEvent(int eType, bool fCoalesce) +{ + Event evt; + memset(&evt, 0, sizeof(evt)); + evt.eType = eType; + PostEvent(&evt); +} + +void EventMgr::SetPenEventInterval(word ctInterval) +{ + // Set the interval at which we'll accept pen events per second. This prevents the OS + // from flooding unneeded events through the system. + + if (ctInterval >= 50) + return; + + // Allow the interval to increase instantly but decrease more slowly. This way we get + // instant game response and the performance doesn't ping-pong back and forth as the + // interval gets adjusted. + + word nctFractionNew = ctInterval << 4; + if (nctFractionNew > m_nctMoveEventFraction) { + m_nctMoveEventFraction = nctFractionNew; + } else { + m_nctMoveEventFraction -= (m_nctMoveEventFraction - nctFractionNew) / 4; + } + + // Divide the final by 2 so that the mouse event rate is twice the frame rate. This way + // there will always be a new pen position available, at the cost of some event overhead. + + m_ctMoveEventInterval = (m_nctMoveEventFraction + 16) >> 5; +} + +void EventMgr::ClearAppStopping() +{ + // Swallow the appStopEvent if it exists. Eating events may set fAppStopping again + // so clear it constantly + + Event evt; + while (GetEvent(&evt, 0, false)) + m_fAppStopping = false; + + // Shouldn't be needed but just in case it is set and GetEvent returns false + + m_fAppStopping = false; +} + +} // namespace wi diff --git a/game/filepdbreader.cpp b/game/filepdbreader.cpp new file mode 100644 index 0000000..e3e3d5c --- /dev/null +++ b/game/filepdbreader.cpp @@ -0,0 +1,240 @@ +// +// Reads pdbs from a filesystem in a streamed manner +// +// Note this implementation has issues on removeable media on PocketPCs. Remove the media and +// PocketPC can't require a mount sometimes, causing the game to crash. +// + +class FilePdbReader : public PdbReader +{ +public: + FilePdbReader() secPackFile; + ~FilePdbReader() secPackFile; + bool Open(char *pszFn) secPackFile; + + virtual void Close() secPackFile; + virtual bool GetRecordSize(word nRec, word *pcb) secPackFile; + virtual bool ReadRecord(word nRec, word n, word cb, void *pv) secPackFile; + virtual byte *MapRecord(word nRec, dword *pdwCookie, word *pcb = NULL) secPackFile; + virtual void UnmapRecord(word nRec, dword dwCookie) secPackFile; + +private: + bool GetRecordEntry(word nRec, int cRec, RecordEntryType *prece, dword *pcb) secPackFile; + bool GetCompressionHeader(word nRec, CompressionHeader *pcoh) secPackFile; + + FILE *m_pfil; + word m_cRecs; + word m_cMapped; + CacheHandle *m_aphcRecordData; +}; + +FilePdbReader::FilePdbReader() +{ + m_pfil = NULL; + m_cRecs = 0; + m_cMapped = 0; + m_aphcRecordData = NULL; +} + +FilePdbReader::~FilePdbReader() +{ + Assert(m_cMapped == 0); + Assert(m_pfil == NULL); + Assert(m_aphcRecordData == NULL); +} + +bool FilePdbReader::Open(char *pszFn) +{ + // Attempt to open + + Assert(m_pfil == NULL); + m_pfil = fopen(pszFn, "rb"); + if (m_pfil == NULL) + return false; + + // First read in the header + + DatabaseHdrType hdr; + int cbHdr = sizeof(hdr) - sizeof(hdr.recordList.firstEntry); + if (fread(&hdr, cbHdr, 1, m_pfil) != 1) { + fclose(m_pfil); + m_pfil = NULL; + return false; + } + m_cRecs = BigWord(hdr.recordList.numRecords); + + // Alloc cache handle array + + m_aphcRecordData = new CacheHandle[m_cRecs]; + if (m_aphcRecordData == NULL) { + fclose(m_pfil); + m_pfil = NULL; + return false; + } + memset(m_aphcRecordData, 0, sizeof(CacheHandle) * m_cRecs); + + return true; +} + +void FilePdbReader::Close() +{ + Assert(m_cMapped == 0); + Assert(m_pfil != NULL); + fclose(m_pfil); + m_pfil = NULL; + delete m_aphcRecordData; +} + +bool FilePdbReader::GetRecordSize(word nRec, word *pcb) +{ + CompressionHeader coh; + if (!GetCompressionHeader(nRec, &coh)) + return false; + *pcb = BigWord(coh.cbUncompressed); + return true; +} + +bool FilePdbReader::GetRecordEntry(word nRec, int cRec, RecordEntryType *prece, dword *pcb) +{ + // Valid? + + Assert(cRec != 0); + Assert(nRec + cRec <= m_cRecs); + if (nRec + cRec > m_cRecs) + return false; + + // Seek to record header offset + + DatabaseHdrType hdr; + hdr; + dword off = (sizeof(hdr) - sizeof(hdr.recordList.firstEntry)) + nRec * sizeof(RecordEntryType); + if (fseek(m_pfil, off, SEEK_SET) != 0) + return false; + + // End of records case? + + if (nRec + cRec == m_cRecs) { + if (fread(prece, sizeof(RecordEntryType) * cRec, 1, m_pfil) != 1) + return false; + fseek(m_pfil, 0, SEEK_END); + if (pcb != NULL) + *pcb = ftell(m_pfil) - BigDword(prece[cRec - 1].localChunkID); + return true; + } + + // 1 record case and not end of records + + if (cRec == 1) { + RecordEntryType arece[2]; + if (fread(arece, sizeof(RecordEntryType) * 2, 1, m_pfil) != 1) + return false; + *prece = arece[0]; + if (pcb != NULL) + *pcb = BigDword(arece[1].localChunkID) - BigDword(arece[0].localChunkID); + return true; + } + + // N record case and not end of records + + if (fread(prece, sizeof(RecordEntryType) * cRec, 1, m_pfil) != 1) + return false; + if (pcb != NULL) + *pcb = BigDword(prece[cRec - 1].localChunkID) - BigDword(prece[0].localChunkID); + return true; +} + +bool FilePdbReader::GetCompressionHeader(word nRec, CompressionHeader *pcoh) +{ + dword cbRecord; + RecordEntryType rece; + if (!GetRecordEntry(nRec, 1, &rece, &cbRecord)) + return false; + if (fseek(m_pfil, BigDword(rece.localChunkID), SEEK_SET) != 0) + return false; + if (fread(pcoh, sizeof(*pcoh), 1, m_pfil) != 1) + return false; + return true; +} + +bool FilePdbReader::ReadRecord(word nRec, word n, word cb, void *pv) +{ + // See if the data is cached + + Assert(nRec < m_cRecs); + CacheHandle hc = m_aphcRecordData[nRec]; + if (!gcam.IsValid(hc)) { + // Not cached, get the compression header + + CompressionHeader coh; + if (!GetCompressionHeader(nRec, &coh)) + return false; + + // If not compressed, read data straight from file + + if (!BigWord(coh.fCompressed)) { + if (n + cb > BigWord(coh.cbUncompressed)) + return false; + if (fseek(m_pfil, n, SEEK_CUR) != 0) + return false; + if (fread(pv, cb, 1, m_pfil) != 1) + return false; + return true; + } + + // It is compressed, but not cached. + + byte *pbCompressed = new byte[BigWord(coh.cbCompressed)]; + if (pbCompressed == NULL) + return false; + if (fread(pbCompressed, BigWord(coh.cbCompressed), 1, m_pfil) != 1) { + delete pbCompressed; + return false; + } + + hc = gcam.NewObject(NULL, BigWord(coh.cbUncompressed)); + if (hc == NULL) { + delete pbCompressed; + return false; + } + m_aphcRecordData[nRec] = hc; + DecompressToCache(pbCompressed, hc, &coh); + delete pbCompressed; + } + + // Read from cached data + + word cbUncompressed = gcam.GetSize(hc); + byte *pbUncompressed = (byte *)gcam.GetPtr(hc); + if (n + cb > cbUncompressed) + return false; + memcpy(pv, pbUncompressed + n, cb); + return true; +} + +byte *FilePdbReader::MapRecord(word nRec, dword *pdwCookie, word *pcb) +{ + word cbT; + if (!GetRecordSize(nRec, &cbT)) + return NULL; + if (pcb != NULL) + *pcb = cbT; + byte *pb = new byte[*pcb]; + if (pb == NULL) + return NULL; + if (!ReadRecord(nRec, 0, *pcb, pb)) { + delete pb; + return NULL; + } + *pdwCookie = (dword)pb; + m_cMapped++; + return pb; +} + +void FilePdbReader::UnmapRecord(word nRec, dword dwCookie) +{ + Assert(m_cMapped > 0); + m_cMapped--; + byte *pb = (byte *)dwCookie; + delete pb; +} + diff --git a/game/fingerhandler.cpp b/game/fingerhandler.cpp new file mode 100644 index 0000000..fdbfc9f --- /dev/null +++ b/game/fingerhandler.cpp @@ -0,0 +1,745 @@ +#include "game/ht.h" +#include "base/tick.h" + +namespace wi { + +const int SMALL_DRAG = kwcTile / 4; +const int MEDIUM_DRAG = kwcTile / 3; +const int LARGE_DRAG = kwcTile / 2; + +const long QUICK_UP_TICKS = 20; + +SimUIForm::FingerHandler::FingerHandler(SimUIForm *psui) { + m_psui = psui; + m_gidHitLast = kgidNull; + m_gidHilight = kgidNull; + m_xDownLast = 0; + m_yDownLast = 0; + m_state = FHS_NONE; + m_fShowUnitMenu = false; + m_pfrmUnitTitle = NULL; + m_ff = 0; + + // Tell the placement form to pass input through, which will + // allow the this handler to scroll the playfield. + if (gpfrmPlace != NULL) { + gpfrmPlace->SetPassOnInput(true); + } + + m_pselspr = gpsprm->CreateSelectionSprite(); + if (m_pselspr != NULL) { + m_pselspr->Show(false); + } +} + +SimUIForm::FingerHandler::~FingerHandler() { + delete m_pfrmUnitTitle; + delete m_pselspr; +} + +bool SimUIForm::FingerHandler::OnPenEvent(Event *pevt, bool fScrollOnly) { +#if 0 + switch (pevt->eType) { + case penDownEvent: + IPhone::Log("penDownEvent x=%d y=%d", pevt->x, pevt->y); + break; + + case penDownEvent2: + IPhone::Log("penDownEvent2 x=%d y=%d", pevt->x, pevt->y); + break; + + case penUpEvent: + IPhone::Log("penUpEvent x=%d y=%d", pevt->x, pevt->y); + break; + + case penUpEvent2: + IPhone::Log("penUpEvent2 x=%d y=%d", pevt->x, pevt->y); + break; + + case penMoveEvent: + IPhone::Log("penMoveEvent x=%d y=%d", pevt->x, pevt->y); + break; + + case penMoveEvent2: + IPhone::Log("penMoveEvent2 x=%d y=%d", pevt->x, pevt->y); + break; + + case penHoldEvent: + IPhone::Log("penHoldEvent x=%d y=%d", pevt->x, pevt->y); + break; + } +#endif + + switch (pevt->eType) { + case penDownEvent: + OnPenDown(pevt, fScrollOnly); + break; + + case penDownEvent2: + OnPenDown2(pevt, fScrollOnly); + break; + + case penUpEvent: + OnPenUp(pevt, fScrollOnly); + break; + + case penUpEvent2: + OnPenUp2(pevt, fScrollOnly); + break; + + case penMoveEvent: + OnPenMove(pevt); + break; + + case penMoveEvent2: + OnPenMove2(pevt); + break; + + case penHoldEvent: + OnPenHold(pevt); + break; + + default: + return false; + } + return true; +} + +void SimUIForm::FingerHandler::OnPenDown(Event *pevt, bool fScrollOnly) { + m_tDown = base::GetTickCount(); + m_xDownLast = pevt->x; + m_yDownLast = pevt->y; + m_x1 = pevt->x; + m_y1 = pevt->y; + m_ff |= kfPhFinger1Down; + + // The first finger can come down while in FHS_SELECT mode + if (m_state == FHS_SELECT) { + UpdateSelect(pevt); + return; + } + + // Not in select mode, then there should be no modes operating + Gob *pgob = m_psui->HitTestGob(pevt->x, pevt->y, true, &m_wxTarget, + &m_wyTarget, &m_fHitSurrounding); + + // Pretend no gob hittesting if it is in fog + if (pgob != NULL) { + TRect trc; + pgob->GetTileRect(&trc); + if (gsim.GetLevel()->GetFogMap()->IsCovered(&trc)) { + pgob = NULL; + } + } + m_gidHitLast = (pgob == NULL) ? kgidNull : pgob->GetId(); + + EnterNone(); + + if (fScrollOnly) { + EnterDrag(pevt); + return; + } + + if (CheckSelect(pevt)) { + EnterSelect(pevt); + return; + } + + if (pgob != NULL) { + EnterHilight(pgob); + return; + } + + // Enter drag right away to make the UI responsive + EnterDrag(pevt); +} + +void SimUIForm::FingerHandler::OnPenDown2(Event *pevt, bool fScrollOnly) { + m_x2 = pevt->x; + m_y2 = pevt->y; + m_ff |= kfPhFinger2Down; + + // fScrollOnly is set if in observe mode, or in structure placement mode + if (fScrollOnly) { + return; + } + + // May already be in FHS_SELECT when finger 2 comes down + if (m_state == FHS_SELECT) { + UpdateSelect(pevt); + return; + } + + // Otherwise, enter it + if (CheckSelect(pevt)) { + EnterSelect(pevt); + return; + } +} + +void SimUIForm::FingerHandler::OnPenMove(Event *pevt) { + m_x1 = pevt->x; + m_y1 = pevt->y; + + if (m_state == FHS_DRAG) { + UpdateDrag(pevt); + return; + } + + if (m_state == FHS_SELECT) { + UpdateSelect(pevt); + return; + } + + if (m_state == FHS_HILIGHT) { + if (CheckDragged(pevt, LARGE_DRAG)) { + EnterDrag(pevt); + } + } +} + +void SimUIForm::FingerHandler::OnPenMove2(Event *pevt) { + m_x2 = pevt->x; + m_y2 = pevt->y; + + if (m_state == FHS_SELECT) { + UpdateSelect(pevt); + return; + } +} + +void SimUIForm::FingerHandler::OnPenUp(Event *pevt, bool fScrollOnly) { + m_x1 = pevt->x; + m_y1 = pevt->y; + m_ff &= ~kfPhFinger1Down; + + // The form manager sometimes posts these when modes need to be + // gracefully broken out of when a new form shows during capture. + + if (pevt->dw == 1) { + m_ff &= ~(kfPhFinger1Down | kfPhFinger2Down); + EnterNone(); + return; + } + + bool fTarget = false; + switch (m_state) { + case FHS_DRAG: + // Try to detect a quick targetting jab and don't scroll the map + if (IsQuickUp(pevt)) { + fTarget = !CheckDragged(pevt, MEDIUM_DRAG); + } else { + fTarget = !CheckDragged(pevt, SMALL_DRAG); + } + if (fTarget) { + break; + } + // fall through + default: + // Update the position, since there may be a new position + OnPenMove(pevt); + } + + if (m_state == FHS_NONE) { + return; + } + + if (m_state == FHS_SELECT) { + if (m_ff & kfPhFinger2Down) { + UpdateSelect(pevt); + } else { + EnterNone(); + } + return; + } + + if (m_state == FHS_DRAG) { + if (gpfrmPlace != NULL && gpfrmPlace->IsBusy()) { + if (!CheckDragged(pevt, LARGE_DRAG)) { + gpfrmPlace->UpdatePosition(pevt); + } + EnterNone(); + return; + } + if (fTarget && !fScrollOnly) { + m_psui->MoveOrAttackOrSelect(NULL, m_wxTarget, m_wyTarget, + kfMasMove); + } + EnterNone(); + return; + } + + // Select or attack hilighted gob. + if (m_state == FHS_HILIGHT) { + if (m_gidHilight == kgidNull) { + EnterNone(); + return; + } + + // EnterNone before showing the menu since that is modal + Gob *pgobT = ggobm.GetGob(m_gidHilight); + bool fShowUnitMenu = m_fShowUnitMenu; + EnterNone(); + + if (pgobT == NULL || (pgobT->GetFlags() & kfGobUnit) == 0) { + return; + } + + // Structures always get their menu shown when selected this way. + if (pgobT->GetFlags() & kfGobStructure) { + fShowUnitMenu = true; + } + + // If showing the unit menu, make sure the command is selection. + // When a miner targets a friendly processor, it isn't selection for + // example. + if (fShowUnitMenu && !m_psui->IsSelectionCommand(pgobT)) { + fShowUnitMenu = false; + } + + // Select or attack + m_psui->MoveOrAttackOrSelect(pgobT, m_wxTarget, m_wyTarget, + kfMasSelect | kfMasAttack); + + // Show the menu last since it is modal + if (fShowUnitMenu) { + m_psui->ShowUnitMenu(pgobT); + } + return; + } + + // What is this state? + Assert(); + EnterNone(); +} + +void SimUIForm::FingerHandler::OnPenUp2(Event *pevt, bool fScrollOnly) { + m_ff &= ~kfPhFinger2Down; + m_x2 = pevt->x; + m_y2 = pevt->y; + + if (m_state == FHS_SELECT) { + if (m_ff & kfPhFinger1Down) { + UpdateSelect(pevt); + } else { + EnterNone(); + } + return; + } +} + +void SimUIForm::FingerHandler::OnPenHold(Event *pevt) { + if (m_state == FHS_HILIGHT) { + if (m_gidHilight == kgidNull) { + return; + } + + // If actually hit on the surrounding gob hit test area, + // and not the gob itself, switch to drag. This allows for + // precise move targeting around gobs. This is only useful + // if there are friendly units selected. + if (m_fHitSurrounding && m_psui->HasSelectedUnits()) { + // Don't enter drag for the mobile HQ, so that transforming + // it is easier. This trades of nearby targetting with easy + // of transforming. + Gob *pgob = ggobm.GetGob(m_gidHilight); + if (pgob == NULL || pgob->GetType() != kgtMobileHeadquarters) { + EnterDrag(pevt); + return; + } + } + + // Hit directly on gob. Don't change modes. This is simple + // to understand for the user. When then pen goes up, + // show the unit menu. + m_fShowUnitMenu = true; + + // In the meantime, show the unit title. This gives confirmation + // to the user that they've held the pen long enough. + ShowUnitTitle(ggobm.GetGob(m_gidHilight)); + return; + } +} + +void SimUIForm::FingerHandler::UnhilightGob() { + if (m_gidHilight != kgidNull) { + Gob *pgobT = ggobm.GetGob(m_gidHilight); + if (pgobT != NULL && (pgobT->GetFlags() & kfGobUnit)) { + ((UnitGob *)pgobT)->Hilight(false); + } + m_gidHilight = kgidNull; + } +} + +void SimUIForm::FingerHandler::EnterNone() { + m_pselspr->Show(false); + UnhilightGob(); + m_fShowUnitMenu = false; + delete m_pfrmUnitTitle; + m_pfrmUnitTitle = NULL; + m_state = FHS_NONE; +} + +void SimUIForm::FingerHandler::EnterHilight(Gob *pgob) { + Assert((pgob->GetFlags() & kfGobUnit) != 0); + ((UnitGob *)pgob)->Hilight(true); + m_gidHilight = pgob->GetId(); + m_state = FHS_HILIGHT; +} + +bool SimUIForm::FingerHandler::CheckSelect(Event *pevt) { + return (m_ff & (kfPhFinger1Down | kfPhFinger2Down)) == + (kfPhFinger1Down | kfPhFinger2Down); +} + +void SimUIForm::FingerHandler::EnterSelect(Event *pevt) { + // Leave current modes + EnterNone(); + + // Form a box around the two down points + Rect rc; + if (m_x1 < m_x2) { + rc.left = m_x1; + rc.right = m_x2; + } else { + rc.left = m_x2; + rc.right = m_x1; + } + if (m_y1 < m_y2) { + rc.top = m_y1; + rc.bottom = m_y2; + } else { + rc.top = m_y2; + rc.bottom = m_y1; + } + + // Initialize the drag rect. It expects a bottom left coordinate + // system. If the rect is rotated 90 cw, then it fits SimUI's + // coordinate system if x/y are swapped. + + DPoint pt0, pt1, pt2; + pt0.x = rc.top; + pt0.y = rc.left; + pt1.x = rc.bottom; + pt1.y = rc.left; + pt2.x = rc.bottom; + pt2.y = rc.right; + DragRect drc; + drc.Init(pt0, pt1, pt2); + + // Figure out the tracking masks + +#define kcpCloseEnough 32.0 + + DPoint ptA; + ptA.x = m_y1; + ptA.y = m_x1; + m_maskA = drc.HitTest(ptA, &m_vOffsetA); + if (m_vOffsetA.mag() > kcpCloseEnough) { + m_vOffsetA = Vec2d(0, 0); + } + + DPoint ptB; + ptB.x = m_y2; + ptB.y = m_x2; + m_maskB = drc.HitTest(ptB, &m_vOffsetB); + if (m_vOffsetB.mag() > kcpCloseEnough) { + m_vOffsetB = Vec2d(0, 0); + } + + // Show the drag rect + + m_pselspr->SetDragRect(drc); + m_pselspr->Show(true); + + // Redraw the screen so the selection updates + gevm.SetRedrawFlags(kfRedrawDirty | kfRedrawBeforeTimer); + + // Set selection based on this rect + SetSelection(); + + // Enter FHS_SELECT mode + m_state = FHS_SELECT; +} + +void SimUIForm::FingerHandler::UpdateSelect(Event *pevt) { + DPoint ptA, ptB; + ptA.x = m_y1; + ptA.y = m_x1; + ptB.x = m_y2; + ptB.y = m_x2; + + // Update tracking masks + DragRect drc = m_pselspr->GetDragRect(); + switch (pevt->eType) { + case penDownEvent: + m_maskA = drc.HitTest(ptA, &m_vOffsetA); + if (m_vOffsetA.mag() > kcpCloseEnough) { + m_vOffsetA = Vec2d(0, 0); + } + break; + + case penDownEvent2: + m_maskB = drc.HitTest(ptB, &m_vOffsetB); + if (m_vOffsetB.mag() > kcpCloseEnough) { + m_vOffsetB = Vec2d(0, 0); + } + break; + + case penUpEvent: + m_maskA = 0; + break; + + case penUpEvent2: + m_maskB = 0; + break; + } + + drc.TrackPoints(m_maskA, m_vOffsetA.add(ptA), m_maskB, m_vOffsetB.add(ptB)); + m_pselspr->SetDragRect(drc); + SetSelection(); + + // Redraw the screen so the selection updates + gevm.SetRedrawFlags(kfRedrawDirty | kfRedrawBeforeTimer); +} + +void SimUIForm::FingerHandler::CheckScroll() { + // Don't auto-scroll if not drag selecting + if (!m_pselspr->IsVisible()) { + return; + } + + // Beta feedback: users don't like auto scrolling. + // Don't auto scroll if both fingers are down. Do auto scroll if + // one finger is extending a rect - this keeps the old behavior. + + if (m_maskA != 0 && m_maskB != 0) { + return; + } + + // First see if either finger is in the "scroll border" area + + Size sizPlayfield; + ggame.GetPlayfieldSize(&sizPlayfield); + int cpBorder = PcFromUwc(kwcScrollBorderSize); + word wfAdjust = 0; + + // The portrait mode status bar on the iPhone isn't visible when + // running the game, but it still eats input! Scroll before hitting + // that. + + int cpLeftExtra = PcFromUwc(kwcScrollLeftExtra); + + // For some reason, the iphone passes a finger up before the finger + // gets close enough to the right edge of the screen to scroll within + // cpBorder. So, subtract extra from the right side. + + int cpRightExtra = cpLeftExtra; + + if (m_maskA != 0) { + if (m_x1 < cpBorder + cpLeftExtra) { + wfAdjust |= 1; + } else if (m_x1 > sizPlayfield.cx - cpBorder - cpRightExtra) { + wfAdjust |= 4; + } + if (m_y1 < cpBorder) { + wfAdjust |= 2; + } else if (m_y1 > sizPlayfield.cy - cpBorder) { + wfAdjust |= 8; + } + } + + if (m_maskB != 0) { + if (m_x2 < cpBorder + cpLeftExtra) { + wfAdjust |= 1; + } else if (m_x2 > sizPlayfield.cx - cpBorder - cpRightExtra) { + wfAdjust |= 4; + } + if (m_y2 < cpBorder) { + wfAdjust |= 2; + } else if (m_y2 > sizPlayfield.cy - cpBorder) { + wfAdjust |= 8; + } + } + if (wfAdjust == 0) { + return; + } + + // Set the new view pos + + WCoord wxView, wyView; + gsim.GetViewPos(&wxView, &wyView); + WCoord wxViewNew, wyViewNew; + wxViewNew = wxView; + wyViewNew = wyView; + if (wfAdjust & 1) { + wxViewNew -= kwcScrollStepSize; + } + if (wfAdjust & 4) { + wxViewNew += kwcScrollStepSize; + } + if (wfAdjust & 2) { + wyViewNew -= kwcScrollStepSize; + } + if (wfAdjust & 8) { + wyViewNew += kwcScrollStepSize; + } + gsim.SetViewPos(wxViewNew, wyViewNew); + + // If tracking two fingers, the rect stays where it is. It + // only needs to update the selection. + + if (m_maskA != 0 && m_maskB != 0) { + SetSelection(); + gevm.SetRedrawFlags(kfRedrawDirty | kfRedrawBeforeTimer); + return; + } + + // Otherwise tracking one finger; expand the size of the rect. + // Note the coordinates passed to scroll are rotated since that + // is how the drag rect is being maintained. + + WCoord wxViewActual, wyViewActual; + gsim.GetViewPos(&wxViewActual, &wyViewActual); + int dx = PcFromWc(wxView - wxViewActual); + int dy = PcFromWc(wyView - wyViewActual); + DragRect drc = m_pselspr->GetDragRect(); + drc.ScrollExpand(m_maskA, m_maskB, dy, dx); + m_pselspr->SetDragRect(drc); + SetSelection(); + gevm.SetRedrawFlags(kfRedrawDirty | kfRedrawBeforeTimer); +} + +bool SimUIForm::FingerHandler::IsQuickUp(Event *pevt) { + if (pevt->eType != penUpEvent) { + return false; + } + return (base::GetTickCount() - m_tDown) <= QUICK_UP_TICKS; +} + +bool SimUIForm::FingerHandler::CheckDragged(Event *pevt, int wcDrag) { + int pcDrag = PcFromWc(wcDrag); + if (abs(pevt->x - m_xDownLast) > pcDrag || + abs(pevt->y - m_yDownLast) > pcDrag) { + return true; + } + return false; +} + +void SimUIForm::FingerHandler::EnterDrag(Event *pevt) { + EnterNone(); + m_xDrag = pevt->x; + m_yDrag = pevt->y; + gsim.GetViewPos(&m_wxViewDrag, &m_wyViewDrag); + m_state = FHS_DRAG; +} + +void SimUIForm::FingerHandler::UpdateDrag(Event *pevt) { + Vec2d v(m_xDrag - pevt->x, m_yDrag - pevt->y); + v = v.scale(gnScrollSpeed); + gsim.SetViewPos(m_wxViewDrag + WcFromPc(v.dx), + m_wyViewDrag + WcFromPc(v.dy)); +} + +void SimUIForm::FingerHandler::OnPaint(DibBitmap *pbm) { +} + +void SimUIForm::FingerHandler::ShowUnitTitle(Gob *pgob) { + // Must be a unit gob + if (pgob == NULL || (pgob->GetFlags() & kfGobUnit) == 0) { + return; + } + + // Don't show this twice + if (m_pfrmUnitTitle != NULL) { + return; + } + + // Create the menu + m_pfrmUnitTitle = new UnitMenu(); + if (m_pfrmUnitTitle == NULL) { + return; + } + if (!m_pfrmUnitTitle->Init(gpmfrmm, gpiniForms, kidfUnitMenu)) { + return; + } + + // Initialize and show, non-modal + m_pfrmUnitTitle->SetOwner((UnitGob *)pgob, false); + m_pfrmUnitTitle->Show(true); + gsndm.PlaySfx(ksfxGuiFormShow); +} + +void SimUIForm::FingerHandler::SetSelection() { + if (m_pselspr == NULL) { + return; + } + + WCoord wxView, wyView; + gsim.GetViewPos(&wxView, &wyView); + + const DragRect& drc = m_pselspr->GetDragRect(); + Rect rcBounding; + drc.GetBoundingRect(&rcBounding); + + // Rotate the rect since drc is rotated + + int t = rcBounding.left; + rcBounding.left = rcBounding.top; + rcBounding.top = t; + t = rcBounding.right; + rcBounding.right = rcBounding.bottom; + rcBounding.bottom = t; + + for (Gob *pgobT = ggobm.GetFirstGob(); pgobT != NULL; + pgobT = ggobm.GetNextGob(pgobT)) { + + // Only unit gobs that are active + dword ff = pgobT->GetFlags(); + if ((ff & (kfGobUnit | kfGobActive)) != (kfGobUnit | kfGobActive)) { + continue; + } + UnitGob *punt = (UnitGob *)pgobT; + + // Player must own this unit, or the game must be in god mode + bool fSelect = true; + if (punt->GetOwner() != gpplrLocal && !gfGodMode) { + fSelect = false; + } + + // No structures unless it's a tower + if ((ff & kfGobStructure) && (punt->GetConsts()->um & kumTowers) == 0) { + fSelect = false; + } + +#if 0 + // No miners, by popular request + if (punt->GetType() == kgtGalaxMiner) { + fSelect = false; + } +#endif + + // If center not in bounding rect, discard + WPoint wptCenter; + punt->GetCenter(&wptCenter); + Point ptCenter; + ptCenter.x = PcFromWc(wptCenter.wx - wxView); + ptCenter.y = PcFromWc(wptCenter.wy - wyView); + if (!rcBounding.PtIn(ptCenter.x, ptCenter.y)) { + fSelect = false; + } + + // Center must be in drag rect + if (fSelect) { + DPoint pt; + pt.y = ptCenter.x; + pt.x = ptCenter.y; + if (!drc.PtIn(pt)) { + fSelect = false; + } + } + punt->Select(fSelect); + } +} + +} // namespace wi diff --git a/game/flickscroller.cpp b/game/flickscroller.cpp new file mode 100644 index 0000000..a0ab071 --- /dev/null +++ b/game/flickscroller.cpp @@ -0,0 +1,105 @@ +#include "ht.h" +#include + +namespace wi { + +FlickScroller::FlickScroller() +{ + m_fHasMagnitude = false; +} + +bool FlickScroller::Init(int nPen, float flMultiplier, + float flDecayPercent, float cmsDecaySpan, bool fChoose) +{ + // Get the best flick vector. + + if (!fChoose) { + gevm.GetFlickVector(nPen, &m_fliv); + } else { + FlickVector flivPreferred; + FlickVector flivAlternate; + gevm.GetFlickVector(nPen, &flivPreferred); + gevm.GetFlickVector(nPen ^ 3, &flivAlternate); + if (flivAlternate.GetMagnitude() > flivPreferred.GetMagnitude()) { + m_fliv = flivAlternate; + } else { + m_fliv = flivPreferred; + } + } + + //Trace("FlickVector: dx=%d dy=%d ms=%ld", m_fliv.dx, m_fliv.dy, m_fliv.cms); + m_msStart = HostGetMillisecondCount(); + m_flMultiplier = flMultiplier; + m_flDecayPercent = flDecayPercent; + m_cmsDecaySpan = cmsDecaySpan; + return CheckMagnitude(m_fliv.dx, m_fliv.dy); +} + +void FlickScroller::Clear() +{ + //Trace("FlickVector cleared!"); + m_fHasMagnitude = false; +} + +bool FlickScroller::GetPosition(Point *ppt) +{ + if (!m_fHasMagnitude) + return false; + + float dx = 0.0f; + float dy = 0.0f; + +#if 0 + float flDecayMultiplier = 1.0f; + + // Not exact, but close enough. + float flDecayPercent = ((float)m_fliv.cms / (float)m_cmsDecaySpan) * m_flDecayPercent; + + dword cmsDelta = HostGetMillisecondCount() - m_msStart; + int count = ((float)cmsDelta / (float)m_fliv.cms + 0.5f); + while (count-- != 0 && m_fHasMagnitude) { + float dxT = m_fliv.dx * flDecayMultiplier; + float dyT = m_fliv.dy * flDecayMultiplier; + flDecayMultiplier = flDecayMultiplier * (1.0f - flDecayPercent); + dx += dxT; + dy += dyT; + CheckMagnitude(dxT, dyT); + } + ppt->x = (int)(dx + 0.5f); + ppt->y = (int)(dy + 0.5f); +#else + + float flDecayMultiplier = 1.0f * ((float)m_cmsDecaySpan / (float)m_fliv.cms); + float flDecayPercent = m_flDecayPercent; + + dword cmsDelta = HostGetMillisecondCount() - m_msStart; + int count = ((float)cmsDelta / (float)m_cmsDecaySpan + 0.5f); + while (count-- != 0 && m_fHasMagnitude) { + float dxT = m_fliv.dx * flDecayMultiplier; + float dyT = m_fliv.dy * flDecayMultiplier; + flDecayMultiplier = flDecayMultiplier * (1.0f - flDecayPercent); + dx += dxT; + dy += dyT; + CheckMagnitude(dxT, dyT); + } + ppt->x = (int)(dx + 0.5f); + ppt->y = (int)(dy + 0.5f); +#endif + + //Trace("FS::GetPosition()-> x=%d y=%d ms=%ld", ppt->x, ppt->y, HostGetMillisecondCount()); + + return true; +} + +bool FlickScroller::HasMagnitude() +{ + return m_fHasMagnitude; +} + +bool FlickScroller::CheckMagnitude(float dx, float dy) +{ + m_fHasMagnitude = (fabsf(dx) > 0.1f || fabsf(dy) > 0.1f); + return m_fHasMagnitude; +} + +}; // namespace wi diff --git a/game/fogmap.cpp b/game/fogmap.cpp new file mode 100644 index 0000000..b90896e --- /dev/null +++ b/game/fogmap.cpp @@ -0,0 +1,804 @@ +#include "ht.h" + +namespace wi { + +FogMap::FogMap() +{ + m_pbMap = NULL; + m_panidWalls = NULL; + memset(m_aptbm, 0, sizeof(m_aptbm)); + memset(m_aptbmGalax, 0, sizeof(m_aptbmGalax)); + + // Each tile holds 4 bits. 0 is right most, 1 is top most, 2 is bottom most, + // 3 is left most + // + // Init the pattern results. The only reason a table exists + // (rather than using mask exclusively) is to disallow + // unwanted results. 1001 and 0110 are the only two illegal + // results with the current scheme. + + for (byte bSrc = 0; bSrc < 16; bSrc++) { + for (byte bDst = 0; bDst < 16; bDst++) { + byte bRes = ~(~bDst & ~bSrc) & 0xf; + if (bRes == 9 || bRes == 6) + bRes = 0; + m_mpSrcDstResult[(((~bSrc) & 0xf) << 4) | bDst] = bRes; + } + } +} + +FogMap::~FogMap() +{ + delete m_panidWalls; + delete m_pbMap; + int n; + for (n = 0; n < 16; n++) + delete m_aptbm[n]; + + for (n = 0; n < 9; n++) + delete m_aptbmGalax[n]; +} + +bool FogMap::Init(Size *psizTile, Size *psizMap) +{ + m_cxTile = psizTile->cx; + m_cyTile = psizTile->cy; + m_ctxMap = psizMap->cx / m_cxTile; + m_ctyMap = psizMap->cy / m_cyTile; + + // Alloc the map and make it all "fogged" and Galaxite-free + + int cb = m_ctxMap * m_ctyMap; + m_pbMap = new byte[cb]; + Assert(m_pbMap != NULL, "out of memory!"); + if (m_pbMap == NULL) + return false; + memset(m_pbMap, kbOpaque, cb); + + // Load the edges + + m_aptbm[15] = NULL; + m_aptbm[14] = LoadTBitmap("fog0001.tbm"); + m_aptbm[13] = LoadTBitmap("fog0010.tbm"); + m_aptbm[12] = LoadTBitmap("fog0011.tbm"); + m_aptbm[11] = LoadTBitmap("fog0100.tbm"); + m_aptbm[10] = LoadTBitmap("fog0101.tbm"); + m_aptbm[9] = NULL; + m_aptbm[8] = LoadTBitmap("fog0111.tbm"); + m_aptbm[7] = LoadTBitmap("fog1000.tbm"); + m_aptbm[6] = NULL; + m_aptbm[5] = LoadTBitmap("fog1010.tbm"); + m_aptbm[4] = LoadTBitmap("fog1011.tbm"); + m_aptbm[3] = LoadTBitmap("fog1100.tbm"); + m_aptbm[2] = LoadTBitmap("fog1101.tbm"); + m_aptbm[1] = LoadTBitmap("fog1110.tbm"); + m_aptbm[0] = LoadTBitmap("fog1111.tbm"); + + int c = 0; + int n; + for (n = 0; n < 16; n++) { + if (m_aptbm[n] == NULL) + c++; + } + if (c != 3) + return false; + + m_aptbmGalax[0] = LoadTBitmap("galax1a.tbm"); + m_aptbmGalax[1] = LoadTBitmap("galax1b.tbm"); + m_aptbmGalax[2] = LoadTBitmap("galax1c.tbm"); + m_aptbmGalax[3] = LoadTBitmap("galax2a.tbm"); + m_aptbmGalax[4] = LoadTBitmap("galax2b.tbm"); + m_aptbmGalax[5] = LoadTBitmap("galax2c.tbm"); + m_aptbmGalax[6] = LoadTBitmap("galax3a.tbm"); + m_aptbmGalax[7] = LoadTBitmap("galax3b.tbm"); + m_aptbmGalax[8] = LoadTBitmap("galax3c.tbm"); + + + for (n = 0; n < 9; n++) { + if (m_aptbmGalax[n] == NULL) { + Assert("Failed to load one of the Galaxite bitmaps"); + return false; + } + } + + // Load the wall bitmaps + + m_panidWalls = LoadAnimationData("walls.anir"); + if (m_panidWalls == NULL) { + Assert("Failed to load wall.anir"); + return false; + } + + return true; +} + +#define knVerFogMapState 4 +bool FogMap::LoadState(Stream *pstm) +{ + // Note at this point the fogmap has already been "initialized" from + // level constants so we just need to load in the contents of the map + // which includes both fog and galaxite state + + byte nVer = pstm->ReadByte(); + if (nVer != knVerFogMapState) + return false; + pstm->ReadBytesRLE(m_pbMap, m_ctxMap * m_ctyMap); + return pstm->IsSuccess(); +} + +bool FogMap::SaveState(Stream *pstm) +{ + pstm->WriteByte(knVerFogMapState); + pstm->WriteBytesRLE(m_pbMap, m_ctxMap * m_ctyMap); + return pstm->IsSuccess(); +} + +bool FogMap::IsCovered(TRect *ptrc) +{ + TRect trcMap; + trcMap.left = 0; + trcMap.top = 0; + trcMap.right = m_ctxMap; + trcMap.bottom = m_ctyMap; + TRect trcT = *ptrc; + if (!trcT.Intersect(&trcT, &trcMap)) { + return false; + } + + for (int ty = trcT.top; ty < trcT.bottom; ty++) { + for (int tx = trcT.left; tx < trcT.right; tx++) { + byte bFog = m_pbMap[ty * m_ctxMap + tx] & kbfFogMask; + if (bFog != 0) { + return false; + } + } + } + return true; +} + +void FogMap::RevealAll(UpdateMap *pupd) +{ + byte *pbT = m_pbMap; + byte *pbEnd = m_pbMap + (m_ctxMap * m_ctyMap); + while (pbT < pbEnd) { +// *pbT++ = (*pbT & ~kbfFogMask) | kbEmpty; + // BUGBUG: if we post-increment pbT as above the CodeWarrior ARM compiler + // screws it up such that the RHS pbT is one byte ahead of the LHS pbT, + // the end result being that all the Galaxite in the map gets shifted one + // tile to the left! + + *pbT = (*pbT & ~kbfFogMask) | kbEmpty; + pbT++; + } + + // Invalidate the whole update map + + pupd->InvalidateRect(); + + // Redraw the whole minimap + + gpmm->Redraw(); +} + +void FogMap::Reveal(TRect *ptrc, UpdateMap *pupd, WCoord wxView, WCoord wyView) +{ + int ctxT = ptrc->Width(); + + // Top row and corners + + byte *pbDst = &m_pbMap[(ptrc->top * m_ctxMap) + ptrc->left]; + int ctx = ctxT - 2; + *pbDst = *pbDst | (15 - 10); + pbDst++; + while (ctx-- > 0) { +// *pbDst++ = *pbDst | (15 - 2); + // BUGBUG: if we post-increment pbDst as above the CodeWarrior ARM compiler + // screws it up such that the RHS pbDst is one byte ahead of the LHS pbDst, + // the end result being that defog pattern isn't applied properly. + *pbDst = *pbDst | (15 - 2); + pbDst++; + } + *pbDst = *pbDst | (15 - 3); + + // Bottom row and corners + + pbDst = &m_pbMap[((ptrc->bottom - 1) * m_ctxMap) + ptrc->left]; + ctx = ctxT - 2; + *pbDst = *pbDst | (15 - 12); + pbDst++; + while (ctx-- > 0) { +// *pbDst++ = *pbDst | (15 - 4); + // BUGBUG: if we post-increment pbDst as above the CodeWarrior ARM compiler + // screws it up such that the RHS pbDst is one byte ahead of the LHS pbDst, + // the end result being that defog pattern isn't applied properly. + *pbDst = *pbDst | (15 - 4); + pbDst++; + } + *pbDst = *pbDst | (15 - 5); + + // Left column + + pbDst = &m_pbMap[((ptrc->top + 1) * m_ctxMap) + ptrc->left]; + ctx = ptrc->Height() - 2; + while (ctx-- > 0) { +// *pbDst++ = *pbDst | (15 - 8); + // BUGBUG: if we post-increment pbDst as above the CodeWarrior ARM compiler + // screws it up such that the RHS pbDst is one byte ahead of the LHS pbDst, + // the end result being that defog pattern isn't applied properly. + *pbDst = *pbDst | (15 - 8); + pbDst += m_ctxMap; + } + + // Right column + + pbDst = &m_pbMap[((ptrc->top + 1) * m_ctxMap) + ptrc->right - 1]; + ctx = ptrc->Height() - 2; + while (ctx-- > 0) { +// *pbDst++ = *pbDst | (15 - 1); + // BUGBUG: if we post-increment pbDst as above the CodeWarrior ARM compiler + // screws it up such that the RHS pbDst is one byte ahead of the LHS pbDst, + // the end result being that defog pattern isn't applied properly. + *pbDst = *pbDst | (15 - 1); + pbDst += m_ctxMap; + } + + // Interior + + TRect trcT; + trcT = *ptrc; + trcT.Inflate(-1, -1); + ctxT = trcT.Width(); + + pbDst = &m_pbMap[(trcT.top * m_ctxMap) + trcT.left]; + int cty = trcT.Height(); + while (cty-- > 0) { + ctx = ctxT; + while (ctx-- > 0) { + *pbDst = *pbDst | kbEmpty; + pbDst++; + } + pbDst += m_ctxMap - ctxT; + } + + // Invalidate + + gpmm->RedrawTRect(ptrc); + Rect rcT; + rcT.FromTileRect(ptrc); + int xView = PcFromWc(wxView) & 0xfffe; + int yView = PcFromWc(wyView) & 0xfffe; + rcT.Offset(-xView, -yView); + pupd->InvalidateRect(&rcT); +} + +void FogMap::Reveal(TCoord txMap, TCoord tyMap, RevealPattern *prvlp, UpdateMap *pupd, WCoord wxView, WCoord wyView) +{ + Assert(txMap >= 0 && txMap < m_ctxMap && tyMap >= 0 && tyMap < m_ctyMap); + + // RevealPattern is centered around txMap and tyMap + + int ctx = prvlp->ctx; + int cty = prvlp->cty; + int tx = txMap - ctx / 2; + int ty = tyMap - cty / 2; + + // Clip to map edges + + int txPattern = 0; + int tyPattern = 0; + + if (tx < 0) { + ctx = tx + ctx; + txPattern = -tx; + tx = 0; + } + if (ty < 0) { + cty = ty + cty; + tyPattern = -ty; + ty = 0; + } + if (tx + ctx > m_ctxMap) + ctx = m_ctxMap - tx; + if (ty + cty > m_ctyMap) + cty = m_ctyMap - ty; + + Assert(ctx > 0 && ctx <= prvlp->ctx && cty > 0 && cty <= prvlp->cty); + + // Reveal + + int cbNextSrc = prvlp->ctx - ctx; + int cbNextDst = m_ctxMap - ctx; + byte *pbDst = &m_pbMap[ty * m_ctxMap + tx]; + byte *pbSrc = &prvlp->ab[tyPattern * prvlp->ctx + txPattern]; + bool fChanged = false; + for (int tyT = ty; tyT < ty + cty; tyT++) { + for (int txT = tx; txT < tx + ctx; txT++) { + byte bDst = *pbDst; + byte bDstNew = (*pbDst & ~kbfFogMask) | m_mpSrcDstResult[((*pbSrc) << 4) | (*pbDst & kbfFogMask)]; + if (bDst != bDstNew) { + *pbDst = bDstNew; + pupd->InvalidateTile(txT, tyT); + gpmm->RedrawTile(txT, tyT); + } + pbDst++; + pbSrc++; + } + pbSrc += cbNextSrc; + pbDst += cbNextDst; + } +} + +void FogMap::Draw(DibBitmap *pbm, int xMap, int yMap, UpdateMap *pupd) +{ + Size siz; + pbm->GetSize(&siz); + Rect rcDib; + rcDib.left = 0; + rcDib.top = 0; + rcDib.right = siz.cx; + rcDib.bottom = siz.cy; + + int tx = xMap / gcxTile; + int ty = yMap / gcyTile; + int ctx = (siz.cx + (gcxTile - 1)) / gcxTile + 1; + if (tx + ctx > m_ctxMap) + ctx = m_ctxMap - tx; + int cty = (siz.cy + (gcyTile - 1)) / gcyTile + 1; + if (ty + cty > m_ctyMap) + cty = m_ctyMap - ty; + Color clrBlack = GetColor(kiclrBlack); + + bool *pfInvalid = pupd->GetInvalidMap(); + Size sizMap; + pupd->GetMapSize(&sizMap); + int cfInvalidNextScan = sizMap.cx - ctx; + Assert(sizMap.cx >= ctx && sizMap.cy >= cty); + + byte *pbDib = pbm->GetBits(); + byte *pbMapT = &m_pbMap[ty * m_ctxMap + tx]; + int cbNextScan = m_ctxMap - ctx; + int xTile = tx * gcxTile - xMap; + int yTile = ty * gcyTile - yMap; + int xTileStart = xTile; + for (int tyT = ty; tyT < ty + cty; tyT++) { + int cEmpty = 0; + int xStart; + for (int txT = tx; txT < tx + ctx; txT++) { + byte bFog = *pbMapT++ & kbfFogMask; + if (*pfInvalid++ == false) + bFog = kbEmpty; + + // Opaque? + + if (bFog == kbOpaque) { + if (cEmpty == 0) + xStart = xTile; + cEmpty++; + xTile += gcxTile; + continue; + } + + // Not opaque; fill block if there is one + + if (cEmpty != 0) { + pbm->Fill(xStart, yTile, cEmpty * gcxTile, gcyTile, clrBlack); + cEmpty = 0; +#ifdef PIL + if (gfOS5Pa1Device) + HostSoundServiceProc(); +#endif + } + + // Not opaque; draw unique tile + + TBitmap *ptbm = m_aptbm[bFog]; + if (ptbm == NULL) { + xTile += gcxTile; + continue; + } + + ptbm->BltTo(pbm, xTile, yTile); + xTile += gcxTile; + } + if (cEmpty != 0) { + pbm->Fill(xStart, yTile, cEmpty * gcxTile, gcyTile, clrBlack); + cEmpty = 0; + +#ifdef PIL + if (gfOS5Pa1Device) + HostSoundServiceProc(); +#endif + } + + pbMapT += cbNextScan; + pfInvalid += cfInvalidNextScan; + yTile += gcxTile; + xTile = xTileStart; + } +} + +byte s_abGxTranslate[] = { 0, 2, 1, 1, 0, 0, 5, 4, 4, 3, 3, 8, 7, 7, 6, 6 }; +byte s_abGxInc[] = { 0, 2, 3, 4, 5, 5, 7, 8, 9, 10, 10, 12, 13, 14, 15, 15 }; +byte s_abGxDec[] = { 0, 0, 1, 2, 3, 4, 0, 6, 7, 8, 9, 0, 11, 12, 13, 14 }; + +void FogMap::DrawGalaxite(DibBitmap *pbm, int xMap, int yMap, UpdateMap *pupd, byte *pbTrMap) +{ + Size siz; + pbm->GetSize(&siz); + Rect rcDib; + rcDib.left = 0; + rcDib.top = 0; + rcDib.right = siz.cx; + rcDib.bottom = siz.cy; + + int tx = xMap / gcxTile; + int ty = yMap / gcyTile; + int ctx = (siz.cx + (gcxTile - 1)) / gcxTile + 1; + if (tx + ctx > m_ctxMap) + ctx = m_ctxMap - tx; + int cty = (siz.cy + (gcyTile - 1)) / gcyTile + 1; + if (ty + cty > m_ctyMap) + cty = m_ctyMap - ty; + byte *pbDib = pbm->GetBits(); + byte *pbMapT = &m_pbMap[ty * m_ctxMap + tx]; + int cbNextScan = m_ctxMap - ctx; + int xTile = tx * gcxTile - xMap; + int yTile = ty * gcyTile - yMap; + int xTileStart = xTile; + + bool *pfInvalid = pupd->GetInvalidMap(); + Size sizMap; + pupd->GetMapSize(&sizMap); + int cfInvalidNextScan = sizMap.cx - ctx; + Assert(sizMap.cx >= ctx && sizMap.cy >= cty); + int nServiceSfx = 0; + + for (int tyT = ty; tyT < ty + cty; tyT++) { + for (int txT = tx; txT < tx + ctx; txT++, pbMapT++) { + byte b = *pbMapT; + if (*pfInvalid++ == false) { + xTile += gcxTile; + continue; + } + + // Is no Galaxite/Wall present or is this tile completely obscured by fog? + + byte *ptt = &pbTrMap[pbMapT - m_pbMap]; + if (((b & kbfGalaxiteMask) == 0 && *ptt != kttWall) || IsFogOpaque(b)) { + xTile += gcxTile; + continue; + } + + // Galaxite? + + if (HasGalaxite(b)) { + // Galaxite + + TBitmap *ptbm = m_aptbmGalax[s_abGxTranslate[(b & kbfGalaxiteMask) >> kcGalaxiteShift]]; + ptbm->BltTo(pbm, xTile, yTile); + +#ifdef PIL + if (gfOS5Pa1Device) { + nServiceSfx++; + if ((nServiceSfx & 31) == 0) + HostSoundServiceProc(); + } +#endif + + } + + // Wall? + + if (*ptt == kttWall) { + +#define HasWall(tt) ((tt) == kttWall) + + // Check neighbor cells for walls to decide what shape this + // segment should take. + + int ifrm = 0; + if (tyT > 0) { + if (HasWall(*(ptt - m_ctxMap))) + ifrm |= 1; + } + if (txT < m_ctxMap - 1) { + if (HasWall(*(ptt + 1))) + ifrm |= 2; + } + if (tyT < m_ctyMap - 1) { + if (HasWall(*(ptt + m_ctxMap))) + ifrm |= 4; + } + if (txT > 0) { + if (HasWall(*(ptt - 1))) + ifrm |= 8; + } + + // The kwfWallMask >> 1 test makes walls with less than half their health + // draw from the 'damaged' strip. + +// m_panidWalls->DrawFrame(w <= (kwfWallMask >> 1) ? 1 : 0, ifrm, pbm, xTile, yTile, ksideNeutral); + m_panidWalls->DrawFrame(0, ifrm, pbm, xTile, yTile, ksideNeutral); + +#ifdef PIL + if (gfOS5Pa1Device) { + nServiceSfx++; + if ((nServiceSfx & 31) == 0) + HostSoundServiceProc(); + } +#endif + } + + xTile += gcxTile; + } + + pfInvalid += cfInvalidNextScan; + pbMapT += cbNextScan; + yTile += gcxTile; + xTile = xTileStart; + } +} + +void FogMap::SetGalaxite(int nGx, TCoord tx, TCoord ty) +{ + Assert(tx >= 0 && ty >= 0 && tx < m_ctxMap && ty < m_ctyMap); + + byte *pb = &m_pbMap[(ty * m_ctxMap) + tx]; + *pb = (*pb & ~kbfGalaxiteMask) | (nGx << kcGalaxiteShift); +} + +int FogMap::GetGalaxite(TCoord tx, TCoord ty) +{ + // Any attempt to look for Galaxite off the edge of the map returns none + + if (tx < 0 || ty < 0 || tx >= m_ctxMap || ty >= m_ctyMap) + return 0; + + byte *pb = &m_pbMap[(ty * m_ctxMap) + tx]; + int nGx = (*pb & kbfGalaxiteMask) >> kcGalaxiteShift; + +#ifdef MP_DEBUG +// MpTrace("GetGalaxite:%d,%d amount %d", tx, ty, nGx); +#endif + + return nGx; +} + +void FogMap::IncGalaxite(TCoord tx, TCoord ty) +{ + Assert(tx >= 0 && ty >= 0 && tx < m_ctxMap && ty < m_ctyMap); + + byte *pb = &m_pbMap[(ty * m_ctxMap) + tx]; + *pb = (*pb & ~kbfGalaxiteMask) | (s_abGxInc[(*pb & kbfGalaxiteMask) >> kcGalaxiteShift] << kcGalaxiteShift); + + // Cause this tile to redraw + + if (gptrcMapOpaque == NULL || !gptrcMapOpaque->PtIn(tx, ty)) + gpupdSim->InvalidateTile(tx, ty); + if (gpmm != NULL) + gpmm->RedrawTile(tx, ty); +} + +bool FogMap::DecGalaxite(TCoord tx, TCoord ty) +{ + Assert(tx >= 0 && ty >= 0 && tx < m_ctxMap && ty < m_ctyMap); + + byte *pb = &m_pbMap[(ty * m_ctxMap) + tx]; + byte bNew = s_abGxDec[(*pb & kbfGalaxiteMask) >> kcGalaxiteShift] << kcGalaxiteShift; + *pb = (*pb & ~kbfGalaxiteMask) | bNew; +#ifdef MP_DEBUG +// MpTrace("DecGalaxite:%d,%d now %d", tx, ty, bNew >> kcGalaxiteShift); +#endif + + // Cause this tile to redraw + + if (gptrcMapOpaque == NULL || !gptrcMapOpaque->PtIn(tx, ty)) + gpupdSim->InvalidateTile(tx, ty); + if (gpmm != NULL) + gpmm->RedrawTile(tx, ty); + + return bNew != 0; +} + +bool FogMap::FindNearestGalaxite(TCoord txOrigin, TCoord tyOrigin, TPoint *ptpt, bool fIgnoreFog) +{ + Assert(txOrigin >= 0 && tyOrigin >= 0 && txOrigin < m_ctxMap && tyOrigin < m_ctyMap); + +#if 1 + TPoint *atptGx = (TPoint *)gpbScratch; +#else + TPoint atptT[sizeof(TPoint) * ktcMax * 4]; // 1024 bytes, these days + TPoint *atptGx = atptT; +#endif + TPoint *ptptGx = atptGx; + int ctptGx; + + int nMax1 = _max((int)txOrigin, m_ctxMap - txOrigin); + int nMax2 = _max((int)tyOrigin, m_ctyMap - tyOrigin); + int nRadiusMax = _max(nMax1, nMax2); + + for (int nRadius = 1; nRadius < nRadiusMax; nRadius++) { + + // We treat Galaxite directly under the Miner as being + // the equivalent of 2.5 tiles away. + + if (nRadius == 3) { + byte *pb = &m_pbMap[(tyOrigin * m_ctxMap) + txOrigin]; + if (HasGalaxite(*pb)) { + ptpt->tx = txOrigin; + ptpt->ty = tyOrigin; +// MpTrace(" -- found %d, %d", txOrigin, tyOrigin); + return true; + } + } + + // look for galaxite across the top + + TCoord txMin = txOrigin - nRadius; + if (txMin < 0) + txMin = 0; + TCoord txMax = txOrigin + nRadius; + if (txMax > m_ctxMap) + txMax = m_ctxMap; + + TCoord tx, ty; + ty = tyOrigin - nRadius; + if (ty >= 0) { + byte *pb = &m_pbMap[(ty * m_ctxMap) + txMin]; + for (tx = txMin; tx < txMax; tx++, pb++) { + if (!HasGalaxite(*pb)) // Galaxite? + continue; // no + if (IsFogOpaque(*pb) && !fIgnoreFog) // Fogged? + continue; // yes +// MpTrace(" t %d, %d, 0x%lx", tx, ty, ptptGx); + ptptGx->tx = tx; + ptptGx->ty = ty; + ptptGx++; + } + } + + // look for galaxite across the bottom + + txMax += 1; + if (txMax > m_ctxMap) + txMax = m_ctxMap; + txMin = txOrigin - nRadius + 1; + if (txMin < 0) + txMin = 0; + + ty = tyOrigin + nRadius; + if (ty < m_ctyMap) { + byte *pb = &m_pbMap[(ty * m_ctxMap) + txMin]; + for (tx = txMin; tx < txMax; tx++, pb++) { + if (!HasGalaxite(*pb)) + continue; + if (IsFogOpaque(*pb) && !fIgnoreFog) + continue; +// MpTrace(" b %d, %d, 0x%lx", tx, ty, ptptGx); + ptptGx->tx = tx; + ptptGx->ty = ty; + ptptGx++; + } + } + + // look for galaxite down the right side + + TCoord tyMin = tyOrigin - nRadius; + if (tyMin < 0) + tyMin = 0; + TCoord tyMax = tyOrigin + nRadius; + if (tyMax > m_ctyMap) + tyMax = m_ctyMap; + + tx = txOrigin + nRadius; + if (tx < m_ctxMap) { + byte *pb = &m_pbMap[(tyMin * m_ctxMap) + tx]; + for (ty = tyMin; ty < tyMax; ty++, pb += m_ctxMap) { + if (!HasGalaxite(*pb)) + continue; + if (IsFogOpaque(*pb) && !fIgnoreFog) + continue; +// MpTrace(" r %d, %d, 0x%lx", tx, ty, ptptGx); + ptptGx->tx = tx; + ptptGx->ty = ty; + ptptGx++; + } + } + + // look for galaxite down the left side + + tyMin = tyOrigin - nRadius + 1; + if (tyMin < 0) + tyMin = 0; + tyMax += 1; + if (tyMax > m_ctyMap) + tyMax = m_ctyMap; + + tx = txOrigin - nRadius; + if (tx >= 0) { + byte *pb = &m_pbMap[(tyMin * m_ctxMap) + tx]; + for (ty = tyMin; ty < tyMax; ty++, pb += m_ctxMap) { + if (!HasGalaxite(*pb)) + continue; + if (IsFogOpaque(*pb) && !fIgnoreFog) + continue; +// MpTrace(" l %d, %d, 0x%lx", tx, ty, ptptGx); + ptptGx->tx = tx; + ptptGx->ty = ty; + ptptGx++; + } + } + + // If Galaxite has been found at this radius randomly pick one + // of the found locations and return it. + + if (ptptGx != atptGx) { + ctptGx = ptptGx - atptGx; + int i = GetRandom() % ctptGx; + *ptpt = atptGx[i]; +// MpTrace(" -- found %d, %d, 0x%lx [%d of %d]", ptpt->tx, ptpt->ty, &atptGx[i], i, ctptGx); + return true; + } + } + +// MpTrace(" -- found none"); + return false; +} + +// +// Wall methods +// + +#if 0 +int FogMap::GetWallHealth(TCoord tx, TCoord ty) +{ + // Any attempt to look for wall off the edge of the map returns none + + if (tx < 0 || ty < 0 || tx >= m_ctxMap || ty >= m_ctyMap) + return 0; + + word *pw = &m_pwMap[(ty * m_ctxMap) + tx]; + return (*pw & kwfWallMask) >> kcWallShift; +} + +void FogMap::SetWallHealth(int nHealth, TCoord tx, TCoord ty) +{ + // If state changes between existing and not existing then the N/S/E/W + // neighbor cells need to be invalidated if they also contain a wall + // segment. + + Assert(tx >= 0 && ty >= 0 && tx < m_ctxMap && ty < m_ctyMap); + + word *pw = &m_pwMap[(ty * m_ctxMap) + tx]; + word wOld = *pw; + word wNew = (wOld & ~kwfWallMask) | (word)(nHealth << kcWallShift); + + if (wNew != wOld) { + *pw = wNew; + + // UNDONE: whoa! Spaghetti! FogMap should not be reaching out to sim/level/etc + TerrainMap *ptrmap = gsim.GetLevel()->GetTerrainMap(); + if (nHealth == 0) + ptrmap->ClearFlags(tx, ty, 1, 1, kbfStructure); + else + ptrmap->SetFlags(tx, ty, 1, 1, kbfStructure); + +#if 0 + // UNDONE: right now there is no dynamic changing of a wall's health so we don't + // need this stuff. If we allow damaging/destroying walls or give the player the + // ability to create walls we'll need to deal with this. + + // Cause this tile to redraw + + if (gptrcMapOpaque == NULL || !gptrcMapOpaque->PtIn(tx, ty)) + gpupdSim->InvalidateTile(tx, ty); + if (gpmm != NULL) + gpmm->RedrawTile(tx, ty); + + // UNDONE: deal with neighbors +#endif + } +} +#endif + +} // namespace wi diff --git a/game/font.cpp b/game/font.cpp new file mode 100644 index 0000000..41f6541 --- /dev/null +++ b/game/font.cpp @@ -0,0 +1,388 @@ +#include "ht.h" + +namespace wi { + +#define kcchFont 256 + +Font *LoadFont(char *pszFont) +{ + Font *pfnt = new Font; + Assert(pfnt != NULL, "out of memory!"); + if (pfnt == NULL) + return NULL; + if (!pfnt->Load(pszFont)) { + delete pfnt; + return NULL; + } + return pfnt; +} + +Font::Font() +{ + m_pfnth = NULL; + m_mpchpbCodeEven = NULL; + m_mpchpbCodeOdd = NULL; + m_nGlyphOverlap = 0; + m_nLineOverlap = 0; + m_cxEllipsis = 0; +} + +Font::~Font() +{ + if (m_pfnth != NULL) + gpakr.UnmapFile(&m_fmap); + + for (int ch = 0; ch < kcchFont; ch++) { + if (m_mpchpbCodeEven != NULL) { + if (m_mpchpbCodeEven[ch] != NULL) + gmmgr.FreePtr(m_mpchpbCodeEven[ch]); + } + if (m_mpchpbCodeOdd != NULL) { + if (m_mpchpbCodeOdd[ch] != NULL) + gmmgr.FreePtr(m_mpchpbCodeOdd[ch]); + } + } + if (m_mpchpbCodeEven != NULL) + gmmgr.FreePtr(m_mpchpbCodeEven); + if (m_mpchpbCodeOdd != NULL) + gmmgr.FreePtr(m_mpchpbCodeOdd); +} + +bool Font::Load(char *pszFont) +{ + m_pfnth = (FontHeader *)gpakr.MapFile(pszFont, &m_fmap); + if (m_pfnth == NULL) + return false; + + byte *apbT[kcchFont]; + memset(apbT, 0, sizeof(apbT)); + + m_mpchpbCodeEven = (byte **)gmmgr.AllocPtr(sizeof(byte *) * kcchFont); + if (m_mpchpbCodeEven == NULL) + return false; + gmmgr.WritePtr(m_mpchpbCodeEven, 0, apbT, sizeof(apbT)); + + m_mpchpbCodeOdd = (byte **)gmmgr.AllocPtr(sizeof(byte *) * kcchFont); + if (m_mpchpbCodeOdd == NULL) + return false; + gmmgr.WritePtr(m_mpchpbCodeOdd, 0, apbT, sizeof(apbT)); + + m_cxEllipsis = GetTextExtent("..."); + + return true; +} + +int Font::GetTextExtent(const char *psz) +{ + int cx = 0; + while (*psz != 0) { + byte ch = *psz++; + cx += m_pfnth->acxChar[ch] - m_nGlyphOverlap; + } + + // Shadow allows 1 pixel overlap but the last char doesn't overlap + + return cx + m_nGlyphOverlap; +} + +int Font::GetTextExtent(const char *psz, int cch) +{ + int cx = 0; + while (cch-- > 0) { + byte ch = *psz++; + cx += m_pfnth->acxChar[ch] - m_nGlyphOverlap; + } + + // Shadow allows 1 pixel overlap but the last char doesn't overlap + + return cx + m_nGlyphOverlap; +} + +int Font::CalcMultilineHeight(char *psz, int cxMultiline) +{ + int cy = 0; + char *pszNext = psz; + while (pszNext != NULL) { + char *pszStart = pszNext; + CalcBreak(cxMultiline, &pszNext); + cy += BigWord(m_pfnth->cy) - m_nLineOverlap; + } + + return cy; +} + +void Font::DrawText(DibBitmap *pbm, char *psz, int x, int y, int cx, int cy, + bool fEllipsis) +{ + int cyFont = BigWord(m_pfnth->cy) - m_nLineOverlap; + int cyT = cyFont; + char *pszNext = psz; + while (pszNext != NULL) { + if (cy != -1 && cyT > cy) + return; + char *pszStart = pszNext; + int cch = CalcBreak(cx, &pszNext); + if (fEllipsis && pszNext != NULL && cy != -1 && cyT + cyFont > cy) { + DrawTextWithEllipsis(pbm, pszStart, cch, x, y, cx, true); + } else if (fEllipsis && pszNext == NULL) { + DrawTextWithEllipsis(pbm, pszStart, cch, x, y, cx, false); + } else { + DrawText(pbm, pszStart, x, y, cch); + } + y += cyFont; + cyT += cyFont; + } +} + +void Font::DrawTextWithEllipsis(DibBitmap *pbm, char *psz, int cch, int x, + int y, int cx, bool fForce) +{ + if (!fForce) { + // If not being forced and text fits, draw it without ellipsis + if (GetTextExtent(psz) < cx) { + DrawText(pbm, psz, x, y, strlen(psz)); + return; + } + } + + // Do a binary search to find out where the ellipsis is needed + + char szT[256]; + int imin = 0; + + // Convert to index and ensure room for ellipsis + int imax = _min(cch - 1, (int)sizeof(szT) - 3 - 1); + if (imax < 0) { + imax = 0; + } + int icur = imax; + int ifit = imax; + while (true) { + int icurT = imin + (imax - imin) / 2; + if (icurT == icur) { + break; + } + icur = icurT; + int cxT = GetTextExtent(psz, icur + 1); + if (cxT >= cx - m_cxEllipsis) { + imax = icur - 1; + } else { + ifit = icur; + imin = icur + 1; + } + } + strncpyz(szT, psz, ifit + 2); // convert to count, add one for 0 + strcat(szT, "..."); // this fits without checks + DrawText(pbm, szT, x, y, strlen(szT)); +} + +#define IsBreakingChar(ch) ((ch) == ' ' || (ch) == '\t') + +char *Font::FindNextNonBreakingChar(char *psz) +{ + while (*psz != 0) { + if (!IsBreakingChar(*psz)) + return psz; + psz++; + } + return NULL; +} + +int Font::CalcBreak(int cx, char **ppsz, bool fChop) +{ + char *pchBreakAfter = NULL; + int cchBreak; + int cxT = 0; + char *pchT = *ppsz; + int cch = 0; + bool fFoundBreak = false; + + while (*pchT != 0) { + char ch = *pchT; + + // Handle any combo of \n, \r, \n\r, or \r\n + + switch (ch) { + case '\n': + pchT++; + if (*pchT == '\r') + pchT++; + *ppsz = FindNextNonBreakingChar(pchT); + return cch; + + case '\r': + pchT++; + if (*pchT == '\n') + pchT++; + *ppsz = FindNextNonBreakingChar(pchT); + return cch; + } + + // Otherwise if breaking char, remember break point + + if (IsBreakingChar(*pchT)) { + fFoundBreak = true; + pchBreakAfter = FindNextNonBreakingChar(pchT); + cchBreak = cch; + } + + // At right edge yet? + + int cxChar = m_pfnth->acxChar[(byte)*pchT] - m_nGlyphOverlap; + if (cxT + cxChar > cx) { + // If last break exists, use it. Return pointer skips past break char + + if (fFoundBreak) { + *ppsz = pchBreakAfter; + return cchBreak; + } + + // If no last break, sometimes callers want word chopping, sometimes not + + if (!fChop) + return 0; + + // No last break; Scan forward for a breaking char so we know where to start + // the next line + + while (*pchT != 0) { + // Start the next past the break; only draw what fits in cx for this line + + if (IsBreakingChar(*pchT)) { + *ppsz = FindNextNonBreakingChar(pchT); + return cch; + } + + // If carriage return, start at next char + + if (ch == '\n') { + *ppsz = pchT + 1; + return cch; + } + + pchT++; + } + + // At end of line with no break. Return length that fits in cx, and NULL to indicate done. + + if (*pchT == 0) { + *ppsz = NULL; + return cch; + } + } + + // Add char; next char + + cxT += cxChar; + cch++; + pchT++; + } + + // Done without hitting edge. Return length and NULL to indicate done + + *ppsz = NULL; + return cch; +} + +int Font::DrawText(DibBitmap *pbm, char *psz, int x, int y, int cch, dword *mpscaiclr) +{ +#ifdef DEBUG + for (int ichT = 0; ichT < cch; ichT++) + Assert((word)psz[ichT] < (word)kcchFont); +#endif + + if (cch == -1) + cch = strlen(psz); + + // Clip entire line of text first. + + Size siz; + pbm->GetSize(&siz); + + // Top clip + + if (y < 0) + return 0; + if (y + BigWord(m_pfnth->cy) > siz.cy) + return 0; + + // Don't include x clipped chars + // Left clip + + int ich = 0; + int xT = x; + char *pszT; + for (pszT = psz; pszT - psz < cch; pszT++) { + if (xT < 0) { + ich++; + xT += m_pfnth->acxChar[(byte)*pszT] - m_nGlyphOverlap; + continue; + } + break; + } + int xDst = xT; + + // Right clip + + int cchT = 0; + for (; pszT - psz < cch; pszT++) { + int cx = m_pfnth->acxChar[(byte)*pszT] - m_nGlyphOverlap; + if (xT + cx <= siz.cx) { + cchT++; + xT += cx; + continue; + } + break; + } + if (cchT == 0) + return 0; + + cch = cchT; + + // Draw + + xT = xDst; + byte *pbDst = pbm->GetBits() + (long)y * siz.cx + xT; + char *pszMax = &psz[ich + cch]; + + for (pszT = &psz[ich]; pszT < pszMax; pszT++) { + char ch = *pszT; + int cxChar = m_pfnth->acxChar[(byte)ch]; + if (cxChar == 0) + continue; + + byte *pbDraw; + if (xT & 1) { + pbDraw = m_mpchpbCodeOdd[(byte)ch]; + } else { + pbDraw = m_mpchpbCodeEven[(byte)ch]; + } + + if (pbDraw == NULL) { + ScanData *psd = (ScanData *)(((byte *)m_pfnth) + + BigWord(m_pfnth->mpchibsd[(byte)*pszT])); + word cb = Compile8Thunk(gpbScratch, psd, xT & 1); + byte *pbT = (byte *)gmmgr.AllocPtr(cb); + if (pbT != NULL) { + gmmgr.WritePtr(pbT, 0, gpbScratch, cb); + if (xT & 1) { + gmmgr.WritePtr(m_mpchpbCodeOdd, (byte)ch * sizeof(byte *), + &pbT, sizeof(byte *)); + } else { + gmmgr.WritePtr(m_mpchpbCodeEven, (byte)ch * sizeof(byte *), + &pbT, sizeof(byte *)); + } + } + pbDraw = gpbScratch; + } + + DrawDispatchThunk(pbDraw, NULL, pbDst, siz.cx - cxChar, mpscaiclr, + gmpiclriclrShadow); + pbDst += cxChar - m_nGlyphOverlap; + xT += cxChar - m_nGlyphOverlap; + } + + return xT - xDst; +} + +} // namespace wi diff --git a/game/form.cpp b/game/form.cpp new file mode 100644 index 0000000..d9865ee --- /dev/null +++ b/game/form.cpp @@ -0,0 +1,1119 @@ +#include "ht.h" + +namespace wi { + +// +// Form +// + +Form::Form() +{ + m_iclrBack = kiclrFormBackground; + m_pctlCapture = NULL; + m_pfrmm = NULL; + m_ptbm = NULL; + m_wf = 0; + m_idf = 0; + m_idcDefault = 0; + m_idcLast = 0; + m_nResult = 0; + m_cctl = 0; + memset(m_apctl, 0, sizeof(m_apctl)); + m_rc.SetEmpty(); + m_pUserData = NULL; +} + +Form::~Form() +{ + // Delete controls + + for (int n = 0; n < m_cctl; n++) + delete m_apctl[n]; + + // Delete TBitmap + + delete m_ptbm; + + // Remove this form + + if (m_pfrmm != NULL) + m_pfrmm->RemoveForm(this); + + if (m_wf & kfFrmHasPalette) + gpakr.UnmapFile(&m_fmapPalette); + if (m_wf & kfFrmHasShadowMap) + gpakr.UnmapFile(&m_fmapShadowMap); + + // Mark deleted for debugging purposes + + m_wf |= kfFrmDeleted; +} + +bool Form::Init(FormMgr *pfrmm, IniReader *pini, word idf) +{ + // Initialize form properties + + char szForm[10]; + itoa(idf, szForm, 10); + if (!InitFromProperties(pfrmm, idf, pini, szForm)) + return false; + + // Step through and create controls + + char szProp[32]; + FindProp find; + while (pini->FindNextProperty(&find, szForm, szProp, sizeof(szProp))) { + // Instantiate and initialize control + + Control *pctl = NULL; + if (strcmp(szProp, "BUTTON") == 0) { + pctl = new ButtonControl; + } + else if (strcmp(szProp, "RADIOBUTTONBAR") == 0) { + pctl = new RadioButtonBarControl; + } + else if (strcmp(szProp, "LABEL") == 0) { + pctl = new LabelControl; + } + else if (strcmp(szProp, "ECOMTEXT") == 0) { + pctl = new EcomTextControl; + } + else if (strcmp(szProp, "CHECKBOX") == 0) { + pctl = new CheckBoxControl; + } + else if (strcmp(szProp, "BITMAP") == 0) { + pctl = new BitmapControl; + } +#if 0 // not used now + else if (strcmp(szProp, "PRESETBUTTON") == 0) { + pctl = new PresetButtonControl; + } +#endif + else if (strcmp(szProp, "EDIT") == 0) { + pctl = new EditControl; + } + else if (strcmp(szProp, "LIST") == 0) { + pctl = new ListControl; + } + else if (strcmp(szProp, "ANIMLIST") == 0) { + pctl = new BuildListControl; + } + else if (strcmp(szProp, "GRAFFITISCROLL") == 0) { + pctl = new GraffitiScrollControl; + } + else if (strcmp(szProp, "SILKBUTTON") == 0) { + pctl = new SilkButtonControl; + } + else if (strcmp(szProp, "SLIDER") == 0) { + pctl = new SliderControl; + } + else if (strcmp(szProp, "MINIMAP") == 0) { + pctl = new MiniMapControl; + } + else if (strcmp(szProp, "PIPMETER") == 0) { + pctl = new PipMeterControl; + } + else if (strcmp(szProp, "DAMAGEMETER") == 0) { + pctl = new DamageMeterControl; + } + else if (strcmp(szProp, "HELP") == 0) { + pctl = new HelpControl; + } + else if (strcmp(szProp, "ALERT") == 0) { + pctl = new AlertControl; + } + else if (strcmp(szProp, "CREDITS") == 0) { + pctl = new CreditsControl; + } + else if (strcmp(szProp, "POWER") == 0) { + pctl = new PowerControl; + } + else { + continue; + } + Assert(pctl != NULL, "out of memory!"); + if (pctl == NULL) + return false; + if (!pctl->Init(this, pini, &find)) + return false; + + // Add it to the list for this form + + Assert(m_cctl < kcControlsMax); + m_apctl[m_cctl++] = pctl; + } + + // Add the form to the form mgr + + m_pfrmm->AddForm(this); + + // Show the form. This is a asynchronous event, which'll give time + // to the derived form to initialize. + + Show(true); + return true; +} + +bool Form::InitFromProperties(FormMgr *pfrmm, word idf, IniReader *pini, char *pszForm) +{ + // FORM=(x y cx cy) idcDefault + + int x, y, cx, cy; + char szBitmap[kcbFilename]; + char szPalette[kcbFilename]; + int idcDefault; + char szArgs[3][32]; + int cArgs = pini->GetPropertyValue(pszForm, "FORM", "(%d %d %d %d) %d %s %s %s", + &x, &y, &cx, &cy, &idcDefault, szArgs[0], szArgs[1], szArgs[2]); + if (cArgs < 5) + return false; + m_idcDefault = (word)idcDefault; + + // Scale form coordinates depending on the resolution of the device. + + bool fCenter = false; + bool fScale = true; + bool fTopMost = false; + + int csz = _min((int)ARRAYSIZE(szArgs), cArgs - 5); + for (int n = 0; n < csz; n++) { + if (strcmp(szArgs[n], "noscale") == 0) { + fScale = false; + continue; + } + if (strcmp(szArgs[n], "center") == 0) { + fCenter = true; + continue; + } + if (strcmp(szArgs[n], "topmost") == 0) { + fTopMost = true; + continue; + } + } + + if (fScale) { + m_wf |= kfFrmScaleCoords; + x = PcFromFc(x); + y = PcFromFc(y); + cx = PcFromFc(cx); + cy = PcFromFc(cy); + } + + if (fCenter) { + DibBitmap *pbm = pfrmm->GetDib(); + Size siz; + pbm->GetSize(&siz); + x = ((siz.cx - cx) / 2) & ~1; + y = (siz.cy - cy) / 2; + } + + if (fTopMost) + m_wf |= kfFrmTopMost; + + cArgs = pini->GetPropertyValue(pszForm, "FORMBITMAP", "%s", szBitmap); + if (cArgs == 0) + szBitmap[0] = 0; + cArgs = pini->GetPropertyValue(pszForm, "FORMPALETTE", "%s", szPalette); + if (cArgs == 0) + szPalette[0] = 0; + cArgs = pini->GetPropertyValue(pszForm, "FORMBACKCOLOR", "%d", &m_iclrBack); + if (cArgs == 0) + m_iclrBack = -1; + + // Fix the size of the form to the bitmap width and height + + m_ptbm = NULL; + m_rc.Set(x, y, x + cx, y + cy); + if (szBitmap[0] != 0) { + m_ptbm = LoadTBitmap(szBitmap); + if (m_ptbm == NULL) + return false; + Size siz; + m_ptbm->GetSize(&siz); + m_rc.Set(x, y, x + siz.cx, y + siz.cy); + } + m_pfrmm = pfrmm; + m_idf = idf; + + // Load and set the form Palette, if any + + if (szPalette[0] != 0) { + Palette *ppal = (Palette *)gpakr.MapFile(szPalette, &m_fmapPalette); + Assert(ppal != NULL); + if (ppal == NULL) + return false; + m_wf |= kfFrmHasPalette; + + // Select palette + + SetHslAdjustedPalette(ppal, gnHueOffset, gnSatMultiplier, gnLumOffset); + + // Load and set a corresponding shadow map if it exists + + strcat(szPalette, ".shadowmap"); + gmpiclriclrShadow = (byte *)gpakr.MapFile(szPalette, &m_fmapShadowMap); + if (gmpiclriclrShadow != NULL) + m_wf |= kfFrmHasShadowMap; + } + + return true; +} + +bool Form::AddControl(Control *pctl) +{ + // Add it to the list for this form + + // bottommost + + Assert(m_cctl < kcControlsMax); + m_apctl[m_cctl++] = pctl; + return true; +} + +void Form::SetUserDataPtr(void *pUserData) +{ + m_pUserData = pUserData; +} + +void *Form::GetUserDataPtr() +{ + return m_pUserData; +} + +word Form::GetId() +{ + return m_idf; +} + +word Form::GetFlags() +{ + return m_wf; +} + +void Form::SetFlags(word wf) +{ + m_wf = wf; +} + +void Form::Show(bool fShow) +{ + if (fShow) { + if (m_wf & kfFrmVisible) + return; + m_wf |= kfFrmVisible; + InvalidateRect(NULL); + } else { + if (!(m_wf & kfFrmVisible)) + return; + InvalidateRect(NULL); + m_wf &= ~kfFrmVisible; + } +} + +void Form::OnUpdateMapInvalidate(UpdateMap *pupd, Rect *prcOpaque) +{ + for (int n = 0; n < m_cctl; n++) { + if (!(m_apctl[n]->m_wf & kfCtlVisible)) + continue; + + Rect rcT; + rcT = m_apctl[n]->m_rc; + rcT.Offset(m_rc.left, m_rc.top); + + // Only mark the control invalid + + if (!prcOpaque->RectIn(&rcT)) { + if (pupd->IsRectInvalidAndTrackDamage(&rcT)) + m_apctl[n]->SetFlags(m_apctl[n]->GetFlags() | kfCtlRedraw); + } + } +} + +void Form::FrameStart() +{ +} + +void Form::FrameComplete() +{ +} + +void Form::OnPaintBackground(DibBitmap *pbm, UpdateMap *pupd) +{ + // Draw invalid parts of the background + + if (m_iclrBack == -1) + return; + FillHelper(pbm, pupd, &m_rc, GetColor(m_iclrBack)); +} + +void Form::OnPaint(DibBitmap *pbm) +{ + if (m_ptbm != NULL) + m_ptbm->BltTo(pbm, m_rc.left, m_rc.top); +} + +void Form::OnPaintControls(DibBitmap *pbm, UpdateMap *pupd) +{ + HostSoundServiceProc(); + int nServiceSfx = 0; + for (int n = 0; n < m_cctl; n++) { + if ((m_apctl[n]->m_wf & (kfCtlVisible | kfCtlRedraw)) != (kfCtlVisible | kfCtlRedraw)) + continue; + nServiceSfx++; + if ((nServiceSfx & 3) == 0) + HostSoundServiceProc(); + m_apctl[n]->OnPaint(pbm); + m_apctl[n]->SetFlags(m_apctl[n]->GetFlags() & ~kfCtlRedraw); + } +} + +void Form::ScrollInvalidate(UpdateMap *pupd) +{ + // Invalidate the children due to scrolling + + for (int n = 0; n < m_cctl; n++) + m_apctl[n]->Invalidate(); +} + +// for override +void Form::OnScroll(int dx, int dy) +{ +} + +bool Form::EventProc(Event *pevt) +{ + switch (pevt->eType) { + case penHoverEvent: + case penDownEvent: + case penMoveEvent: + case penUpEvent: + case penHoldEvent: + case penDownEvent2: + case penMoveEvent2: + case penUpEvent2: + return OnPenEvent(pevt); + } + return false; +} + +Control *Form::GetControlPtr(word idc) +{ + for (int n = 0; n < m_cctl; n++) { + if (m_apctl[n]->GetId() == idc) + return m_apctl[n]; + } + return NULL; +} + +bool Form::OnPenEvent(Event *pevt) +{ + // Auto-handle control capturing. Auto-release, auto-capture. + // Notify the control of important states: + // - penDownEvent and captured + // - penMoveEvent from inside to outside of captured control + // - penMoveEvent from outside to inside of captured control + // - penUpEvent inside control releasing capture + // - penUpEvent outside control releasing capture + + if (m_pctlCapture != NULL) { + // Send on pen event to the captured control + + m_pctlCapture->OnPenEvent(pevt); + word wf = m_pctlCapture->OnHitTest(pevt) >= 0 ? kfFrmPenInside : 0; + + // Handle the transition states + + Control *pctl = m_pctlCapture; + switch (pevt->eType) { + case penUpEvent: + // Release capture. Are we inside or outside of the control? + + m_idcLast = pctl->GetId(); + m_wf &= ~kfFrmPenInside; + m_pctlCapture = NULL; + pctl->OnSelect(((wf & kfFrmPenInside) && pevt->dw == 0) ? knSelUpInside : knSelUpOutside); + break; + + case penMoveEvent: + if ((m_wf & kfFrmPenInside) != wf) { + m_wf &= ~kfFrmPenInside; + m_wf |= wf; + pctl->OnSelect((wf & kfFrmPenInside) ? knSelMoveInside : knSelMoveOutside); + } + break; + + // This does happen. For example if a modal form is invoked in response + // to a penHoldEvent it will take the penUpEvent. Also happens on + // Windows a lot when debugging if something happens to invoke the + // debugger (e.g., fault, breakpoint) while the mousebutton is down. + + case penDownEvent: + //Assert("this shouldn't happen"); + break; + + case penHoldEvent: + pctl->OnSelect(knSelHoldInside); + break; + + // These events don't affect capture once it has already been set. + case penDownEvent2: + case penUpEvent2: + case penMoveEvent2: + break; + } + return true; + } + + // If pen isn't captured and this is a pen down on a control, + // capture future pen events to the control and let it know it + // has been pressed. + + if (pevt->eType != penDownEvent) + return false; + + // Auto-capture if we've hit a control + + Control *pctlHit = HitTestControls(pevt); + if (pctlHit != NULL) { + m_wf |= kfFrmPenInside; + m_pctlCapture = pctlHit; + m_pctlCapture->OnPenEvent(pevt); + m_pctlCapture->OnSelect(knSelDownInside); + return true; + } + + return false; +} + +Control *Form::HitTestControls(Event *pevt) { + // Finger input may register hits on more than one control at a time. + // In this case, use the "closest" control, measured by hit distance. + // Pen input will simply register on the first control registering a hit. + + int nControlBest = -1; + int nDistanceBest = 9999; + for (int n = m_cctl - 1; n >= 0; n--) { + // Is it on this control? + + Control *pctl = m_apctl[n]; + int nDistance = pctl->OnHitTest(pevt); + if (nDistance < 0) { + continue; + } + + // Remember closest + + if (nDistance < nDistanceBest) { + nDistanceBest = nDistance; + nControlBest = n; + } + + // No need to enumerate all controls if not finger input + + if (!(pevt->ff & kfEvtFinger)) { + break; + } + } + if (nControlBest >= 0) { + return m_apctl[nControlBest]; + } + return NULL; +} + +void Form::BreakCapture() +{ + if (m_pctlCapture != NULL) { + m_pctlCapture->OnBreakCapture(); + m_pctlCapture = NULL; + m_wf &= ~kfFrmPenInside; + } +} + +bool Form::OnHitTest(Event *pevt) +{ + if (!(m_wf & kfFrmVisible)) + return false; + + + // If this is a finger event, Check first if any control reports hittest. + // This allows hit testing outside the normal rect of the form. + + bool fHit = false; + if (pevt->ff & kfEvtFinger) { + for (int n = 0; n < m_cctl; n++) { + Control *pctl = m_apctl[n]; + if (pctl->OnHitTest(pevt) >= 0) { + fHit = true; + break; + } + } + } + + if (!fHit) { + fHit = m_rc.PtIn(pevt->x, pevt->y); + } + + if (fHit || !(m_wf & kfFrmAutoTakedown)) + return fHit; + + // We're AutoTakedown and hit is outside form + // We can receive pen ups and holds here if the pen was already + // down and a trigger invokes than ecom. + + if (pevt->eType == penDownEvent) + EndForm(kidcCancel); + + return false; +} + +bool Form::OnKeyTest(Event *pevt) +{ + // If pevt == NULL we're just checking; no actual event + + if (pevt == NULL) { + if (m_wf & kfFrmNoFocus) + return false; + return true; + } + + if (m_wf & kfFrmAutoTakedown) { + EndForm(kidcCancel); + return false; + } + + if (m_wf & kfFrmNoFocus) + return false; + return true; +} + +bool Form::DoModal(int *pnResult, Sfx sfxShow, Sfx sfxHide) +{ +#ifdef DEBUG + bool fFirstTimeThrough = true; +#endif + + Show(true); + gsndm.PlaySfx(sfxShow); + m_wf |= kfFrmDoModal; + while (m_wf & kfFrmDoModal) { + // Only process if there is an event + + Event evt; + if (!gevm.PeekEvent(&evt, -1)) + continue; + + // Leave appStopEvent and gameOverEvents in the queue so the whole + // call chain will see it and be able to respond appropriately. + + switch (evt.eType) { + case appStopEvent: + case gameOverEvent: + case cancelModeEvent: + m_idcLast = m_idcDefault; + m_nResult = m_idcDefault; + m_wf &= ~kfFrmDoModal; + break; + } + + // Autotakedown is handled inside PeekEvent/CookEvent/Form::OnHitTest + // and can take us out of modal mode. If so, drop out here so the + // event is left on the queue. + + if (!(m_wf & kfFrmDoModal)) + break; + +#ifdef DEBUG + fFirstTimeThrough = false; +#endif + if (!gevm.GetEvent(&evt)) + continue; + if (ggame.FilterEvent(&evt)) + continue; + if (OnFilterEvent(&evt)) + continue; + gevm.DispatchEvent(&evt); + } + + if (pnResult != NULL) + *pnResult = m_nResult; + gsndm.PlaySfx(sfxHide); + Show(false); + return m_idcLast == kidcOk; +} + +void Form::EndForm(int nResult) +{ + m_nResult = nResult; + m_wf &= ~kfFrmDoModal; +} + +void Form::GetRect(Rect *prc) +{ + *prc = m_rc; +} + +void Form::SetRect(Rect *prc) +{ + InvalidateRect(NULL); + m_rc = *prc; + InvalidateRect(NULL); +} + +bool Form::IsControlInside(Control *pctl) +{ + if (m_pctlCapture != NULL && (m_wf & kfFrmPenInside)) + return m_pctlCapture == pctl; + return false; +} + +void Form::OnControlSelected(word idc) +{ + EndForm(idc); +} + +bool Form::OnControlHeld(word idc) +{ + return false; +} + +void Form::OnControlNotify(word idc, int nNotify) +{ +} + +void Form::InvalidateRect(Rect *prc) +{ + // If invisible, return + + if (!(m_wf & kfFrmVisible)) + return; + if (prc == NULL) + prc = &m_rc; + + // If not opaqued, then invalidate + + Rect rcOpaque; + gpmfrmm->CalcOpaqueRect(this, NULL, &rcOpaque); + Rect rcT; + gpmfrmm->GetFormMgrRect(m_pfrmm, &rcT); + rcOpaque.Intersect(&rcOpaque, &rcT); + rcOpaque.Offset(-rcT.left, -rcT.top); + if (!rcOpaque.RectIn(prc)) + m_pfrmm->InvalidateRect(prc); +} + +void ShadowHelper(DibBitmap *pbm, UpdateMap *pupd, Rect *prc) +{ + Rect rc; + if (prc == NULL) { + Size siz; + pbm->GetSize(&siz); + rc.Set(0, 0, siz.cx, siz.cy); + prc = &rc; + } + + if (pupd == NULL) { + pbm->Shadow(prc->left, prc->top, prc->Width(), prc->Height()); + } else { + Rect rcInvalid; + bool fFirst = true; + while (pupd->EnumUpdateRects(fFirst, prc, &rcInvalid)) { + fFirst = false; + pbm->Shadow(rcInvalid.left, rcInvalid.top, rcInvalid.Width(), rcInvalid.Height()); + } + } +} + +void FillHelper(DibBitmap *pbm, UpdateMap *pupd, Rect *prc, Color clr) +{ + Rect rc; + if (prc == NULL) { + Size siz; + pbm->GetSize(&siz); + rc.Set(0, 0, siz.cx, siz.cy); + prc = &rc; + } + + if (pupd == NULL) { + pbm->Fill(prc->left, prc->top, prc->Width(), prc->Height(), clr); + } else { + Rect rcInvalid; + bool fFirst = true; + while (pupd->EnumUpdateRects(fFirst, prc, &rcInvalid)) { + fFirst = false; + pbm->Fill(rcInvalid.left, rcInvalid.top, rcInvalid.Width(), rcInvalid.Height(), clr); + } + } +} + +void BltHelper(DibBitmap *pbm, HtBitmap *phtbm, UpdateMap *pupd, int xDst, int yDst) +{ + if (pupd == NULL) { + phtbm->BltTo(pbm, xDst, yDst); + } else { + Size siz; + phtbm->GetSize(&siz); + Rect rc; + rc.Set(xDst, yDst, xDst + siz.cx, yDst + siz.cy); + Rect rcInvalid; + bool fFirst = true; + while (pupd->EnumUpdateRects(fFirst, &rc, &rcInvalid)) { + fFirst = false; + Rect rcSrc; + rcSrc.left = rcInvalid.left - xDst; + rcSrc.top = rcInvalid.top - yDst; + rcSrc.right = rcSrc.left + rcInvalid.Width(); + rcSrc.bottom = rcSrc.top + rcInvalid.Height(); + phtbm->BltTo(pbm, rcInvalid.left, rcInvalid.top, &rcSrc); + } + } +} + +// +// DialogForm +// + +DialogForm::DialogForm() +{ + m_clrTitle = GetColor(kiclrGreen); + m_iclrBorder = kiclrBlack; + m_fClearDib = false; + SetBackgroundColorIndex(kiclrShadow); +} + +// The compiler generates this no matter what so by declaring it ourselves +// we can control which section it ends up in (not .text) + +DialogForm::~DialogForm() +{ +} + +void DialogForm::OnPaintBackground(DibBitmap *pbm, UpdateMap *pupd) +{ + // First clear dib if asked + + if (m_fClearDib) + FillHelper(pbm, pupd, NULL, GetColor(m_iclrBackground)); + + // Draw invalid parts of the background + + int cyTitle = PcFromFc(kcyTitle); + Rect rc = m_rc; + rc.left += gcxyBorder; + rc.right -= gcxyBorder; + rc.top += cyTitle; + rc.bottom -= gcxyBorder; + + switch (m_iclrBackground) { + case kiclrShadow2x: + ShadowHelper(pbm, pupd, &rc); + // ...FALL THROUGH... + + case kiclrShadow: + ShadowHelper(pbm, pupd, &rc); + break; + + default: + FillHelper(pbm, pupd, &rc, GetColor(m_iclrBackground)); + break; + } + + // Draw form border + + DrawBorder(pbm, &m_rc, gcxyBorder, GetColor(m_iclrBorder), pupd); + + // Draw title background + + rc = m_rc; + rc.left += gcxyBorder; + rc.right -= gcxyBorder; + rc.top += gcxyBorder; + rc.bottom = m_rc.top + cyTitle; + + FillHelper(pbm, pupd, &rc, m_clrTitle); +} + +void DialogForm::SetBackgroundColorIndex(int iclr) +{ + m_iclrBackground = iclr; + if (m_iclrBackground == kiclrShadow || m_iclrBackground == kiclrShadow2x) { + m_wf |= kfFrmTranslucent; + } else { + m_wf &= ~kfFrmTranslucent; + } +} + +void DialogForm::OnPaint(DibBitmap *pbm) +{ + Form::OnPaint(pbm); +} + +bool DialogForm::OnPenEvent(Event *pevt) +{ + if (Form::OnPenEvent(pevt)) + return true; + + return FormDragger(this, pevt); +} + +bool DialogForm::DoModal(int *pnResult, Sfx sfxShow, Sfx sfxHide) +{ + bool fResult = Form::DoModal(pnResult, sfxShow, sfxHide); + if (m_fClearDib) { + m_pfrmm->InvalidateRect(NULL); + } + return fResult; +} + +bool FormDragger(Form *pfrm, Event *pevt) +{ + static bool s_fDragging = false; + static Rect s_rcInitial; + static int s_xDown, s_yDown; + + if (pevt->eType == penDownEvent) { + + Rect rcForm; + pfrm->GetRect(&rcForm); + + // Is event in title? If so, capture and save drag start info + + if (pevt->y - rcForm.top <= PcFromFc(kcyTitle) + gcxyBorder) { + s_fDragging = true; + s_rcInitial = rcForm; + s_xDown = pevt->x; + s_yDown = pevt->y; + return true; + } + return false; + } else if (pevt->eType == penMoveEvent) { + + // Are we dragging? If so, reposition form at new location + + if (s_fDragging) { + Rect rc; + rc.left = s_rcInitial.left + pevt->x - s_xDown; + rc.top = s_rcInitial.top + pevt->y - s_yDown; + rc.right = rc.left + s_rcInitial.Width(); + rc.bottom = rc.top + s_rcInitial.Height(); + pfrm->SetRect(&rc); + return true; + } + } else if (pevt->eType == penUpEvent) { + + // Are we dragging? If so, release capture + + if (s_fDragging) { + s_fDragging = false; + return true; + } + } + + return false; +} + +// +// Control +// + +Control::Control() +{ + m_rc.SetEmpty(); + m_wf = 0; + m_idc = 0; + m_pfrm = NULL; + m_pceh = NULL; +} + +Control::~Control() +{ +} + +bool Control::Init(Form *pfrm, word idc, int x, int y, int cx, int cy) +{ + m_idc = idc; + m_pfrm = pfrm; + m_pceh = pfrm; + m_wf |= kfCtlVisible; + + m_rc.Set(x, y, x + cx, y + cy); + return true; +} + +bool Control::Init(Form *pfrm, IniReader *pini, FindProp *pfind) +{ + // idc (x y cx cy) + + int x, y, cx, cy; + int idc; + int cArgs = pini->GetPropertyValue(pfind, "%d (%d %d %d %d)", &idc, + &x, &y, &cx, &cy); + if (cArgs != 5) + return false; + + // Scale form coordinates depending on the resolution of the device. + + if (pfrm->GetFlags() & kfFrmScaleCoords) { + x = PcFromFc(x); + y = PcFromFc(y); + cx = PcFromFc(cx); + cy = PcFromFc(cy); + } + m_rc.Set(x, y, x + cx, y + cy); + return Init(pfrm, idc, x, y, cx, cy); +} + +void Control::OnSelect(int nSelect) +{ + if (m_wf & kfCtlDisabled) + return; + + if (nSelect == knSelUpInside) + m_pceh->OnControlSelected(m_idc); + else if (nSelect == knSelHoldInside) { + if (m_pceh->OnControlHeld(m_idc)) { +// m_pctlCapture = NULL; +// m_wf &= ~kfFrmPenInside; +// OnSelect(knSelUpOutside); + } + } +} + +int Control::OnHitTest(Event *pevt) +{ + if (!(m_wf & kfCtlVisible)) + return -1; + + // Get the distance to a side of the control + + Rect rcForm; + m_pfrm->GetRect(&rcForm); + int nDist = m_rc.GetDistance(pevt->x - rcForm.left, pevt->y - rcForm.top); + + // Finger input hits on a larger rect and returns the distance to + // the inner rect. Non-finger input just hits against the inner rect. + + if (pevt->ff & kfEvtFinger) { + Rect rcT; + GetFingerRect(&rcT); + if (rcT.PtIn(pevt->x - rcForm.left, pevt->y - rcForm.top)) { + return nDist; + } + } else { + if (nDist == 0) { + return 0; + } + } + + // No hit + + return -1; +} + +word Control::GetId() +{ + return m_idc; +} + +void Control::GetRect(Rect *prc) +{ + *prc = m_rc; +} + +void Control::GetFingerRect(Rect *prc) +{ + *prc = m_rc; + + // Hack for now + prc->Inflate(20, 20); +} + +void Control::OnBreakCapture() +{ + Invalidate(); +} + +void Control::SetRect(Rect *prc, bool fCompareRect) +{ + if (fCompareRect) { + if (prc->Equal(&m_rc)) + return; + } + Invalidate(); + m_rc = *prc; + Invalidate(); +} + +void Control::SetPosition(int x, int y) +{ + if (m_rc.left == x && m_rc.top == y) + return; + + Invalidate(); + m_rc.Offset(x - m_rc.left, y - m_rc.top); + Invalidate(); +} + +void Control::Show(bool fShow) +{ + if (fShow) { + if (m_wf & kfCtlVisible) + return; + m_wf |= kfCtlVisible; + Invalidate(); + } else { + if (!(m_wf & kfCtlVisible)) + return; + Invalidate(); + m_wf &= ~kfCtlVisible; + } +} + +void Control::Enable(bool fEnable) +{ + if (fEnable) { + if (!(m_wf & kfCtlDisabled)) + return; + m_wf &= ~kfCtlDisabled; + Invalidate(); + } else { + if (m_wf & kfCtlDisabled) + return; + m_wf |= kfCtlDisabled; + Invalidate(); + } +} + +void Control::OnPaint(DibBitmap *pbm) +{ +} + +void Control::OnPenEvent(Event *pevt) +{ +} + +void Control::Invalidate() +{ + if (!(m_wf & kfCtlVisible)) + return; + Rect rcForm; + m_pfrm->GetRect(&rcForm); + Rect rcCtl; + rcCtl = m_rc; + rcCtl.Offset(rcForm.left, rcForm.top); + m_pfrm->InvalidateRect(&rcCtl); +} + +word Control::GetFlags() +{ + return m_wf; +} + +void Control::SetFlags(word wf) +{ + word wfT = m_wf; + m_wf = wf; + if ((wfT ^ wf) & kfCtlSet) + Invalidate(); +} + +} // namespace wi diff --git a/game/formmgr.cpp b/game/formmgr.cpp new file mode 100644 index 0000000..fa1ca27 --- /dev/null +++ b/game/formmgr.cpp @@ -0,0 +1,1066 @@ +#include "ht.h" + +namespace wi { + +FormMgr *CreateFormMgr(DibBitmap *pbm) +{ + FormMgr *pfrmm = new FormMgr(); + Assert(pfrmm != NULL, "out of memory!"); + if (pfrmm == NULL) + return NULL; + if (!pfrmm->Init(pbm, true)) { + delete pfrmm; + return NULL; + } + return pfrmm; +} + +FormMgr::FormMgr() +{ + m_wf = 0; + m_fFreeDib = false; + m_pupd = NULL; + m_idfCapture = 0; + m_cCaptureDowns = 0; + m_cfrm = 0; + m_dxScrollAccumulate = 0; + m_dyScrollAccumulate = 0; + memset(m_apfrm, 0, sizeof(m_apfrm)); +} + +FormMgr::~FormMgr() +{ + if (m_fFreeDib) + delete m_pbm; + while (m_cfrm != 0) { + Form *pfrm = m_apfrm[0]; + RemoveForm(pfrm); + delete pfrm; + } + delete m_pupd; +} + +bool FormMgr::Init(DibBitmap *pbm, bool fFreeDib) +{ + m_pbm = pbm; + m_fFreeDib = fFreeDib; + m_pupd = new UpdateMap(); + Assert(m_pupd != NULL, "out of memory!"); + if (m_pupd == NULL) + return false; + Size siz; + pbm->GetSize(&siz); + if (!m_pupd->Init(&siz)) + return false; + return true; +} + +void FormMgr::AddForm(Form *pfrm) +{ + Assert(m_cfrm < kcFormsMax); + if (m_cfrm >= kcFormsMax) + return; + + // Newly added forms go on top, unless there aren't topmost + // and topmost forms are already present, in which case the + // added form is placed immediately underneath the bottomost + // topmost form. + + int i; + if (pfrm->GetFlags() & kfFrmTopMost) { + i = m_cfrm; + } else { + for (i = 0; i < m_cfrm; i++) { + if (m_apfrm[i]->GetFlags() & kfFrmTopMost) + break; + } + + if (i != m_cfrm) + memmove(&m_apfrm[i + 1], &m_apfrm[i], sizeof(pfrm) * (m_cfrm - i)); + } + + m_apfrm[i] = pfrm; + m_cfrm++; + pfrm->InvalidateRect(NULL); +} + +void FormMgr::RemoveForm(Form *pfrm) +{ + for (int n = 0; n < m_cfrm; n++) { + if (m_apfrm[n] == pfrm) { + if (n < kcFormsMax - 1) + memmove(&m_apfrm[n], &m_apfrm[n + 1], sizeof(Form *) * (kcFormsMax - 1 - n)); + m_cfrm--; + break; + } + } + pfrm->InvalidateRect(NULL); +} + +bool FormMgr::EcomSuppressed() +{ + for (int n = 0; n < m_cfrm; n++) { + if (m_apfrm[n]!= NULL) { + if (m_apfrm[n]->GetFlags() & kfFrmNoEcom) + return true; + } + } + return false; +} + +bool FormMgr::CookKeyEvent(Event *pevt) +{ + // Hack for now: assume the topmost form wants key input + // Maybe this'll be enough focus "knowledge". + + for (int n = m_cfrm - 1; n >= 0; n--) { + if (m_apfrm[n]->OnKeyTest(pevt)) { + pevt->idf = m_apfrm[n]->GetId(); + return true; + } + } + return false; +} + +Form *FormMgr::FindPen2Form() +{ + // Find a form that demands pen2 + + for (int i = 0; i < m_cfrm; i++) { + Form *pfrm = m_apfrm[i]; + if (pfrm->m_wf & kfFrmDemandPen2) { + return pfrm; + } + } + return NULL; +} + +void FormMgr::BreakCapture() +{ + Form *pfrm = GetFormCapture(); + if (pfrm == NULL) { + return; + } + pfrm->BreakCapture(); + m_idfCapture = 0; + m_cCaptureDowns = 0; +} + +bool FormMgr::CookPenEvent(Event *pevt) +{ + // Send the input to the capturing form. Auto-release if pen up, + // auto-capture if pen down. + + if (m_idfCapture != 0) { + pevt->idf = m_idfCapture; + if (pevt->eType == penDownEvent || pevt->eType == penDownEvent2) { + m_cCaptureDowns++; + } + if (pevt->eType == penUpEvent || pevt->eType == penUpEvent2) { + m_cCaptureDowns--; + } + if (m_cCaptureDowns <= 0) { + m_cCaptureDowns = 0; + m_idfCapture = 0; + } + return true; + } else { + // Ask the topmost form first if it wants this pen input. + // All coords are global. + + word idf = 0; + for (int n = m_cfrm - 1; n >= 0; n--) { + Form *pfrm = m_apfrm[n]; + if (pfrm->OnHitTest(pevt)) { + idf = pfrm->GetId(); + break; + } + + // If the top-most form is modal, don't pass the event on + // to any lower forms. + + if (pfrm->GetFlags() & kfFrmDoModal) { + pevt->idf = 0; + return true; + } + } + pevt->idf = idf; + if (pevt->eType == penDownEvent || pevt->eType == penDownEvent2) { + m_cCaptureDowns = 1; + m_idfCapture = idf; + } + return pevt->idf != 0; + } +} + +Form *FormMgr::GetFocus() +{ + // Hack for now: assume the topmost form wants key input + // Maybe this'll be enough focus "knowledge". + + for (int n = 0; n < m_cfrm; n++) { + if (m_apfrm[n]->OnKeyTest(NULL)) { + return m_apfrm[n]; + } + } + return NULL; +} + +bool FormMgr::CookEvent(Event *pevt) +{ + pevt->idf = 0; + switch (pevt->eType) { + case penHoverEvent: + case penUpEvent: + case penDownEvent: + case penMoveEvent: + case penUpEvent2: + case penDownEvent2: + case penMoveEvent2: + case penHoldEvent: + { + // If no capture, process as usual + + if (m_idfCapture == 0) { + return CookPenEvent(pevt); + } + + // Mouse is captured. If captureed but there is a new form on top, + // gracefully release the capture (can happen if form shows while + // mouse is captured) + + if (!CookPenEvent(pevt)) + return false; + for (int n = m_cfrm - 1; n >= 0; n--) { + Form *pfrm = m_apfrm[n]; + if ((pfrm->GetFlags() & (kfFrmDoModal | kfFrmVisible)) == (kfFrmDoModal | kfFrmVisible)) { + if (pfrm->m_idf != pevt->idf) { + pevt->dw = 1; + pevt->eType = penUpEvent; + m_idfCapture = 0; + m_cCaptureDowns = 0; + } + } + break; + } + return true; + } + break; + + case keyDownEvent: + return CookKeyEvent(pevt); + } + return false; +} + +Form *FormMgr::GetFormPtr(word idf) +{ + for (int n = m_cfrm - 1; n >= 0; n--) { + if (m_apfrm[n]->GetId() == idf) + return m_apfrm[n]; + } + return NULL; +} + +Form *FormMgr::LoadForm(IniReader *pini, word idf, Form *pfrm) +{ + if (pfrm == NULL) + return NULL; + + if (!pfrm->Init(this, pini, idf)) { + delete pfrm; + return NULL; + } + + return pfrm; +} + +void FormMgr::CalcOpaqueRect(Form *pfrmStop, Rect *prcOpaqueStart, Rect *prcResult) +{ + // Start from the top and expand the opaque rect when a portion lies safely inside + // the new rectangle. + + if (prcOpaqueStart == NULL) { + prcResult->SetEmpty(); + } else { + *prcResult = *prcOpaqueStart; + } + for (int n = m_cfrm - 1; n >= 0; n--) { + Form *pfrm = m_apfrm[n]; + if (pfrm == pfrmStop) + break; + word wf = pfrm->GetFlags(); + if (wf & kfFrmTranslucent) + continue; + if (!(wf & kfFrmVisible)) + continue; + prcResult->Add(prcResult, &pfrm->m_rc); + } +} + +void FormMgr::Paint(bool fScrolled, Rect *prcOpaqueStart) +{ + // If scrolling, we want to scroll from a valid back buffer. + // After invalidation has been calculated for this frame, merge in + // damaged invalid; this'll make the back buffer valid after redraw. + + if (fScrolled) + m_pupd->StartMergeDamagedInvalid(); + + // Give some time to sound servicing + + HostSoundServiceProc(); + + // Invalidate UpdateMap for this frame + + int j; + Rect rcOpaque; + for (j = 0; j < m_cfrm; j++) { + if (!(m_apfrm[j]->m_wf & kfFrmVisible)) + continue; + CalcOpaqueRect(m_apfrm[j], prcOpaqueStart, &rcOpaque); + if (!rcOpaque.RectIn(&m_apfrm[j]->m_rc)) + m_apfrm[j]->OnUpdateMapInvalidate(m_pupd, &rcOpaque); + } + + // Give some time to sound servicing + + HostSoundServiceProc(); + + // The merge is a two part process + + if (fScrolled) + m_pupd->EndMergeDamagedInvalid(); + + // If nothing invalid, nothing to paint + + if (!m_pupd->IsInvalid()) + return; + + // Now paint against the update map + + for (j = 0; j < m_cfrm; j++) { + if (!(m_apfrm[j]->m_wf & kfFrmVisible)) + continue; + if (m_pupd->IsRectInvalid(&m_apfrm[j]->m_rc)) { + CalcOpaqueRect(m_apfrm[j], prcOpaqueStart, &rcOpaque); + if (!rcOpaque.RectIn(&m_apfrm[j]->m_rc)) { + m_apfrm[j]->OnPaintBackground(m_pbm, m_pupd); + HostSoundServiceProc(); + m_apfrm[j]->OnPaint(m_pbm); + HostSoundServiceProc(); + m_apfrm[j]->OnPaintControls(m_pbm, m_pupd); + HostSoundServiceProc(); + } + } + } +} + +void FormMgr::Scroll(int dx, int dy) +{ + // If nothing to scroll, return + + if (dx == 0 && dy == 0) + return; + + // Force the forms to invalidate the updatemap appropriately before the map + // is scrolled + + int i; + for (i = 0; i < m_cfrm; i++) + m_apfrm[i]->ScrollInvalidate(m_pupd); + + // Now scroll the update map + + bool fScrolled = m_pupd->Scroll(dx, dy); + + // Give forms a chance to respond to the scroll + + for (i = 0; i < m_cfrm; i++) + m_apfrm[i]->OnScroll(dx, dy); + + // Now invalidate again after the map has been scrolled + + for (i = 0; i < m_cfrm; i++) + m_apfrm[i]->ScrollInvalidate(m_pupd); + + // Accumulate + + if (fScrolled) { + m_dxScrollAccumulate += dx; + m_dyScrollAccumulate += dy; + } +} + +bool FormMgr::ScrollBits() +{ + if (m_dxScrollAccumulate == 0 && m_dyScrollAccumulate == 0) + return false; + Size sizSrc; + m_pbm->GetSize(&sizSrc); + Rect rcSrc; + + rcSrc.left = 0; + if (m_dxScrollAccumulate < 0) { + rcSrc.left = -m_dxScrollAccumulate; + m_dxScrollAccumulate = 0; + } + rcSrc.top = 0; + if (m_dyScrollAccumulate < 0) { + rcSrc.top = -m_dyScrollAccumulate; + m_dyScrollAccumulate = 0; + } + rcSrc.right = sizSrc.cx; + if (m_dxScrollAccumulate > 0) + rcSrc.right -= m_dxScrollAccumulate; + rcSrc.bottom = sizSrc.cy; + if (m_dyScrollAccumulate > 0) + rcSrc.bottom -= m_dyScrollAccumulate; + + // Hack: if the front dib is a rotated dib, it is a 3 dib architecture and + // we can scroll the middle (rotated) dib rather than the back dib, giving + // us a speedup. + +#if !defined(WIN) + DibBitmap *pbmFront = gpdisp->GetFrontDib(); + if (pbmFront->GetFlags() & kfDibWantScrolls) { +#if defined(PALM) + // Change this over to DibBitmap::Scroll in the future + RotatedFrontDib *pbmScroll = (RotatedFrontDib *)pbmFront; + pbmScroll->ScrollBlt(&rcSrc, m_dxScrollAccumulate, m_dyScrollAccumulate); +#else + pbmFront->Scroll(&rcSrc, m_dxScrollAccumulate, m_dyScrollAccumulate); +#endif + m_dxScrollAccumulate = 0; + m_dyScrollAccumulate = 0; + return true; + } +#endif + + // Perform blt of the back buffer + + m_pbm->Blt(m_pbm, &rcSrc, m_dxScrollAccumulate, m_dyScrollAccumulate); + m_dxScrollAccumulate = 0; + m_dyScrollAccumulate = 0; + return true; +} + +bool FormMgr::HasCapture() +{ + return m_idfCapture != 0; +} + +Form *FormMgr::GetFormCapture() +{ + if (m_idfCapture == 0) { + return NULL; + } + for (int n = m_cfrm - 1; n >= 0; n--) { + if (m_apfrm[n]->m_idf == m_idfCapture) { + return m_apfrm[n]; + } + } + return NULL; +} + +Form *FormMgr::GetModalForm() +{ + for (int n = m_cfrm - 1; n >= 0; n--) { + if ((m_apfrm[n]->m_wf & (kfFrmDoModal | kfFrmVisible)) == (kfFrmDoModal | kfFrmVisible)) + return m_apfrm[n]; + } + return NULL; +} + +void FormMgr::InvalidateRect(Rect *prc) +{ + Rect rcDib; + if (prc == NULL) { + Size siz; + m_pbm->GetSize(&siz); + rcDib.Set(0, 0, siz.cx, siz.cy); + prc = &rcDib; + } + + if (m_pupd != NULL) + m_pupd->InvalidateRect(prc); +} + +DibBitmap *FormMgr::GetDib() +{ + return m_pbm; +} + +bool FormMgr::BltTo(DibBitmap *pbmDst, int yTop, bool fScrolled) +{ + // If nothing invalid, nothing to do + + if (!m_pupd->IsInvalid()) + return false; + + // If we've scrolled, we've already assured the back dib is valid, so just + // copy it Unless the front dib is handling scrolls in which case do the + // normal blt tiles thing. + + if (!(gpdisp->GetFrontDib()->GetFlags() & kfDibWantScrolls)) { + if (fScrolled && !(m_wf & kfFrmmNoScroll)) { + Size sizSrc; + m_pbm->GetSize(&sizSrc); + Rect rcSrc; + rcSrc.Set(0, 0, sizSrc.cx, sizSrc.cy); + pbmDst->Blt(m_pbm, &rcSrc, 0, yTop); + return true; + } + } + + // Copy to pbmDst only tiles that are invalid + + pbmDst->BltTiles(m_pbm, m_pupd, yTop); + return true; +} + +void FormMgr::FrameStart() +{ + for (int n = 0; n < m_cfrm; n++) { + m_apfrm[n]->FrameStart(); + } +} + +void FormMgr::FrameComplete() +{ + for (int n = 0; n < m_cfrm; n++) { + m_apfrm[n]->FrameComplete(); + } +} + +// +// MultiFormMgr +// + +MultiFormMgr::MultiFormMgr() +{ + m_cfrmm = 0; + memset(m_apfrmm, 0, sizeof(m_apfrmm)); + memset(&m_evtPen1Down, 0, sizeof(m_evtPen1Down)); +} + +MultiFormMgr::~MultiFormMgr() +{ + while (m_cfrmm != 0) { + FormMgr *pfrmm = m_apfrmm[0]; + RemoveFormMgr(pfrmm); + delete pfrmm; + } +} + +void MultiFormMgr::AddFormMgr(FormMgr *pfrmm, Rect *prc) +{ + Assert(m_cfrmm < kcFormMgrMax); + m_apfrmm[m_cfrmm] = pfrmm; + m_arcFormMgr[m_cfrmm] = *prc; + m_cfrmm++; +} + +void MultiFormMgr::RemoveFormMgr(FormMgr *pfrmm) +{ + for (int n = 0; n < m_cfrmm; n++) { + if (m_apfrmm[n] == pfrmm) { + if (n < kcFormMgrMax - 1) { + memmove(&m_apfrmm[n], &m_apfrmm[n + 1], sizeof(FormMgr *) * (kcFormMgrMax - 1 - n)); + memmove(&m_arcFormMgr[n], &m_arcFormMgr[n + 1], sizeof(Rect) * (kcFormMgrMax - 1 - n)); + } + m_cfrmm--; + break; + } + } +} + +void MultiFormMgr::AddForm(Form *pfrm) +{ + FormMgr::AddForm(pfrm); + InvalidateRect(NULL); +} + +void MultiFormMgr::RemoveForm(Form *pfrm) +{ + FormMgr::RemoveForm(pfrm); + InvalidateRect(NULL); +} + +bool MultiFormMgr::EcomSuppressed() +{ + return FormMgr::EcomSuppressed(); +} + +Form *MultiFormMgr::GetFocus() +{ + Form *pfrm = FormMgr::GetFocus(); + if (pfrm == NULL) { + if (m_cfrmm > 0) + return m_apfrmm[m_cfrmm - 1]->GetFocus(); + } + return NULL; +} + +bool MultiFormMgr::StealCapturePen2(Event *pevt, FormMgr *pfrmmChildCapture) +{ + // If this is penDownEvent, remember the pre-cooked event for later + // use. + + if (pevt->eType == penDownEvent) { + m_evtPen1Down = *pevt; + return false; + } + + // The playfield allows multi-touch select. Sometimes + // finger 1 goes down on a control surrounding the playfield accidentially, + // and finger 2 goes down on the playfield. In this case, finger 1 + // needs to be "uncaptured", and both finger 1 and finger 2 down events + // routed to the playfield. + + if (pevt->eType != penDownEvent2) { + return false; + } + + // Find a form that demands pen 2 + + Form *pfrmWantsPen2 = FindPen2Form(); + if (pfrmWantsPen2 == NULL) { + for (int i = 0; i < m_cfrmm; i++) { + pfrmWantsPen2 = m_apfrmm[i]->FindPen2Form(); + if (pfrmWantsPen2 != NULL) { + break; + } + } + } + if (pfrmWantsPen2 == NULL) { + return false; + } + + // If this form already has capture, then no need to "steal" capture; + // process normally. + + FormMgr *pfrmmWantsPen2 = pfrmWantsPen2->GetFormMgr(); + if (pfrmmWantsPen2->HasCapture()) { + return false; + } + + // Any modal forms up? Don't steal pen2 + + if (GetModalForm() != NULL) { + return false; + } + for (int i = 0; i < m_cfrmm; i++) { + if (m_apfrmm[i]->GetModalForm() != NULL) { + return false; + } + } + + // Pen1 is captured in a different form manager. Steal that, then + // send pen1down and pen2down to the new form. + + if (pfrmmChildCapture != NULL) { + pfrmmChildCapture->BreakCapture(); + } else { + if (HasCapture()) { + BreakCapture(); + } + } + + // Force capture to this form specifically. This will by-pass + // hit testing. + + pfrmmWantsPen2->m_idfCapture = pfrmWantsPen2->GetId(); + + // Find the formmgr's rect + Rect rc; + rc.SetEmpty(); + for (int i = 0; i < m_cfrmm; i++) { + if (m_apfrmm[i] == pfrmmWantsPen2) { + rc = m_arcFormMgr[i]; + break; + } + } + + // These events need to be processed in the right order. + // To do this, they will get posted after being cooked. + + Event evtT; + evtT = m_evtPen1Down; + evtT.x -= rc.left; + evtT.y -= rc.top; + pfrmmWantsPen2->CookEvent(&evtT); + gevm.PostEvent(&evtT, false); + + evtT = *pevt; + evtT.x -= rc.left; + evtT.y -= rc.top; + pfrmmWantsPen2->CookEvent(&evtT); + gevm.PostEvent(&evtT, false); + + // This current event is going to be processed so change it + // to something nop-ish. + + pevt->eType = nullEvent; + pevt->idf = 0; + return true; +} + +bool MultiFormMgr::CookEvent(Event *pevt) +{ + // If no child has capture, give the multi form manager a crack at it + + FormMgr *pfrmmChildCapture = NULL; + int n; + for (n = 0; n < m_cfrmm; n++) { + if (m_apfrmm[n]->HasCapture()) { + pfrmmChildCapture = m_apfrmm[n]; + break; + } + } + + // Treat Pen2 specially. + + if (StealCapturePen2(pevt, pfrmmChildCapture)) { + return false; + } + + // If no child has it captured and if the multiformmgr wants it, give it + + if (pfrmmChildCapture == NULL) { + if (FormMgr::CookEvent(pevt)) + return true; + } else { + // Child has it captured. If the multiformmgr has a modal form, force + // the child to release capture. Can happen if a form shows while + // a child has the capture. + + if (GetModalForm() != NULL) { + pevt->dw = 1; + pevt->eType = penUpEvent; + + // Set this to zero so it doesn't get out of whack when two + // fingers are down. It won't underflow since that is checked. + + pfrmmChildCapture->m_cCaptureDowns = 0; + } + } + + // Try to handle intelligently + + switch (pevt->eType) { + case penHoverEvent: + case penDownEvent: + case penUpEvent: + case penMoveEvent: + case penDownEvent2: + case penUpEvent2: + case penMoveEvent2: + case penHoldEvent: + // Send to the form with the capture + + for (n = 0; n < m_cfrmm; n++) { + if (m_apfrmm[n]->HasCapture() || + m_apfrmm[n]->GetModalForm() != NULL) { + pevt->x -= m_arcFormMgr[n].left; + pevt->y -= m_arcFormMgr[n].top; + if (m_apfrmm[n]->CookEvent(pevt)) + return true; + + // else allow it to choose not to handle an event even if it + // had capture IE when it releases capture/ends modal and wants + // to pass on that event. + + Assert(!m_apfrmm[n]->HasCapture() && + m_apfrmm[n]->GetModalForm() == NULL); + + // restore the pevt + pevt->x += m_arcFormMgr[n].left; + pevt->y += m_arcFormMgr[n].top; + } + } + + // Send to the form the event is inside + + for (n = 0; n < m_cfrmm; n++) { + if (m_arcFormMgr[n].PtIn(pevt->x, pevt->y)) { + pevt->x -= m_arcFormMgr[n].left; + pevt->y -= m_arcFormMgr[n].top; + return m_apfrmm[n]->CookEvent(pevt); + } + } + break; + + case keyDownEvent: + if (FormMgr::CookKeyEvent(pevt)) + return true; + + // fall through + + default: + // Send to the formmgr added last + + if (m_cfrmm > 0) { + return m_apfrmm[m_cfrmm - 1]->CookEvent(pevt); + } + } + + return false; +} + +Form *MultiFormMgr::GetFormPtr(word idf) +{ + Form *pfrm = FormMgr::GetFormPtr(idf); + if (pfrm != NULL) + return pfrm; + + for (int n = 0; n < m_cfrmm; n++) { + pfrm = m_apfrmm[n]->GetFormPtr(idf); + if (pfrm != NULL) + return pfrm; + } + + return NULL; +} + +void MultiFormMgr::CalcOpaqueRect(Form *pfrmStop, Rect *prcOpaqueStart, Rect *prcResult) +{ + // If this form is part of the multi form mgr, then it's simple + + if (pfrmStop == NULL || pfrmStop->GetFormMgr() == this) { + FormMgr::CalcOpaqueRect(pfrmStop, prcOpaqueStart, prcResult); + return; + } + + // Otherwise it's whatever is in the multi form mgr plus the child that + // this form is within + + FormMgr::CalcOpaqueRect(NULL, prcOpaqueStart, prcResult); + pfrmStop->GetFormMgr()->CalcOpaqueRect(pfrmStop, prcResult, prcResult); +} + +bool MultiFormMgr::GetFormMgrRect(FormMgr *pfrmm, Rect *prc) +{ + if (pfrmm == this) { + Size sizDib; + m_pbm->GetSize(&sizDib); + prc->left = 0; + prc->top = 0; + prc->right = sizDib.cx; + prc->bottom = sizDib.cy; + return true; + } + + for (int n = 0; n < m_cfrmm; n++) { + if (m_apfrmm[n] == pfrmm) { + *prc = m_arcFormMgr[n]; + return true; + } + } + + Assert(); + return false; +} + +bool MultiFormMgr::Paint(bool fForceBackBufferValid) +{ + // Calc opaque rect + + Rect rcOpaque; + rcOpaque.SetEmpty(); + CalcOpaqueRect(NULL, NULL, &rcOpaque); + + bool fAnyScroll = false; + int n; + for (n = 0; n < m_cfrmm; n++) { + // Paint children form mgr bits + + bool fScrolled = m_apfrmm[n]->ScrollBits(); + if (fForceBackBufferValid) + fScrolled = true; + if (fScrolled) + fAnyScroll = true; + + // Paint / calc updatemaps for children + + Rect rcT; + rcT.Intersect(&m_arcFormMgr[n], &rcOpaque); + rcT.Offset(-m_arcFormMgr[n].left, -m_arcFormMgr[n].top); + m_apfrmm[n]->Paint(fScrolled, &rcT); + } + + // There is a form, need the children updatemap data to be reflected in the + // parent updatemap + + for (n = 0; n < m_cfrmm; n++) { + FormMgr *pfrmm = m_apfrmm[n]; + UpdateMap *pupd = pfrmm->GetUpdateMap(); + Rect rcT; + bool fFirst = true; + while (pupd->EnumUpdateRects(fFirst, NULL, &rcT)) { + fFirst = false; + rcT.Offset(m_arcFormMgr[n].left, m_arcFormMgr[n].top); + m_pupd->InvalidateRect(&rcT); + } + } + + // Now that the multi-form mgr has all the invalid state reflected, + // it is ok to paint + + rcOpaque.SetEmpty(); + FormMgr::Paint(false, &rcOpaque); + return fAnyScroll; +} + +bool MultiFormMgr::BltTo(DibBitmap *pbmDst, int yTop, bool fScrolled) +{ + bool fBlt = false; + for (int n = 0; n < m_cfrmm; n++) { + fBlt |= m_apfrmm[n]->BltTo(pbmDst, m_arcFormMgr[n].top, fScrolled); + } + + return fBlt; +} + +void MultiFormMgr::DrawFrame(bool fForceBackBufferValid, bool fPaint) +{ + // Tell forms about to draw frame + + for (int n = 0; n < m_cfrmm; n++) + m_apfrmm[n]->FrameStart(); + + // Notify display we're starting a frame + + gpdisp->FrameStart(); + + // Draw forms + + bool fAnyScroll = fPaint ? Paint(fForceBackBufferValid) : false; + + // Copy to front buffer + + BltTo(gpdisp->GetFrontDib(), 0, fAnyScroll); + + // Notify the display we've completed a frame. Pass in update maps + // so selective invalidation can occur + + UpdateMap *apupd[kcFormMgrMax]; + Rect arc[kcFormMgrMax]; + for (int n = 0; n < m_cfrmm; n++) { + apupd[n] = m_apfrmm[n]->GetUpdateMap(); + arc[n] = m_arcFormMgr[n]; + } + gpdisp->FrameComplete(m_cfrmm, apupd, arc, fAnyScroll); + + // Save avi frame + +#if defined(WIN) && !defined(CE) + if (gpavir != NULL) + gpavir->AddFrame(gpdisp->GetFrontDib()); +#endif + + // Draw update rects + +#ifdef DRAW_UPDATERECTS + if (gfDrawUpdateRects) { + for (int n = 0; n < m_cfrmm; n++) + DrawUpdateRects(m_apfrmm[n]->GetUpdateMap(), m_arcFormMgr[n].top); + } +#endif + + // Validate updatemaps + + m_pupd->Validate(); + for (int n = 0; n < m_cfrmm; n++) { + m_apfrmm[n]->GetUpdateMap()->Validate(); + } + + // Tell forms the frame is complete. + + for (int n = 0; n < m_cfrmm; n++) { + m_apfrmm[n]->FrameComplete(); + } +} + +#ifdef DRAW_UPDATERECTS +void MultiFormMgr::DrawUpdateRects(UpdateMap *pupd, int yTop) +{ + // Draw update rects + + if (gfDrawUpdateRects) { + for (int n = 0; n < m_cfrmm; n++) { + bool fFirst = true; + Rect rc; + while (pupd->EnumUpdateRects(fFirst, NULL, &rc)) { + fFirst = false; + rc.Offset(0, yTop); + gpdisp->DrawFrameInclusive(&rc); + } + } + } +} +#endif + +void MultiFormMgr::InvalidateRect(Rect *prc) +{ + // Distribute to this form mgr + + Rect rcDib; + if (prc == NULL) { + Size siz; + m_pbm->GetSize(&siz); + rcDib.Set(0, 0, siz.cx, siz.cy); + prc = &rcDib; + } + + // Distribute to children form mgr's + + for (int n = 0; n < m_cfrmm; n++) { + Rect rcT; + if (rcT.Intersect(prc, &m_arcFormMgr[n])) { + rcT.Offset(-m_arcFormMgr[n].left, -m_arcFormMgr[n].top); + m_apfrmm[n]->InvalidateRect(&rcT); + } + } +} + +void MultiFormMgr::CheckSetRedrawDirty() +{ + for (int n = 0; n < m_cfrmm; n++) { + if (m_apfrmm[n]->GetUpdateMap()->IsInvalid()) { + gevm.SetRedrawFlags(kfRedrawDirty); + return; + } + } +} + +bool MultiFormMgr::IsInvalid() +{ + for (int n = 0; n < m_cfrmm; n++) { + if (m_apfrmm[n]->GetUpdateMap()->IsInvalid()) { + return true; + } + } + return false; +} + +#ifdef STATS_DISPLAY +int MultiFormMgr::GetUpdateRectCount() +{ + int c = 0; + for (int n = 0; n < m_cfrmm; n++) { + UpdateMap *pupd = m_apfrmm[n]->GetUpdateMap(); + Size sizMap; + pupd->GetMapSize(&sizMap); + int cb = sizMap.cx * sizMap.cy; + bool *pfInvalid = pupd->GetInvalidMap(); + while (cb-- != 0) { + if (*pfInvalid++) + c++; + } + } + return c; +} +#endif + +} // namespace wi diff --git a/game/game.cpp b/game/game.cpp new file mode 100644 index 0000000..3bf7d33 --- /dev/null +++ b/game/game.cpp @@ -0,0 +1,2680 @@ +#include "game/ht.h" +#include "game/strings.h" +#include "mpshared/netmessage.h" +#include "game/httppackmanager.h" +#include "game/completemanager.h" +#include "game/chatter.h" + +#if 0 +//temp +#include "base/bytebuffer.h" +#include "game/serviceurls.h" +#include "game/uploader.h" +#include "game/httpservice.h" +#endif + +namespace wi { + +Display *gpdisp; +Game ggame; +Simulation gsim; +TimerMgr gtimm; +IniReader *gpiniForms; +IniReader *gpiniGame; +Font *gapfnt[kcFonts]; +GobStateMachineMgr gsmm; +GobMgr ggobm; +byte *gpbScratch; +word gcbScratch; +byte *gmpiclriclrShadow; +int gimmReinitialize = -1; +bool gfLoadReinitializeSave = false; +int gcxTile; +int gcyTile; +int gcxFullScreenFormDiv10; +int gcxyBorder; +bool gfGrayscale = false; +#ifdef DRAW_UPDATERECTS +bool gfDrawUpdateRects; +#endif +short *gmpPcFromWc; +WCoord *gmpWcFromPc; +DibBitmap *gpbmClip; +FormMgr *gpfrmmSim; +FormMgr *gpfrmmInput; +MultiFormMgr *gpmfrmm; +int gnHueOffset = 0; +int gnSatMultiplier = 0; +int gnLumOffset = 0; +int gnDemoRank = 0; +double gnScrollSpeed = 1.0; +char gszAskURL[512]; +char gszDeviceId[34]; +Stream *gpstmSavedGame; +UpdateMap *gpupdSim; +long gtGameSpeed = kcmsUpdate / 10; // 80 ms/frame or 12.5 FPS (1000/80) +Color *gaclrFixed; +bool gfClearFog; +word gwfPerfOptions = kfPerfAll; +word gwfHandicap = kfHcapDefault; +int gcStructGobsHumanLimitSP; +int gcStructGobsComputerLimitSP; +int gcMuntGobsHumanLimitSP; +int gcMuntGobsComputerLimitSP; +int gcStructGobsComputerDeltaSP; +int gcStructGobsHumanDeltaSP; +int gcStructGobsLimitMP; +int gcMuntGobsLimitMP; +int gcSceneryGobsLimit; +int gcScorchGobsLimit; +int gcSupportGobsLimit; +bool gfMultiplayer; +bool gfIgnoreBluetoothWarning; +SpriteManager *gpsprm; + +#ifdef STRESS +bool gfStress = false; +long gtStressTimeout; +#endif + +#ifndef PIL +#if defined(IPHONE) && !defined(DEV_BUILD) && !defined(BETA_TIMEOUT) +char *gszVersion = "1.6"; +#else +char *gszVersion = "+++VERSION+++"; +#endif +#endif + +// Forward declarations + +bool AllocComputerPlayers() secGame; +void FreeComputerPlayers() secGame; + +// +// +// + +void GameMain(char *pszCmds) +{ +#if defined(DEBUG) && !defined(CE) && !defined(PIL) && !defined(IPHONE) && !defined(SDL) + _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); +#endif + char *pszLevel = NULL; + char szLevel[kcbFilename]; +#ifdef WIN + char szData[_MAX_PATH]; +#endif + + // Parse command line + + int nchAbs = 0; + int nchRel; + int imm = -1; + while (true) { + char szT[80]; + int c = IniScanf(&pszCmds[nchAbs], "%s %+", szT, &nchRel); + nchAbs += nchRel; + if (c == 0) + break; + + // -m %d (mode / data match: index) + + if (strcmp(szT, "-m") == 0) { + c = IniScanf(&pszCmds[nchAbs], "%d%+", &imm, &nchRel); + nchAbs += nchRel; + if (c == 0) + return; + + // -l %s (level: level.lvl) + + } else if (strcmp(szT, "-l") == 0) { + c = IniScanf(&pszCmds[nchAbs], "%s", szLevel, &nchRel); + nchAbs += nchRel; + if (c == 0) + return; + pszLevel = szLevel; +#ifdef STRESS + // -s + + } else if (strcmp(szT, "-s") == 0) { + gfStress = true; +#endif + } else if (strcmp(szT, "-nofog") == 0) { + gfClearFog = true; + +#if defined(MP_STRESS) && !defined(PIL) && !defined(CE) && !defined(IPHONE) + // -mp-spawn + // TODO: -mp-spawn side arg_string + + } else if (strcmp(szT, "-mp-spawn") == 0) { + int cSpawn; + c = IniScanf(&pszCmds[nchAbs], "%d %+", &cSpawn, &nchRel); + nchAbs += nchRel; + if (c != 2) + return; + + for (int nSpawn = 0; nSpawn < cSpawn; nSpawn++) { + char szModule[MAX_PATH]; + GetModuleFileName(NULL, szModule, sizeof(szModule) - 1); + char szCmdLine[MAX_PATH]; + sprintf(szCmdLine, "%s -mp-start %d", szModule, nSpawn + 1); + WinExec(szCmdLine, SW_SHOW); + } + + // -mp-start + // TODO: -mp-start side arg_string + + } else if (strcmp(szT, "-mp-start") == 0) { + int nSpawn; + c = IniScanf(&pszCmds[nchAbs], "%d %+", &nSpawn, &nchRel); + nchAbs += nchRel; + if (c != 2) + return; + + gfMPStress = true; + gnMPPos = nSpawn; +#endif + } + } + +#ifdef DEBUG_HELPERS + InitDebugHelpers(); +#endif + + if (!HostInit()) { + return; + } + + while (true) { + if (!ggame.Init(imm)) { + ggame.Exit(); + return; + } + +#if 0 +//for testing sync error uploading + //temp + base::ByteBuffer *bb = new base::ByteBuffer(); + bb->WriteString("hello world"); + const char *url = base::Format::ToString("%s?gameid=1&pid=2", + kszSyncErrorUploadUrl); + UploadByteBuffer(gphttp, bb, url); + ggame.Exit(); + return; +#endif + + if (gfLoadReinitializeSave) { + gfLoadReinitializeSave = false; + gshl.Launch(true); + } else { + gshl.Launch(false); + } + + if (gimmReinitialize != -1) { + imm = gimmReinitialize; + gimmReinitialize = -1; + ggame.Exit(); + continue; + } + break; + } + + // Demo mode only nag screen + // Will mean you'll need to press the applications silkscreen button twice + // in order to exit from demo + + if (gfDemo) { + gevm.ClearAppStopping(); + DoRegisterNowForm(); + } + + ggame.Exit(); + + HostExit(); + +#ifdef DEBUG_HELPERS + ExitDebugHelpers(); +#endif + +#if defined(CHECK_OLD_ALLOCS) && defined(DEBUG) && !defined(CE) && !defined(PIL) + _CrtDumpMemoryLeaks(); +#endif +} + +Game::Game() +{ + m_wf = 0; + m_pfrmSimUI = NULL; + m_fSimUninitialized = true; + m_cmm = 0; + m_cmmAlloc = 0; + m_immCurrent = -1; + m_immBest = -1; + m_szNextLevel[0] = 0; + m_fSkipSaveReinitialize = false; + m_fUpdateTriggers = false; + m_gameid = 0; +} + +Game::~Game() +{ + Assert(m_pfrmSimUI == NULL); +} + +bool Game::Init(int imm) +{ + // Haven't initialized yet + + m_wf &= ~kfGameInitDone; + + // Check Dynamic Memory size + + if (!CheckMemoryAvailable()) + return false; + + // Load prefs first + + LoadPreferences(); + + // Suck what we can from preferences + + gtGameSpeed = gprefsInit.ctGameSpeed; + gfLassoSelection = gprefsInit.fLassoSelection != 0; + gnHueOffset = gprefsInit.nHueOffset; + gnSatMultiplier = gprefsInit.nSatMultiplier; + gnLumOffset = gprefsInit.nLumOffset; + strncpyz(gszUsername, gprefsInit.szUsername, sizeof(gszUsername)); + strncpyz(gszPassword, gprefsInit.szPassword, sizeof(gszPassword)); + strncpyz(gszToken, gprefsInit.szToken, sizeof(gszToken)); + gfAnonymous = (gprefsInit.fAnonymous != 0); + gwfPerfOptions = gprefsInit.wfPerfOptions; + gwfHandicap = gprefsInit.wfHandicap; + gkey = gprefsInit.key; + gnDemoRank = gprefsInit.nDemoRank; + gnScrollSpeed = gprefsInit.nScrollSpeed; + gfIgnoreBluetoothWarning = (gprefsInit.wfPrefs & kfPrefIgnoreBluetoothWarning) ? true : false; + strncpyz(gszAskURL, gprefsInit.szAskURL, sizeof(gszAskURL)); + strncpyz(gszDeviceId, gprefsInit.szDeviceId, sizeof(gszDeviceId)); + + // Temp buffer used for several things, including decompression, TBitmap compiling. + + gcbScratch = 10 * 1024; + gpbScratch = new byte[gcbScratch]; + if (gpbScratch == NULL) + return false; + + // Requires gpbScratch to work + +#if defined(PIL) && defined(TRACE_TO_DB_LOG) + DbLogInit(); +#endif + + // Init event manager + + gevm.Init(); + + // Match display with data + + if (!InitDisplay(imm)) { + HostOutputDebugString("Display didn't initialize."); + return false; + } + + if (!LoadGameData()) { + HostOutputDebugString("Game data didn't load."); + return false; + } + + // Init memory manager *after* setting screen mode so we know how much dyn ram is left after + // allocating back buffers (potentially from main memory). + + if (!InitMemMgr()) { + HostOutputDebugString("Memory manager didn't initialize."); + return false; + } + + // Init cache mgr + + if (!gcam.Init()) { + HostOutputDebugString("Cache manager didn't initialize."); + return false; + } + + // Init mapping tables after mem mgr since it uses mem mgr to store the tables + + if (!ggame.InitCoordMappingTables()) { + HostMessageBox(TEXT("Out of memory, could not initialize game!")); + return false; + } + + // Init MultiFormMgr + + if (!InitMultiFormMgr()) { + HostOutputDebugString("Multi form manager didn't initialize."); + return false; + } + + // Initialize the Shell + + if (!gshl.Init()) { + HostOutputDebugString("Shell didn't initialize."); + return false; + } + + // Make Shell palette and shadow map active + + ClearDisplay(); + gshl.SetPalette(); + + // Init TBitmapCode + + TBitmap::InitClass(); + m_wf |= kfGameInitBitmap; + + // Init form / control requirements + + ButtonControl::InitClass(); + CheckBoxControl::InitClass(); + PipMeterControl::InitClass(); + ListControl::InitClass(); + DamageMeterControl::InitClass(); + + // Save around game.ini + + gpiniGame = LoadIniFile(gpakr, "game.ini"); + if (gpiniGame == NULL) + return false; + + // Save around the form.ini + + gpiniForms = LoadIniFile(gpakr, "forms.ini"); + if (gpiniForms == NULL) + return false; + + // Load fonts + + for (int ifnt = 0; ifnt < kcFonts; ifnt++) { + char szProp[2]; + szProp[0] = ifnt + '0'; + szProp[1] = 0; + char szT[kcbFilename]; + if (gpiniGame->GetPropertyValue("Fonts", szProp, szT, sizeof(szT))) { + gapfnt[ifnt] = LoadFont(szT); + if (gapfnt[ifnt] == NULL) { + Assert("LoadFont failed"); + return false; + } + + // HACK: shadow font glyphs need to overlap by one pixel when drawn + + if (strcmp(szT, "shadowfont.fnt") == 0) { + gapfnt[ifnt]->SetGlyphOverlap(1); + gapfnt[ifnt]->SetLineOverlap(2); + } + } + } + m_wf |= kfGameInitFonts; + + // Load string table + + gpstrtbl = new StringTable(); + Assert(gpstrtbl != NULL, "out of memory!"); + if (gpstrtbl == NULL) + return false; + if (!gpstrtbl->Init("strings.bin")) + return false; + + // Init sound system + + gsndm.Init(); + if (gprefsInit.nVolume != (word)-1) + gsndm.SetVolume(gprefsInit.nVolume); + gsndm.Enable((gprefsInit.wfPrefs & kfPrefSoundMuted) == 0); + + // Clear out stale save games + + DeleteStaleSaveGames(); + + // We're done with game initialization + + m_wf |= kfGameInitDone; + + return true; +} + +bool Game::CheckMemoryAvailable() +{ + // The following constants have been calculated based on actual device measurements, + // scenario tests and gob count partitioning as of 8/16/03. Actual data is in \ht\docs\size.xls. + // The "min" scenario is the minimum playable scenario. If kcbDynMemMin is available, the following + // are the unit counts. If "max" scenario is the maximum scenario. If kcbDynMemMax is available, the + // following are the unit counts. This is partitioned by the gob count maximum and not the memory + // available (once over kcbDynMemMax it's gravy). The mem sizes are what is available at this + // point in time: PalmOS allocs globals and calls PilotMain. + // Note also the computer unit counts are all shared between all computer sides. + // Note that some of the computer units counts in certain levels may already be exceeded; that is ok. + +// TODO: All gob limits should be validated in M. + +// Non-unit gob limits. + +#define kcSceneryGobsLimit 100 +#define kcScorchGobsLimit 30 +#define kcSupportGobsLimit 50 + +// min limits +#define kcbDynMemMin 130000 +#define kcStructGobsHumanMinSP 39 +#define kcStructGobsComputerMinSP 52 +#define kcMuntGobsHumanMinSP 60 +#define kcMuntGobsComputerMinSP 80 + +// med limits +#define kcbDynMemMed 140000 +#define kcStructGobsHumanMedSP 49 +#define kcStructGobsComputerMedSP 65 +#define kcMuntGobsHumanMedSP 79 +#define kcMuntGobsComputerMedSP 105 + +// max limits +#define kcbDynMemMax 150000 +#define kcStructGobsHumanMaxSP 55 +#define kcMuntGobsHumanMaxSP 88 +#define kcStructGobsComputerMaxSP 72 +#define kcMuntGobsComputerMaxSP 117 + +#if (kcSceneryGobsLimit+kcScorchGobsLimit+kcSupportGobsLimit+kcStructGobsHumanMaxSP+kcStructGobsComputerMaxSP+kcMuntGobsHumanMaxSP+kcMuntGobsComputerMaxSP > kcpgobMax) +#error Gob count limit error! +#endif + +// mp limits + +#define kcStructGobsMaxMP 55 // 33 +#define kcMuntGobsMaxMP 88 // 66 + +#ifndef __CPU_68K +#if (kcSceneryGobsLimit+kcScorchGobsLimit+kcSupportGobsLimit+kcStructGobsMaxMP*4+kcMuntGobsMaxMP*4 > kcpgobMax) +#error Gob count limit error! +#endif +#endif + + // Assume max. Works for all platforms except lowest end Palms. + + gcStructGobsHumanLimitSP = kcStructGobsHumanMaxSP; + gcStructGobsComputerLimitSP = kcStructGobsComputerMaxSP; + gcMuntGobsHumanLimitSP = kcMuntGobsHumanMaxSP; + gcMuntGobsComputerLimitSP = kcMuntGobsComputerMaxSP; + gcStructGobsLimitMP = kcStructGobsMaxMP; + gcMuntGobsLimitMP = kcMuntGobsMaxMP; + gcSceneryGobsLimit = kcSceneryGobsLimit; + gcScorchGobsLimit = kcScorchGobsLimit; + gcSupportGobsLimit = kcSupportGobsLimit; + +#ifdef PIL + +#if 0 + // Limit test + + gcStructGobsHumanLimitSP = kcStructGobsHumanMinSP; + gcStructGobsComputerLimitSP = kcStructGobsComputerMinSP; + gcMuntGobsHumanLimitSP = kcMuntGobsHumanMinSP; + gcMuntGobsComputerLimitSP = kcMuntGobsComputerMinSP; + +#else + + // Max scenario? + UInt32 cbFree, cbMax; + MemHeapFreeBytes(0, &cbFree, &cbMax); + if (cbFree < kcbDynMemMax) { + if (cbFree >= kcbDynMemMed) { + gcStructGobsHumanLimitSP = kcStructGobsHumanMedSP; + gcStructGobsComputerLimitSP = kcStructGobsComputerMedSP; + gcMuntGobsHumanLimitSP = kcMuntGobsHumanMedSP; + gcMuntGobsComputerLimitSP = kcMuntGobsComputerMedSP; + } + else if (cbFree >= kcbDynMemMin) { + gcStructGobsHumanLimitSP = kcStructGobsHumanMinSP; + gcStructGobsComputerLimitSP = kcStructGobsComputerMinSP; + gcMuntGobsHumanLimitSP = kcMuntGobsHumanMinSP; + gcMuntGobsComputerLimitSP = kcMuntGobsComputerMinSP; + } + else { + HostNotEnoughMemory(false, cbFree, kcbDynMemMin); + return false; + } + } +#endif +#endif + + return true; +} + +void Game::CalcUnitCountDeltas(Level *plvl) +{ + // After the level is loaded this gets called. It calcs how many initial + // units there are and recalcs unit deltas. + // + // NOTE: For now all this does is take the computer's extra structure count + // and gives it to the human player. + + int cComputerStructuresInitial = 0; + for (int side = ksideNeutral; side < kcSides; side++) { + Player *pplr = gplrm.GetPlayer(side); + if (pplr->GetFlags() & kfPlrComputer) + cComputerStructuresInitial += plvl->GetSideInfo(side)->cStructuresInitial; + } + + // HACK ALERT: Give the computer room to build 5 structures + + int cStructuresAvailable = gcStructGobsComputerLimitSP - cComputerStructuresInitial - 5; + + // Give the rest to the human player + + if (cStructuresAvailable > 0) { + gcStructGobsComputerDeltaSP = -cStructuresAvailable; + gcStructGobsHumanDeltaSP = cStructuresAvailable; + } else { + gcStructGobsComputerDeltaSP = 0; + gcStructGobsHumanDeltaSP = 0; + } +} + +bool Game::LoadGameData() +{ + // We've choosen the best mode / data combo. Load the matching data + + Assert(m_immCurrent != -1); + char szPdb[20]; + sprintf(szPdb, "htdata%d%d.pdb", m_amm[m_immCurrent].nDepthData, m_amm[m_immCurrent].nSizeData); + + const char *pszMainDataDir = HostGetMainDataDir(); + if (!gpakr.Push(pszMainDataDir, szPdb)) + return false; + + // Load sound effects + + if (!gpakr.Push(pszMainDataDir, "htsfx.pdb")){ + HostMessageBox(TEXT("Required sound effects file missing")); + return false; + } + +#if 0 + // Enum and push addon data + + Enum enm; + char szAddon[64]; + char szDir[1024]; + while (HostEnumAddonFiles(&enm, szDir, sizeof(szDir), + szAddon, sizeof(szAddon))) { + if (CheckDatabaseVersion(szDir, szAddon, true)) { + gpakr.Push(szDir, szAddon); + } + } +#endif + + // Set global tile dimensions + // Perform hack for size 20 which really reuses 24x24 tiles but sizes other graphics proportionally + + gcxTile = m_amm[m_immCurrent].nSizeData; + if (gcxTile == 20) + gcxTile = 24; + gcyTile = gcxTile; + + // If the tile size is 24, then gpbScratch is ~2.25 times bigger to accomodate TBitmap compiling size + // increase + + if (gcxTile > 16) { + if (gcbScratch <= 10 * 1024) { + delete gpbScratch; + if (gcxTile == 32) { + gcbScratch = 35 * 1024; + } else { + gcbScratch = 23 * 1024; + } + gpbScratch = new byte[gcbScratch]; + if (gpbScratch == NULL) + return false; + } + } + + return true; +} + +bool Game::InitCoordMappingTables() +{ + // Initialize world -> pixel coord mapping table + +#define knSplit 32 +//#define knSplit 4 + gmpPcFromWc = (short *)gmmgr.AllocPtr(2 * kwcMax); + if (gmpPcFromWc == NULL) + return false; + + Assert(kwcMax / knSplit <= gcbScratch); + Assert((kwcMax / knSplit) * knSplit == kwcMax); + + short s = 0; + short nOverflow = 0; + for (int j = 0; j < knSplit; j++) { + short *psT = (short *)gpbScratch; + for (int i = 0; i < kwcMax / knSplit; i++) { + // Duplicate the behavior of gmpWcFromPc so that + // gmpPcFromWc[gmpWcFromPc[2]] == 2 + + *psT++ = s; + nOverflow += gcxTile; + if (nOverflow == 256) { + nOverflow = 0; + s++; + } else if (nOverflow > 256) { + nOverflow -= 256; + } else if (nOverflow + gcxTile - 1 >= 256) { + s++; + } + } + gmmgr.WritePtr(gmpPcFromWc, j * 2 * (kwcMax / knSplit), gpbScratch, 2 * (kwcMax / knSplit)); + } + + // Initialize pixel -> world coord mapping table + + WCoord *pwcT = (WCoord *)gpbScratch; + for (int i = 0; i <= 64 * 32; i++) { + *pwcT++ = (WCoord)((i * 256L) / gcxTile); + } + + // These "+ 1"s are to support the mapping of inclusive/exclusive rects + // that lay at the extreme of the allowed coordinate range + + gmpWcFromPc = (WCoord *)gmmgr.AllocPtr((2 * 64 * 32) + 1); + if (gmpWcFromPc == NULL) + return false; + gmmgr.WritePtr(gmpWcFromPc, 0, gpbScratch, (2 * 64 * 32) + 1); + + return true; +} + +bool Game::InitMemMgr() +{ + // 8/30/03 settings + // There are 12 save game slots typically + 1 auto-save slot + // Reserve 20K * 13 for save games (260K ideally) + + // For cache use: There is approximately 75K of fixed allocs + // Hi-res runs smoothly at 1M cache with room to spare + // Lo-res settings are (1M - 75K) * 16 / 24 (approximately) + // Should grab more than enough space if possible to run smoothly + +// How much to reserve from dynamic memory for dynamic allocations - the rest +// will be used by the storage memory manager. This is more than enough; we don't get +// anywhere close this much on low-end Palms. + +#ifdef __CPU_68K +// Single player only +#define kcbDynReserve ((dword)384 * 1024) +#else +// Single and multi player +#define kcbDynReserve ((dword)512 * 1024) +#endif + +#define kcbSaveGame ((dword)20 * 1024) +#define kcbMaxSaveGame ((dword)13 * kcbSaveGame) +#define kcbMinSaveGame ((dword)6 * kcbSaveGame) + +// Fixed allocs from storage mem + +#define kcbFixed ((dword)150 * 1024) + +// Min / max needed for storage memory manager + +#define kcbMinHires ((dword)512 * 1024) +#define kcbWarnHires ((dword)768 * 1024) +#define kcbMaxHires ((dword)1024 * 1024) + +#define kcbMinLores ((kcbMinHires - kcbFixed) * 2 / 3 + kcbFixed) +#define kcbWarnLores ((kcbWarnHires - kcbFixed) * 2 / 3 + kcbFixed) +#define kcbMaxLores ((kcbMaxHires - kcbFixed) * 2 / 3 + kcbFixed) + + bool fHires = (m_amm[m_immCurrent].nSizeData > 16); + dword cbMin, cbWarn, cbMax; + if (fHires) { + cbMin = kcbMinHires; + cbWarn = kcbWarnHires; + cbMax = kcbMaxHires; + } else { + cbMin = kcbMinLores; + cbWarn = kcbWarnLores; + cbMax = kcbMaxLores; + } + + // How much storage memory is available? + + dword cbDyn; + dword cbStorage; + MemMgr::GetInitSize(kcbDynReserve, &cbDyn, &cbStorage); + dword cbAvail = cbDyn + cbStorage; + + // Prioritize storage memory size over # of save games, but keep at least + // a min save game size + // Calc space to reserve for save games + + dword cbSaveGameReserve = kcbMinSaveGame; + if (cbAvail > cbMax) { + if (cbAvail - cbMax >= kcbMaxSaveGame) + cbSaveGameReserve = kcbMaxSaveGame; + } + + // Calc what is left over + + dword cbAlloc = 0; + if (cbAvail > cbSaveGameReserve) + cbAlloc = cbAvail - cbSaveGameReserve; + + // If there is space left over, take it up to cbMax * 1 1/2 + + dword cbCeiling = cbMax * 3 / 2; + if (cbAlloc > cbCeiling) + cbAlloc = cbCeiling; + + // Error if not enough memory + + if (cbAlloc < cbMin) { + HostNotEnoughMemory(true, cbStorage, cbMin + cbSaveGameReserve - cbDyn); + return false; + } + + // Warn if below threshold + + if (cbAlloc < cbWarn) + HostMessageBox(TEXT("For better performance, increase the amount of memory available!")); + + // Now initialize + + dword cbTotal; + gmmgr.Init(kcbDynReserve, cbAlloc, &cbTotal); + + return true; +} + +#define kcmmGrow 32 + +void Game::AddModeMatches(int nDepthData, int nSizeData, int nDepthOrGreater, int cxWidthOrGreater) +{ + HostOutputDebugString("AddModeMatches %d, %d, %d, %d", + nDepthData, nSizeData, nDepthOrGreater, cxWidthOrGreater); + + int cmodes = gpdisp->GetModeCount(); + for (int n = 0; n < cmodes; n++) { + ModeInfo mode; + gpdisp->GetModeInfo(n, &mode); + HostOutputDebugString("mode %d cx %d cy %d nDepth %d nDegree %d", + n, mode.cx, mode.cy, mode.nDepth, mode.nDegreeOrientation); + if (mode.nDepth < nDepthOrGreater) + continue; + if (mode.cx < cxWidthOrGreater) + continue; + if (mode.cy < cxWidthOrGreater) + continue; + if (m_cmm == m_cmmAlloc) { + ModeMatch *pmm = new ModeMatch[m_cmmAlloc + kcmmGrow]; + if (pmm == NULL) + continue; + if (m_amm != NULL) { + memcpy(pmm, m_amm, sizeof(ModeMatch) * m_cmm); + delete m_amm; + } + m_amm = pmm; + m_cmmAlloc += kcmmGrow; + } + + m_amm[m_cmm].imode = n; + m_amm[m_cmm].nDepthData = nDepthData; + m_amm[m_cmm].nSizeData = nSizeData; + m_cmm++; + } +} + +#define kfMatchWidthBounded 1 +#define kfMatchExactDepth 2 +#define kfMatchNative 4 + +int Game::FindBestModeMatch2(int nDepthData, int nSizeData, int nDepthMode, int cxWidthModeMin, int cxWidthModeMax, byte bfMatch) +{ + // Also, the largest Y is returned at a given width + + int nBest = -1; + int cyLargestLast = -1; + int cxLargestLast = -1; + int nDepthClosestLast = 0x7fff; + for (int n = 0; n < m_cmm; n++) { + // Is this match for the requested data depth / data size? + + ModeMatch *pmm = &m_amm[n]; + if (pmm->nDepthData != nDepthData || pmm->nSizeData != nSizeData) + continue; + + // Native modes only? + + ModeInfo mode; + gpdisp->GetModeInfo(pmm->imode, &mode); + if (bfMatch & kfMatchNative) { + if (!mode.fNative) + continue; + } + + // Is mode width compatible? + + if (bfMatch & kfMatchWidthBounded) { + if (mode.cx < cxWidthModeMin || mode.cx > cxWidthModeMax) + continue; + } else { + if (mode.cx < cxWidthModeMin) + continue; + } + + // Is depth compatible? + + if (bfMatch & kfMatchExactDepth) { + // Must be exact depth + + if (mode.nDepth != nDepthMode) + continue; + } else { + // Must be closest depth that is >= mode.nDepth + + if (mode.nDepth < nDepthMode) + continue; + } + + // Found a criteria match. + // Remember closest depth + + if (mode.nDepth < nDepthClosestLast) { + nDepthClosestLast = mode.nDepth; + nBest = n; + } + + // Remember largest width + + if (mode.cx > cxLargestLast) { + cxLargestLast = mode.cx; + nBest = n; + } + + // Remember largest Y + + if (mode.cx == cxLargestLast && mode.cy > cyLargestLast) { + cyLargestLast = mode.cy; + nBest = n; + } + } + + return nBest; +} + +int Game::FindBestModeMatch(int nSizeDataAbove) +{ + // Now find best match if we don't already have a match + + static byte s_abfMatchOrder[] = { + kfMatchWidthBounded | kfMatchExactDepth, // width bounded exact depth + kfMatchWidthBounded, // width bounded closest depth + kfMatchExactDepth, // closest width exact depth + 0, // closest width closest depth emulated + }; + + struct DataModePairs { + byte nDepthData; + byte nSizeData; + int nDepthMode; + int cxWidthModeMin; + int cxWidthModeMax; + }; + + static DataModePairs s_admp[] = { + // Color high res first + + { 8, 32, 8, 320, 9999 }, + { 8, 24, 8, 320, 9999 }, + { 8, 20, 8, 240, 319 }, + { 8, 16, 8, 160, 239 }, + + // Grayscale next at 8bpp (since grayscale data is stored 8bpp this is faster) + + { 4, 24, 8, 320, 9999 }, + { 4, 20, 8, 240, 319 }, + { 4, 16, 8, 160, 239 }, + + // Grayscale next at 4bpp + + { 4, 24, 4, 320, 9999 }, + { 4, 20, 4, 240, 319 }, + { 4, 16, 4, 160, 239 }, + }; + + // Go through each data / mode pairing and perform searches based on the match order + // Find the best match + + int immBest = -1; + for (int n = 0; n < ARRAYSIZE(s_admp); n++) { + DataModePairs *pdmp = &s_admp[n]; + if (pdmp->nSizeData > nSizeDataAbove) { + for (int j = 0; j < ARRAYSIZE(s_abfMatchOrder); j++) { + immBest = FindBestModeMatch2(pdmp->nDepthData, pdmp->nSizeData, pdmp->nDepthMode, pdmp->cxWidthModeMin, pdmp->cxWidthModeMax, s_abfMatchOrder[j]); + if (immBest != -1) + break; + } + if (immBest != -1) + break; + } + } + return immBest; +} + +bool Game::InitDisplay(int immRequested) +{ + // Create a display. + + gpdisp = HostCreateDisplay(); + if (gpdisp == NULL) + return false; + + gpsprm = gpdisp->GetSpriteManager(); + + // Find all raw data / mode matches + + ddword ddwSizes8bpp = IsDataPresent(8); + ddword ddwSizes4bpp = IsDataPresent(4); + if (ddwSizes8bpp == 0 && ddwSizes4bpp == 0) { + HostMessageBox(TEXT("Incorrect game graphics version or no game graphics installed")); + return false; + } + + if (ddwSizes8bpp & (((ddword)1) << 32)) + AddModeMatches(8, 32, 8, 320); // 8 depth data, 24 size data, 8 or higher dst, 320 or higher dst + if (ddwSizes8bpp & (((ddword)1) << 24)) + AddModeMatches(8, 24, 8, 320); // 8 depth data, 24 size data, 8 or higher dst, 320 or higher dst + if (ddwSizes8bpp & (((ddword)1) << 20)) + AddModeMatches(8, 20, 8, 240); + if (ddwSizes8bpp & (((ddword)1) << 16)) + AddModeMatches(8, 16, 8, 160); + + if (ddwSizes4bpp & (((ddword)1) << 24)) + AddModeMatches(4, 24, 4, 320); // 4 depth data, 24 size data, 4 or higher dst, 320 or higher dst + if (ddwSizes4bpp & (((ddword)1) << 20)) + AddModeMatches(4, 20, 4, 240); + if (ddwSizes4bpp & (((ddword)1) << 16)) + AddModeMatches(4, 16, 4, 160); + +#if 0 // def DEBUG + // Print out mode matches + + HostMessageBox("Mode Matches:"); + ModeInfo mode; + for (int n = 0; n < m_cmm; n++) { + ModeMatch *pmm = &m_amm[n]; + gpdisp->GetModeInfo(pmm->imode, &mode); + HostMessageBox("%dx%dx%d cyGraffiti=%d, depthData=%d, sizeData=%d", mode.cx, mode.cy, mode.nDepth, mode.cyGraffiti, pmm->nDepthData, pmm->nSizeData); + } + + // Print out modes + + HostMessageBox("Modes:"); + for (n = 0; n < gpdisp->GetModeCount(); n++) { + gpdisp->GetModeInfo(n, &mode); + HostMessageBox("%dx%dx%d cyGraffiti=%d", mode.cx, mode.cy, mode.nDepth, mode.cyGraffiti); + } +#endif + + // Find best match + + int immBest = FindBestModeMatch(0); + + // If we a not requesting a specific size, choose one + + int immNew = immRequested; + if (immNew == -1) { + // If we found a best, check to see if it matches what is best in prefs. If it doesn't match best + // in prefs, then assume there has been a config change and ignore prefs, otherwise try to use + // prefs + + if (immBest != -1) { + bool fMatchesPrefs = true; + ModeInfo mode; + gpdisp->GetModeInfo(m_amm[immBest].imode, &mode); + if (mode.cx != gprefsInit.cxModeBest) + fMatchesPrefs = false; + if (mode.cy != gprefsInit.cyModeBest) + fMatchesPrefs = false; + if (mode.nDepth != gprefsInit.nDepthModeBest) + fMatchesPrefs = false; + if (mode.nDegreeOrientation != gprefsInit.nDegreeOrientationBest) + fMatchesPrefs = false; + if (m_amm[immBest].nDepthData != gprefsInit.nDepthDataBest) + fMatchesPrefs = false; + if (m_amm[immBest].nSizeData != gprefsInit.nSizeDataBest) + fMatchesPrefs = false; + if (fMatchesPrefs) { + // Looks like there hasn't been a config change (new data / new device) so try + // to find the last settings. + + for (int immT = 0; immT < m_cmm; immT++) { + ModeMatch *pmmT = &m_amm[immT]; + ModeInfo modeT; + gpdisp->GetModeInfo(pmmT->imode, &modeT); + if (modeT.cx != gprefsInit.cxModeLast) + continue; + if (modeT.cy != gprefsInit.cyModeLast) + continue; + if (modeT.nDepth != gprefsInit.nDepthModeLast) + continue; + if (modeT.nDegreeOrientation != gprefsInit.nDegreeOrientationLast) + continue; + if (pmmT->nDepthData != gprefsInit.nDepthDataLast) + continue; + if (pmmT->nSizeData != gprefsInit.nSizeDataLast) + continue; + immNew = immT; + break; + } + + if (immNew == -1) { + // Couldn't find what was requested. Use best + + immNew = immBest; + } + } else { + // Doesn't match prefs, there has been a config change. Use what we think is best + // rather than prefs + + immNew = immBest; + } + } + } + if (immNew == -1) { + HostMessageBox(TEXT("No suitable graphics mode found for installed graphics data")); + return false; + } + + // If a mode was not explicitly chosen, bring up helpful suggestions about resolution / data size + + if (immRequested == -1) { +#if 0 +//doesn't work since if the higher res data isn't in memory then the option to use it won't be considered + + // See if there is a mode that'll support higher-res graphics + + int immBetter = FindBestModeMatch(m_amm[immNew].nSizeData); + if (immBetter != -1 && immBetter != immNew) + HostMessageBox(TEXT("This device supports higher resolution game graphics.")); +#endif + + // Give a message if grayscale data is being shown on a color device + + if (m_amm[immNew].nDepthData < 8) { + for (int n = 0; n < gpdisp->GetModeCount(); n++) { + ModeInfo mode; + gpdisp->GetModeInfo(n, &mode); + if (mode.nDepth >= 8) { + HostMessageBox(TEXT("The game graphics database is grayscale and your device supports color. You may wish to install a color game graphics database.")); + break; + } + } + } + } + + // Try to set this mode + +#if defined(WIN) && !defined(CE) + // If changing modes, use the default scale (-1) + + int nScale = gprefsInit.nScale; + if (immRequested != -1) + nScale = -1; + if (!gpdisp->SetMode(m_amm[immNew].imode, nScale)) + return false; +#else + if (!gpdisp->SetMode(m_amm[immNew].imode)) + return false; +#endif + + // Worked, remember new and best + + m_immCurrent = immNew; + m_immBest = immBest; + + // Get clipping dib + + gpbmClip = gpdisp->GetClippingDib(); + + // Set the global fixed colors table + + switch (m_amm[m_immCurrent].nDepthData) { + case 4: + gaclrFixed = gaclr4bpp; + gfGrayscale = true; + break; + + case 8: + gaclrFixed = gaclr8bpp; + gfGrayscale = false; + break; + + default: + Assert("Whoa! Unsupported color depth"); + break; + } + + return true; +} + +void Game::RequestModeChange(int immNew) +{ + // If using this already, nothing to do + + if (immNew == m_immCurrent) + return; + + // Stop the app; this'll cause it to restart with the new selection + + gevm.SetAppStopping(); + gimmReinitialize = immNew; +} + +bool Game::InitMultiFormMgr() +{ + // Create the MultiFormMgr + + gpmfrmm = new MultiFormMgr(); + Assert(gpmfrmm != NULL, "out of memory!"); + if (gpmfrmm == NULL) + return false; + if (!gpmfrmm->Init(gpdisp->GetBackDib(), false)) + return false; + + // Grab dib size + + DibBitmap *pbm = gpdisp->GetBackDib(); + if (pbm == NULL) + return false; + Size sizDib; + pbm->GetSize(&sizDib); + + // Initialize some global metrics used for drawing things as appropriate + // for the data size being used. + + switch (m_amm[m_immCurrent].nSizeData) { + case 16: + gcxFullScreenFormDiv10 = 160 / 10; + break; + + case 20: + gcxFullScreenFormDiv10 = 240 / 10; + break; + + case 24: + case 32: + gcxFullScreenFormDiv10 = 320 / 10; + break; + } + gcxyBorder = PcFromFc(1); + + // HT runs on known and future unknown screen modes. Because of this the main game form + // is dynamically sized and repositioned based on knowledge of how the game form is + // layed out. + + TBitmap *ptbmMenuButton = LoadTBitmap("menuup.tbm"); + if (ptbmMenuButton == NULL) { + return false; + } + Size sizMenuButton; + ptbmMenuButton->GetSize(&sizMenuButton); + delete ptbmMenuButton; + + // cySim is the simulation area + // cyInput is the control stip at the bottom + // cyInputForm is present for capturing input in the graffiti area + + int cyInput = sizMenuButton.cy; + int cySim = sizDib.cy - cyInput; + ModeInfo mode; + gpdisp->GetMode(&mode); + int cyInputForm = cyInput + mode.cyGraffiti; + + // We know the playfield size + + m_sizPlayfield.cx = sizDib.cx; + m_sizPlayfield.cy = cySim; + + // Add two partitions + + DibBitmap *pbmSim = pbm->Suballoc(0, cySim); + if (pbmSim == NULL) + return false; + gpfrmmSim = CreateFormMgr(pbmSim); + if (gpfrmmSim == NULL) { + delete pbmSim; + return false; + } + gpupdSim = gpfrmmSim->GetUpdateMap(); + Rect rc; + rc.Set(0, 0, m_sizPlayfield.cx, cySim); + gpmfrmm->AddFormMgr(gpfrmmSim, &rc); + DibBitmap *pbmInput = pbm->Suballoc(cySim, cyInput); + if (pbmInput == NULL) + return false; + gpfrmmInput = CreateFormMgr(pbmInput); + if (gpfrmmInput == NULL) { + delete pbmInput; + return false; + } + gpfrmmInput->SetFlags(gpfrmmInput->GetFlags() | kfFrmmNoScroll); + rc.Set(0, cySim, m_sizPlayfield.cx, cySim + cyInputForm); + gpmfrmm->AddFormMgr(gpfrmmInput, &rc); + + // Tell the display about the two formmgr's that make + // up the screen. Some displays will want this for managing + // the front dib specially. + + gpdisp->SetFormMgrs(gpfrmmSim, gpfrmmInput); + + return true; +} + +void Game::ParseVersion(char *pszVersion, int *pnShipVersion, int *pnMajorVersion, char *pchMinorVersion) +{ + // Parse the version. + // A.B[c], for example 1.0a + // A is ship version + // B is major version + // C is minor version + + // Scan for ship version. Allow 0., or no ship version specified + + int ichAfterDot = 0; + int c = IniScanf(pszVersion, "%d.%+", pnShipVersion, &ichAfterDot); + if (c == 0) { + *pnShipVersion = 0; + ichAfterDot = 0; + } + + // Scan for major version. Do it this way to support 1.01 as less than 1.10 + + *pnMajorVersion = 0; + int ichAfterMajor = ichAfterDot; + if (pszVersion[ichAfterMajor] >= '0' && pszVersion[ichAfterMajor] <= '9') { + *pnMajorVersion += (pszVersion[ichAfterMajor] - '0') * 10; + ichAfterMajor++; + } + if (pszVersion[ichAfterMajor] >= '0' && pszVersion[ichAfterMajor] <= '9') { + *pnMajorVersion += (pszVersion[ichAfterMajor] - '0'); + ichAfterMajor++; + } + + // Scan for minor version. Allow none + + char *pszAfterMajor = &pszVersion[ichAfterMajor]; + if (*pszAfterMajor >= 'a' && *pszAfterMajor <= 'z') { + *pchMinorVersion = *pszAfterMajor; + } else { + *pchMinorVersion = 0; + } +} + +bool Game::GetFormattedVersionString(char *pszVersion, char *pszOut) +{ + int nShipVersion, nMajorVersion; + char chMinorVersion; + ggame.ParseVersion(pszVersion, &nShipVersion, &nMajorVersion, &chMinorVersion); + if (nShipVersion == 0 && nMajorVersion == 0 && chMinorVersion == 0) + return false; + + if (nMajorVersion % 10 == 0) { + if (chMinorVersion == 0) { + sprintf(pszOut, "v%d.%d", nShipVersion, nMajorVersion / 10); + } else { + sprintf(pszOut, "v%d.%d%c", nShipVersion, nMajorVersion / 10, chMinorVersion); + } + } else { + if (chMinorVersion == 0) { + sprintf(pszOut, "v%d.%d%d", nShipVersion, nMajorVersion / 10, nMajorVersion % 10); + } else { + sprintf(pszOut, "v%d.%d%d%c", nShipVersion, nMajorVersion / 10, nMajorVersion % 10, chMinorVersion); + } + } + return true; +} + +bool Game::CheckDatabaseVersion(const char *pszDir, char *pszPdb, + bool fUpwardCompatOK) +{ + if (!gpakr.Push(pszDir, pszPdb)) + return false; + +#ifdef DEV_BUILD + // If DEV_BUILD, assume all database versions are compatible so that we can run in + // mismatched situations on purpose + + gpakr.Pop(); + return true; +#else + + FileMap fmap; + char *pszDataVersion = (char *)gpakr.MapFile("version.txt", &fmap); + if (pszDataVersion == NULL) { + gpakr.Pop(); + return false; + } + + // Parse the version + + int nVersionDataShip; + int nVersionDataMajor; + char chVersionDataMinor; + ParseVersion(pszDataVersion, &nVersionDataShip, &nVersionDataMajor, &chVersionDataMinor); + gpakr.UnmapFile(&fmap); + gpakr.Pop(); + + // Compare versions + + return IsVersionCompatibleWithExe(nVersionDataShip, nVersionDataMajor, chVersionDataMinor, fUpwardCompatOK); +#endif +} + +bool Game::IsVersionCompatibleWithExe(int nVersionCompareShip, int nVersionCompareMajor, char chVersionCompareMinor, bool fUpwardCompatOK) +{ + // Unstamped EXEs can load any version database + + if (stricmp(gszVersion + 1, "++VERSION+++") == 0) // this wierd +1 stuff is so the version stamper doesn't stamp this string + return true; + + int nVersionShip, nVersionMajor; + char chVersionMinor; + ParseVersion(gszVersion, &nVersionShip, &nVersionMajor, &chVersionMinor); + + if (fUpwardCompatOK) { + // If ship version less, not ok + + if (nVersionShip < nVersionCompareShip) + return false; + + // If ship version equal, check major + + if (nVersionShip == nVersionCompareShip) { + if (nVersionMajor < nVersionCompareMajor) + return false; + } + } else { + // Ship versions must match + + if (nVersionShip != nVersionCompareShip) + return false; + + // Major versions must match too. + + if (nVersionMajor != nVersionCompareMajor) + return false; + } + + // Minor versions can be different. + + return true; +} + +ddword Game::IsDataPresent(int cBpp) +{ + const char *pszMainDataDir = HostGetMainDataDir(); + char szPdb[kcbFilename]; + ddword ddwSizes = 0; + sprintf(szPdb, "htdata%d16.pdb", cBpp); + if (CheckDatabaseVersion(pszMainDataDir, szPdb, false)) + ddwSizes |= (((ddword)1) << 16); + sprintf(szPdb, "htdata%d20.pdb", cBpp); + if (CheckDatabaseVersion(pszMainDataDir, szPdb, false)) + ddwSizes |= (((ddword)1) << 20); + sprintf(szPdb, "htdata%d24.pdb", cBpp); + if (CheckDatabaseVersion(pszMainDataDir, szPdb, false)) + ddwSizes |= (((ddword)1) << 24); + sprintf(szPdb, "htdata%d32.pdb", cBpp); + if (CheckDatabaseVersion(pszMainDataDir, szPdb, false)) + ddwSizes |= (((ddword)1) << 32); + + return ddwSizes; +} + +// PlayLevel closes and deletes pstmSavedGame at its first opportunity or +// in the event of an error. Either way when it returns pstmSavedGame has +// been deleted. + +int Game::PlayLevel(MissionIdentifier *pmiid, Stream *pstmSavedGame, int nRank) +{ + Dictionary dictPvarsRetry; + + m_szNextLevel[0] = 0; + + if (pstmSavedGame != NULL) { + // Load MissionIdentifier + + pstmSavedGame->Read(&m_miid.packid, sizeof(m_miid.packid)); + pstmSavedGame->ReadString(m_miid.szLvlFilename, + sizeof(m_miid.szLvlFilename)); + + // Load next level filename + + pstmSavedGame->ReadString(m_szNextLevel, sizeof(m_szNextLevel)); + + // Load pvars + + m_dictPvars.LoadState(pstmSavedGame); + + // Prep for level retry + + dictPvarsRetry.Init(&m_dictPvars); + + } else { + // This is the mission we want to load + m_miid = *pmiid; + + // Clear pvars + + m_dictPvars.Clear(); + + // hack for carrying forward a rank from the demo + + if (nRank > 0) { + char szT[20]; + itoa(nRank, szT, 10); + m_dictPvars.Set("rank", szT); + } + + } + + // Loop while the player retries the level or as new levels are set via SetNextLevel. + + int nGo = knGoSuccess; + do { + // Randomness is good but not REAL randomness. + +#ifdef STRESS + if (gfStress) { + SetRandomSeed(gtimm.GetTickCount()); + } else { + SetRandomSeed(0x29a); + } +#else + SetRandomSeed(gfMultiplayer ? gtimm.GetTickCount() : 0x29a); +#endif + // Mount the pdb with the mission pack + + if (!gppackm->Mount(gpakr, &m_miid.packid)) { + pstmSavedGame->Close(); + delete pstmSavedGame; + nGo = knGoInitFailure; + break; + } + + // The level can be changed by a SetNextMisson action. This is how + // we manage progressing from one level to the next. + + if (pstmSavedGame != NULL) { + if (!gplrm.LoadState(pstmSavedGame)) { + gppackm->Unmount(gpakr, &m_miid.packid); + pstmSavedGame->Close(); + delete pstmSavedGame; + nGo = knGoInitFailure; + break; + } + } else { + // Prep the Player instances that govern the local player and the + // computer players using the SideInfo contained in the level. + + gplrm.Init(m_miid.szLvlFilename); + } + + // Play the level until the player loses, exits, or advances to the + // next level + + nGo = RunSimulation(pstmSavedGame, m_miid.szLvlFilename, 0, 0, NULL); + + // Only use the saved game stream the first time through. Any + // restarts start from the beginning of the level. + + pstmSavedGame = NULL; + + gppackm->Unmount(gpakr, &m_miid.packid); + + switch (nGo) { + case knGoAppStop: + case knGoInitFailure: + case knGoLoadSavedGame: + return nGo; + + case knGoSuccess: + + // If no next level, we're done. + + if (m_szNextLevel[0] == 0) { + if (gfDemo && + (stricmp(m_miid.szLvlFilename, "d_03.lvl") == 0)) { + + // if player just finished D3, save rank in prefs so we + // know to start from M3 when they buy a new copy. If they + // played D3 as a challenge mission, their rank will be -1 + + char szT[kcbPvarValueMax]; + if (ggame.GetVar("rank", szT, sizeof(szT))) + gnDemoRank = atoi(szT); + if (gnDemoRank < 0) + gnDemoRank = 0; + + SavePreferences(); + } + nGo = knGoAbortLevel; + } else { + // Mark this mission complete! + + gpcptm->MarkComplete(&m_miid); + + // Copy over new mission filename + + strncpyz(m_miid.szLvlFilename, m_szNextLevel, + sizeof(m_miid.szLvlFilename)); + + // Successful level completion carries pvars forward + + dictPvarsRetry.Init(&m_dictPvars); + m_szNextLevel[0] = 0; + } + break; + + // Retrying a level reverts pvars back to the state they were at upon + // level entry + + case knGoRetryLevel: + m_dictPvars.Init(&dictPvarsRetry); + break; + + default: + // Clear pvars + + m_dictPvars.Clear(); + dictPvarsRetry.Clear(); + break; + } + + } while (nGo != knGoAbortLevel); + + return nGo; +} + +// RunSimulation will Close and delete the passed-in Stream before returning + +int Game::RunSimulation(Stream *pstm, char *pszLevel, word wfRole, + dword gameid, Chatter *chatter) +{ + DialogForm *pfrm = (DialogForm *)gpmfrmm->LoadForm(gpiniForms, kidfLoading, new DialogForm()); + if (pfrm != NULL) { + pfrm->SetBackgroundColorIndex(kiclrBlack); + pfrm->SetTitleColor(kiclrBlack); + pfrm->SetBorderColorIndex(kiclr0CyanSide); + gpmfrmm->DrawFrame(false); + } + + // Retain whether or not this is a multiplayer game. Inquiring minds will + // want to know later. + + m_wf &= ~(kfGameMultiplayer | kfGameRoleServer); + if (wfRole & kfRoleMultiplayer) { + m_wf |= kfGameMultiplayer; + gfMultiplayer = true; + + // Pvars shouldn't carry into multiplayer games. + + m_dictPvars.Clear(); + } else { + gfMultiplayer = false; + } + if (wfRole & kfRoleServer) + m_wf |= kfGameRoleServer; + + gsndm.WaitSilence(); + bool fSuccess = InitSimulation(pstm, pszLevel, wfRole, gameid, chatter); + + if (pstm != NULL) { + if (!pstm->IsSuccess()) + fSuccess = false; + + pstm->Close(); + delete pstm; + } + + delete pfrm; + + if (!fSuccess) { + gfMultiplayer = false; + return knGoInitFailure; + } + + // Clear fog if asked ("-nofog" command line switch) + +//temp +#if 0 + gfClearFog = true; +#endif + + if (gfClearFog) + gsim.GetLevel()->GetFogMap()->RevealAll(gpupdSim); + + // Game loop + + m_fUpdateTriggers = false; + + int nGo; + Event evt; + while (true) { + if (!gevm.GetEvent(&evt, -1, false)) { + continue; + } + + if (FilterEvent(&evt)) + continue; + + if (evt.eType == appStopEvent) { + // Warn about multiplayer if the player pressed the home button + + if (m_wf & kfGameMultiplayer) { + if (!AskResignGame()) + continue; + } + if (evt.dw != knAppExit) + SaveReinitializeGame(); + nGo = knGoAppStop; + break; + } + + if (evt.eType == gameOverEvent) { + nGo = (int)evt.dw; + break; + } + + gevm.DispatchEvent(&evt); + } + + ExitSimulation(); + + // Eat potential cancel mode messages that due to MP exit ordering got + // posted after gameOverEvent + + if (gevm.PeekEvent(&evt, 0)) { + if (evt.eType == cancelModeEvent) + gevm.GetEvent(&evt, 0); + } + + // Assume not multiplayer anymore + + gfMultiplayer = false; + m_wf &= ~(kfGameMultiplayer | kfGameRoleServer); + + return nGo; +} + +bool Game::AskResignGame(bool fTellHost) +{ + bool fAppStopping = gevm.IsAppStopping(); + bool fAsk = true; +#ifdef IPHONE + // When an iPhone game exits, it exits without the opportunity to + // confirm with the user. So if Iphone and the app is stopping, just + // resign. + if (fAppStopping) { + fAsk = false; + } +#endif + + Assert(m_wf & kfGameMultiplayer); + + if (fAsk) { + // If app stopping is set + + if (fAppStopping) { + gevm.ClearAppStopping(); + } + + // Ask user + + char sz[128]; + if (m_wf & kfGameRoleServer) { + gpstrtbl->GetString(kidsExitHost, sz, sizeof(sz)); + } else { + gpstrtbl->GetString(kidsExitClient, sz, sizeof(sz)); + } + + // If the user wants to keep going, return with "app stopping" cleared + + if (!HtMessageBox(kidfMessageBoxQuery, kfMbWhiteBorder | kfMbKeepTimersEnabled, "Warning", sz)) + return false; + + // If app stopping set it again + + if (fAppStopping) { + gevm.SetAppStopping(); + } + } + + if (fTellHost && gptra != NULL) { + // Tell host + + PlayerResignNetMessage prng; + prng.pid = gpplrLocal->GetId(); + gptra->SendNetMessage(&prng); + } + return true; +} + +bool Game::AskObserveGame() +{ + bool fYes = false; + gpplrLocal->SetFlags(gpplrLocal->GetFlags() | kfPlrObserver); + + SimUIForm *psui = ggame.GetSimUIForm(); + psui->ClearSelection(); + + if (!gevm.IsAppStopping()) { + if (psui->GetRole() & kfRoleServer) { + fYes = HtMessageBox(kidfMessageBoxQuery, kfMbKeepTimersEnabled | kfMbWhiteBorder, + "GAME OVER", "Do you want to continue as an observer? Since this device hosting, answering no will stop the game."); + } else { + fYes = HtMessageBox(kidfMessageBoxQuery, kfMbKeepTimersEnabled | kfMbWhiteBorder, + "GAME OVER", "Do you want to continue as an observer?"); + } + + if (fYes) { + psui->SetObserving(); + } + } + + return fYes; +} + +bool Game::SaveReinitializeGame() +{ + if (m_fSkipSaveReinitialize) { + return false; + } + + // We don't save "reinitialize" games when playing multiplayer + + if (gfMultiplayer) + return false; + + Status("** Saving Game for Return **"); + Stream *pstm = HostNewSaveGameStream(knGameReinitializeSave, "Reinitialize"); + Assert(pstm != NULL); + if (pstm != NULL) { + ggame.SaveGame(pstm); // SaveGame closes and deletes the stream + return true; + } + + return false; +} + +bool Game::InitSimulation(Stream *pstm, char *pszLevel, word wfRole, + dword gameid, Chatter *chatter) { + + // Perform one-time Simulation initialization. This might seem like a + // strange place to do it but the goal is to put off the expensive stuff + // it does so it doesn't slow game launch time. + + m_gameid = gameid; + + if (m_fSimUninitialized) { + Status("Simulation Init (one-time)..."); + if (!gsim.OneTimeInit()) + return false; + m_fSimUninitialized = false; + } + + Status("Simulation Init (per-level)..."); + if (!gsim.PerLevelInit()) + return false; + + // Initialize the Simulation StateMachineMgr + + if (!gsmm.Init(&gsim)) { + gsim.PerLevelExit(); + return false; + } + + // Either loading a saved game or a new level + + if (pstm == NULL) { + // Load the first level + + if (!gsim.LoadLevel(pszLevel)) { + gsim.PerLevelExit(); + return false; + } + } else { + if (!gsim.LoadState(pstm)) { + gsim.PerLevelExit(); + return false; + } + } + + // Load the sim UI form + + m_pfrmSimUI = (SimUIForm *)gpfrmmSim->LoadForm(gpiniForms, kidfSimUI, new SimUIForm(wfRole, gameid, chatter)); + if (m_pfrmSimUI == NULL) { + Assert("LoadForm(SimUIForm) failed"); + gsim.PerLevelExit(); + return false; + } + + m_pfrmSimUI->CalcLevelSpecificConstants(); + + // Load the input UI form + + m_pfrmInputUI = (InputUIForm *)gpfrmmInput->LoadForm(gpiniForms, kidfInputUI, new InputUIForm(chatter)); + if (m_pfrmInputUI == NULL) { + Assert("LoadForm(InputUIForm) failed"); + delete m_pfrmSimUI; + m_pfrmSimUI = NULL; + gsim.PerLevelExit(); + return false; + } + + // Load minimap form + + m_pfrmMiniMap = gpmfrmm->LoadForm(gpiniForms, kidfMiniMap, new Form()); + if (m_pfrmMiniMap == NULL) { + Assert("LoadForm(Minimap) failed"); + delete m_pfrmInputUI; + m_pfrmInputUI = NULL; + delete m_pfrmSimUI; + m_pfrmSimUI = NULL; + gsim.PerLevelExit(); + return false; + } + + // This form doesn't want key events + + m_pfrmMiniMap->SetFlags(m_pfrmMiniMap->GetFlags() | kfFrmNoFocus); + + // Position minimap + + MiniMapControl *pctlMiniMap = (MiniMapControl *)m_pfrmMiniMap->GetControlPtr(kidcMiniMap); + Assert(pctlMiniMap != NULL); + pctlMiniMap->SetPosition(0, 0); + Rect rcMiniMap; + pctlMiniMap->GetRect(&rcMiniMap); + Size sizDibT; + gpmfrmm->GetDib()->GetSize(&sizDibT); + Rect rcT; + rcT.Set(sizDibT.cx - rcMiniMap.Width(), sizDibT.cy - rcMiniMap.Height(), sizDibT.cx, sizDibT.cy); + m_pfrmMiniMap->SetRect(&rcT); + + // This hack is so the Simulation's first Update occurs before its first + // paint. By doing it this way the MissionLoaded trigger gets to determine + // the first image the player sees. Otherwise the map will paint first, + // then when the update timer goes off 80 milliseconds later the + // MissionLoaded trigger will go off and pop up an ecom, reposition the + // view, etc. + + if ((wfRole & kfRoleMultiplayer) == 0) + m_pfrmSimUI->OnTimer(0); + + // Clear the screen so the ugly palette change isn't apparent + + ClearDisplay(); + + // Set palette + + Palette *ppal = gsim.GetLevel()->GetPalette(); + SetHslAdjustedPalette(ppal, gnHueOffset, gnSatMultiplier, gnLumOffset); + gmpiclriclrShadow = gsim.GetLevel()->GetShadowMap(); + + // Tell the sprite manager the clipping rects + + Rect rcClip1; + rcClip1.left = 0; + rcClip1.top = 0; + rcClip1.right = m_sizPlayfield.cx - rcMiniMap.Width(); + rcClip1.bottom = m_sizPlayfield.cy; + Rect rcClip2; + rcClip2.left = rcClip1.right; + rcClip2.top = 0; + rcClip2.right = m_sizPlayfield.cx; + rcClip2.bottom = rcT.top; + SpriteManager *psprm = gpdisp->GetSpriteManager(); + if (psprm != NULL) { + psprm->SetClipRects(&rcClip1, &rcClip2); + } + + return true; +} + +void Game::ClearDisplay() +{ + if (gpdisp == NULL) + return; + DibBitmap *pbmBack = gpdisp->GetBackDib(); + if (pbmBack == NULL) + return; + pbmBack->Clear(GetColor(kiclrBlack)); + if (gpmfrmm != NULL) { + gpmfrmm->InvalidateRect(NULL); + gpmfrmm->DrawFrame(false, false); + gpmfrmm->InvalidateRect(NULL); + } +} + +void Game::ExitSimulation() +{ + Status("Exit Simulation (per-level)..."); + gsim.PerLevelExit(); + gsmm.ClearDelayedMessages(); + delete m_pfrmSimUI; + m_pfrmSimUI = NULL; + delete m_pfrmInputUI; + m_pfrmInputUI = NULL; + delete m_pfrmMiniMap; + m_pfrmMiniMap = NULL; + + // Before the shell changes the palette... + + ClearDisplay(); + + // Too many places are forgetting to set the palette when the + // simulation exits, so set it back to the shell palette and + // shadow map here. Note InitSimulation sets it to the level + // palette / shadowmap, so this is appropriate. + + gshl.SetPalette(); +} + +void Game::Exit() +{ +#if defined(WIN) && !defined(CE) + // If the game exits without pressing F8, stop the avi recording automatically + + if (gpavir != NULL) { + delete gpavir; + gpavir = NULL; + } +#endif + + // Save preferences + + SavePreferences(); + + Status("Exit Simulation (one-time)..."); + gsim.OneTimeExit(); + + // Clear so that when PalmOS switches palette we don't have screen trash + + ClearDisplay(); + + m_pfrmSimUI = NULL; + delete gpmfrmm; + gpmfrmm = NULL; + delete gpiniForms; + gpiniForms = NULL; + delete gpiniGame; + gpiniGame = NULL; + + if (m_wf & kfGameInitFonts) { + for (int ifnt = 0; ifnt < kcFonts; ifnt++) { + delete gapfnt[ifnt]; + gapfnt[ifnt] = NULL; + } + m_wf &= ~kfGameInitFonts; + } + + gsndm.Exit(); + + ButtonControl::ExitClass(); + CheckBoxControl::ExitClass(); + PipMeterControl::ExitClass(); + ListControl::ExitClass(); + DamageMeterControl::ExitClass(); + + FreeSharedTBitmaps(); + + if (m_wf & kfGameInitBitmap) { + TBitmap::ExitClass(); + m_wf &= ~kfGameInitBitmap; + } + + gshl.Exit(); + + delete gpstrtbl; + gpstrtbl = NULL; + + // Pop off all data files + + while (gpakr.Pop()) + ; + + if (gmpWcFromPc != NULL) { + gmmgr.FreePtr(gmpWcFromPc); + gmpWcFromPc = NULL; + } + if (gmpPcFromWc != NULL) { + gmmgr.FreePtr(gmpPcFromWc); + gmpPcFromWc = NULL; + } + + delete gpdisp; + gpdisp = NULL; + + delete m_amm; + m_amm = NULL; + m_cmm = 0; + m_cmmAlloc = 0; + + gplrm.Reset(); + + gcam.Exit(); + gmmgr.Exit(); + + // Depends on gpbScratch so clean up before deleting + +#if defined(PIL) && defined(TRACE_TO_DB_LOG) + DbLogExit(); +#endif + + delete gpbScratch; + gpbScratch = NULL; + + m_fSimUninitialized = true; +} + +void Game::ScheduleUpdateTriggers() { + m_fUpdateTriggers = true; + + // Force GetEvent to return a message. This will cause FilterEvent + // to get called, where the check for m_fUpdateTriggers will be made, + // to update triggers immediately. + + Event evt; + evt.eType = updateTriggersEvent; + gevm.PostEvent(&evt); +} + +void Game::UpdateTriggers() { + // Always run triggers this way, so that trigger execution is in sync + // with multiplayer updates. This also allows triggers to go into modal + // loops, while the simulation continues (with special care). + + if (m_fUpdateTriggers) { + m_fUpdateTriggers = false; + if (gsim.GetLevel() != NULL) { + if (gsim.GetLevel()->GetTriggerMgr() != NULL) + gsim.GetLevel()->GetTriggerMgr()->Update(); + } + } +} + +bool Game::FilterEvent(Event *pevt) +{ +#ifdef INCL_VALIDATEHEAP + gmmgr.Validate(); +#endif + + UpdateTriggers(); + + switch (pevt->eType) { + case checkGameOverEvent: + if (m_pfrmSimUI != NULL) { + m_pfrmSimUI->CheckMultiplayerGameOver((Pid)pevt->dw); + } + break; + + case gamePaintEvent: + // Keep track of # of paints for fps stat + + extern short gcPaint; + gcPaint++; + + // Draw + + gpmfrmm->DrawFrame(false); + return true; + + case gameSuspendEvent: + // OS alert is showing. + + Suspend(); + return true; + + case transportEvent: + if (gptra != NULL) { + gptra->OnEvent(pevt); + } + return true; + + case mpEndMissionWinEvent: + EndMissionAction::OnMPEndMissionActionEvent(knWinLoseTypeWin, + pevt->dw); + break; + + case mpEndMissionLoseEvent: + EndMissionAction::OnMPEndMissionActionEvent(knWinLoseTypeLose, + pevt->dw); + break; + + case mpShowObjectivesEvent: + ShowObjectivesAction::OnMPShowObjectivesEvent(pevt->dw); + break; + } + + return false; +} + +char *gszPaused = "PAUSED"; + +void Game::Suspend() +{ + // Force back buffer to be valid + + gpmfrmm->DrawFrame(true); + + // Darken back buffer + + DibBitmap *pbmBack = gpdisp->GetBackDib(); + Size sizDib; + pbmBack->GetSize(&sizDib); + pbmBack->Shadow(0, 0, sizDib.cx, sizDib.cy); + + // Draw "Paused" sign in the middle + // TODO: Make prettier + + Font *pfnt = gapfnt[kifntTitle]; + int cxText = pfnt->GetTextExtent(gszPaused); + int cyText = pfnt->GetHeight(); + int cxBox = cxText * 3; + int cyBox = cyText * 3; + int x = (sizDib.cx - cxBox) / 2; + int y = (sizDib.cy - cyBox) / 2; + pbmBack->Fill(x, y, cxBox, cyBox, GetColor(kiclrBlack)); + pfnt->DrawText(pbmBack, gszPaused, x + cxText, y + cyText); + + // Call host. This is a modal loop. When it returns, the game isn't + // paused anymore + + HostSuspendModalLoop(pbmBack); + + // Ok, invalidate everything so it all redraws + + gpmfrmm->InvalidateRect(NULL); +} + +void Game::GetPlayfieldSize(Size *psiz) +{ + *psiz = m_sizPlayfield; +} + +SimUIForm *Game::GetSimUIForm() +{ + return m_pfrmSimUI; +} + +InputUIForm *Game::GetInputUIForm() +{ + return m_pfrmInputUI; +} + +Form *Game::GetMiniMapForm() +{ + return m_pfrmMiniMap; +} + +#define knVerGameState 9 +int Game::PlaySavedGame(Stream *pstm) +{ + // Overall, look at build version first + + char szVersion[32]; + szVersion[0] = 0; + pstm->ReadString(szVersion, sizeof(szVersion)); + + // Read version + + bool fStale = false; + byte nVer = pstm->ReadByte(); + if (nVer != knVerGameState) + fStale = true; + + // Read platform + + if (!fStale) { + byte bPlatform = 0; + if (nVer >= 6) + bPlatform = pstm->ReadByte(); + + // Check version + + if (!CheckSaveGameVersion(szVersion, bPlatform)) + fStale = true; + } + + // If old, bail + + if (fStale) { + pstm->Close(); + delete pstm; + HtMessageBox(kfMbWhiteBorder | kfMbClearDib, "Error", "Save game out of date!"); + return knGoFailure; + } + + // PlayLevel will close and delete pstm, guaranteed. + + return PlayLevel(NULL, pstm); +} + +void Game::SaveGame(Stream *pstm) +{ + if (pstm == NULL) { + if (!PickSaveGameStream(&pstm)) + return; + } + + bool fSuccess = false; + if (pstm != NULL) { + // Write build version string first + // NOTE: these first 3 entries are assumed in CheckSaveGameVersion() and DeleteStaleSaveGames()!! + + pstm->WriteString(gszVersion); + + // Save version + + pstm->WriteByte(knVerGameState); + + // Save platform byte. Needed for platform compatibility check (68K->ARM transition) + +#ifdef PIL +#ifdef PNO + // ARM saved game + + pstm->WriteByte(1); +#else + // 68K saved game + + pstm->WriteByte(0); +#endif +#else + // Whatever, doesn't matter since it isn't Palm + + pstm->WriteByte(0); +#endif + + // Save mission identifier + + pstm->Write(&m_miid.packid, sizeof(m_miid.packid)); + pstm->WriteString(m_miid.szLvlFilename); + + // Save next level filename + + pstm->WriteString(m_szNextLevel); + + // Save pvars + + m_dictPvars.SaveState(pstm); + + // Save player info + + gplrm.SaveState(pstm); + + // Save simulation + + gsim.SaveState(pstm); + + // Check for error + + fSuccess = pstm->IsSuccess(); + } + + if (!fSuccess) + HtMessageBox(0, "Save Game", "Error saving game! This device is too low on memory to save this game. Save over an existing same game or free some space and try again."); + + if (pstm != NULL) { + pstm->Close(); + delete pstm; + } +} + +void Game::SetNextLevel(char *pszLevel) +{ + // Perform simple lower case + + char szT[sizeof(m_szNextLevel)]; + strncpyz(szT, pszLevel, sizeof(szT)); + char *pszT = szT; + while (*pszT != 0) { + if (*pszT >= 'A' && *pszT <= 'Z') + (*pszT) += 'a' - 'A'; + pszT++; + } + + // Some missions don't have correct "next level" filenames, + // in that .lvl isn't there. Append if it's not there. + + int cch = strlen(szT); + if (sizeof(szT) - 1 - cch >= 4) { + bool fAppend = false; + if (cch < 4) { + fAppend = true; + } else if (szT[cch - 4] != '.' || + szT[cch - 3] != 'l' || + szT[cch - 2] != 'v' || + szT[cch - 1] != 'l') { + fAppend = true; + } + if (fAppend) { + strncpyz(&szT[cch], ".lvl", + sizeof(szT) - cch); + } + } + strncpyz(m_szNextLevel, szT, sizeof(m_szNextLevel)); +} + +char *Game::GetNextLevel() +{ + return m_szNextLevel; +} + +bool Game::IsMultiplayer() +{ + return (m_wf & kfGameMultiplayer) != 0; +} + +void Game::SetGameSpeed(long t) +{ + gtGameSpeed = t; + if (m_pfrmSimUI != NULL) + gtimm.SetTimerRate(m_pfrmSimUI, gtGameSpeed); +} + +// Preferences support. +// +// Note always save and load assuming a storage format of big endian. +// That way we don't have a headache when someone upgrades from a 68K Palm to a +// ARM Palm with ARM HT and then HotSyncs, restoring a little endian preferences database. + +Preferences gprefsInit; + +void Game::LoadPreferences() +{ + // Try to load preferences. If this fails, + // initialize preferences to default values. + // gprefsInit is a global that holds the preferences that are used during initialization + // It's a global because we won't be able to process all preferences related initialization + // here. + + if (!LoadPreferences2()) { + // Initialize preferences to default values + + memset(&gprefsInit, 0, sizeof(gprefsInit)); + Date date; + HostGetCurrentDate(&date); + gprefsInit.fAnonymous = 1; + gprefsInit.nYearLastRun = date.nYear; + gprefsInit.nMonthLastRun = date.nMonth; + gprefsInit.nDayLastRun = date.nDay; + gprefsInit.nVolume = (word)-1; + gprefsInit.wfPerfOptions = kfPerfMax; + gprefsInit.ctGameSpeed = kctUpdate; + gprefsInit.wfHandicap = kfHcapDefault; +#if defined(WIN) && !defined(CE) + gprefsInit.nScale = -1; +#endif + gprefsInit.nScrollSpeed = 1.0; + strncpyz(gprefsInit.szAskURL, "http://", sizeof(gprefsInit.szAskURL)); + strncpyz(gprefsInit.szDeviceId, HostGenerateDeviceId(), sizeof(gprefsInit.szDeviceId)); + } +} + +bool Game::LoadPreferences2() +{ + Preferences prefs; + int cbRead = HostLoadPreferences(&prefs, sizeof(prefs)); + if (cbRead != (int)BigDword(prefs.prefv.cbSize)) + return false; + + switch (BigDword(prefs.prefv.dwVersion)) { + case knVersionPreferencesV100: + if (cbRead != sizeof(PreferencesV100)) + return false; + if (!LoadPreferencesV100((PreferencesV100 *)&prefs)) + return false; + strncpyz(gprefsInit.szDeviceId, HostGenerateDeviceId(), sizeof(gprefsInit.szDeviceId)); + return true; + + case knVersionPreferencesV101: + if (cbRead != sizeof(PreferencesV101)) + return false; + return LoadPreferencesV101((PreferencesV101 *)&prefs); + } + + return false; +} + +bool Game::LoadPreferencesV100(PreferencesV100 *pprefsV100) +{ + strncpyz(gprefsInit.szUsername, pprefsV100->szUsername, + sizeof(gprefsInit.szUsername)); + strncpyz(gprefsInit.szPassword, pprefsV100->szPassword, + sizeof(gprefsInit.szPassword)); + strncpyz(gprefsInit.szToken, pprefsV100->szToken, + sizeof(gprefsInit.szToken)); + gprefsInit.fAnonymous = BigWord(pprefsV100->fAnonymous); + gprefsInit.nYearLastRun = BigWord(pprefsV100->nYearLastRun); + gprefsInit.nMonthLastRun = BigWord(pprefsV100->nMonthLastRun); + gprefsInit.nDayLastRun = BigWord(pprefsV100->nDayLastRun); + gprefsInit.nVolume = BigWord(pprefsV100->nVolume); + gprefsInit.wfPrefs = BigWord(pprefsV100->wfPrefs); + gprefsInit.wfPerfOptions = BigWord(pprefsV100->wfPerfOptions); + gprefsInit.ctGameSpeed = BigDword(pprefsV100->ctGameSpeed); + gprefsInit.fLassoSelection = BigWord(pprefsV100->fLassoSelection); + gprefsInit.nHueOffset = BigWord(pprefsV100->nHueOffset); + gprefsInit.nSatMultiplier = BigWord(pprefsV100->nSatMultiplier); + gprefsInit.nLumOffset = BigWord(pprefsV100->nLumOffset); + gprefsInit.cxModeBest = BigWord(pprefsV100->cxModeBest); + gprefsInit.cyModeBest = BigWord(pprefsV100->cyModeBest); + gprefsInit.nDepthModeBest = BigWord(pprefsV100->nDepthModeBest); + gprefsInit.nDepthDataBest = BigWord(pprefsV100->nDepthDataBest); + gprefsInit.nSizeDataBest = BigWord(pprefsV100->nSizeDataBest); + gprefsInit.nDegreeOrientationBest = BigWord(pprefsV100->nDegreeOrientationBest); + gprefsInit.cxModeLast = BigWord(pprefsV100->cxModeLast); + gprefsInit.cyModeLast = BigWord(pprefsV100->cyModeLast); + gprefsInit.nDepthModeLast = BigWord(pprefsV100->nDepthModeLast); + gprefsInit.nDepthDataLast = BigWord(pprefsV100->nDepthDataLast); + gprefsInit.nSizeDataLast = BigWord(pprefsV100->nSizeDataLast); + gprefsInit.nDegreeOrientationLast = BigWord(pprefsV100->nDegreeOrientationLast); + gprefsInit.nDemoRank = BigWord(pprefsV100->nDemoRank); + gprefsInit.nScrollSpeed = pprefsV100->nScrollSpeed; + strncpyz(gprefsInit.szAskURL, pprefsV100->szAskURL, + sizeof(gprefsInit.szAskURL)); + + // Migrate obsolete handicap combinations + + gprefsInit.wfHandicap = BigWord(pprefsV100->wfHandicap); + if (gprefsInit.wfHandicap == 0) // old hard + gprefsInit.wfHandicap = kfHcapHard; // new hard + else if (gprefsInit.wfHandicap == (kfHcapDecreasedTimeToBuild | kfHcapIncreasedFirePower | kfHcapIncreasedMinerLoadValue | kfHcapShowEnemyBuildProgress | kfHcapShowEnemyResourceStatus)) + gprefsInit.wfHandicap = kfHcapEasy; + else if (gprefsInit.wfHandicap == (kfHcapDecreasedTimeToBuild | kfHcapShowEnemyBuildProgress | kfHcapShowEnemyResourceStatus)) + gprefsInit.wfHandicap = kfHcapNormal; + else if (gprefsInit.wfHandicap != kfHcapEasy && gprefsInit.wfHandicap != kfHcapNormal && gprefsInit.wfHandicap != kfHcapHard) + gprefsInit.wfHandicap = kfHcapDefault; + + gprefsInit.key = pprefsV100->key; +#if defined(WIN) && !defined(CE) + gprefsInit.nScale = BigWord(pprefsV100->nScale); +#endif + + return true; +} + +bool Game::LoadPreferencesV101(PreferencesV101 *pprefsV101) +{ + // Since order is the same as V100 except for extra fields at the end, load V100 first + if (!LoadPreferencesV100((PreferencesV100 *)pprefsV101)) + return false; + + // PreferencesV101 has this new field, load it + strncpyz(gprefsInit.szDeviceId, pprefsV101->szDeviceId, sizeof(gprefsInit.szDeviceId)); + return true; +} + +void Game::SavePreferences() +{ + // Only if we've gone through initialization ok + + if (!(m_wf & kfGameInitDone)) + return; + + // Always save the latest version of the preferences + + Preferences prefs; + + prefs.prefv.dwVersion = BigDword(knVersionPreferences); + prefs.prefv.cbSize = BigDword(sizeof(prefs)); + strncpyz(prefs.szUsername, gszUsername, sizeof(prefs.szUsername)); + strncpyz(prefs.szPassword, gszPassword, sizeof(prefs.szPassword)); + strncpyz(prefs.szToken, gszToken, sizeof(prefs.szToken)); + prefs.fAnonymous = gfAnonymous ? BigWord(1) : 0; + Date date; + HostGetCurrentDate(&date); + prefs.nYearLastRun = BigWord(date.nYear); + prefs.nMonthLastRun = BigWord(date.nMonth); + prefs.nDayLastRun = BigWord(date.nDay); + prefs.nVolume = BigWord(gsndm.GetVolume()); + word wfPrefs = gsndm.IsEnabled() ? 0 : kfPrefSoundMuted; + wfPrefs |= gfIgnoreBluetoothWarning ? kfPrefIgnoreBluetoothWarning : 0; + prefs.wfPrefs = BigWord(wfPrefs); + prefs.wfPerfOptions = BigWord((gwfPerfOptions & kfPerfAll) | (kfPerfMax & ~kfPerfAll)); + prefs.ctGameSpeed = BigDword(gtGameSpeed); + prefs.fLassoSelection = gfLassoSelection ? BigWord(1) : 0; + prefs.nHueOffset = BigWord(gnHueOffset); + prefs.nSatMultiplier = BigWord(gnSatMultiplier); + prefs.nLumOffset = BigWord(gnLumOffset); + prefs.wfHandicap = BigWord(gwfHandicap); + prefs.nDemoRank = BigWord(gnDemoRank); + prefs.nScrollSpeed = gnScrollSpeed; + strncpyz(prefs.szAskURL, gszAskURL, sizeof(prefs.szAskURL)); + strncpyz(prefs.szDeviceId, gszDeviceId, sizeof(prefs.szDeviceId)); + + if (gpdisp == NULL || m_immBest == -1) { + prefs.cxModeBest = 0; + prefs.cyModeBest = 0; + prefs.nDepthModeBest = 0; + prefs.nDepthDataBest = 0; + prefs.nSizeDataBest = 0; + prefs.nDegreeOrientationBest = 0; + } else { + ModeInfo mode; + gpdisp->GetModeInfo(m_amm[m_immBest].imode, &mode); + prefs.cxModeBest = BigWord(mode.cx); + prefs.cyModeBest = BigWord(mode.cy); + prefs.nDepthModeBest = BigWord(mode.nDepth); + prefs.nDepthDataBest = BigWord(m_amm[m_immBest].nDepthData); + prefs.nSizeDataBest = BigWord(m_amm[m_immBest].nSizeData); + prefs.nDegreeOrientationBest = BigWord(mode.nDegreeOrientation); + } + if (gpdisp == NULL || m_immCurrent == -1) { + prefs.cxModeLast = 0; + prefs.cyModeLast = 0; + prefs.nDepthModeLast = 0; + prefs.nDepthDataLast = 0; + prefs.nSizeDataLast = 0; + prefs.nDegreeOrientationLast = 0; + } else { + ModeInfo mode; + gpdisp->GetModeInfo(m_amm[m_immCurrent].imode, &mode); + prefs.cxModeLast = BigWord(mode.cx); + prefs.cyModeLast = BigWord(mode.cy); + prefs.nDepthModeLast = BigWord(mode.nDepth); + prefs.nDepthDataLast = BigWord(m_amm[m_immCurrent].nDepthData); + prefs.nSizeDataLast = BigWord(m_amm[m_immCurrent].nSizeData); + prefs.nDegreeOrientationLast = BigWord(mode.nDegreeOrientation); + } + prefs.key = gkey; +#if defined(WIN) && !defined(CE) + if (gpdisp == NULL) { + prefs.nScale = (word)-1; + } else { + prefs.nScale = BigWord(gpdisp->GetScale()); + } +#endif + + HostSavePreferences(&prefs, sizeof(prefs)); +} + +bool Game::GetVar(const char *pszName, char *pszBuff, int cbBuff) +{ + // 'system variable' + + if (*pszName == '$') { + if (stricmp(pszName, "$ranktitle") == 0) { + GetRankTitle(pszBuff, cbBuff); + + } else if (stricmp(pszName, "$grayscale") == 0) { + strncpyz(pszBuff, gfGrayscale ? "1" : "0", cbBuff); + + } else if (stricmp(pszName, "$iphone") == 0) { +#ifdef IPHONE + strncpyz(pszBuff, "1", cbBuff); +#else + strncpyz(pszBuff, "0", cbBuff); +#endif + + } else if (stricmp(pszName, "$difficulty") == 0) { + char *pszT = "0"; + switch (gwfHandicap) { + case kfHcapEasy: + pszT = "1"; + break; + + case kfHcapNormal: + pszT = "2"; + break; + + case kfHcapHard: + pszT = "3"; + break; + } + + strncpyz(pszBuff, pszT, cbBuff); + + } else if (stricmp(pszName, "$demo") == 0) { + strncpyz(pszBuff, gfDemo ? "1" : "0", cbBuff); + + } else { + + // Bogus request + + strncpyz(pszBuff, "?var?", cbBuff); + return false; + } + + // 'persistent variable' + + } else { + const char *pszValue = m_dictPvars.Get(pszName); + if (pszValue == NULL) { + strncpyz(pszBuff, "?pvar?", cbBuff); + return false; + } + strncpyz(pszBuff, pszValue, cbBuff); + } + + return true; +} + +bool Game::SetVar(const char *pszName, const char *pszValue) +{ + Assert(*pszName != '$', "Setting of sysvars not allowed."); + return m_dictPvars.Set(pszName, pszValue); +} + +} // namespace wi diff --git a/game/gameform.cpp b/game/gameform.cpp new file mode 100644 index 0000000..0e176ee --- /dev/null +++ b/game/gameform.cpp @@ -0,0 +1,607 @@ +#include "ht.h" + +#include "game/gameform.h" +#include "game/multiplayer.h" +#include "mpshared/messages.h" +#include "game/httppackmanager.h" +#include "base/format.h" + +namespace wi { + +GameForm::GameForm(LoginHandler& handler, const GameInfo& info, + bool creator, Chatter& chatter) : handler_(handler), info_(info), + creator_(creator), chatter_(chatter), joined_(false) { + chatter_.SignalOnBlink.connect(this, &GameForm::OnChatBlink); + chatter_.SignalOnPlayers.connect(this, &GameForm::OnPlayers); +} + +GameForm::~GameForm() { + chatter_.SignalOnBlink.disconnect(this); + chatter_.SignalOnPlayers.disconnect(this); + chatter_.HideChat(); +} + +void GameForm::ShowJoinMessage(dword result) { + char *message = NULL; + switch (result) { + case knGameJoinResultSuccess: + break; + case knGameJoinResultFail: + default: + message = "Error joining this game."; + break; + case knGameJoinResultRoomNotFound: + message = "This game room no longer available."; + break; + case knGameJoinResultGameNotFound: + message = "This game is no longer available."; + break; + case knGameJoinResultGameFull: + message = "This game is full."; + break; + case knGameJoinResultInProgress: + message = "This game is already in progress."; + break; + } + if (message != NULL) { + HtMessageBox(kfMbWhiteBorder, "Join Game", message); + } +} + +dword GameForm::DoForm(LoginHandler& handler, const GameInfo& info, + bool creator, Chatter& chatter) { + if (gptra == NULL) { + return knGameStartResultFail; + } + + GameForm *pfrm = (GameForm *)gpmfrmm->LoadForm(gpiniForms, kidfGameStart, + new GameForm(handler, info, creator, chatter)); + if (pfrm == NULL) { + return knGameStartResultFail; + } + + int result = 0; + pfrm->DoModal(&result); + delete pfrm; + + if (result == kidcCancel) { + return knGameStartResultDone; + } + + if (result == kidcOk) { + return knGameStartResultStart; + } + + return knGameStartResultFail; +} + +bool GameForm::DoModal(int *presult) { + if (creator_) { + ButtonControl *pbtn = (ButtonControl *)GetControlPtr(kidcOk); + pbtn->SetText("BEGIN GAME"); + LabelControl *plbl = (LabelControl *)GetControlPtr(kidcGameName); + char name[kcbPlayerName]; + handler_.GetPlayerName(name, sizeof(name)); + plbl->SetText(base::Format::ToString("%s's game", name)); + } + + // min/max players + LabelControl *plbl = (LabelControl *)GetControlPtr(kidcMapName); + plbl->SetText(info_.title); + plbl = (LabelControl *)GetControlPtr(kidcNumPlayers); + if (info_.minplayers == info_.maxplayers) { + plbl->SetText(base::Format::ToString("%d", info_.minplayers)); + } else { + plbl->SetText(base::Format::ToString("%d-%d", info_.minplayers, + info_.maxplayers)); + } + + // Game speed + char szT[16]; + GetSpeedMultiplierString(szT, info_.params.tGameSpeed); + plbl = (LabelControl *)GetControlPtr(kidcGameSpeedLabel); + plbl->SetText(szT); + + // If not creator, state readiness any time + if (!creator_) { + ButtonControl *pbtn = (ButtonControl *)GetControlPtr(kidcOk); + pbtn->Show(true); + } + + // Join the game and bring up the form. + Show(true); + gptra->SetCallback(this); + gptra->SetGameCallback(this); + dword result = gptra->JoinGame(info_.gameid, info_.roomid); + if (result != knGameJoinResultSuccess) { + gptra->SetGameCallback(NULL); + gptra->SetCallback(NULL); + ShowJoinMessage(result); + *presult = 0; + return false; + } + joined_ = true; + + // Set up chat + chatter_.SetChatTitle("Game Chat"); + chatter_.AddChat("", base::Format::ToString("%s created game %s.", + info_.creator, info_.GetDescription()), true); + + *presult = 0; + bool success = ShellForm::DoModal(presult); + if (*presult != kidcOk && joined_) { + gptra->LeaveGame(); + } + gptra->SetGameCallback(NULL); + gptra->SetCallback(NULL); + + return success; +} + +void GameForm::OnChatBlink(bool on) { + GetControlPtr(kidcChat)->Show(on); +} + +void GameForm::OnPlayers() { + std::string s = GetPlayersString(); + chatter_.AddChat("", s.c_str(), true); +} + +const char *GameForm::GetColorName(int side) { + switch (side) { + case 0: + return "gray"; + case 1: + return "blue"; + case 2: + return "red"; + case 3: + return "yellow"; + case 4: + return "cyan"; + } + return "unknown"; +} + +std::string GameForm::GetPlayersPlayingString() { + // Extract the player names from Player objects. Make this player + // player 0. + + std::vector names; + names.push_back(base::Format::ToString("%s (%s)", gpplrLocal->GetName(), + GetColorName(gpplrLocal->GetSide()))); + Player *pplr = NULL; + while ((pplr = gplrm.GetNextHumanPlayer(pplr)) != NULL) { + if (pplr->GetFlags() & kfPlrUnfulfilled) { + continue; + } + if (pplr == gpplrLocal) { + continue; + } + names.push_back(base::Format::ToString("%s (%s)", pplr->GetName(), + GetColorName(pplr->GetSide()))); + } + + std::string s; + if (gpplrLocal->GetFlags() & kfPlrObserver) { + s = base::Format::ToString("You are %s and are observing. ", + GetColorName(gpplrLocal->GetSide())); + } else { + s = base::Format::ToString("You are %s. ", + GetColorName(gpplrLocal->GetSide())); + } + + if (names.size() == 1) { + s += "No other players are in this game."; + return s; + } + + if (names.size() == 2) { + s += base::Format::ToString("%s is also in this game.", + names[1].c_str()); + return s; + } + + for (int i = 1; i < names.size(); i++) { + if (i != names.size() - 1) { + s += base::Format::ToString("%s, ", names[i].c_str()); + } else { + s += base::Format::ToString("and %s ", names[i].c_str()); + } + } + s += "are also in this game."; + return s; +} + +std::string GameForm::GetPlayersObservingString() { + // Collect observing players. There are none if game is just starting + // however this static method is also called in-game. + Player *pplr = NULL; + std::vector observers; + while ((pplr = gplrm.GetNextObservingPlayer(pplr)) != NULL) { + if (pplr->GetFlags() & kfPlrUnfulfilled) { + continue; + } + if (pplr == gpplrLocal) { + continue; + } + observers.push_back(base::Format::ToString("%s (%s)", pplr->GetName(), + GetColorName(pplr->GetSide()))); + } + + if (observers.size() == 0) { + return ""; + } + + if (observers.size() == 1) { + return observers[0] + " is observing."; + } + + std::string s; + for (int i = 0; i < observers.size(); i++) { + if (i != observers.size() - 1) { + s += base::Format::ToString("%s, ", observers[i].c_str()); + } else { + s += base::Format::ToString("and %s ", observers[i].c_str()); + } + } + return s + "are observing."; +} + +std::string GameForm::GetPlayersString() { + std::string playing = GetPlayersPlayingString(); + std::string observing = GetPlayersObservingString(); + + if (observing.size() == 0) { + return playing; + } + return playing + " " + observing; +} + +void GameForm::OnControlSelected(word idc) { + switch (idc) { + + // "Ready"/"Begin Game" button has been pressed + + case kidcOk: + OnReadyBeginGame(); + break; + + case kidcCancel: + EndForm(kidcCancel); + break; + + case kidcChat: + chatter_.ShowChat(); + break; + } +} + +void GameForm::OnReadyBeginGame() { + // Players only need to say they're ready once + ButtonControl *pbtn = (ButtonControl *)GetControlPtr(kidcOk); + pbtn->Show(false); + + if (creator_) { + // The server may fail this, + // because a player may drop out at the last second, so + // this dialog isn't dismissed until the ScBeginGame + // net message is received. + NetMessage nmsg(knmidCsRequestBeginGame); + gptra->SendNetMessage(&nmsg); + return; + } + + // Let the server know this player is ready + NetMessage nmsg(knmidCsClientReady); + gptra->SendNetMessage(&nmsg); +} + +void GameForm::OnReceiveChat(const char *player, const char *chat) { + chatter_.AddChat(player, chat, false); +} + +void GameForm::OnNetMessage(NetMessage **ppnm) { + NetMessage *pnm = *ppnm; + switch (pnm->nmid) { + case knmidScBeginGameFail: + OnBeginGameFail(); + break; + + case knmidScPlayersUpdate: + OnPlayersUpdate((PlayersUpdateNetMessage *)pnm); + break; + + case knmidScGameParams: + OnGameParams((GameParamsNetMessage *)pnm); + break; + + case knmidScBeginGame: + SetRandomSeed(((BeginGameNetMessage *)pnm)->ulRandomSeed); + EndForm(kidcOk); + break; + + case knmidScCantAcceptMoreConnections: + HtMessageBox(kfMbWhiteBorder, "Game Full", + "Sorry, the requested game is full and can't accept any more players."); + EndForm(kidcCancel); + break; + } +} + +void GameForm::OnBeginGameFail() { + // Beginning the game failed; show the Begin Game button again. + // This message rarely happens, in the central server + // case where a game is requested to begin, but the server + // fails the request (a player left the game at the last + // second). + ButtonControl *pbtn = (ButtonControl *)GetControlPtr(kidcOk); + pbtn->Show(true); +} + +std::vector > GameForm::GetPlayerReadies() { + std::vector > readies; + Player *pplr = NULL; + while ((pplr = gplrm.GetNextHumanPlayer(pplr)) != NULL) { + if (pplr->GetFlags() & (kfPlrUnfulfilled | kfPlrComputer)) { + continue; + } + readies.push_back(std::make_pair(pplr, + (pplr->GetFlags() & kfPlrReady) != 0)); + } + return readies; +} + +void GameForm::OnPlayersUpdate(PlayersUpdateNetMessage *pnm) { + std::vector > oldreadies = + GetPlayerReadies(); + gplrm.Init((PlayersUpdateNetMessage *)pnm); + std::vector > newreadies = + GetPlayerReadies(); + + // Announce joins and leaves + bool sfx = false; + for (int i = 0; i < newreadies.size(); i++) { + bool found = false; + for (int j = 0; j < oldreadies.size(); j++) { + if (strcmp(newreadies[i].first->GetName(), + oldreadies[j].first->GetName()) == 0) { + found = true; + break; + } + } + if (!found) { + chatter_.AddChat("", base::Format::ToString( + "%s (%s) joined this game.", + newreadies[i].first->GetName(), + GetColorName(newreadies[i].first->GetSide())), + true); + + // sfx on add if it isn't this player + if (newreadies[i].first != gpplrLocal) { + sfx = true; + } + } + } + for (int j = 0; j < oldreadies.size(); j++) { + bool found = false; + for (int i = 0; i < newreadies.size(); i++) { + if (strcmp(newreadies[i].first->GetName(), + oldreadies[j].first->GetName()) == 0) { + found = true; + break; + } + } + if (!found) { + chatter_.AddChat("", base::Format::ToString( + "%s (%s) left this game.", + oldreadies[j].first->GetName(), + GetColorName(oldreadies[j].first->GetSide())), + true); + sfx = true; + } + } + if (sfx) { + gsndm.PlaySfx(ksfxGuiScrollingListSelectItem); + } + + // Announce ready transitions from not-ready to ready + for (int i = 0; i < newreadies.size(); i++) { + bool found = false; + bool ready = false; + for (int j = 0; j < oldreadies.size(); j++) { + if (strcmp(newreadies[i].first->GetName(), + oldreadies[j].first->GetName()) == 0) { + if (!oldreadies[j].second && newreadies[i].second) { + ready = true; + break; + } + found = true; + } + } + if (!found && newreadies[i].second) { + ready = true; + } + if (ready) { + chatter_.AddChat("", base::Format::ToString("%s (%s) is READY.", + newreadies[i].first->GetName(), + GetColorName(newreadies[i].first->GetSide())), true); + } + } + + RefreshPlayerList(); + + const char *pszCreator = gplrm.GetCreatorName(); + if (pszCreator != NULL) { + LabelControl *plbl = (LabelControl *)GetControlPtr(kidcGameName); + plbl->SetText(base::Format::ToString("%s's game", pszCreator)); + } + + if (creator_) { + ShowOrHideBeginGameButton(); + } +} + +void GameForm::OnGameParams(GameParamsNetMessage *pgpnm) { + // Check to see if this client is compatible with this server, + // and if this client has the same mission the server has + if (!IsExpectedGameParams(pgpnm->rams)) { + EndForm(kidcCancel); + return; + } + + // Join the game, NetMessage variety. This causes the server + // to allocate a player for this client, and report back error + // if there are no slots available. + char name[kcbPlayerName]; + handler_.GetPlayerName(name, sizeof(name)); + PlayerJoinNetMessage pjnm; + strncpyz(pjnm.szPlayerName, name, sizeof(pjnm.szPlayerName)); + gptra->SendNetMessage(&pjnm); + + // Pretend the creator pressed the ready button + if (creator_) { + NetMessage nmsg(knmidCsClientReady); + gptra->SendNetMessage(&nmsg); + } +} + +void GameForm::OnGameDisconnect() { + chatter_.HideChat(); + HtMessageBox(kfMbWhiteBorder, "Game Cancelled", + "This game has been cancelled. Press OK to continue."); + joined_ = false; + EndForm(kidcCancel); +} + +void GameForm::OnStatusUpdate(char *pszStatus) { + // There's no status we care to display on this form +} + +void GameForm::OnConnectionClose() { + Event evt; + memset(&evt, 0, sizeof(evt)); + evt.idf = m_idf; + evt.eType = connectionCloseEvent; + gevm.PostEvent(&evt); +} + +void GameForm::OnShowMessage(const char *message) { + message_ = message; + Event evt; + memset(&evt, 0, sizeof(evt)); + evt.idf = m_idf; + evt.eType = showMessageEvent; + gevm.PostEvent(&evt); +} + +bool GameForm::OnFilterEvent(Event *pevt) { + if (pevt->eType == connectionCloseEvent) { + chatter_.HideChat(); + HtMessageBox(kfMbWhiteBorder, "Comm Problem", "The server has closed your connection."); + EndForm(kidcCancel); + return true; + } + + if (pevt->eType == showMessageEvent) { + chatter_.HideChat(); + HtMessageBox(kfMbWhiteBorder, "Server Message", message_.c_str()); + message_ = ""; + return true; + } + return false; +} + +bool GameForm::IsExpectedGameParams(const GameParams& params) { + // This is all really unnecessary. + if (memcmp(¶ms.packid, &info_.params.packid, sizeof(PackId)) != 0) { + return false; + } + if (params.dwVersionSimulation != info_.params.dwVersionSimulation) { + return false; + } + if (params.tGameSpeed != info_.params.tGameSpeed) { + return false; + } + if (strcmp(params.szLvlFilename, info_.params.szLvlFilename) != 0) { + return false; + } + return true; +} + +int GameForm::GetPlayerCountNeeded(bool fReady) { + int cplrTotal = 0; + int cplrReady = 0; + Player *pplr = NULL; + while ((pplr = gplrm.GetNextHumanPlayer(pplr)) != NULL) { + word wf = pplr->GetFlags(); + if ((wf & kfPlrUnfulfilled) != 0) { + continue; + } + if (wf & kfPlrReady) { + cplrReady++; + } + cplrTotal++; + } + + int cNeeded; + if (fReady) { + cNeeded = info_.minplayers - cplrReady; + } else { + cNeeded = info_.minplayers - cplrTotal; + } + if (cNeeded <= 0) { + return 0; + } + return cNeeded; +} + +void GameForm::ShowOrHideBeginGameButton() { + bool fHaveNeededPlayers = (GetPlayerCountNeeded(true) == 0); + + bool fAllPlayersReady = true; + Player *pplr = NULL; + while ((pplr = gplrm.GetNextHumanPlayer(pplr)) != NULL) { + word wf = pplr->GetFlags(); + if ((wf & kfPlrUnfulfilled) != 0) { + continue; + } + if ((wf & kfPlrReady) == 0) { + fAllPlayersReady = false; + break; + } + } + + ButtonControl *pbtn = (ButtonControl *)GetControlPtr(kidcOk); + pbtn->Show(fHaveNeededPlayers && fAllPlayersReady); +} + +void GameForm::RefreshPlayerList() { + ListControl *plstc = (ListControl *)GetControlPtr(kidcPlayerList); + plstc->Clear(); + + Player *pplr = NULL; + int cAdded = 0; + while ((pplr = gplrm.GetNextHumanPlayer(pplr)) != NULL) { + + // Don't show unfulfilled players + + if (pplr->GetFlags() & kfPlrUnfulfilled) { + continue; + } + + char szT[100]; + sprintf(szT, "%s (%s) is %s", pplr->GetName(), + GetColorName(pplr->GetSide()), + pplr->GetFlags() & kfPlrReady ? "READY" : "not ready"); + plstc->Add(szT, pplr); + cAdded++; + } + int cNeeded = GetPlayerCountNeeded(false); + if (cNeeded != 0) { + plstc->Add(base::Format::ToString("%d more player%s needed", + cNeeded, cNeeded != 1 ? "s" : ""), NULL); + } +} + +} // namespace wi diff --git a/game/gameform.h b/game/gameform.h new file mode 100644 index 0000000..1c209c7 --- /dev/null +++ b/game/gameform.h @@ -0,0 +1,80 @@ +#ifndef __GAMEFORM_H__ +#define __GAMEFORM_H__ + +#include "game/ht.h" +#include "game/roomform.h" +#include "game/chatter.h" +#include "mpshared/netmessage.h" +#include "base/misc.h" +#include "base/sigslot.h" +#include +#include + +namespace wi { + +const dword knGameStartResultDone = 0; +const dword knGameStartResultFail = 1; +const dword knGameStartResultStart = 2; + +STARTLABEL(GameStartResults) + LABEL(knGameStartResultDone) + LABEL(knGameStartResultFail) + LABEL(knGameStartResultStart) +ENDLABEL(GameStartResults) + +class GameForm : public ShellForm, ITransportCallback, IGameCallback, + public base::has_slots<> { +public: + GameForm(LoginHandler& handler, const GameInfo& info, bool creator, + Chatter& chatter); + virtual ~GameForm(); + + static void ShowJoinMessage(dword result); + static dword DoForm(LoginHandler& handler, const GameInfo& info, + bool creator, Chatter& chatter); + + virtual bool DoModal(int *pnResult = NULL); + virtual void OnControlSelected(word idc); + virtual bool OnFilterEvent(Event *pevt); + + // ITransportCallback + virtual void OnStatusUpdate(char *pszStatus); + virtual void OnConnectionClose(); + virtual void OnShowMessage(const char *message); + + // IGameCallback + virtual void OnReceiveChat(const char *player, const char *chat); + virtual void OnNetMessage(NetMessage **ppnm); + virtual void OnGameDisconnect(); + + // Utility helpers + static std::string GetPlayersString(); + static const char *GetColorName(int side); + +private: + static std::string GetPlayersObservingString(); + static std::string GetPlayersPlayingString(); + + void OnChatBlink(bool on); + void OnPlayers(); + void OnReadyBeginGame(); + void OnBeginGameFail(); + void OnPlayersUpdate(PlayersUpdateNetMessage *pnm); + void OnGameParams(GameParamsNetMessage *pgpnm); + bool IsExpectedGameParams(const GameParams& params); + int GetPlayerCountNeeded(bool fReady); + void ShowOrHideBeginGameButton(); + void RefreshPlayerList(); + std::vector > GetPlayerReadies(); + + Chatter& chatter_; + LoginHandler& handler_; + const GameInfo& info_; + bool creator_; + bool joined_; + std::string message_; +}; + +} // namespace wi + +#endif // __GAMEFORM_H__ diff --git a/game/hashtablecode.cpp b/game/hashtablecode.cpp new file mode 100644 index 0000000..bd0750e --- /dev/null +++ b/game/hashtablecode.cpp @@ -0,0 +1,130 @@ +#include "ht.h" + +HashTableCode ghstCode; + +// CacheEntry for TBitmapCode code that also understands how to be part of a hash table + +void HashEntryCode::Discard() +{ + CacheEntry::Discard(); + ghstCode.FreeEntry(this); +} + +#define kcHashEntries 1024 // must be power of 2 +#define kwIndexMask (kcHashEntries - 1) + +HashTableCode::HashTableCode() +{ + m_aphec = NULL; + m_phecFree = NULL; +} + +HashTableCode::~HashTableCode() +{ + Assert(m_aphec == NULL); + Assert(m_phecFree == NULL); +} + +bool HashTableCode::Init() +{ + m_aphec = new HashEntryCode *[kcHashEntries]; + Assert(m_aphec != NULL); + if (m_aphec == NULL) + return false; + memset(m_aphec, 0, sizeof(HashEntryCode *) * kcHashEntries); + return true; +} + +void HashTableCode::Exit() +{ + DiscardHashEntries(); + delete m_aphec; + m_aphec = NULL; + + // Free free list entries + + HashEntryCode *phec = m_phecFree; + while (phec != NULL) { + HashEntryCode *phecNext = phec->m_phecNext; + delete phec; + phec = phecNext; + } + m_phecFree = NULL; +} + +void HashTableCode::DiscardHashEntries() +{ + // Free hash table entries + + for (int i = 0; i < kcHashEntries; i++) { + while (m_aphec[i] != NULL) + m_aphec[i]->Discard(); + } +} + +void HashTableCode::FreeEntry(HashEntryCode *phec) +{ + HashEntryCode **pphec = &m_aphec[(int)(phec->m_hk & kwIndexMask)]; + for (; (*pphec) != NULL; pphec = &(*pphec)->m_phecNext) { + if (*pphec == phec) { + *pphec = phec->m_phecNext; + phec->m_phecNext = m_phecFree; + m_phecFree = phec; + return; + } + } + Assert(false); +} + +HashEntryCode *HashTableCode::FindEntry(dword hkName, int x, int y, int cx, int cy, bool fOdd) +{ + // Try to find it + + //dword hk = hkName ^ (cy << (y & 7)) + (y << (cy & 7)) + (cx << (x & 7)) + (x << (cx & 7)) + (fEven ? 0x5050 : 0); + dword hk = hkName ^ ((y << 8) + cy) + ((x << 8) + cx) + (fOdd ? 0x5050 : 0); + HashEntryCode *phec = m_aphec[(int)(hk & kwIndexMask)]; + for (; phec != NULL; phec = phec->m_phecNext) { + if (phec->m_hk != hk) + continue; + if (fOdd == phec->m_fOdd && x == phec->m_x && y == phec->m_y && cx == phec->m_cx && cy == phec->m_cy) + return phec; + // This is a legal condition, but the assert is to notify me of collision. + //Assert(false); + } + + // Can't find it, so create it + // Alloc entry + + if (m_phecFree != NULL) { + phec = m_phecFree; + m_phecFree = phec->m_phecNext; + } else { + phec = new HashEntryCode; + if (phec == NULL) + return NULL; + } + + // Init + + phec->m_x = x; + phec->m_y = y; + phec->m_cx = cx; + phec->m_cy = cy; + phec->m_fOdd = fOdd; + phec->m_hk = hk; + + // Add to table + + HashEntryCode **pphec = &m_aphec[(int)(phec->m_hk & kwIndexMask)]; + if (*pphec != NULL) { + phec->m_phecNext = *pphec; + } else { + phec->m_phecNext = NULL; + } + *pphec = phec; + + // Add to cache + + gcam.Add((CacheEntry *)phec); + return phec; +} \ No newline at end of file diff --git a/game/ht.h b/game/ht.h new file mode 100644 index 0000000..be83f2d --- /dev/null +++ b/game/ht.h @@ -0,0 +1,8975 @@ +#ifndef __HT_H__ +#define __HT_H__ + +namespace wi { + +#define HOSTILE_TAKEOVER + +// Beta timeout +//#define BETA_TIMEOUT + +#ifdef DEBUG +#define INCL_TRACE +#define MP_DEBUG // Multiplayer debugging +#endif + +// Build types +// These flags are passed in by makefiles / devenv configurations + +//#define ESD_BUILD // ESD release builds +//#define RETAIL_BUILD // Retail release builds +//#define DEV_BUILD // Developer build + +// ESD_BUILD settings + +#if defined(ESD_BUILD) +#define DRM +#endif + +// RETAIL_BUILD settings + +#if defined(RETAIL_BUILD) +#define DRM +#define DRM_KEYONLY +#endif + +// DEV_BUILD settings + +#if defined(DEV_BUILD) +#ifdef DEBUG +//#define INCL_VALIDATEHEAP +//#define INCL_TESTS +//#define INCL_MEMTRACE + +#if defined(WIN) && !defined(CE) +//#define MP_DEBUG_SHAREDMEM +#define DEBUG_HELPERS +#endif +#endif + +// MP stress + +#if !defined(PIL) && !defined(CE) && !defined(IPHONE) && !defined(SDL) +#define MP_STRESS +#endif + +// Enable stress testing +#define STRESS + +// Enable this to allow little arrows to be drawn showing the paths being followed +#define DRAW_PATHS + +// Enable this to draw a box around each tile a unit believes it is occupying +//#define DRAW_OCCUPIED_TILE_INDICATOR + +// Enable this to draw a white pixel at the upper left corner of each tile +//#define MARK_TILE_BOUNDARIES + +// Enable this to see which tiles are occupied according to the terrain map +//#define MARK_OCCUPIED_TILES + +// Enable this to allow stats display to be toggled via the Find button +// +//#define STATS_DISPLAY + +// Draw rects around invalid tiles +#define DRAW_UPDATERECTS + +// Enable this to have HostOutputDebugString echo to hotsync log on Palm devices +//#define TRACE_TO_HOTSYNC_LOG + +// Enable this to have HostOutputDebugString echo to database log +#define TRACE_TO_DB_LOG + +// Maintain and check a ring buffer of old allocs +#if defined(WIN) && defined(DEBUG) +//#define CHECK_OLD_ALLOCS +#endif +#endif // defined(DEV_BULD) + +// Things that are permanently on + +#define DRAW_LINES // Enable this to allow lines to be drawn to visualize unit targeting +#define STATUS_LINE // Enable this to get certain status displayed at the bottom of the screen +//#define RALLY_POINTS + +// Enable this to sychronize Updates between server and all clients and validate +// the Update results +//#define DETECT_SYNC_ERRORS + +#ifdef DEBUG_HELPERS +class TriggerViewer; +class UnitGroupViewer; +class DelayedMessageViewer; +class CommandQueueViewer; +#endif + +#ifdef IPHONE +#pragma pack(2) +#endif + +} // namespace wi + +#include "inc/basictypes.h" +//#include "base/log.h" +#include "base/misc.h" +#include "mpshared/mpht.h" +#include "mpshared/misc.h" +#include "mpshared/enum.h" +#include "mpshared/ini.h" +#include "mpshared/decompress.h" +#include "game/missionlist.h" +#include "mpshared/netmessage.h" +#include "game/dragrect.h" +//#include "yajl/wrapper/jsontypes.h" + +namespace wi { + +class StateFrame; +class StateTracker; +class RefMap; + +typedef void *FileHandle; // hf +typedef word CacheHandle; // hc + +// Game version + +#define knVersion 1 + +#define MAKEDWORD(x) ((((dword)x) << 24) | (((dword)x) << 16) | (((dword)x) << 8) | (x)) + +#if defined(PIL) && defined(__CPU_68K) && defined(__GNUC__) +#define secCode1 +#define secCode2 __attribute__((section("code2"))) +#define secCode3 __attribute__((section("code3"))) +#define secCode4 __attribute__((section("code4"))) +#define secCode5 __attribute__((section("code5"))) +#define secCode6 __attribute__((section("code6"))) +#define secCode7 __attribute__((section("code7"))) +#define secCode8 __attribute__((section("code8"))) +#define secCode9 __attribute__((section("code9"))) +#define secCode10 __attribute__((section("code10"))) +#define secCode11 __attribute__((section("code11"))) +#define secCode12 __attribute__((section("code12"))) +#define secCode13 __attribute__((section("code13"))) +#define secCode14 __attribute__((section("code14"))) +#define secCode15 __attribute__((section("code15"))) +#define secCode16 __attribute__((section("code16"))) +#define secCode17 __attribute__((section("code17"))) +#else +#define secCode1 +#define secCode2 +#define secCode3 +#define secCode4 +#define secCode5 +#define secCode6 +#define secCode7 +#define secCode8 +#define secCode9 +#define secCode10 +#define secCode11 +#define secCode12 +#define secCode13 +#define secCode14 +#define secCode15 +#define secCode16 +#define secCode17 +#endif + +#define secGame secCode9 +#define secHost secCode17 +#define secRect secCode5 +#define secDibBitmap secCode5 +#define secTBitmap secCode16 +#define secIni secCode16 +#define secFormMgr secCode5 +#define secForm secCode10 +#define secControl secCode2 +#define secTimer secCode16 +#define secTimerMgr secCode11 +#define secEventMgr secCode4 +#define secEmptyForm secCode4 +#define secFont secCode6 +#define secButtonControl secCode4 +#define secPresetButtonControl secCode8 +#define secLabelControl secCode4 +#define secEditControl secCode4 +#define secHelpControl secCode16 +#define secHelpForm secCode13 +#define secListControl secCode6 +#define secSliderControl secCode13 +#define secMisc secCode16 +#define secGameOptionsForm secCode2 +#define secInputPanelForm secCode2 +#define secTileMap secCode2 +#define secSimulation secCode12 +#define secSimUIForm secCode8 +#define secGob secCode12 +#define secTankGob secCode5 +#define secInfantryGob secCode5 +#define secStructures secCode3 +#define secLevel secCode11 +#define secStateMachine secCode13 +#define secAnimation secCode12 +#define secFogMap secCode4 +#define secTerrainMap secCode2 +#define secSoundMgr secCode6 +#define secSoundDevice secCode6 +#define secColorMgr secCode2 +#define secPackFile secCode13 +#define secCacheMgr secCode13 +#define secCheckBoxControl secCode11 +#define secDisplay secCode7 +#define secWaveOut secCode2 +#define secMemMgr secCode16 +#define secMultiplayer secCode7 +#define secCommandQueue secCode11 +#define secComm secCode7 +#define secPlayer secCode13 +#define secBitmapControl secCode2 +#define secMiner secCode6 +#define secUpdateMap secCode4 +#define secGraffitiScrollControl secCode10 +#define secInputUIForm secCode10 +#define secMiniMapControl secCode10 +#define secThunks secCode11 +#define secArmThunks secCode11 +#define secLoadSave secCode5 +#define secStream secCode5 +#define secOvermindGob secCode8 +#define secGobMgr secCode4 +#define secAnimGob secCode11 +#define secScorchGob secCode11 +#define secSurfaceDecalGob secCode4 +#define secSceneryGob secCode4 +#define secUnitGob secCode15 +#define secTrigger secCode16 +#define secAction secCode14 +#define secCondition secCode11 +#define secUnitGroup secCode13 +#define secBuildMgr secCode14 +#define secShell secCode13 +#define secRawBitmap secCode13 +#define secEcom secCode12 +#define secPipMeterControl secCode13 +#define secDamageMeterControl secCode13 +#define secTowers secCode13 +#define secStringTable secCode5 +#define secAlertControl secCode16 +#define secArtilleryGob secCode5 +#define secCreditsControl secCode16 +#define secPowerControl secCode16 +#define secRadar secCode14 +#define secWarehouse secCode14 +#define secReplicatorGob secCode4 +#define secObjectives secCode14 +#define secCutScene secCode14 +#define secBuildQueue secCode12 +#define secBuilderGob secCode12 +#define secDrm secCode16 +#define secBtTransport secCode17 + +// Performance options + +const word kfPerfRocketShots = 0x0001; +const word kfPerfRocketTrails = 0x0002; +const word kfPerfRocketImpacts = 0x0004; +const word kfPerfShots = 0x0008; +const word kfPerfShotImpacts = 0x0010; +const word kfPerfSelectionBrackets = 0x0020; +const word kfPerfSmoke = 0x0040; +const word kfPerfEnemyDamageIndicator = 0x0080; +const word kfPerfScorchMarks = 0x0100; +const word kfPerfSymbolFlashing = 0x0200; +const word kfPerfAll = 0x03ff; +const word kfPerfMax = 0xffff; + +// Handicap flags and values + +const word kfHcapIncreasedFirePower = 0x0001; +const word kfHcapDecreasedTimeToBuild = 0x0002; +const word kfHcapIncreasedMinerLoadValue = 0x0004; +const word kfHcapShowEnemyBuildProgress = 0x0010; // build progress & upgrading +const word kfHcapShowEnemyResourceStatus = 0x0020; // power & credit symbol flash +const word kfHcapIncreasedArmor = 0x0040; + +const word kfHcapEasy = kfHcapDecreasedTimeToBuild | kfHcapIncreasedArmor | kfHcapIncreasedFirePower | kfHcapIncreasedMinerLoadValue | kfHcapShowEnemyBuildProgress | kfHcapShowEnemyResourceStatus; +const word kfHcapNormal = kfHcapDecreasedTimeToBuild | kfHcapIncreasedArmor | kfHcapShowEnemyBuildProgress | kfHcapShowEnemyResourceStatus; +const word kfHcapHard = kfHcapShowEnemyBuildProgress; +const word kfHcapDefault = kfHcapNormal; + +const int knDecreasedTimeToBuildPercent = -20; +const int knIncreasedFirePowerPercent = 20; +const int knIncreasedMinerLoadValuePercent = 20; +const int knDecreasedDamagePercent = 90; + +// Color type + +typedef word Color; // clr + +// Dib bitmap + +#define kfDibFreeMem 0x0001 +#define kfDibBackWindow 0x0002 +#define kfDibWantScrolls 0x0004 + +class Rect; +class UpdateMap; +class DibBitmap // bm +{ +public: + DibBitmap() secDibBitmap; + virtual ~DibBitmap() secDibBitmap; + bool Init(byte *pb, int cx, int cy) secDibBitmap; + virtual byte *GetBits() secDibBitmap; + virtual void Clear(Color clr) secDibBitmap; + virtual void Fill(int x, int y, int cx, int cy, Color clr) secDibBitmap; + virtual void Shadow(int x, int y, int cx, int cy) secDibBitmap; + virtual void GetSize(Size *psiz) secDibBitmap; + virtual int GetRowBytes() secDibBitmap; + virtual void DrawLine(short x1, short y1, short x2, short y2, Color clr) secDibBitmap; + virtual void Blt(DibBitmap *pbmSrc, Rect *prcSrc, int xDst, int yDst) secDibBitmap; + virtual void BltTiles(DibBitmap *pbmSrc, UpdateMap *pupd, int yTopDst) secDibBitmap; + virtual void Scroll(Rect *prcSrc, int xDst, int yDst); + virtual DibBitmap *Suballoc(int yTop, int cy) secDibBitmap; + word GetFlags() { + return m_wf; + } + +protected: + word m_wf; + Size m_siz; + byte *m_pb; + dword m_cb; + int m_cbRow; +}; +DibBitmap *CreateDibBitmap(byte *pb, int cx, int cy) secDibBitmap; + +// Network stuff + +class ITransportCallback; +class IRoomCallback; +class ILobbyCallback; +class NetMessage; +class Transport; + +const int kcbTransportName = 100; + +enum TransportType { + ktratBluetoothPAN, + ktratBluetoothSer, + ktratIR, + ktratIP, + ktratX, +}; + +const dword knTransportOpenResultSuccess = 0; +const dword knTransportOpenResultFail = 1; +const dword knTransportOpenResultNoNetwork = 2; +const dword knTransportOpenResultCantConnect = 3; +const dword knTransportOpenResultNotResponding = 4; +const dword knTransportOpenResultProtocolMismatch = 5; +const dword knTransportOpenResultServerFull = 6; + +struct TransportDescription // trad +{ + TransportType trat; + char szName[kcbTransportName]; + dword (*pfnOpen)(TransportDescription *ptrad, Transport **pptra); + dword dwTransportSpecific; +}; + +struct DeleteRecord { + DeleteRecord *pdrNext; + bool fDeleted; +}; + +#define knWaitStrConnectToHost 1 +#define knWaitStrClientDisconnecting 2 +#define knWaitStrDisconnectingClients 3 +#define knWaitStrDisconnectingClient 4 +#define knWaitStrClosingTransport 5 +#define knWaitStrBeginGameSearch 6 +#define knWaitStrAdvertisingGame 7 + +const int kcbBluetoothMtuMin = 100; // something >= our largest message but less than BT_L2CAP_MTU (672) +const int kctradMax = 10; + +class Event; +class IGameCallback; +class Transport // tra +{ +public: + Transport(); + virtual ~Transport() {} + virtual dword Open() = 0; + virtual void Close() = 0; + virtual bool IsClosed() = 0; + virtual dword Login(const char *username, const char *token) = 0; + virtual dword SignOut() = 0; + virtual const char *GetAnonymousUsername() = 0; + virtual dword JoinLobby(ILobbyCallback *plcb) = 0; + virtual bool LeaveLobby() = 0; + virtual dword CreateRoom(const char *roomname, const char *password, + dword *roomid) = 0; + virtual dword CanJoinRoom(dword roomid, const char *password) = 0; + virtual dword JoinRoom(dword roomid, const char *password, + IRoomCallback *prcb) = 0; + virtual bool SendChat(const char *chat) = 0; + virtual dword CreateGame(GameParams *params, PackId *ppackidUpgrade, + dword *gameid) = 0; + virtual dword CanJoinGame(dword gameid) = 0; + virtual void LeaveRoom(dword hint) = 0; + virtual dword JoinGame(dword gameid, dword roomid) = 0; + virtual bool SendNetMessage(NetMessage *pnm) = 0; + virtual void LeaveGame() = 0; + virtual void OnEvent(Event *pevt) = 0; + virtual ITransportCallback *SetCallback(ITransportCallback *ptcb); + virtual ITransportCallback *GetCallback(); + virtual IGameCallback *SetGameCallback(IGameCallback *pgcb); + virtual IGameCallback *GetGameCallback(); + +protected: + ITransportCallback *m_ptcb; + ILobbyCallback *m_plcb; + IRoomCallback *m_prcb; + IGameCallback *m_pgcb; +}; + +class TransportMgr // tram +{ +public: + virtual int GetTransportDescriptions(TransportDescription *atrad, int ctradMax); +}; +extern TransportMgr gtram; + +class IGameCallback { +public: + virtual void OnReceiveChat(const char *player, const char *chat) = 0; + virtual void OnNetMessage(NetMessage **ppnm) = 0; + virtual void OnGameDisconnect() = 0; +}; + +class IRoomCallback { +public: + virtual void OnAddGame(const char *player, dword gameid, + const GameParams& params, dword minplayers, dword maxplayers, + const char *title, dword ctotal) = 0; + virtual void OnRemoveGame(dword gameid, dword ctotal) = 0; + virtual void OnGameInProgress(dword gameid) = 0; + virtual void OnGamePlayerNames(dword gameid, dword cnames, + const PlayerName *anames) = 0; + virtual void OnAddPlayer(const char *player) = 0; + virtual void OnRemovePlayer(dword hint, const char *player) = 0; + virtual void OnReceiveChat(const char *player, const char *chat) = 0; + virtual void OnStatusComplete() = 0; +}; + +class ILobbyCallback { +public: + virtual void OnLurkerCount(dword count) = 0; + virtual void OnAddRoom(const char *name, dword roomid, bool priv, + dword cPlayers, dword cGames) = 0; + virtual void OnRemoveRoom(dword roomid) = 0; + virtual void OnUpdateRoom(dword roomid, dword cPlayers, dword cGames) = 0; +}; + +class ITransportCallback { +public: + virtual void OnStatusUpdate(char *pszStatus) = 0; + virtual void OnConnectionClose() = 0; + virtual void OnShowMessage(const char *message) = 0; +}; + +// Timer support + +class Timer; +class TimerMgr // timm +{ +public: + TimerMgr() secTimerMgr; + ~TimerMgr() secTimerMgr; + + void AddTimer(Timer *ptmr, long ct) secTimerMgr; + void RemoveTimer(Timer *ptmr) secTimerMgr; + void SetTimerRate(Timer *ptmr, long ct) secTimerMgr; + void BoostTimer(Timer *ptmr, long ct); + long ScanDispatch(long tCurrent) secTimerMgr; + virtual long GetTickCount() secTimerMgr; + void Enable(bool fEnable) { + m_fEnabled = fEnable; + } + void TriggerTimer(Timer *ptmr) secTimerMgr; + bool IsAdded(Timer *ptmr) secTimerMgr; + +private: + Timer *m_ptmrFirst; + Timer *m_ptmrNotifying; + Timer *m_ptmrTriggerNext; + bool m_fForceScan; + bool m_fEnabled; +}; +extern TimerMgr gtimm; + +// Timer + +class Timer // tmr +{ +public: + Timer() { m_ptimm = NULL; } + virtual ~Timer(); + virtual void OnTimer(long tCurrent) = 0; + +private: + TimerMgr *m_ptimm; + Timer *m_ptmrNext; + Timer *m_ptmrPrev; + long m_ctRate; + long m_tTrigger; + + friend class TimerMgr; +}; + +// CONSIDER: add id to Timer instead + +class ITimeout { +public: + virtual void OnTimeout(int id) = 0; +}; + +class TimeoutTimer : public Timer { +public: + TimeoutTimer() secMisc; + + ~TimeoutTimer() { + if (m_ptimo != NULL) { + Stop(); + } + } + + void Start(ITimeout *ptimo, int cms, int id = 0, bool fOneShot = true) { + if (m_ptimo != NULL) { + Stop(); + } + m_id = id; + m_fOneShot = fOneShot; + m_ptimo = ptimo; + gtimm.AddTimer(this, cms / 10); + } + + void Stop() { + if (m_ptimo == NULL) { + return; + } + gtimm.RemoveTimer(this); + m_ptimo = NULL; + } + + bool IsStarted() { + return m_ptimo != NULL; + } + + virtual void OnTimer(long tCurrent) { + ITimeout *ptimo = m_ptimo; + if (m_fOneShot) { + Stop(); + } + ptimo->OnTimeout(m_id); + } + +private: + ITimeout *m_ptimo; + bool m_fOneShot; + int m_id; +}; + +// Point + +struct Point // pt +{ + int x, y; +}; + +// Rectangle class + +class WRect; +class TRect; + +class Rect // rc +{ +public: + int left; + int top; + int right; + int bottom; + + bool Intersect(Rect *prcSrc1, Rect *prcSrc2) secRect; + void Set(Point pt1, Point pt2) secRect; + bool Subtract(Rect *prcSrc1, Rect *prcSrc2) secRect; + void Add(Rect *prcSrc1, Rect *prcSrc2) secRect; + void FromWorldRect(WRect *pwrc) secRect; + void FromTileRect(TRect *ptrc) secRect; + void Union(Rect *prc) secRect; + bool Equal(Rect *prc) secRect; + void GetCenter(Point *ppt) secRect; + int GetDistance(int x, int y) secRect; + + void Offset(int dx, int dy) + { + left += dx; + right += dx; + top += dy; + bottom += dy; + } + + void Inflate(int cx, int cy) + { + left -= cx; + right += cx; + top -= cy; + bottom += cy; + } + + bool PtIn(int x, int y) + { + return (x >= left && x < right && y >= top && y < bottom); + } + + bool RectIn(Rect *prc) + { + return (prc->left >= left && prc->top >= top && prc->right <= right && prc->bottom <= bottom); + } + + void Set(int l, int t, int r, int b) + { + left = l; + top = t; + right = r; + bottom = b; + } + + void SetEmpty() + { + left = 0; + right = 0; + bottom = 0; + top = 0; + } + + bool IsEmpty() + { + return ((left >= right) || (top >= bottom)); + } + int Width() + + { + return right - left; + } + + int Height() + { + return bottom - top; + } +}; + +// Palette + +struct Palette // pal +{ + word cEntries; + byte argb[1][3]; +}; + +} // namespace wi + +#include +#include "game/sprite.h" +#include "soundeffects.h" +#include "mpshared/packfile.h" +#include +#include "game/res.h" + +namespace wi { + +// PalmOS sdk 3.5 has min/max but sdk 4.0 doesn't + +#if 0 +#undef min +#undef max +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#define max(a, b) (((a) > (b)) ? (a) : (b)) +#endif + +// Ensures the same types are being compared +template inline T _min(const T a, const T b) { return (a > b) ? b : a; } +template inline T _max(const T a, const T b) { return (a < b) ? b : a; } + +#define sign(x) ((x) > 0 ? 1 : (x) < 0 ? -1 : 0) + +// For forward references + +class Level; +class Gob; +class SimUIForm; +typedef int GobType; // gt +typedef word UpgradeMask; +struct StructConsts; +class UnitGob; +class Player; +class Trigger; +class UnitGroup; + +const StateMachineId ksmidNull = (word)-1; + +#ifdef MP_DEBUG_SHAREDMEM +extern Side gsideCurrent; +extern Gid ggidCurrent; +extern long gcupdCurrent; +extern GobType ggtCurrent; +extern bool gfInsideUpdate; +#endif +extern bool gfMultiplayer; +extern bool gfLockStep; + +#ifdef DEBUG_HELPERS +char *PszFromUnitMask(UnitMask um); +char *PszFromCaSideMask(word wfCaSideMask); +#endif + +class UpdateInterval // unvl +{ +public: + UpdateInterval() { + m_cSkip = 0; + m_cDecrementsLast = 0; + m_cDecrements = 0; + } + + void MinSkip() { + m_cSkip = 0; + } + + void MinSkip(int cSkip) { + m_cSkip = _min(m_cSkip, cSkip); + } + + bool Decrement() { + if (gfMultiplayer) { + // Can't gracefully support update intervals in the multiplayer + // version due to synchronization issues caused by differing + // client-side state such as visibility and gpplrLocal conditionals + + return true; + } + + if (m_cSkip <= 0) { + m_cSkip = 0x7fff; + m_cDecrementsLast = m_cDecrements; + m_cDecrements = 0; + return true; + } + + // Count Decs separately since m_cSkip can be arbitrarily Min'ed + + m_cSkip--; + m_cDecrements++; + return false; + } + + int GetUpdateCount() { + // A skip of 0 (meaning next update) is an update count of 1 + + return m_cDecrementsLast + 1; + } + +private: + int m_cSkip; + int m_cDecrementsLast; + +#ifdef DEV_BUILD +public: +#endif + int m_cDecrements; +}; + +// World coordinates are high-byte = tile coord, low-byte = sub-tile coord +// Coordinate types and macros + +const short ktcMax = 64; +const short kpcMax = ktcMax * 32; // + 1 to allow for conversion of inclusive/exclusive rects +const WCoord kwcMax = ktcMax * 256; +const WCoord kwcTile = 0x0100; +const WCoord kwcTileHalf = kwcTile / 2; +const WCoord kwcTile16th = kwcTile / 16; + +#ifdef IPHONE +// IPhone titlebar area eats input; this is compensation +#define kwcScrollLeftExtra WcFromTile16ths(5) +#else +#define kwcScrollLeftExtra 0 +#endif + +#define kwcScrollBorderSize WcFromTile16ths(5) +#define kwcScrollStepSize kwcTileHalf + +extern short *gmpPcFromWc; // [64 * 256]; // 16,384 * 2 = 32,768 bytes +extern WCoord *gmpWcFromPc; // [64 * 32]; // 2,048 * 2 = 4,096 bytes + +inline TCoord TcFromWc(WCoord wc) { + return wc >> 8; +} + +inline int PcFracFromUpc(int pc) { + return gmpPcFromWc[gmpWcFromPc[pc] & 0xff]; +} + +inline WCoord WcFromPc(int pc) { + // gmpWcFromPc has one extra entry to allow an exclusive bottom/right coord + // to be mapped, hence the "<=" below + + Assert(abs(pc) <= kpcMax); + if (pc >= 0) + return gmpWcFromPc[pc]; + else + return -gmpWcFromPc[-pc]; +} + +inline int PcFromWc(WCoord wc) { + Assert(abs(wc) < kwcMax); + if (wc >= 0) + return gmpPcFromWc[wc]; + else + return -gmpPcFromWc[-wc]; +} + +inline TCoord TcFromPc(int pc) { + return TcFromWc(WcFromPc(pc)); +} + +inline WCoord WcFromUpc(int pc) { + Assert(pc >= 0 && pc < kpcMax); + return gmpWcFromPc[pc]; +} + +inline int PcFromUwc(WCoord wc) { + Assert(wc >= 0 && wc < kwcMax); + return gmpPcFromWc[wc]; +} + +inline TCoord TcFromUpc(int pc) { + return TcFromWc(WcFromUpc(pc)); +} + +inline WCoord WcFromTc(TCoord tc) { + return tc << 8; +} + +inline WCoord WcFromTile16ths(int n) { + return kwcTile16th * n; +} + +inline WCoord WcTrunc(WCoord wc) { + return wc & 0xff00; +} + +inline WCoord WcFrac(WCoord wc) { + return wc & 0x00ff; +} + +inline int PcFromTc(TCoord tc) { + return PcFromWc(WcFromTc(tc)); +} + +extern int gcxFullScreenFormDiv10; + +inline int PcFromFc(int fc) { + return (fc * gcxFullScreenFormDiv10) / 16; +} + +struct TPoint // pt +{ + TCoord tx, ty; +}; + +class WRect : public Rect +{ +public: + void Set(WPoint wpt1, WPoint wpt2) secRect; +#if 0 // UNUSED + void FromPixelRect(Rect *prc) secRect; +#endif + void FromTileRect(TRect *ptrc) secRect; +}; + +class TRect : public Rect +{ +public: + void FromWorldRect(WRect *wrc) secRect; +}; + +// Specialized TRect. Its specialty is being 1/2 the size + +struct TRectSmall +{ + char txLeft; + char tyTop; + char txRight; + char tyBottom; + + void Union(TRectSmall *ptrc) { + if (ptrc->txLeft >= ptrc->txRight || ptrc->tyTop >= ptrc->tyBottom) + return; + if (txLeft > ptrc->txLeft) + txLeft = ptrc->txLeft; + if (tyTop > ptrc->tyTop) + tyTop = ptrc->tyTop; + if (txRight < ptrc->txRight) + txRight = ptrc->txRight; + if (tyBottom < ptrc->tyBottom) + tyBottom = ptrc->tyBottom; + } + + void Offset(char tx, char ty) { + txLeft += tx; + tyTop += ty; + txRight += tx; + tyBottom += ty; + } +}; + + +// NOTE: predefined iclr constants (e.g., kiclrBlack) are in res.h +// except for these non-index-type virtual colors + +#define kiclrShadow -1 +#define kiclrShadow2x -2 + +// Fixed point number types and macros + +#define INTBITS 11 +#define FRACBITS 5 +#define INTBITS32 (INTBITS + 16) + +typedef long fix32; // INTBITS32 integer bits, FRACBITS fractional bits +typedef short fix; // INTBITS integer bits, FRACBITS fractional bits + +#define itofx(x) ((x) << 5) // Integer to fixed point +#define itofx32(x) (((long)(x)) << 5) // Integer to fixed point +#define ftofx(x) ((fix)((x) * 32)) // Float to fixed point +#define ftofx32(x) ((fix32)((x) * 32)) // Float to fixed point +#define dtofx(x) ((fix)((x) * 32)) // Double to fixed point +#define fxtoi(x) ((x) >> 5) // Fixed point to integer +#define fx32toi(x) ((x) >> 5) // Fixed point to integer +#define fxtof(x) ((float) (x) / 32) // Fixed point to float +#define fxtod(x) ((double)(x) / 32) // Fixed point to double +#define mulfx(x, y) ((((fix32)(x)) * (y)) >> 5) // Multiply a fixed by a fixed +#define mulfx32(x, y) ((((fix32)(x)) * (y)) >> 5) // Multiply a fixed by a fixed +#define divfx(x, y) ((((fix32)(x)) << 5) / (y)) // Divide a fixed by a fixed +#define divfx32(x, y) ((((fix32)(x)) << 5) / (y)) // Divide a fixed by a fixed +#define addfx(x, y) ((fix)(x) + (fix)(y)) +#define addfx32(x, y) ((fix32)(x) + (fix32)(y)) +#define subfx(x, y) ((x) - (y)) +#define fracfx(x) ((x) & ((1 << FRACBITS) - 1)) // Fraction of a fixed point +fix32 sqrtfx(fix32 x) secMisc; +unsigned int isqrt(unsigned long val) secMisc; +int isqrt(int val1, int val2) secMisc; + +#ifdef OLD_LINE_ITERATOR +// For iterating along a line at arbitrary (possibly fractional) increments + +class LineIterator { +public: + void Init(int x1, int y1, int x2, int y2, fix nStep) secMisc; + + bool Step() { + if (m_cStepsRemaining == 0) + return false; + + m_cStepsRemaining--; + m_fx = addfx(m_fx, m_fdx); + m_fy = addfx(m_fy, m_fdy); + return true; + } + + int GetX() { + return fxtoi(m_fx); + } + + int GetY() { + return fxtoi(m_fy); + } + + fix GetFixedX() { + return m_fx; + } + + fix GetFixedY() { + return m_fy; + } + +private: + fix m_fx, m_fy; + fix m_fdx, m_fdy; + int m_cStepsRemaining; +}; +#endif // OLD_LINE_ITERATOR + +// For iterating along a line at arbitrary increments + +class WLineIterator { +public: + void Init(WCoord wx1, WCoord wy1, WCoord wx2, WCoord wy2, int nIncr) secMisc; + + bool Step() { + if (m_cStepsRemaining == 0) + return false; + + m_cStepsRemaining--; + m_wx += m_wdx; + m_wy += m_wdy; + return true; + } + + int GetWX() { + return m_wx; + } + + int GetWY() { + return m_wy; + } + + int GetStepsRemaining() { + return m_cStepsRemaining; + } +private: + // OPT: can save space by using caller's m_wx, m_wy + WCoord m_wx, m_wy; + WCoord m_wdx, m_wdy; + int m_cStepsRemaining; +}; + +// Handy functions for dealing with time and update conversions + +const int kcmsUpdate = 80; // 80ms per Update +const int kctUpdate = 8; +const int kctMapScrollRate = 8; // TUNE: + +inline int UpdFromSec(int csec) { + // This is to catch any overflows. If we find ourselves concerned with + // intervals > 327 seconds we can add a cast to LONG in here. + Assert(csec < (32768 / 100)); + + // We assume kcmsUpdate can be divided by 10 without loss of precision + Assert(((kcmsUpdate / 10) * 10) == kcmsUpdate); + + return (csec * 100) / (kcmsUpdate / 10); +} + +inline int UpdFromMsec(int cms) { + return cms / kcmsUpdate; +} + +// Transparent bitmap + +struct ScanData // sd +{ + word ibaiclr; + word cbaiclrUnpacked; +}; + +struct TBitmapEntry // tbe +{ + word cx; + word cy; + word yBaseline; + word ibsd; + word cbsd; +}; +#define kcbTBitmapEntry (sizeof(word) * 5) + +struct TBitmapHeader // tbh +{ + word ctbm; + TBitmapEntry atbe[1]; +}; + +// Abstract base bitmap class so TBitmaps and RawBitmaps can be used +// interchangeably in certain basic situations + +class HtBitmap +{ +public: + virtual ~HtBitmap() secRawBitmap; + virtual bool Init(char *pszFn) = 0; + virtual void BltTo(class DibBitmap *pbmDst, int xDst, int yDst, Rect *prcSrc = NULL) = 0; + virtual void BltTo(class DibBitmap *pbmDst, int xDst, int yDst, Side side, Rect *prcSrc = NULL) = 0; + virtual void GetSize(Size *psiz) = 0; +}; + +#define kfTbCloseFile 1 +#define kfTbShared 2 + +#define kcCopyBy4Procs 32 +#define kcColoredSides 6 +class TBitmap : public HtBitmap +{ +public: + TBitmap() secTBitmap; + + bool Init(File *pfil, word ib) secTBitmap; + void BltTo(int itbm, class DibBitmap *pbmDst, int xDst, int yDst, Side side = ksideNeutral, Rect *prcSrc = NULL) secTBitmap; + void GetSize(int itbm, Size *psiz) secTBitmap; + void FillTo(int itbm, class DibBitmap *pbmDst, int xDst, int yDst, + int cxDst, int cyDst, int xOrigin = 0, int yOrigin = 0) secTBitmap; + + int GetBaseline() { + return (int)m_atbe[0].yBaseline; + } + + int GetBaseline(int itbm) { + Assert(itbm >= 0 && itbm < m_ctbm); + return (int)m_atbe[itbm].yBaseline; + } + + void SetShared() { + m_wf |= kfTbShared; + } + + void ClearShared() { + m_wf &= ~kfTbShared; + } + + static bool InitClass() secTBitmap; + static void ExitClass() secTBitmap; + + // HtBitmap overrides + + virtual ~TBitmap() secTBitmap; + virtual bool Init(char *pszFn) secTBitmap; + virtual void BltTo(class DibBitmap *pbmDst, int xDst, int yDst, Rect *prcSrc = NULL) secTBitmap; + virtual void BltTo(class DibBitmap *pbmDst, int xDst, int yDst, Side side, Rect *prcSrc = NULL) secTBitmap; + virtual void GetSize(Size *psiz) secTBitmap; + +private: + byte *GetCompiledBits(int itbm, bool fOdd) secTBitmap; + static void MakeSideCodeMapping(Color *aclr, dword *mpscaiclrSide) secTBitmap; + void BltToScan(byte *pbDraw, int cx, int cy, DibBitmap *pbmDst, + int xDst, int yDst, Side side, Rect *prcSrc); + + File *m_pfil; + word m_ibtbh; + word m_wf; + int m_ctbm; + TBitmapEntry *m_atbe; + CacheHandle *m_ahc; + +public: + static dword *s_ampscaiclrSide[kcColoredSides]; +}; +TBitmap *LoadTBitmap(char *pszFn) secTBitmap; +void FreeSharedTBitmaps() secTBitmap; +TBitmap *GetSharedTBitmap(char *pszFn) secTBitmap; +void FindSharedTBitmapFilename(TBitmap *ptbm, char *psz, int cb) secTBitmap; + +// For bltting raw bitmaps in a non-clipped, streamed manner + +class RawBitmap : public HtBitmap // rbm +{ +public: + RawBitmap() secRawBitmap; + + // HtBitmap overrides + + virtual ~RawBitmap() secRawBitmap; + virtual bool Init(char *pszFn) secRawBitmap; + virtual void BltTo(class DibBitmap *pbmDst, int xDst, int yDst, Rect *prcSrc = NULL) secRawBitmap; + virtual void BltTo(class DibBitmap *pbmDst, int xDst, int yDst, Side side, Rect *prcSrc = NULL) secRawBitmap; + virtual void GetSize(Size *psiz) secRawBitmap; + +public: + File *m_pfil; + int m_cx; + int m_cy; +}; +RawBitmap *LoadRawBitmap(char *pszFn) secRawBitmap; + +// Font / Text output + +struct FontHeader // fnth +{ + word cy; + byte acxChar[256]; + word mpchibsd[256]; + ScanData asd[1]; +}; + +class Font // fnt +{ +public: + Font() secFont; + ~Font() secFont; + + bool Load(char *pszFont) secFont; + + int GetHeight() { + return BigWord(m_pfnth->cy); + } + + void SetGlyphOverlap(int nGlyphOverlap) { + m_nGlyphOverlap = nGlyphOverlap; + } + + void SetLineOverlap(int nLineOverlap) { + m_nLineOverlap = nLineOverlap; + } + + int GetGlyphOverlap() { + return m_nGlyphOverlap; + } + + int GetLineOverlap() { + return m_nLineOverlap; + } + + int GetTextExtent(const char *psz) secFont; + int GetTextExtent(const char *psz, int cch) secFont; + int DrawText(DibBitmap *pbm, char *psz, int x, int y, int cch = -1, + dword *mpscaiclr = NULL) secFont; + void DrawText(DibBitmap *pbm, char *psz, int x, int y, int cx, + int cy, bool fEllipsis = false) secFont; + void DrawTextWithEllipsis(DibBitmap *pbm, char *psz, int cch, + int x, int y, int cx, bool fForce = false); + int CalcMultilineHeight(char *psz, int cxMultiline) secFont; + int CalcBreak(int cx, char **psz, bool fChop = true) secFont; + +private: + char *FindNextNonBreakingChar(char *psz) secFont; + + FileMap m_fmap; + FontHeader *m_pfnth; + byte **m_mpchpbCodeEven; + byte **m_mpchpbCodeOdd; + int m_nGlyphOverlap; + int m_nLineOverlap; + int m_cxEllipsis; +}; +Font *LoadFont(char *pszFont) secFont; + +struct TileSetHeader // tseth +{ + word cTiles; + word cxTile; + word cyTile; +}; +#define kcbTileSetHeader 6 + +struct MiniTileSetHeader // mtseth +{ + word offNext; + word cTiles; + word cxTile; + word cyTile; +}; + +#define kcchFnTset 28 +struct TileMapHeader // tmaph +{ + char szFnTset[kcchFnTset]; + word ctx; + word cty; +}; + +#define kfTmapMapped 1 +#define kfMiniTsetMapped 2 + +#define kcTileSetsMax 64 +class TileMap // tmap +{ +public: + TileMap() secTileMap; + ~TileMap() secTileMap; + + bool Load(char *psz, Size *psizPlayfield) secTileMap; + void GetMapSize(Size *psiz) secTileMap; + void GetTCoordMapSize(Size *psiz) { + psiz->cx = BigWord(m_ptmaph->ctx); + psiz->cy = BigWord(m_ptmaph->cty); + } + void GetTileSize(Size *psiz) secTileMap; + void Draw(DibBitmap *pbm, int x, int y, int cx, int cy, int xMap, int yMap, byte *pbFogMap, UpdateMap *pupd) secTileMap; + MiniTileSetHeader *GetMiniTileSetHeader(int nScale) secTileMap; + +private: + FileMap m_fmapTmap; + TileMapHeader *m_ptmaph; + FileMap m_afmapTset[kcTileSetsMax]; + TileSetHeader *m_aptseth[kcTileSetsMax]; + MiniTileSetHeader *m_pmtseth; + FileMap m_fmapMiniTset; + int m_cTileSets; + int m_cTiles; + int m_cxTile; + int m_cyTile; + word m_wf; + +public: + byte **m_apbTileData; + word *m_pwMapData; + int m_ctx; + int m_cty; + byte **m_apbDrawMap; +}; +TileMap *LoadTileMap(char *pszFn, Size *psizPlayfield) secTileMap; + +// Fog map + +struct RevealPattern // rvlp +{ + byte ctx; + byte cty; + byte ab[1]; +}; + +#define kbOpaque 0 +#define kbEmpty 15 + +const word kbfFogMask = 0x000f; +const int kcFogShift = 0; +#define IsFogOpaque(bFog) (((bFog) & kbfFogMask) == kbOpaque) + +const word kbfGalaxiteMask = 0x00f0; +const int kcGalaxiteShift = 4; +#define HasGalaxite(bFog) (((bFog) & kbfGalaxiteMask) != 0) + +//#define HasWall(wFog) (((wFog) & kwfWallMask) != 0) +//const word kwfWallMask = 0x0f00; +//const int kcWallShift = 8; + +class Stream; +class AnimationData; +class FogMap // fogm +{ +public: + FogMap() secFogMap; + ~FogMap() secFogMap; + + bool Init(Size *psizTile, Size *psizMap) secFogMap; + bool LoadState(Stream *pstm) secFogMap; + bool SaveState(Stream *pstm) secFogMap; + bool IsCovered(TRect *ptrc) secFogMap; + void RevealAll(UpdateMap *pupd) secFogMap; + void Reveal(TCoord txMap, TCoord tyMap, RevealPattern *prvlp, UpdateMap *pupd, WCoord wxView, WCoord wyView) secFogMap; + void Reveal(TRect *ptrc, UpdateMap *pupd, WCoord wxView, WCoord wyView) secFogMap; + void Draw(DibBitmap *pbm, int xMap, int yMap, UpdateMap *pupd) secFogMap; + void DrawGalaxite(DibBitmap *pbm, int xMap, int yMap, UpdateMap *pupd, byte *pbTrMap) secFogMap; + + byte *GetMapPtr() { + return m_pbMap; + } + + // Galaxite methods + + int GetGalaxite(TCoord tx, TCoord ty) secFogMap; + void SetGalaxite(int nGx, TCoord tx, TCoord ty) secFogMap; + bool DecGalaxite(TCoord tx, TCoord ty) secFogMap; + void IncGalaxite(TCoord tx, TCoord ty) secFogMap; + bool FindNearestGalaxite(TCoord tx, TCoord ty, TPoint *ptpt, bool fIgnoreFog) secFogMap; + + // Wall methods + + int GetWallHealth(TCoord tx, TCoord ty) secFogMap; + void SetWallHealth(int nHealth, TCoord tx, TCoord ty) secFogMap; + + +private: + int m_ctxMap; + int m_ctyMap; + int m_cxTile; + int m_cyTile; + byte m_mpSrcDstResult[256]; + byte *m_pbMap; + TBitmap *m_aptbm[16]; + TBitmap *m_aptbmGalax[9]; + AnimationData *m_panidWalls; +}; + +// Directions + +typedef char Direction; // dir + +const Direction kdirInvalid = -1; +const Direction kdirN = 0; +const Direction kdirNE = 1; +const Direction kdirE = 2; +const Direction kdirSE = 3; +const Direction kdirS = 4; +const Direction kdirSW = 5; +const Direction kdirW = 6; +const Direction kdirNW = 7; + +typedef int Direction16; // dir16 +const Direction16 kdir16Invalid = -1; + +// TerrainMap + +struct PathHead // pathh +{ + PathHead *ppathhNext; + int txLast; + int tyLast; + int cSteps; + word offHead; + word wScoreKnown; + word wScore; +}; + +#define kbfMobileUnit 0x80 +#define kbfStructure 0x40 +#define kbfReserved 0x20 +#define kbfLinked 0x10 +#define kbfHead 0x08 +#define kbfDirMask 0x07 + +class Path; +class TrackPoint +{ +public: + bool Init(Path *ppath, TCoord txFrom, TCoord tyFrom, int itptStart, int ctptFurtherStop) secTerrainMap; + void InitFrom(TrackPoint *ptrkp) secTerrainMap; + bool IsProgress(TrackPoint *ptrkpA) secTerrainMap; + bool IsCloser(TrackPoint *ptrkpA) secTerrainMap; + bool IsBetterSort(TrackPoint *ptrkpA) secTerrainMap; + void GetClosestPoint(TPoint *ptpt) { + *ptpt = m_tptClosest; + } + int GetClosestPointIndex() { + return m_itptClosest; + } + void GetAfterPoint(TPoint *ptpt) { + *ptpt = m_tptAfter; + } + void GetInitialPoint(TPoint *ptpt) { + *ptpt = m_tptInitial; + } + +protected: + TPoint m_tptInitial; + TPoint m_tptClosest; + TPoint m_tptBefore; + TPoint m_tptAfter; + int m_itptClosest; + int m_n2Before; + int m_n2After; +}; + +class TerrainMap; +class Path +{ +public: + Path() secTerrainMap; + ~Path() secTerrainMap; + + bool Init(TerrainMap *ptrmap, TCoord txStart, TCoord tyStart, Direction *adir, int cdir) secTerrainMap; + int GetCount() secTerrainMap; + bool GetPoint(int itpt, TPoint *ptpt, byte bf) secTerrainMap; + bool GetPointRaw(int itpt, TPoint *ptpt) secTerrainMap; + void SetCacheIndex(int itpt) secTerrainMap; + int FindClosestPoint(TCoord txFrom, TCoord tyFrom, int itptStart, int ctptFurtherStop, TPoint *ptptClosest) secTerrainMap; + bool LoadState(TerrainMap *ptrmap, Stream *pstm) secTerrainMap; + bool SaveState(Stream *pstm) secTerrainMap; + bool Append(Path *ppath) secTerrainMap; + bool TrimEnd(int itptStart) secTerrainMap; + Path *Clone() secTerrainMap; + void GetStartPoint(TPoint *ptpt) secTerrainMap; +#ifdef DRAW_PATHS + void Draw(DibBitmap *pbm, int xView, int yView, Side side) secTerrainMap; +#endif + +protected: + void CalcTo(int idir, TCoord *ptx, TCoord *pty) secTerrainMap; + + TerrainMap *m_ptrmap; + int m_cdirs; + Direction *m_adir; + TCoord m_txStart, m_tyStart; + int m_idirCache; + TCoord m_txCache, m_tyCache; +}; +Path *CreatePath(TerrainMap *ptrmap, TCoord txStart, TCoord tyStart, TPoint *ptpt, int ctpt) secTerrainMap; +Path *CreatePath(TerrainMap *ptrmap, TCoord txStart, TCoord tyStart, Direction *adir, int cdir) secTerrainMap; + +// 0 = area +// 1 = open +// 2 = wall +// 3 = blocked + +#define kttArea 0 +#define kttOpen 1 +#define kttWall 2 +#define kttBlocked 3 + +struct TerrainMapHeader // trmaph +{ + word ctx; + word cty; +}; + +class TerrainMap // trmap +{ +public: + TerrainMap() secTerrainMap; + ~TerrainMap() secTerrainMap; + + bool Init(char *pszFn) secTerrainMap; + bool LoadState(Stream *pstm) secTerrainMap; + bool SaveState(Stream *pstm) secTerrainMap; + void SetFlags(int tx, int ty, int ctx, int cty, byte bf) secTerrainMap; + bool TestFlags(int tx, int ty, int ctx, int cty, byte bf) secTerrainMap; + void ClearFlags(int tx, int ty, int ctx, int cty, byte bf) secTerrainMap; + bool IsOccupied(int tx, int ty, int ctx, int cty, byte bf) secTerrainMap; + Path *FindPath(int txFrom, int tyFrom, int txTo, int tyTo, byte bfTerrainAvoid) secTerrainMap; + Path *FindLinePath(TCoord txFrom, TCoord tyFrom, TCoord txTo, TCoord tyTo, byte bfTerrainAvoid) secTerrainMap; + bool IsLineOccupied(TCoord txFrom, TCoord tyFrom, TCoord txTo, TCoord tyTo, byte bfTerrainAvoid) secTerrainMap; + bool FindFirstUnoccupied(TCoord txFrom, TCoord tyFrom, TCoord txTo, TCoord tyTo, TCoord *ptxFree, TCoord *ptyFree) secTerrainMap; + bool FindLastUnoccupied(TCoord txFrom, TCoord tyFrom, TCoord txTo, TCoord tyTo, TCoord *ptxFree, TCoord *ptyFree) secTerrainMap; + bool IsBlocked(TCoord tx, TCoord ty, byte bf) secTerrainMap; + bool GetFlags(int tx, int ty, byte *pbf) secTerrainMap; + int GetTerrainType(TCoord tx, TCoord ty) secTerrainMap; + + byte *GetMapPtr() { + return (byte *)(m_ptrmaph + 1); + } + + TCoord GetWidth() { + return m_ctx; + } + TCoord GetHeight() { + return m_cty; + } + +#ifdef MP_DEBUG + dword GetChecksum() secTerrainMap; +#endif + +private: + PathHead *AddPathHead(word wScoreKnown, word wScore, int cSteps, word offHead, int txNew, int tyNew) secTerrainMap; + void RemovePathHead(PathHead *ppathh) secTerrainMap; + Path *MakePath(TCoord txStart, TCoord tyStart, word off) secTerrainMap; + + FileMap m_fmap; + TerrainMapHeader *m_ptrmaph; + byte *m_abBuffer; + int m_cbBuffer; + int m_ctx; + int m_cty; + word *m_pwSquared; + PathHead *m_ppathhList; + PathHead *m_ppathhFreeList; + int m_mpDirDelta[8]; + static word s_anScoreTerrain[4]; + static word s_anScoreVector[8]; + int m_cNodes; + + friend class Path; +}; + +// Animation & AnimationData + +struct FrameData // frmd +{ + byte ibm; // bitmap index + byte ibm2; // second bitmap index + byte cHold; // frame delay + char xOrigin; // x offset for drawing + char yOrigin; // y offset for drawing + char xOrigin2; // x offset for drawing second bitmap + char yOrigin2; // y offset for drawing second bitmap + byte bCustomData1; // first custom data value + byte bCustomData2; // second custom data value +}; +#define kcbFrameData 9 + +class StripData // stpd +{ +private: + char szName[26]; // name of the Strip + byte cDelay; // inter-frame delay (in ??? units) + byte bfFlags; // e.g., fLoop + word cfrmd; // count of FrameData structures + FrameData afrmd[1]; // array of FrameData structures + +public: + const char *GetName() { + return szName; + } + + int GetDefaultDelay() { + return cDelay; + } + + byte GetFlags() { + return bfFlags; + } + + word GetFrameCount() { + return BigWord(cfrmd); + } + + FrameData *GetFrameData(int ifrmd) { + return (FrameData *)(((byte *)afrmd) + ifrmd * kcbFrameData); + //return &afrmd[ifrmd]; + } +}; + +// NOTE: All values are stored big-endian + +struct AnimationFileHeader // anih +{ + word cstpd; // count of StripData structures + word aoffStpd[1]; // array of offsets to StripData structures +}; + +class AnimationData // anid +{ +public: + AnimationData() secAnimation; + ~AnimationData() secAnimation; + + bool Init(const char *pszAniName) secAnimation; + int GetStripCount() secAnimation; + int GetFrameCount(int nStrip) secAnimation; + int GetStripIndex(const char *pszStripName) secAnimation; + void GetFrameOrigin(int nStrip, int nFrame, Point *pptOrigin) secAnimation; + void GetSpecialPoint(int nStrip, int nFrame, Point *pptSpecial) secAnimation; + void GetBounds(int nStrip, int nFrame, Rect *prc) secAnimation; + void DrawFrame(int nStrip, int nFrame, DibBitmap *pbm, int x, int y, Side side, Rect *prcSrc = NULL) secAnimation; + int GetStripDelay(int nStrip) secAnimation; + int GetFrameDelay(int nStrip, int nFrame) secAnimation; + +private: + TBitmap *m_ptbm; + FileMap m_fmap; + AnimationFileHeader *m_panih; +}; +AnimationData *LoadAnimationData(const char *pszAniName) secAnimation; + +const word kfAniLoop = 0x0001; +const word kfAniDone = 0x0002; +const word kfAniIgnoreFirstAdvance = 0x0004; +const word kfAniResetWhenDone = 0x0008; +const word kfAniFreeAnimationData = 0x8000; + +class Animation // ani +{ +public: + Animation() { + m_panid = NULL; + m_nStrip = 0; + m_nFrame = 0; + m_cDelay = 0; + m_wf = kfAniDone; + } + + ~Animation() { + if (m_wf & kfAniFreeAnimationData) + delete m_panid; + } + + void Init(AnimationData *panid) secAnimation; + bool Start(int nStrip = 0, int nFrame = 0, word wf = 0) secAnimation; + bool Start(const char *pszStripName, word wf = 0) secAnimation; + bool LoadState(Stream *pstm) secAnimation; + bool SaveState(Stream *pstm) secAnimation; +#ifdef MP_DEBUG_SHAREDMEM + void MPValidate(Animation *paniRemote); +#endif + + void Stop() { + m_wf |= kfAniDone; + } + + bool IsDone() { + return (m_wf & kfAniDone) != 0; + } + + void SetStrip(int nStrip) { + Assert(nStrip >= 0 && nStrip < m_panid->GetStripCount()); + m_nStrip = nStrip; + } + + void SetStrip(const char *pszStripName) secAnimation; + + int GetStrip() { + return m_nStrip; + } + + void SetFrame(int nFrame) { + Assert(nFrame >= 0 && nFrame < m_panid->GetFrameCount(m_nStrip)); + m_nFrame = nFrame; + } + + int GetFrame() { + return m_nFrame; + } + + void SetDelay(int cDelay) { + m_cDelay = cDelay; + } + + void GetSpecialPoint(Point *pptSpecial, int nFrame = -1) { + if (nFrame < 0) + nFrame = m_nFrame; + m_panid->GetSpecialPoint(m_nStrip, nFrame, pptSpecial); + } + + void GetBounds(Rect *prc) { + m_panid->GetBounds(m_nStrip, m_nFrame, prc); + } + + void Draw(DibBitmap *pbm, int x, int y, Side side = ksideNeutral, Rect *prcSrc = NULL) { + m_panid->DrawFrame(m_nStrip, m_nFrame, pbm, x, y, side, prcSrc); + } + + word GetFlags() { + return m_wf; + } + + int GetRemainingFrameTime() { + return m_cCountdown; + } + + AnimationData *GetAnimationData() { + return m_panid; + } + + bool Advance(int cAdvance) secAnimation; + long GetRemainingStripTime() secAnimation; + +private: + AnimationData *m_panid; + int m_nStrip; + int m_nFrame; + word m_wf; + byte m_cCountdown; + byte m_cDelay; +}; + +// Event Manager + +struct Event { // evt + int idf; + int eType; + union { + int chr; + }; + int x; + int y; + dword dw; + dword ff; + long ms; +}; +#define kfEvtFinger 0x00000001 +#define kfEvtCoalesce 0x00000002 + +// Custom events + +#define gamePaintEvent 0x6000 +#define penHoldEvent 0x6001 +#define gameOverEvent 0x6002 +#define penHoverEvent 0x6003 +#define updateTriggersEvent 0x6004 +#define gameSuspendEvent 0x6005 +#define gameResumeEvent 0x6006 +#define resizeEvent 0x6007 +#define hidePinEvent 0x6008 +#define cancelModeEvent 0x6009 +#define nullEvent 0x600a +#define transportEvent 0x600b +#define runUpdatesNowEvent 0x600c +#define askStringEvent 0x600d +#define mpEndMissionWinEvent 0x600e +#define mpEndMissionLoseEvent 0x600f +#define mpShowObjectivesEvent 0x6010 +#define checkGameOverEvent 0x6011 +#define connectionCloseEvent 0x6012 +#define showMessageEvent 0x6013 + +// gameOverEvent constants (placed in Event::dw) + +enum { + knGoSuccess, + knGoFailure, + knGoInitFailure, + knGoAbortLevel, + knGoRetryLevel, + knGoAppStop, + knGoLoadSavedGame, +}; +#define knAppExit 0xBEEFFEED // modifer to AppStopEvent, used only in windows for WM_CLOSE. + +#define kcevtPostMax 20 + +#define kfRedrawDirty 1 +#define kfRedrawMax 2 +#define kfRedrawBeforeTimer 4 +#define kfRedrawBeforeInput 8 + +struct FlickVector { + int GetMagnitude() { + return dx * dx + dy * dy; + } + int dx; + int dy; + dword cms; +}; +#define kcevtPenHistory 32 // keep power of 2 +#define kcmsFlickQuantum 100 + +class EventMgr // evm +{ +public: + EventMgr() secEventMgr; + ~EventMgr() secEventMgr; + + bool PeekEvent(Event *pevt, long cWait = 0) secEventMgr; + bool GetEvent(Event *pevt, long cWait = -1, bool fCheckPaints = true) secEventMgr; + void PostEvent(Event *pevt, bool fCoalesce = true) secEventMgr; + void PostEvent(int eType, bool fCoalesce = true) secEventMgr; + bool DispatchEvent(Event *pevt) secEventMgr; + void Init() secEventMgr; + void SetPenEventInterval(word ctInterval) secEventMgr; + void ClearAppStopping() secEventMgr; + bool GetFlickVector(int nPen, FlickVector *pfliv); + + void SetAppStopping() { + m_fAppStopping = true; + } + + bool IsAppStopping() { + return m_fAppStopping; + } + + void SetRedrawFlags(word wfRedraw) + { + m_wfRedraw |= wfRedraw; + } + + void ClearRedrawFlags(word wfRedraw) + { + m_wfRedraw &= ~wfRedraw; + } + + word GetRedrawFlags() + { + return m_wfRedraw; + } + +private: + void UpdatePenHistory(Event *pevt) secEventMgr; + bool QueryPenHistory(int nPen, long t, Point *ppt); + + int m_ievtPen1Next; + Event m_aevtPen1History[kcevtPenHistory]; + int m_ievtPen2Next; + Event m_aevtPen2History[kcevtPenHistory]; + bool m_fPenHistoryInitialized; + Event m_evtPeek; + bool m_fPeekKeep; + Event m_aevtQ[kcevtPostMax]; + int m_cevt; + bool m_fAppStopping; + word m_wfRedraw; + bool m_fTimingPenHold; + long m_tPenDown; + int m_xHold, m_yHold; + word m_ctMoveEventInterval; + word m_nctMoveEventFraction; + long m_tLastMoveEvent; +}; +extern EventMgr gevm; + +//--------------------------------------------------------------------------- +// StateMachine +// Portions Copyright (C) Steve Rabin, 2000 + +class StateMachine; +class StateMachineMgr; + +// NOTE: be sure to update the corresponding string table in StateMachine.cpp + +enum State { // st + kstReservedNull, + kstReservedZombie, + kstReservedGlobal, + kstGuard, + kstMove, + kstAttack, + kstChase, + kstIdle, + kstDying, + kstBuildOtherCompleting, + kstBeingBuilt, + kstHuntEnemies, + kstProcessorGetMiner, + kstProcessorPutMiner, + kstProcessorTakeGalaxite, + kstMinerMoveToProcessor, + kstMinerRotateForEntry, + kstMinerMine, + kstMinerFindGalaxite, + kstMinerFaceGalaxite, + kstMinerApproachGalaxite, + kstMinerSuck, + kstMinerStepAside, + kstChangeStatePendingFireComplete, + kstContinueActionPendingFireComplete, +}; + +STARTLABEL(StateNames) + LABEL(kstReservedNull) + LABEL(kstReservedZombie) + LABEL(kstReservedGlobal) + LABEL(kstGuard) + LABEL(kstMove) + LABEL(kstAttack) + LABEL(kstChase) + LABEL(kstIdle) + LABEL(kstDying) + LABEL(kstBuildOtherCompleting) + LABEL(kstBeingBuilt) + LABEL(kstHuntEnemies) + LABEL(kstProcessorGetMiner) + LABEL(kstProcessorPutMiner) + LABEL(kstProcessorTakeGalaxite) + LABEL(kstMinerMoveToProcessor) + LABEL(kstMinerRotateForEntry) + LABEL(kstMinerMine) + LABEL(kstMinerFindGalaxite) + LABEL(kstMinerFaceGalaxite) + LABEL(kstMinerApproachGalaxite) + LABEL(kstMinerSuck) + LABEL(kstMinerStepAside) + LABEL(kstChangeStatePendingFireComplete) + LABEL(kstContinueActionPendingFireComplete) +ENDLABEL(StateNames) + +STARTLABEL(GobTypes) + LABEL(kgtNone) + LABEL(kgtShortRangeInfantry) + LABEL(kgtLongRangeInfantry) + LABEL(kgtHumanResourceCenter) + LABEL(kgtSurfaceDecal) + LABEL(kgtScenery) + LABEL(kgtAnimation) + LABEL(kgtReactor) + LABEL(kgtProcessor) + LABEL(kgtStructure) + LABEL(kgtUnit) + LABEL(kgtGalaxMiner) + LABEL(kgtHeadquarters) + LABEL(kgtResearchCenter) + LABEL(kgtVehicleTransportStation) + LABEL(kgtRadar) + LABEL(kgtLightTank) + LABEL(kgtMediumTank) + LABEL(kgtMachineGunVehicle) + LABEL(kgtRocketVehicle) + LABEL(kgtTakeoverSpecialist) + LABEL(kgtWarehouse) + LABEL(kgtMobileHeadquarters) + LABEL(kgtOvermind) + LABEL(kgtTankShot) + LABEL(kgtRocket) + LABEL(kgtMachineGunTower) + LABEL(kgtRocketTower) + LABEL(kgtScorch) + LABEL(kgtSmoke) + LABEL(kgtPuff) + LABEL(kgtBullet) + LABEL(kgtArtillery) + LABEL(kgtArtilleryShot) + LABEL(kgtAndy) + LABEL(kgtReplicator) + LABEL(kgtActivator) + LABEL(kgtFox) + LABEL(kgtAndyShot) +ENDLABEL(GobTypes) + +// Helper macros for sending messages +// (would be better as inline functions but I don't trust gcc to do the right thing) + +#if 0 // not used +#define SendHitMsg(_gidReceiver, _pplrAttacker, _nDamage) { \ + Message msgT; \ + msgT.mid = kmidHit; \ + msgT.smidSender = _gidAttacker; \ + msgT.smidReceiver = _gidReceiver; \ + msgT.Hit.gidAssailant = _pplrAttacker->GetId(); \ + msgT.Hit.sideAttacker = _pplrAttacker->GetSide(); \ + msgT.Hit.nDamage = _nDamage; \ + gsmm.SendMsg(&msgT); \ +} +#endif + +#define SendMoveAction(_gidReceiver, _wx, _wy, _tcRadius, _wcMoveDistPerUpdate) { \ + Message msgT; \ + msgT.mid = kmidMoveAction; \ + msgT.smidSender = kgidNull; \ + msgT.smidReceiver = _gidReceiver; \ + msgT.MoveCommand.wptTarget.wx = _wx; \ + msgT.MoveCommand.wptTarget.wy = _wy; \ + msgT.MoveCommand.gidTarget = kgidNull; \ + msgT.MoveCommand.wptTargetCenter.wx = _wx; \ + msgT.MoveCommand.wptTargetCenter.wy = _wy; \ + msgT.MoveCommand.tcTargetRadius = _tcRadius; \ + msgT.MoveCommand.wcMoveDistPerUpdate = _wcMoveDistPerUpdate; \ + gsmm.SendMsg(&msgT); \ +} + +#define SendGuardAreaAction(_gidReceiver, _nArea) { \ + Message msgT; \ + msgT.mid = kmidGuardAreaAction; \ + msgT.smidSender = kgidNull; \ + msgT.smidReceiver = _gidReceiver; \ + msgT.GuardAreaCommand.nArea = _nArea; \ + gsmm.SendMsg(&msgT); \ +} + +#define SendGuardVicinityAction(_gidReceiver) { \ + Message msgT; \ + msgT.mid = kmidGuardVicinityAction; \ + msgT.smidSender = kgidNull; \ + msgT.smidReceiver = _gidReceiver; \ + gsmm.SendMsg(&msgT); \ +} + +#define SendMineCommand(_gidReceiver, _wx, _wy) { \ + Message msgT; \ + msgT.mid = kmidMineCommand; \ + msgT.smidSender = kgidNull; \ + msgT.smidReceiver = _gidReceiver; \ + msgT.MoveCommand.wptTarget.wx = _wx; \ + msgT.MoveCommand.wptTarget.wy = _wy; \ + msgT.MoveCommand.gidTarget = kgidNull; \ + msgT.MoveCommand.wptTargetCenter.wx = _wx; \ + msgT.MoveCommand.wptTargetCenter.wy = _wy; \ + msgT.MoveCommand.tcTargetRadius = 0; \ + msgT.MoveCommand.wcMoveDistPerUpdate = 0; \ + gsmm.SendMsg(&msgT); \ +} + +#define SendHuntEnemiesAction(_gidReceiver, _um) { \ + Message msgT; \ + msgT.mid = kmidHuntEnemiesAction; \ + msgT.smidSender = kgidNull; \ + msgT.smidReceiver = _gidReceiver; \ + msgT.HuntEnemiesCommand.um = _um; \ + gsmm.SendMsg(&msgT); \ +} + +// NOTE: The upgrade bits must not overlap the ksum bits used for structures +// because they are used as prerequisites. The InProgress version don't matter. + +const word kfUpgradeHrc = 0x0001; +const word kfUpgradeVts = 0x0002; +const word kfUpgradeHrcInProgress = 0x0004; +const word kfUpgradeVtsInProgress = 0x0008; + +struct DelayedMessage // dmsg +{ + Message msg; + DelayedMessage *pdmsgNext; +}; + +const int knHandled = 1; +const int knNotHandled = 0; +const int knDeleted = -1; + +// StateMachine block macros. One version with embedded tracing, one without. + +#define BeginStateMachine if (st == kstReservedGlobal) { if(0) { +#define State(stTest) return knHandled; } } else if (st == stTest) { if(0) { + +#if defined(DEBUG_HELPERS) +void Log(StateMachine *psm, Message *pmsg); +void Log(StateMachine *psm, State stOld, State stNew); + +#define OnEnter return knHandled; } else if (pmsg->mid == kmidReservedEnter) { \ + Log(this, pmsg); +#define OnExit return knHandled; } else if (pmsg->mid == kmidReservedExit) { \ + Log(this, pmsg); +#define OnUpdate return knHandled; } else if (pmsg->mid == kmidReservedUpdate) { \ + Log(this, pmsg); \ + if (m_fDebug) DebugBreak(); +#define OnMsg(midTest) return knHandled; } else if (pmsg->mid == midTest) { \ + Log(this, pmsg); \ + if (m_fDebug) DebugBreak(); +#define SetState(stNext) { m_stNext = stNext; m_fForceStateChange = true; m_unvl.MinSkip(); \ + Log(this, m_st, stNext); } +#define DiscardMsgs return knHandled; } else { \ + Log(this, pmsg); \ + if (m_fDebug) DebugBreak(); +#else +#define OnEnter return knHandled; } else if (pmsg->mid == kmidReservedEnter) { +#define OnExit return knHandled; } else if (pmsg->mid == kmidReservedExit) { +#define OnUpdate return knHandled; } else if (pmsg->mid == kmidReservedUpdate) { +#define OnMsg(midTest) return knHandled; } else if (pmsg->mid == midTest) { +#define SetState(stNext) { m_stNext = stNext; m_fForceStateChange = true; m_unvl.MinSkip(); } +#define DiscardMsgs return knHandled; } else { +#endif + +#define EndStateMachineInherit(base) return knHandled; } } else { \ + return (int)##base::ProcessStateMachineMessage(st, pmsg); } return (int)##base::ProcessStateMachineMessage(st, pmsg); +#if 0 +#define EndStateMachine return knHandled; } } else { Assert("Invalid State"); \ + return knNotHandled; } return knNotHandled; +#else +#define EndStateMachine return knHandled; } } else { return knNotHandled; } return knNotHandled; +#endif + +class StateMachine // sm +{ + friend class StateMachineMgr; + +public: + StateMachine() secStateMachine; + virtual int ProcessStateMachineMessage(State st, Message *pmsg) secStateMachine; + +#if defined(DEBUG_HELPERS) + virtual char *GetName() = 0; +#endif + + UpdateInterval *GetUpdateInterval() { + return &m_unvl; + } + +protected: + int ProcessMessage(State st, Message *pmsg) { + return ProcessStateMachineMessage(st, pmsg); + } + + UpdateInterval m_unvl; +#ifdef MP_STRESS +public: +#endif + State m_st; +#ifdef MP_STRESS +protected: +#endif + State m_stNext; + bool m_fForceStateChange; +#ifdef DEBUG_HELPERS +public: + friend void Log(StateMachine *psm, Message *pmsg); + bool m_fDebug; + void EnableDebug(bool fDebug) { + m_fDebug = fDebug; + } + + friend void paint(); +#endif +}; + +class StateMachineMgr // smm +{ +public: + StateMachineMgr() secStateMachine; + ~StateMachineMgr() secStateMachine; + bool Init(TimerMgr *ptimm) secStateMachine; + bool SaveState(Stream *pstm) secStateMachine; + bool LoadState(Stream *pstm) secStateMachine; + void ClearDelayedMessages() secStateMachine; + void SendMsg(MessageId mid, StateMachineId smidReceiver) secStateMachine; + void SendMsg(MessageId mid, StateMachineId smidSender, StateMachineId smidReceiver) secStateMachine; + void SendMsg(Message *pmsg) secStateMachine; + void PostMsg(Message *pmsg) secStateMachine; + void PostMsg(MessageId mid, StateMachineId smidSender, StateMachineId smidReceiver) secStateMachine; + void SendDelayedMsg(Message *pmsg, long tDelay) secStateMachine; + void SendDelayedMsg(MessageId mid, long tDelay, StateMachineId smidSender, StateMachineId smidReceiver) secStateMachine; + void DispatchDelayedMessages() secStateMachine; + void RouteMessage(Message *pmsg) secStateMachine; + virtual StateMachineId GetId(StateMachine *psm) = 0; + virtual StateMachine *GetStateMachine(StateMachineId smid) = 0; + +private: + void ProcessStateChange(StateMachine *psm) secStateMachine; + bool StoreDelayedMessage(Message *pmsg) secStateMachine; + +private: + DelayedMessage *m_pdmsgHead; + TimerMgr *m_ptimm; + +#ifdef DEBUG_HELPERS + friend class DelayedMessageViewer; +#endif + +#ifdef TRACKSTATE + void TrackState(StateFrame *frame, Message *pmsg); +#endif +}; + +const int kcmsgCommandQueueMax = 300; + +class CommandQueue // cmdq +{ +public: + CommandQueue() secCommandQueue; + ~CommandQueue() secCommandQueue; + + bool Init(int cmsgMax = kcmsgCommandQueueMax) secCommandQueue; + void Exit() secCommandQueue; + + bool LoadState(Stream *pstm) secCommandQueue; + bool SaveState(Stream *pstm) secCommandQueue; + void Enqueue(MessageId mid, StateMachineId smidReceiver) secCommandQueue; + void Enqueue(Message *pmsg) secCommandQueue; + + void Clear() { + m_cmsg = 0; + } + + Message *GetFirst() { + return m_amsg; + } + + int GetCount() { + return m_cmsg; + } + +private: + void SetEntry(Message *pqmsg) secCommandQueue; + +private: + int m_cmsg; + int m_cmsgMax; + Message *m_amsg; +}; + + +#define kcbPvarNameMax 21 +#define kcbPvarValueMax kcbFilename + +struct DictionaryEntry // de +{ + DictionaryEntry *pdeNext; + char szName[kcbPvarNameMax]; + char szValue[kcbPvarValueMax]; +}; + +class Dictionary // dict +{ +public: + Dictionary() secMisc; + ~Dictionary() secMisc; + bool Init(Dictionary *pdict) secMisc; + void Clear() secMisc; + const char *Get(const char *pszName) secMisc; + bool Set(const char *pszName, const char *pszValue) secMisc; + bool Remove(const char *pszName) secMisc; + bool LoadState(Stream *pstm) secMisc; + bool SaveState(Stream *pstm) secMisc; + +private: + DictionaryEntry **Find(const char *pszName) secMisc; + + int m_cde; + DictionaryEntry *m_pdeHead; +}; + +// Game + +struct ModeMatch +{ + int imode; + int nDepthData; + int nSizeData; +}; + +#define kfGameRoleServer 2 +#define kfGameMultiplayer 4 +#define kfGameInitFonts 0x10 +#define kfGameInitBitmap 0x20 +#define kfGameInitDone 0x40 + +class MultiFormMgr; +class InputUIForm; +class Form; +class Chatter; +struct PreferencesV100; +struct PreferencesV101; +class Game // game +{ +public: + Game() secGame; + ~Game() secGame; + + bool Init(int imm) secGame; + void Exit() secGame; + void Shell() secGame; + int RunSimulation(Stream *pstm, char *pszLevel, word wfRole, dword gameid, + Chatter *chatter); + bool FilterEvent(Event *pevt) secGame; + void GetPlayfieldSize(Size *psiz) secGame; + void RequestModeChange(int imm) secGame; + SimUIForm *GetSimUIForm() secGame; + InputUIForm *GetInputUIForm() secGame; + Form *GetMiniMapForm() secGame; + void SaveGame(Stream *pstm = NULL) secGame; + char *GetNextLevel() secGame; + void SetNextLevel(char *pszLevel) secGame; + int PlayLevel(MissionIdentifier *pmiid, Stream *pstmSavedGame = NULL, + int nRank = 0) secGame; + int PlaySavedGame(Stream *pstm) secGame; + bool IsMultiplayer() secGame; + void SetGameSpeed(long t) secGame; + void SavePreferences() secGame; + bool SaveReinitializeGame() secGame; + bool GetFormattedVersionString(char *pszVersion, char *pszOut) secGame; + void ClearDisplay() secGame; + bool GetVar(const char *pszName, char *pszBuff, int cbBuff) secGame; + bool SetVar(const char *pszName, const char *pszValue) secGame; + void CalcUnitCountDeltas(Level *plvl) secGame; + bool IsVersionCompatibleWithExe(int nVersionCompareShip, int nVersionCompareMajor, char chVersionCompareMinor, bool fUpwardCompatOK) secGame; + void ParseVersion(char *pszVersion, int *pnShipVersion, int *pnMajorVersion, char *pchMinorVersion) secGame; + bool AskResignGame(bool fTellHost = true) secGame; + bool AskObserveGame() secGame; + bool CheckDatabaseVersion(const char *pszDir, char *pszPdb, + bool fUpwardCompatOK) secGame; + + // ModeMatch helpers + + int GetModeMatchBest() { + return m_immBest; + } + + int GetModeMatchCurrent() { + return m_immCurrent; + } + + int GetModeMatchCount() { + return m_cmm; + } + + void GetModeMatch(int imm, ModeMatch *pmm) { + *pmm = m_amm[imm]; + } + + word GetFlags() { + return m_wf; + } + + const MissionIdentifier& GetLastMissionIdentifier() { + return m_miid; + } + + void SkipSaveReinitializeGame() { + m_fSkipSaveReinitialize = true; + } + + dword GetGameId() { + return m_gameid; + } + + void ScheduleUpdateTriggers(); + void UpdateTriggers(); + +private: + bool CheckMemoryAvailable() secGame; + void Suspend() secGame; + void LoadPreferences() secGame; + bool LoadPreferences2() secGame; + bool LoadPreferencesV100(PreferencesV100 *pprefsV100); + bool LoadPreferencesV101(PreferencesV101 *pprefsV101); + bool InitDisplay(int imm) secGame; + int FindBestModeMatch2(int nDepthData, int nSizeData, int nDepthMode, int cxWidthModeMin, int cxWidthModeMax, byte bfMatch) secGame; + int FindBestModeMatch(int nSizeDataAbove) secGame; + void AddModeMatches(int nDepthData, int nSizeData, int nDepthOrGreater, int cxWidthOrGreater) secGame; + bool LoadGameData() secGame; + bool InitCoordMappingTables() secGame; + bool InitSimulation(Stream *pstm, char *pszLevel, word wfRole, + dword gameid, Chatter *chatter); + void ExitSimulation() secGame; + ddword IsDataPresent(int cBpp) secGame; + bool InitMemMgr() secGame; + bool InitMultiFormMgr() secGame; + + dword m_gameid; + int m_cmm; + int m_cmmAlloc; + ModeMatch *m_amm; + int m_immCurrent; + int m_immBest; + SimUIForm *m_pfrmSimUI; + InputUIForm *m_pfrmInputUI; + Form *m_pfrmMiniMap; + Size m_sizPlayfield; + word m_wf; + bool m_fSimUninitialized; + char m_szNextLevel[kcbFilename]; + MissionIdentifier m_miid; + Dictionary m_dictPvars; + bool m_fSkipSaveReinitialize; + bool m_fUpdateTriggers; +}; +extern Game ggame; + +void GameMain(char *psz) secGame; + +enum PlayMode { + kpmSavedGame, + kpmNormal +}; + +class Shell +{ +public: + Shell() secShell; + bool Init() secShell; + void Exit() secShell; + void Launch(bool fLoadReinitializeSave = false, + MissionIdentifier *pmiid = NULL) secShell; + int PlayGame(PlayMode pm, MissionIdentifier *pmiid, Stream *pstm, + int nRank) secShell; + void SetPalette() secShell; + +private: + bool DoPlay(); + bool PlayChallengeLevel(bool fStory = false) secShell; + bool BeginNewGame() secShell; + bool PlaySinglePlayer(const PackId *ppackid) secShell; + bool PlayMultiplayer(const PackId *ppackid) secShell; + void DownloadMissionPack() secShell; + + FileMap m_fmapPalette; + FileMap m_fmapShadowMap; + Palette *m_ppal; + byte *m_mpiclriclrShadow; +}; +extern Shell gshl; + +#define knStageNone 0 +#define knStageCollect 1 +#define knStageCheckDraw 2 + +// UpdateMap + +struct MapInfo +{ + int cxLeftTile; + int cyTopTile; + int cxRightTile; + int cyBottomTile; + int ctxInside; + int ctyInside; +}; + +class UpdateMap +{ +public: + UpdateMap() secUpdateMap; + ~UpdateMap() secUpdateMap; + + bool Init(Size *psiz) secUpdateMap; + void Reset() secUpdateMap; + void InvalidateRect(Rect *prc = NULL) secUpdateMap; + void InvalidateMapTileRect(TRectSmall *ptrc) secUpdateMap; + bool EnumUpdateRects(bool fFirst, Rect *prcBounds, Rect *prc) secUpdateMap; + bool Scroll(int dx, int dy) secUpdateMap; + bool *GetInvalidMap() secUpdateMap; + void GetMapSize(Size *psiz) secUpdateMap; + MapInfo *GetMapInfo() secUpdateMap; + bool IsInvalid() secUpdateMap; + void SetViewOrigin(int xOrigin, int yOrigin) secUpdateMap; + void Validate() secUpdateMap; + void InvalidateTile(TCoord tx, TCoord ty) secUpdateMap; + void StartMergeDamagedInvalid() secUpdateMap; + void EndMergeDamagedInvalid() secUpdateMap; + + bool IsDamagedInvalid() + { + return m_fInvalidDamage; + } + + bool IsRectInvalid(Rect *prc) + { + TRectSmall trc; + CalcTileRect(prc, &trc); + return IsTileRectInvalid(&trc); + } + + bool IsRectInvalidAndTrackDamage(Rect *prc) + { + TRectSmall trc; + CalcTileRect(prc, &trc); + bool fNewInvalid; + return IsTileRectInvalidAndTrackDamage(&trc, &fNewInvalid); + } + bool IsMapTileRectInvalidAndTrackDamage(TRectSmall *ptrc, bool *pfNewInvalid) secUpdateMap; + +private: + bool EnumRowRects(bool fFirst, Rect *prcBounds, Rect *prc) secUpdateMap; + void InvalidateTileRect(TRectSmall *ptrc) secUpdateMap; + bool IsTileRectInvalidAndTrackDamage(TRectSmall *ptrc, bool *pfNewInvalid) secUpdateMap; + bool IsTileRectInvalid(TRectSmall *ptrc) secUpdateMap; + void CalcTileRect(Rect *prc, TRectSmall *ptrc) secUpdateMap; + void SetMapOffset(int xMapOffset, int yMapOffset, bool fInvalidate = true) secUpdateMap; + + Rect m_rcDib; + int m_ctx; + int m_cty; + int m_txOrigin; + int m_tyOrigin; + int m_xOriginView; + int m_yOriginView; + bool *m_afInvalid; + bool *m_afInvalidDamage; + int m_xMapOffset; + int m_yMapOffset; + bool m_fInvalid; + bool m_fInvalidDamage; + bool m_fMergeDamage; + MapInfo m_mnfo; +}; + +class FlickScroller // flics +{ +public: + FlickScroller(); + bool Init(int nPen, float flMultiplier = 1.0f, + float flDecayPercent = 0.01f, + float cmsDecaySpan = kcmsFlickQuantum / 10, bool fChoose = true); + void Clear(); + bool GetPosition(Point *ppt); + bool HasMagnitude(); + +private: + bool CheckMagnitude(float dx, float dy); + + FlickVector m_fliv; + long m_msStart; + float m_flMultiplier; + float m_flDecayPercent; + dword m_cmsDecaySpan; + bool m_fHasMagnitude; +}; + +// Form Manager + +#define kcFormsMax 16 + +#define kfFrmmNoScroll 1 + +class Form; +class FormMgr // frmm +{ +public: + FormMgr() secFormMgr; + virtual ~FormMgr() secFormMgr; + + virtual bool Init(DibBitmap *pbm, bool fFreeDib) secFormMgr; + virtual void AddForm(Form *pfrm) secFormMgr; + virtual void RemoveForm(Form *pfrm) secFormMgr; + virtual bool EcomSuppressed() secFormMgr; + virtual bool CookEvent(Event *pevt) secFormMgr; + virtual Form *GetFormPtr(word idf) secFormMgr; + virtual Form *LoadForm(IniReader *pini, word idf, Form *pfrm) secFormMgr; + virtual void Paint(bool fScrolled, Rect *prcOpaqueStart) secFormMgr; + virtual void Scroll(int dx, int dy) secFormMgr; + virtual bool HasCapture() secFormMgr; + virtual Form *GetFormCapture() secFormMgr; + virtual Form *GetModalForm() secFormMgr; + virtual void InvalidateRect(Rect *prc) secFormMgr; + virtual DibBitmap *GetDib() secFormMgr; + virtual void CalcOpaqueRect(Form *pfrmStop, Rect *prcOpaqueStart, Rect *prcResult) secFormMgr; + virtual Form *GetFocus() secFormMgr; + virtual void FrameStart() secFormMgr; + virtual void FrameComplete() secFormMgr; + virtual void BreakCapture(); + + void SetFlags(word wf) { + m_wf = wf; + } + word GetFlags() { + return m_wf; + } + int GetFormCount() { + return m_cfrm; + } + +protected: + Form *FindPen2Form(); + bool CookPenEvent(Event *pevt) secFormMgr; + bool CookKeyEvent(Event *pevt) secFormMgr; + virtual bool BltTo(DibBitmap *pbmDst, int yTop, bool fScrolled) secFormMgr; + virtual bool ScrollBits() secFormMgr; + + word m_wf; + word m_idfCapture; + int m_cCaptureDowns; + int m_cfrm; + Form *m_apfrm[kcFormsMax]; + DibBitmap *m_pbm; + UpdateMap *m_pupd; + bool m_fFreeDib; + int m_dxScrollAccumulate; + int m_dyScrollAccumulate; + +public: + UpdateMap *GetUpdateMap() { + return m_pupd; + } + + friend class MultiFormMgr; +}; + +#define kcFormMgrMax 5 +class MultiFormMgr : public FormMgr +{ +public: + MultiFormMgr() secFormMgr; + virtual ~MultiFormMgr() secFormMgr; + + void AddFormMgr(FormMgr *pfrmm, Rect *prc) secFormMgr; + void RemoveFormMgr(FormMgr *pfrmm) secFormMgr; + void DrawFrame(bool fForceBackBufferValid, bool fPaint = true) secFormMgr; + void CheckSetRedrawDirty() secFormMgr; + bool IsInvalid() secFormMgr; + bool GetFormMgrRect(FormMgr *pfrmm, Rect *prc) secFormMgr; + +#ifdef STATS_DISPLAY + int GetUpdateRectCount() secFormMgr; +#endif + + virtual void AddForm(Form *pfrm) secFormMgr; + virtual void RemoveForm(Form *pfrm) secFormMgr; + virtual bool EcomSuppressed() secFormMgr; + virtual bool CookEvent(Event *pevt) secFormMgr; + virtual Form *GetFormPtr(word idf) secFormMgr; + virtual bool Paint(bool fForceBackBufferValid) secFormMgr; + virtual void InvalidateRect(Rect *prc) secFormMgr; + virtual void CalcOpaqueRect(Form *pfrmStop, Rect *prcOpaqueStart, Rect *prcResult) secFormMgr; + virtual Form *GetFocus() secFormMgr; + +private: +#ifdef DRAW_UPDATERECTS + void DrawUpdateRects(UpdateMap *pupd, int yTop) secFormMgr; +#endif + virtual bool BltTo(DibBitmap *pbmDst, int yTop, bool fScrolled) secFormMgr; + bool StealCapturePen2(Event *pevt, FormMgr *pfrmmChildCapture); + + int m_cfrmm; + FormMgr *m_apfrmm[kcFormMgrMax]; + Rect m_arcFormMgr[kcFormMgrMax]; + Event m_evtPen1Down; +}; + +// Form + +#define kcControlsMax 32 +#define kfFrmDeleted 0x01 +#define kfFrmDoModal 0x02 +#define kfFrmVisible 0x04 +#define kfFrmPenInside 0x08 +#define kfFrmHasPalette 0x10 +#define kfFrmHasShadowMap 0x20 +#define kfFrmScaleCoords 0x40 +#define kfFrmAutoTakedown 0x80 +#define kfFrmTranslucent 0x100 +#define kfFrmNoFocus 0x200 +#define kfFrmShowSound 0x400 +#define kfFrmNoEcom 0x800 +#define kfFrmTopMost 0x1000 +#define kfFrmDemandPen2 0x2000 + +#define knNotifySelectionChange 0 +#define knNotifySelectionTap 1 + +class IControlEventHandler { +public: + virtual void OnControlSelected(word nSelect) = 0; + virtual bool OnControlHeld(word idc) = 0; + virtual void OnControlNotify(word idc, int nNotify) = 0; +}; + +class Control; +class Form : public IControlEventHandler +{ +public: + Form() secForm; + virtual ~Form() secForm; + + virtual bool Init(FormMgr *pfrmm, IniReader *pini, word idf) secForm; + virtual bool OnFilterEvent(Event *pevt) { return false; } + virtual bool EventProc(Event *pevt) secForm; + virtual bool DoModal(int *pnResult = NULL, Sfx sfxShow = ksfxGuiFormShow, Sfx sfxHide = ksfxGuiFormHide) secForm; + virtual bool OnHitTest(Event *pevt) secForm; + virtual bool OnKeyTest(Event *pevt) secForm; + virtual bool OnPenEvent(Event *pevt) secForm; + virtual void OnUpdateMapInvalidate(UpdateMap *pupd, Rect *prcOpaque) secForm; + virtual void OnPaintBackground(DibBitmap *pbm, UpdateMap *pupd) secForm; + virtual void OnPaint(DibBitmap *pbm) secForm; + virtual void OnPaintControls(DibBitmap *pbm, UpdateMap *pupd) secForm; + virtual void OnScroll(int dx, int dy) secForm; + virtual void ScrollInvalidate(UpdateMap *pupd) secForm; + virtual void InvalidateRect(Rect *prc) secForm; + virtual void FrameStart() secForm; + virtual void FrameComplete() secForm; + virtual Control *HitTestControls(Event *pevt); + virtual void BreakCapture(); + + // IControlEventHandler methods + + virtual void OnControlSelected(word idc) secForm; + virtual bool OnControlHeld(word idc) secForm; + virtual void OnControlNotify(word idc, int nNotify) secForm; + + FormMgr *GetFormMgr() secForm; + + word GetId() secForm; + word GetFlags() secForm; + void SetFlags(word wf) secForm; + void GetRect(Rect *prc) secForm; + void SetRect(Rect *prc) secForm; + void Show(bool fShow) secForm; + void EndForm(int nResult = 0) secForm; + Control *GetControlPtr(word idc) secForm; + bool IsControlInside(Control *pctl) secForm; + bool AddControl(Control *pctl) secForm; + void SetUserDataPtr(void *pUserData) secForm; + void *GetUserDataPtr() secForm; + Form *FindPen2Form(); + + bool HasCapture() { + return m_pfrmm->GetFormCapture() == this; + } + + Control *GetControlCapture() { + return m_pctlCapture; + } + + int GetResult() { + return m_nResult; + } + +protected: + bool InitFromProperties(FormMgr *pfrmm, word idf, IniReader *pini, char *pszForm) secForm; + + FormMgr *m_pfrmm; + TBitmap *m_ptbm; + Control *m_pctlCapture; + Rect m_rc; + int m_nResult; + word m_wf; + word m_idf; + word m_idcDefault; + word m_idcLast; + int m_cctl; + Sfx m_sfxShow; + Sfx m_sfxHide; + Control *m_apctl[kcControlsMax]; + FileMap m_fmapPalette; + FileMap m_fmapShadowMap; + int m_iclrBack; + void* m_pUserData; + + friend class FormMgr; +}; + +inline FormMgr *Form::GetFormMgr() +{ + return m_pfrmm; +} + +// DialogForm + +#define kcyTitle 10 + +class DialogForm : public Form +{ +public: + DialogForm() secForm; + ~DialogForm() secForm; + + void SetBackgroundColorIndex(int iclr) secForm; + void SetTitleColor(Color clr) secForm; + void SetBorderColorIndex(int iclr) secForm; + void SetClearDibFlag() secForm; + + // Form overrides + + virtual void OnPaintBackground(DibBitmap *pbm, UpdateMap *pupd) secForm; + virtual void OnPaint(DibBitmap *pbm) secForm; + virtual bool OnPenEvent(Event *pevt) secForm; + virtual bool DoModal(int *pnResult = NULL, Sfx sfxShow = ksfxGuiFormShow, Sfx sfxHide = ksfxGuiFormHide) secForm; + +private: + Color m_clrTitle; + int m_iclrBorder; + int m_iclrBackground; + bool m_fClearDib; +}; + +inline void DialogForm::SetTitleColor(Color clr) { + m_clrTitle = clr; +} + +inline void DialogForm::SetBorderColorIndex(int iclr) { + m_iclrBorder = iclr; +} + +inline void DialogForm::SetClearDibFlag() { + m_fClearDib = true; +} + +// ShellForm + +class ShellForm : public Form, public Timer +{ +public: + ShellForm() secForm; + virtual ~ShellForm() secForm; + + // Doesn't match Form's DoModal signature so it's not really an override + + virtual bool DoModal(int *pnResult = NULL, bool fAnimate = true, bool fShowSound = true) secForm; + + // Form overrides + + virtual bool Init(FormMgr *pfrmm, IniReader *pini, word idf) secForm; + virtual void OnPaintBackground(DibBitmap *pbm, UpdateMap *pupd) secForm; + + // Timer interface + + virtual void OnTimer(long tCurrent) secForm; + virtual void OnZipDone() {} + +protected: + bool m_fAnimate; + +private: + bool m_fCached; + int m_axDst[kcControlsMax]; + bool m_fTimerEnabled; + int m_cctlToZip; + long m_tLast; +}; + +// InGameOptions + +class InGameOptionsForm : public ShellForm +{ +public: + virtual bool Init(FormMgr *pfrmm, IniReader *pini, word idf); + virtual void OnControlSelected(word idc); + +private: + void InitResettableControls(); + void UpdateLabels(); + + bool m_fLassoSelection; + long m_tGameSpeed; + word m_wfHandicap; + double m_nScrollSpeed; +}; + +// Control + +#define knSelDownInside 0 +#define knSelUpInside 1 +#define knSelUpOutside 2 +#define knSelMoveInside 3 +#define knSelMoveOutside 4 +#define knSelHoldInside 5 + +#define kfCtlVisible 1 +#define kfCtlSet 2 +#define kfCtlRedraw 4 +#define kfCtlDisabled 8 +#define kfCtlUseSide1Colors 16 + +class Control +{ +public: + Control() secControl; + virtual ~Control() secControl; + + virtual bool Init(Form *pfrm, word idc, int x, int y, int cx, int cy) secControl; + virtual bool Init(Form *pfrm, IniReader *pini, FindProp *pfind) secControl; + virtual void OnPaint(DibBitmap *pbm) secControl; + virtual void OnPenEvent(Event *pevt) secControl; + virtual void OnSelect(int nSelect) secControl; + virtual int OnHitTest(Event *pevt) secControl; + virtual void Invalidate() secControl; + virtual void GetFingerRect(Rect *prc) secControl; + virtual void OnBreakCapture() secControl; + + word GetId() secControl; + void GetRect(Rect *prc) secControl; + void SetRect(Rect *prc, bool fCompareRect = true) secControl; + void SetPosition(int x, int y) secControl; + void Show(bool fShow) secControl; + word GetFlags() secControl; + void SetFlags(word wf) secControl; + void SetEventHandler(IControlEventHandler *pceh) secControl; + void Enable(bool fEnable) secControl; + +protected: + Rect m_rc; + word m_wf; + word m_idc; + Form *m_pfrm; + IControlEventHandler *m_pceh; + + friend class Form; +}; + +inline void Control::SetEventHandler(IControlEventHandler *pceh) { + m_pceh = pceh; +} + +// Button Control + +class ButtonControl : public Control // btn +{ +public: + static bool InitClass() secButtonControl; + static void ExitClass() secButtonControl; + + ButtonControl() secButtonControl; + virtual ~ButtonControl() secButtonControl; + + virtual bool Init(Form *pfrm, word idc, int x, int y, int cx, int cy, + char *pszLabel, int nfnt, char *szFnUp = NULL, char *szFnDown = NULL, char *szFnDisabled = NULL) secButtonControl; + virtual bool Init(Form *pfrm, IniReader *pini, FindProp *pfind) secButtonControl; + virtual void OnPaint(DibBitmap *pbm) secButtonControl; + virtual void OnSelect(int nSelect) secButtonControl; + + void SetText(char *psz) secButtonControl; + +private: + virtual bool Init(char *pszLabel, int nfnt, char *szFnUp, char *szFnDown, char *szFnDisabled, bool fCenter) secButtonControl; + +protected: + TBitmap *m_ptbmUp; + TBitmap *m_ptbmDown; + TBitmap *m_ptbmDisabled; + int m_nfnt; + char *m_szLabel; + +private: + static TBitmap *s_ptbmLeftUp; + static TBitmap *s_ptbmMidUp; + static TBitmap *s_ptbmRightUp; + static TBitmap *s_ptbmLeftDown; + static TBitmap *s_ptbmMidDown; + static TBitmap *s_ptbmRightDown; +}; + +class RadioButtonBarControl : public Control +{ +public: + RadioButtonBarControl(); + virtual ~RadioButtonBarControl(); + + virtual bool Init(Form *pfrm, IniReader *pini, FindProp *pfind); + virtual bool Init(const char *pszLabel, int ifnt, int isel); + virtual void OnPaint(DibBitmap *pbm); + virtual void OnPenEvent(Event *pevt); + + int GetSelectionIndex(); + void SetSelectionIndex(int isel); + +private: + void AddLabel(const char *psz, int cch); + void GetCellRects(int icell, Rect *prcInner, Rect *prcOuter); + void GetOuterCellSize(int icell, Size *psiz); + + int m_ifnt; + int m_isel; + int m_cLabels; + const char *m_apszLabels[20]; +}; + +class SilkButtonControl : public Control +{ +public: + ~SilkButtonControl() secButtonControl; + +public: + virtual void OnSelect(int nSelect) secButtonControl; +}; + +inline SilkButtonControl::~SilkButtonControl() { +} + +#if 0 // not used now +// PresetButtonControl + +class PresetButtonControl : public ButtonControl // btn +{ +public: + virtual void OnPaint(DibBitmap *pbm) secPresetButtonControl; + + static bool InitClass() secPresetButtonControl; + static void ExitClass() secPresetButtonControl; + +private: + virtual bool Init(char *pszLabel, int nfnt, char *szFnUp, char *szFnDown, bool fCenter) secPresetButtonControl; +}; +#endif + +// Checkbox Control + +class CheckBoxControl : public Control // cbox +{ +public: + CheckBoxControl() secCheckBoxControl; + virtual ~CheckBoxControl() secCheckBoxControl; + + virtual bool Init(Form *pfrm, word idc, int x, int y, char *pszLabel, int nfnt, bool fChecked) secCheckBoxControl; + virtual bool Init(Form *pfrm, IniReader *pini, FindProp *pfind) secCheckBoxControl; + virtual void OnPaint(DibBitmap *pbm) secCheckBoxControl; + virtual void OnSelect(int nSelect) secCheckBoxControl; + + void SetText(char *psz) secCheckBoxControl; + bool IsChecked() secCheckBoxControl; + void SetChecked(bool fChecked) secCheckBoxControl; + static bool InitClass() secCheckBoxControl; + static void ExitClass() secCheckBoxControl; + +private: + bool Init(char *pszLabel, int nfnt, bool fChecked) secButtonControl; + + static TBitmap *s_ptbmOnUp; + static TBitmap *s_ptbmOnDown; + static TBitmap *s_ptbmOffUp; + static TBitmap *s_ptbmOffDown; + int m_ifnt; + char m_szLabel[64]; + bool m_fChecked; +}; + +// Label Control + +#define kfLblCenterText 0x8000 +#define kfLblMultiLine 0x4000 +#define kfLblRightText 0x2000 +#define kfLblClipVertical 0x1000 +#define kfLblEllipsis 0x0800 +#define kfLblHitTest 0x0400 + +const int kcbLabelTextMax = 1000; + +class LabelControl : public Control // lbl +{ +public: + LabelControl() secLabelControl; + virtual ~LabelControl() secLabelControl; + bool Init(int nfnt, char *pszLabel, char *pszFlags1, char *pszFlags2) secLabelControl; + + virtual bool Init(Form *pfrm, IniReader *pini, FindProp *pfind) secLabelControl; + virtual void OnPaint(DibBitmap *pbm) secLabelControl; + virtual int OnHitTest(Event *pevt) secLabelControl; + + virtual void SetText(const char *psz) secLabelControl; + const char *GetText() secLabelControl; + +protected: + virtual void CalcRect() secLabelControl; + + char *m_szLabel; + int m_nfnt; +}; + +inline const char *LabelControl::GetText() { + return m_szLabel; +} + +class EcomTextControl : public LabelControl // ect +{ +public: + EcomTextControl() secEcom; + virtual ~EcomTextControl() secEcom; + + virtual bool Init(Form *pfrm, IniReader *pini, FindProp *pfind) secEcom; + virtual void OnPaint(DibBitmap *pbm) secEcom; + virtual int OnHitTest(Event *pevt) secEcom; + virtual void SetText(char *psz) secEcom; + + // new methods + + bool ShowMoreText() secEcom; + void ShowAll() secEcom; + void DrawText(DibBitmap *pbm, Font *pfnt, char *psz, int x, int y, int cx, int cchMax) secEcom; + +private: + void CalcRect() secEcom; + + int m_cchCur; + dword m_aiclrEcom[4]; + long m_ctPrevTime; +}; + +const int kctEcomOutputInterval = kctUpdate; // how often we call the timer + +// Help Control + +// define types + +#define knChunkRawText 0 +#define knChunkBitmap 1 +#define knChunkLinkText 2 +#define knChunkLargeText 3 +#define knChunkGenericTag 4 +#define knChunkAniData 5 +#define knChunkHRTag 6 + +typedef struct Chunk { + int nType; + char *psz; + int cch; + DibBitmap *pbm; + void *pv; + char szText[30]; + bool fLargeFont; +} Chunk; + +typedef struct HitTest { + int x; + int y; + int nchBuffer; + int cch; + int dBest; + bool fHit; + char szText[30]; +} HitTest; + +// Positioning Constants +#define knFindPosRunToIndex 0 +#define knFindPosAtLeastY 1 +#define knFindPosAtMostY 2 +#define knFindPosFingerScroll 3 + +typedef struct FindPositionHelper { + int nDistY; + int nIndex; + int cyControl; + int cySpan; + bool fLargeFontSpanMet; + int nchSpanMet; + bool fLargeFontLastHR; + int nchLastHR; + bool fLargeFontFirstHR; + int nchFirstHR; + int nCondition; + bool fLargeFont; +} FindPositionHelper; + +#define knHelpControlBRHeight (gapfnt[kifntDefault]->GetHeight() * 3) + +typedef bool (*ChunkProc)(int x,int y, int cx, int cy, int nchBuffer, int yCurrent, int nyBottom, Chunk *pchk, void *pv); + +#define kfHelpScrollPosition 0x8000 + +class HelpControl : public Control, Timer +{ +public: + HelpControl() secHelpControl; + ~HelpControl() secHelpControl; + + bool FollowLink(const char *pszLink, int cch = -1) secHelpControl; + void DoNextPage() secHelpControl; + void DoPrevPage() secHelpControl; + void DoIndex() secHelpControl; + void DoBack() secHelpControl; + bool SetFile(const char *pszFile) secHelpControl; + + // Control overrides + + virtual bool Init(Form *pfrm, IniReader *pini, FindProp *pfind) secHelpControl; + virtual void OnPaint(DibBitmap *pbm) secHelpControl; + virtual void OnPenEvent(Event *pevt) secHelpControl; + +private: + virtual void OnTimer(long tCurrent); + + bool Layout(dword nchStart, bool fLargeFont, DibBitmap *pbm, ChunkProc pfn, void *pv) secHelpControl; + int LineHeight(bool) secHelpControl; + int FindPrevPosition(int nchFrom, int cyAmount, bool *pfLargeFont) secHelpControl; + int FindNextPosition(int nchFrom, int cySpan, bool *pfLargeFont, + bool fCondition, bool fSmooth) secHelpControl; + void GetSubRects(Rect *prcInterior, Rect *prcScrollPos = NULL); + void DragScroll(int y); + + File *m_pfil; + int m_cb; + bool m_fLargeFont; + char m_szText[129]; + int m_nchCurrent; + int m_nchBack[10]; + int m_cyPageAmount; + bool m_fDrag; + int m_yDrag; + FlickScroller m_flics; + int m_yDragUp; + bool m_fTimerAdded; + HitTest m_hittest; +}; + +void Help(const char *pszAnchor = NULL, bool fPauseSimulation = false, const char *pszFile = NULL) secHelpControl; +void CutScene(const char *pszScene, bool fPauseSimulation) secCutScene; + +// Edit Control + +class EditControl : public Control // edc +{ +public: + EditControl() secEditControl; + ~EditControl() secEditControl; + + virtual bool Init(Form *pfrm, IniReader *pini, FindProp *pfind) secEditControl; + virtual void OnPaint(DibBitmap *pbm) secEditControl; + + void SetText(const char *psz) secEditControl; + void GetText(char *psz, int cb) secEditControl; + +private: + char m_szText[100]; + int m_nfnt; +}; + +inline EditControl::~EditControl() { +} + +// List Control + +#define kfLstcBorder 0x8000 +#define kfLstcScrollPosition 0x4000 +#define kfLstcKeepInteriorPositioning 0x2000 + +#define kfLstTabEllipsis 1 +#define kfLstTabCenter 2 +#define kfLstTabCenterOn 4 +#define kfLstTabRight 8 + +struct ListItem; + +class ListControl : public Control, IControlEventHandler, Timer // lstc +{ +public: + static bool InitClass() secListControl; + static void ExitClass() secListControl; + + ListControl() secListControl; + virtual ~ListControl() secListControl; + + Font *GetFont() secListControl; + void Clear() secListControl; + bool Add(ListItem *pli) secListControl; + bool Add(const char *psz, void *pvData = NULL) secListControl; +// ListItem *EnumItemPtr(Enum *penum) secListControl; + int GetSelectedItemIndex() secListControl; + ListItem *GetSelectedItem() secListControl; + void *GetSelectedItemData() secListControl; + void SetSelectedItemData(void *pvData) secListControl; + bool GetSelectedItemText(char *psz, int cb) secListControl; + bool SetSelectedItemText(const char *psz) secListControl; + void Select(int iItem, bool fOnly = false, bool fMakeCenter = false) secListControl; + int GetCount() secListControl; + void SetTabStops(int x0, int x1 = -1, int x2 = -1, int x3 = -1); + void SetTabFlags(word wf0, word wf1 = 0, word wf2 = 0, word wf3 = 0); + virtual void GetSubRects(Rect *prcInterior, Rect *prcUpArrow = NULL, + Rect *prcDownArrow = NULL, Rect *prcScrollPosition = NULL); + void SetScrollPosColorIndex(int iclr) { m_iclrScrollPos = iclr; } + + // IControlEventHandler methods + + virtual void OnControlSelected(word idc) secForm; + virtual bool OnControlHeld(word idc) secForm; + virtual void OnControlNotify(word idc, int nNotify) secForm; + + // Control overrides + + virtual bool Init(Form *pfrm, IniReader *pini, FindProp *pfind) secListControl; + virtual void OnPaint(DibBitmap *pbm) secListControl; + virtual void OnSelect(int nSelect) secListControl; + virtual void OnPenEvent(Event *pevt) secListControl; + + // Timer methods + + virtual void OnTimer(long tCurrent); + + static TBitmap *s_ptbmScrollUpUp; + static TBitmap *s_ptbmScrollUpDown; + static TBitmap *s_ptbmScrollDownUp; + static TBitmap *s_ptbmScrollDownDown; + +protected: + virtual void OnPenEvent2(Event *pevt); + virtual void DrawItem(DibBitmap *pbm, ListItem *pli, int x, int y, + int cx, int cy) secListControl; + virtual void DrawText(DibBitmap *pbm, char *psz, int x, int y, + int cx, int cy, word wf); + +private: + bool NeedsScrollUpArrow() secListControl; + bool NeedsScrollDownArrow() secListControl; + int GetVisibleItemCount() secListControl; + void DragScroll(int y); + +private: + ListItem *m_pliFirst, *m_pliLast; + int m_nfnt; + int m_cli; + int m_iliTop; + int m_cxEllipsis; + int m_yDrag; + int m_yDragUp; + bool m_fTimerAdded; + bool m_fDrag; + int m_iliTopDrag; + FlickScroller m_flics; + int m_iclrScrollPos; + bool m_fPenDown; + +protected: + int m_cyItem; + int m_axTab[4]; + word m_awfTab[4]; +}; + +const int kidcScrollDownButton = 0x7fff; +const int kidcScrollUpButton = 0x7ffe; + +struct ListItem { + ListItem *pliNext; + union { + char szText[80]; + struct { + AnimationData *panid; + int nStrip; + int nFrame; + } Anim; + }; + void *pvData; + bool fSelected; + bool fDisabled; +}; + +class BuilderGob; +class BuildQueue; + +// Animation List Control + +class BuildListControl : public ListControl // alc +{ +public: + // ListControl overrides + + virtual bool Init(Form *pfrm, IniReader *pini, FindProp *pfind) secListControl; + virtual void OnPaint(DibBitmap *pbm) secListControl; + virtual void DrawItem(DibBitmap *pbm, ListItem *pli, int x, int y, int cx, int cy) secListControl; + virtual void GetSubRects(Rect *prcInterior, Rect *prcUpArrow = NULL, + Rect *prcDownArrow = NULL, Rect *prcScrollPos = NULL); + + bool Add(AnimationData *panid, int nStrip, int nFrame, void *pvData, bool fDisabled = false) secListControl; + void SetQueueInfo(BuilderGob *pbldr, BuildQueue *pbq) { m_pbldr = pbldr; m_pbq = pbq;} + +private: + BuilderGob *m_pbldr; + BuildQueue *m_pbq; +}; + +// Bitmap Control + +class BitmapControl : public Control // bmc +{ +public: + BitmapControl() secBitmapControl; + virtual ~BitmapControl() secBitmapControl; + + virtual bool Init(Form *pfrm, IniReader *pini, FindProp *pfind) secBitmapControl; + virtual void OnPaint(DibBitmap *pbm) secBitmapControl; + + void SetBitmap(HtBitmap *phtbm) secBitmapControl; + +private: + HtBitmap *m_phtbm; +}; + +// Slider Control + +class SliderControl : public Control // sldr +{ +public: + SliderControl() secSliderControl; + ~SliderControl() secSliderControl; + + virtual void OnPaint(DibBitmap *pbm) secSliderControl; + virtual void OnSelect(int nSelect) secSliderControl; + virtual void OnPenEvent(Event *pevt) secSliderControl; + + void SetRange(long nMin, long nMax) secSliderControl; + void SetValue(long n) secSliderControl; + long GetValue() secSliderControl; + +private: + long m_nMin, m_nMax; + long m_nValue; +}; + +inline SliderControl::~SliderControl() { +}; + +class GraffitiScrollControl : public Control // grfs +{ +public: + GraffitiScrollControl() secGraffitiScrollControl; + bool IsPainting() secGraffitiScrollControl; + + virtual void OnPenEvent(Event *pevt) secGraffitiScrollControl; + virtual void OnPaint(DibBitmap *pbm) secGraffitiScrollControl; + virtual bool Init(Form *pfrm, IniReader *pini, FindProp *pfind) secGraffitiScrollControl; + +private: + int m_nScale; + int m_xDragStart; + int m_yDragStart; + WCoord m_wxViewStart; + WCoord m_wyViewStart; + bool m_fFrame; +}; + +// MiniMap Control + +#define kfMmVertBorderInside 1 +#define kfMmRedraw 2 +#define kfMmHasPoweredRadar 4 +#define kfMmPenDownTimeout 8 + +class MiniMapControl : public Control // mmc +{ +public: + MiniMapControl() secMiniMapControl; + virtual ~MiniMapControl() secMiniMapControl; + + virtual int OnHitTest(Event *pevt) secMiniMapControl; + virtual void OnPaint(DibBitmap *pbm) secMiniMapControl; + virtual void OnPenEvent(Event *pevt) secMiniMapControl; + virtual bool Init(Form *pfrm, IniReader *pini, FindProp *pfind) secMiniMapControl; + virtual void OnBreakCapture(); + + void Redraw() secMiniMapControl; + void RedrawTRect(TRect *ptrc) secMiniMapControl; + void RedrawTile(TCoord tx, TCoord ty) secMiniMapControl; + void Update() secMiniMapControl; + bool CalcPoweredRadar() secMiniMapControl; + static int CalcWidth() secMiniMapControl; + +private: + void OnPenEvent2(Event *pevt); + + long m_tPenDown; + Event m_evtPenDown; + long m_tInvalidateLast; + int m_nScale; + int m_cyInputUI; + int m_cxyBorder; + DibBitmap *m_pbm; + word m_wfMm; + int m_xOff; + int m_yOff; + TCoord m_ctx; + TCoord m_cty; + byte *m_pbTileData; + word *m_pwTileMap; + int m_cbRowBytes; + byte *m_pbFogMap; + byte m_clrWhite; + byte m_clrBlack; + byte m_clrGalaxite; + byte m_aclrSide[kcSides]; +}; + +// Pip Meter Control + +class PipMeterControl : public Control // btn +{ +public: + static bool InitClass() secPipMeterControl; + static void ExitClass() secPipMeterControl; + + PipMeterControl() secPipMeterControl; + virtual ~PipMeterControl() secPipMeterControl; + + virtual bool Init(Form *pfrm, word idc, int x, int y, int cx, int cy, + char *szPip = NULL) secPipMeterControl; + virtual bool Init(Form *pfrm, IniReader *pini, FindProp *pfind) secPipMeterControl; + virtual void OnPaint(DibBitmap *pbm) secPipMeterControl; + + void SetValue(int nValue) secPipMeterControl; + +private: + virtual bool Init(char *szPip) secPipMeterControl; + +protected: + TBitmap *m_ptbmPip; + int m_nValue; + +private: + static TBitmap *s_ptbmPip; +}; + +// Damge Meter Control + +struct UnitConsts; + +class DamageMeterControl : public Control // btn +{ +public: + static bool InitClass() secDamageMeterControl; + static void ExitClass() secDamageMeterControl; + + DamageMeterControl() secDamageMeterControl; + virtual ~DamageMeterControl() secDamageMeterControl; + + virtual void OnPaint(DibBitmap *pbm) secDamageMeterControl; + + void SetUnitConsts(UnitConsts *puntc) secDamageMeterControl; + +private: + UnitConsts *m_puntc; + static TBitmap *s_ptbmInfantry; + static TBitmap *s_ptbmVehicle; + static TBitmap *s_ptbmStructure; +}; + +// Credits Control + +class CreditsControl : public Control +{ +public: + CreditsControl() {m_cCreditNeeders = 0;m_fDrawCreditSymbol = true;}; + void Update(long nCredits, word cCreditNeeders) secCreditsControl; + + virtual bool Init(Form *pfrm, IniReader *pini, FindProp *pfind) secCreditsControl; + virtual void OnPaint(DibBitmap *pbm) secCreditsControl; + +private: + long m_nCredits; + bool m_fDrawCreditSymbol; + word m_cCreditNeeders; +}; + +// Power Control + +class PowerControl : public Control +{ +public: + PowerControl() {m_fShowPowerSymbol = true; m_fPowerLow = false;}; + void Update(int nDemand, int nSupply) secPowerControl; + + virtual bool Init(Form *pfrm, IniReader *pini, FindProp *pfind) secPowerControl; + virtual void OnPaint(DibBitmap *pbm) secPowerControl; + +private: + + int m_nDemand, m_nSupply; + int m_nDemandMax; + bool m_fShowPowerSymbol; + bool m_fPowerLow; +}; + +// InputUIForm + +class InputUIForm : public Form, public Timer +{ +public: + InputUIForm(Chatter *chatter) secInputUIForm; + ~InputUIForm() secInputUIForm; + void Update() secInputUIForm; + + // form + + virtual bool Init(FormMgr *pfrmm, IniReader *pini, word idf) secInputUIForm; + virtual void OnPaintBackground(DibBitmap *pbm, UpdateMap *pupd) secInputUIForm; + virtual bool EventProc(Event *pevt) secInputUIForm; + virtual void OnControlSelected(word idc) secInputUIForm; + virtual bool OnControlHeld(word idc) secInputUIForm; + + // timer + + virtual void OnTimer(long tCurrent) secInputUIForm; + +private: + Chatter *m_pchatter; + bool m_fTimerAdded; + + void InGameMenu() secInputUIForm; + void TestOptions() secInputUIForm; +}; + +const int kfMasSelect = 1; +const int kfMasMove = 2; +const int kfMasAttack = 4; +const int kfMasShowMenu = 8; + +// SimUIForm + +struct UpdateNetMessageRecord { // unmr + long msReceived; + UpdateNetMessage *punm; + UpdateNetMessageRecord *punmrNext; +}; + +class CommandQueue; +class MobileUnitGob; +class UnitMenu; + +enum UIType { kuitFinger, kuitStylus }; + +const dword kfPhFinger1Down = 0x8000; +const dword kfPhFinger2Down = 0x4000; + +class SimUIForm : public Form, public Timer, IGameCallback, ITransportCallback +{ +public: + SimUIForm(word wfRole, dword gameid, Chatter *chatter); + virtual ~SimUIForm() secSimUIForm; + void Update() secSimUIForm; + void SendUpdateResult(long cUpdatesBlock, long cmsLatency, dword hash); + void CalcLevelSpecificConstants() secSimUIForm; + void InvalidateDragSelection() secSimUIForm; + Gob *HitTestGob(int x, int y, bool fFinger, WCoord *pwx, WCoord *pwy, + bool *pfHitSurrounding); + void MoveOrAttackOrSelect(int x, int y, dword ff) secSimUIForm; + void MoveOrAttackOrSelect(Gob *pgobHit, WCoord wxTarget, WCoord wyTarget, + dword ff); + bool HasSelectedUnits(); + void ShowUnitMenu(Gob *pgob); + void SelectSameUnitTypes(Gob *pgob, bool fSfx); + bool IsSelectionCommand(Gob *pgobHit); + void ClearSelection() secSimUIForm; + void SetUIType(UIType uit); + void CheckMultiplayerGameOver(Pid pid); + void SetObserving(); + + word GetRole() { + return m_wfRole; + } + + class PenHandler { + public: + virtual ~PenHandler() {} + virtual bool OnPenEvent(Event *pevt, bool fScrollOnly) = 0; + virtual void OnPaint(DibBitmap *pbm) = 0; + virtual void CheckScroll() = 0; + virtual dword GetFlags() = 0; + virtual void SetFlags(dword ff) = 0; + }; + + PenHandler *GetPenHandler() { + return m_ppenh; + } + + // Form methods + + virtual bool Init(FormMgr *pfrmm, IniReader *pini, word idf) secSimUIForm; + virtual bool OnFilterEvent(Event *pevt); + virtual bool OnPenEvent(Event *pevt) secSimUIForm; + virtual void OnUpdateMapInvalidate(UpdateMap *pupd, Rect *prcOpaque) secSimUIForm; + virtual void OnPaint(DibBitmap *pbm) secSimUIForm; + virtual void OnPaintControls(DibBitmap *pbm, UpdateMap *pupd) secSimUIForm; + virtual void OnPaintBackground(DibBitmap *pbm, UpdateMap *pupd) secSimUIForm; + virtual void ScrollInvalidate(UpdateMap *pupd) secSimUIForm; + virtual bool EventProc(Event *pevt) secSimUIForm; + virtual void FrameComplete() secSimUIForm; + virtual void OnControlSelected(word idc); + + // Timer methods + + virtual void OnTimer(long tCurrent) secSimUIForm; + + void OnChatButtonBlink(bool fOn); + void OnPlayersButton(); + +private: + UpdateNetMessageRecord *m_punmrFirst; + PenHandler *m_ppenh; + dword m_gameid; + Chatter *m_pchatter; + + class StylusHandler : public PenHandler { + public: + StylusHandler(SimUIForm *psui); + virtual bool OnPenEvent(Event *pevt, bool fScrollOnly); + virtual void OnPaint(DibBitmap *pbm) {} + virtual void CheckScroll(); + virtual dword GetFlags() { return 0; } + virtual void SetFlags(dword ff) {} + private: + SimUIForm *m_psui; + bool m_fDragging; + + void OnPenDown(Event *pevt); + void OnPenUp(Event *pevt); + void OnPenDrag(Event *pevt); + void OnPenHold(Event *pevt); + void CancelModes(); + }; + + class FingerHandler : public PenHandler { + public: + FingerHandler(SimUIForm *psui); + ~FingerHandler(); + virtual bool OnPenEvent(Event *pevt, bool fScrollOnly); + virtual void OnPaint(DibBitmap *pbm); + virtual void CheckScroll(); + virtual dword GetFlags() { return m_ff; } + virtual void SetFlags(dword ff) { m_ff = ff; } + private: + void OnPenDown(Event *pevt, bool fScrollOnly); + void OnPenDown2(Event *pevt, bool fScrollOnly); + void OnPenUp(Event *pevt, bool fScrollOnly); + void OnPenUp2(Event *pevt, bool fScrollOnly); + void OnPenMove(Event *pevt); + void OnPenMove2(Event *pevt); + void OnPenHold(Event *pevt); + void UnhilightGob(); + void EnterNone(); + void EnterHilight(Gob *pgob); + bool CheckSelect(Event *pevt); + void EnterSelect(Event *pevt); + void UpdateSelect(Event *pevt); + bool CheckDragged(Event *pevt, int wcDrag); + void EnterDrag(Event *pevt); + void UpdateDrag(Event *pevt); + void ShowUnitMenu(Gob *pgob); + void ShowUnitTitle(Gob *pgob); + void SetSelection(); + bool IsQuickUp(Event *pevt); + + SimUIForm *m_psui; + int m_xDownLast, m_yDownLast; + WCoord m_wxTarget, m_wyTarget; + bool m_fHitSurrounding; + Gid m_gidHitLast; + Gid m_gidHilight; + long m_tDoubleTap; + int m_xDoubleTapDownLast, m_yDoubleTapDownLast; + bool m_fTimerSet; + bool m_fShowUnitMenu; + UnitMenu *m_pfrmUnitTitle; + dword m_ff; + SelectionSprite *m_pselspr; + dword m_maskA, m_maskB; + int m_x1, m_y1; + int m_x2, m_y2; + Vec2d m_vOffsetA, m_vOffsetB; + int m_xDrag, m_yDrag; + WCoord m_wxViewDrag, m_wyViewDrag; + long m_tDown; + + enum State { + FHS_NONE, FHS_HILIGHT, FHS_SELECT, FHS_DRAG + }; + State m_state; + }; + + MobileUnitGob *SetSelectionTargets(Gid gid, WCoord wxTarget, + WCoord wyTarget) secSimUIForm; + void OnLagNotify(Pid pidLagging, int cSeconds) secSimUIForm; + void ReportLaggingPlayer(Player *pplrBehind) secSimUIForm; + void ShowKillLaggyPlayerForm(Pid pidLaggy) secSimUIForm; + void TrackLocalLag() secSimUIForm; + void QueueUpdateMessage(UpdateNetMessage *punm); + bool ProcessUpdateMessage(CommandQueue *pcmdq); + void RunUpdatesNow(); + void CheckLagForm(); + void OnPlayerDisconnectNotify(Pid pid, int nReason); + void OnCheckWin(Pid pid); + + // IGameCallback + void OnReceiveChat(const char *player, const char *chat); + void OnNetMessage(NetMessage **ppnm); + void OnGameDisconnect(); + + // ITransportCallback + void OnStatusUpdate(char *pszStatus); + void OnConnectionClose(); + void OnShowMessage(const char *message); + + static bool s_fReadyForPaint; + + bool m_fTimerAdded; +#ifdef STATS_DISPLAY + bool m_fShowStats; +#endif + word m_wfRole; + CommandQueue *m_pcmdqServer; + Form *m_pfrmWaitingForAllPlayers; + long m_tLastCommunication; + long m_tStartTimeout; + Animation m_aniMoveTarget; + WCoord m_wxMoveTarget; + WCoord m_wyMoveTarget; + int m_nStateMoveTarget; + long m_cUpdatesBlock; + long m_msUpdatesBlock; + DialogForm *m_pfrmLag; + Pid m_pidLagging; + +#ifdef TRACKSTATE + void ReportSyncError(); + void BeginTrackState(); + void EndTrackState(); + StateTracker *m_ptracker; + bool m_fSyncError; +#endif +}; + +// Role defaults to single player client unless these flags are specified +// It is possible to be the creator, but not the server, when connecting to a dedicated game server + +const word kfRoleMultiplayer = 0x0001; // client of a multiplayer game +const word kfRoleServer = 0x0002; // server of a multiplayer game +const word kfRoleCreator = 0x0004; // creator of a multiplayer game + +// Pick a level form + +enum LevelType // lt +{ + kltMultiplayer, + kltChallenge, + kltStory, +}; + +int CreateLevelList(char **asz) secGame; + +class PickLevelForm : public ShellForm +{ +public: + PickLevelForm(LevelType lt) secGame; + + // Form overrides + + virtual bool Init(FormMgr *pfrmm, IniReader *pini, word idf) secGame; + virtual void OnControlSelected(word idc) secGame; + +public: + char m_szLevel[kcbFilename]; + +private: + LevelType m_lt; +}; + +class MissionList; + +class SelectMissionForm : public ShellForm +{ +public: + SelectMissionForm(MissionList *pml, const MissionIdentifier *pmiidFind); + bool GetSelectedMission(MissionIdentifier *pmiid); + + // Form overrides + + virtual bool Init(FormMgr *pfrmm, IniReader *pini, word idf) secGame; + virtual void OnControlSelected(word idc) secGame; + virtual void OnControlNotify(word idc, int nNotify); + virtual bool OnPenEvent(Event *pevt); + +private: + MissionType InitLists(int iMissionSelect); + int IndexFromMissionType(MissionType mt); + MissionType MissionTypeFromIndex(int i); + void SwitchMissionType(MissionType mt); + int GetSelectedMissionIndex(ListControl *plstc); + bool IsSelectedMissionLocked(ListControl *plstc); + void SwitchToMissionType(MissionType mt); + void UpdateDescription(); + + int m_fMagicUnlock; + int m_cMagic; + MissionList *m_pml; + const MissionIdentifier *m_pmiidFind; + MissionType m_mt; + ListControl *m_aplstc[3]; +}; + +// LoadGameForm + +class LoadGameForm : public ShellForm +{ +public: + LoadGameForm() secLoadSave; + void SelectLast(bool fLast) secLoadSave; + + // Form overrides + + virtual bool Init(FormMgr *pfrmm, IniReader *pini, word idf) secLoadSave; + virtual void OnControlSelected(word idc) secLoadSave; + +private: + int m_nGameLast; +}; + +class TestOptionsForm : public DialogForm +{ +public: + virtual bool Init(FormMgr *pfrmm, IniReader *pini, word idf) secGameOptionsForm; + virtual void OnControlSelected(word idc) secGameOptionsForm; +}; + +class MemoryUseForm : public DialogForm +{ +public: + virtual bool Init(FormMgr *pfrmm, IniReader *pini, word idf) secGameOptionsForm; + virtual void OnControlSelected(word idc) secGameOptionsForm; + +private: + void UpdateLabels() secGameOptionsForm; +}; + +// +// Triggers, Conditions, Actions and Condition/Action parameter types +// + +// Condition/Action parameter types + +class QualifiedNumber +{ +public: + bool Parse(char **ppsz) secTrigger; + bool Compare(long nNumber) secTrigger; +#ifdef DEBUG_HELPERS + char *ToString() { + char *pszQualifier = "???"; + switch (m_nQualifier) { + case knQualifierAtLeast: + pszQualifier = "at least"; + break; + + case knQualifierAtMost: + pszQualifier = "at most"; + break; + + case knQualifierExactly: + pszQualifier = "exactly"; + break; + } + + sprintf(s_szDebugHelpers, "%s %d", pszQualifier, m_nNumber); + return s_szDebugHelpers; + } + + static char s_szDebugHelpers[200]; +#endif + + int m_nQualifier; + long m_nNumber; +}; + +// class to run a countdown timer managed by triggers and +// shown in a SimUIForm control. + +#define kfCtVisibleAtStart 1 +#define kfCtRunning 2 + +class CountdownTimer +{ +public: + CountdownTimer() secTrigger; + + bool LoadState(Stream *pstm) secTrigger; + bool SaveState(Stream *pstm) secTrigger; + + void SetTimer(int csecs, char *pszFormatString) secTrigger; + bool GetTimer(int *psecs) secTrigger; + void StartTimer(bool fStart) secTrigger; + void ShowTimer(bool fShow) secTrigger; + void Update() secTrigger; + + word GetFlags() { + return m_wf; + } + +private: + void UpdateString() secTrigger; + + int m_secs; + char m_szFormat[80]; + word m_wf; + long m_tLast; +}; + +// Conditions + +class Condition // cdn +{ +public: + Condition() secCondition; + virtual ~Condition() {} + virtual bool Init(char *psz) secCondition; + virtual bool IsTrue(Side side) = 0; +#ifdef DEBUG_HELPERS + virtual bool SafeIsTrue(Side side) secCondition; + virtual char *ToString() { + return "Base"; + } + static char s_szDebugHelpers[300]; +#endif + Condition *m_pcdnNext; +}; + +class MissionLoadedCondition : public Condition +{ +public: + // REMOVE_SOMEDAY: keep m68k-gcc from generating default constructor in .text section + MissionLoadedCondition() secCondition; + virtual bool IsTrue(Side side) secCondition; +#ifdef DEBUG_HELPERS + virtual char *ToString() { + return "Mission loaded"; + } +#endif +}; + +class CreditsCondition : public Condition +{ +public: + // REMOVE_SOMEDAY: keep m68k-gcc from generating default constructor in .text section + CreditsCondition() secCondition; + virtual bool Init(char *psz) secCondition; + virtual bool IsTrue(Side side) secCondition; +#ifdef DEBUG_HELPERS + virtual char *ToString() { + sprintf(s_szDebugHelpers, "%s has %s credits", PszFromCaSideMask(m_wfCaSideMask), m_qnum.ToString()); + return s_szDebugHelpers; + } +#endif + +private: + word m_wfCaSideMask; + QualifiedNumber m_qnum; +}; + +class OwnsUnitsCondition : public Condition +{ +public: + // REMOVE_SOMEDAY: keep m68k-gcc from generating default constructor in .text section + OwnsUnitsCondition() secCondition; + virtual bool Init(char *psz) secCondition; + virtual bool IsTrue(Side side) secCondition; +#ifdef DEBUG_HELPERS + virtual char *ToString() { + sprintf(s_szDebugHelpers, "%s owns %s %s", PszFromCaSideMask(m_wfCaSideMask), m_qnum.ToString(), PszFromUnitMask(m_um)); + return s_szDebugHelpers; + } +#endif + +private: + word m_wfCaSideMask; + QualifiedNumber m_qnum; + UnitMask m_um; +}; + +class AreaContainsUnitsCondition : public Condition +{ +public: + // REMOVE_SOMEDAY: keep m68k-gcc from generating default constructor in .text section + AreaContainsUnitsCondition() secCondition; + virtual bool Init(char *psz) secCondition; + virtual bool IsTrue(Side side) secCondition; +#ifdef DEBUG_HELPERS + virtual char *ToString(); +#endif + +private: + word m_wfCaSideMask; + QualifiedNumber m_qnum; + UnitMask m_um; + int m_nArea; + int m_nVersionLevel; +}; + +class PlaceStructureModeCondition : public Condition +{ +public: + // REMOVE_SOMEDAY: keep m68k-gcc from generating default constructor in .text section + PlaceStructureModeCondition() secCondition; + virtual bool IsTrue(Side side) secCondition; +#ifdef DEBUG_HELPERS + virtual char *ToString() { + sprintf(s_szDebugHelpers, "place structure mode"); + return s_szDebugHelpers; + } +#endif +}; + +class MinerCantFindGalaxiteCondition : public Condition +{ +public: + // REMOVE_SOMEDAY: keep m68k-gcc from generating default constructor in .text section + MinerCantFindGalaxiteCondition() secCondition; + virtual bool Init(char *psz) secCondition; + virtual bool IsTrue(Side side) secCondition; +#ifdef DEBUG_HELPERS + virtual char *ToString() { + sprintf(s_szDebugHelpers, "%s's Miner can't find Galaxite", PszFromCaSideMask(m_wfCaSideMask)); + return s_szDebugHelpers; + } +#endif + +private: + word m_wfCaSideMask; +}; + +class GalaxiteCapacityReachedCondition : public Condition +{ +public: + // REMOVE_SOMEDAY: keep m68k-gcc from generating default constructor in .text section + GalaxiteCapacityReachedCondition() secCondition; + virtual bool Init(char *psz) secCondition; + virtual bool IsTrue(Side side) secCondition; +#ifdef DEBUG_HELPERS + virtual char *ToString() { + sprintf(s_szDebugHelpers, "%s's Galaxite capacity reached", PszFromCaSideMask(m_wfCaSideMask)); + return s_szDebugHelpers; + } +#endif + +private: + word m_wfCaSideMask; +}; + +class ElapsedTimeCondition : public Condition +{ +public: + // REMOVE_SOMEDAY: keep m68k-gcc from generating default constructor in .text section + ElapsedTimeCondition() secCondition; + virtual bool Init(char *psz) secCondition; + virtual bool IsTrue(Side side) secCondition; +#ifdef DEBUG_HELPERS + virtual char *ToString(); +#endif + +private: + QualifiedNumber m_qnum; +}; + +class SwitchCondition : public Condition +{ +public: + // REMOVE_SOMEDAY: keep m68k-gcc from generating default constructor in .text section + SwitchCondition() secCondition; + virtual bool Init(char *psz) secCondition; + virtual bool IsTrue(Side side) secCondition; +#ifdef DEBUG_HELPERS + virtual char *ToString(); +#endif + +private: + int m_iSwitch; + bool m_fOn; +}; + +class PeriodicTimerCondition : public Condition +{ +public: + // REMOVE_SOMEDAY: keep m68k-gcc from generating default constructor in .text section + PeriodicTimerCondition() secCondition; + virtual bool Init(char *psz) secCondition; + virtual bool IsTrue(Side side) secCondition; +#ifdef DEBUG_HELPERS + virtual bool SafeIsTrue(Side side) secCondition; + virtual char *ToString(); +#endif + +private: + int m_iTimer; +}; + +class CountdownTimerCondition : public Condition +{ +public: + virtual bool Init(char *psz) secCondition; + virtual bool IsTrue(Side side) secCondition; +#ifdef DEBUG_HELPERS + virtual char *ToString() { + sprintf(s_szDebugHelpers, "Coundown Timer is %s", m_qnum.ToString()); + return s_szDebugHelpers; + } +#endif + +private: + QualifiedNumber m_qnum; +}; + +class DiscoversSideCondition : public Condition +{ +public: + // REMOVE_SOMEDAY: keep m68k-gcc from generating default constructor in .text section + DiscoversSideCondition() secCondition; + virtual bool Init(char *psz) secCondition; + virtual bool IsTrue(Side side) secCondition; +#ifdef DEBUG_HELPERS + virtual char *ToString() { + char szSideA[100]; + strcpy(szSideA, PszFromCaSideMask(m_wfCaSideMaskA)); + sprintf(s_szDebugHelpers, "%s discovers %s", szSideA, PszFromCaSideMask(m_wfCaSideMaskB)); + return s_szDebugHelpers; + } +#endif + +private: + word m_wfCaSideMaskA; + word m_wfCaSideMaskB; +}; + +class TestPvarCondition : public Condition +{ +public: + // REMOVE_SOMEDAY: keep m68k-gcc from generating default constructor in .text section + TestPvarCondition() secCondition; + virtual bool Init(char *psz) secCondition; + virtual bool IsTrue(Side side) secCondition; +#ifdef DEBUG_HELPERS + virtual char *ToString() { + sprintf(s_szDebugHelpers, "Pvar \"%s\": is %s", m_szName, m_qnum.ToString()); + return s_szDebugHelpers; + } +#endif + +private: + char m_szName[kcbPvarNameMax]; + QualifiedNumber m_qnum; +}; + +class HasUpgradesCondition : public Condition +{ +public: + // REMOVE_SOMEDAY: keep m68k-gcc from generating default constructor in .text section + HasUpgradesCondition() secCondition; + virtual bool Init(char *psz) secCondition; + virtual bool IsTrue(Side side) secCondition; +#ifdef DEBUG_HELPERS + virtual char *ToString() { + sprintf(s_szDebugHelpers, "%s has ??? upgrades", PszFromCaSideMask(m_wfCaSideMask)); + return s_szDebugHelpers; + } +#endif + +private: + word m_wfCaSideMask; + UpgradeMask m_upgm; +}; + +// Actions + +class TriggerAction // actn +{ +public: + TriggerAction() secAction; + virtual ~TriggerAction() secAction; + virtual bool LoadState(Stream *pstm) secAction; + virtual bool SaveState(Stream *pstm) secAction; + virtual bool Init(char *psz) secAction; + virtual bool Perform(Trigger *ptgr, Side side) = 0; + TriggerAction *m_pactnNext; + +#ifdef DEBUG_HELPERS + virtual char *ToString() { + return "Action"; + } + static char s_szDebugHelpers[200]; +#endif +}; + +class PreserveTriggerAction : public TriggerAction +{ +public: + virtual bool Perform(Trigger *ptgr, Side side) secAction; +}; + +class WaitAction : public TriggerAction +{ +public: + WaitAction() secAction; + virtual bool LoadState(Stream *pstm) secAction; + virtual bool SaveState(Stream *pstm) secAction; + virtual bool Init(char *psz) secAction; + virtual bool Perform(Trigger *ptgr, Side side) secAction; +#ifdef DEBUG_HELPERS + virtual char *ToString(); +#endif + +private: + dword m_ctWait; + dword m_atStartSide[kcSides]; + bool m_afWaitingSide[kcSides]; +}; + +class CenterViewAction : public TriggerAction +{ +public: + virtual bool Init(char *psz) secAction; + virtual bool Perform(Trigger *ptgr, Side side) secAction; +#ifdef DEBUG_HELPERS + virtual char *ToString() { + return "Center view"; + } +#endif + +private: + int m_nArea; +}; + +class SetNextMissionAction : public TriggerAction +{ +public: + virtual bool Init(char *psz) secAction; + virtual bool Perform(Trigger *ptgr, Side side) secAction; +#ifdef DEBUG_HELPERS + virtual char *ToString() { + return "Set next mission"; + } +#endif + +private: + char m_szLevel[kcbFilename]; +}; + +class EndMissionAction : public TriggerAction +{ +public: + virtual bool Init(char *psz) secAction; + virtual bool Perform(Trigger *ptgr, Side side) secAction; +#ifdef DEBUG_HELPERS + virtual char *ToString() { + return "End mission"; + } +#endif + + static void OnMPEndMissionActionEvent(int nWinLose, Side side); + +private: + int m_nWinLose; +}; + +class EcomAction : public TriggerAction +{ +public: + virtual ~EcomAction() secAction; + virtual bool Init(char *psz) secAction; + virtual bool Perform(Trigger *ptgr, Side side) secAction; +#ifdef DEBUG_HELPERS + virtual char *ToString() { + return "Ecom"; + } +#endif + +private: + int m_nBackground; + bool m_fMore; + int m_nCharFrom; + int m_nCharTo; + char *m_pszMessage; +}; + +class SetAllowedUnitsAction : public TriggerAction +{ +public: + virtual bool Init(char *psz) secAction; + virtual bool Perform(Trigger *ptgr, Side side) secAction; +#ifdef DEBUG_HELPERS + virtual char *ToString() { + return "Set allowed units"; + } +#endif + +private: + word m_wfCaSideMask; + UnitMask m_um; +}; + +class SetAllowedUpgradesAction : public TriggerAction +{ +public: + virtual bool Init(char *psz) secAction; + virtual bool Perform(Trigger *ptgr, Side side) secAction; +#ifdef DEBUG_HELPERS + virtual char *ToString() { + return "Set allowed upgrades"; + } +#endif + +private: + word m_wfCaSideMask; + UpgradeMask m_upgm; +}; + +class SetUpgradesAction : public TriggerAction +{ +public: + virtual bool Init(char *psz) secAction; + virtual bool Perform(Trigger *ptgr, Side side) secAction; +#ifdef DEBUG_HELPERS + virtual char *ToString() { + return "Set upgrades"; + } +#endif + +private: + word m_wfCaSideMask; + UpgradeMask m_upgm; +}; + +class AlliesAction : public TriggerAction +{ +public: + virtual bool Init(char *psz) secAction; + virtual bool Perform(Trigger *ptgr, Side side) secAction; +#ifdef DEBUG_HELPERS + virtual char *ToString() { + return "Allies"; + } +#endif + +private: + word m_wfCaSideMaskA; + word m_wfCaSideMaskB; +}; + +class SetObjectiveAction : public TriggerAction +{ +public: + SetObjectiveAction() secAction; + virtual ~SetObjectiveAction() secAction; + + virtual bool Init(char *psz) secAction; + virtual bool Perform(Trigger *ptgr, Side side) secAction; +#ifdef DEBUG_HELPERS + virtual char *ToString() { + return "Set objective"; + } +#endif + +private: + word m_wfCaSideMask; + char *m_szObjective; +}; + +class SetSwitchAction : public TriggerAction +{ +public: + virtual bool Init(char *psz) secAction; + virtual bool Perform(Trigger *ptgr, Side side) secAction; +#ifdef DEBUG_HELPERS + virtual char *ToString() { + return "Set switch"; + } +#endif + +private: + int m_iSwitch; + bool m_fOn; +}; + +class DefogAreaAction : public TriggerAction +{ +public: + virtual bool Init(char *psz) secAction; + virtual bool Perform(Trigger *ptgr, Side side) secAction; +#ifdef DEBUG_HELPERS + virtual char *ToString() { + return "Defog area"; + } +#endif + +private: + int m_nArea; +}; + +// This is the "CreateUnitGroup-Action", not "Create-UnitGroupAction" + +class CreateUnitGroupAction : public TriggerAction +{ +public: + virtual bool Init(char *psz) secAction; + virtual bool Perform(Trigger *ptgr, Side side) secAction; +#ifdef DEBUG_HELPERS + virtual char *ToString() { + return "Create unit group"; + } +#endif + +private: + int m_nUnitGroup; +}; + +class HuntAction : public TriggerAction +{ +public: + virtual bool Init(char *psz) secAction; + virtual bool Perform(Trigger *ptgr, Side side) secAction; +#ifdef DEBUG_HELPERS + virtual char *ToString() { + return "Hunt"; + } +#endif + +private: + word m_wfCaSideMask1; + UnitMask m_um1; + word m_wfCaSideMask2; + UnitMask m_um2; +}; + +// This is the "CreateRandomUnitGroup-Action", not "Create-RandomUnitGroupAction" + +class CreateRandomUnitGroupAction : public TriggerAction +{ +public: + virtual bool Init(char *psz) secAction; + virtual bool Perform(Trigger *ptgr, Side side) secAction; +#ifdef DEBUG_HELPERS + virtual char *ToString() { + return "Create random unit group"; + } +#endif +}; + +// Start Countdown Action + +class StartCountdownAction : public TriggerAction +{ +public: + StartCountdownAction() secAction; + virtual ~StartCountdownAction() secAction; + + virtual bool Init(char *psz) secAction; + virtual bool Perform(Trigger *ptgr, Side side) secAction; + +private: + int m_nSecs; + char *m_szCountdown; +}; + +// Modify countdown action + +class ModifyCountdownAction : public TriggerAction +{ +public: + virtual bool Init(char *psz) secAction; + virtual bool Perform(Trigger *ptgr, Side side) secAction; +#ifdef DEBUG_HELPERS + virtual char *ToString(); +#endif + +private: + int m_nAction; +}; + +class RepairAction : public TriggerAction +{ +public: + virtual bool Init(char *psz) secAction; + virtual bool Perform(Trigger *ptgr, Side side) secAction; +#ifdef DEBUG_HELPERS + virtual char *ToString() { + return "Repair"; + } +#endif + +private: + word m_wfCaSideMask; + bool m_fOn; +}; + +class EnableReplicatorAction : public TriggerAction +{ +public: + virtual bool Init(char *psz) secAction; + virtual bool Perform(Trigger *ptgr, Side side) secAction; +#ifdef DEBUG_HELPERS + virtual char *ToString() { + return "Enable Replicator"; + } +#endif + +private: + word m_wfCaSideMask; + bool m_fOn; +}; + +// Modify Credits action + +class ModifyCreditsAction : public TriggerAction +{ +public: + virtual bool Init(char *psz) secAction; + virtual bool Perform(Trigger *ptgr, Side side) secAction; +#ifdef DEBUG_HELPERS + virtual char *ToString(); +#endif + +private: + word m_wfCaSideMask; + long m_nAmount; + int m_nAction; +}; + +// Move Units In Area action + +class MoveUnitsInAreaAction : public TriggerAction +{ +public: + virtual bool Init(char *psz) secAction; + virtual bool Perform(Trigger *ptgr, Side side) secAction; +#ifdef DEBUG_HELPERS + virtual char *ToString() { + return "MoveUnitsInArea"; + } +#endif + +private: + word m_wfCaSideMask; + UnitMask m_um; + int m_nAreaSrc; + int m_nAreaDst; +}; + +// Set Formal Objective Text action + +const int kcchObjectiveMax = 80; +const int kcchObjectiveStatusMax = 15; +const int kcchObjectiveInfoMax = 350; + +class SetFormalObjectiveTextAction : public TriggerAction +{ +public: + SetFormalObjectiveTextAction() secAction; + virtual ~SetFormalObjectiveTextAction() secAction; + + virtual bool Init(char *psz) secAction; + virtual bool Perform(Trigger *ptgr, Side side) secAction; +#ifdef DEBUG_HELPERS + virtual char *ToString() { + return "SetFormalObjectiveText"; + } +#endif + +private: + int m_iObjective; + char *m_szObjective;; +}; + +// Set Formal Objective Status action + +class SetFormalObjectiveStatusAction : public TriggerAction +{ +public: + SetFormalObjectiveStatusAction() secAction; + ~SetFormalObjectiveStatusAction() secAction; + + virtual bool Init(char *psz) secAction; + virtual bool Perform(Trigger *ptgr, Side side) secAction; +#ifdef DEBUG_HELPERS + virtual char *ToString() { + return "SetFormalObjectiveStatus"; + } +#endif + +private: + int m_iObjective; + char *m_szStatus; +}; + +// Set Formal Objective Info action + +class SetFormalObjectiveInfoAction : public TriggerAction +{ +public: + SetFormalObjectiveInfoAction() secAction; + virtual ~SetFormalObjectiveInfoAction() secAction; + + virtual bool Init(char *psz) secAction; + virtual bool Perform(Trigger *ptgr, Side side) secAction; +#ifdef DEBUG_HELPERS + virtual char *ToString() { + return "SetFormalObjectiveInfo"; + } +#endif + +private: + char *m_szInfo; +}; + +// Show Objectives action + +class ShowObjectivesAction : public TriggerAction +{ +public: + virtual bool Perform(Trigger *ptgr, Side side) secAction; + static void OnMPShowObjectivesEvent(Side side); +}; + +// Show Cut Scene action + +class CutSceneAction : public TriggerAction +{ +public: + CutSceneAction() secAction; + virtual ~CutSceneAction() secAction; + virtual bool Init(char *psz) secAction; + virtual bool Perform(Trigger *ptgr, Side side) secAction; +#ifdef DEBUG_HELPERS + virtual char *ToString() { + return "Cut scene"; + } +#endif + +private: + char *m_pszMessage; +}; + +// Jump To Mission action + +class JumpToMissionAction : public TriggerAction +{ +public: + virtual bool Init(char *psz) secAction; + virtual bool Perform(Trigger *ptgr, Side side) secAction; +#ifdef DEBUG_HELPERS + virtual char *ToString() { + return "Jump to mission"; + } +#endif + +private: + char m_szLevel[kcbFilename]; +}; + +// For the moment, only actions can "block". +// Per-side context is stored in the actions that need it for blocking. + +class Trigger +{ +public: + Trigger() secTrigger; + ~Trigger() secTrigger; + + bool LoadState(Stream *pstm) secTrigger; + bool SaveState(Stream *pstm) secTrigger; + bool Init(IniReader *pini, FindProp *pfind) secTrigger; + void Execute(Side side, bool fForce = false) secTrigger; + void Arm(Side side) secTrigger; + void SetCurrentActionComplete(Side side) secTrigger; + +private: + bool LoadCondition(IniReader *pini, FindProp *pfind) secCondition; + bool LoadAction(IniReader *pini, FindProp *pfind) secAction; + + Condition *m_pcdn; + TriggerAction *m_pactn; + TriggerAction *m_apactnLast[kcSides]; + bool m_afArmed[kcSides]; + +#ifdef DEBUG_HELPERS + friend class TriggerViewer; +#endif +}; + +// Modify Persistent Variable action + +class ModifyPvarAction : public TriggerAction +{ +public: + virtual bool Init(char *psz) secAction; + virtual bool Perform(Trigger *ptgr, Side side) secAction; +#ifdef DEBUG_HELPERS + virtual char *ToString(); +#endif + +private: + char m_szName[kcbPvarNameMax]; + long m_nAmount; + int m_nAction; +}; + +// Set Persistent Variable Text action + +class SetPvarTextAction : public TriggerAction +{ +public: + virtual bool Init(char *psz) secAction; + virtual bool Perform(Trigger *ptgr, Side side) secAction; +#ifdef DEBUG_HELPERS + virtual char *ToString() { + return "Set Pvar Text"; + } +#endif + +private: + char m_szName[kcbPvarNameMax]; + char m_szValue[kcbPvarValueMax]; +}; + +// ShowAlert action + +class ShowAlertAction : public TriggerAction +{ +public: + virtual bool Init(char *psz) secAction; + virtual bool Perform(Trigger *ptgr, Side side) secAction; +#ifdef DEBUG_HELPERS + virtual char *ToString() { + return "Show Alert"; + } +#endif + +private: + char m_szAlert[80]; +}; + +#define kcTriggersPerSide 128 +#define kcSwitchMax 16 +#define kcTriggerTimersMax 16 +#define kctTimerNotStarted -100000 + +class TriggerMgr // tgrm +{ +public: + TriggerMgr() secTrigger; + ~TriggerMgr() secTrigger; + + bool LoadState(Stream *pstm) secTrigger; + bool SaveState(Stream *pstm) secTrigger; + bool Init(IniReader *pini) secTrigger; + void Exit() secTrigger; + void Update() secTrigger; + void SetConditionTrue(int nCondition, SideMask sidm) secTrigger; + bool IsConditionTrue(int nCondition, SideMask sidm) secTrigger; + + CountdownTimer *GetCountdownTimer() { + return &m_cdt; + } + + void SetSwitch(int iSwitch, bool fOn) { + m_abSwitch[iSwitch] = (byte)fOn; + } + + bool GetSwitch(int iSwitch) { + return m_abSwitch[iSwitch] != 0; + } + + int AddPeriodicTimer(long ctPeriod) { + Assert(m_cTimers < kcTriggerTimersMax); + m_actPeriod[m_cTimers] = ctPeriod; + m_actCountdown[m_cTimers] = kctTimerNotStarted; + return m_cTimers++; + } + + // Periodic timers only start counting the first time they are tested. + // This way they can sit 'below' other conditions and have deterministic + // firing times. + + void StartPeriodicTimer(int iTimer) { + if (m_actCountdown[iTimer] == kctTimerNotStarted) + m_actCountdown[iTimer] = m_actPeriod[iTimer]; + } + + bool IsPeriodicTimerTriggered(int iTimer) { + return m_actCountdown[iTimer] <= 0; + } + +#ifdef DEBUG_HELPERS + long GetTimerCountdown(int iTimer) { + return m_actCountdown[iTimer]; + } + + long GetTimerPeriod(int iTimer) { + return m_actPeriod[iTimer]; + } + + char *GetSwitchName(int iSwitch) { + return m_aszSwitchNames[iSwitch]; + } +#endif + + void Enable(bool fEnable) { + m_fEnabled = fEnable; + } + +private: + bool AssignTriggerSides(int ntgr, char *psz) secTrigger; + + int m_ctgr; + Trigger *m_atgr; + byte m_mpSide2nTrigger[kcSides][kcTriggersPerSide]; + SideMask m_asidmCondition[knConditionMax]; + byte m_abSwitch[kcSwitchMax]; + int m_cTimers; + long m_actPeriod[kcTriggerTimersMax]; + long m_actCountdown[kcTriggerTimersMax]; + long m_tLastUpdate; + bool m_fEnabled; + CountdownTimer m_cdt; + +#ifdef DEBUG_HELPERS + char m_aszSwitchNames[kcSwitchMax][50]; + friend TriggerViewer; +#endif +}; + +// UnitGroup +// - directs the process of building all the Units in the group +// - needs to know when Units under its direction complete commands given to them (or deactivate) +// - loads Unit list and UnitGroupActions (called by UnitGroupMgr at Level load time) +// - loads/saves itself + +const byte kfUleBuilt = 0x01; +const byte kfUleReplicant = 0x02; + +struct UnitListEntry { + UnitType ut; + Gid gid; + byte bf; +}; + +class UnitGroupAction; // forward declaration + +class UnitGroup +{ +public: + UnitGroup() secUnitGroup; + ~UnitGroup() secUnitGroup; + + bool LoadState(Stream *pstm) secUnitGroup; + bool SaveState(Stream *pstm) secUnitGroup; + bool Init(IniReader *pini, int iug) secUnitGroup; + void Activate() secUnitGroup; + void Update() secUnitGroup; + void OnBuilt(UnitGob *punt) secUnitGroup; + void AddUnit(UnitGob *punt, bool fReplicant) secUnitGroup; + void RemoveReplicants() secUnitGroup; + + word GetFlags() { + return m_wf; + } + + void SetFlags(word wf) { + m_wf = wf; + } + + Player *GetOwner() { + return m_pplr; + } + + UnitListEntry *GetUnitList() { + return m_aule; + } + + int GetUnitCount() { + return m_cule; + } +#ifdef DEBUG + char *GetName() { + return m_szName; + } +#endif + +private: + bool LoadAction(IniReader *pini, FindProp *pfind) secUnitGroup; + + word m_wf; + UnitGroupAction *m_pactn; + UnitGroupAction *m_pactnLast; + Player *m_pplr; + int m_cule; + UnitListEntry *m_aule; + word m_wfMunt; // flags for built MobileUnits + int m_nSpawnArea; + int m_nHealth; // percent +#ifdef DEBUG + char m_szName[64]; +#endif + +#ifdef DEBUG_HELPERS + friend class UnitGroupViewer; +#endif +}; + +const word kfUgNeedsUnit = 0x0001; +const word kfUgActive = 0x0002; +const word kfUgLoopForever = 0x0004; +const word kfUgCreateAtLevelLoad = 0x0008; +const word kfUgRandomGroup = 0x0010; +const word kfUgReplaceGroup = 0x0020; +const word kfUgSpawn = 0x0040; +const word kfUgWaitingToReplace = 0x0080; +const word kfUgActivatedBefore = 0x0100; +const word kfUgNotRecentlyActivated = 0x0200; + +// UnitGroupMgr +// - Updates the active UnitGroups +// - deactivates groups when the contained units are all deactivated +// - loads group data from Level file +// - loads/saves itself and UnitGroups + +class UnitGroupMgr // tgrm +{ +public: + UnitGroupMgr() secUnitGroup; + ~UnitGroupMgr() secUnitGroup; + + bool LoadState(Stream *pstm) secUnitGroup; + bool SaveState(Stream *pstm) secUnitGroup; + bool Init(IniReader *pini) secUnitGroup; + void Exit() secUnitGroup; + void Update() secUnitGroup; + void ActivateUnitGroup(int iug) secUnitGroup; + void ActivateRandomUnitGroup() secUnitGroup; + bool CreateAtLevelLoadGroups() secUnitGroup; + UnitGroup *GetUnitGroup(Gid gid) secUnitGroup; + +private: + int m_cug; + UnitGroup *m_aug; + +#ifdef DEBUG_HELPERS + friend class UnitGroupViewer; +#endif +}; + +// UnitGroup Actions + +class UnitGroupAction +{ +public: + UnitGroupAction() secAction; + virtual ~UnitGroupAction() {} + virtual bool LoadState(Stream *pstm) secAction; + virtual bool SaveState(Stream *pstm) secAction; + virtual bool Init(char *psz) = 0; + virtual void Reset() secAction; + virtual bool Perform(UnitGroup *pug) = 0; + + UnitGroupAction *m_pactnNext; + +#ifdef DEBUG_HELPERS + virtual char *ToString() { + return "UnitGroupAction"; + } + + static char s_szDebugHelpers[200]; +#endif +}; + +class WaitUnitGroupAction : public UnitGroupAction +{ +public: + WaitUnitGroupAction() secAction; + virtual bool LoadState(Stream *pstm) secAction; + virtual bool SaveState(Stream *pstm) secAction; + virtual bool Init(char *psz) secAction; + virtual void Reset() secAction; + virtual bool Perform(UnitGroup *pug) secAction; +#ifdef DEBUG_HELPERS + virtual char *ToString(); +#endif + +private: + dword m_ctWait; + dword m_tStart; + bool m_fWaiting; +}; + +class SetSwitchUnitGroupAction : public UnitGroupAction +{ +public: + virtual bool Init(char *psz) secAction; + virtual bool Perform(UnitGroup *pug) secAction; +#ifdef DEBUG_HELPERS + virtual char *ToString() { + return "Set switch"; + } +#endif + +private: + int m_iSwitch; + bool m_fOn; +}; + +class MoveUnitGroupAction : public UnitGroupAction +{ +public: + virtual bool LoadState(Stream *pstm) secAction; + virtual bool SaveState(Stream *pstm) secAction; + virtual bool Init(char *psz) secAction; + virtual void Reset() secAction; + virtual bool Perform(UnitGroup *pug) secAction; +#ifdef DEBUG_HELPERS + virtual char *ToString(); +#endif + +private: + bool m_fWaiting; + int m_nArea; +}; + +class AttackUnitGroupAction : public UnitGroupAction +{ +public: + virtual bool LoadState(Stream *pstm) secAction; + virtual bool SaveState(Stream *pstm) secAction; + virtual bool Init(char *psz) secAction; + virtual void Reset() secAction; + virtual bool Perform(UnitGroup *pug) secAction; +#ifdef DEBUG_HELPERS + virtual char *ToString(); +#endif + +private: + // These are initialized from the loaded level + + UnitMask m_um; + word m_wfCaSideMask; + dword m_ctWait; + + // These are persisted in the save game + + bool m_fWaiting; + dword m_tStart; + Gid m_gidTarget; +}; + +class GuardUnitGroupAction : public UnitGroupAction +{ +public: + virtual bool LoadState(Stream *pstm) secAction; + virtual bool SaveState(Stream *pstm) secAction; + virtual bool Init(char *psz) secAction; + virtual void Reset() secAction; + virtual bool Perform(UnitGroup *pug) secAction; +#ifdef DEBUG_HELPERS + virtual char *ToString() { + return "Guard"; + } +#endif + +private: + // These are initialized from the loaded level + + dword m_ctWait; + + // These are persisted in the save game + + bool m_fWaiting; + dword m_tStart; +}; + +class MineUnitGroupAction : public UnitGroupAction +{ +public: + virtual bool Init(char *psz) secAction; + virtual bool Perform(UnitGroup *pug) secAction; +#ifdef DEBUG_HELPERS + virtual char *ToString() { + return "Mine"; + } +#endif +}; + +class GuardVicinityUnitGroupAction : public UnitGroupAction +{ +public: + virtual bool LoadState(Stream *pstm) secAction; + virtual bool SaveState(Stream *pstm) secAction; + virtual bool Init(char *psz) secAction; + virtual void Reset() secAction; + virtual bool Perform(UnitGroup *pug) secAction; +#ifdef DEBUG_HELPERS + virtual char *ToString() { + return "Guard vicinity"; + } +#endif + +private: + // These are initialized from the loaded level + + dword m_ctWait; + + // These are persisted in the save game + + bool m_fWaiting; + dword m_tStart; +}; + +// BuildMgr +// - directs BuilderGobs to build desired Units +// - keeps track of which BuilderGobs are building which Units for which UnitGroups +// - is notified when a BuilderGob completes a build (and what Unit was +// completed and for which UnitGroup) + +struct BuildEntry { + BuildEntry *pbldeNext; + UnitType ut; + int nArea; + UnitGroup *pug; + Gid gidBuilder; +}; + +class BuildMgr +{ +public: + BuildMgr() secBuildMgr; + ~BuildMgr() secBuildMgr; + bool BuildUnit(UnitType ut, UnitGroup *pug, int nArea) secBuildMgr; + void OnBuilt(UnitGob *punt, BuilderGob *pbldr) secBuildMgr; + void Update() secBuildMgr; + +private: + BuildEntry *m_pbldeFirst; +}; + +// Simulation + +// Use min gob count for 68K build because of stack variables + +#ifdef __CPU_68K +#define kcpgobMax 512 +#else +#define kcpgobMax 768 +#endif +#define kcpgobVisibleMax 200 + +class Simulation : public TimerMgr // sim +{ +public: + Simulation() secSimulation; + ~Simulation() secSimulation; + + bool OneTimeInit() secSimulation; + void OneTimeExit() secSimulation; + bool PerLevelInit() secSimulation; + void PerLevelExit() secSimulation; + + bool LoadLevel(const char *pszLevelName) secSimulation; + bool LoadState(Stream *pstm) secSimulation; + bool SaveState(Stream *pstm) secSimulation; + void ClearGobSelection() secSimulation; + void SetGobSelected(Gob *pgob) secSimulation; + void SelectSameUnitTypes(UnitGob *punt, TRect *ptrc) secSimulation; + + Level *GetLevel() { + return m_plvl; + } + + long GetUpdateCount() { + return m_cUpdates; + } + + void SetMiniMapScale(int nScale) { + m_nMiniMapScale = nScale; + } + + int GetMiniMapScale() { + return m_nMiniMapScale; + } + + BuildMgr *GetBuildMgr() { + return m_pbldm; + } + + void Update(CommandQueue *pcmdq) secSimulation; + void Draw(UpdateMap *pupd, DibBitmap *pbm) secSimulation; + void DrawBackground(UpdateMap *pupd, DibBitmap *pbm) secSimulation; + void DrawFog(UpdateMap *pupd, DibBitmap *pbm) secSimulation; + bool SetViewPos(WCoord wx, WCoord wy, bool fInit = false) secSimulation; + void GetViewPos(WCoord *pwx, WCoord *pwy) secSimulation; + bool HitTest(Enum *penm, WCoord wx, WCoord wy, word wf, Gob **ppgob) secSimulation; + Gob *FingerHitTest(WCoord wx, WCoord wy, word wf, bool *pfHitSurrounding) secSimulation; + void Pause(bool fPause) secSimulation; + bool IsPaused() secSimulation; + void FindVisibleGobs(Gob ***pppgobVisible = NULL, int *pcgobVisible = NULL) secSimulation; + void SetSelection(Rect *prc) secSimulation; + + // TimerMgr override + + virtual long GetTickCount() secSimulation; + virtual void AddTimer(Timer *ptmr, long ct) secSimulation; //stub + +#ifdef TRACKSTATE + void TrackState(StateFrame *frame); +#endif + +private: + void HandlePlayerDisconnect(Message *pmsg); + + Level *m_plvl; + WCoord m_wxView, m_wyView; + bool m_fGameOver; + long m_cUpdates; // Can count 80 ms updates for over 10 years without wrapping + long m_tCurrent; + bool m_fPaused; + int m_nMiniMapScale; + BuildMgr *m_pbldm; + + Gob *m_apgobVisible[kcpgobVisibleMax]; + int m_cgobVisible; + WCoord m_wxViewSave; + WCoord m_wyViewSave; + long m_cUpdatesSave; + long m_cupdTriggerMgrUpdateLast; +}; +extern Simulation gsim; + +// Minimap coord from World coord + +inline int MmcFromWc(WCoord wc) { + return ((wc + 0x007f) & 0xff80) >> (9 - gsim.GetMiniMapScale()); // == 7 for hires, 8 for lores +} + +// Level + +// NOTE: changing the sizes any of the 'int's used in the types and +// member variables below will break .ini and .lvl file reading which +// uses the '%d' feature of IniScanf which always stores parsed numbers +// as 'int's. + +struct SideInfo // sidi +{ + long nInitialCredits; + WPoint wptInitialView; + int nIntelligence; + int cStructuresInitial; + int cMobileUnitsInitial; +}; + +class Level // lvl +{ +public: + Level() secLevel; + ~Level() secLevel; + bool Init(const char *pszLevelName, bool fConstantsOnly = false) secLevel; + + bool LoadState(Stream *pstm) secLevel; + bool SaveState(Stream *pstm) secLevel; + bool LoadLevelInfo(const char *pszLevelName, IniReader *piniLoaded = NULL) secLevel; + + TileMap *GetTileMap() { + return m_ptmap; + } + + Palette *GetPalette() { + return m_ppal; + } + + byte *GetShadowMap() { + return m_mpiclriclrShadow; + } + + FogMap *GetFogMap() { + return m_pfogm; + } + + TerrainMap *GetTerrainMap() { + return m_ptrmap; + } + + int GetMinPlayers() { + return m_nPlayersMin; + } + + int GetMaxPlayers() { + return m_nPlayersMax; + } + + const char *GetTitle() { + return m_szTitle; + } + + SideInfo *GetSideInfo(Side side) { + return &m_asidi[side]; + } + + TriggerMgr *GetTriggerMgr() { + return &m_tgrm; + } + + UnitGroupMgr *GetUnitGroupMgr() { + return &m_ugm; + } + + char *GetFilename() { + return m_szFileLevel; + } + + // This version is the "level file format" version + + int GetVersion() { + return m_nVersion; + } + + // This is the per-save unique revision number + + dword GetRevision() { + return m_dwRevision; + } + +private: + bool LoadSideInfo(IniReader *pini, char *pszSideName, SideInfo *psidi) secLevel; + bool LoadLevelConstants(const char *pszLevelName, IniReader *pini) secLevel; + bool LoadLevelVariables(IniReader *pini) secLevel; + +private: + char m_szTitle[kcbLevelTitle]; + char m_szFileLevel[kcbFilename]; + int m_nPlayersMin, m_nPlayersMax; + TileMap *m_ptmap; + Palette *m_ppal; + FileMap m_fmapPalette; + byte *m_mpiclriclrShadow; + FileMap m_fmapShadowMap; + FogMap *m_pfogm; + TerrainMap *m_ptrmap; + bool m_fInitialized; + SideInfo m_asidi[5]; + TriggerMgr m_tgrm; + UnitGroupMgr m_ugm; + + // This is the "level format" version. This is versioned upward over time and is here so + // that WI knows what format the level was saved in. + + int m_nVersion; + + // This is the revision # of the level. So saved games with older revision numbers + // aren't loaded. Note this isn't an incrementing version number. It is a unique + // mark that every mission has. + + dword m_dwRevision; +}; + +// Structure/Unit type values and masks + +const UnitMask kumShortRangeInfantry = 1L << kutShortRangeInfantry; +const UnitMask kumLongRangeInfantry = 1L << kutLongRangeInfantry; +const UnitMask kumTakeoverSpecialist = 1L << kutTakeoverSpecialist; +const UnitMask kumAndy = 1L << kutAndy; +const UnitMask kumFox = 1L << kutFox; +const UnitMask kumInfantry = kumShortRangeInfantry | kumLongRangeInfantry | kumTakeoverSpecialist | kumAndy | kumFox; + +const UnitMask kumGalaxMiner = 1L << kutGalaxMiner; +const UnitMask kumLightTank = 1L << kutLightTank; +const UnitMask kumMediumTank = 1L << kutMediumTank; +const UnitMask kumMachineGunVehicle = 1L << kutMachineGunVehicle; +const UnitMask kumRocketVehicle = 1L << kutRocketVehicle; +const UnitMask kumMobileHeadquarters = 1L << kutMobileHeadquarters; +const UnitMask kumArtillery = 1L << kutArtillery; +const UnitMask kumVehicles = kumGalaxMiner | kumLightTank | kumMediumTank | kumMachineGunVehicle | + kumRocketVehicle | kumMobileHeadquarters | kumArtillery; +const UnitMask kumMobileUnits = kumInfantry | kumVehicles; + +const UnitMask kumHumanResourceCenter = 1L << kutHumanResourceCenter; +const UnitMask kumReactor = 1L << kutReactor; +const UnitMask kumProcessor = 1L << kutProcessor; +const UnitMask kumHeadquarters = 1L << kutHeadquarters; +const UnitMask kumResearchCenter = 1L << kutResearchCenter; +const UnitMask kumVehicleTransportStation = 1L << kutVehicleTransportStation; +const UnitMask kumRadar = 1L << kutRadar; +const UnitMask kumWarehouse = 1L << kutWarehouse; +const UnitMask kumReplicator = 1L << kutReplicator; + +const UnitMask kumMachineGunTower = 1L << kutMachineGunTower; +const UnitMask kumRocketTower = 1L << kutRocketTower; +const UnitMask kumTowers = kumMachineGunTower | kumRocketTower; + +const UnitMask kumStructures = kumHumanResourceCenter | kumReactor | kumProcessor | kumHeadquarters | + kumResearchCenter | kumVehicleTransportStation | kumRadar | kumWarehouse | kumMachineGunTower | + kumRocketTower | kumReplicator; + +// NOTE: we don't count the Replicator as a Builder so the BuildMgr won't start using it +const UnitMask kumBuilder = kumHeadquarters | kumVehicleTransportStation | kumHumanResourceCenter; +const UnitMask kumAll = kumInfantry | kumVehicles | kumStructures; +const UnitMask kumImpossible = 0xffffffff; + +// Upgrade type values and masks (values to be moved to res.h most likely) + +struct Upgrade { // upg + UpgradeMask upgm; + char szName[50]; + char szIconName[20]; + UpgradeMask upgmPrerequisites; + UnitMask umPrerequisites; + int nCost; + int ctTimeToBuild; + char *szDescription; + char *szPrerequisites; +}; + +#define kupgtAdvancedHRC 0 +#define kupgtAdvancedVTS 1 +#define kupgtIncreasedBullpupSpeed 2 +#define kupgtMax 2 + +const UpgradeMask kupgmAdvancedHRC = 1 << kupgtAdvancedHRC; +const UpgradeMask kupgmAdvancedVTS = 1 << kupgtAdvancedVTS; +const UpgradeMask kupgmIncreasedBullpupSpeed = 1 << kupgtIncreasedBullpupSpeed; +const UpgradeMask kupgmAll = kupgmAdvancedHRC | kupgmAdvancedVTS | kupgmIncreasedBullpupSpeed; + +// Player-related types + +class PlayerMgr; + +// UNDONE: pull these from game.ini? + +const long knWarehouseCapacity = 5000; +const long knProcessorCapacity = 3000; + +// Who is using credits during a given update? Useful for sound effects + +#define knConsumerGeneric 0 +#define knConsumerRepair 1 +#define knConsumerMax 1 + +const int kcFormalObjectivesMax = 4; + +const int ksoObjectives = 1; +const int ksoWinSummary = 2; +const int ksoLoseSummary = 3; + +class Player // plr +{ + friend class PlayerMgr; + +public: + Player() secPlayer; + ~Player() secPlayer; + + void Init(Pid pid) secPlayer; + bool LoadState(Stream *pstm) secPlayer; + bool SaveState(Stream *pstm) secPlayer; + void SetName(const char *pszName) secPlayer; + UnitMask GetUnitMask() secPlayer; + void SetCredits(long nCredits, bool fAffectTotals, int nConsumer = knConsumerGeneric) secPlayer; + int GetCreditsDirection() secPlayer; + int GetCreditsConsumer() secPlayer; + void AddPowerSupplyAndDemand(int nPowerSupply, int nPowerDemand) secPlayer; + void Repair(bool fOn) secPlayer; + void Update(long cUpdates) secPlayer; + void SetFormalObjectiveText(int iObjective, char *pszText) secPlayer; + void SetFormalObjectiveStatus(int iObjective, char *pszStatus) secPlayer; + void SetFormalObjectiveInfo(char *pszInfo) secPlayer; + int ShowObjectives(int so, bool fForceInfoDisplay = false, bool fAborting = false) secObjectives; + void ModifyNeedCreditsCount(int cDelta)secPlayer; + bool IsBehind(long cUpdates) secPlayer; + void SetLeftGame() { + m_fLeftGame = true; + } + bool GetLeftGame() { + return m_fLeftGame; + } + char *GetFormalObjectiveText(int iObjective) { + return m_aszFormalObjectiveText[iObjective]; + } + + char *GetFormalObjectiveStatus(int iObjective) { + return m_aszFormalObjectiveStatus[iObjective]; + } + + char *GetFormalObjectiveInfo() { + return m_szFormalObjectiveInfo; + } + + const char *GetName() { + return m_szName; + } + + word GetFlags() { + return m_wf; + } + + void SetFlags(word wf) { + m_wf = wf; + } + + Pid GetId() { + return m_pid; + } + + Side GetSide() { + return m_side; + } + + void SetSide(Side side) { + m_side = side; + m_sidmAllies = GetSideMask(m_side); + m_sidmDiscovered = GetSideMask(m_side); + } + + SideMask GetAllies() { + return m_sidmAllies; + } + + void SetAllies(SideMask sidm) { + m_sidmAllies = sidm | GetSideMask(m_side); + } + + SideMask GetDiscoveredSides() { + return m_sidmDiscovered; + } + + void SetDiscoveredSides(SideMask sidm) { + m_sidmDiscovered = sidm; + } + + void SetDiscoverPoint(TPoint *ptpt) { + m_tptDiscover = *ptpt; + } + + TPoint GetDiscoverPoint() { + return m_tptDiscover; + } + + long GetCredits() { + return m_nCredits; + } + + word GetNeedCreditsCount() { + return m_cStructsNeedCredits; + } + + long GetCapacity() { + return (m_acut[kutWarehouse] * knWarehouseCapacity) + (m_acut[kutProcessor] * knProcessorCapacity); + } + + int GetPowerSupply() { + return m_nPowerSupply; + } + + int GetPowerDemand() { + return m_nPowerDemand; + } + + bool IsPowerLow() { + return m_nPowerDemand > m_nPowerSupply; + } + + void IncUnitCount(UnitType ut) { + m_acut[ut]++; + } + + void DecUnitCount(UnitType ut) { + Assert(m_acut[ut] != 0); + m_acut[ut]--; + } + + int GetUnitCount(UnitType ut) { + return m_acut[ut]; + } + + void IncUnitBuiltCount(UnitType ut) { + m_acutBuilt[ut]++; + } + + void DecUnitBuiltCount(UnitType ut) { + m_acutBuilt[ut]--; + if (m_acutBuilt[ut] < 0) { + m_acutBuilt[ut] = 0; + } + } + + int GetUnitActiveCountFromMask(UnitMask um) secPlayer; + int GetUnitInstanceCountFromMask(UnitMask um) secPlayer; + + word GetUpgrades() { + return m_wfUpgrades; + } + + void SetUpgrades(word wfUpgrades) secPlayer; + + UnitMask GetAllowedUnits() { + return m_umAllowed; + } + + void SetAllowedUnits(UnitMask um) { + m_umAllowed = um; + } + + void SetObjective(char *psz) secSimulation; + + char *GetObjective() { + return m_szObjective; + } + + UpgradeMask GetUpgradeMask() { + return m_upgm; + } + + void SetUpgradeMask(UpgradeMask upgm) { + m_upgm = upgm; + } + + UpgradeMask GetAllowedUpgrades() { + return m_upgmAllowed; + } + + void SetAllowedUpgrades(UpgradeMask um) { + m_upgmAllowed = um; + } + + void IncEnemyMobileUnitsKilled() { + m_cmuntKilled++; + } + + int GetEnemyMobileUnitsKilled() { + return m_cmuntKilled; + } + + void IncEnemyStructuresKilled() { + m_cstruKilled++; + } + + int GetEnemyStructuresKilled() { + return m_cstruKilled; + } + + void IncMobileUnitsLost() { + m_cmuntLost++; + } + + int GetMobileUnitsLost() { + return m_cmuntLost; + } + + void IncStructuresLost() { + m_cstruLost++; + } + + int GetStructuresLost() { + return m_cstruLost; + } + + long GetTotalCreditsAcquired() { + return m_nTotalCreditsAcquired; + } + + void ModifyTotalCreditsConsumed(long nAmount) { + m_nTotalCreditsConsumed += nAmount; + } + + long GetTotalCreditsConsumed() { + return m_nTotalCreditsConsumed; + } + + word GetHandicap() { + return m_wfHandicap; + } + + void SetHandicap(word wf) { + m_wfHandicap = wf; + } + + // Only valid for server-side Player proxies + + long GetUpdateCount() { + return m_cUpdates; + } + + void SetUpdateCount(long cUpdates) { + m_cUpdates = cUpdates; + } + + bool IsLagging(long cUpdates) secPlayer; + int GetLagState() secPlayer; + void SetLagState(int nLagState) secPlayer; + int GetLagTimeout() secPlayer; + +#ifdef TRACKSTATE + void TrackState(StateFrame *frame); +#endif + +private: + word m_wfUpgrades; + Side m_side; + SideMask m_sidmAllies; + SideMask m_sidmDiscovered; + TPoint m_tptDiscover; + char m_szName[kcbPlayerName]; + word m_wf; + Pid m_pid; + long m_nCredits; + int m_nCreditsAcquired, m_nCreditsConsumed; + int m_nDirCredits; + int m_nConsumerCredits; + int m_nPowerSupply; + int m_nPowerDemand; + int m_acut[kutMax]; + int m_acutBuilt[kutMax]; + UnitMask m_umAllowed; + char m_szObjective[kcchObjectiveMax]; + UpgradeMask m_upgm; + UpgradeMask m_upgmAllowed; + char *m_aszFormalObjectiveText[kcFormalObjectivesMax]; + char *m_aszFormalObjectiveStatus[kcFormalObjectivesMax]; + char *m_szFormalObjectiveInfo; + int m_cmuntKilled; + int m_cstruKilled; + int m_cmuntLost; + int m_cstruLost; + long m_nTotalCreditsAcquired; + long m_nTotalCreditsConsumed; + long m_cUpdatesRepairLast; + word m_cStructsNeedCredits; + word m_wfHandicap; + long m_cUpdates; // only valid for server-side Player proxies + int m_nLagState; + long m_tLagStart; + long m_tLastLag; + bool m_fLeftGame; + +#ifdef DETECT_SYNC_ERRORS +public: + UpdateResult m_ur; +#endif +}; + +class PlayersUpdateNetMessage; + +class PlayerMgr // plrm +{ +public: + PlayerMgr() secPlayer; + ~PlayerMgr() secPlayer; + void Reset() secPlayer; + void Init(PlayersUpdateNetMessage *ppunm) secPlayer; + bool Init(char *pszLevel) secPlayer; + bool LoadState(Stream *pstm) secPlayer; + bool SaveState(Stream *pstm) secPlayer; + Player *AllocPlayer(word wf = 0) secPlayer; + void Update(long cUpdates) secPlayer; + void SetAllies(Player **applr, int cplrs, SideMask sidmAllies) secPlayer; + + void FreePlayer(Player *pplr) { + pplr->m_wf = 0; + } + + Player *GetPlayer(Side side) secPlayer; + + Player *GetPlayerFromPid(Pid pid) { + // Pid is unsigned + if (pid != kpidNeutral && pid < kcPlayersMax) { + return &m_aplr[((int)pid)]; + } + return NULL; + } + + Player* GetNextPlayer(Player *pplr) secPlayer; + Player* GetNextHumanPlayer(Player *pplr) secPlayer; + Player* GetNextObservingPlayer(Player *pplr) secPlayer; + const char *GetCreatorName(); + + int GetPlayerCount() secPlayer; + bool DetectTransitionToSingleHumanTeam(Pid pidLeft) secPlayer; + int GetUnitInstanceCountFromMask(UnitMask um, word wfPlr) secPlayer; + void SendWinStats(); + +#ifdef TRACKSTATE + void TrackState(StateFrame *frame); +#endif + +protected: + int GetHumanTeamCount(bool fExtra, Pid pidExtra); + + Player *m_aplr; +}; + +// Gob + +#define kfGobNoEnemiesNearby 0x00000001 // No enemies nearby so don't waste time looking +#define kfGobRedraw 0x00000002 // Something has changed that requires this gob to redraw +//#define kfGobInvalidated 0x00000004 // Gob has been invalidated +#define kfGobBeingUpgraded 0x00000008 // Gob is being upgraded +#define kfGobBeingBuilt 0x00000010 // Gob is in the process of being built, not yet ready for active duty +//#define kfGobOccupyingTerrain 0x00000020 +#define kfGobFlashing 0x00000040 // Gob is flashing +#define kfGobDrawFlashed 0x00000080 // Gob will be drawn flashing +//#define kfGobRepairing 0x00000100 // unused +#define kfGobActive 0x00000200 // Gob is alive and doing its thing +#define kfGobStructure 0x00000400 // Gob is a fixed structure +#define kfGobMobileUnit 0x00000800 // Gob is a mobile unit +#define kfGobUnit 0x00001000 // Gob is a Unit (our version of RTTI!) +#define kfGobDebug 0x00002000 // Gobs DebugBreak at strategic points when this is enabled +#define kfGobSelected 0x00004000 // Gob is selected +#define kfGobStateMachine 0x00008000 // Gob has an active StateMachine that may want Update messages +#define kfGobVisibleLastFrame 0x00010000 // Gob was visible last frame +#define kfGobIncludeFindVisible 0x00020000 // Include gob in next FindVisibleGobs search +#define kfGobTransitioningToVisible 0x00040000 // Gob is going from non-visible to visible +#define kfGobAnimationChanged 0x00080000 // UpdateCount must not affect next AdvanceAnimation + +#define kfGobLayerSurfaceDecal 0x00100000 +#define kfGobLayerDepthSorted 0x00200000 +#define kfGobLayerSmokeFire 0x00400000 +#define kfGobLayerSymbols 0x00800000 // Repairing and need-power symbols draw on this layer +#define kfGobLayerSelection 0x01000000 +#define kfGobLayerMask (kfGobLayerSurfaceDecal | kfGobLayerDepthSorted | kfGobLayerSmokeFire | kfGobLayerSymbols | kfGobLayerSelection) + +// Layers + +#define knLayerSurfaceDecal 0 +#define knLayerDepthSorted 1 +#define knLayerSmokeFire 2 +#define knLayerSymbols 3 +#define knLayerSelection 4 +#define knLayerEnd 4 + +#define knLayerMiniMap -1 + +// When a y coordinate is passed to MakeSortKey as the first value and a Gob +// id (gid) is passed as the second value the resultant key when used for +// sorting will give perfect vertical ordering and stable horizontal ordering. +// +// NOTE: will become spotty when gid values wrap or exceed 1024 (more than +// 256 ids assigned) but this is deemed inconsequential. + +#define MakeSortKey(a, b) (((long)(a) << 8) | (((b) >> 2) & 0xff)) + +class Gob : public StateMachine // gob +{ + friend class StateMachineMgr; + friend class GobMgr; + +public: + Gob() secGob; + virtual ~Gob() secGob; + + dword GetFlags() secGob; + void Flash() secGob; + void MarkRedraw() secGob; + bool Invalidate() secGob; + bool AdvanceAnimation(Animation *pani) secGob; + void StartAnimation(Animation *pani, int nStrip, int nFrame, word wfAni) secGob; + void SetAnimationStrip(Animation *pani, int nStrip) secGob; + void SetAnimationFrame(Animation *pani, int nFrame) secGob; + void SetFlags(dword ff) secGob; + Gid GetId() secGob; + Side GetSide() secGob; + Player *GetOwner() secGob; + void SetOwner(Player *pplr) secGob; + + bool IsGobWithinRange(Gob *pgobTarget, TCoord tcRange) secGob; + bool IsTargetWithinRange(WPoint *pwptTarget, Gob *pgobTarget, TCoord tcRange) secGob; + + virtual void SetPosition(WCoord wx, WCoord wy) secGob; + virtual bool Init(IniReader *pini, FindProp *pfind, const char *pszName) = 0; + virtual bool LoadState(Stream *pstm) secGob; + virtual bool SaveState(Stream *pstm) secGob; + virtual bool IsSavable(); + virtual GobType GetType() = 0; + virtual void Draw(DibBitmap *pbm, int xViewOrigin, int yViewOrigin, int nLayer) = 0; + virtual dword GetSortKey() secGob; + virtual void GetClippingBounds(Rect *prc) secGob; + virtual void GetUIBounds(WRect *pwrc) secGob; + virtual void GetPosition(WPoint *pwpt) secGob; + virtual void GetTilePosition(TPoint *ptpt) secGob; + virtual void GetTileRect(TRect *ptrc) secGob; + virtual void GetTilePaddedWRect(WRect *pwrc) secGob; + virtual void GetCenter(WPoint *pwpt) secGob; + virtual void PopupMenu() secGob; + virtual void InitMenu(Form *pfrm) secGob; + virtual void OnMenuItemSelected(int id) secGob; +#if defined(WIN) && defined(DEBUG) + virtual void ToString(char *psz); +#endif +#ifdef TRACKSTATE + virtual void TrackState(StateFrame *frame); +#endif + + // StateMachine methods + +#if defined(DEBUG_HELPERS) + virtual char *GetName() secGob; +#endif + + // Used for tracking visibility + + TRectSmall m_trcBoundingLast; + +protected: + Gid m_gid; + dword m_ff; + WCoord m_wx, m_wy; + Player *m_pplr; + +private: + Gob *m_pgobNext; + Gid m_gidNext; +}; + +inline dword Gob::GetFlags() { + return m_ff; +} + +inline void Gob::Flash() { + m_ff |= kfGobFlashing | kfGobRedraw; + m_unvl.MinSkip(); +} + +inline void Gob::SetFlags(dword ff) { + m_ff = ff; +} + +inline Gid Gob::GetId() { + return m_gid; +} + +inline Side Gob::GetSide() { + return m_pplr->GetSide(); +} + +inline Player *Gob::GetOwner() { + return m_pplr; +} + +inline void Gob::SetOwner(Player *pplr) { + m_pplr = pplr; +} + +Gob *CreateGob(GobType gt) secGob; + +// GobMgr stuff + +const Gid kgidNull = (Gid)-1; +const word kwfGidEndMarker = 0x8000; + +// AreaMask; assumes 32 areas max + +#define kcAreasMax 32 +#define knAreaLastDiscovery -1 +#if kcAreasMax > 32 +#error Code assumes 32 areas max! Could change to 64 easily enough +#endif +typedef dword AreaMask; + +struct AreaEntry // are +{ + word iareNext; + Gid gid; +}; + +struct Area // ar +{ + TRect trc; + word iareFree; + word iareHead; + word careAlloc; + word careUsed; + SideMask sidm; + UnitMask um; + AreaEntry *aare; +}; + +#define knLimitStruct 0 +#define knLimitMobileUnit 1 +#define knLimitScenery 2 +#define knLimitScorch 3 +#define knLimitSupport 4 + +class GobMgr // gobm +{ +public: + GobMgr() secGobMgr; + ~GobMgr() secGobMgr; + + bool Init(TCoord ctx, TCoord cty, int cpgobMax) secGobMgr; + void Reset() secGobMgr; + bool SaveState(Stream *pstm) secGobMgr; + bool LoadState(Stream *pstm) secGobMgr; + void AddGob(Gob *pgob, Gid gid = kgidNull) secGobMgr; + void RemoveGob(Gob *pgob) secGobMgr; + bool MoveGob(Gob *pgob, WCoord wxOld, WCoord wyOld, WCoord wxNew, WCoord wyNew) secGobMgr; + Gob *GetGob(Gid gid, bool fActiveOnly = true) secGobMgr; + UnitGob *GetUnitGob(TCoord tx, TCoord ty) secGobMgr; + void ShadowGob(Gob *pgob, TCoord tx, TCoord ty, int ctx, int cty) secGobMgr; + void UnshadowGob(Gob *pgob, TCoord tx, TCoord ty, int ctx, int cty) secGobMgr; + Gob *GetShadowGob(TCoord tx, TCoord ty) secGobMgr; + void GetAreaRect(int nArea, TRect *ptrc, Side side = ksideNeutral) secGobMgr; + bool LoadAreas(IniReader *pini) secGobMgr; + Gob *EnumGobsInArea(Enum *penm, int nArea, SideMask sidm, UnitMask um) secGobMgr; + void MoveGobBetweenAreas(Gid gid, AreaMask amOld, AreaMask amNew) secGobMgr; + AreaMask CalcAreaMask(TCoord tx, TCoord ty) secGobMgr; + AreaMask CalcAreaMask(TCoord tx, TCoord ty, int ctx, int cty) secGobMgr; + bool CheckUnitsInArea(int nArea, SideMask sidm, UnitMask um) secGobMgr; + bool IsGobWithinArea(Gob *pgobTarget, int nArea) secGobMgr; + +#ifdef DEBUG_HELPERS + char m_aszAreaNames[kcAreasMax][50]; + char *GetAreaName(int nArea) { + return m_aszAreaNames[nArea]; + } +#endif + + Gob *GetFirstGob() { + return m_pgobHead; + } + + // UNDONE: will making this static generate better code? + class Gob *GetNextGob(Gob *pgob) { + return pgob->m_pgobNext; + } + + // First gid at TCoord + + Gid GetFirstGid(TCoord tx, TCoord ty) { + Gid gid = m_pgidMap[ty * m_ctx + tx]; + if (gid & kwfGidEndMarker) + return kgidNull; + return gid; + } + + // Next gid in list + + Gid GetNextGid(Gid gid) { + Gob *pgob = GetGob(gid, false); + if (pgob == NULL) + return kgidNull; + return pgob->m_gidNext; + } + + void GetMapSize(TCoord *pctx, TCoord *pcty) { + *pctx = m_ctx; + *pcty = m_cty; + } + + int GetGobCount() { + return m_cpgobActive; + } + + int FindGobs(const Rect *prcBounds, Gob **apgob, int cpgobMax, byte *pbFogMap) secGobMgr; + bool IsStructurePlacementValid(StructConsts *pstruc, TCoord tx, TCoord ty, Player *pplr) secGobMgr; +#if defined(DEBUG) && defined(WIN) + Gob *FindEnemyWithinRange(UnitGob *punt, TCoord tcRange, bool fStructures = false) secGobMgr; +#endif + int FindGobs(const TRect *ptrcBounds, Gob **apgob, int cpgobMax) secGobMgr; + void TrackGobCounts(Gob *pgob, bool fIncrement) secGobMgr; + bool IsBelowLimit(int nLimit, Player *pplr = NULL) secGobMgr; + +private: + void AddGobToAreas(Gid gid, AreaMask am) secGobMgr; + void RemoveGobFromAreas(Gid gid, AreaMask am) secGobMgr; + int GetAreasFromMask(AreaMask am, int *pnArea) secGobMgr; + void FreeAreas() secGobMgr; + + Gob *m_pgobHead; + Gid *m_pgidMap; + int m_cpgobMax; + int m_cpgobActive; // UNDONE: _DEBUG only? + Gob **m_ppgobFreeHead; + Gob **m_ppgobFreeTail; + TCoord m_ctx, m_cty; + int m_car; + Area m_aar[kcAreasMax]; + int m_cSceneryGobs; + int m_cScorchGobs; + int m_cSupportGobs; + +#ifdef MP_DEBUG_SHAREDMEM +public: +#endif + Gob **m_apgobMaster; +}; +extern GobMgr ggobm; + +// GobStateMachineMgr + +class GobStateMachineMgr : public StateMachineMgr +{ +public: + virtual StateMachineId GetId(StateMachine *psm) { + return (StateMachineId)((Gob *)psm)->GetId(); + }; + virtual StateMachine *GetStateMachine(StateMachineId smid) { + return (StateMachine *)ggobm.GetGob((Gid)smid, false); + }; +}; +extern GobStateMachineMgr gsmm; + +// Gob derivatives + +class OvermindGob : public Gob // ovr +{ +public: + OvermindGob() secOvermindGob; + virtual ~OvermindGob() secOvermindGob; + static bool InitClass(IniReader *pini) secOvermindGob; + bool Init(const char *pszName = NULL) secOvermindGob; + void Toggle() secOvermindGob; + + // Gob methods + + virtual bool Init(IniReader *pini, FindProp *pfind, const char *pszName) secOvermindGob; + virtual bool IsSavable() secOvermindGob; + virtual GobType GetType() secOvermindGob; + virtual void Draw(DibBitmap *pbm, int xViewOrigin, int yViewOrigin, int nLayer) secOvermindGob; + + // StateMachine methods + + virtual int ProcessStateMachineMessage(State st, Message *pmsg) secOvermindGob; +#if defined(DEBUG_HELPERS) + virtual char *GetName() secOvermindGob; +#endif + +private: + bool m_fEnabled; + int m_cAttackCountdown; + int m_cBuildUnitCountdown; + bool m_fInitializationComplete; +}; + +class SurfaceDecalGob : public Gob // dcl +{ +public: + SurfaceDecalGob() secSurfaceDecalGob; + virtual ~SurfaceDecalGob() secSurfaceDecalGob; + static bool InitClass(IniReader *pini) secSurfaceDecalGob; + bool Init(WCoord wx, WCoord wy, dword ff, const char *pszBitmap, TBitmap *ptbm = NULL, const char *pszName = NULL) secSurfaceDecalGob; + + // Gob methods + + virtual bool Init(IniReader *pini, FindProp *pfind, const char *pszName) secSurfaceDecalGob; + virtual bool IsSavable() secSurfaceDecalGob; + virtual GobType GetType() secSurfaceDecalGob; + virtual void Draw(DibBitmap *pbm, int xViewOrigin, int yViewOrigin, int nLayer) secSurfaceDecalGob; + virtual void GetClippingBounds(Rect *prc) secSurfaceDecalGob; + +protected: + TBitmap *m_ptbm; +}; + +// ScorchGob different than SurfaceDecalGob so that m_nScorch can be persisted + +class ScorchGob : public SurfaceDecalGob +{ +public: + ScorchGob() secScorchGob; + bool Init(WCoord wx, WCoord wy, int nScorch) secScorchGob; + + // Scorch methods + + int GetSequence() secScorchGob; + + // Gob methods + + virtual GobType GetType() secScorchGob; + virtual bool LoadState(Stream *pstm) secScorchGob; + virtual bool SaveState(Stream *pstm) secScorchGob; + virtual bool IsSavable() secScorchGob; + +private: + int m_nScorch; + int m_nSequence; +}; + +class SceneryGob : public Gob // scn +{ +public: + SceneryGob() secSceneryGob; + virtual ~SceneryGob() secSceneryGob; + static bool InitClass(IniReader *pini) secSceneryGob; + bool Init(WCoord wx, WCoord wy, dword ff, const char *pszBitmap, const char *pszName = NULL) secSceneryGob; + + // Gob methods + + virtual bool Init(IniReader *pini, FindProp *pfind, const char *pszName) secSceneryGob; + virtual bool LoadState(Stream *pstm) secSceneryGob; + virtual bool SaveState(Stream *pstm) secSceneryGob; + virtual GobType GetType() secSceneryGob; + virtual dword GetSortKey() secSceneryGob; + virtual void Draw(DibBitmap *pbm, int xViewOrigin, int yViewOrigin, int nLayer) secSceneryGob; + virtual void GetClippingBounds(Rect *prc) secSceneryGob; + +private: + TBitmap *m_ptbm; +}; + +class AnimGob : public Gob // anm +{ +public: + AnimGob() secAnimGob; + virtual ~AnimGob() secAnimGob; + bool Init(WCoord wx, WCoord wy, word wfAnm, const char *pszAniName, AnimationData *panid = NULL, + int nStrip = 0, StateMachineId smidNotify = ksmidNull, const char *pszName = NULL) secAnimGob; + virtual bool OnStripDone() secAnimGob; + + // Gob methods + + virtual bool Init(IniReader *pini, FindProp *pfind, const char *pszName) secAnimGob; + virtual bool IsSavable() secAnimGob; + virtual GobType GetType() secAnimGob; + virtual void Draw(DibBitmap *pbm, int xViewOrigin, int yViewOrigin, int nLayer) secAnimGob; + virtual void GetClippingBounds(Rect *prc) secAnimGob; + + // StateMachine methods + + virtual int ProcessStateMachineMessage(State st, Message *pmsg) secAnimGob; +#if defined(DEBUG_HELPERS) + virtual char *GetName() secAnimGob; +#endif + +protected: + StateMachineId m_smidNotify; + word m_wfAnm; + Animation m_ani; +}; +AnimGob *CreateAnimGob(WCoord wx, WCoord wy, word wfAnm, const char *pszAniName, AnimationData *panid, + int nStrip = 0, StateMachineId smidNotify = ksmidNull, const char *pszName = NULL) secAnimGob; + +const word kfAnmDeleteWhenDone = 0x0001; +const word kfAnmLoop = 0x0002; +const word kfAnmSmokeFireLayer = 0x0004; +const word kfAnmSurfaceDecalLayer = 0x0008; + +const int kcbStructUnitName = 50; + +struct GobConsts { // gobc + GobType gt; +}; + +#define GetUnitCostMin() (gfMultiplayer ? gnUnitCostMPMin : gnUnitCostMin) +#define GetUnitCostMax() (gfMultiplayer ? gnUnitCostMPMax : gnUnitCostMax) + +#define kfUntcNotifyEnemyNearby 0x0001 // wants to be notified of nearby mobile enemies +#define kfUntcNotifyPowerLowHigh 0x0002 // bld is power sensitive, needs notifications +#define kfUntcLargeDefog 0x0004 // this unit defogs a larger area +#define kfUntcMobileUnitBuilder 0x0008 // builds mobile units +#define kfUntcStructureBuilder 0x0010 // builds structures +#define kfUntcHasFullnessIndicator 0x0020 // draws fullness indicator + +struct UnitConsts : public GobConsts { // untc + UnitType ut; + UnitMask um; + UnitMask umPrerequisites; + UpgradeMask upgmPrerequisites; + Sfx sfxImpact; + int nCost; + int nCostMP; + fix fxArmorStrength; + fix fxArmorStrengthMP; + int nInfantryDamage; + int nInfantryDamageMP; + int nVehicleDamage; + int nVehicleDamageMP; + int nStructureDamage; + int nStructureDamageMP; + int ctFiringRate; + int tcFiringRange; // kept as int instead of TCoord to accommodate Ini::GetPropertyValue + int cupdTimeToBuild; + int cupdTimeToBuildMP; + char szAniName[kcbFilename]; + AnimationData *panid; + WRect wrcUIBounds; + WRect wrcUIBoundsFinger; + int wdySortOffset; + char szName[kcbStructUnitName]; + char szLongName[kcbStructUnitName]; + UnitMenu *pfrmMenu; + word wf; + char *szDescription; + + int GetCost() { + return gfMultiplayer ? nCostMP : nCost; + } + fix GetArmorStrength() { + return gfMultiplayer ? fxArmorStrengthMP : fxArmorStrength; + } + int GetTimeToBuild() { + return gfMultiplayer ? cupdTimeToBuildMP : cupdTimeToBuild; + } + int GetInfantryDamage() { + return gfMultiplayer ? nInfantryDamageMP : nInfantryDamage; + } + int GetVehicleDamage() { + return gfMultiplayer ? nVehicleDamageMP : nVehicleDamage; + } + int GetStructureDamage() { + return gfMultiplayer ? nStructureDamageMP : nStructureDamage; + } +}; + +struct StructConsts : public UnitConsts { // struc + int ctx, cty; // kept as int instead of TCoord to accommodate Ini::GetPropertyValue + int ctxReserve, ctyReserve; // kept as int instead of TCoord to accommodate Ini::GetPropertyValue + int nPowerDemand; + int nPowerSupply; + int nUpgradeCost; + Sfx sfxAbortRepair; + Sfx sfxRepair; + Sfx sfxDamaged; + Sfx sfxDestroyed; + Sfx sfxSelect; + Sfx sfxUpgrade; + Sfx sfxAbortUpgrade; +}; + +struct BuilderConsts : public StructConsts { // bldc + int nBuildRate; + Sfx sfxUnitBuild; + Sfx sfxUnitBuildAbort; + Sfx sfxUnitReady; + UnitMask umCanBuild; +}; + +struct TowerConsts : public StructConsts { // twrc + Sfx sfxAttack; + Sfx sfxFire; + int *anFiringStripIndices; +}; + +struct MobileUnitConsts : public UnitConsts { // muntc + Sfx sfxFire; + SfxCategory sfxcDestroyed; + SfxCategory sfxcSelect; + SfxCategory sfxcMove; + SfxCategory sfxcAttack; + WCoord wcMoveDistPerUpdate; + WCoord wcMoveDistPerUpdateMP; + int *anFiringStripIndices; + int *anMovingStripIndices; + int *anIdleStripIndices; + + WCoord GetMoveDistPerUpdate() { + return gfMultiplayer ? wcMoveDistPerUpdateMP : wcMoveDistPerUpdate; + } +}; + +class MobileUnitBuildForm; +struct MobileUnitBuilderConsts : public BuilderConsts { // mubc + word fUpgrade; + word fUpgradeInProgress; + MobileUnitBuildForm *pfrmBuild; +}; + +struct SpConsts : public MobileUnitConsts { // spc + Sfx sfxStructureCaptured; +}; + +struct MinerConsts : public MobileUnitConsts { // mnrc + Sfx sfxMine; + Sfx sfxUnderAttack; +}; + +const int kcmsDamageIndicatorTimeout = 4000; // 4.0 secs TUNE: + +#define kcEnemyNearbyRemember 5 // # of enemies to remember + +#define kcyHealthBar 4 // Height of health bar above selection + +const WCoord kwcFullnessPip = WcFromTile16ths(2); + +#define kcyFullnessBar (PcFromWc(kwcFullnessPip) + 2) // Height of fullness bar below miner, processor, warehouse + +class UnitGob : public Gob // unt +{ +public: + UnitGob(UnitConsts *puntc) secUnitGob; + ~UnitGob() secUnitGob; + + static bool InitClass(UnitConsts *psubc, IniReader *pini) secUnitGob; + static void InitFingerUIBounds(UnitConsts *psubc); + static void ExitClass(UnitConsts *psubc) secUnitGob; + static bool LoadMenu(UnitConsts *psubc, IniReader *pini, char *pszTemplate, int idfDefault) secUnitGob; + virtual bool Init(WCoord wx, WCoord wy, Player *pplr, fix fxHealth, dword ff, const char *pszName) secUnitGob; + virtual void Delete() secUnitGob; + virtual void DefUpdate() secUnitGob; + virtual void Activate() secUnitGob; + virtual void Deactivate() secUnitGob; + virtual bool IsIdle() secUnitGob; + virtual bool IsValidTarget(Gob *pgobTarget) secUnitGob; + virtual void SetTarget(Gid gid, WCoord wxTarget = 0, WCoord wyTarget = 0, WCoord wxCenter = 0, WCoord wyCenter = 0, TCoord tcRadius = 0, WCoord wcMoveDistPerUpdate = 0) secUnitGob; + virtual void GetAttackPoint(WPoint *pwpt) secUnitGob; + virtual dword GetAnimationHash() secUnitGob; + virtual void GetAnimationBounds(Rect *prc, bool fBase) secUnitGob; + virtual void DrawAnimation(DibBitmap *pbm, int x, int y) secUnitGob; + virtual AnimSprite *CreateHilightSprite(); secUnitGob; +#ifdef MP_DEBUG_SHAREDMEM + virtual void MPValidate() secUnitGob; +#endif + void NotifyNearbyAlliesOfHit(Gid gidAssailant) secUnitGob; + void ShowDamageIndicator() secUnitGob; + void ClearDamageIndicator() secUnitGob; + int GetDamageTo(UnitGob *puntTarget) secUnitGob; + void RecalcEnemyNearby(bool fClearOnly) secUnitGob; + + UnitConsts *GetConsts() { + return m_puntc; + } + + UnitType GetUnitType() { + return m_puntc->ut; + } + + word GetUnitFlags() { + return m_wfUnit; + } + + void SetUnitFlags(word wf) { + m_wfUnit = wf; + } + + bool IsAlly(Side side) secUnitGob; + + fix GetHealth() { + return m_fxHealth; + } + + AnimSprite *GetAnimSprite() { + return m_panispr; + } + + void Select(bool fSelect) secUnitGob; + void Hilight(bool fHilight) secUnitGob; + + virtual void SetHealth(fix fxHealth) secUnitGob; + + // Gob overrides + + virtual bool Init(IniReader *pini, FindProp *pfind, const char *pszName) secUnitGob; + virtual bool LoadState(Stream *pstm) secUnitGob; + virtual bool SaveState(Stream *pstm) secUnitGob; + virtual GobType GetType() secUnitGob; + virtual void Draw(DibBitmap *pbm, int xViewOrigin, int yViewOrigin, int nLayer) secUnitGob; + virtual dword GetSortKey(); + virtual void GetClippingBounds(Rect *prc) secUnitGob; + virtual void GetUIBounds(WRect *pwrc) secUnitGob; + virtual void GetCenter(WPoint *pwpt) secUnitGob; + virtual void PopupMenu() secUnitGob; + +protected: + void RememberEnemyNearby(Gid gidEnemy) secUnitGob; + TCoord CalcRange(TCoord tx, TCoord ty, Gob *pgob) secUnitGob; + virtual UnitGob *FindEnemyNearby(TCoord tc) secUnitGob; + void NotifyEnemyNearby() secUnitGob; + + union { + UnitConsts *m_puntc; + StructConsts *m_pstruc; + TowerConsts *m_ptwrc; + SpConsts *m_pspc; + MinerConsts *m_pmnrc; + MobileUnitConsts *m_pmuntc; + BuilderConsts *m_pbldrc; + MobileUnitBuilderConsts *m_pmubc; + }; + Gid m_agidEnemyNearby[kcEnemyNearbyRemember]; + fix m_fxHealth; + Animation m_ani; + AnimSprite *m_panispr; + word m_wfUnit; + long m_cupdLastHitNotify; + short m_cDamageCountdown; +}; + +const word kfUnitInvulnerable = 0x0001; +const word kfUnitDrawHealthIndicator = 0x0002; +const word kfUnitDrawRepairingSymbol = 0x0004; +const word kfUnitRepairing = 0x0008; +const word kfUnitDrawNeedsPowerSymbol = 0x0010; +const word kfUnitDrawNeedCreditsSymbol = 0x0020; +const word kfUnitNeedCredits = 0x0040; +const word kfUnitHilighted = 0x0080; + +class StructGob : public UnitGob // stru +{ +public: + StructGob(StructConsts *pstruc) secStructures; + virtual ~StructGob() secStructures; + static bool InitClass(StructConsts *pstruc, IniReader *pini) secStructures; + static void ExitClass(StructConsts *pstruc) secStructures; + + void Repair(bool fOn) secStructures; + bool NeedsRepair() secStructures; + void EnableOrDisableSymbolLayer() secStructures; + virtual bool IsTakeoverable(Player *pplr) secStructures; + virtual void Takeover(Player *pplr) secStructures; + + static bool LoadConfirmation() secStructures; + bool PopupConfirmation(char *pszTitle) secStructures; + void TakedownConfirmation() secStructures; + void SelfDestruct(bool fRecycleValue = true) secStructures; + bool IsAccessible(TCoord tx, TCoord ty) secStructures; + + // UnitGob overrides + + virtual bool Init(WCoord wx, WCoord wy, Player *pplr, fix fxHealth, dword ff, const char *pszName) secStructures; + virtual void Delete() secStructures; + virtual void Draw(DibBitmap *pbm, int xViewOrigin, int yViewOrigin, int nLayer) secStructures; + virtual void DefUpdate() secStructures; + virtual void Activate() secStructures; + virtual void Deactivate() secStructures; + virtual bool IsValidTarget(Gob *pgobTarget) secStructures; + virtual void SetHealth(fix fxHealth) secStructures; + virtual void GetAttackPoint(WPoint *pwpt) secStructures; + + // Gob overrides + + virtual bool LoadState(Stream *pstm) secStructures; + virtual bool SaveState(Stream *pstm) secStructures; + virtual void GetTileRect(TRect *ptrc) secStructures; + virtual void GetTilePaddedWRect(WRect *pwrc) secStructures; + virtual void InitMenu(Form *pfrm) secStructures; + virtual void OnMenuItemSelected(int id) secStructures; + + // StateMachine methods + + virtual int ProcessStateMachineMessage(State st, Message *pmsg) secStructures; + + // Used to track visibility in FindGobs + + word m_nSeqLastVisible; + +protected: + void OnPowerLowHigh() secStructures; + void DrawSymbol(TBitmap *ptbm, DibBitmap *pbm, int xViewOrigin, int yViewOrigin) secStructures; + virtual void WaitingForCredits(bool fNeed, bool fOverride = false, Player *pplr = NULL) secStructures; + +private: + ScorchGob *GetScorchGob() secStructures; + + long m_tLastSmoke; + + static TBitmap *s_ptbmRepairing; + static TBitmap *s_ptbmNeedsPower; + static TBitmap *s_ptbmNeedCredits; +}; + +// TUNE: +const fix kfxHealthUnitsRepairedPerUpdate = itofx(1); +const int knCreditsPerRepairUpdate = 1; +const long kcupdSymbolFlashRate = 8; // ideally a power of 2 + +#define GetReplicationCost(nCost) ((nCost) / 3) + +class UnitMenu : public Form, public Timer +{ +public: + void SetOwner(UnitGob *punt, bool fPerUnitInit = true) secUnitGob; + UnitGob *GetOwner() secUnitGob; + + // Form overrides + + virtual bool DoModal(int *pnResult = NULL, Sfx sfxShow = ksfxGuiFormShow, Sfx sfxHide = ksfxGuiFormHide) secUnitGob; + virtual void OnPaintBackground(DibBitmap *pbm, UpdateMap *pupd) secUnitGob; + virtual void OnPaint(DibBitmap *pbm) secUnitGob; + virtual bool OnHitTest(Event *pevt) secUnitGob; + + // Timer overrides + + virtual void OnTimer(long tCurrent) secUnitGob; + +protected: + UnitGob *m_punt; + Rect m_rcButtons; +}; + +enum MobileUnitAction // mua +{ + kmuaNone, + kmuaGuard, + kmuaGuardVicinity, + kmuaGuardArea, + kmuaMove, + kmuaAttack, + kmuaHuntEnemies, +}; + +struct MoveDirections // movd +{ + MobileUnitGob *pmunt; + byte idir; + byte cdir; + Direction adir[8]; +}; + +const word kfMuntChaseEnemies = 0x0001; // Will chase enemies when attacking them +const word kfMuntReturnFire = 0x0002; // Will attack anything that hits it +const word kfMuntAttackEnemiesWhenMoving = 0x0004; // Will attack enemies in range when moving +const word kfMuntAttackEnemiesWhenGuarding = 0x0008; // Will attack enemies in range when guarding +const word kfMuntRunAwayWhenHit = 0x0010; // Will run away when hit +const word kfMuntMoveWait = 0x0020; // Is waiting for a tile to free up +const word kfMuntPathPending = 0x0040; // Will be calcing a path soon +const word kfMuntCommandPending = 0x0080; // Has a command pending +const word kfMuntFiring = 0x0100; // Is shooting at something +const word kfMuntMoveWaitingNearby = 0x0200; // Gob nearby is "move waiting" on this gob +const word kfMuntDestinationReserved = 0x0400; // Dest tile reserved +const word kfMuntStuck = 0x0800; // Stuck bit for debugging +const word kfMuntAtReplicatorInput = 0x1000; // At replicator input spot +const word kfMuntStayPut = 0x2000; // Don't move to attack enemies + +// All the flags that control a MobileUnit's aggressiveness + +const word kfMuntAggressivenessBits = kfMuntChaseEnemies | kfMuntReturnFire | + kfMuntAttackEnemiesWhenMoving | kfMuntAttackEnemiesWhenGuarding | + kfMuntRunAwayWhenHit | kfMuntStayPut; + +// Most MobileUnits move (follow paths) and like to shoot + +extern bool gfGodMode; +#define kcPathsCache 5 + +class MobileUnitGob : public UnitGob // munt +{ +public: + MobileUnitGob(MobileUnitConsts *pmuntc) secUnitGob; + virtual ~MobileUnitGob() secUnitGob; + // NOTE: derivatives should initialize the an*StripIndices arrays of the MobileUnitConsts + static bool InitClass(MobileUnitConsts *pmuntc, IniReader *pini) secUnitGob; + static void ExitClass(MobileUnitConsts *pmuntc) secUnitGob; + static MobileUnitGob *AnyTransitionsIntoTile(TCoord tx, TCoord ty, Gob *pgobIgnore) secUnitGob; + static void FreeCachedPaths() secUnitGob; + bool IsReadyForCommand() secUnitGob; + bool IsMobile() secUnitGob; + bool IsStandingOnActivator() secUnitGob; + + bool HasAttackTarget() { + return m_gidTarget != kgidNull; + } + + word GetMobileUnitFlags() { + return m_wfMunt; + } + + void SetMobileUnitFlags(word wf) { + m_wfMunt = wf; + } + + void ClearAction() { + m_mua = kmuaNone; + } + + bool IsHumanOrGodControlled() { + return !(m_pplr->GetFlags() & kfPlrComputer) || gfGodMode; + } + + bool IsMoveWaiting() { + return (m_wfMunt & kfMuntMoveWait) != 0; + } +#ifdef MP_DEBUG_SHAREDMEM + virtual void MPValidate() secUnitGob; +#endif + + // UnitGob overrides + + virtual bool Init(IniReader *pini, FindProp *pfind, const char *pszName) secUnitGob; + virtual bool Init(WCoord wx, WCoord wy, Player *pplr, fix fxHealth, dword ff, const char *pszName) secUnitGob; +#ifdef DRAW_PATHS + void DrawPath(DibBitmap *pbm, WCoord wxViewOrigin, WCoord wyViewOrigin) secUnitGob; +#endif +#ifdef DRAW_LINES + void DrawTargetLine(DibBitmap *pbm, int xViewOrigin, int yViewOrigin) secUnitGob; +#endif + virtual bool IsIdle() secUnitGob; + virtual void SetTarget(Gid gid, WCoord wxTarget = 0, WCoord wyTarget = 0, WCoord wxCenter = 0, WCoord wyCenter = 0, TCoord tcRadius = 0, WCoord wcMoveDistPerUpdate = 0) secUnitGob; + virtual void Activate() secUnitGob; + virtual void Deactivate() secUnitGob; + virtual void GetAttackPoint(WPoint *pwpt) secUnitGob; + + // Gob overrides + + virtual bool LoadState(Stream *pstm) secUnitGob; + virtual bool SaveState(Stream *pstm) secUnitGob; + virtual void SetPosition(WCoord wx, WCoord wy) secUnitGob; + + // StateMachine methods + + virtual int ProcessStateMachineMessage(State st, Message *pmsg) secUnitGob; + +protected: + void MoveEnter(bool fReset = false) secUnitGob; + void MoveExit() secUnitGob; + int MoveUpdate() secUnitGob; + + virtual bool Fire(UnitGob *puntTarget, WCoord wx, WCoord wy, WCoord wdx, WCoord wdy) secUnitGob; + virtual void Idle() secUnitGob; + virtual void PerformAction(char *szAction) secUnitGob; + bool InTransition() secUnitGob; + +private: + bool IsAttackPointWithinFiringRangeOfTarget(UnitGob *puntTarget) secUnitGob; + bool PendOrProcessCommand(Message *pmsg, State stNew) secUnitGob; + bool PendOrProcessAction(Message *pmsg, State stNew, MobileUnitAction mua) secUnitGob; + bool IsTargetInRange() secUnitGob; + void SetStatePendingFireComplete(State st) secUnitGob; + void ContinueActionPendingFireComplete() secUnitGob; + void ReserveTile(TCoord tx, TCoord ty, bool fReserve) secUnitGob; + bool IsTileReserved(TCoord tx, TCoord ty) secUnitGob; + byte GetTileFlags(TCoord tx, TCoord ty) secUnitGob; + MobileUnitGob *GetReservingUnit(TCoord tx, TCoord ty) secUnitGob; + int GetNextLocations(TPoint *atpt) secUnitGob; + int GetNextLocations2(Path *ppath, int *pitptStart, int cStepsFurtherStop, TPoint *atpt) secUnitGob; + int FindClosestPoint(TPoint *ptptClosest, int ctptFurtherStop) secUnitGob; + bool PrepPath(WCoord wxDst, WCoord wyDst) secUnitGob; + Path *FindPath(TCoord txFrom, TCoord tyFrom, TCoord txTo, TCoord tyTo) secUnitGob; + bool PrepareNextTransition(TPoint *ptptNext, int *pnMoveResult) secUnitGob; + void EnterTransition(TPoint *ptptNext) secUnitGob; + void ContinueTransition() secUnitGob; + bool HandleCollision(MobileUnitGob **apmunt, byte *abf, TPoint *atpt, int ctpt) secUnitGob; + bool PlanMoveAsidePath(MobileUnitGob *pmunt, TCoord tx, TCoord ty) secUnitGob; + bool GetMoveAsideDirections(Direction dirMover, MoveDirections *pmovd) secUnitGob; + bool AcceptMoveAsideRequest(Direction dir) secUnitGob; + MobileUnitGob *GetMobileUnitAt(TCoord tx, TCoord ty) secUnitGob; + bool CheckDestinationReached() secUnitGob; + bool IsMoveWaitCycle(MobileUnitGob *pmunt) secUnitGob; + bool MoveWaitForUnit(MobileUnitGob *pmunt, TCoord tx, TCoord ty) secUnitGob; + bool ContinueMoveWaiting(MobileUnitGob **apmunt, byte *abf, TPoint *atpt, int ctpt, TPoint *ptptNext) secUnitGob; + void NotifyMoveWaitingNearby(WCoord wx, WCoord wy) secUnitGob; + bool FindLocalAvoidPath(TCoord tx, TCoord ty) secUnitGob; + int FindCloserTrackPoint(TrackPoint *ptrkpStart, Direction dirBlocked, bool fClockwise, int ctpt, TPoint *atpt, TrackPoint *ptrkp) secUnitGob; + bool CheckReplicatorPoint() secUnitGob; + UnitGob *FindValidTargetInArea(int nArea) secUnitGob; + +protected: + Direction m_dir, m_dirNext; + Gid m_gidTarget; + long m_tLastFire; + Message m_msgPending; + Message m_msgAction; + int m_cCountdown; // multi-purpose countdown timer + int m_cMoveStepsRemaining; + TPoint m_tptChaseInitial; + MobileUnitAction m_mua, m_muaPending; + word m_wfMunt; + State m_stPending; + long m_cupdLastHitOrNearbyAllyHit; + +#ifdef DRAW_PATHS +public: + WCoord m_wxDst, m_wyDst; +protected: +#endif + +#ifdef DEBUG_HELPERS +public: +#endif + TCoord m_txDst, m_tyDst; +#ifdef DEBUG_HELPERS +protected: +#endif + +#ifdef DRAW_LINES +public: +#endif + WPoint m_wptTarget; + +#ifdef DRAW_PATHS +public: +#endif + word m_nSeqMoveAside; + Path *m_ppathUnit; + Path *m_ppathAvoid; + int m_itptPath; + WPoint m_wptTargetCenter; + TCoord m_tcTargetRadius; + WCoord m_wcMoveDistPerUpdate; + + static Path *s_apathCached[kcPathsCache]; + static int s_cpathCached; + +#ifdef DEBUG_HELPERS + friend void paint(); + friend class UnitGroupViewer; +#endif +}; + +// Values returned by UnitGob::MoveUpdate() + +const int knMoveMoving = 0; +const int knMoveTargetReached = 1; +const int knMoveStuck = 2; +const int knMoveWaiting = 3; + +const int kxInvalid = -1000; +const WCoord kwxInvalid = -1000; +const TCoord ktxInvalid = -100; +const word kcBuildQueueMax = 10; + +class BuildQueue +{ +public: + BuildQueue() secBuildQueue; + BuildQueue &operator=( BuildQueue &bqRHS) secBuildQueue; + void SetSize(word cutMax) secBuildQueue; + int GetRemainingCapacity() secBuildQueue; + void Enqueue(UnitType ut) secBuildQueue; + void Dequeue() secBuildQueue; + bool RemoveUnit(UnitType ut) secBuildQueue; + UnitType Peek() secBuildQueue; + bool IsEmpty() secBuildQueue; + bool IsFull() secBuildQueue; + void Clear() secBuildQueue; + int GetUnitCount(UnitType ut) secBuildQueue; + + // called by builder gob + virtual bool LoadState(Stream *pstm) secBuildQueue; + virtual bool SaveState(Stream *pstm) secBuildQueue; + +private: + char m_achBuildQueue[kcBuildQueueMax]; + word m_cchQueueMax; +}; + +class BuilderGob : public StructGob // bldr +{ +public: + BuilderGob(BuilderConsts *pbldrc) secBuilderGob; + virtual ~BuilderGob() secBuilderGob; + static bool InitClass(BuilderConsts *pbldrc, IniReader *pini) secBuilderGob; + static void ExitClass(BuilderConsts *pbldrc) secBuilderGob; + void DrawUpgradeEffect(DibBitmap *pbm, int xViewOrigin, int yViewOrigin) secBuilderGob; + void AbortBuild(bool fRefundCreditsSpent = false, UnitType ut = kutNone) secBuilderGob; + static int GetGlobalQueuedCount(Player *pplr, word wfTest) secBuilderGob; + static int GetQueuedCount(Player *pplr, word wfTest) secBuilderGob; + + void SetRallyPoint(TCoord tx, TCoord ty) { + m_tptRally.tx = tx; + m_tptRally.ty = ty; + } + + // in builder gob so it will be tracked per-instance + + void SetLastSelection(short iSel) { m_iLastSelection = iSel;} + short GetLastSelection() {return m_iLastSelection;} + + // Build management methods + + virtual void FindInitPosition(WPoint *pwpt) secBuilderGob; + virtual void Build(UnitType ut, Gid gid = kgidNull) secBuilderGob; + bool IsBuildInProgress() { + return !m_bq.IsEmpty(); + } + UnitType UnitBuildInProgress() {return m_bq.Peek();} + void DrawBuildProgress(DibBitmap *pbm, Rect *prc) secBuilderGob; + void SyncBuildQueue(BuildQueue *pbq) secBuilderGob; + + UnitGob *GetBuiltGob() secBuilderGob; + void ClearBuiltGob() secBuilderGob; + + // UnitGob overrides + + virtual bool Init(WCoord wx, WCoord wy, Player *pplr, fix fxHealth, dword ff, const char *pszName) secBuilderGob; + virtual void Draw(DibBitmap *pbm, int xViewOrigin, int yViewOrigin, int nLayer) secBuilderGob; + virtual void DefUpdate() secBuilderGob; + virtual void Deactivate() secBuilderGob; + + // Gob overrides + + virtual bool LoadState(Stream *pstm) secBuilderGob; + virtual bool SaveState(Stream *pstm) secBuilderGob; + virtual void GetClippingBounds(Rect *prc); + +protected: + bool ShowingBuildProgress(); + + short m_iLastSelection; + Gid m_gidBuildVisible; + fix m_fxHealthBuilding; + int m_nCreditsSpentOnBuilding; + int m_nCostRemainder; + TPoint m_tptRally; + +#ifdef MP_STRESS +public: +#endif + BuildQueue m_bq; +}; + +class MobileUnitBuildForm : public Form +{ +public: + BuilderGob *GetOwner() { + return m_pbldr; + } + + void SetOwner(BuilderGob *pbldr) secStructures; + void UpdateUnitInfo(ListItem *pli) secStructures; + void OnUnitCompleted(BuilderGob *pbldr, UnitType ut) secStructures; + void DefUpdate(BuilderGob *pbldr, bool fBuildInProgress) secStructures; + int CountQueue(UnitType ut) secStructures; + void UpdateOrderButton(bool fCalcLimit) secStructures; + + // Form overrides + + void EndForm(int nResult = 0) secStructures; + virtual void OnControlSelected(word idc) secStructures; + virtual void OnControlNotify(word idc, int nNotify); + virtual void OnPaintBackground(DibBitmap *pbm, UpdateMap *pupd) secStructures; + +private: + long m_cupdLast; + bool m_fLimitReached; + bool m_fOrderValid; + BuilderGob *m_pbldr; + BuildQueue m_bqPrivate; + + static int s_iliCurrent; +}; + +class TankGob : public MobileUnitGob // tnk +{ +public: + TankGob(MobileUnitConsts *pmuntc) secTankGob; + static bool InitClass(MobileUnitConsts *pmuntc, IniReader *pini) secTankGob; + static void ExitClass(MobileUnitConsts *pmuntc) secTankGob; + virtual void LaunchProjectile(WCoord wx, WCoord wy, WCoord wxTarget, WCoord wyTarget, int nDamage, Gid gidOwner, Gid gidTarget) secTankGob; + + // UnitGob overrides + + virtual bool Init(WCoord wx, WCoord wy, Player *pplr, fix fxHealth, dword ff, const char *pszName) secTankGob; + virtual void DefUpdate() secTankGob; + + // UnitGob overrides + + virtual void Draw(DibBitmap *pbm, int xViewOrigin, int yViewOrigin, int nLayer) secTankGob; + virtual bool Fire(UnitGob *puntTarget, WCoord wx, WCoord wy, WCoord wdx, WCoord wdy) secTankGob; + virtual void Idle() secTankGob; + virtual dword GetAnimationHash() secTankGob; + virtual void GetAnimationBounds(Rect *prc, bool fBase) secTankGob; + virtual void DrawAnimation(DibBitmap *pbm, int x, int y) secTankGob; + + // Gob overrides + + virtual void GetClippingBounds(Rect *prc) secTankGob; + virtual bool LoadState(Stream *pstm) secTankGob; + virtual bool SaveState(Stream *pstm) secTankGob; + +protected: + Animation m_aniTurret; + + // Note: If this is a char, iphone/llvm will encounter an alignment bug + // between this member and MTankGob::m_pgobSecondShot. Direction16 + // has been changed to an int for this reason. + + Direction16 m_dir16Turret; +}; + +class SRInfantryGob : public MobileUnitGob // sri +{ +public: + SRInfantryGob() secInfantryGob; + static bool InitClass(IniReader *pini) secInfantryGob; + static void ExitClass() secInfantryGob; + + // UnitGob overrides + + virtual bool Fire(UnitGob *puntTarget, WCoord wx, WCoord wy, WCoord wdx, WCoord wdy) secInfantryGob; + virtual void Idle() secInfantryGob; + + // StateMachine methods + + virtual int ProcessStateMachineMessage(State st, Message *pmsg) secInfantryGob; +#if defined(DEBUG_HELPERS) + virtual char *GetName() secInfantryGob; +#endif +}; + +class LRInfantryGob : public MobileUnitGob // lri +{ +public: + LRInfantryGob() secInfantryGob; + static bool InitClass(IniReader *pini) secInfantryGob; + static void ExitClass() secInfantryGob; + + // UnitGob overrides + + virtual bool Fire(UnitGob *puntTarget, WCoord wx, WCoord wy, WCoord wdx, WCoord wdy) secInfantryGob; + virtual void Idle() secInfantryGob; + + // StateMachine methods + + virtual int ProcessStateMachineMessage(State st, Message *pmsg) secInfantryGob; +#if defined(DEBUG_HELPERS) + virtual char *GetName() secInfantryGob; +#endif +}; + +class SpInfantryGob : public MobileUnitGob // lri +{ +public: + SpInfantryGob() secInfantryGob; + static bool InitClass(IniReader *pini) secInfantryGob; + static void ExitClass() secInfantryGob; + + // UnitGob overrides + + virtual bool IsValidTarget(Gob *pgobTarget) secInfantryGob; + virtual bool Fire(UnitGob *puntTarget, WCoord wx, WCoord wy, WCoord wdx, WCoord wdy) secInfantryGob; + virtual void Idle() secInfantryGob; + + // StateMachine methods + + virtual int ProcessStateMachineMessage(State st, Message *pmsg) secInfantryGob; +#if defined(DEBUG_HELPERS) + virtual char *GetName() secInfantryGob; +#endif +}; + +class LTankGob : public TankGob // ltnk +{ +public: + LTankGob() secTankGob; + static bool InitClass(IniReader *pini) secTankGob; + static void ExitClass() secTankGob; +#if defined(DEBUG_HELPERS) + virtual char *GetName() secGob; +#endif +}; + +class TankShotGob; // it's down there + +class MTankGob : public TankGob // mtnk +{ +public: + MTankGob() secTankGob; + ~MTankGob() secTankGob; + static bool InitClass(IniReader *pini) secTankGob; + static void ExitClass() secTankGob; + void LaunchProjectile(WCoord wx, WCoord wy, WCoord wxTarget, WCoord wyTarget, int nDamage, Gid gidOwner, Gid gidTarget) secTankGob; +#if defined(DEBUG_HELPERS) + virtual char *GetName() secGob; +#endif + + // UnitGob overrides + + virtual void DefUpdate() secTankGob; + virtual void Deactivate() secTankGob; + +private: + TankShotGob *m_pgobSecondShot; + short m_cShotcountdown; +}; + +class RTankGob : public TankGob // rtnk +{ +public: + RTankGob() secTankGob; + static bool InitClass(IniReader *pini) secTankGob; + static void ExitClass() secTankGob; + void LaunchProjectile(WCoord wx, WCoord wy, WCoord wxTarget, WCoord wyTarget, int nDamage, Gid gidOwner, Gid gidTarget) secTankGob; + +#if defined(DEBUG_HELPERS) + virtual char *GetName() secGob; +#endif +}; + +class GTankGob : public TankGob // gtnk +{ +public: + GTankGob() secTankGob; + static bool InitClass(IniReader *pini) secTankGob; + static void ExitClass() secTankGob; + + // MobileUnitGob overrides + + virtual int ProcessStateMachineMessage(State st, Message *pmsg) secTankGob; + + // TankGob overrides + + virtual bool Fire(UnitGob *puntTarget, WCoord wx, WCoord wy, WCoord wdx, WCoord wdy) secTankGob; + +#if defined(DEBUG_HELPERS) + virtual char *GetName() secGob; +#endif +}; + +class MinerGob : public MobileUnitGob // mnr +{ +public: + MinerGob() secMiner; + static bool InitClass(IniReader *pini) secMiner; + static void ExitClass() secMiner; + + int GetGalaxiteAmount() secMiner; + void SetGalaxiteAmount(int nAmount) secMiner; + void SetFavoriteProcessor(Gid gid) secMiner; + void Hide(bool fHide) secMiner; + void SendDeliverCommand(Gid gidProcessor) secMiner; + + bool IsAttemptingToDeliver(Gid gid) { + if (m_fAttemptingToDeliver && gid == m_gidFavoriteProcessor) + return true; + return false; + } + + // Gob overrides + + virtual void InitMenu(Form *pfrm) secMiner; + virtual void OnMenuItemSelected(int id) secMiner; + virtual bool LoadState(Stream *pstm) secMiner; + virtual bool SaveState(Stream *pstm) secMiner; + virtual void GetClippingBounds(Rect *prc) secMiner; + + // UnitGob overrides + + virtual bool IsValidTarget(Gob *pgobTarget) secMiner; + virtual void Draw(DibBitmap *pbm, int xViewOrigin, int yViewOrigin, int nLayer) secMiner; + virtual void Deactivate() secMiner; + virtual void SetTarget(Gid gid, WCoord wxTarget = 0, WCoord wyTarget = 0, WCoord wxCenter = 0, WCoord wyCenter = 0, TCoord tcRadius = 0, WCoord wcMoveDistPerUpdate = 0) secMiner; + +#ifdef MP_DEBUG_SHAREDMEM + virtual void MPValidate() secMiner; +#endif + + // MobileUnitGob overrides + + void PerformAction(char *szAction) secMiner; + + // StateMachine methods + + virtual int ProcessStateMachineMessage(State st, Message *pmsg) secMiner; +#if defined(DEBUG_HELPERS) + virtual char *GetName() secGob; +#endif + +private: + void Mine(WCoord wx, WCoord wy) secMiner; + Gid FindClosestProcessor() secMiner; + +private: + Animation m_aniVacuum; + int m_nGalaxiteAmount; + char m_cDelay; + Gid m_gidFavoriteProcessor; + TPoint m_tptGalaxite; + bool m_fMinerUnderAttack; + static AnimationData *s_panidVacuum; + bool m_fAttemptingToDeliver; +#ifdef MP_STRESS +public: +#endif + bool m_fHidden; +}; + +inline void MinerGob::SetGalaxiteAmount(int nAmount) +{ + m_nGalaxiteAmount = nAmount; +} + +inline int MinerGob::GetGalaxiteAmount() +{ + return m_nGalaxiteAmount; +} + +inline void MinerGob::SetFavoriteProcessor(Gid gid) +{ + m_gidFavoriteProcessor = gid; +} + +// TUNE: + +const int knMinerGalaxiteMax = 100; // two full patches of Galaxite +const int knGalaxiteValue = 10; +const int kcMinerSuckDelay = 7; // number of updates between Galaxite decrements + +class MobileHqGob : public MobileUnitGob // mhq +{ +public: + MobileHqGob() secGob; + static bool InitClass(IniReader *pini) secGob; + static void ExitClass() secGob; + bool CanTransform(TPoint *ptp = NULL) secGob; + + // Gob overrides + + virtual void InitMenu(Form *pfrm) secGob; + virtual void OnMenuItemSelected(int id) secGob; + + // StateMachine methods + + virtual int ProcessStateMachineMessage(State st, Message *pmsg) secGob; +#if defined(DEBUG_HELPERS) + virtual char *GetName() secGob; +#endif +}; + +class MobileUnitBuilderGob : public BuilderGob // mub +{ +public: + MobileUnitBuilderGob(MobileUnitBuilderConsts *pmubc) secStructures; + + // Gob overrides + + virtual void Draw(DibBitmap *pbm, int xViewOrigin, int yViewOrigin, int nLayer) secStructures; + virtual void InitMenu(Form *pfrm) secStructures; + virtual void OnMenuItemSelected(int id) secStructures; + virtual void DefUpdate() secStructures; + + // UnitGob overrides + + virtual void Deactivate() secStructures; + + // StructGob overrides + + virtual void Takeover(Player *pplr) secStructures; + + // StateMachine methods + + virtual int ProcessStateMachineMessage(State st, Message *pmsg) secStructures; +#if defined(DEBUG_HELPERS) + virtual char *GetName() secStructures; +#endif +}; + +class HrcGob : public MobileUnitBuilderGob // hrc +{ +public: + HrcGob() secStructures; + static bool InitClass(IniReader *pini) secStructures; + static void ExitClass() secStructures; + + // StateMachine methods + +#if defined(DEBUG_HELPERS) + virtual char *GetName() secStructures; +#endif +private: + static MobileUnitBuildForm *s_pfrmBuild; +}; + +class ReactorGob : public StructGob // rctr +{ +public: + ReactorGob() secStructures; + static bool InitClass(IniReader *pini) secStructures; + static void ExitClass() secStructures; + + // StateMachine methods + +#if defined(DEBUG_HELPERS) + virtual char *GetName() secStructures; +#endif +}; + +class ProcessorGob : public StructGob // prcr +{ +public: + ProcessorGob() secStructures; + static bool InitClass(IniReader *pini) secStructures; + static void ExitClass() secStructures; + + // UnitGob overrides + + virtual dword GetAnimationHash(); + virtual void GetAnimationBounds(Rect *prc, bool fBase); + virtual void DrawAnimation(DibBitmap *pbm, int x, int y); + + // StructGob overrides + + virtual bool IsTakeoverable(Player *pplr) secStructures; + virtual void Takeover(Player *pplr) secStructures; + + // Gob overrides + + virtual void Draw(DibBitmap *pbm, int xViewOrigin, int yViewOrigin, int nLayer) secStructures; + virtual bool LoadState(Stream *pstm) secStructures; + virtual bool SaveState(Stream *pstm) secStructures; + virtual void GetClippingBounds(Rect *prc) secStructures; + + // StateMachine methods + + virtual int ProcessStateMachineMessage(State st, Message *pmsg) secStructures; +#if defined(DEBUG_HELPERS) + virtual char *GetName() secStructures; +#endif + +private: + void NotifyMinersAttemptingDelivery(bool fDying) secStructures; + + Animation m_aniOverlay; + Gid m_gidMiner; + WPoint m_wptFakeMiner; + bool m_fProcessingAnimationInProgress; + bool m_fDoorMoving; +}; + +class StructureBuildForm; + +class HqGob : public BuilderGob // hq +{ +public: + HqGob() secStructures; + virtual ~HqGob() secStructures; + + static bool InitClass(IniReader *pini) secStructures; + static void ExitClass() secStructures; + + // Gob overrides + + virtual void InitMenu(Form *pfrm) secGob; + virtual void OnMenuItemSelected(int id) secGob; + + // UnitGob overrides + + virtual void Deactivate() secStructures; + + // StateMachine methods + + virtual int ProcessStateMachineMessage(State st, Message *pmsg) secStructures; +#if defined(DEBUG_HELPERS) + virtual char *GetName() secStructures; +#endif +private: + + void RemoveScorch(StructGob *pstru) secStructures; + + static StructureBuildForm *s_pfrmBuild; +}; + +class PlaceStructureForm : public Form, Timer +{ +public: + PlaceStructureForm() secStructures; + + void SetOwner(BuilderGob *pgobOwner, UnitType ut) secStructures; + BuilderGob *GetOwner() { + return m_pgobOwner; + } + void UpdatePlacementIndicator(WCoord wxViewStart, WCoord wyViewStart, WCoord wxView, WCoord wyView) secStructures; + + void SetPassOnInput(bool fPassOn) { m_fPassOnInput = fPassOn; } + bool IsBusy() { return m_pgobOwner != NULL; } + void OnPaintSimUI(DibBitmap *pbm) secStructures; + void OnPaintControlsSimUI(DibBitmap *pbm, UpdateMap *pupd) secStructures; + void UpdatePosition(Event *pevt); + + // Timer implementation + + virtual void OnTimer(long tCurrent) secStructures; + + // Form overrides + + virtual void OnControlSelected(word idc) secStructures; + virtual bool OnPenEvent(Event *pevt) secStructures; + virtual void OnScroll(int dx, int dy) secStructures; + virtual bool OnHitTest(Event *pevt) secStructures; + +private: + bool FingerHitTest(Event *pevt); + void GetSubRects(WCoord wx, WCoord wy, Rect *prcInside, Rect *prcOutside); + + BuilderGob *m_pgobOwner; + StructConsts *m_pstruc; + TCoord m_tx, m_ty; + TCoord m_wxPen, m_wyPen; + int m_wxDragStart, m_wyDragStart; + TCoord m_txStart, m_tyStart; + bool m_fDragging; + bool m_fPassOnInput; +}; + +class WaitForm : public Form +{ +public: + WaitForm(char *pszTitle, bool fFullScreen = false) secForm; + + // Form overrides + + virtual bool Init(FormMgr *pfrmm, IniReader *pini, word idf) secForm; + virtual void OnPaintBackground(DibBitmap *pbm, UpdateMap *pupd) secForm; + +private: + dword m_ctWaitPopup; + char *m_pszTitle; + bool m_fFullScreen; + Rect m_rcOrig; +}; + +class TransportWaitingUI +{ +public: + TransportWaitingUI(int nWaitStr, bool fShow = true) secComm; + TransportWaitingUI(char *psz, bool fShow = true) secComm; + ~TransportWaitingUI() secComm; + void Show() secComm; + void Hide() secComm; + +private: + Form *m_pfrm; + char *m_psz; +}; + +class RadarGob : public StructGob // rdr +{ +public: + RadarGob() secRadar; + + static bool InitClass(IniReader *pini) secRadar; + static void ExitClass() secRadar; + + // Struct gob methods + + virtual void Activate() secRadar; + virtual void Deactivate() secRadar; + + // StateMachine methods + + virtual int ProcessStateMachineMessage(State st, Message *pmsg) secRadar; +#if defined(DEBUG_HELPERS) + virtual char *GetName() secRadar; +#endif +}; + +class ResearchGob : public StructGob // rsrch +{ +public: + ResearchGob() secStructures; + static bool InitClass(IniReader *pini) secStructures; + static void ExitClass() secStructures; + virtual void UpgradeUpdate() secStructures; + virtual void AbortUpgrade() secStructures; + + // Gob overrides - these need to become virtual if this gets a derived class! + + virtual void InitMenu(Form *pfrm) secStructures; + virtual void OnMenuItemSelected(int id) secStructures; + virtual void Draw(DibBitmap *pbm, int xViewOrigin, int yViewOrigin, int nLayer) secStructures; + virtual void Deactivate() secStructures; + virtual bool LoadState(Stream *pstm) secStructures; + virtual bool SaveState(Stream *pstm) secStructures; + + // StateMachine methods + + virtual int ProcessStateMachineMessage(State st, Message *pmsg) secStructures; +#if defined(DEBUG_HELPERS) + virtual char *GetName() secStructures; +#endif + +private: + int m_nCreditsSpentOnUpgrade; + UnitType m_utTarget; +}; + +class VtsGob : public MobileUnitBuilderGob // vts +{ +public: + VtsGob() secStructures; + static bool InitClass(IniReader *pini) secStructures; + static void ExitClass() secStructures; + +private: + static MobileUnitBuildForm *s_pfrmBuild; +}; + +// TUNE: tankshot movement rate + +const WCoord kwcTankShotRate = kwcTile / 2; + +class TankShotGob : public Gob +{ +public: + TankShotGob() secGob; + static bool InitClass(IniReader *pini) secGob; + static void ExitClass() secGob; + bool Init(AnimationData *panid, WCoord wx, WCoord wy, WCoord wxTarget, WCoord wyTarget, int nDamage, Gid gidOwner, Gid gidTarget, WCoord wcMoveRate) secGob; + void Launch() secGob; + + // Gob methods + + virtual bool Init(IniReader *pini, FindProp *pfind, const char *pszName) secGob; + virtual GobType GetType() secGob; + virtual void Draw(DibBitmap *pbm, int xViewOrigin, int yViewOrigin, int nLayer) secGob; + virtual void GetClippingBounds(Rect *prc) secGob; + virtual bool IsSavable() secGob; + + // StateMachine methods + + virtual int ProcessStateMachineMessage(State st, Message *pmsg) secGob; +#if defined(DEBUG_HELPERS) + virtual char *GetName() secGob; +#endif + +private: + Gid m_gidOwner; + Gid m_gidTarget; + WLineIterator m_li; + Animation m_ani; + int m_nDamage; + +public: + static AnimationData *s_panidShot; +}; +TankShotGob *CreateTankShotGob(WCoord wx, WCoord wy, WCoord wxTarget, WCoord yTarget, int nDamage, Gid gidOwner, Gid gidTarget) secGob; + +class BulletGob : public TankShotGob +{ +public: + static bool InitClass(IniReader *pini) secGob; + static void ExitClass() secGob; + + // Gob methods + + virtual GobType GetType() secGob; + + // StateMachine methods + +#if defined(DEBUG_HELPERS) + virtual char *GetName() secGob; +#endif + +public: + static AnimationData *s_panidShot; +}; +BulletGob *CreateBulletGob(WCoord wx, WCoord wy, WCoord wxTarget, WCoord yTarget, int nDamage, Gid gidOwner, Gid gidTarget, bool fEagle = false) secGob; + +class AndyShotGob : public TankShotGob +{ +public: + static bool InitClass(IniReader *pini) secGob; + static void ExitClass() secGob; + + // Gob methods + + virtual GobType GetType() secGob; + + // StateMachine methods + +#if defined(DEBUG_HELPERS) + virtual char *GetName() secGob; +#endif + +public: + static AnimationData *s_panidShot; +}; +AndyShotGob *CreateAndyShotGob(WCoord wx, WCoord wy, WCoord wxTarget, WCoord yTarget, int nDamage, Gid gidOwner, Gid gidTarget, bool fEagle = false) secGob; + +class RocketGob : public Gob +{ +public: + RocketGob() secGob; + static bool InitClass(IniReader *pini) secGob; + static void ExitClass() secGob; + bool Init(WCoord wx, WCoord wy, WCoord wxTarget, WCoord wyTarget, int nDamage, Gid gidOwner, Gid gidTarget) secGob; + + // Gob methods + + virtual bool Init(IniReader *pini, FindProp *pfind, const char *pszName) secGob; + virtual bool IsSavable() secGob; + virtual GobType GetType() secGob; + virtual void Draw(DibBitmap *pbm, int xViewOrigin, int yViewOrigin, int nLayer) secGob; + virtual void GetClippingBounds(Rect *prc) secGob; + + // StateMachine methods + + virtual int ProcessStateMachineMessage(State st, Message *pmsg) secGob; +#if defined(DEBUG_HELPERS) + virtual char *GetName() secGob; +#endif + +private: + Gid m_gidOwner; + Gid m_gidTarget; + WLineIterator m_li; + Animation m_ani; + int m_nDamage; + + static AnimationData *s_panidRocket; + static int s_nTrailStrip; +}; +RocketGob *CreateRocketGob(WCoord wx, WCoord wy, WCoord wxTarget, WCoord wyTarget, int nDamage, Gid gidOwner, Gid gidTarget) secGob; + +// SmokeGob + +class SmokeGob : public AnimGob +{ +public: + SmokeGob(int cLoops) secGob; + bool Init(WCoord wx, WCoord wy, StateMachineId smidNotify) secGob; + + // Gob overrides + + virtual bool LoadState(Stream *pstm) secGob; + virtual bool SaveState(Stream *pstm) secGob; + virtual bool IsSavable() secGob; + virtual GobType GetType() secGob; + + // AnimGob overrides + + virtual bool OnStripDone() secGob; + +private: + int m_cLoops; +}; + +// PuffGob + +class PuffGob : public AnimGob +{ +public: + PuffGob() secGob; + bool Init(WCoord wx, WCoord wy, StateMachineId smidNotify) secGob; + + // Gob overrides + + virtual bool LoadState(Stream *pstm) secGob; + virtual bool SaveState(Stream *pstm) secGob; + virtual bool IsSavable() secGob; + virtual GobType GetType() secGob; +}; + +#if 0 // not used anymore +class RicochetGob : public AnimGob +{ +public: + bool Init(WCoord wx, WCoord wy, word wfAnm, const char *pszAniName, AnimationData *panid = NULL, + StateMachineId smidNotify = ksmidNull, const char *pszName = NULL) secGob; + + // AnimGob overrides + + virtual bool OnStripDone() secGob; + +private: + bool m_fNew; +}; +#endif + +// WarehouseGob + +class WarehouseGob : public StructGob +{ +public: + WarehouseGob() secWarehouse; + static bool InitClass(IniReader *pini) secWarehouse; + static void ExitClass() secWarehouse; + + // StructGob overrides + + virtual void Draw(DibBitmap *pbm, int xViewOrigin, int yViewOrigin, int nLayer) secWarehouse; + virtual void Takeover(Player *pplr) secWarehouse; + + // StateMachine methods + + virtual int ProcessStateMachineMessage(State st, Message *pmsg) secWarehouse; +#if defined(DEBUG_HELPERS) + virtual char *GetName() secWarehouse; +#endif +}; + +// TowerGob + +class TowerGob : public StructGob +{ +public: + TowerGob(TowerConsts *ptwrc) secTowers; + static bool InitClass(TowerConsts *ptwrc, IniReader *pini) secTowers; + static void ExitClass(TowerConsts *ptwrc) secTowers; + virtual bool Fire(UnitGob *puntTarget, WCoord wx, WCoord wy, WCoord wdx, WCoord wdy) = 0; +#ifdef DRAW_LINES + void DrawTargetLine(DibBitmap *pbm, int xViewOrigin, int yViewOrigin) secTowers; +#endif + + // Gob overrides + + virtual void Draw(DibBitmap *pbm, int xViewOrigin, int yViewOrigin, int nLayer) secTowers; + virtual bool LoadState(Stream *pstm) secTowers; + virtual bool SaveState(Stream *pstm) secTowers; + virtual void GetClippingBounds(Rect *prc) secTowers; + + // UnitGob overrides + + virtual void Activate() secTowers; + virtual void DefUpdate() secTowers; + virtual void SetTarget(Gid gid, WCoord wxTarget = 0, WCoord wyTarget = 0, WCoord wxCenter = 0, WCoord wyCenter = 0, TCoord tcRadius = 0, WCoord wcMoveDistPerUpdate = 0) secTowers; + virtual bool IsValidTarget(Gob *pgobTarget) secTowers; + virtual dword GetAnimationHash() secTowers; + virtual void GetAnimationBounds(Rect *prc, bool fBase) secTowers; + virtual void DrawAnimation(DibBitmap *pbm, int x, int y) secTowers; + + // StructGob override + + virtual bool IsTakeoverable(Player *pplr) secTowers; + + // StateMachine methods + + virtual int ProcessStateMachineMessage(State st, Message *pmsg) secTowers; + +protected: + + // UnitGob protected override + + virtual UnitGob *FindEnemyNearby(TCoord tcRange) secTowers; + + // members + + Animation m_aniTurret; + Direction16 m_dir16Turret; + Gid m_gidTargetPrimary; + Gid m_gidTargetSecondary; + long m_tLastFire; + +#ifdef DRAW_LINES +public: +#endif + WPoint m_wptTarget; +}; + +// GunTowerGob + +class GunTowerGob : public TowerGob +{ +public: + GunTowerGob() secTowers; + static bool InitClass(IniReader *pini) secTowers; + static void ExitClass() secTowers; + + // TowerGob overrides + + virtual bool Fire(UnitGob *puntTarget, WCoord wx, WCoord wy, WCoord wdx, WCoord wdy) secTowers; + +#if defined(DEBUG_HELPERS) + virtual char *GetName() secTowers; +#endif +}; + +// RocketTowerGob + +class RocketTowerGob : public TowerGob +{ +public: + RocketTowerGob() secTowers; + static bool InitClass(IniReader *pini) secTowers; + static void ExitClass() secTowers; + + // TowerGob overrides + + virtual bool Fire(UnitGob *puntTarget, WCoord wx, WCoord wy, WCoord wdx, WCoord wdy) secTowers; + +#if defined(DEBUG_HELPERS) + virtual char *GetName() secTowers; +#endif +}; + +class ArtilleryGob : public MobileUnitGob // art +{ +public: + ArtilleryGob() secArtilleryGob; + static bool InitClass(IniReader *pini) secArtilleryGob; + static void ExitClass() secArtilleryGob; + + // UnitGob overrides + + virtual bool Fire(UnitGob *puntTarget, WCoord wx, WCoord wy, WCoord wdx, WCoord wdy) secArtilleryGob; + + // StateMachine methods + +// virtual int ProcessStateMachineMessage(State st, Message *pmsg) secArtilleryGob; +#if defined(DEBUG_HELPERS) + virtual char *GetName() secArtilleryGob; +#endif +}; + +class ArtilleryShotGob : public TankShotGob +{ +public: + static bool InitClass(IniReader *pini) secArtilleryGob; + static void ExitClass() secArtilleryGob; + + // Gob methods + + virtual GobType GetType() secArtilleryGob; + + // StateMachine methods + +#if defined(DEBUG_HELPERS) + virtual char *GetName() secArtilleryGob; +#endif + +public: + static AnimationData *s_panidShot; +}; +ArtilleryShotGob *CreateArtilleryShotGob(WCoord wx, WCoord wy, WCoord wxTarget, WCoord yTarget, int nDamage, Gid gidOwner, Gid gidTarget) secArtilleryGob; + +class AndyGob : public MobileUnitGob +{ +public: + AndyGob() secInfantryGob; + AndyGob(MobileUnitConsts *pmuntc) secInfantryGob; + static bool InitClass(IniReader *pini) secInfantryGob; + static void ExitClass() secInfantryGob; + + // UnitGob overrides + + virtual bool Init(WCoord wx, WCoord wy, Player *pplr, fix fxHealth, dword ff, const char *pszName) secInfantryGob; + virtual bool Fire(UnitGob *puntTarget, WCoord wx, WCoord wy, WCoord wdx, WCoord wdy) secInfantryGob; + virtual void Idle() secInfantryGob; + + // StateMachine methods + + virtual int ProcessStateMachineMessage(State st, Message *pmsg) secInfantryGob; +#if defined(DEBUG_HELPERS) + virtual char *GetName() secInfantryGob; +#endif +}; + +class FoxGob : public AndyGob // fox +{ +public: + FoxGob() secInfantryGob; + static bool InitClass(IniReader *pini) secInfantryGob; + static void ExitClass() secInfantryGob; + +#if defined(DEBUG_HELPERS) + virtual char *GetName() secInfantryGob; +#endif +}; + +class ReplicatorGob : public StructGob // rep +{ +public: + ReplicatorGob() secReplicatorGob; + static bool InitClass(IniReader *pini) secReplicatorGob; + static void ExitClass() secReplicatorGob; + void GetInputTilePosition(TPoint *ptpt) secReplicatorGob; + void Enable(bool fEnable) secReplicatorGob; + + // UnitGob overrides + + virtual bool Init(WCoord wx, WCoord wy, Player *pplr, fix fxHealth, dword ff, const char *pszName) secReplicatorGob; + virtual void Draw(DibBitmap *pbm, int xViewOrigin, int yViewOrigin, int nLayer) secReplicatorGob; + virtual void GetClippingBounds(Rect *prc) secReplicatorGob; + virtual bool LoadState(Stream *pstm) secReplicatorGob; + virtual bool SaveState(Stream *pstm) secReplicatorGob; + virtual AnimSprite *CreateHilightSprite() { return NULL; } + + // StructGob overrides + + virtual void Activate() secReplicatorGob; + virtual void Deactivate() secReplicatorGob; + + // StateMachine methods + + virtual int ProcessStateMachineMessage(State st, Message *pmsg) secReplicatorGob; +#if defined(DEBUG_HELPERS) + virtual char *GetName() secReplicatorGob; +#endif + + // For query of replicator input points + + static void ClearReplicatorCount() { + s_cReplicators = 0; + } + + static int GetReplicatorCount() { + return s_cReplicators; + } + static void GetReplicatorInputPoint(int n, TPoint *ptpt) { + *ptpt = s_atptInput[n]; + } + +protected: + virtual void WaitingForCredits(bool fNeed, bool fOverride = false, Player *pplr = NULL) secReplicatorGob; + +private: + bool ClearOutputBay(TCoord txBay, TCoord tyBay, TCoord txOut, TCoord tyOut) secReplicatorGob; + + bool m_fEnabled; + bool m_fReplicating; + int m_ifrmLights; + Player *m_pplrNeedCredits; + + // For management of replicator input points + + static void AddReplicatorInputPoint(TCoord tx, TCoord ty) secReplicatorGob; + static void RemoveReplicatorInputPoint(TCoord tx, TCoord ty) secReplicatorGob; + static int s_cReplicators; + static TPoint s_atptInput[10]; +}; + +const int kdtxReplicatorInput = 2; +const int kdtyReplicatorInput = 0; +const int kdtxReplicatorOutput1 = 1; +const int kdtyReplicatorOutput1 = 2; // 1; +const int kdtxReplicatorOutput2 = 3; +const int kdtyReplicatorOutput2 = 2; // 1; + +// ActivatorGob + +class ActivatorGob : public AnimGob +{ +public: + // Gob overrides + + ActivatorGob() {m_fActivated = false;}; + virtual bool Init(IniReader *pini, FindProp *pfind, const char *pszName) secReplicatorGob; + virtual bool LoadState(Stream *pstm) secReplicatorGob; + virtual bool SaveState(Stream *pstm) secReplicatorGob; + virtual bool IsSavable() secReplicatorGob; + virtual GobType GetType() secReplicatorGob; + + // StateMachine methods + + virtual int ProcessStateMachineMessage(State st, Message *pmsg) secReplicatorGob; +#if defined(DEBUG_HELPERS) + virtual char *GetName() secReplicatorGob; +#endif + +private: + bool m_fActivated; +}; + +// MemMgr - for allocs that are mostly read-only. + +typedef void *HMem; +typedef word HandleEntry; +typedef word HandleEntryRef; + +struct HMemStruct +{ + word her; + byte iheap; +}; + +struct MemHeader +{ + word w; + word cbBlock; +}; + +#define kcHandlesPerTable 64 +struct HandleTable +{ + int iheFree; + HandleEntry ahe[kcHandlesPerTable]; +}; + +struct Heap +{ + byte *pbHeap; + word cbHeap; + MemHeader *pmhdrFreeFirst; + word cbLargestFreeBlock; + word wf; + word nRec; + void *hMem; +}; + +#define kfHintWillLock 0x1 + +class MemMgr +{ +public: + MemMgr() secMemMgr; + ~MemMgr() secMemMgr; + + void Init(dword cbDynReserve, dword cbMaxNeeded, dword *pcbTotal) secMemMgr; + void Exit() secMemMgr; + + HMem AllocHandle(word cb, word wfHints = 0) secMemMgr; + void FreeHandle(HMem hmem) secMemMgr; + void SetLocked(HMem hmem) secMemMgr; + void ClearLocked(HMem hmem) secMemMgr; + void *GetPtr(HMem hmem) secMemMgr; + void WriteHandle(HMem hmem, word ib, void *pvSrc, word cb) secMemMgr; + + void *AllocPtr(word cb) secMemMgr; + void FreePtr(void *pv) secMemMgr; + void WritePtr(void *pvDst, word ib, void *pvSrc, word cb) secMemMgr; + + dword GetFreeSize(word *pcbLargestFree = NULL) secMemMgr; + dword GetTotalSize(dword *pcbDyn, dword *pcbDb) secMemMgr; + static void GetInitSize(dword cbDynReserve, dword *pcbDyn, dword *pcbStorage) secMemMgr; + static void OnRebootFreeStorageDatabase(); // must remain in .text section + +#ifdef INCL_VALIDATEHEAP + void Validate() secMemMgr; + void ValidateHandleTableFreeEntries() secMemMgr; +#endif + +private: + HandleTable *NewHandleTable() secMemMgr; + bool AllocHandleEntry(word *pher) secMemMgr; + void FreeHandleEntry(word her) secMemMgr; + Heap *NewHeap(void *pvHeap, word cbHeap, bool fDbRam, word nRec, void *hMem) secMemMgr; + void WriteHeader(Heap *pheap, MemHeader *pmhdrDst, MemHeader *pmhdrSrc, word cb = sizeof(MemHeader)) secMemMgr; + HMem AllocBlock(int iheap, MemHeader *pmhdr, MemHeader *pmhdrFreeLast, word cbBlock, word wfHints) secMemMgr; + MemHeader *Compact(Heap *pheap, word cbBlock, MemHeader **ppmhdrFreeLast, word wfHints) secMemMgr; + MemHeader *SplitFreeBlock(Heap *pheap, MemHeader *pmhdr, MemHeader *pmhdrFreeLast, MemHeader *pmhdrNew, word wfHints) secMemMgr; + word GetLargestFreeBlockSize(Heap *pheap) secMemMgr; + +#ifdef INCL_VALIDATEHEAP + void ValidateHandle(HMem hmem) secMemMgr; +#endif + + Heap *m_apheap[64]; + int m_cheap; + HandleTable **m_aphtab; + int m_chtab; + int m_chtabAlloced; + dword m_cbTotalFreeBlocks; + +#ifdef PIL + LocalID m_lid; + DmOpenRef m_pdb; +#endif +}; +extern MemMgr gmmgr; + +// Cache Manager. +// 16 bytes for quick lookup + +struct CacheEntry // ce +{ + CacheEntry *pceNext; + CacheEntry *pcePrev; + word wUniqueLock; + word cbSize; + HMem hmem; +}; + +#define kcCacheEntries 512 +#define kwIndexMask 0x1ff +#define kwUniqueMask 0xfe00 +#define kwLockMask 0x001ff +#define kwIncUnique 0x0200 +#define kwIncLock 0x0001 + +class CacheMgr { +public: + CacheMgr() secCacheMgr; + ~CacheMgr() secCacheMgr; + bool Init() secCacheMgr; + void Exit() secCacheMgr; + + void *GetPtr(CacheHandle hc) secCacheMgr; + void *Lock(CacheHandle hc) secCacheMgr; + void Unlock(CacheHandle hc) secCacheMgr; + bool IsValid(CacheHandle hc) secCacheMgr; + CacheHandle NewObject(void *pv, word cb, word wfHints = 0) secCacheMgr; + void Write(CacheHandle hc, word ib, void *pvSrc, word cb) secCacheMgr; + word GetSize(CacheHandle hc) secCacheMgr; + bool MakeSpace(dword cb) secCacheMgr; + dword GetTotalSize() secCacheMgr; + dword GetLimit() secCacheMgr; + void SetLimit(dword cbLimit) secCacheMgr; + +private: + CacheEntry *m_pceList; + CacheEntry *m_pceFirst; + CacheEntry *m_pceFree; + dword m_cbTotalSize; + dword m_cbLimit; + + inline CacheHandle MakeHandle(CacheEntry *pce) + { + return (CacheHandle)((pce - m_pceList) | (pce->wUniqueLock & kwUniqueMask)); + } + + inline CacheEntry *ValidateHandle(CacheHandle hc) + { + if (hc == 0) + return NULL; + word ice = (hc & kwIndexMask); + if (ice >= kcCacheEntries) + return NULL; + CacheEntry *pce = &m_pceList[ice]; + if ((pce->wUniqueLock & kwUniqueMask) != (hc & kwUniqueMask)) + return NULL; + return pce; + } + + inline void Add(CacheEntry *pce) + { + if (m_pceFirst != NULL) { + pce->pceNext = m_pceFirst; + m_pceFirst->pcePrev->pceNext = pce; + pce->pcePrev = m_pceFirst->pcePrev; + m_pceFirst->pcePrev = pce; + } else { + pce->pceNext = pce; + pce->pcePrev = pce; + } + m_pceFirst = pce; + } + + inline void Remove(CacheEntry *pce) + { + pce->pcePrev->pceNext = pce->pceNext; + pce->pceNext->pcePrev = pce->pcePrev; + if (pce == m_pceFirst) { + m_pceFirst = pce->pceNext; + if (m_pceFirst == pce) + m_pceFirst = NULL; + } + } + + inline void AddToFreeList(CacheEntry *pce) + { + if (m_pceFree != NULL) { + pce->pceNext = m_pceFree; + m_pceFree->pcePrev->pceNext = pce; + pce->pcePrev = m_pceFree->pcePrev; + m_pceFree->pcePrev = pce; + } else { + pce->pceNext = pce; + pce->pcePrev = pce; + } + m_pceFree = pce; + } + + inline void RemoveFromFreeList(CacheEntry *pce) + { + pce->pcePrev->pceNext = pce->pceNext; + pce->pceNext->pcePrev = pce->pcePrev; + if (pce == m_pceFree) { + m_pceFree = pce->pceNext; + if (m_pceFree == pce) + m_pceFree = NULL; + } + } + + void Discard(CacheEntry *pce) secCacheMgr; +}; +extern CacheMgr gcam; + +// SoundMgr + +#define kcChannelsMax 16 + +} // namespace wi + +#include "sounddevice.h" + +namespace wi { + +struct PcmHeader // pcmh +{ + byte *pb; + word cb; +}; + +struct SfxEntry // sfxe +{ + byte nSound; + byte nChannel; // unused + byte nPriority; +}; +#define kcbSfxEntry 3 + +class SoundMgr // sndm +{ +public: + SoundMgr() secSoundMgr; + ~SoundMgr() secSoundMgr; + + bool Init() secSoundMgr; + void Exit() secSoundMgr; + void Enable(bool fEnable) secSoundMgr; + bool IsEnabled() secSoundMgr; + void PlaySfx(Sfx sfx) secSoundMgr; + void WaitSilence() secSoundMgr; + long FilterSleepTicks(long ct) secSoundMgr; + void SetVolume(int nVolume) secSoundMgr; + int GetVolume() secSoundMgr; + bool SaveStateAndClear() secSoundMgr; + void RestoreState() secSoundMgr; + +private: + int m_cChannels; + byte m_anPriorityChannel[kcChannelsMax]; + FileMap *m_afmap; + PcmHeader *m_apcmh; + int m_cpcmh; + SfxEntry *m_asfxe; + int m_csfxe; + FileMap m_fmapSfxEntries; + SoundDevice *m_psndd; + bool m_fStateSaved; + bool m_fEnabledSav; + int m_nVolumeSav; +}; +extern SoundMgr gsndm; + +// StringTable Class + +class StringTable +{ +public: + StringTable() secStringTable; + ~StringTable() secStringTable; + + bool Init(char *pszFilename) secStringTable; + bool GetString(int id, char *psz, int cb) secStringTable; + +private: + File *m_pfil; +}; +extern StringTable *gpstrtbl; + + +// AlertControl + +#define kcchAlertText 64 +class AlertControl : public LabelControl, public Timer +{ +public: + AlertControl() secAlertControl; + ~AlertControl() secAlertControl; + + void AddText(const char *psz) secAlertControl; + + // Control methods + + virtual bool Init(Form *pfrm, IniReader *pini, FindProp *pfind) secAlertControl; + + // Timer methods + + virtual void OnTimer(long tCurrent) secAlertControl; + +private: + word m_wf; +}; + +// DRM stuff + +struct Key // key +{ + byte ab[9]; +}; +extern Key gkey; +bool DrmValidate() secDrm; + +// +// Preferences support +// + +// Preferences structures change over time. They get backed up by HotSync. +// By carefully versioning the structure HT can load older preference versions +// successfully. Every prefs structure has a version; these are meant to be supported +// versions of preferences; meaning HT will load older versions of each versioned prefs +// structure. They are also sub-versioned by size; this allows us during development to +// add / remove fields and not have HT crash because it is assuming the wrong thing. + +// prefs flags + +#define kfPrefSoundMuted 1 // must be 1 for compatibility with fMute +#define kfPrefIgnoreBluetoothWarning 2 + +// The header necessary on all preferences structures + +struct PreferencesVersion +{ + dword dwVersion; + dword cbSize; +}; + +// The versions of preferences structures HT can successfully read +// Use fixed size datatypes + +#define knVersionPreferencesV100 0x100 +struct PreferencesV100 // prefs +{ + PreferencesVersion prefv; + char szUsername[kcbPlayerName]; + char szPassword[kcbPlayerName]; + char szToken[kcbTokenMax]; + word fAnonymous; + word nYearLastRun; // demo time check + word nMonthLastRun; + word nDayLastRun; + word nVolume; // volume + word wfPrefs; + word wfPerfOptions; + word wfHandicap; // Difficulty-affecting flags + long ctGameSpeed; // game speed + word fLassoSelection; + short nHueOffset; // HSL settings + short nSatMultiplier; + short nLumOffset; + word cxModeBest; // Remember what mode / data is best. Check against in case config changes + word cyModeBest; + word nDepthModeBest; + word nDepthDataBest; + word nSizeDataBest; + word nDegreeOrientationBest; + word cxModeLast; // Remember what mode / data was chosen + word cyModeLast; + word nDepthModeLast; + word nDepthDataLast; + word nSizeDataLast; + word nDegreeOrientationLast; + Key key; // DRM key + short nDemoRank; +#if defined(WIN) && !defined(CE) + short nScale; +#endif + double nScrollSpeed; + char szAskURL[512]; +}; + +#define knVersionPreferencesV101 0x101 +struct PreferencesV101 : public PreferencesV100 +{ + // md5 hash (16 bytes) to hex chars (32 bytes) plus zero terminator, rounded up to even number + char szDeviceId[34]; +}; + +// The current version + +#define knVersionPreferences knVersionPreferencesV101 +typedef PreferencesV101 Preferences; +extern Preferences gprefsInit; +bool HostSavePreferences(void *pv, int cb) secHost; +int HostLoadPreferences(void *pv, int cb) secHost; + +// Our in-game MessageBox + +bool HtMessageBox(word wf, const char *pszTitle, const char *pszFormat, ...) secMisc; +bool HtMessageBox(word idf, word wf, const char *pszTitle, const char *pszBody) secMisc; +DialogForm *CreateHtMessageBox(word idf, word wf, const char *pszTitle, const char *pszBody) secMisc; + +const word kfMbWhiteBorder = 0x0001; +const word kfMbClearDib = 0x0002; +const word kfMbKeepTimersEnabled = 0x0004; + +// For Wc14Transport.dll to utilize external functionality + +struct TransportHost +{ + void *(*New)(int size); + void (*Delete)(void *pv); + void (*strncpyz)(char *pszDst, const char *pszSrc, int cb); + void (*Status)(const char *psz); + bool (*HtMessageBox)(unsigned short wf, const char *pszTitle, const char *pszFormat, ...); + TimerMgr *ptimm; +}; + +// Globals + +extern byte grvlp[]; +extern byte grvlpLarge[]; +extern IniReader *gpiniForms; +extern IniReader *gpiniGame; +extern Font *gapfnt[]; +extern Player *gpplrLocal; +extern byte *gmpiclriclrShadow; +extern UnitConsts *gapuntc[kutMax]; +extern PlaceStructureForm *gpfrmPlace; +#ifdef DRAW_PATHS +extern bool gfDrawPaths; +#endif +#ifdef DRAW_LINES +extern bool gfDrawLines; +#endif +extern bool gfPlayingBack; +extern CommandQueue gcmdq; +extern bool gfOvermindEnabled; +extern PlayerMgr gplrm; +extern Player gplrDummy; +extern TBitmap *gaptbmScorches[4]; +extern BuilderConsts gbldcHq; +extern byte *gpbScratch; +extern word gcbScratch; +extern bool gfLassoSelection; +extern int g_mpDirToDx[8]; +extern int g_mpDirToDy[8]; +extern int gcxTile; +extern int gcyTile; +extern int gcxyBorder; +extern char gszPlayerName[kcbPlayerName]; +extern char gszUsername[kcbPlayerName]; +extern char gszPassword[kcbPlayerName]; +extern char gszToken[kcbTokenMax]; +extern bool gfAnonymous; +extern bool gfDrawUpdateRects; +extern bool gfAutosave; +extern DibBitmap *gpbmClip; +extern FormMgr *gpfrmmSim; +extern FormMgr *gpfrmmInput; +extern MultiFormMgr *gpmfrmm; +extern SpriteManager *gpsprm; +#ifdef STATS_DISPLAY +extern long gcPathScoresCalced; +#endif +extern int gnHueOffset; +extern int gnSatMultiplier; +extern int gnLumOffset; +extern int gnDemoRank; +extern double gnScrollSpeed; +extern char gszAskURL[512]; +extern char gszDeviceId[34]; +extern long gtGameSpeed; +extern AnimationData *g_panidMoveTarget; +extern UpdateMap *gpupdSim; +extern Color *gaclrFixed; +extern Color gaclr8bpp[]; +extern Color gaclr4bpp[]; +extern byte gmpDistFromDxy[10][10]; +extern TRect *gptrcMapOpaque; +extern TRect gtrcMapOpaque; +extern Upgrade gaupg[kupgtMax]; +extern MiniMapControl *gpmm; +#ifdef STRESS +extern bool gfStress; +extern long gtStressTimeout; +#endif +extern byte gmpdirdirOpposite[8]; +extern int ganSquared[64]; +extern bool gfClearFog; +extern int gnUnitCostMin; +extern int gnUnitCostMax; +extern int gnUnitCostMPMin; +extern int gnUnitCostMPMax; +extern fix gfxMobileUnitArmorStrengthMin; +extern fix gfxMobileUnitArmorStrengthMax; +extern fix gfxStructureArmorStrengthMin; +extern fix gfxStructureArmorStrengthMax; +extern fix gfxStructureDamageMin; +extern fix gfxStructureDamageMax; +extern fix gfxVehicleDamageMin; +extern fix gfxVehicleDamageMax; +extern fix gfxInfantryDamageMin; +extern fix gfxInfantryDamageMax; +extern TCoord gtcFiringRangeMin; +extern TCoord gtcFiringRangeMax; +extern WCoord gwcMoveDistPerUpdateMin; +extern WCoord gwcMoveDistPerUpdateMax; +extern int gnPowerSupplyMin; +extern int gnPowerSupplyMax; +extern int gnPowerDemandMin; +extern int gnPowerDemandMax; +extern int gaiclrSide[kcSides]; +extern char *gszVersion; +extern char gszBuildDate[]; +extern char gszBuildTime[]; +extern bool gfDragSelecting; +extern WPoint s_wptSelect1, s_wptSelect2; +extern WRect gwrcSelection; +extern WPoint s_awptSelection[]; +extern int s_cwptSelection; +extern word gwfPerfOptions; +extern word gwfHandicap; +extern int gcStructGobsHumanLimitSP; +extern int gcStructGobsComputerLimitSP; +extern int gcMuntGobsHumanLimitSP; +extern int gcMuntGobsComputerLimitSP; +extern int gcStructGobsComputerDeltaSP; +extern int gcStructGobsHumanDeltaSP; +extern int gcStructGobsLimitMP; +extern int gcMuntGobsLimitMP; +extern int gcSceneryGobsLimit; +extern int gcScorchGobsLimit; +extern int gcSupportGobsLimit; +extern bool gfGrayscale; +extern bool gfDemo; +extern Transport *gptra; +#ifdef MP_STRESS +extern bool gfMPStress; +extern int gnMPPos; +#endif +extern char *gpszDataDir; +extern bool gfIgnoreBluetoothWarning; + +inline Color GetColor(int iclr) { + return gaclrFixed[iclr]; +} + +inline Color GetSideColor(Side side) { + return GetColor(gaiclrSide[side]); +} + +// Helpers +inline UnitType UnitTypeFromPVoid(void *pv) { + return *((UnitType *)&pv); +} + +inline int IntFromPVoid(void *pv) { + return *((int *)&pv); +} + +// Out-of-band data for gameOverEvent (knGoLoadSavedGame case) +extern Stream *gpstmSavedGame; +extern int gimodeReinitialize; +extern bool gfLoadReinitializeSave; + +char *_fgets(char *psz, int cch, File *pfil) secMisc; +void HtStrupr(char *psz) secMisc; + +#ifdef DEBUG +int _GetRandom(char *pszFile, int nLine) secMisc; +#define GetRandom() _GetRandom(__FILE__, __LINE__) +#else +int _GetRandom() secMisc; +#define GetRandom() _GetRandom() +#endif +void SetRandomSeed(unsigned long nSeed) secMisc; +unsigned long GetRandomSeed() secMisc; +int GetAsyncRandom() secMisc; +void DrawBuildProgressIndicator(DibBitmap *pbm, Rect *prc, int nNumerator, int nDenominator) secGob; +void DrawSelectionIndicator(DibBitmap *pbm, Rect *prc, int nNumerator, int nDenominator) secGob; +void DrawHealthIndicator(DibBitmap *pbm, Rect *prc, int nNumerator, int nDenominator) secGob; +void DrawFullnessIndicator(DibBitmap *pbm, Rect *prc, int nPips, int nPipsMax) secMiner; +void DrawBorder(DibBitmap *pbm, Rect *prc, int nThickness, Color clr, UpdateMap *pupd = NULL) secDibBitmap; +void DrawBitmapBorder(DibBitmap *pbm, UpdateMap *pupd, Rect *prc, AnimationData *panid, int ifrm = 0, Side side = ksideNeutral) secDibBitmap; +int DrawFancyText(DibBitmap *pbm, Font *pfntDefault, char *psz, int x, int y, int cch = 0) secLabelControl; +int GetFancyTextExtent(Font *pfntDefault, char *psz, int cch = 0) secLabelControl; +Direction CalcDir(int dx, int dy) secGob; +Direction16 CalcDir16(int dx, int dy) secGob; +bool FormDragger(Form *pfrm, Event *pevt) secForm; +bool HostMultiplayerGame() secMultiplayer; +bool JoinOrHostMultiplayerGame(const PackId *ppackid) secMultiplayer; +Direction TurnToward(Direction dirTo, Direction dirFrom) secMisc; +long CalcCreditsShare(Player *pplr) secStructures; +void DrawTileMap(byte **ppbMap, int ctx, int cty, byte *pbDst, int cbDstStride, int cxLeftTile, int cyTopTile, int cxRightTile, int cyBottomTile, int ctxInside, int ctyInside, int cxTile, int cyTile) secTileMap; +FormMgr *CreateFormMgr(DibBitmap *pbm) secFormMgr; +void ShadowHelper(DibBitmap *pbm, UpdateMap *pupd, Rect *prc) secForm; +void FillHelper(DibBitmap *pbm, UpdateMap *pupd, Rect *prc, Color clr) secForm; +void BltHelper(DibBitmap *pbm, HtBitmap *phtbm, UpdateMap *pupd, int xDst, int yDst) secForm; +void RgbToHsl(byte bR, byte bG, byte bB, word *pnH, word *pnS, word *pnL) secMisc; +void HslToRgb(word nH, word nS, word nL, byte *pbR, byte *pbG, byte *pbB) secMisc; +void SetHslAdjustedPalette(Palette *ppal, short nHueOffset, short nSatMultiplier, short nLumOffset) secMisc; +UnitConsts *GetUnitConsts(GobType gt) secGob; +Sfx SfxFromCategory(SfxCategory sfxc) secMisc; +bool ParseNumber(char **ppsz, int *pn) secTrigger; +bool ParseLong(char **ppsz, long *pn) secTrigger; +bool ParseArea(char **ppsz, int *pn) secTrigger; +bool ParseUnitMask(char **ppsz, UnitMask *pum) secTrigger; +bool ParseUpgradeMask(char **ppsz, UpgradeMask *pupgm) secTrigger; +bool ParseString(char **ppsz, char *psz) secTrigger; +SideMask GetSideMaskFromCaSideMask(Side sideCur, word wfCaSideMask) secTrigger; +int GetPlayersListFromCaSideMask(Side sideCur, word wfMask, Player **applr) secTrigger; +void Ecom(int nCharFrom, int nCharTo, char *pszMessage, int nBackground, bool fMore) secEcom; +bool DoModalGameOptionsForm(Palette *ppal, bool fInGame) secGameOptionsForm; +bool ShowDownloadMissionPackForm(PackId *ppackid); +bool DownloadMissionPack(const PackId *ppackid, const char *pszTitle, + bool fPlayButton); +bool DownloadMissionPackFromURL(const char *pszURL, PackId *ppackid, + bool *play); +bool AskInstallMissionPack(const PackId *ppackid, const char *pszUITitle, + const char *pszPackTitle); +bool IsTileFree(TCoord tx, TCoord ty, byte bf = kbfStructure, Gob **ppgob = NULL) secMisc; +void ShowAlert(int id) secAlertControl; +void ShowAlert(const char *psz) secAlertControl; +void SendAttackCommand(Gid gidReceiver, Gid gidTarget) secUnitGob; +word AggBitsFromAgg(int nAggressiveness) secUnitGob; +void FindNearestFreeTile(TCoord tx, TCoord ty, WPoint *pwpt, byte bf = kbfStructure | kbfMobileUnit) secMisc; +void GetPrerequisiteString(char *psz, UnitConsts *puntc) secUnitGob; +void BringInBounds(WCoord *pwx, WCoord *pwy) secMisc; +Direction DirectionFromLocations(TCoord txOld, TCoord tyOld, TCoord txNew, TCoord tyNew) secTerrainMap; +int RadiusFromUnitCount(int cUnits) secMisc; +void MoveUnitsToArea(MobileUnitGob **apmunt, int cpmunt, TRect *ptrc) secTrigger; +void AddPointToLassoSelection(WPoint wpt) secSimUIForm; +void ExpandVars(char *pszSrc, char *pszBuff, int cbBuff) secMisc; +void GetRankTitle(char *psz, int cb) secMisc; +void DoRegisterNowForm() secShell; +bool PickTransport(Transport **pptra) secMultiplayer; +void FormatButtons(Form *pfrm, word *aidc, int cidc, int idcRef, int idcRefNext) secGameOptionsForm; +bool DoInputPanelForm(char **ppszChars, int cRows, const char *pszLabel, + const char *pszEdit, char *pszOut, int cbOut, + bool (*pfnValidateInput)(const char *psz) = NULL) secInputPanelForm; +void DoInputPanelForm(Form *pfrm, word idcLabel, word idcEdit) secInputPanelForm; +void GetSpeedMultiplierString(char *psz, long tGameSpeed) secGameOptionsForm; +bool PtInPolygon(WPoint *awpt, int cwpt, WCoord wx, WCoord wy); +void AddPointToLassoSelection(WPoint wpt); +//const char *GetString(const json::JsonMap *map, const char *key); + +#ifdef MP_DEBUG_SHAREDMEM +void MPInitSharedMemoryWindow(bool fServer); +void MPExitSharedMemoryWindow(bool fServer); +void MPUpdateState(); +void MPValidateState(); +Gob *MPGetGobPtr(Gid gid); +void MPCopyMem(void *pvTo, void *pvFrom, int cb); +void MPValidateMemory(void *pvRemote, void *pvLocal, int cb); +void MPValidateMember2(void *pvLocal, void *pvRemote, int cbThis, int cbOffsetMem, int cbMem); +#define MPValidateMember(c, m, pr) MPValidateMember2(this, pr, sizeof(c), OFFSETOF(c, m), sizeof(m)) +#define MPValidateGobMember(c, m) MPValidateMember(c, m, MPGetGobPtr(GetId())) + +struct SharedMemWindow +{ + bool fDetectSyncErrors; + DWORD pidProcess; + Gob *apgobMaster[kcpgobMax + 1]; +}; + +extern SharedMemWindow gsmw; +extern HANDLE ghSharedMem; +extern SharedMemWindow *gpsmw; +#endif + +// Misc runtime stuff + +int strnicmp(const char *psz1, const char *psz2, int cch); +char *itoa (int val, char *buf, int radix); + +#ifdef DRAW_PATHS +void LoadArrows() secGob; +void FreeArrows() secGob; +void DrawArrow(DibBitmap *pbm, int x, int y, Direction dir, Side side) secGob; +#endif + +#ifdef BETA_TIMEOUT +bool CheckBetaTimeout() secMisc; +#endif + +struct ClipInfo +{ + int cxClip; + int cx; + int cbRowDst; + int cyClip; +}; + +#ifdef __CPU_68K +extern "C" void DrawTileMap816(byte **ppbMap, int ctx, int cty, byte *pbDst, int cbDstStride, int cxLeftTile, int cyTopTile, int cxRightTile, int cyBottomTile, int ctxInside, int ctyInside) secCode7; +extern "C" void DrawTileMap824(byte **ppbMap, int ctx, int cty, byte *pbDst, int cbDstStride, int cxLeftTile, int cyTopTile, int cxRightTile, int cyBottomTile, int ctxInside, int ctyInside) secCode9; +extern "C" void CopyToScreen4bpp(byte *pbSrc, byte *pbDst, byte *mp8to4, int c) secCode14; +extern "C" void FillShadow68K(byte *pbDst, int cbRowDst, int cx, int cy, byte *aclrMap) secCode14; +extern "C" void Fill68K(byte *pbRow, int cx, int cy, int cbStride, byte bFill) secCode11; +extern "C" void LeftToRightBlt68K(byte *pbSrc, int cxSrcStride, byte *pbDst, int cxDstStride, int cx, int cy) secCode10; +extern "C" void RightToLeftBlt68K(byte *pbSrc, int cxSrcStride, byte *pbDst, int cxDstStride, int cx, int cy) secCode10; +extern "C" dword DecodeAndMix8BitAdpcmChannelsBy4(MixerState *pmxst, bool fZeroSilence) secCode6; +extern "C" void Decode8BitAdpcmTable() secCode6; +extern "C" word Compile868K(byte *pb, ScanData *psd, bool fOdd) secCode8; +extern "C" void DrawDispatch68K(byte *pb, byte *pbSrc, byte *pbDst, int cbReturn, dword *mpscaiclrSide, byte *mpiclriclrShadow) secCode8; +extern "C" void DrawSelection68k(byte *pbDst, int cxDib, int cxSel, int cySel, int cxTab, int cyTab, byte clrSel, int cxHealth, byte clrHealth, byte *mpclrclrShadow, bool fBrackets) secCode14; +extern "C" void DebugBreak() secCode14; +extern "C" void CopyToScreen2bpp(byte *pbSrc, byte *pbDst, byte *mp8to2Low, byte *mp8to2High) secCode14; +extern "C" void CopyToScreen4bpp(byte *pbSrc, byte *pbDst, byte *mp8to4, int c) secCode14; +extern "C" void CopyToScreen8bpp(byte *pbSrc, byte *pbDst, int cx, int cy) secCode14; +extern "C" void FastMemSet(void *pv, long cb, byte b) secCode14; +extern "C" void UpdateScreen824(bool *pfMap, int ctx, int cty, byte *pbSrc, byte *pbDst, int cbStride, int cxLeftTile, int cyTopTile, int cxRightTile, int cyBottomTile, int ctxInside, int ctyInside) secCode10; +extern "C" void UpdateScreen816(bool *pfMap, int ctx, int cty, byte *pbSrc, byte *pbDst, int cbStride, int cxLeftTile, int cyTopTile, int cxRightTile, int cyBottomTile, int ctxInside, int ctyInside) secCode10; +extern "C" void UpdateScreen416(byte *mp8to4, bool *pfMap, int ctx, int cty, byte *pbSrc, byte *pbDst, int cbStride, int cxLeftTile, int cyTopTile, int cxRightTile, int cyBottomTile, int ctxInside, int ctyInside) secCode11; +#endif + +extern "C" word DecompressChunk(byte **ppbCompressed, byte *pbDecompressed, byte *pbCacheEnd, word cbMax) secPackFile; +extern "C" void DecompressToCache(byte *pbCompressed, CacheHandle hc, CompressionHeader *pcoh) secPackFile; + +// Thunks + +void FillShadowThunk(byte *pbDst, int cbRowDst, int cx, int cy, byte *aclrMap) secThunks; +void FillThunk(byte *pbDst, int cx, int cy, int cbStride, byte bFill) secThunks; +void LeftToRightBltThunk(byte *pbSrc, int cxSrcStride, byte *pbDst, int cxDstStride, int cx, int cy) secThunks; +void RightToLeftBltThunk(byte *pbSrc, int cxSrcStride, byte *pbDst, int cxDstStride, int cx, int cy) secThunks; +void DrawTileMapThunk(byte **ppbMap, int ctx, int cty, byte *pbDst, int cbDstStride, int cxLeftTile, int cyTopTile, int cxRightTile, int cyBottomTile, int ctxInside, int ctyInside, int cxTile, int cyTile) secThunks; +word Compile8Thunk(byte *pb, ScanData *psd, bool fOdd) secThunks; +void DrawDispatchThunk(byte *pb, byte *pbSrc, byte *pbDst, int cbReturn, dword *mpscaiclrSide, byte *mpiclriclrShadow) secThunks; + +int YClipToScan(int yClip, int cx, byte*& pop, byte*& pbSrc, word*& psc); +int DrawScan(byte *pbDst, int cx, byte*& pbSrc, byte*& pop, word*& psc, + dword *mpscaiclr, byte *mpiclriclrShadow); + +// ARM stuff + +void InitArmCode() secArmThunks; +void ExitArmCode() secArmThunks; +void DrawTileMapArm(byte **ppbMap, int ctx, int cty, byte *pbDst, int cbDstStride, int cxLeftTile, int cyTopTile, int cxRightTile, int cyBottomTile, int ctxInside, int ctyInside, int cxTile, int cyTile) secArmThunks; +void LeftToRightBltArm(byte *pbSrc, int cxSrcStride, byte *pbDst, int cxDstStride, int cx, int cy) secArmThunks; +void RightToLeftBltArm(byte *pbSrc, int cxSrcStride, byte *pbDst, int cxDstStride, int cx, int cy) secArmThunks; +void memsetArm(byte *pbDst, byte b, dword cb) secArmThunks; +void FillArm(byte *pbDst, int cx, int cy, int cbStride, byte bFill) secArmThunks; +void FillShadowArm(byte *pbDst, int cbRowDst, int cx, int cy, byte *aclrMap) secArmThunks; +word Compile8Arm(byte *pb, ScanData *psd, bool fOdd) secArmThunks; +void DrawDispatchArm(byte *pb, byte *pbSrc, byte *pbDst, int cbReturn, dword *mpscaiclrSide, byte *mpiclriclrShadow) secArmThunks; +extern bool gfArmPresent; + +// Host stuff + +bool HostInit(); +void HostExit(); +void HostOpenUrl(const char *pszUrl); +bool HostGetEvent(Event *pevt, long ctWait = -1) secHost; +void HostServiceGetEvent() secHost; +void HostOutputDebugString(char *pszFormat, ...) secHost; +long HostGetTickCount() secHost; +long HostGetMillisecondCount() secHost; +long HostRunSpeedTests(DibBitmap *pbmSrc) secHost; +dword HostGetCurrentKeyState(dword keyBit) secHost; +void HostMessageBox(TCHAR *pszFormat, ...) secHost; +Display *HostCreateDisplay() secDisplay; +bool HostIsPenDown() secHost; +const char *HostGetMainDataDir() secHost; +void HostSuspendModalLoop(DibBitmap *pbm) secHost; +void HostNotEnoughMemory(bool fStorage, dword cbFree, dword cbNeed) secHost; +bool HostGetOwnerName(char *pszBuff, int cb, bool fShowError) secHost; +bool HostEnumAddonFiles(Enum *penm, char *pszAddonDir, int cbDir, + char *pszAddon, int cb) secHost; +bool HostIsPOSE() secHost; +void HostGetUserName(char *pszBuff, int cbMax) secHost; +SoundDevice *HostOpenSoundDevice() secSoundDevice; +bool HostSoundServiceProc() secSoundDevice; +void HostSleep(dword ct) secHost; + +const int knKeyboardAskDefault = 0; +const int knKeyboardAskURL = 1; + +void HostInitiateAsk(const char *title, int max, const char *def, + int keyboard = knKeyboardAskDefault, bool secure = false); +void HostGetAskString(char *psz, int cb); + +class IChatController; +IChatController *HostGetChatController(); + +void HostInitiateWebView(const char *title, const char *url); +const char *HostGenerateDeviceId(); + +// Date + +struct Date +{ + int nYear; + int nMonth; + int nDay; +}; +int CompareDates(Date *pdate1, Date *pdate2) secMisc; + +void HostGetCurrentDate(Date *pdate) secHost; + +// Silk rects + +#define kircSilkGraffiti 0 +#define kircSilkApps 1 +#define kircSilkMenu 2 +#define kircSilkCalc 3 +#define kircSilkFind 4 + +void HostGetSilkRect(int irc, Rect *prc) secHost; + +// Host file IO + +const word kfOfRead = 0x0001; // same as "rb" +const word kfOfWrite = 0x0002; // same as "wb" + +FileHandle HostOpenFile(const char *pszFilename, word wf) secHost; +void HostCloseFile(FileHandle hf) secHost; +dword HostWriteFile(FileHandle hf, void *pv, dword cb) secHost; +dword HostReadFile(FileHandle hf, void *pv, dword cb) secHost; + +// Save game + +class Stream +{ +public: + virtual ~Stream() {} + virtual void Close() = 0; + virtual dword Read(void *pv, dword cb) = 0; + virtual dword Write(void *pv, dword cb) = 0; + virtual bool IsSuccess() = 0; + + // Helpers + + void ReadString(char *psz, int cb) secStream; + void ReadBytesRLE(byte *pb, int cb) secStream; + inline byte ReadByte() + { + byte b = 0; + Read(&b, sizeof(b)); + return b; + } + inline word ReadWord() + { + word w = 0; + Read(&w, sizeof(w)); + return w; + } + inline dword ReadDword() + { + dword dw = 0; + Read(&dw, sizeof(dw)); + return dw; + } + + void WriteString(char *psz) secStream; + void WriteBytesRLE(byte *pb, int cb) secStream; + inline void WriteByte(byte b) + { + Write(&b, sizeof(b)); + } + inline void WriteWord(word w) + { + Write(&w, sizeof(w)); + } + inline void WriteDword(dword dw) + { + Write(&dw, sizeof(dw)); + } + +private: + byte *FindRLERepeat(byte *pbStart, byte *pbMax, int cbMin) secStream; + void WriteRLEChunk(byte *pb, int cb, bool fRepeat) secStream; +}; + +const int knGameReinitializeSave = -3; +const int knGameAutosave = 10; + +#define kszTempName "htsavetemp" +int HostGetSaveGameCount() secHost; +bool HostGetSaveGameName(int nGame, char *psz, int cb, Date *pdate, int *pnHours24, int *pnMinutes, int *pnSeconds) secHost; +Stream *HostNewSaveGameStream(int nGame, char *pszName) secHost; +Stream *HostOpenSaveGameStream(int nGame, bool fDelete = false) secHost; +Stream *PickLoadGameStream() secLoadSave; +bool PickSaveGameStream(Stream **ppstm) secLoadSave; +void DeleteStaleSaveGames() secLoadSave; +bool CheckSaveGameVersion(char *pszVersion, byte bPlatform) secLoadSave; +bool HostDeleteSaveGame(char *psz, int nGame) secLoadSave; + +// Debug stuff + +#if defined(MP_DEBUG) && defined(INCL_TRACE) +#define MpTrace ::wi::HostOutputDebugString +//#define MpTrace(fmt, args...) LOG() << base::Log::Format(fmt, ## args) +#else +#define MpTrace 1 ? (void)0 : ::wi::HostOutputDebugString +#endif + +#ifdef INCL_TRACE +#define Trace ::wi::HostOutputDebugString +//#define Trace(fmt, args...) LOG() << base::Log::Format(fmt, ## args) +#else +#define Trace 1 ? (void)0 : ::wi::HostOutputDebugString +#endif + +#ifdef STATUS_LINE +void Status(const char *psz) secMisc; +#else +#define Status(psz) +#endif + +#ifdef DEBUG_HELPERS +void InitDebugHelpers(); +void ExitDebugHelpers(); +#endif + +#if 0 +// Don't need this now that we discovered CE's COREDLL takes care of this. +// However, if we ever build a Windows desktop version of Wc14Transport.dll +// we will want to do this. + +// Transport DLLs must call back to the WI .exe for memory management +// This allows us to continue our practice of having the Transport and +// its Connections perform 'new' operations and leave it to the rest +// of the game to 'delete' them. + +#if defined(CE) && defined(DLL) +extern TransportHost *gptrah; + +inline void* operator new(size_t size) +{ + if (size == 0) + size = 1; + return gptrah->New(size); +} + +inline void operator delete(void* ptr) +{ + if (ptr != NULL) + gptrah->Delete(ptr); +} +#endif +#endif + +#ifdef IPHONE +#pragma options align=reset +#endif + +} // namespace wi + +#endif // __HT_H__ diff --git a/game/htdata832.pdb b/game/htdata832.pdb new file mode 100755 index 0000000..2e2e7ce Binary files /dev/null and b/game/htdata832.pdb differ diff --git a/game/htsfx.pdb b/game/htsfx.pdb new file mode 100644 index 0000000..00a55ec Binary files /dev/null and b/game/htsfx.pdb differ diff --git a/game/httpindexloader.cpp b/game/httpindexloader.cpp new file mode 100644 index 0000000..b232b99 --- /dev/null +++ b/game/httpindexloader.cpp @@ -0,0 +1,344 @@ +#include "game/ht.h" +#include "game/httpindexloader.h" +#include "game/serviceurls.h" +#include + +namespace wi { + +HttpIndexLoader::HttpIndexLoader(HttpService *service, PackManager *ppackm) { + service_ = service; + s_ppackm_ = ppackm; + req_ = NULL; + callback_ = NULL; + ctx_ = NULL; + Reset(); +} + +HttpIndexLoader::~HttpIndexLoader() { + Reset(); +} + +void HttpIndexLoader::MergeInstalled(PackManager *ppackm) { + // Merge in packs that are installed but not in the index. This way, + // they show up in the mission list, can be deleted, etc. + // Enumerate installed packs and do a lookup. To make this fast, + // first create a map. + + std::map map; + for (int i = 0; i < (int)index_.size(); i++) { + IndexEntry *entry = &index_[i]; + map.insert(std::map::value_type(entry->packid.id, + entry)); + } + + // Enumerate installed packs + + Enum enm; + PackId packid; + while (ppackm->EnumPacks(&enm, &packid)) { + // If in the index, move on. + std::map::const_iterator it = map.find(packid.id); + if (it != map.end()) { + continue; + } + + // Add it to the index. These packs don't have index info nor + // necessarily PackInfo, so it has to be accurately generated. + + AddIndexEntry(&packid, false); + } +} + +void HttpIndexLoader::AddIndexEntry(const PackId *ppackid, bool fDupCheck) { + if (fDupCheck) { + for (int i = 0; i < (int)index_.size(); i++) { + IndexEntry *entry = &index_[i]; + if (memcmp(ppackid, &entry->packid, sizeof(*ppackid)) == 0) { + return; + } + } + } + + MissionList *pml = CreateMissionList(ppackid, kmltAll); + if (pml == NULL) { + return; + } + if (pml->GetCount() == 0) { + delete pml; + return; + } + + int cPlayersMin = 999; + int cPlayersMax = -1; + MissionDescription md; + for (int i = pml->GetCount() - 1; i >= 0; i--) { + if (!pml->GetMissionDescription(i, &md)) { + delete pml; + return; + } + if (md.cPlayersMin < cPlayersMin) { + cPlayersMin = md.cPlayersMin; + } + if (md.cPlayersMax > cPlayersMax) { + cPlayersMax = md.cPlayersMax; + } + } + + IndexEntry entry; + entry.packid = *ppackid; + entry.title = md.szPackName; + entry.cMissions = pml->GetCount(); + entry.cPlayersMin = cPlayersMin; + entry.cPlayersMax = cPlayersMax; + entry.inIndex = false; + index_.push_back(entry); + delete pml; + sort_ = SORT_UNSORTED; +} + +bool HttpIndexLoader::OnRemoved(const PackId *ppackid) { + // Find the pack in the list. If it's not part of the original index, + // remove it. + + Index::iterator it = index_.begin(); + for (; it != index_.end(); it++) { + if (it->packid.id != ppackid->id) { + continue; + } + if (memcmp(&(it->packid), ppackid, sizeof(it->packid)) != 0) { + continue; + } + if (it->inIndex) { + break; + } + index_.erase(it); + return true; + } + return false; +} + +void HttpIndexLoader::AddFakeEntry(const PackId *ppackid, const char *title, + int cPlayersMin, int cPlayersMax, int cMissions) { + IndexEntry entry; + entry.packid = *ppackid; + entry.title = title; + entry.cPlayersMin = cPlayersMin; + entry.cPlayersMax = cPlayersMax; + entry.cMissions = cMissions; + entry.inIndex = false; + index_.push_back(entry); + sort_ = SORT_UNSORTED; +} + +bool HttpIndexLoader::Start(void *ctx, ProgressCallback *callback) { + if (service_ == NULL) { + return false; + } + + // Cancel any ongoing requests + Reset(); + + // Start a new request + ctx_ = ctx; + callback_ = callback; + req_ = service_->NewRequest(this); + if (req_ == NULL) { + return false; + } + SetServiceUrl(req_); + + // Start the json builder. Tell it to call back with array items. + builder_.Start(this); + + // Submit the request. This will asynchronously call back. + service_->SubmitRequest(req_); + return true; +} + +void HttpIndexLoader::SetServiceUrl(HttpRequest *req) { + req->SetURL(base::Format::ToString("%s?c=%d&v=%d", kszIndexUrl, + kdwClientID, knVersionSimulation)); +} + +void HttpIndexLoader::Reset() { + code_ = 0; + error_ = false; + if (req_ != NULL) { + if (service_ != NULL) { + service_->ReleaseRequest(req_); + } + req_ = NULL; + } + builder_.Reset(); + index_.clear(); + sort_ = SORT_UNSORTED; + cbTotal_ = 0; + cbLength_ = 0; +} + +void HttpIndexLoader::OnReceivedResponse(HttpRequest *preq, int code, + const Map *pheaders) { + code_ = code; + if (code >= 400) { + // Http error + error_ = true; + if (callback_ != NULL) { + callback_->OnError(ctx_, + base::Format::ToString("Server returned error %d", code)); + } + return; + } + if (code >= 200 && code < 300) { + // Success! Get Content-Length and call back + + cbLength_ = -1; + char szLength[32]; + if (pheaders->GetValue("Content-Length", szLength, + sizeof(szLength))) { + base::Format::ToInteger(szLength, 10, &cbLength_); + } + if (callback_ != NULL) { + callback_->OnBegin(ctx_, cbLength_); + } + cbTotal_ = 0; + return; + } + // Ignore other status codes. If it's a redirect, OnReceivedResponse + // will get called again. + return; +} + +void HttpIndexLoader::OnReceivedData(HttpRequest *preq, + const base::ByteBuffer *pbb) { + if (error_) { + return; + } + if (code_ >= 200 && code_ < 300) { + if (!builder_.Update((const char *)pbb->Data(), pbb->Length())) { + if (callback_ != NULL) { + error_ = true; + callback_->OnError(ctx_, "Error parsing Mission Pack list"); + } + } else { + cbTotal_ += pbb->Length(); + if (callback_ != NULL) { + callback_->OnProgress(ctx_, cbTotal_, cbLength_); + } + } + } +} + +void HttpIndexLoader::OnFinishedLoading(HttpRequest *preq) { + if (error_) { + return; + } + if (code_ >= 200 && code_ < 300) { + if (callback_ != NULL) { + callback_->OnFinished(ctx_); + } + } +} + +void HttpIndexLoader::OnError(HttpRequest *preq, const char *pszError) { + if (error_) { + return; + } + error_ = true; + if (callback_ != NULL) { + callback_->OnError(ctx_, pszError); + } +} + +void HttpIndexLoader::OnObject(json::JsonObject *obj) { + if (error_) { + delete obj; + return; + } + IndexLoader::OnObject(obj); +} + +void HttpIndexLoader::OnParseError() { + error_ = true; + if (callback_ != NULL) { + callback_->OnError(ctx_, "Error parsing Mission Pack list"); + } +} + +bool InstallAscendingSort(const IndexEntry& e1, const IndexEntry& e2) { + PackManager *s_ppackm = HttpIndexLoader::s_ppackm_; + return s_ppackm->IsInstalled(&e1.packid) < + s_ppackm->IsInstalled(&e2.packid); +} + +bool InstallDescendingSort(const IndexEntry& e1, const IndexEntry& e2) { + PackManager *s_ppackm = HttpIndexLoader::s_ppackm_; + return s_ppackm->IsInstalled(&e1.packid) > + s_ppackm->IsInstalled(&e2.packid); +} + +bool TitleAscendingSort(const IndexEntry& e1, const IndexEntry& e2) { + return stricmp(e1.title.c_str(), e2.title.c_str()) < 0; +} + +bool TitleDescendingSort(const IndexEntry& e1, const IndexEntry& e2) { + return stricmp(e1.title.c_str(), e2.title.c_str()) > 0; +} + +bool PlayersAscendingSort(const IndexEntry& e1, const IndexEntry& e2) { + if (e1.cPlayersMin < e2.cPlayersMin) { + return true; + } + if (e1.cPlayersMin == e2.cPlayersMin) { + if (e1.cPlayersMax < e2.cPlayersMax) { + return true; + } + } + return false; +} + +bool PlayersDescendingSort(const IndexEntry& e1, const IndexEntry& e2) { + if (e1.cPlayersMin > e2.cPlayersMin) { + return true; + } + if (e1.cPlayersMin == e2.cPlayersMin) { + if (e1.cPlayersMax > e2.cPlayersMax) { + return true; + } + } + return false; +} + +bool MissionsAscendingSort(const IndexEntry& e1, + const IndexEntry& e2) { + return e1.cMissions < e2.cMissions; +} + +bool MissionsDescendingSort(const IndexEntry& e1, + const IndexEntry& e2) { + return e1.cMissions > e2.cMissions; +} + +typedef bool (* SortFunction)(const IndexEntry& e1, const IndexEntry& e2); + +static SortFunction s_apfnSort[] = { + NULL, + InstallAscendingSort, + InstallDescendingSort, + TitleAscendingSort, + TitleDescendingSort, + PlayersAscendingSort, + PlayersDescendingSort, + MissionsAscendingSort, + MissionsDescendingSort +}; + +PackManager *HttpIndexLoader::s_ppackm_; +void HttpIndexLoader::Sort(SortType sort) { + if (sort == sort_) { + return; + } + sort_ = sort; + std::stable_sort(index_.begin(), index_.end(), s_apfnSort[sort_]); +} + +} // namespace wi diff --git a/game/httpindexloader.h b/game/httpindexloader.h new file mode 100644 index 0000000..1694870 --- /dev/null +++ b/game/httpindexloader.h @@ -0,0 +1,77 @@ +#ifndef __HTTPINDEXLOADER_H__ +#define __HTTPINDEXLOADER_H__ + +#include "inc/basictypes.h" +#include "base/format.h" +#include "mpshared/indexloader.h" +#include "mpshared/packmanager.h" +#include "mpshared/mpht.h" +#include "game/map.h" +#include "game/httpservice.h" +#include "game/progresscallback.h" + +namespace wi { + +class HttpIndexLoader : public IndexLoader, HttpResponseHandler { +public: + HttpIndexLoader(HttpService *service, PackManager *ppackm); + ~HttpIndexLoader(); + + bool Start(void *ctx, ProgressCallback *callback); + void Reset(); + void AddIndexEntry(const PackId *ppackid, bool fDupCheck = true); + void AddFakeEntry(const PackId *ppackid, const char *title, + int cPlayersMin, int cPlayersMax, int cMissions); + void MergeInstalled(PackManager *ppackm); + bool OnRemoved(const PackId *ppackid); + + enum SortType { + SORT_UNSORTED, + SORT_INSTALLEDASCENDING, + SORT_INSTALLEDDESCENDING, + SORT_TITLEASCENDING, + SORT_TITLEDESCENDING, + SORT_PLAYERSASCENDING, + SORT_PLAYERSDESCENDING, + SORT_MISSIONSASCENDING, + SORT_MISSIONSDESCENDING + }; + void Sort(SortType sort); + +private: + void SetServiceUrl(HttpRequest *req); + virtual void OnParseError(); + + // HttpResponseHandler methods + virtual void OnReceivedResponse(HttpRequest *preq, int code, + const Map *pheaders); + virtual void OnReceivedData(HttpRequest *preq, + const base::ByteBuffer *pbb); + virtual void OnFinishedLoading(HttpRequest *preq); + virtual void OnError(HttpRequest *preq, const char *pszError); + + // ArrayItemsCallback + virtual void OnObject(json::JsonObject *obj); + + HttpService *service_; + void *ctx_; + ProgressCallback *callback_; + HttpRequest *req_; + json::JsonBuilder builder_; + int code_; + bool error_; + int cbTotal_; + int cbLength_; + SortType sort_; + static PackManager *s_ppackm_; + + friend bool InstallAscendingSort(const IndexEntry& e1, + const IndexEntry& e2); + friend bool InstallDescendingSort(const IndexEntry& e1, + const IndexEntry& e2); +}; + +} // namespace wi + +#endif // __HTTPINDEXLOADER_H__ + diff --git a/game/httppackinfomanager.cpp b/game/httppackinfomanager.cpp new file mode 100644 index 0000000..7f4892a --- /dev/null +++ b/game/httppackinfomanager.cpp @@ -0,0 +1,211 @@ +#include "game/httppackinfomanager.h" +#include "game/serviceurls.h" +#include "base/format.h" +#include + +namespace wi { + +HttpPackInfoManager::HttpPackInfoManager(HttpService *service, + const char *cachedir, const char *tempdir) : PackInfoManager(cachedir), + service_(service) { + tempdir_ = tempdir; + req_ = NULL; + callback_ = NULL; + ctx_ = NULL; + tempfile_ = NULL; + Reset(); +} + +HttpPackInfoManager::~HttpPackInfoManager() { + Reset(); +} + +bool HttpPackInfoManager::Start(const PackId *ppackid, void *ctx, + ProgressCallback *callback) { + if (service_ == NULL) { + return false; + } + + // Initialize + Reset(); + temppackid_ = *ppackid; + ctx_ = ctx; + callback_ = callback; + + // Create a temp filename for the temp file + char szTemp[256]; + strncpyz(szTemp, + base::Format::ToString("%s/packinfodl.XXXXX", tempdir_.c_str()), + sizeof(szTemp)); + if (mktemp(szTemp) == NULL) { + return false; + } + tempfilename_ = szTemp; + + // Open the temp file that will receive the bytes + tempfile_ = fopen(tempfilename_.c_str(), "w+b"); + if (tempfile_ == NULL) { + return false; + } + + // Build the request + req_ = service_->NewRequest(this); + if (req_ == NULL) { + return false; + } + SetServiceUrl(req_); + + // Submit the request. This will asynchronously call back. + service_->SubmitRequest(req_); + return true; + +} + +void HttpPackInfoManager::SetServiceUrl(HttpRequest *req) { + std::string hash(base::Format::ToHex(temppackid_.hash, + sizeof(temppackid_.hash))); + req->SetURL(base::Format::ToString("%s/%08x-%s?c=%d&v=%d", + kszPackInfoUrl, temppackid_.id, hash.c_str(), + kdwClientID, knVersionSimulation)); +} + +void HttpPackInfoManager::Reset() { + callback_ = NULL; + ctx_ = NULL; + code_ = 0; + error_ = false; + if (req_ != NULL) { + if (service_ != NULL) { + service_->ReleaseRequest(req_); + } + req_ = NULL; + } + cbTotal_ = 0; + cbLength_ = 0; + if (tempfile_ != NULL) { + fclose(tempfile_); + tempfile_ = NULL; + unlink(tempfilename_.c_str()); + } +} + +void HttpPackInfoManager::OnReceivedResponse(HttpRequest *preq, int code, + const Map *pheaders) { + code_ = code; + if (code >= 400) { + // Http error + error_ = true; + if (callback_ != NULL) { + callback_->OnError(ctx_, + base::Format::ToString("Server returned error %d", code)); + } + return; + } + if (code >= 200 && code < 300) { + // Success! Get Content-Length and call back + + cbLength_ = -1; + char szLength[32]; + if (pheaders->GetValue("Content-Length", szLength, + sizeof(szLength))) { + base::Format::ToInteger(szLength, 10, &cbLength_); + } + if (callback_ != NULL) { + callback_->OnBegin(ctx_, cbLength_); + } + cbTotal_ = 0; + return; + } + // Ignore other status codes. If it's a redirect, OnReceivedResponse + // will get called again. + return; +} + +void HttpPackInfoManager::OnReceivedData(HttpRequest *preq, + const base::ByteBuffer *pbb) { + if (error_) { + return; + } + if (code_ >= 200 && code_ < 300) { + size_t cb = fwrite(pbb->Data(), 1, pbb->Length(), tempfile_); + if (cb != pbb->Length()) { + if (callback_ != NULL) { + error_ = true; + callback_->OnError(ctx_, "Error saving Mission Pack info!"); + } + } else { + cbTotal_ += pbb->Length(); + if (callback_ != NULL) { + callback_->OnProgress(ctx_, cbTotal_, cbLength_); + } + } + } +} + +void HttpPackInfoManager::OnFinishedLoading(HttpRequest *preq) { + if (error_) { + Reset(); + return; + } + if (code_ >= 200 && code_ < 300) { + if (FinishInstall()) { + if (callback_ != NULL) { + callback_->OnFinished(ctx_); + } + } else { + if (callback_ != NULL) { + error_ = true; + callback_->OnError(ctx_, "Error parsing Mission Pack info!"); + } + } + } + Reset(); +} + +void HttpPackInfoManager::OnError(HttpRequest *preq, const char *pszError) { + if (error_) { + return; + } + error_ = true; + if (callback_ != NULL) { + callback_->OnError(ctx_, pszError); + } +} + +bool HttpPackInfoManager::FinishInstall() { + if (tempfile_ == NULL) { + return false; + } + + // Build a json object to make sure it can be parsed + + json::JsonMap *map = LoadInfoMap(tempfile_); + fclose(tempfile_); + tempfile_ = NULL; + if (map == NULL) { + unlink(tempfilename_.c_str()); + return false; + } + + // Check that the id and hash matches + PackId packid; + if (!GetPackId(map, &packid) || + packid.id != temppackid_.id || + memcmp(packid.hash, temppackid_.hash, 16) != 0) { + delete map; + unlink(tempfilename_.c_str()); + return false; + } + delete map; + + // Move from temp directory to cache directory. Don't bother to delete + // old info's with the same id (but different hash). + + if (rename(tempfilename_.c_str(), GetInfoFilename(&temppackid_)) < 0) { + unlink(tempfilename_.c_str()); + return false; + } + return true; +} + +} // namespace wi diff --git a/game/httppackinfomanager.h b/game/httppackinfomanager.h new file mode 100644 index 0000000..ca43a34 --- /dev/null +++ b/game/httppackinfomanager.h @@ -0,0 +1,52 @@ +#ifndef __HTTPPACKINFOMANAGER_H__ +#define __HTTPPACKINFOMANAGER_H__ + +#include "inc/basictypes.h" +#include "mpshared/packinfomanager.h" +#include "mpshared/mpht.h" +#include "game/httpservice.h" +#include "game/httprequest.h" +#include "game/progresscallback.h" +#include + +namespace wi { + +class HttpPackInfoManager : public PackInfoManager, HttpResponseHandler { +public: + HttpPackInfoManager(HttpService *service, const char *cachedir, + const char *tempdir); + ~HttpPackInfoManager(); + + bool Start(const PackId *ppackid, void *ctx, ProgressCallback *callback); + void Reset(); + +private: + void SetServiceUrl(HttpRequest *req); + bool FinishInstall(); + + // HttpResponseHandler methods + virtual void OnReceivedResponse(HttpRequest *preq, int code, + const Map *pheaders); + virtual void OnReceivedData(HttpRequest *preq, + const base::ByteBuffer *pbb); + virtual void OnFinishedLoading(HttpRequest *preq); + virtual void OnError(HttpRequest *preq, const char *pszError); + + HttpService *service_; + void *ctx_; + ProgressCallback *callback_; + HttpRequest *req_; + int code_; + bool error_; + int cbTotal_; + int cbLength_; + std::string tempfilename_; + FILE *tempfile_; + PackId temppackid_; + std::string tempdir_; +}; +extern HttpPackInfoManager *gppim; + +} // namespace wi + +#endif // __HTTPPACKINFOMANAGER_H__ diff --git a/game/httppackmanager.cpp b/game/httppackmanager.cpp new file mode 100644 index 0000000..dd0ddb6 --- /dev/null +++ b/game/httppackmanager.cpp @@ -0,0 +1,307 @@ +#include "game/httppackmanager.h" +#include "game/serviceurls.h" +#include "mpshared/mpht.h" +#include "base/format.h" +#include "base/md5.h" +#include +#include +#include +#include + +namespace wi { + +HttpPackManager::HttpPackManager(HttpService *service, const char *cachedir, + const char *tempdir) : PackManager(cachedir), service_(service) { + haveHash_ = false; + ppackidUpdate_ = NULL; + tempdir_ = tempdir; + req_ = NULL; + tempfile_ = NULL; + Cancel(); +} + +HttpPackManager::~HttpPackManager() { + Cancel(); +} + +bool HttpPackManager::Install(const PackId *ppackid, void *ctx, + ProgressCallback *callback) { + // No service? + if (service_ == NULL) { + return false; + } + + // Cancel any ongoing requests + Cancel(); + + // Remember what we're downloading + temppackid_ = *ppackid; + ppackidUpdate_ = NULL; + haveHash_ = true; + + // Create a temp filename for the temp file + char szTemp[256]; + strncpyz(szTemp, + base::Format::ToString("%s/packdl.XXXXX", tempdir_.c_str()), + sizeof(szTemp)); + if (mktemp(szTemp) == NULL) { + return false; + } + tempfilename_ = szTemp; + + // Open the temp file that will receive the bytes + tempfile_ = fopen(tempfilename_.c_str(), "w+b"); + if (tempfile_ == NULL) { + return false; + } + + + // Start a new request + ctx_ = ctx; + callback_ = callback; + req_ = service_->NewRequest(this); + if (req_ == NULL) { + fclose(tempfile_); + return false; + } + SetServiceUrl(req_); + + // Submit the request. This will asynchronously call back. + service_->SubmitRequest(req_); + return true; +} + +bool HttpPackManager::Install(const char *pszURL, PackId *ppackidUpdate, + void *ctx, ProgressCallback *callback) { + // No service? + if (service_ == NULL) { + return false; + } + + // Cancel any ongoing requests + Cancel(); + + // Remember what we're downloading - an url. Use a hash of the URL + // for the pack id. We don't know the pack hash yet. + memset(&temppackid_, 0, sizeof(temppackid_)); + MD5_CTX md5; + MD5Init(&md5); + MD5Update(&md5, (const byte *)pszURL, strlen(pszURL)); + byte hashURL[16]; + MD5Final(hashURL, &md5); + temppackid_.id = *(dword *)(&hashURL[0]) ^ *(dword *)(&hashURL[4]) ^ + *(dword *)(&hashURL[8]) ^ *(dword *)(&hashURL[12]); + + // Since this is a custom pack, we don't have the pack hash yet. + // Remember this so it can be calculated later. + ppackidUpdate_ = ppackidUpdate; + haveHash_ = false; + + // Create a temp filename for the temp file + char szTemp[256]; + strncpyz(szTemp, + base::Format::ToString("%s/packdl.XXXXX", tempdir_.c_str()), + sizeof(szTemp)); + if (mktemp(szTemp) == NULL) { + return false; + } + tempfilename_ = szTemp; + + // Open the temp file that will receive the bytes + tempfile_ = fopen(tempfilename_.c_str(), "w+b"); + if (tempfile_ == NULL) { + return false; + } + + // Start a new request using the custom URL + ctx_ = ctx; + callback_ = callback; + req_ = service_->NewRequest(this); + if (req_ == NULL) { + fclose(tempfile_); + return false; + } + req_->SetURL(pszURL); + + // Submit the request. This will asynchronously call back. + service_->SubmitRequest(req_); + return true; +} + +void HttpPackManager::SetServiceUrl(HttpRequest *req) { + std::string hash(base::Format::ToHex(temppackid_.hash, + sizeof(temppackid_.hash))); + req->SetURL(base::Format::ToString("%s/%08x-%s?c=%d&v=%d", + kszPackUrl, temppackid_.id, hash.c_str(), + kdwClientID, knVersionSimulation)); +} + +void HttpPackManager::Cancel() { + ppackidUpdate_ = NULL; + callback_ = NULL; + ctx_ = NULL; + code_ = 0; + error_ = false; + if (req_ != NULL) { + if (service_ != NULL) { + service_->ReleaseRequest(req_); + } + req_ = NULL; + } + cbTotal_ = 0; + cbLength_ = 0; + if (tempfile_ != NULL) { + fclose(tempfile_); + tempfile_ = NULL; + unlink(tempfilename_.c_str()); + } +} + +void HttpPackManager::OnReceivedResponse(HttpRequest *preq, int code, + const Map *pheaders) { + code_ = code; + if (code >= 400) { + // Http error + error_ = true; + if (callback_ != NULL) { + callback_->OnError(ctx_, + base::Format::ToString("Server returned error %d", code)); + } + return; + } + if (code >= 200 && code < 300) { + // Success! Get Content-Length and call back + + cbLength_ = -1; + char szLength[32]; + if (pheaders->GetValue("Content-Length", szLength, + sizeof(szLength))) { + base::Format::ToInteger(szLength, 10, &cbLength_); + } + if (callback_ != NULL) { + callback_->OnBegin(ctx_, cbLength_); + } + cbTotal_ = 0; + return; + } + // Ignore other status codes. If it's a redirect, OnReceivedResponse + // will get called again. + return; +} + +void HttpPackManager::OnReceivedData(HttpRequest *preq, + const base::ByteBuffer *pbb) { + if (error_) { + return; + } + if (code_ >= 200 && code_ < 300) { + size_t cb = fwrite(pbb->Data(), 1, pbb->Length(), tempfile_); + if (cb != pbb->Length()) { + if (callback_ != NULL) { + error_ = true; + callback_->OnError(ctx_, + "Error saving Mission Pack. Out of space?"); + } + } else { + cbTotal_ += pbb->Length(); + if (callback_ != NULL) { + callback_->OnProgress(ctx_, cbTotal_, cbLength_); + } + } + } +} + +void HttpPackManager::OnFinishedLoading(HttpRequest *preq) { + if (error_) { + Cancel(); + return; + } + if (code_ >= 200 && code_ < 300) { + if (FinishInstall()) { + if (callback_ != NULL) { + callback_->OnFinished(ctx_); + } + } else { + if (callback_ != NULL) { + error_ = true; + callback_->OnError(ctx_, "Downloaded mission pack is invalid!"); + } + } + } + Cancel(); +} + +void HttpPackManager::OnError(HttpRequest *preq, const char *pszError) { + if (error_) { + return; + } + error_ = true; + if (callback_ != NULL) { + callback_->OnError(ctx_, pszError); + } +} + +bool HttpPackManager::FinishInstall() { + if (tempfile_ == NULL) { + return false; + } + + // Make sure it is an addon pack + char szType[5]; + szType[4] = 0; + fseek(tempfile_, 0x3c, SEEK_SET); + fread(szType, 4, 1, tempfile_); + if (strcmp(szType, kszTypeAddon) != 0) { + fclose(tempfile_); + tempfile_ = NULL; + unlink(tempfilename_.c_str()); + return false; + } + + // Calculate the hash + fseek(tempfile_, 0, SEEK_SET); + MD5_CTX ctx; + MD5Init(&ctx); + while (true) { + byte ab[256]; + size_t cb = fread(ab, 1, sizeof(ab), tempfile_); + if (cb == 0) { + break; + } + MD5Update(&ctx, ab, cb); + } + byte hash[16]; + MD5Final(hash, &ctx); + fclose(tempfile_); + tempfile_ = NULL; + + // Check that the hash matches, if we have it, otherwise use this + // hash! + if (haveHash_) { + if (memcmp(hash, temppackid_.hash, sizeof(hash)) != 0) { + unlink(tempfilename_.c_str()); + return false; + } + } else { + memcpy(temppackid_.hash, hash, sizeof(temppackid_.hash)); + if (ppackidUpdate_ != NULL) { + *ppackidUpdate_ = temppackid_; + } + } + + // Hash matches. Now remove the pack with the same id, if there is one. + Remove(&temppackid_, false); + + // Install the pack into the cache. This better not fail since + // the old pack has been removed already. + if (rename(tempfilename_.c_str(), GetPackFilepath(&temppackid_)) < 0) { + unlink(tempfilename_.c_str()); + return false; + } + + // Add this to the map + map_.insert(PackMap::value_type(temppackid_.id, temppackid_)); + return true; +} + +} // namespace wi diff --git a/game/httppackmanager.h b/game/httppackmanager.h new file mode 100644 index 0000000..a464486 --- /dev/null +++ b/game/httppackmanager.h @@ -0,0 +1,54 @@ +#ifndef __HTTPPACKMANAGER_H__ +#define __HTTPPACKMANAGER_H__ + +#include "mpshared/packmanager.h" +#include "game/httpservice.h" +#include "game/progresscallback.h" +#include + +namespace wi { + +class HttpPackManager : public PackManager, HttpResponseHandler { +public: + HttpPackManager(HttpService *service, const char *cachedir, + const char *tempdir); + ~HttpPackManager(); + + bool Install(const PackId *ppackid, void *ctx, ProgressCallback *callback); + bool Install(const char *pszURL, PackId *ppackid, void *ctx, + ProgressCallback *callback); + void Cancel(); + +private: + void SetServiceUrl(HttpRequest *req); + bool FinishInstall(); + + // HttpResponseHandler methods + virtual void OnReceivedResponse(HttpRequest *preq, int code, + const Map *pheaders); + virtual void OnReceivedData(HttpRequest *preq, + const base::ByteBuffer *pbb); + virtual void OnFinishedLoading(HttpRequest *preq); + virtual void OnError(HttpRequest *preq, const char *pszError); + + HttpService *service_; + void *ctx_; + ProgressCallback *callback_; + HttpRequest *req_; + int code_; + bool error_; + int cbTotal_; + int cbLength_; + std::string tempfilename_; + FILE *tempfile_; + PackId temppackid_; + PackId *ppackidUpdate_; + std::string tempdir_; + bool haveHash_; +}; +extern HttpPackManager *gppackm; + +} // namespace wi + +#endif // __HTTPPACKMANAGER_H__ + diff --git a/game/httprequest.cpp b/game/httprequest.cpp new file mode 100644 index 0000000..55bcb5a --- /dev/null +++ b/game/httprequest.cpp @@ -0,0 +1,56 @@ +#include "game/httprequest.h" + +namespace wi { + +HttpRequest::HttpRequest() { + method_ = "GET"; + timeout_ = 30; + pbb_ = NULL; +} + +HttpRequest::~HttpRequest() { + delete pbb_; +} + +void HttpRequest::SetURL(const char *pszUrl) { + url_ = pszUrl; +} + +void HttpRequest::GetURL(char *pszUrl, int cb) { + strncpyz(pszUrl, url_.c_str(), cb); +} + +void HttpRequest::SetMethod(const char *pszMethod) { + method_ = pszMethod; +} + +void HttpRequest::GetMethod(char *pszMethod, int cb) { + strncpyz(pszMethod, method_.c_str(), cb); +} + +void HttpRequest::SetBody(base::ByteBuffer *pbb) { + delete pbb_; + pbb_ = pbb; +} + +base::ByteBuffer *HttpRequest::GetBody() { + return pbb_; +} + +void HttpRequest::SetTimeout(int cSeconds) { + timeout_ = cSeconds; +} + +int HttpRequest::GetTimeout() { + return timeout_; +} + +void HttpRequest::SetHeaders(const Map *pheaders) { + headers_ = *pheaders; +} + +const Map *HttpRequest::GetHeaders() { + return &headers_; +} + +} // namespace wi diff --git a/game/httprequest.h b/game/httprequest.h new file mode 100644 index 0000000..17e9ad6 --- /dev/null +++ b/game/httprequest.h @@ -0,0 +1,37 @@ +#ifndef __HTTPREQUEST_H__ +#define __HTTPREQUEST_H__ + +#include "inc/basictypes.h" +#include "game/map.h" +#include "base/bytebuffer.h" +#include + +namespace wi { + +class HttpRequest { +public: + HttpRequest(); + virtual ~HttpRequest(); + + void SetURL(const char *pszUrl); + void GetURL(char *pszUrl, int cb); + void SetMethod(const char *pszMethod); + void GetMethod(char *pszMethod, int cb); + void SetBody(base::ByteBuffer *pbb); + base::ByteBuffer *GetBody(); + void SetTimeout(int cSeconds); + int GetTimeout(); + void SetHeaders(const Map *pheaders); + const Map *GetHeaders(); + +protected: + std::string url_; + std::string method_; + base::ByteBuffer *pbb_; + int timeout_; + Map headers_; +}; + +} // namespace wi + +#endif // __HTTPREQUEST_H__ diff --git a/game/httpservice.h b/game/httpservice.h new file mode 100644 index 0000000..2b807eb --- /dev/null +++ b/game/httpservice.h @@ -0,0 +1,32 @@ +#ifndef __HTTPSERVICE_H__ +#define __HTTPSERVICE_H__ + +#include "inc/basictypes.h" +#include "game/httprequest.h" +#include "game/map.h" +#include "base/bytebuffer.h" + +namespace wi { + +class HttpResponseHandler { +public: + virtual void OnReceivedResponse(HttpRequest *preq, int code, + const Map *pheaders) = 0; + virtual void OnReceivedData(HttpRequest *preq, + const base::ByteBuffer *pbb) = 0; + virtual void OnFinishedLoading(HttpRequest *preq) = 0; + virtual void OnError(HttpRequest *preq, const char *pszError) = 0; +}; + +class HttpService { +public: + virtual ~HttpService() {} + virtual HttpRequest *NewRequest(HttpResponseHandler *phandler) = 0; + virtual void SubmitRequest(HttpRequest *preq) = 0; + virtual void ReleaseRequest(HttpRequest *preq) = 0; +}; +extern HttpService *gphttp; + +} // namespace wi + +#endif // __HTTPSERVICE_H__ diff --git a/game/iphone/Info.plist b/game/iphone/Info.plist new file mode 100644 index 0000000..a60d89f --- /dev/null +++ b/game/iphone/Info.plist @@ -0,0 +1,36 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + Hostile Takeover + CFBundleExecutable + wi + CFBundleIconFile + icon + CFBundleIdentifier + com.spiffcode.ht + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Hostile Takeover + CFBundlePackageType + APPL + CFBundleSignature + HTAK + CFBundleVersion + 1.2 + UIApplicationExitsOnSuspend + + UIPrerenderedIcon + + UIRequiresPersistentWiFi + + UIStatusBarHidden + + UISupportedInterfaceOrientations + + + diff --git a/game/iphone/NavBackSmall.png b/game/iphone/NavBackSmall.png new file mode 100644 index 0000000..ff27aa1 Binary files /dev/null and b/game/iphone/NavBackSmall.png differ diff --git a/game/iphone/NavForwardSmall.png b/game/iphone/NavForwardSmall.png new file mode 100644 index 0000000..3623ba4 Binary files /dev/null and b/game/iphone/NavForwardSmall.png differ diff --git a/game/iphone/cgdib.cpp b/game/iphone/cgdib.cpp new file mode 100644 index 0000000..c996d40 --- /dev/null +++ b/game/iphone/cgdib.cpp @@ -0,0 +1,323 @@ +#include "cgdib.h" + +namespace wi { + +CgDib *CreateCgDib(byte *pb, const SurfaceProperties *pprops, int cx, int cy, + int nDegreeOrientation) +{ + CgDib *pbm = new CgDib; + if (pbm == NULL) + return NULL; + if (!pbm->Init(pb, pprops, cx, cy, nDegreeOrientation)) { + delete pbm; + return NULL; + } + return pbm; +} + +bool CgDib::Init(byte *pb, const SurfaceProperties *pprops, int cx, int cy, + int nDegreeOrientation) +{ + m_props = *pprops; + switch (nDegreeOrientation) { + case 0: + // Standard portrait, button on bottom + + m_cbOffOrigin = 0; + m_cbxPitch = m_props.cbxPitch; + m_cbyPitch = m_props.cbyPitch; + break; + + case 90: + // button on left + + m_cbOffOrigin = m_props.cbyPitch * (m_props.cyHeight - 1); + m_cbxPitch = -m_props.cbyPitch; + m_cbyPitch = m_props.cbxPitch; + break; + + case 180: + // button on top + + m_cbOffOrigin = m_props.cbyPitch * (m_props.cyHeight - 1) + m_props.cbxPitch * (m_props.cxWidth - 1); + m_cbxPitch = -m_props.cbxPitch; + m_cbyPitch = -m_props.cbyPitch; + break; + + case 270: + // button on right + + m_cbOffOrigin = m_props.cbxPitch * (m_props.cxWidth - 1); + m_cbxPitch = m_props.cbyPitch; + m_cbyPitch = -m_props.cbxPitch; + break; + + default: + return false; + } + + m_nDegreeOrientation = nDegreeOrientation; + m_pb = pb; + m_cbRow = m_cbxPitch * cx; + m_siz.cx = cx; + m_siz.cy = cy; + + //temp + //m_wf |= kfDibWantScrolls; + + return true; +} + +void CgDib::SetPalette(void *palette, int cb) +{ + if (palette_ != NULL) + free(palette_); + palette_ = malloc(cb); + memcpy((byte *)palette_, (byte *)palette, cb); +} + +void CgDib::Blt(DibBitmap *pbmSrc, Rect *prcSrc, int xDst, int yDst) +{ + // Get src dib dimensions + + Size sizSrc; + pbmSrc->GetSize(&sizSrc); + + // Clip to source rect + // NOTE: Not exactly good form to change the caller's rect. + + if (prcSrc->left < 0) + prcSrc->left = 0; + if (prcSrc->top < 0) + prcSrc->top = 0; + if (prcSrc->right > sizSrc.cx) + prcSrc->right = sizSrc.cx; + if (prcSrc->bottom > sizSrc.cy) + prcSrc->bottom = sizSrc.cy; + + // Clip to dest + + if (xDst < 0) { + prcSrc->left -= xDst; + xDst = 0; + } + if (yDst < 0) { + prcSrc->top -= yDst; + yDst = 0; + } + + int xRightDst = xDst + prcSrc->Width(); + if (xRightDst > m_siz.cx) + prcSrc->right -= xRightDst - m_siz.cx; + int yBottomDst = yDst + prcSrc->Height(); + if (yBottomDst > m_siz.cy) + prcSrc->bottom -= yBottomDst - m_siz.cy; + + // Anything to blt? + + if (prcSrc->IsEmpty()) + return; + + int xRight = _min(prcSrc->right, m_siz.cx); + int yBottom = _min(prcSrc->bottom, m_siz.cy); + byte *pbSrc = pbmSrc->GetBits() + (prcSrc->top * m_siz.cx) + prcSrc->left; + int cbDstInset = xDst * m_cbxPitch; + int cbSrcStride = m_siz.cx - prcSrc->Width(); + + byte *pbScreen = m_pb; + if (pbScreen == NULL || palette_ == NULL) + return; + + changed_ = true; + + pbScreen += m_cbOffOrigin; + +#ifdef k555 + word *pwDst = (word*)(pbScreen + (yDst * m_cbyPitch)); + word *palette = (word *)palette_; + + for (int y = prcSrc->top; y < yBottom; y++) { + word *pwDstT = (word*)(((byte*)pwDst) + cbDstInset); + for (int x = prcSrc->left; x < xRight; x++) { + *pwDstT = palette[*pbSrc++]; + pwDstT = (word*)(((byte*)pwDstT) + m_cbxPitch); + } + pbSrc += cbSrcStride; + pwDst = (word*)(((byte*)pwDst) + m_cbyPitch); + } +#else + dword *pdwDst = (dword *)(pbScreen + (yDst * m_cbyPitch)); + dword *palette = (dword *)palette_; + + for (int y = prcSrc->top; y < yBottom; y++) { + dword *pdwDstT = (dword *)(((byte *)pdwDst) + cbDstInset); + for (int x = prcSrc->left; x < xRight; x++) { + *pdwDstT = palette[*pbSrc++]; + pdwDstT = (dword *)(((byte *)pdwDstT) + m_cbxPitch); + } + pbSrc += cbSrcStride; + pdwDst = (dword *)(((byte *)pdwDst) + m_cbyPitch); + } +#endif +} + +#define TopToBottomBlt LeftToRightBlt +#define FastestBlt LeftToRightBlt +#define BottomToTopBlt RightToLeftBlt + +void CgDib::Scroll(Rect *prcSrc, int xDst, int yDst) +{ + // Rotate the coordinates as appropriate and call FastIntraBlt() + + int xDstT, yDstT; + Rect rcT; + switch (m_nDegreeOrientation) { + case 0: + rcT = *prcSrc; + xDstT = xDst; + yDstT = yDst; + break; + +#if 0 +// these two need a little help if they are ever used. + case 90: + rcT.left = prcSrc->top; + rcT.top = m_siz.cy - (prcSrc->left + prcSrc->Width()); + rcT.right = rcT.left + prcSrc->Height(); + rcT.bottom = rcT.top + prcSrc->Width(); + xDstT = yDst; + yDstT = m_siz.cy - (xDst + prcSrc->Width()); + break; + + case 180: + rcT.left = m_siz.cx - (prcSrc->left + prcSrc->Width()); + rcT.top = m_siz.cy - (prcSrc->top + prcSrc->Height()); + rcT.right = rcT.left + prcSrc->Width(); + rcT.bottom = rcT.top + prcSrc->Height(); + xDstT = m_siz.cx - (xDst + prcSrc->Width()); + yDstT = m_siz.cy - (yDst + prcSrc->Height()); + break; +#endif + + case 270: + rcT.left = m_siz.cy - prcSrc->bottom; + rcT.top = prcSrc->left; + rcT.right = rcT.left + prcSrc->Height(); + rcT.bottom = rcT.top + prcSrc->Width(); + xDstT = m_siz.cy - (yDst + prcSrc->Height()); + yDstT = xDst; + break; + } + + FastIntraBlt(&rcT, xDstT, yDstT); +} + +void CgDib::FastIntraBlt(Rect *prcSrc, int xDst, int yDst) +{ + // This blt expects parameters relative to how the memory is physically + // arranged. + + // Clip to source rect + + if (prcSrc->left < 0) + prcSrc->left = 0; + if (prcSrc->top < 0) + prcSrc->top = 0; + if (prcSrc->right > m_props.cxWidth) + prcSrc->right = m_props.cxWidth; + if (prcSrc->bottom > m_props.cyHeight) + prcSrc->bottom = m_props.cyHeight; + + // Clip to dest + + if (xDst < 0) { + prcSrc->left -= xDst; + xDst = 0; + } + if (yDst < 0) { + prcSrc->top -= yDst; + yDst = 0; + } + + int xRightDst = xDst + prcSrc->Width(); + if (xRightDst > m_props.cxWidth) + prcSrc->right -= xRightDst - m_props.cxWidth; + int yBottomDst = yDst + prcSrc->Height(); + if (yBottomDst > m_props.cyHeight) + prcSrc->bottom -= yBottomDst - m_props.cyHeight; + + // Anything to blt? + + if (prcSrc->IsEmpty()) + return; + + // Calc addresses + + int cbRow = m_props.cxWidth * 4; + dword *pdwSrc = (dword *)(m_pb + (long)prcSrc->top * cbRow) + prcSrc->left; + dword *pdwDst = (dword *)(m_pb + (long)yDst * cbRow) + xDst; + + // If same y, ... + + if (yDst == prcSrc->top) { + // Overlap? + + int cxInterval = prcSrc->right - xDst; + if (cxInterval > 0 && cxInterval < prcSrc->Width() * 2) { + // Overlap. If bltting to the left, copy from left to right + + if (xDst < prcSrc->left) { + LeftToRightBlt(pdwSrc, pdwDst, prcSrc->Width(), prcSrc->Height()); + } else { + RightToLeftBlt(pdwSrc, pdwDst, prcSrc->Width(), prcSrc->Height()); + } + } else { + // No overlap. Do the fastest blt + + FastestBlt(pdwSrc, pdwDst, prcSrc->Width(), prcSrc->Height()); + } + } else { + int cyInterval = prcSrc->bottom - yDst; + if (cyInterval > 0 && cyInterval < prcSrc->Height() * 2) { + // Overlap. If bltting upwards, copy from top to bottom + + if (yDst < prcSrc->top) { + TopToBottomBlt(pdwSrc, pdwDst, prcSrc->Width(), prcSrc->Height()); + } else { + BottomToTopBlt(pdwSrc, pdwDst, prcSrc->Width(), prcSrc->Height()); + } + } else { + // No overlap. Do the fastest blt + + FastestBlt(pdwSrc, pdwDst, prcSrc->Width(), prcSrc->Height()); + } + } +} + +void CgDib::LeftToRightBlt(dword *pdwSrc, dword *pdwDst, int cx, int cy) +{ + int cdwReturn = m_props.cxWidth - cx; + while (cy-- > 0) { + int cdw = cx; + while (cdw-- > 0) + *pdwDst++ = *pdwSrc++; + pdwSrc += cdwReturn; + pdwDst += cdwReturn; + } +} + +void CgDib::RightToLeftBlt(dword *pdwSrc, dword *pdwDst, int cx, int cy) +{ + int cdwReturn = m_props.cxWidth - cx; + pdwSrc += (cy - 1) * m_props.cxWidth + cx - 1; + pdwDst += (cy - 1) * m_props.cxWidth + cx - 1; + while (cy-- > 0) { + int cdw = cx; + while (cdw-- > 0) + *pdwDst-- = *pdwSrc--; + pdwSrc -= cdwReturn; + pdwDst -= cdwReturn; + } +} + +} // namespace wi diff --git a/game/iphone/cgdib.h b/game/iphone/cgdib.h new file mode 100644 index 0000000..0f8c3df --- /dev/null +++ b/game/iphone/cgdib.h @@ -0,0 +1,43 @@ +#ifndef __CGDIB_H__ +#define __CGDIB_H__ + +#include "../ht.h" + +namespace wi { + +//#define k555 + +class CgDib : public DibBitmap +{ +public: + CgDib() : palette_(NULL),changed_(false) { } + virtual ~CgDib() { if (palette_ != NULL) free(palette_); } + + virtual bool Init(byte *pb, const SurfaceProperties *pprops, int cx, + int cy, int nDegreeOrientation); + virtual void SetPalette(void *palette, int cb); + virtual void Blt(DibBitmap *pbmSrc, Rect *prcSrc, int xDst, int yDst); + virtual void Scroll(Rect *prcSrc, int xDst, int yDst); + + bool HasChanged() { return changed_; } + void ResetChanged() { changed_ = false; } + +protected: + void LeftToRightBlt(dword *pdwSrc, dword *pdwDst, int cx, int cy); + void RightToLeftBlt(dword *pdwSrc, dword *pdwDst, int cx, int cy); + void FastIntraBlt(Rect *prcSrc, int xDst, int yDst); + + bool changed_; + void *palette_; + long m_cbOffOrigin; + int m_cbxPitch; + int m_cbyPitch; + int m_nDegreeOrientation; + SurfaceProperties m_props; +}; +CgDib *CreateCgDib(byte *pb, const SurfaceProperties *pprops, int cx, int cy, + int nDegreeOrientation); + +} // namespace wi + +#endif // __CGDIB_H__ diff --git a/game/iphone/cgview.h b/game/iphone/cgview.h new file mode 100644 index 0000000..98b83a5 --- /dev/null +++ b/game/iphone/cgview.h @@ -0,0 +1,37 @@ +#ifndef __CGVIEW_H__ +#define __CGVIEW_H__ + +#import +#import +#import +#import +#import +#import +#include "game/ht.h" +#import "game/iphone/wiview.h" +#include "game/iphone/cgdib.h" +#include "game/sprite.h" +#include "base/criticalsection.h" +#include + +@interface CgView : WiView { + CGContextRef bmCtx_; + wi::CgDib *pbm_; + CGAffineTransform tmFlip_; + CGImageRef image_; + CGRect *arcInvalid_; + int crcInvalid_; + int crcInvalidMax_; +} +- (id)initWithFrame:(CGRect)rect; +- (void)initGraphics; +- (void)dealloc; +- (void)frameStart; +- (void)frameComplete:(int)cfrmm maps:(wi::UpdateMap **)apupd + rects:(wi::Rect *)arc scrolled:(bool)fScrolled; +- (wi::DibBitmap *)createFrontDibWithOrientation:(int)nDegreeOrientation + width:(int)cx height:(int)cy; +- (void)setPalette:(wi::Palette *)ppal; +@end + +#endif // __CGVIEW_H__ diff --git a/game/iphone/cgview.mm b/game/iphone/cgview.mm new file mode 100644 index 0000000..7e7ba5c --- /dev/null +++ b/game/iphone/cgview.mm @@ -0,0 +1,228 @@ +#import +#import "game/iphone/cgview.h" +#import "game/iphone/iphone.h" +#include "base/log.h" + +@implementation CgView + +- (id)initWithFrame:(struct CGRect)rect +{ + self = [super initWithFrame: rect]; + if (self != nil) { + [self initGraphics]; + image_ = NULL; + arcInvalid_ = NULL; + crcInvalid_ = 0; + crcInvalidMax_ = 0; + } + return self; +} + +- (void)dealloc +{ + CGContextRelease(bmCtx_); + if (image_ != NULL) { + CGImageRelease(image_); + } + delete arcInvalid_; + [super dealloc]; +} + +- (void)setFormMgrs:(wi::FormMgr *)pfrmmSimUI input:(wi::FormMgr *)pfrmmInput +{ + // Now allocate the max # of invalid rects that may result + + wi::Size sizMap0; + pfrmmSimUI->GetUpdateMap()->GetMapSize(&sizMap0); + wi::Size sizMap1; + pfrmmInput->GetUpdateMap()->GetMapSize(&sizMap1); + + int crcMax = (sizMap0.cx * sizMap0.cy + sizMap1.cx * sizMap1.cy) / 2; + delete arcInvalid_; + arcInvalid_ = new CGRect[crcMax]; + crcInvalid_ = 0; + crcInvalidMax_ = crcMax; +} + +- (void)initGraphics +{ + // The OS erases invalid areas black whether clearsContext is YES or NO. + + [[self layer] setOpaque: YES]; + self.clearsContextBeforeDrawing = NO; + + int w = rect_.size.width; + int h = rect_.size.height; + + int allocSize = 4 * w * h; + + pb_ = (unsigned char *)malloc(allocSize); + memset(pb_, 0, allocSize); + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + + bmCtx_ = CGBitmapContextCreate(pb_, w, h, 8, w * 4, colorSpace, + kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrderDefault); + CGColorSpaceRelease(colorSpace); + + tmFlip_ = CGAffineTransformMake(1.0f, 0.0f, 0.0f, -1.0f, 0.0f, h); +} + +- (void)drawRect:(CGRect)frame +{ + // OS 4.0 introduces contentScaleFactor. If this is set to anything other + // than 1.0, the OS performs a very slow blt. OS 4.0 defaults it to 2.0 + // on iPhone 4. Set it back to 1.0 if the selector is present. + + if ([self respondsToSelector:@selector(setContentScaleFactor:)]) { + [self setContentScaleFactor: 1.0]; + } + + base::CritScope cs(pcrit_); + + if (image_ == NULL) { + return; + } + CGRect rc = [self bounds]; + CGContextRef ctx = UIGraphicsGetCurrentContext(); + CGContextSaveGState(ctx); + CGContextConcatCTM(ctx, tmFlip_); + CGContextDrawImage(ctx, rc, image_); + [self drawSprites: ctx]; + CGContextRestoreGState(ctx); +} + +- (void)frameStart +{ + pcrit_->Enter(); + pbm_->ResetChanged(); +} + +- (void)invalidate +{ + // Runs on main thread + + base::CritScope cs(pcrit_); + for (int i = 0; i < crcInvalid_; i++) { + [self setNeedsDisplayInRect:arcInvalid_[i]]; + } + crcInvalid_ = 0; +} + +//#define INVALID_RECTS + +- (void)frameComplete:(int)cfrmm maps:(wi::UpdateMap **)apupd + rects:(wi::Rect *)arc scrolled:(bool)fScrolled +{ + if (!havePalette_) { + pcrit_->Leave(); + return; + } + + // Sprites may have changes, or front dib may have changed, or neither. + bool fImageChanged = false; + if (pbm_->HasChanged()) { + if (image_ != NULL) { + CGImageRelease(image_); + image_ = NULL; + } + image_ = CGBitmapContextCreateImage(bmCtx_); + fImageChanged = true; + } + + bool fSpriteChanged = false; + if (fSpriteDirty_) { + fSpriteDirty_ = false; + fSpriteChanged = true; + } + + if (!fSpriteChanged && !fImageChanged) { + pcrit_->Leave(); + return; + } + + if (fScrolled || fSpriteChanged) { +#ifdef INVALID_RECTS + arcInvalid_[0] = rect_; + crcInvalid_ = 1; + [self performSelectorOnMainThread:@selector(invalidate) + withObject:nil waitUntilDone: NO]; +#else + [self performSelectorOnMainThread:@selector(setNeedsDisplay) + withObject:nil waitUntilDone: NO]; +#endif + pcrit_->Leave(); + return; + } + + if (fImageChanged) { +#ifdef INVALID_RECTS + // Collect the invalid rects, and send them over to the main + // thread + + CGRect *prc = &arcInvalid_[crcInvalid_]; + CGRect *prcMax = &arcInvalid_[crcInvalidMax_]; + int cy = (int)rect_.size.width; + + for (int i = 0; i < cfrmm; i++) { + wi::UpdateMap *pupd = apupd[i]; + int yTop = arc[i].top; + + bool fFirst = true; + wi::Rect rc; + while (prc < prcMax && pupd->EnumUpdateRects(fFirst, NULL, &rc)) { + fFirst = false; + prc->origin.x = cy - (rc.bottom + yTop); + prc->origin.y = rc.left; + prc->size.width = rc.Height(); + prc->size.height = rc.Width(); + prc++; + } + } + crcInvalid_ = prc - arcInvalid_; + [self performSelectorOnMainThread:@selector(invalidate) + withObject:nil waitUntilDone: NO]; +#else + [self performSelectorOnMainThread:@selector(setNeedsDisplay) + withObject:nil waitUntilDone: NO]; +#endif + pcrit_->Leave(); + return; + } +} + +- (wi::DibBitmap *)createFrontDibWithOrientation:(int)nDegreeOrientation width:(int)cx height:(int)cy +{ + wi::SurfaceProperties props; + [self getSurfaceProperties:&props]; + pbm_ = wi::CreateCgDib(pb_, &props, cx, cy, nDegreeOrientation); + return pbm_; +} + +- (void)setPalette:(wi::Palette *)ppal +{ + if (pbm_ != NULL) { + // 8->8888 mapping table. 3x faster than 555 format even though it is + // converted to 565 for display. + dword mp8bpp32bpp[256]; + for (int n = 0; n < BigWord(ppal->cEntries); n++) { + byte *pb = (byte *)&mp8bpp32bpp[n]; + *pb++ = 0; + *pb++ = ppal->argb[n][0]; + *pb++ = ppal->argb[n][1]; + *pb++ = ppal->argb[n][2]; + } + pbm_->SetPalette(mp8bpp32bpp, sizeof(mp8bpp32bpp)); + + havePalette_ = true; + } +} + +- (void)getSurfaceProperties:(wi::SurfaceProperties *)pprops +{ + pprops->cxWidth = (int)rect_.size.width; + pprops->cyHeight = (int)rect_.size.height; + pprops->cbxPitch = 4; + pprops->cbyPitch = (int)rect_.size.width * 4; + pprops->ffFormat = wi::kfDirect888; +} +@end diff --git a/game/iphone/chatcell.h b/game/iphone/chatcell.h new file mode 100644 index 0000000..4cdfab1 --- /dev/null +++ b/game/iphone/chatcell.h @@ -0,0 +1,17 @@ +#ifndef __CHATCELL_H__ +#define __CHATCELL_H__ + +#import +#import +#import +#import +#import + +@interface ChatCell : UITableViewCell { + UILabel *nameLabel_; + UILabel *chatLabel_; +} +- (void)setup:(NSString *)chat user:(NSString *)user size:(CGSize)size; +@end + +#endif // __CHATCELL_H__ diff --git a/game/iphone/chatcell.mm b/game/iphone/chatcell.mm new file mode 100644 index 0000000..458f475 --- /dev/null +++ b/game/iphone/chatcell.mm @@ -0,0 +1,56 @@ +#import "game/iphone/chatcell.h" + +@implementation ChatCell +- (id)initWithFrame:(CGRect)frame reuseIdentifier:(NSString *)reuseIdentifier { + self = [super initWithFrame:frame reuseIdentifier:reuseIdentifier]; + if (self == nil) { + return nil; + } + + self.selectionStyle = UITableViewCellSelectionStyleNone; + + chatLabel_ = [[UILabel alloc] initWithFrame:CGRectZero]; + chatLabel_.backgroundColor = [UIColor whiteColor]; + chatLabel_.opaque = YES; + chatLabel_.font = [UIFont systemFontOfSize:12]; + chatLabel_.lineBreakMode = UILineBreakModeWordWrap; + chatLabel_.numberOfLines = 0; + + [self addSubview:chatLabel_]; + + nameLabel_ = [[UILabel alloc] initWithFrame:CGRectZero]; + nameLabel_.backgroundColor = [UIColor whiteColor]; + nameLabel_.opaque = YES; + nameLabel_.textColor = [UIColor blueColor]; + nameLabel_.font = [UIFont systemFontOfSize:12]; + nameLabel_.lineBreakMode = UILineBreakModeWordWrap; + nameLabel_.numberOfLines = 0; + + [self addSubview:nameLabel_]; + + return self; +} + +- (void)setup:(NSString *)chat user:(NSString *)user size:(CGSize)size +{ + chatLabel_.text = chat; + CGRect frame = chatLabel_.frame; + frame.size = size; + chatLabel_.frame = frame; + nameLabel_.text = user; + frame = nameLabel_.frame; + frame.size = [user sizeWithFont:nameLabel_.font]; + nameLabel_.frame = frame; + + // length 0 means system message + if ([user length] == 0) { + chatLabel_.textColor = [UIColor grayColor]; + } else { + chatLabel_.textColor = [UIColor blackColor]; + } + + // Invalidate the cached image + [self setNeedsLayout]; + [self setNeedsDisplay]; +} +@end diff --git a/game/iphone/chatview.h b/game/iphone/chatview.h new file mode 100644 index 0000000..7f5765b --- /dev/null +++ b/game/iphone/chatview.h @@ -0,0 +1,21 @@ +#ifndef __CHATVIEW_H__ +#define __CHATVIEW_H__ + +#import +#import +#import +#import +#import + +@interface ChatView : UIView { + UINavigationBar *navBar_; + UINavigationItem *navItem_; + CGRect rect_; + //UITextField *textField_; +} +- (id)initWithFrame:(CGRect)rect; +- (void)dealloc; +- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event; +@end + +#endif // __CHATVIEW_H__ diff --git a/game/iphone/chatview.mm b/game/iphone/chatview.mm new file mode 100644 index 0000000..01b3ed5 --- /dev/null +++ b/game/iphone/chatview.mm @@ -0,0 +1,47 @@ +#import "game/iphone/chatview.h" + +@implementation ChatView + +- (id)initWithFrame:(CGRect)rect { + + rect_ = CGRectMake(0, 0, rect.size.height, rect.size.width); + + self = [super initWithFrame:rect_]; + if (self == nil) { + return nil; + } + +#if 0 + // Rotate this view + self.center = CGPointMake(rect.size.height / 2, rect.size.width / 2); + CGAffineTransform transform = self.transform; + self.transform = CGAffineTransformRotate(transform, (M_PI / 2.0)); +#endif + +#if 0 + CGRect rcNav = CGRectMake(0, 0, rect_.size.width, 48); + navBar_ = [[UINavigationBar alloc] initWithFrame: rcNav]; + [navBar_ setDelegate: self]; + navItem_ = [[UINavigationItem alloc] initWithTitle:@"Chat: "]; + [navBar_ pushNavigationItem: navItem_ animated:NO]; + //[navBar_ showButtonsWithRightTitle:@"" rightTitle:@"Dismiss" leftBack: NO]; + + [self addSubview: navBar_]; +#endif + + //CGRect rcField; + //textField_ = [[UITextField alloc] initWithFrame:rcField]; + + return self; +} + +- (void)dealloc { + //[navBar_ release]; + //[textField_ release]; + [super dealloc]; +} + +- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event { + +} +@end diff --git a/game/iphone/chatviewcontroller.h b/game/iphone/chatviewcontroller.h new file mode 100644 index 0000000..e456cee --- /dev/null +++ b/game/iphone/chatviewcontroller.h @@ -0,0 +1,96 @@ +#ifndef __CHATVIEWCONTROLLER_H__ +#define __CHATVIEWCONTROLLER_H__ + +#import +#import +#import +#import +#import + +namespace wi { +class ChatController; +} + +@class ChatViewController; + +@protocol ChatViewControllerDelegate +- (void)onDone:(ChatViewController *)c; +@end + +@interface ChatViewController : NSObject { + UIView *parent_; + UIView *view_; + id delegate_; + UITableView *tableView_; + UIToolbar *toolbar_; + UITextField *textField_; + bool keyboardShown_; + NSMutableArray *chatEntries_; + UIFont *entryFont_; + CGFloat width10Spaces_; + wi::ChatController *chatc_; + NSString *title_; + UIToolbar *toolbar2_; + UILabel *titleLabel_; + NSMutableArray *chatQueue_; + BOOL suspended_; + BOOL clear_; +} +- (id)init:(id)delegate parent:(UIView *)parent; +- (void)loadView; +- (void)show; +- (void)layoutViews; +- (void)registerNotifications; +- (void)testChat; +- (int)getRowHeight:(int)rowIndex forWidth:(int)width; +- (void)reCreateTableView; +- (void)layoutTableView; +- (void)suspendUpdates:(BOOL)suspend; +- (NSArray *)updateChatModel; +@end + +// +++ + +#import +#include "base/thread.h" +#include "game/chatcontroller.h" +#include + +#define kidmOnSend 1 +#define kidmOnDismissed 2 +#define kidmOnPlayers 3 + +namespace wi { + +struct ChatParams : base::MessageData { + ChatParams(const std::string& chat) : chat(chat) {} + std::string chat; +}; + +// Game thread c++ interface to the ChatViewController +class ChatController : public IChatController, public base::MessageHandler { +public: + ChatController(WiViewController *vcwi, ChatViewController *vcchat); + + // IChatController + void Clear(); + void AddChat(const char *player, const char *chat); + void Show(bool fShow); + void SetTitle(const char *title); + const char *GetTitle(); + IChatControllerCallback *SetCallback(IChatControllerCallback *pcccb); + +private: + // MessageHandler + virtual void OnMessage(base::Message *pmsg); + + std::string title_; + WiViewController *vcwi_; + ChatViewController *vcchat_; + IChatControllerCallback *pcccb_; +}; + +} // namespace wi + +#endif // __CHATVIEWCONTROLLER_H__ diff --git a/game/iphone/chatviewcontroller.mm b/game/iphone/chatviewcontroller.mm new file mode 100644 index 0000000..46a19e2 --- /dev/null +++ b/game/iphone/chatviewcontroller.mm @@ -0,0 +1,711 @@ +#import "game/iphone/chatviewcontroller.h" +#import "game/iphone/chatcell.h" + +@implementation ChatViewController + +#define TEXTFIELDWIDTH_LANDSCAPE 397 +#define TEXTFIELDWIDTH_PORTRAIT 238 + +#define TEXTFIELDHEIGHT_LANDSCAPE 24 +#define TEXTFIELDHEIGHT_PORTRAIT 29 + +#define TEXTFIELDFONTSIZE_LANDSCAPE 16 +#define TEXTFIELDFONTSIZE_PORTRAIT 20 + +#define TITLELABELWIDTH_LANDSCAPE 335 + +#define CHAT_HISTORY 100 +#define CHAT_QUEUE 100 + +- (id)init:(id)delegate parent:(UIView *)parent { + parent_ = parent; + [parent_ retain]; + delegate_ = delegate; + [delegate_ retain]; + view_ = nil; + tableView_ = nil; + toolbar_ = nil; + textField_ = nil; + keyboardShown_ = false; + chatQueue_ = [[NSMutableArray alloc] initWithCapacity:CHAT_QUEUE]; + suspended_ = NO; + chatEntries_ = [[NSMutableArray alloc] initWithCapacity:CHAT_HISTORY]; + entryFont_ = [UIFont systemFontOfSize:12]; + CGSize size10Spaces = [@" " sizeWithFont:entryFont_]; + width10Spaces_ = size10Spaces.width; + chatc_ = NULL; + title_ = @"Chat"; + [title_ retain]; + titleLabel_ = nil; + toolbar2_ = nil; + clear_ = YES; + + [self registerNotifications]; + + [self loadView]; + + return self; +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [tableView_ release]; + [toolbar_ release]; + [textField_ release]; + [chatEntries_ release]; + [chatQueue_ release]; + [title_ release]; + [toolbar2_ release]; + [titleLabel_ release]; + [view_ release]; + [delegate_ release]; + [parent_ release]; + + [super dealloc]; +} + +- (void)testChat +{ +#if 0 + [self addChat:@"it is raining today" user:@"scott"]; + [self addChat:@"it will be sunny tomorrow" user:@"valerie"]; + [self addChat:@"the lazy fox jumped over the brown cow's back and sang the pledge of allegiance more words please word wrap yes please do it now asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf" user:@"scott"]; + [self addChat:@"I don't believe you." user:@"valerie"]; +#endif +} + +- (void)registerNotifications +{ + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(onKeyboardShown:) + name:UIKeyboardDidShowNotification object:nil]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(onKeyboardHidden:) + name:UIKeyboardDidHideNotification object:nil]; + + keyboardShown_ = false; +} + +- (void)loadView +{ + // Create parent view that subviews go into + CGRect frame = CGRectMake(0, 0, parent_.frame.size.height, + parent_.frame.size.width); + view_ = [[UIView alloc] initWithFrame:frame]; + view_.autoresizesSubviews = YES; + view_.autoresizingMask = UIViewAutoresizingFlexibleHeight | + UIViewAutoresizingFlexibleWidth; + + // Create toolbar, add to parent + + toolbar_ = [[UIToolbar alloc] initWithFrame:CGRectZero]; + toolbar_.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | + UIViewAutoresizingFlexibleWidth; + toolbar_.autoresizesSubviews = YES; + [view_ addSubview: toolbar_]; + + // Create text field - will be added to toolbar in layout because + // it is the only way the size custom toolbar views + + textField_ = [[UITextField alloc] initWithFrame:CGRectZero]; + textField_.borderStyle = UITextBorderStyleRoundedRect; + textField_.textColor = [UIColor blackColor]; + textField_.placeholder = @""; + textField_.autocorrectionType = UITextAutocorrectionTypeNo; + //textField_.keyboardType = UIKeyboardTypeDefault; + textField_.keyboardType = UIKeyboardTypeASCIICapable; + textField_.returnKeyType = UIReturnKeyDefault; + textField_.clearButtonMode = UITextFieldViewModeWhileEditing; + textField_.delegate = self; + + // toolbar2 is the navigation header + + toolbar2_ = [[UIToolbar alloc] initWithFrame:CGRectZero]; + toolbar2_.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | + UIViewAutoresizingFlexibleWidth; + toolbar2_.autoresizesSubviews = YES; + [view_ addSubview: toolbar2_]; + + // titleLabel that will be added to toolbar2 later + + titleLabel_ = [[UILabel alloc] initWithFrame:CGRectZero]; + titleLabel_.font = [UIFont boldSystemFontOfSize:18]; + titleLabel_.textAlignment = UITextAlignmentCenter; + titleLabel_.textColor = [UIColor whiteColor]; + titleLabel_.backgroundColor = [UIColor clearColor]; + titleLabel_.shadowColor = [UIColor darkGrayColor]; + titleLabel_.opaque = NO; + titleLabel_.text = title_; + + // Layout views. Repositions and resizes appropriately. + + [self layoutViews]; +} + +- (void)reCreateTableView { + [tableView_ removeFromSuperview]; + [tableView_ release]; + tableView_ = nil; + + // Create tableview and add to parent + + tableView_ = [[UITableView alloc] initWithFrame:CGRectZero + style:UITableViewStylePlain]; + tableView_.delegate = self; + tableView_.dataSource = self; + tableView_.separatorStyle = UITableViewCellSeparatorStyleNone; + tableView_.autoresizesSubviews = YES; + tableView_.autoresizingMask = UIViewAutoresizingFlexibleHeight | + UIViewAutoresizingFlexibleWidth; + [view_ addSubview: tableView_]; +} + +- (void)layoutTableView { + CGRect rcToolbar = toolbar_.frame; + CGRect rcToolbar2 = toolbar2_.frame; + CGRect rcTableView = tableView_.frame; + rcTableView.origin.y = rcToolbar2.size.height; + rcTableView.size.height = rcToolbar.origin.y - rcTableView.origin.y; + rcTableView.size.width = rcToolbar.size.width; + tableView_.frame = rcTableView; +} + +- (void)layoutViews +{ + // Resize the textField appropriately. Note the toolbar is a different + // height in portrait vs. landscape. Also note, custom view button + // bar items can only be resized by re-added them to the toolbar. + + CGRect rcTextField = textField_.frame; + if (view_.bounds.size.width <= 320) { + rcTextField.size.height = TEXTFIELDHEIGHT_PORTRAIT; + rcTextField.size.width = TEXTFIELDWIDTH_PORTRAIT; + textField_.font = [UIFont systemFontOfSize: + TEXTFIELDFONTSIZE_PORTRAIT]; + } else { + rcTextField.size.height = TEXTFIELDHEIGHT_LANDSCAPE; + rcTextField.size.width = TEXTFIELDWIDTH_LANDSCAPE; + textField_.font = [UIFont systemFontOfSize: + TEXTFIELDFONTSIZE_LANDSCAPE]; + } + textField_.frame = rcTextField; + + UIBarButtonItem *textFieldButton = [[[UIBarButtonItem alloc] + initWithCustomView:textField_] autorelease]; + UIBarButtonItem *sendButton = [[[UIBarButtonItem alloc] + initWithTitle:@"Send" style:UIBarButtonItemStyleBordered + target:self action:@selector(onSend)] autorelease]; + NSArray *array = [NSArray arrayWithObjects: textFieldButton, sendButton, + (char *)NULL]; + [toolbar_ setItems:array animated:NO]; + [toolbar_ sizeToFit]; + + // The toolbar is taller when in portrait mode, so other things + // need to be layed out. + + int cyToolbar = rcTextField.size.height + 8; + + CGRect rcToolbar = toolbar_.frame; + CGRect rcParent = view_.bounds; + rcToolbar.size.height = cyToolbar; + rcToolbar.origin.y = rcParent.size.height - rcToolbar.size.height; + toolbar_.frame = rcToolbar; + + // Size the label width + [titleLabel_ sizeToFit]; + CGRect frame = titleLabel_.frame; + frame.size.width = TITLELABELWIDTH_LANDSCAPE; + titleLabel_.frame = frame; + + // Toolbar2 goes at the top + UIBarButtonItem *playersButton = [[[UIBarButtonItem alloc] + initWithTitle:@"Players" + style:UIBarButtonItemStyleDone + target:self action:@selector(onPlayers)] autorelease]; + UIBarButtonItem *doneButton = [[[UIBarButtonItem alloc] + initWithTitle:@"Done" + style:UIBarButtonItemStyleDone + target:self action:@selector(onDone)] autorelease]; + UIBarButtonItem *titleItem = [[[UIBarButtonItem alloc] + initWithCustomView:titleLabel_] autorelease]; + array = [NSArray arrayWithObjects: doneButton, titleItem, + playersButton, (char *)NULL]; + [toolbar2_ setItems:array animated:NO]; + [toolbar2_ sizeToFit]; + + // Toolbar2 goes at the top + + CGRect rcToolbar2 = toolbar2_.frame; + rcToolbar2.origin.y = 0; + rcToolbar2.size.height = cyToolbar; + toolbar2_.frame = rcToolbar2; +} + +- (void)onSend { + if (chatc_ == NULL) { + return; + } + + // For some currently unknown reason, sometimes there are leading zeros + // in this UITextField text. Remove them, and remove whitespace. + + NSString *text = textField_.text; + int count = text.length; + int index = 0; + for (; index < count; index++) { + unichar ch = [text characterAtIndex:index]; + if (ch != 0) { + break; + } + } + text = [[text substringFromIndex:index] stringByTrimmingCharactersInSet: + [NSCharacterSet whitespaceCharacterSet]]; + + if (text.length != 0) { + NSData *data = [text + dataUsingEncoding:[NSString defaultCStringEncoding] + allowLossyConversion:YES]; + std::string s((const char *)[data bytes], [data length]); + wi::ChatParams *params = new wi::ChatParams(s); + chatc_->thread().Post(kidmOnSend, chatc_, params); + } + textField_.text = nil; +} + +- (BOOL)textFieldShouldReturn:(UITextField *)textField { + [self onSend]; + return NO; +} + +- (void)scrollToBottom { + [tableView_ beginUpdates]; + + // Scroll to the bottom + int index = [chatEntries_ count] - 1; + if (index >= 0) { + NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index + inSection:0]; + [tableView_ scrollToRowAtIndexPath:indexPath + atScrollPosition:UITableViewScrollPositionBottom + animated:NO]; + } + + [tableView_ endUpdates]; +} + +- (void)onPlayers +{ + if (chatc_ != NULL) { + chatc_->thread().Post(kidmOnPlayers, chatc_); + } +} + +- (void)show { + BOOL created = NO; + if (clear_ || tableView_ == nil) { + // Put queued up chat into the model + suspended_ = NO; + [self updateChatModel]; + + // Create the table view. It will automagically reload the model + [self reCreateTableView]; + [self layoutTableView]; + clear_ = NO; + created = YES; + } + [parent_ addSubview:view_]; + + [textField_ becomeFirstResponder]; + + // Only scroll to the bottom if the table view existed already. + // Otherwise it will crash, because data model loading is + // asynchronous. + if (created == NO) { + [self scrollToBottom]; + } +} + +- (void)onDone +{ + [view_ removeFromSuperview]; + if (delegate_ != nil) { + [delegate_ onDone:self]; + } + if (chatc_ != NULL) { + chatc_->thread().Post(kidmOnDismissed, chatc_); + } +} + +- (void)onKeyboardShown:(NSNotification *)notification +{ + if (keyboardShown_) { + return; + } + keyboardShown_ = true; + + NSDictionary *info = [notification userInfo]; + NSValue *value = [info objectForKey:UIKeyboardBoundsUserInfoKey]; + CGSize sizeKeyboard = [value CGRectValue].size; + + CGRect rcToolbar = toolbar_.frame; + rcToolbar = CGRectOffset(rcToolbar, 0, -sizeKeyboard.height); + toolbar_.frame = rcToolbar; + + CGRect rcTableView = tableView_.frame; + rcTableView.size.height -= sizeKeyboard.height; + tableView_.frame = rcTableView; + + [self scrollToBottom]; +} + +- (void)onKeyboardHidden:(NSNotification *)notification +{ + if (!keyboardShown_) { + return; + } + keyboardShown_ = false; + + NSDictionary *info = [notification userInfo]; + NSValue *value = [info objectForKey:UIKeyboardBoundsUserInfoKey]; + CGSize sizeKeyboard = [value CGRectValue].size; + + CGRect rcToolbar = toolbar_.frame; + rcToolbar = CGRectOffset(rcToolbar, 0, sizeKeyboard.height); + toolbar_.frame = rcToolbar; + + CGRect rcTableView = tableView_.frame; + rcTableView.size.height += sizeKeyboard.height; + tableView_.frame = rcTableView; +} + +#if 0 +// Fires on the simulator, but not the device. Go figure. +- (void)viewDidAppear:(BOOL)animated +{ + [self scrollToBottom]; +} +#endif + +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { + return 1; +} + +- (NSInteger)tableView:(UITableView *)tableView + numberOfRowsInSection:(NSInteger)section { + return [chatEntries_ count]; +} + +- (void)initializeChatCell:(int)rowIndex cell:(ChatCell *)cell +{ + CGSize size; + size.width = view_.bounds.size.width; + size.height = [self getRowHeight:rowIndex forWidth:size.width]; + + NSMutableDictionary *entry = (NSMutableDictionary *) + [chatEntries_ objectAtIndex:rowIndex]; + if (entry == nil) { + [cell setup:@"error" user:@"error" size:size]; + return; + } + NSString *user = [entry objectForKey:@"user"]; + NSString *chat = [entry objectForKey:@"chat"]; + [cell setup:chat user:user size:size]; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: + (NSIndexPath *)indexPath { + ChatCell *cell = (ChatCell *)[tableView_ + dequeueReusableCellWithIdentifier:@"chatcell"]; + if (cell == nil) { + cell = [[[ChatCell alloc] initWithFrame:CGRectZero + reuseIdentifier:@"chatcell"] autorelease]; + } + [self initializeChatCell:indexPath.row cell:cell]; + return cell; +} + +- (CGFloat)tableView:(UITableView *)tableView + heightForRowAtIndexPath:(NSIndexPath *)indexPath +{ + NSMutableDictionary *entry = (NSMutableDictionary *) + [chatEntries_ objectAtIndex:indexPath.row]; + if (entry == nil) { + return 0; + } + return [self getRowHeight:indexPath.row + forWidth:view_.bounds.size.width]; +} + +- (NSArray *)updateChatModel { + NSMutableArray *indexPaths = [NSMutableArray arrayWithCapacity:10]; + while ([chatQueue_ count] != 0) { + NSDictionary *dict = (NSDictionary *)[chatQueue_ objectAtIndex:0]; + NSString *chat = [dict objectForKey:@"chat"]; + NSString *user = [dict objectForKey:@"player"]; + + if ([user length] == 0) { + // System message. + NSMutableDictionary *entry = [NSMutableDictionary + dictionaryWithObjectsAndKeys: @"", @"user", + chat, @"chat", (char *)NULL]; + [chatEntries_ addObject:entry]; + } else { + // Regular chat message. Add ':' to the end of user + NSString *newUser = [NSString stringWithFormat:@"%@:", user]; + + // Insert spaces into chat so that user's name can draw into that + // space with a different UILabel. Hack-o-rama. + + CGSize sizeUser = [newUser sizeWithFont:entryFont_]; + CGFloat ratio = sizeUser.width / width10Spaces_; + int countSpaces = ceil(ratio * 10.0) + 2; + + char szSpaces[256]; + if (countSpaces > sizeof(szSpaces)) { + countSpaces = sizeof(szSpaces); + } + memset(szSpaces, ' ', sizeof(szSpaces)); + szSpaces[countSpaces - 1] = 0; + NSString *newChat = [NSString stringWithFormat:@"%s%@", szSpaces, + chat]; + NSMutableDictionary *entry = [NSMutableDictionary + dictionaryWithObjectsAndKeys: newUser, @"user", + newChat, @"chat", (char *)NULL]; + [chatEntries_ addObject:entry]; + } + [chatQueue_ removeObjectAtIndex:0]; + NSIndexPath *indexPath = [NSIndexPath indexPathForRow: + [chatEntries_ count] - 1 inSection:0]; + [indexPaths addObject:indexPath]; + } + return indexPaths; +} + +- (void)updateChatRows:(BOOL)scroll { + if (suspended_ == YES) { + return; + } + NSArray *indexPaths = [self updateChatModel]; + if ([indexPaths count] != 0) { + [tableView_ beginUpdates]; + [tableView_ insertRowsAtIndexPaths:indexPaths + withRowAnimation:UITableViewRowAnimationBottom]; + [tableView_ endUpdates]; + } + if (scroll == YES) { + NSIndexPath *indexPath = [NSIndexPath indexPathForRow: + [chatEntries_ count] - 1 inSection:0]; + [tableView_ scrollToRowAtIndexPath:indexPath + atScrollPosition:UITableViewScrollPositionBottom + animated:NO]; + } +} + +- (void)suspendUpdates:(BOOL)suspend { + if (suspend == suspended_) { + return; + } + if (suspended_ == NO & suspend == YES) { + suspended_ = suspend; + return; + } + if (suspended_ == YES && suspend == NO) { + suspended_ = suspend; + [self updateChatRows:NO]; + } +} + +- (int)getRowHeight:(int)rowIndex forWidth:(int)width +{ + NSMutableDictionary *entry = (NSMutableDictionary *) + [chatEntries_ objectAtIndex:rowIndex]; + if (entry == nil) { + return 0; + } + + // See if the height is cached; if so return it + NSString *key = [NSString stringWithFormat:@"%d", width]; + NSString *value = [entry objectForKey:key]; + if (value != nil) { + return [value intValue]; + } + + // Calculate what the height is, cache it, return it + NSString *chat = (NSString *)[entry objectForKey:@"chat"]; + + CGSize sizeBox = CGSizeMake(width, 2800); + CGSize size = [chat sizeWithFont:entryFont_ constrainedToSize:sizeBox + lineBreakMode:UILineBreakModeWordWrap]; + value = [NSString stringWithFormat:@"%d", (int)size.height]; + [entry setObject:value forKey:key]; + return size.height; +} + +- (void)doClear +{ + [chatEntries_ removeAllObjects]; + [chatQueue_ removeAllObjects]; + + // Clear occurs when switching between chat contexts (room, game, etc). + // It would be nice to be able to call reloadData here. Unfortunately, + // it causes "stuck chat cells" when the TableView isn't visible. As + // a hack workaround, destroy the tableView_ and re-created it when + // showing. During this period, queue up chat. + [tableView_ removeFromSuperview]; + [tableView_ release]; + tableView_ = nil; + clear_ = YES; + [self suspendUpdates:YES]; +} + +- (void)doAddChat:(NSDictionary *)dict +{ + [chatQueue_ addObject:dict]; + [self updateChatRows:YES]; +} + +- (void)doShow:(UINavigationController *)vcwi +{ + [self show]; + titleLabel_.text = title_; +} + +- (void)doHide +{ + [self onDone]; +} + +- (void)doSetTitle:(NSString *)title +{ + [title_ release]; + title_ = title; + [title_ retain]; +} + +- (void)setController:(wi::ChatController *)chatc +{ + chatc_ = chatc; +} + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)way { + return way == UIInterfaceOrientationLandscapeRight; +} + +// UIScrollViewDelegate +// Inserting items while scrolling causes UITableView to crash. Track +// when scrolling happens an cache inserts until scrolling stops. + +- (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView { + [self suspendUpdates:YES]; + return YES; +} + +- (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView { + [self suspendUpdates:NO]; +} + +- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { + [self suspendUpdates:YES]; +} + +- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView + willDecelerate:(BOOL)decelerate { + if (decelerate == NO) { + [self suspendUpdates:NO]; + } +} + +- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { + [self suspendUpdates:NO]; +} +@end + +// ++++ +// This is the game thread callable class for controlling chat + +namespace wi { + +// These get called on the game thread, and do the real work on the main +// thread. + +ChatController::ChatController(WiViewController *vcwi, + ChatViewController *vcchat) : vcwi_(vcwi), vcchat_(vcchat), + pcccb_(NULL) { + [vcchat_ setController: this]; +} + +void ChatController::Clear() { + [vcchat_ performSelectorOnMainThread:@selector(doClear) + withObject:nil waitUntilDone: NO]; +} + +void ChatController::AddChat(const char *player, const char *chat) { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + NSString *player_s = [NSString stringWithCString:player + encoding:[NSString defaultCStringEncoding]]; + NSString *chat_s = [NSString stringWithCString:chat + encoding:[NSString defaultCStringEncoding]]; + NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys: + player_s, @"player", chat_s, @"chat", (char *)NULL]; + [vcchat_ performSelectorOnMainThread:@selector(doAddChat:) + withObject:dict waitUntilDone: NO]; + [pool release]; +} + +void ChatController::Show(bool fShow) { + if (fShow) { + [vcchat_ performSelectorOnMainThread:@selector(doShow:) + withObject:vcwi_ waitUntilDone: NO]; + } else { + [vcchat_ performSelectorOnMainThread:@selector(doHide) + withObject:nil waitUntilDone: NO]; + } +} + +void ChatController::SetTitle(const char *title) { + title_ = title; + NSString *title_s = [NSString stringWithCString:title + encoding:[NSString defaultCStringEncoding]]; + [vcchat_ performSelectorOnMainThread:@selector(doSetTitle:) + withObject:title_s waitUntilDone: NO]; +} + +const char *ChatController::GetTitle() { + return title_.c_str(); +} + +IChatControllerCallback *ChatController::SetCallback( + IChatControllerCallback *pcccb) { + IChatControllerCallback *old = pcccb_; + pcccb_ = pcccb; + return old; +} + +void ChatController::OnMessage(base::Message *pmsg) { + switch (pmsg->id) { + case kidmOnDismissed: + [vcwi_ becomeFirstResponder]; + if (pcccb_ != NULL) { + pcccb_->OnChatDismissed(); + } + break; + + case kidmOnSend: + if (pcccb_ != NULL) { + ChatParams *params = (ChatParams *)pmsg->data; + pcccb_->OnChatSend(params->chat.c_str()); + delete params; + } + break; + + case kidmOnPlayers: + if (pcccb_ != NULL) { + pcccb_->OnPlayers(); + } + break; + } +} + +} // namespace wi diff --git a/game/iphone/display.cpp b/game/iphone/display.cpp new file mode 100644 index 0000000..560dce0 --- /dev/null +++ b/game/iphone/display.cpp @@ -0,0 +1,204 @@ +#include "../ht.h" + +namespace wi { + +Display *HostCreateDisplay() { + // Create a display + + Display *pdisp = new Display(); + if (pdisp == NULL) + return NULL; + if (!pdisp->Init()) { + delete pdisp; + return NULL; + } + return pdisp; +} + +Display::Display() +{ + m_cx = 0; + m_cy = 0; + memset(m_amodeInfo, 0, sizeof(m_amodeInfo)); + m_imode = -1; + m_cmodes = 0; + m_pbmBack = NULL; + m_pbmFront = NULL; + m_pbmClip = NULL; +} + +Display::~Display() +{ + delete m_pbmBack; + m_pbmBack = NULL; + delete m_pbmFront; + m_pbmFront = NULL; + delete m_pbmClip; + m_pbmClip = NULL; +} + +bool Display::Init() +{ + SurfaceProperties props; + IPhone::GetSurfaceProperties(&props); + + ModeInfo *pmode = m_amodeInfo; + +#if 1 + pmode->nDepth = 8; + pmode->cx = props.cxWidth; + pmode->cy = props.cyHeight; + pmode->cyGraffiti = 0; + pmode->fNative = true; + pmode->nDegreeOrientation = 0; + pmode++; +#endif + +#if 0 + pmode->nDepth = 8; + pmode->cx = props.cyHeight; + pmode->cy = props.cxWidth; + pmode->cyGraffiti = 0; + pmode->fNative = true; + pmode->nDegreeOrientation = 90; // leftie controls + pmode++; + + pmode->nDepth = 8; + pmode->cx = props.cxWidth; + pmode->cy = props.cyHeight; + pmode->cyGraffiti = 0; + pmode->fNative = true; + pmode->nDegreeOrientation = 180; // bizzaro controls + pmode++; +#endif + +#if 0 + pmode->nDepth = 8; + pmode->cx = props.cyHeight; + pmode->cy = props.cxWidth; + pmode->cyGraffiti = 0; + pmode->fNative = true; + pmode->nDegreeOrientation = 270; // rightie controls + pmode++; +#endif + + m_cmodes = pmode - m_amodeInfo; + + HostOutputDebugString("Display::Init %d", pmode - m_amodeInfo); + HostOutputDebugString("Display::Init %d modes", m_cmodes); + + return true; +} + +void Display::SetPalette(Palette *ppal) +{ + IPhone::SetPalette(ppal); +} + +int Display::GetModeCount() +{ + return m_cmodes; +} + +void Display::GetModeInfo(int imode, ModeInfo *pmode) +{ + memset(pmode, 0, sizeof(*pmode)); + if (imode >= 0 && imode < m_cmodes) + *pmode = m_amodeInfo[imode]; +} + +int Display::GetMode(ModeInfo *pmode) +{ + if (pmode != NULL) { + if (m_imode == -1) { + memset(pmode, 0, sizeof(*pmode)); + } else { + *pmode = m_amodeInfo[m_imode]; + } + } + return m_imode; +} + +bool Display::SetMode(int imode) +{ + // Allocate dib + + ModeInfo *pmode = &m_amodeInfo[imode]; + + DibBitmap *pbmBack = CreateDibBitmap(NULL, pmode->cx, pmode->cy); + if (pbmBack == NULL) + return false; + DibBitmap *pbmFront = IPhone::CreateFrontDib(pmode->cx, pmode->cy, pmode->nDegreeOrientation); + if (pbmFront == NULL) { + delete pbmBack; + return NULL; + } + delete m_pbmBack; + delete m_pbmFront; + m_pbmBack = pbmBack; + m_pbmFront = pbmFront; + m_imode = imode; + return true; +} + +void Display::DrawText(const char *psz, int x, int y, word wf) +{ +} + +void Display::DrawFrameInclusive(Rect *prc) +{ +} + +DibBitmap *Display::GetBackDib() +{ + return m_pbmBack; +} + +DibBitmap *Display::GetFrontDib() +{ + return m_pbmFront; +} + +DibBitmap *Display::GetClippingDib() +{ + DibBitmap *pbm = CreateDibBitmap(NULL, kcCopyBy4Procs * 4, kcCopyBy4Procs * 4); + if (pbm == NULL) + return NULL; + m_pbmClip = pbm; + return pbm; +} + +void Display::GetHslAdjustments(short *pnHueOffset, short *pnSatMultiplier, short *pnLumOffset) +{ + *pnHueOffset = 0; + *pnSatMultiplier = 0; + *pnLumOffset = 0; +} + +void Display::FrameStart() +{ + IPhone::FrameStart(); +} + +void Display::FrameComplete(int cfrmm, UpdateMap **apupd, Rect *arc, + bool fScrolled) +{ + IPhone::FrameComplete(cfrmm, apupd, arc, fScrolled); +} + +void Display::ResetScrollOffset() +{ + IPhone::ResetScrollOffset(); +} + +SpriteManager *Display::GetSpriteManager() +{ + return IPhone::GetSpriteManager(); +} + +void Display::SetFormMgrs(FormMgr *pfrmmSimUI, FormMgr *pfrmmInput) +{ + IPhone::SetFormMgrs(pfrmmSimUI, pfrmmInput); +} + +} // namespace wi diff --git a/game/iphone/host.cpp b/game/iphone/host.cpp new file mode 100644 index 0000000..84ff3de --- /dev/null +++ b/game/iphone/host.cpp @@ -0,0 +1,416 @@ +#include "game/ht.h" +#include "game/iphone/iphone.h" +#include "game/iphone/input.h" +#include "base/tick.h" +#include "base/log.h" +#include "base/thread.h" +#include "base/md5.h" +#include "game/httppackmanager.h" +#include "game/httppackinfomanager.h" +#include "game/iphone/iphonehttpservice.h" +#include "game/completemanager.h" +#include +#include +#include +#include + +namespace wi { + +IPhonePackFileReader gpakr; +HttpPackManager *gppackm; +HttpPackInfoManager *gppim; +HttpService *gphttp; +CompleteManager *gpcptm; + +bool HostInit() +{ + gphttp = (HttpService *)new IPhoneHttpService; + gppackm = new HttpPackManager(gphttp, IPhone::GetMissionPacksDir(), + IPhone::GetTempDir()); + gppackm->InitFromInstalled(); + gppim = new HttpPackInfoManager(gphttp, IPhone::GetMissionPackInfosDir(), + IPhone::GetTempDir()); + gpcptm = new CompleteManager(IPhone::GetCompletesDir()); + gpcptm->Init(); + + return true; +} + +void HostExit() +{ + delete gppim; + delete gppackm; + delete (IPhoneHttpService *)gphttp; +} + +const char *HostGenerateDeviceId() { + // Hash it so query params aren't obnoxious + MD5_CTX md5; + MD5Init(&md5); + const char *pszUUID = IPhone::GetStaticUUID(); + MD5Update(&md5, (const byte *)pszUUID, strlen(pszUUID)); + byte hash[16]; + MD5Final(hash, &md5); + return base::Format::ToHex(hash, 16); +} + +void HostInitiateWebView(const char *title, const char *url) { + return IPhone::InitiateWebView(title, url); +} + +IChatController *HostGetChatController() { + return IPhone::GetChatController(); +} + +void HostInitiateAsk(const char *title, int max, const char *def, + int keyboard, bool secure) { + return IPhone::InitiateAsk(title, max, def, keyboard, secure); +} + +void HostGetAskString(char *psz, int cb) { + return IPhone::GetAskString(psz, cb); +} + +void HostOpenUrl(const char *pszUrl) { + IPhone::OpenUrl(pszUrl); +} + +void HostSuspendModalLoop(DibBitmap *pbm) +{ + // Wait for WI to become active again + + LOG() << "Entering SuspendModalLoop"; + + base::Thread& thread = base::Thread::current(); + while (true) { + base::Message msg; + thread.Get(&msg); + if (msg.id == kidmAppSetFocus) { + break; + } + if (msg.id == kidmAppTerminate) { + thread.Post(&msg); + break; + } + thread.Dispatch(&msg); + } + + LOG() << "Leaving SuspendModalLoop"; +} + +bool ProcessMessage(base::Message *pmsg, Event *pevt) +{ + switch (pmsg->id) { + case kidmMouseDown: + pevt->eType = penDownEvent; + pevt->ff = kfEvtFinger; + break; + + case kidmMouseDown2: + pevt->eType = penDownEvent2; + pevt->ff = kfEvtFinger; + break; + + case kidmMouseUp: + pevt->eType = penUpEvent; + pevt->ff = kfEvtFinger; + break; + + case kidmMouseUp2: + pevt->eType = penUpEvent2; + pevt->ff = kfEvtFinger; + break; + + case kidmMouseMove: + pevt->eType = penMoveEvent; + pevt->ff = kfEvtFinger; + break; + + case kidmMouseMove2: + pevt->eType = penMoveEvent2; + pevt->ff = kfEvtFinger; + break; + + case kidmAppTerminate: + pevt->eType = appStopEvent; + pevt->ff = 0; + break; + + case kidmAppKillFocus: + pevt->eType = gameSuspendEvent; + pevt->ff = 0; + break; + + case base::kidmNullEvent: + pevt->eType = nullEvent; + pevt->ff = 0; + break; + + case base::kidmTransportEvent: + pevt->eType = transportEvent; + pevt->ff = 0; + break; + + case kidmAskStringEvent: + pevt->eType = askStringEvent; + pevt->ff = 0; + break; + + default: + return false; + } + + pevt->dw = 0; + pevt->x = pmsg->x; + pevt->y = pmsg->y; + pevt->ms = pmsg->ms; + if (pmsg->ff & kfMsgCoalesce) { + pevt->ff |= kfEvtCoalesce; + } + if (pmsg->ff & kfMsgCancelMode) { + pevt->dw = 1; + } + + SurfaceProperties props; + IPhone::GetSurfaceProperties(&props); + int cx = props.cxWidth; + int cy = props.cyHeight; + + if (gpdisp != NULL) { + ModeInfo mode; + gpdisp->GetMode(&mode); + switch (mode.nDegreeOrientation) { + case 0: + // Screen rotated 90 degrees but coordinates unrotated + pevt->x = pmsg->y; + pevt->y = (cy - 1) - pmsg->x; + break; + + case 90: + pevt->x = (cy - 1) - pmsg->y; + pevt->y = pmsg->x; + break; + + case 180: + pevt->x = (cx - 1) - pmsg->x; + pevt->y = (cy - 1) - pmsg->y; + break; + + case 270: + pevt->x = pmsg->y; + pevt->y = (cx - 1) - pmsg->x; + break; + } + } + + return true; +} + +bool HostGetEvent(Event *pevt, long ctWait) +{ + base::Thread& thread = base::Thread::current(); + while (true) { + base::Message msg; + if (!thread.Get(&msg, ctWait)) { + return false; + } + + if (msg.handler != NULL) { + thread.Dispatch(&msg); + continue; + } + + if (msg.id == kidmBreakEvent) { + return false; + } + + if (ProcessMessage(&msg, pevt)) { + return true; + } + } +} + +void HostOutputDebugString(char *pszFormat, ...) +{ +#ifdef DEBUG + va_list va; + va_start(va, pszFormat); + IPhone::Log(pszFormat, va); + va_end(va); +#endif +} + +long HostGetTickCount() +{ + return base::GetTickCount(); +} + +long HostGetMillisecondCount() +{ + return base::GetMillisecondCount(); +} + +long HostRunSpeedTests(DibBitmap *pbmSrc) +{ + return 0; +} + +dword HostGetCurrentKeyState(dword keyBit) +{ + return 0; +} + +bool HostIsPenDown() +{ + return false; +} + +void HostMessageBox(TCHAR *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + IPhone::MessageBox(pszFormat, va); + va_end(va); +} + +void HostGetUserName(char *pszBuff, int cb) +{ + strncpyz(pszBuff, "anonymous", cb); +} + +bool HostGetOwnerName(char *pszBuff, int cb, bool fShowError) +{ + strncpyz(pszBuff, "Player", cb); + return true; +} + +// UNDONE: prefix directory? + +FileHandle HostOpenFile(const char *pszFilename, word wf) +{ + char *pszMode; + if (wf == kfOfRead) + pszMode = "rb"; + else if (wf == kfOfWrite) + pszMode = "wb"; + else if (wf == (kfOfRead | kfOfWrite)) + pszMode = "rb+"; + + return (FileHandle)fopen((char *)pszFilename, pszMode); +} + +void HostCloseFile(FileHandle hf) +{ + fclose((FILE *)hf); +} + +dword HostWriteFile(FileHandle hf, void *pv, dword cb) +{ + return fwrite(pv, 1, cb, (FILE *)hf); +} + +dword HostReadFile(FileHandle hf, void *pv, dword cb) +{ + return fread(pv, 1, cb, (FILE *)hf); +} + +void HostSleep(dword ct) +{ + int cms = ct * 10; + struct timeval tvWait; + tvWait.tv_sec = cms / 1000; + tvWait.tv_usec = (cms % 1000) * 1000; + select(0, NULL, NULL, NULL, &tvWait); +} + +void HostGetSilkRect(int irc, Rect *prc) +{ + return; +} + +// Figure out what kind of sound device exists, and return a SoundDevice for it + +SoundDevice *HostOpenSoundDevice() +{ + return CreateIPhoneSoundDevice(); +} + +SoundDevice::~SoundDevice() +{ +} + +// Used for sound buffer maintenance requirements + +SoundDevice *gpsnddService; +void SetSoundServiceDevice(SoundDevice *psndd) +{ + gpsnddService = psndd; +} + +bool HostSoundServiceProc() +{ + if (gpsnddService == NULL) + return false; + gpsnddService->ServiceProc(); + return true; +} + +void HostGetCurrentDate(Date *pdate) +{ + time_t result = time(NULL); + struct tm *ptm = localtime(&result); + pdate->nYear = ptm->tm_year + 1900; + pdate->nMonth = ptm->tm_mon + 1; + pdate->nDay = ptm->tm_mday; +} + +bool HostSavePreferences(void *pv, int cb) +{ + LOG() << IPhone::GetPrefsFilename(); + + FILE *pf = fopen(IPhone::GetPrefsFilename(), "wb"); + if (pf == NULL) { + LOG() << "error opening preferences! " << errno; + return false; + } + if (fwrite(pv, cb, 1, pf) != 1) { + LOG() << "error writing preferences! " << errno; + fclose(pf); + return false; + } + fclose(pf); + return true; +} + +int HostLoadPreferences(void *pv, int cb) +{ + FILE *pf = fopen(IPhone::GetPrefsFilename(), "rb"); + if (pf == NULL) { + return -1; + } + + // Read prefs + + int cbRead = (int)fread(pv, 1, cb, pf); + fclose(pf); + return cbRead; +} + +const char *HostGetMainDataDir() +{ + return IPhone::GetMainDataDir(); +} + +void HostNotEnoughMemory(bool fStorage, dword cbFree, dword cbNeed) +{ + HostMessageBox(TEXT("Need %ld bytes of memory but only %ld bytes are free!"), cbNeed, cbFree); +} + +bool HostEnumAddonFiles(Enum *penm, char *pszAddonDir, int cbDir, + char *pszAddon, int cb) +{ + // PackManager is the way to do this now + return false; +} + +} // namespace wi diff --git a/game/iphone/htplatform.h b/game/iphone/htplatform.h new file mode 100644 index 0000000..f20594a --- /dev/null +++ b/game/iphone/htplatform.h @@ -0,0 +1,150 @@ +// htplatform.h + +//#include +#include +#include +#include +#include +#include +#include +#include +#include "game/iphone/iphone.h" +#include "game/iphone/iphonepackfile.h" + +// To determine if running on simulator, the sdk sets TARGET_IPHONE_SIMULATOR +// to 0 or 1. + +#include +#if TARGET_IPHONE_SIMULATOR == 1 +#define SIMULATOR +#endif + +namespace wi { + +#define MakeDword(a, b, c, d) ((a) | ((b) << 8) | ((c) << 16) | ((d) << 24)) +#define BigWord(x) ((((x)&0xFF)<<8) | (((x)&0xFF00)>>8)) +#define BigDword(x) ((((x)&0xFF)<<24) | (((x)&0xFF00)<<8) | (((x)&0xFF0000)>>8) | (((x)&0xFF000000)>>24)) + +typedef char TCHAR; +#define TEXT(str) str + +#define _MAX_PATH PATH_MAX +#define MAX_PATH PATH_MAX + +#define chrPageUp 0xb +#define chrPageDown 0xc +#define chrUp chrPageUp +#define chrDown chrPageDown +#define vchrMenu 0x105 +#define vchrFind 0x10a +#define vchrCalc 0x10b +#define penDownEvent 1 +#define penUpEvent 2 +#define penMoveEvent 3 +#define keyDownEvent 4 +#define penDownEvent2 5 +#define penUpEvent2 6 +#define penMoveEvent2 7 +#define appStopEvent 22 +#define chrBackspace 0x0008 +#define chrDelete 0x007f + +#define keyBitPower 0x0001 // Power key +#define keyBitPageUp 0x0002 // Page-up +#define keyBitPageDown 0x0004 // Page-down +#define keyBitHard1 0x0008 // App #1 +#define keyBitHard2 0x0010 // App #2 +#define keyBitHard3 0x0020 // App #3 +#define keyBitHard4 0x0040 // App #4 +#define keyBitCradle 0x0080 // Button on cradle +#define keyBitAntenna 0x0100 // Antenna "key" +#define keyBitContrast 0x0200 // Contrast key + +// From PalmSG + +#define keyBitDpadLeft 0x01000000 // d-pad left +#define keyBitDpadRight 0x02000000 // d-pad right +#define keyBitDpadButton 0x04000000 // d-pad center button + +// From PalmSource + +#define keyBitRockerUp 0x00010000 // 5-way rocker +#define keyBitRockerDown 0x00020000 +#define keyBitRockerLeft 0x00040000 +#define keyBitRockerRight 0x00080000 +#define keyBitRockerCenter 0x00100000 + +#define keyBitsAll 0xFFFFFFFF // all keys + +struct ModeInfo +{ + int cx; + int cy; + int cyGraffiti; + int nDepth; + bool fNative; + int nDegreeOrientation; +}; +#define kcmodesMax 16 + +struct Palette; +class Rect; +class DibBitmap; +class UpdateMap; +class SpriteManager; +class FormMgr; + +class Display // disp +{ +public: + Display(); + ~Display(); + + bool Init(); + void SetPalette(Palette *ppal); + int GetModeCount(); + void GetModeInfo(int imode, ModeInfo *pmode); + int GetMode(ModeInfo *pmode); + bool SetMode(int imode); + void DrawText(const char *psz, int x, int y, word wf); + void DrawFrameInclusive(Rect *prc); + DibBitmap *GetBackDib(); + DibBitmap *GetFrontDib(); + DibBitmap *GetClippingDib(); + void GetHslAdjustments(short *pnHueOffset, short *pnSatMultiplier, short *pnLumOffset); + void FrameStart(); + void FrameComplete(int cfrmm, UpdateMap **apupd, Rect *arc, bool fScrolled); + void ResetScrollOffset(); + SpriteManager *GetSpriteManager(); + void SetFormMgrs(FormMgr *pfrmmSim, FormMgr *pfrmmInput); + +private: + int m_imode; + ModeInfo m_amodeInfo[kcmodesMax]; + int m_cmodes; + + int m_cx; + int m_cy; + DibBitmap *m_pbmBack; + DibBitmap *m_pbmFront; + DibBitmap *m_pbmClip; + bool m_fIPAQ3800Hack; +}; + +#define kfDtClearLine 1 + +extern Display *gpdisp; + +class PlatformSprite { +public: + virtual void Draw(void *pv, Size *psiz) = 0; +}; + +class SoundDevice; +void SetSoundServiceDevice(SoundDevice *psndd); +SoundDevice *CreateIPhoneSoundDevice(); +void PrependDataDirectory(char *pszIn, char *pszOut); + +extern IPhonePackFileReader gpakr; + +} // namespace wi diff --git a/game/iphone/icon.png b/game/iphone/icon.png new file mode 100644 index 0000000..6908552 Binary files /dev/null and b/game/iphone/icon.png differ diff --git a/game/iphone/icon_old.png b/game/iphone/icon_old.png new file mode 100644 index 0000000..c432d40 Binary files /dev/null and b/game/iphone/icon_old.png differ diff --git a/game/iphone/icon_old2.png b/game/iphone/icon_old2.png new file mode 100644 index 0000000..6908552 Binary files /dev/null and b/game/iphone/icon_old2.png differ diff --git a/game/iphone/input.h b/game/iphone/input.h new file mode 100644 index 0000000..0a20f78 --- /dev/null +++ b/game/iphone/input.h @@ -0,0 +1,29 @@ +#ifndef __INPUT_H__ +#define __INPUT_H__ + +#include "base/messagequeue.h" + +const int kidmMouseDown = 1; +const int kidmMouseUp = 2; +const int kidmMouseMove = 3; +const int kidmMouseDown2 = 4; +const int kidmMouseUp2 = 5; +const int kidmMouseMove2 = 6; +const int kidmAppTerminate = 7; +const int kidmBreakEvent = 8; +//const int kidmNullEvent = 9; // now: base::kidmNullEvent +//const int kidmTransportEvent = 10; // now: base::kidmTransportEvent +const int kidmReceivedResponse = 11; +const int kidmReceivedData = 12; +const int kidmFinishedLoading = 13; +const int kidmError = 14; +const int kidmAnimationTimer = 15; +const int kidmDestroyAfterAnimation = 16; +const int kidmAppSetFocus = 17; +const int kidmAppKillFocus = 18; +const int kidmAskStringEvent = 19; +const int kidmKeyDown = 20; + +extern base::MessageQueue gmq; + +#endif // __INPUT_H__ diff --git a/game/iphone/inputcontroller.h b/game/iphone/inputcontroller.h new file mode 100644 index 0000000..0fe5da8 --- /dev/null +++ b/game/iphone/inputcontroller.h @@ -0,0 +1,32 @@ +#ifndef __INPUTCONTROLLER_H__ +#define __INPUTCONTROLLER_H__ + +#import +#import +#import +#import +#import + +@protocol InputDelgate; +@class InputController; + +@protocol InputDelegate +- (void)onDone:(InputController *)c text:(NSString *)text; +@end + +@interface InputController : NSObject { + NSString *title_; + NSString *default_string_; + UIKeyboardType keyboard_type_; + BOOL secure_; + UIAlertView *alert_view_; + int cchMax_; + id delegate_; +} +- (id)init:(NSString *)title default:(NSString *)default_string + keyboardType:(UIKeyboardType)keyboard_type delegate:(id)delegate + maxChars:(int)cchMax secure:(BOOL)secure; +- (void)loadView; +@end + +#endif // __INPUTCONTROLLER_H__ diff --git a/game/iphone/inputcontroller.mm b/game/iphone/inputcontroller.mm new file mode 100644 index 0000000..3e8d45b --- /dev/null +++ b/game/iphone/inputcontroller.mm @@ -0,0 +1,66 @@ +#import "game/iphone/inputcontroller.h" + +@implementation InputController + +- (id)init:(NSString *)title default:(NSString *)default_string + keyboardType:(UIKeyboardType)keyboard_type delegate:(id)delegate + maxChars:(int)cchMax secure:(BOOL)secure { + + title_ = title; + [title_ retain]; + default_string_ = default_string; + [default_string_ retain]; + keyboard_type_ = keyboard_type; + delegate_ = delegate; + [delegate_ retain]; + cchMax_ = cchMax; + secure_ = secure; + alert_view_ = nil; + + [self loadView]; + + return self; +} + +- (void)dealloc { + [title_ release]; + [default_string_ release]; + [alert_view_ release]; + [delegate_ release]; + [super dealloc]; +} + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)way +{ + return way == UIInterfaceOrientationLandscapeRight; +} + +- (void)loadView { + UIAlertView *view = [[UIAlertView alloc] initWithTitle:title_ + message:nil delegate:self cancelButtonTitle:@"Cancel" + otherButtonTitles:@"OK", nil]; + alert_view_ = view; + alert_view_.alertViewStyle = secure_ ? UIAlertViewStyleSecureTextInput : UIAlertViewStylePlainTextInput; + [alert_view_ textFieldAtIndex:0].delegate = self; + [alert_view_ show]; +} + +- (void)alertView:(UIAlertView *)actionSheet + clickedButtonAtIndex:(NSInteger)buttonIndex { + if (buttonIndex > 0) { + [delegate_ onDone:self text:[alert_view_ textFieldAtIndex:0].text]; + } +} + +- (BOOL)textField:(UITextField *)textField + shouldChangeCharactersInRange:(NSRange)range + replacementString:(NSString *)string { + if (cchMax_ == -1) { + return YES; + } + if (textField.text.length >= cchMax_ && range.length == 0) { + return NO; + } + return YES; +} +@end diff --git a/game/iphone/iphone.h b/game/iphone/iphone.h new file mode 100644 index 0000000..2fd4a97 --- /dev/null +++ b/game/iphone/iphone.h @@ -0,0 +1,64 @@ +#ifndef __IPHONE_H__ +#define __IPHONE_H__ + +#include +#include "game/ht.h" +#include "game/chatcontroller.h" + +namespace wi { + +struct SurfaceProperties { + int cxWidth; + int cyHeight; + int cbxPitch; + int cbyPitch; + unsigned short ffFormat; +}; + +const unsigned short kfDirect565 = 0x0001; +const unsigned short kfDirect888 = 0x0002; + +class SpriteManager; +class FormMgr; + +class IPhone +{ +public: + static void OpenUrl(const char *pszUrl); + static void Log(char *pszFormat, ...); + static void Log(char *pszFormat, va_list va); + static void MessageBox(char *pszFormat, va_list va); + static void Break(); + static void GetSurfaceProperties(SurfaceProperties *pprops); + static void FrameStart(); + static void FrameComplete(int cfrmm, UpdateMap **apupd, Rect *arc, + bool fScrolled); + static void ResetScrollOffset(); + static SpriteManager *GetSpriteManager(); + static void SetFormMgrs(FormMgr *pfrmmSimUI, FormMgr *pfrmmInput); + static DibBitmap *CreateFrontDib(int cx, int cy, int nDegreeOrientation); + static void SetPalette(Palette *ppal); + static const char *GetMissionPacksDir(); + static const char *GetMissionPackInfosDir(); + static const char *GetSaveGamesDir(); + static const char *GetPrefsFilename(); + static const char *GetMainDataDir(); + static const char *GetTempDir(); + static const char *GetCompletesDir(); + static const char *GetStaticUUID(); + static void InitiateAsk(const char *title, int max, const char *def, + int keyboard, bool secure); + static void GetAskString(char *psz, int cb); + static IChatController *GetChatController(); + static void InitiateWebView(const char *title, const char *url); + static bool IsExiting(); + static void GameThreadStart(void *pv); + static int main(int argc, char **argv); + +private: + static void PrepareDirs(); +}; + +} // namespace wi + +#endif // __IPHONE_H__ diff --git a/game/iphone/iphone.mm b/game/iphone/iphone.mm new file mode 100644 index 0000000..985cdb3 --- /dev/null +++ b/game/iphone/iphone.mm @@ -0,0 +1,499 @@ +#import +#import +#import +#import +#import + +#include +#include +#include +#include "iphone.h" +#include "input.h" +#include "base/log.h" +#include "base/thread.h" + +#import "game/iphone/wiviewcontroller.h" +#import "game/iphone/chatviewcontroller.h" +#include "game/ht.h" + +@interface IPhoneAppDelegate : NSObject +{ + UIWindow *m_window; + WiViewController *m_vcwi; + ChatViewController *m_vcchat; + wi::IChatController *m_pchat; + id m_view; + base::Thread *m_game_thread; + bool m_fExiting; + char *m_pszMissionPacksDir; + char *m_pszMissionPackInfosDir; + char *m_pszSaveGamesDir; + char *m_pszPrefsFilename; + char *m_pszMainDataDir; + char *m_pszTempDir; + char *m_pszCompletesDir; + char *m_pszUUID; +} +@end + +namespace wi { +IPhoneAppDelegate *g_appDelegate; +} + +@implementation IPhoneAppDelegate + ++ (void)initialize +{ +} + +- (void)allocPath:(char **)ppsz baseDir:(const char *)baseDir + subDir:(const char *)subDir +{ + int cb = strlen(baseDir) + strlen(subDir) + 1; + char *psz = (char *)malloc(cb); + strcpy(psz, baseDir); + strcat(psz, subDir); + *ppsz = psz; +} + +- (void)allocPaths +{ + // .app -> main app directory + // .app/htdata832.pdb, htsfx.pdb -> main data files + // Library/MissionPacks/ -> downloaded mission packs + // Library/SaveGames/ -> saved games + // Library/prefs.bin -> game prefs + // tmp -> tmp directory + + // This returns the home directory; .app, Library, and Documents are in it + NSString *homeDir = NSHomeDirectory(); + const char *pszHomeDir = [homeDir cStringUsingEncoding: + [NSString defaultCStringEncoding]]; + + [self allocPath:&m_pszMissionPacksDir baseDir:pszHomeDir + subDir:"/Library/MissionPacks"]; + [self allocPath:&m_pszMissionPackInfosDir baseDir:pszHomeDir + subDir:"/Library/MissionPackInfos"]; + [self allocPath:&m_pszSaveGamesDir baseDir:pszHomeDir + subDir:"/Library/SaveGames"]; + [self allocPath:&m_pszPrefsFilename baseDir:pszHomeDir + subDir:"/Library/prefs.bin"]; + [self allocPath:&m_pszTempDir baseDir:pszHomeDir subDir:"/tmp"]; + [self allocPath:&m_pszCompletesDir baseDir:pszHomeDir + subDir:"/Library/Completes"]; + + // Make the directories under Library + mkdir(m_pszMissionPacksDir, 0755); + mkdir(m_pszMissionPackInfosDir, 0755); + mkdir(m_pszSaveGamesDir, 0755); + mkdir(m_pszCompletesDir, 0755); + + // Get directories off .app + NSBundle *bundle = [NSBundle mainBundle]; + NSString *appDir = [bundle bundlePath]; + const char *pszAppDir = [appDir cStringUsingEncoding: + [NSString defaultCStringEncoding]]; + + [self allocPath:&m_pszMainDataDir baseDir:pszAppDir + subDir:""]; + +#if 0 + NSArray *pathsLibrary = NSSearchPathForDirectoriesInDomains( + NSLibraryDirectory, NSUserDomainMask, YES); + NSString *libraryDirectory = [pathsLibray objectAtIndex:0]; +#endif +} + +- (void)initiateAsk:(const char *)title max:(int)max default:(const char *)def + keyboard:(int)keyboard secure:(BOOL)secure +{ + return [m_vcwi initiateAsk:title max:max default:def keyboard:keyboard + secure:secure]; +} + +- (void)getAskString:(char *)psz size:(int)cb +{ + return [m_vcwi getAskString:psz size:cb]; +} + +- (wi::IChatController *)getChatController +{ + if (m_pchat == NULL) { + m_pchat = new wi::ChatController(m_vcwi, m_vcchat); + } + return m_pchat; +} + +- (void)initiateWebView:(const char *)title withUrl:(const char *)url +{ + [m_vcwi initiateWebView:title withUrl:url]; +} + +#if 0 +- (void)application:(UIApplication *)app + willChangeStatusBarOrientation:(UIInterfaceOrientation)orientation + duration:(NSTimeInterval)duration +{ + // This prevents the view from autorotating to portrait in the simulator + if ((orientation == UIInterfaceOrientationPortrait) || + (orientation== UIInterfaceOrientationPortraitUpsideDown)) { + [app setStatusBarOrientation: + UIInterfaceOrientationLandscapeRight animated:NO]; + } +} +#endif + +- (void)applicationDidFinishLaunching:(UIApplication *)application +{ + // Create the window and view + + wi::g_appDelegate = self; + m_fExiting = false; + + // Set these here rather than in Info.plist, because devices with OS's + // before iPhone OS 2.1 don't honor the Info.plist settings. + // Hide the status bar. Unfortunately, the status bar area still + // eat events. No known workaround currently. + // NOTE: This is fixed on iOS >= 7 by the implementation of prefersStatusBarHidden + // in wiviewcontroller.mm. + [application setStatusBarHidden:YES animated:NO]; + + // Tell the application object to turn off the screen dimming idle + // timer. + application.idleTimerDisabled = YES; + + CGRect frame = [[UIScreen mainScreen] bounds]; + m_window = [[UIWindow alloc] initWithFrame: frame]; + + // Create the Wi view controller, remember the view for shortcut purposes + m_vcwi = [[WiViewController alloc] initWithNibName:nil bundle:nil]; + m_window.rootViewController = m_vcwi; + m_view = [[m_vcwi view] retain]; + + m_vcchat = [[ChatViewController alloc] init:nil parent:m_view]; + m_pchat = NULL; + + // Show the window with table view + [m_window addSubview:m_view]; + [m_window makeKeyAndVisible]; + + // Must do this after makeKeyAndVisible, in order for it all to rotate + [application setStatusBarOrientation:UIInterfaceOrientationLandscapeRight + animated:NO]; + + // Alloc the key directories + [self allocPaths]; + + // Generate a UUID and keep around for later query + CFUUIDRef uuid = CFUUIDCreate(NULL); + NSString *strUUID = (NSString *)CFUUIDCreateString(NULL, uuid); + CFRelease(uuid); + const char *pszUUID = [strUUID cStringUsingEncoding: + [NSString defaultCStringEncoding]]; + m_pszUUID = (char *)malloc(strlen(pszUUID) + 1); + strcpy(m_pszUUID, pszUUID); + CFRelease(strUUID); + + // Spin off a thread to run the game + m_game_thread = new base::Thread(); + m_game_thread->Start(wi::IPhone::GameThreadStart, NULL); + [m_vcwi setGameThread: m_game_thread]; +} + +- (wi::SpriteManager *)getSpriteManager +{ + return [m_view getSpriteManager]; +} + +- (void)setFormMgrs:(wi::FormMgr *)pfrmmSim input:(wi::FormMgr *)pfrmmInput +{ + [m_view setFormMgrs:pfrmmSim input:pfrmmInput]; +} + +- (const char *)getMissionPacksDir +{ + return m_pszMissionPacksDir; +} + +- (const char *)getMissionPackInfosDir +{ + return m_pszMissionPackInfosDir; +} + +- (const char *)getSaveGamesDir +{ + return m_pszSaveGamesDir; +} + +- (const char *)getPrefsFilename +{ + return m_pszPrefsFilename; +} + +- (const char *)getMainDataDir +{ + return m_pszMainDataDir; +} + +- (const char *)getTempDir +{ + return m_pszTempDir; +} + +- (const char *)getCompletesDir +{ + return m_pszCompletesDir; +} + +- (const char *)staticUUID +{ + return m_pszUUID; +} + +- (bool)isExiting +{ + return m_fExiting; +} + +- (void)applicationDidBecomeActive:(UIApplication *)application +{ + // WI is now active (app launch, device turned on / unlocked) + + m_game_thread->Post(kidmAppSetFocus, NULL); +} + +- (void)applicationWillResignActive:(UIApplication *)application +{ + // WI is not active (app terminating, device turned off / locked) + // If already exiting, don't do anything special. + + if (m_fExiting) { + return; + } + + m_game_thread->Post(kidmAppKillFocus, NULL); +} + +- (void)applicationWillTerminate:(UIApplication *)application +{ + // This method is called when the user presses the device exit button. + // Ask the game thread to exit. + + m_fExiting = true; + m_game_thread->Post(kidmAppTerminate, NULL); + + // This will tell the thread to exit, and block until it does + delete m_game_thread; + m_game_thread = NULL; +} + +- (void)getSurfaceProperties:(wi::SurfaceProperties *)pprops +{ + [m_view getSurfaceProperties:pprops]; +} + +- (void)frameStart +{ + [m_view frameStart]; +} + +- (void)frameComplete:(int)cfrmm maps:(wi::UpdateMap **)apupd + rects:(wi::Rect *)arc scrolled:(bool)fScrolled +{ + [m_view frameComplete:cfrmm maps:apupd rects:arc scrolled:fScrolled]; +} + +- (void)resetScrollOffset +{ + [m_view resetScrollOffset]; +} + +- (wi::DibBitmap *)createFrontDibWithOrientation:(int)nDegreeOrientation + width:(int)cx height:(int)cy +{ + return [m_view createFrontDibWithOrientation:nDegreeOrientation + width:cx height:cy]; +} + +- (void)setPalette:(wi::Palette *)ppal +{ + [m_view setPalette:ppal]; +} +@end + +// +// C++ wrapper class around obj-c IPhoneApp class +// + +namespace wi { + +void IPhone::OpenUrl(const char *pszUrl) { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + NSString *s = [NSString stringWithCString:pszUrl + encoding:[NSString defaultCStringEncoding]]; + [[UIApplication sharedApplication] openURL:[NSURL URLWithString:s]]; + [pool release]; +} + +void IPhone::Log(char *pszFormat, va_list va) +{ +#if 0 + LOGX() << base::Log::vFormat(pszFormat, va); +#endif + + char sz[512]; + vsnprintf(sz, sizeof(sz), pszFormat, va); + +#ifdef SIMULATOR + printf("%s\n", sz); +#else + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + NSString *s = [NSString stringWithCString:sz + encoding:[NSString defaultCStringEncoding]]; + NSLog(s); + [pool release]; +#endif +} + +void IPhone::Log(char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + Log(pszFormat, va); + va_end(va); +} + +void IPhone::MessageBox(char *pszFormat, va_list va) +{ + Log(pszFormat, va); +} + +void IPhone::Break() +{ + Log("BREAK!!"); +} + +void IPhone::GetSurfaceProperties(SurfaceProperties *pprops) +{ + [g_appDelegate getSurfaceProperties: pprops]; +} + +void IPhone::FrameStart() +{ + [g_appDelegate frameStart]; +} + +void IPhone::FrameComplete(int cfrmm, UpdateMap **apupd, Rect *arc, + bool fScrolled) +{ + [g_appDelegate frameComplete:cfrmm maps:apupd rects:arc scrolled:fScrolled]; +} + +void IPhone::ResetScrollOffset() +{ + [g_appDelegate resetScrollOffset]; +} + +SpriteManager *IPhone::GetSpriteManager() +{ + return [g_appDelegate getSpriteManager]; +} + +void IPhone::SetFormMgrs(FormMgr *pfrmmSim, FormMgr *pfrmmInput) +{ + return [g_appDelegate setFormMgrs:pfrmmSim input:pfrmmInput]; +} + +DibBitmap *IPhone::CreateFrontDib(int cx, int cy, int nDegreeOrientation) +{ + return [g_appDelegate createFrontDibWithOrientation:nDegreeOrientation width:cx height:cy]; +} + +void IPhone::SetPalette(Palette *ppal) +{ + [g_appDelegate setPalette:ppal]; +} + +const char *IPhone::GetMissionPacksDir() +{ + return [g_appDelegate getMissionPacksDir]; +} + +const char *IPhone::GetMissionPackInfosDir() +{ + return [g_appDelegate getMissionPackInfosDir]; +} + +const char *IPhone::GetSaveGamesDir() +{ + return [g_appDelegate getSaveGamesDir]; +} + +const char *IPhone::GetPrefsFilename() +{ + return [g_appDelegate getPrefsFilename]; +} + +const char *IPhone::GetMainDataDir() +{ + return [g_appDelegate getMainDataDir]; +} + +const char *IPhone::GetTempDir() +{ + return [g_appDelegate getTempDir]; +} + +const char *IPhone::GetCompletesDir() +{ + return [g_appDelegate getCompletesDir]; +} + +const char *IPhone::GetStaticUUID() +{ + return [g_appDelegate staticUUID]; +} + +bool IPhone::IsExiting() +{ + return [g_appDelegate isExiting]; +} + +void IPhone::InitiateAsk(const char *title, int max, const char *def, + int keyboard, bool secure) +{ + BOOL s = secure ? YES : NO; + return [g_appDelegate initiateAsk:title max:max default:def + keyboard:keyboard secure:s]; +} + +void IPhone::GetAskString(char *psz, int cb) +{ + return [g_appDelegate getAskString:psz size:cb]; +} + +IChatController *IPhone::GetChatController() +{ + return [g_appDelegate getChatController]; +} + +void IPhone::InitiateWebView(const char *title, const char *url) { + [g_appDelegate initiateWebView:title withUrl:url]; +} + +void IPhone::GameThreadStart(void *pv) { + NSLog(@"Starting game..."); + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + wi::GameMain(""); + [pool release]; +} + +int IPhone::main(int argc, char **argv) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + return UIApplicationMain(argc, argv, nil, @"IPhoneAppDelegate"); +} + +} // namespace wi + diff --git a/game/iphone/iphoneanimsprite.h b/game/iphone/iphoneanimsprite.h new file mode 100644 index 0000000..d5aa5e2 --- /dev/null +++ b/game/iphone/iphoneanimsprite.h @@ -0,0 +1,62 @@ +#ifndef __IPHONEANIMSPRITE_H__ +#define __IPHONEANIMSPRITE_H__ + +#include "base/criticalsection.h" +#include "base/messagehandler.h" +#include "inc/basictypes.h" +#include "game/ht.h" +#import +#import + +namespace wi { + +class IPhoneAnimSprite : public AnimSprite, base::MessageHandler { +public: + IPhoneAnimSprite(SpriteManager *psprm); + ~IPhoneAnimSprite(); + + // AnimationSprite + virtual void SetPalette(Palette *ppal); + virtual void CaptureFrame(UnitGob *pgob); + virtual void SetScaleAnimation(float nScaleStart, float nScaleEnd, + dword cms, dword cmsRate, bool fAutoDestroy); + + // Sprite + virtual SpriteManager *GetManager() { return psprm_; } + virtual void SetScale(float scale); + virtual void SetPosition(int x, int y); + virtual void Show(bool fShow); + virtual bool IsVisible() { return fVisible_; } + virtual void GetBounds(Rect *prc); + + // PlatformSprite + virtual void Draw(void *pv, Size *psiz); + +private: + bool CreateLayer(UnitGob *pgob); + + // MessageHandler + virtual void OnMessage(base::Message *pmsg); + + base::CriticalSection crit_; + Rect rcUI_; + int x_, y_; + Rect rcBase_; + float nScale_; + bool fVisible_; + dword hash_; + int cx_, cy_; + int xOrigin_, yOrigin_; + CGLayer *layer_; + SpriteManager *psprm_; + dword mp8bpp32bpp_[256]; + float nScaleStart_, nScaleEnd_; + long msAnimateStart_; + dword cmsAnimate_; + dword cmsAnimationRate_; + bool fAutoDestroy_; +}; + +} // namespace wi + +#endif // __IPHONEANIMSPRITE_H__ diff --git a/game/iphone/iphoneanimsprite.mm b/game/iphone/iphoneanimsprite.mm new file mode 100644 index 0000000..a01b894 --- /dev/null +++ b/game/iphone/iphoneanimsprite.mm @@ -0,0 +1,359 @@ +#include "base/tick.h" +#include "base/thread.h" +#include "game/iphone/iphoneanimsprite.h" +#include "game/iphone/cgdib.h" +#include "game/iphone/input.h" +#include +#import + +namespace wi { + +IPhoneAnimSprite::IPhoneAnimSprite(SpriteManager *psprm) { + hash_ = 0; + cx_ = 0; + cy_ = 0; + xOrigin_ = 0; + yOrigin_ = 0; + layer_ = NULL; + nScale_ = 1.0f; + x_ = 0; + y_ = 0; + fVisible_ = true; + psprm_ = psprm; +} + +IPhoneAnimSprite::~IPhoneAnimSprite() { + psprm_->Remove(this); + + crit_.Enter(); + if (layer_ != NULL) { + CGLayerRelease(layer_); + layer_ = NULL; + } + crit_.Leave(); +} + +void IPhoneAnimSprite::CaptureFrame(UnitGob *pgob) { + // Layer already created? + + dword hash = pgob->GetAnimationHash(); + if (hash == hash_) { + return; + } + + // Enter critical section since drawing occurs on the main thread + + crit_.Enter(); + hash_ = hash; + + // Remember UIBounds + + UnitConsts *puntc = GetUnitConsts(pgob->GetType()); + rcUI_.FromWorldRect(&puntc->wrcUIBounds); + + // Create layer + + bool fSuccess = CreateLayer(pgob); + crit_.Leave(); + if (!fSuccess) { + return; + } + + // Add this sprite + psprm_->Add(this); +} + +bool IPhoneAnimSprite::CreateLayer(UnitGob *pgob) { + // Get height/width + Rect rcAni; + pgob->GetAnimationBounds(&rcAni, false); + cx_ = (rcAni.Width() + 1) & ~1; + cy_ = rcAni.Height(); + + // Remember the bounds of the base. This'll be used later for y + // positioning. + + pgob->GetAnimationBounds(&rcBase_, true); + + // rcAni has negative left and top. This identifies the origin. + xOrigin_ = rcAni.left; + yOrigin_ = rcAni.top; + + // Alloc the 8bpp buffer. + int cp = cx_ * cy_; + byte *pb8 = new byte[cp]; + if (pb8 == NULL) { + return false; + } + + // Draw the animation into an 8 bit DibBitmap + // The 8->32 conversion palette has been tweaked so that 255 + // will map to RGBA transparent on output. + + DibBitmap bm; + bm.Init(pb8, cx_, cy_); + memset(pb8, 255, cp); + + // Subvert the TBitmap shdowing to our purpose. + // The background is 255. Force TBitmap shadowing to turn this into 254. + // 254 has been to RGBA color with appropriate alpha, when the 8->32bpp + // conversion occurs. + + byte bSav = gmpiclriclrShadow[255]; + gmpiclriclrShadow[255] = 254; + pgob->DrawAnimation(&bm, -xOrigin_, -yOrigin_); + gmpiclriclrShadow[255] = bSav; + + // Alloc the 32bpp buffer. + dword *pdw32 = new dword[cp]; + if (pdw32 == NULL) { + delete pb8; + return false; + } + + // Convert to 32bpp. IPhone will rotate at draw time. + byte *pbT = pb8; + dword *pdwT = pdw32; + int cpT = cp; + while (cpT-- != 0) { + *pdwT++ = mp8bpp32bpp_[*pbT++]; + } + delete pb8; + + // Create the BitmapContext, which allows passing in of pixels + // Create it with an alpha channel. + + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CGContextRef bmCtx = CGBitmapContextCreate(pdw32, cx_, cy_, + 8, cx_ * 4, colorSpace, kCGImageAlphaPremultipliedFirst | + kCGBitmapByteOrderDefault); + CGColorSpaceRelease(colorSpace); + if (bmCtx == NULL) { + delete pdw32; + return false; + } + + // Drawing occurs on the main thread, so the layer needs to be + // synchronized + + if (layer_ != NULL) { + CGLayerRelease(layer_); + layer_ = NULL; + } + if (layer_ == NULL) { + CGSize size; + size.width = cx_; + size.height = cy_; + layer_ = CGLayerCreateWithContext(bmCtx, size, NULL); + } + + if (layer_ == NULL) { + CGContextRelease(bmCtx); + delete pdw32; + return false; + } + + // Create an image from the bitmap context + // Done with the bitmap context and buffer. + + CGImageRef image = CGBitmapContextCreateImage(bmCtx); + CGContextRelease(bmCtx); + delete pdw32; + + // Draw this image into the layer. The layer is only a speed up. + // Technically, drawing could create a new image each time. + + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + CGContextRef ctx = CGLayerGetContext(layer_); + CGRect rc = CGRectMake(0, 0, cx_, cy_); + CGContextDrawImage(ctx, rc, image); + CGImageRelease(image); + [pool release]; + return true; +} + +void IPhoneAnimSprite::Draw(void *pv, Size *psiz) { + if (!fVisible_) { + return; + } + + // This gets called on the drawing thread. + // The coordinate space pre-rotated to be the game coordinate space but at + // sector 0 (0, 0 is at bottom left). As always, xOrigin_ and yOrigin_ + // are negative. + + crit_.Enter(); + + Rect rcBounds; + GetBounds(&rcBounds); + CGRect rc; + rc.origin.x = x_ + rcBounds.left; + rc.origin.y = psiz->cy - (y_ + rcBounds.top) - rcBounds.Height(); + rc.size.width = rcBounds.Width(); + rc.size.height = rcBounds.Height(); + + if (layer_ != NULL) { + CGContextRef ctx = (CGContextRef)pv; + CGContextDrawLayerInRect(ctx, rc, layer_); + } + + crit_.Leave(); +} + +void IPhoneAnimSprite::GetBounds(Rect *prc) { + base::CritScope cs(&crit_); + + // Scale the UI rect, centered on the non-scaled UI rect + + Rect rcUIScaled = rcUI_; + rcUIScaled.Inflate(rcUI_.Width() * (nScale_ - 1.0f) / 2, + rcUI_.Height() * (nScale_ - 1.0f) / 2); + + // Center the height within the scaled UI rect, so it is centered under + // the finger. Use the base height, since that doesn't change as other + // parts animate. Offset by the base origin so the base stays in one + // place (even if the height changes). Finally, use the same percentage + // offset from the UI rect to position correctly. + + int xNew = rcUIScaled.left + (xOrigin_ - rcUI_.left) * nScale_; + int yNew = (rcUIScaled.top + rcUIScaled.bottom - + rcBase_.Height() * nScale_) / 2 - rcBase_.top * nScale_ + + yOrigin_ * nScale_; + int cxNew = (float)cx_ * nScale_; + int cyNew = (float)cy_ * nScale_; + + prc->left = xNew; + prc->top = yNew; + prc->right = xNew + cxNew; + prc->bottom = yNew + cyNew; +} + +void IPhoneAnimSprite::SetPalette(Palette *ppal) { + // Set the palette mapping table + // Note the AMXs use the first 131 colors of the palette. + for (int n = 0; n < BigWord(ppal->cEntries); n++) { + byte *pb = (byte *)&mp8bpp32bpp_[n]; + *pb++ = 255; + *pb++ = ppal->argb[n][0]; + *pb++ = ppal->argb[n][1]; + *pb++ = ppal->argb[n][2]; + } + + // Make the last color of the palete RGBA for transparent. + // We'll fill the 8bpp image with this, so that at 8->32 conversion, + // we get transparency. + + mp8bpp32bpp_[255] = 0; + + // Make the second to last 40% black. AMX transparent will map to this + // with clever remapping. 40% is equivalent to tbitmap shadowmap. + + byte *pb = (byte *)&mp8bpp32bpp_[254]; + *pb++ = 102; + *pb++ = 0; + *pb++ = 0; + *pb++ = 0; +} + +void IPhoneAnimSprite::SetScale(float nScale) { + if (nScale_ == nScale) { + return; + } + nScale_ = nScale; + psprm_->Update(this); +} + +void IPhoneAnimSprite::SetPosition(int x, int y) { + if (x_ == x && y_ == y) { + return; + } + x_ = x; + y_ = y; + psprm_->Update(this); +} + +void IPhoneAnimSprite::Show(bool fShow) { + if (fVisible_ == fShow) { + return; + } + fVisible_ = fShow; + psprm_->Update(this); +} + +void IPhoneAnimSprite::SetScaleAnimation(float nScaleStart, float nScaleEnd, + dword cms, dword cmsRate, bool fAutoDestroy) { +#if 1 + SetScale(nScaleEnd); + return; +#endif + + // If the current scale is inside the requested range, advance the + // animation timing to that point. For example if while it is scaling + // up there is a request to scale it down, this keeps the animation + // stepping constant. + + if (nScale_ >= nScaleStart && nScale_ <= nScaleEnd) { + float nPercent = (float)(nScaleEnd - nScale_) / + (float)(nScaleEnd - nScaleStart); + cms *= nPercent; + nScaleStart = nScale_; + } + nScaleStart_ = nScaleStart; + nScaleEnd_ = nScaleEnd; + msAnimateStart_ = base::GetMillisecondCount(); + cmsAnimate_ = cms; + cmsAnimationRate_ = cmsRate; + fAutoDestroy_ = fAutoDestroy; + + thread_.Clear(this, kidmAnimationTimer); + base::Message msg; + msg.handler = this; + msg.id = kidmAnimationTimer; + thread_.PostDelayed(&msg, cmsAnimationRate_ / 10); + + SetScale(nScaleStart_); + psprm_->Update(this); +} + +void IPhoneAnimSprite::OnMessage(base::Message *pmsg) { + if (pmsg->id == kidmAnimationTimer) { + long msCurrent = base::GetMillisecondCount(); + float nPercent = (float)(msCurrent - msAnimateStart_) / + (float)cmsAnimate_; + + bool fFinished = false; + if (nPercent >= 1.0f) { + nPercent = 1.0f; + fFinished = true; + } + SetScale(nScaleStart_ + (nScaleEnd_ - nScaleStart_) * nPercent); + psprm_->Update(this); + + if (fFinished) { + if (fAutoDestroy_) { + // Delete after the next timer, so the current frame shows + + base::Message msg; + msg.handler = this; + msg.id = kidmDestroyAfterAnimation; + thread_.PostDelayed(&msg, cmsAnimationRate_ / 10); + } + return; + } + + // Schedule the next timer + + base::Message msg; + msg.handler = this; + msg.id = kidmAnimationTimer; + thread_.PostDelayed(&msg, cmsAnimationRate_ / 10); + } + + if (pmsg->id == kidmDestroyAfterAnimation) { + // This deletes asynchronously - very convenient + + thread_.Dispose(this); + } +} + +} // namespace wi diff --git a/game/iphone/iphonehttprequest.h b/game/iphone/iphonehttprequest.h new file mode 100644 index 0000000..818e8d3 --- /dev/null +++ b/game/iphone/iphonehttprequest.h @@ -0,0 +1,65 @@ +#ifndef __IPHONEHTTPREQUEST_H__ +#define __IPHONEHTTPREQUEST_H__ + +#import +#import + +#include "game/httpservice.h" +#include "game/httprequest.h" +#include "base/thread.h" +#include "base/bytebuffer.h" + +namespace wi { +class IPhoneHttpRequest; +} + +@interface ConnectionDelegate : NSObject { + NSURLConnection *conn_; + wi::IPhoneHttpRequest *req_; +} +- (void)submit; +- (void)cancel; +- (void)connection:(NSURLConnection *)conn + didFailWithError:(NSError *)error; +- (void)connection:(NSURLConnection *)conn didReceiveData:(NSData *)data; +- (void)connectionDidFinishLoading:(NSURLConnection *)conn; +@end + +namespace wi { + +class IPhoneHttpRequest : public HttpRequest, base::MessageHandler { +public: + IPhoneHttpRequest(HttpResponseHandler *phandler); + ~IPhoneHttpRequest(); + + void Submit(); + void Release(); + NSURLRequest *CreateNSURLRequest(); + void OnReceivedResponse(NSHTTPURLResponse *resp); + void OnReceivedData(NSData *data); + void OnFinishedLoading(); + void OnError(NSError *error); + +private: + virtual void OnMessage(base::Message *pmsg); + + HttpResponseHandler *handler_; + ConnectionDelegate *delegate_; +}; + +struct ReceivedResponseParams : base::MessageData { + int code; + Map headers; +}; + +struct ReceivedDataParams : base::MessageData { + base::ByteBuffer bb; +}; + +struct ErrorParams : base::MessageData { + char szError[80]; +}; + +} // namespace wi + +#endif // __IPHONEHTTPREQUEST_H__ diff --git a/game/iphone/iphonehttprequest.mm b/game/iphone/iphonehttprequest.mm new file mode 100644 index 0000000..a73c86e --- /dev/null +++ b/game/iphone/iphonehttprequest.mm @@ -0,0 +1,216 @@ +#include "game/iphone/iphonehttprequest.h" +#include "game/iphone/input.h" +#include "game/iphone/iphone.h" +#include "base/thread.h" + +// Requests come in from the game thread; NS* api calls occur on main +// thread. + +@implementation ConnectionDelegate + +- (id)initWithRequest:(wi::IPhoneHttpRequest *)req { + self = [super init]; + if (self != nil) { + req_ = req; + conn_ = nil; + } + return self; +} + +- (void)dealloc { + [conn_ release]; + [super dealloc]; +} + +- (void)submit { + NSURLRequest *req = req_->CreateNSURLRequest(); + conn_ = [NSURLConnection + connectionWithRequest:req + delegate:self]; + [conn_ retain]; + [req release]; +} + +- (void)cancel { + [conn_ cancel]; + [conn_ release]; + conn_ = nil; +} + +- (void)connection:(NSURLConnection *)conn + didReceiveResponse:(NSURLResponse *)resp { + req_->OnReceivedResponse((NSHTTPURLResponse *)resp); +} + +- (void)connection:(NSURLConnection *)conn didReceiveData:(NSData *)data { + req_->OnReceivedData(data); +} + +- (void)connectionDidFinishLoading:(NSURLConnection *)conn { + req_->OnFinishedLoading(); +} + +- (void)connection:(NSURLConnection *)conn + didFailWithError:(NSError *)error { + NSLog(@"error: %@", error); + req_->OnError(error); +} +@end + +// C++ implementation of HttpRequest interface for iPhone + +namespace wi { + +IPhoneHttpRequest::IPhoneHttpRequest(HttpResponseHandler *handler) : + handler_(handler), delegate_(nil) { +} + +IPhoneHttpRequest::~IPhoneHttpRequest() { +} + +void IPhoneHttpRequest::Submit() { + delegate_ = [[ConnectionDelegate alloc] initWithRequest:this]; + [delegate_ performSelectorOnMainThread:@selector(submit) + withObject:nil waitUntilDone: NO]; +} + +void IPhoneHttpRequest::Release() { + // This can cause a deadlock when exiting because of how the main thread + // is synchronizing with the game thread to exit before it does + + if (!IPhone::IsExiting()) { + [delegate_ performSelectorOnMainThread:@selector(cancel) + withObject:nil waitUntilDone: YES]; + [delegate_ release]; + } + delegate_ = nil; + thread_.Clear(this); + Dispose(); +} + +NSURLRequest *IPhoneHttpRequest::CreateNSURLRequest() { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + NSMutableURLRequest *req = [[NSMutableURLRequest alloc] init]; + + // Set the url + NSString *s = [NSString stringWithCString:url_.c_str() + encoding:[NSString defaultCStringEncoding]]; + [req setURL:[NSURL URLWithString:s]]; + + // Set the method + s = [NSString stringWithCString:method_.c_str() + encoding:[NSString defaultCStringEncoding]]; + [req setHTTPMethod:s]; + + // Set the body + if (pbb_ != NULL) { + int cb; + void *pv = pbb_->Strip(&cb); + NSData *data = [NSData dataWithBytesNoCopy:(void *)pv length:cb]; + [req setHTTPBody:data]; + } + + // Set timeout + [req setTimeoutInterval:timeout_]; + + // Set cache policy + [req setCachePolicy:NSURLRequestReloadIgnoringCacheData]; + + // Set headers + Enum enm; + char szKey[128]; + while (headers_.EnumKeys(&enm, szKey, sizeof(szKey))) { + char szValue[256]; + if (headers_.GetValue(szKey, szValue, sizeof(szValue))) { + NSString *key = [NSString stringWithCString:szKey + encoding:[NSString defaultCStringEncoding]]; + NSString *value = [NSString stringWithCString:szValue + encoding:[NSString defaultCStringEncoding]]; + [req setValue:value forHTTPHeaderField:key]; + } + } + + // Done + [pool release]; + return req; +} + +void IPhoneHttpRequest::OnMessage(base::Message *pmsg) { + switch (pmsg->id) { + case kidmReceivedResponse: + { + ReceivedResponseParams *pparams = + (ReceivedResponseParams *)pmsg->data; + handler_->OnReceivedResponse(this, pparams->code, + &pparams->headers); + delete pparams; + } + break; + + case kidmReceivedData: + { + ReceivedDataParams *pparams = + (ReceivedDataParams *)pmsg->data; + handler_->OnReceivedData(this, &pparams->bb); + delete pparams; + } + break; + + case kidmFinishedLoading: + handler_->OnFinishedLoading(this); + break; + + case kidmError: + { + ErrorParams *pparams = (ErrorParams *)pmsg->data; + handler_->OnError(this, pparams->szError); + delete pparams; + } + break; + } +} + +void IPhoneHttpRequest::OnReceivedResponse(NSHTTPURLResponse *resp) { + // Called on main thread. Populate ReceivedResponseParams + ReceivedResponseParams *pparams = new ReceivedResponseParams; + NSDictionary *dict = [resp allHeaderFields]; + for (NSString *k in dict) { + NSString *v = [dict objectForKey:k]; + pparams->headers.SetValue( + [k cStringUsingEncoding:[NSString defaultCStringEncoding]], + [v cStringUsingEncoding:[NSString defaultCStringEncoding]]); + } + pparams->code = [resp statusCode]; + + // Post this to the game thread + thread_.Post(kidmReceivedResponse, this, pparams); +} + +void IPhoneHttpRequest::OnReceivedData(NSData *data) { + // Called on main thread. Populate ReceivedDataParams + ReceivedDataParams *pparams = new ReceivedDataParams; + pparams->bb.WriteBytes((const byte *)[data bytes], [data length]); + + // Post this to the game thread + thread_.Post(kidmReceivedData, this, pparams); +} + +void IPhoneHttpRequest::OnFinishedLoading() { + // Called on main thread. Post this to game thread. + thread_.Post(kidmFinishedLoading, this); +} + +void IPhoneHttpRequest::OnError(NSError *error) { + // Called on main thread. Populate ErrorParams. Use + // localizedDescription. Note there is also localizedFailureReason; + // not sure which is better at the moment + const char *psz = [[error localizedDescription] cStringUsingEncoding: + [NSString defaultCStringEncoding]]; + ErrorParams *pparams = new ErrorParams; + strncpyz(pparams->szError, psz, sizeof(pparams->szError)); + + // Called on main thread. Post this to game thread. + thread_.Post(kidmError, this, pparams); +} + +} // namespace wi diff --git a/game/iphone/iphonehttpservice.h b/game/iphone/iphonehttpservice.h new file mode 100644 index 0000000..6e6f735 --- /dev/null +++ b/game/iphone/iphonehttpservice.h @@ -0,0 +1,19 @@ +#ifndef __IPHONEHTTPSERVICE_H__ +#define __IPHONEHTTPSERVICE_H__ + +#include "inc/basictypes.h" +#include "game/httpservice.h" + +namespace wi { + +class IPhoneHttpService : public HttpService { +public: + // HttpService methods + virtual HttpRequest *NewRequest(HttpResponseHandler *phandler); + virtual void SubmitRequest(HttpRequest *preq); + virtual void ReleaseRequest(HttpRequest *preq); +}; + +} // namespace wi + +#endif // __IPHONEHTTPSERVICE_H__ diff --git a/game/iphone/iphonehttpservice.mm b/game/iphone/iphonehttpservice.mm new file mode 100644 index 0000000..ede67d4 --- /dev/null +++ b/game/iphone/iphonehttpservice.mm @@ -0,0 +1,24 @@ +#include "game/iphone/iphonehttpservice.h" +#include "game/iphone/iphonehttprequest.h" +#include "game/iphone/input.h" + +// HttpService calls come in on the game thread. In order to use +// iPhone NS* Http apis, requests execute on the main thread. + +namespace wi { + +HttpRequest *IPhoneHttpService::NewRequest(HttpResponseHandler *phandler) { + return new IPhoneHttpRequest(phandler); +} + +void IPhoneHttpService::SubmitRequest(HttpRequest *preq) { + IPhoneHttpRequest *preqT = (IPhoneHttpRequest *)preq; + preqT->Submit(); +} + +void IPhoneHttpService::ReleaseRequest(HttpRequest *preq) { + IPhoneHttpRequest *preqT = (IPhoneHttpRequest *)preq; + preqT->Release(); +} + +} // namespace wi diff --git a/game/iphone/iphonepackfile.cpp b/game/iphone/iphonepackfile.cpp new file mode 100644 index 0000000..f90f542 --- /dev/null +++ b/game/iphone/iphonepackfile.cpp @@ -0,0 +1,46 @@ +#include "game/iphone/iphonepackfile.h" +#include "game/mempdbreader.h" +#include "inc/rip.h" +#include +#include + +namespace wi { + +PdbReader *IPhonePackFileReader::OpenPdb(const char *pszDir, + const char *pszFn) { + // Load data from this directory + + char szT[PATH_MAX]; + if (pszDir != NULL) { + strcpy(szT, pszDir); + strcat(szT, "/"); + strcat(szT, pszFn); + } else { + strcpy(szT, pszFn); + } + + // PdbReader over memory + MemPdbReader *ppdbReader = new MemPdbReader; + if (ppdbReader == NULL) { + Trace("MemPdbReader null"); + return false; + } + if (!ppdbReader->Open(szT)) { + delete ppdbReader; + Trace("MemPdbReader::Open(%s) failed", pszFn); + return false; + } + + return ppdbReader; +} + +bool IPhonePackFileReader::DeletePdb(const char *pszDir, const char *pszFn) { + char szT[PATH_MAX]; + strcpy(szT, pszDir); + strcat(szT, "/"); + strcat(szT, pszFn); + unlink(szT); + return true; +} + +} // namespace wi diff --git a/game/iphone/iphonepackfile.h b/game/iphone/iphonepackfile.h new file mode 100644 index 0000000..27beb86 --- /dev/null +++ b/game/iphone/iphonepackfile.h @@ -0,0 +1,18 @@ +#ifndef __IPHONEPACKFILE_H__ +#define __IPHONEPACKFILE_H__ + +#include "inc/basictypes.h" +#include "mpshared/packfile.h" + +namespace wi { + +class IPhonePackFileReader : public PackFileReader +{ +private: + virtual PdbReader *OpenPdb(const char *pszDir, const char *pszFn); + virtual bool DeletePdb(const char *pszDir, const char *pszFn); +}; + +} // namespace wi + +#endif // __IPHONEPACKFILE_H__ diff --git a/game/iphone/iphonesounddev.cpp b/game/iphone/iphonesounddev.cpp new file mode 100644 index 0000000..fed4efc --- /dev/null +++ b/game/iphone/iphonesounddev.cpp @@ -0,0 +1,269 @@ +#include "../ht.h" + +#include +#include +#include "../sounddevice.h" +#include "../mixer.h" +#include "criticalsection.h" +#include "iphone.h" + +namespace wi { + +#define kcBuffers 4 +#define kcbBuffer 256 +#define kcChannels 4 + +extern long HostGetTickCount(); +extern void SetSoundServiceDevice(SoundDevice *psndd); +void AudioCallback(void *pvUser, AudioQueueRef haq, AudioQueueBuffer *paqb); + +class IPhoneSoundDevice : public SoundDevice // sndd +{ +public: + IPhoneSoundDevice(); + virtual ~IPhoneSoundDevice(); + + bool Init(); + virtual void Enable(bool fEnable); + virtual bool IsEnabled(); + virtual void PlayAdpcm(int ichnl, byte *pb, word cb); + virtual int GetChannelCount(); + virtual bool IsChannelFree(int ichnl); + virtual void ServiceProc(); + virtual bool IsSilent(); + virtual void SetVolume(int nVolume); + virtual int GetVolume(); + +private: + void InitAudioBuffer(AudioQueueBuffer *paqb); + void InterruptionListener(UInt32 interruptionState); + + bool m_fEnable; + long m_tSilence; + AudioQueueRef m_haq; + AudioQueueBuffer *m_apaqb[kcBuffers]; + Channel m_achnl[kcChannels]; + base::CriticalSection m_crit; + + friend void AudioCallback(void *pvUser, AudioQueueRef haq, + AudioQueueBuffer *paqb); + friend void InterruptionListener(void *data, UInt32 interruptionState); +}; + +SoundDevice *CreateIPhoneSoundDevice() +{ + IPhoneSoundDevice *psndd = new IPhoneSoundDevice; + if (psndd == NULL) + return NULL; + if (!psndd->Init()) { + delete psndd; + return NULL; + } + return (SoundDevice *)psndd; +} + +void InterruptionListener(void *data, UInt32 interruptionState) +{ + IPhoneSoundDevice *psndd = (IPhoneSoundDevice *)data; + psndd->InterruptionListener(interruptionState); +} + +IPhoneSoundDevice::IPhoneSoundDevice() +{ + m_haq = NULL; + for (int i = 0; i < ARRAYSIZE(m_apaqb); i++) { + m_apaqb[i] = NULL; + } + m_fEnable = false; + m_tSilence = 0; +} + +IPhoneSoundDevice::~IPhoneSoundDevice() +{ + if (m_haq != NULL) { + AudioQueueDispose(m_haq, true); + } +} + +bool IPhoneSoundDevice::Init() +{ + // Initialize the default audio session object to tell it + // to allow background music, and to tell us when audio + // gets resumed (like if a phone call comes in, iphone takes + // over audio. If the user then ignores the phone call, the + // audio needs to be turned on again. + + AudioSessionInitialize(NULL, NULL, wi::InterruptionListener, this); + UInt32 category = kAudioSessionCategory_UserInterfaceSoundEffects; + AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, + sizeof(category), &category); + AudioSessionSetActive(true); + + // Set up streaming + + AudioStreamBasicDescription desc; + desc.mSampleRate = 8000; + desc.mFormatID = kAudioFormatLinearPCM; + desc.mFormatFlags = kAudioFormatFlagIsPacked; + desc.mBytesPerPacket = 1; + desc.mFramesPerPacket = 1; + desc.mBytesPerFrame = 1; + desc.mChannelsPerFrame = 1; + desc.mBitsPerChannel = 8; + + OSStatus err = AudioQueueNewOutput(&desc, AudioCallback, this, + NULL, + kCFRunLoopCommonModes, + 0, &m_haq); + if (err != 0) { + return false; + } + + for (int i = 0; i < kcBuffers; i++) { + err = AudioQueueAllocateBuffer(m_haq, kcbBuffer, &m_apaqb[i]); + if (err != 0) { + return false; + } + } + + return true; +} + +void IPhoneSoundDevice::InterruptionListener(UInt32 interruptionState) { + switch (interruptionState) { + case kAudioSessionBeginInterruption: + AudioSessionSetActive(false); + break; + + case kAudioSessionEndInterruption: + AudioSessionSetActive(true); + break; + } +} + +bool IPhoneSoundDevice::IsEnabled() +{ + return m_fEnable; +} + +void IPhoneSoundDevice::Enable(bool fEnable) +{ + base::CritScope cs(&m_crit); + + if (fEnable) { + if (!m_fEnable) { + memset(m_achnl, 0, sizeof(m_achnl)); + m_tSilence = 0; + m_fEnable = true; + for (int i = 0; i < kcBuffers; i++) { + InitAudioBuffer(m_apaqb[i]); + } + AudioQueuePrime(m_haq, 0, NULL); + AudioQueueStart(m_haq, NULL); + SetSoundServiceDevice(this); + } + } else { + if (m_fEnable) { + m_fEnable = false; + memset(m_achnl, 0, sizeof(m_achnl)); + m_tSilence = 0; + AudioQueueStop(m_haq, false); + SetSoundServiceDevice(NULL); + } + } +} + +int IPhoneSoundDevice::GetChannelCount() +{ + return kcChannels; +} + +bool IPhoneSoundDevice::IsChannelFree(int ichnl) +{ + if (ichnl < 0 || ichnl >= kcChannels) + return false; + Channel *pchnl = &m_achnl[ichnl]; + return (pchnl->pb >= pchnl->pbEnd); +} + +bool IPhoneSoundDevice::IsSilent() +{ + if (!m_fEnable) + return true; + if (m_tSilence == 0) + return true; + long tCurrent = HostGetTickCount(); + if (tCurrent < 0 && m_tSilence >= 0) + return true; + return tCurrent >= m_tSilence; +} + +void IPhoneSoundDevice::ServiceProc() +{ +} + +void IPhoneSoundDevice::PlayAdpcm(int ichnl, byte *pb, word cb) +{ + if (ichnl < 0 || ichnl >= kcChannels) + return; + if (!m_fEnable) + return; + + // Start it up + + { + base::CritScope cs(&m_crit); + + Channel *pchnl = &m_achnl[ichnl]; + pchnl->pb = pb; + pchnl->pbEnd = pb + cb; + pchnl->nStepIndex = 0; + pchnl->bSampleLast = 128; + m_tSilence = HostGetTickCount() + + (cb + kcBuffers * kcbBuffer + 39) / 40 + 1; + } +} + +void IPhoneSoundDevice::SetVolume(int nVolume) +{ + if (nVolume < 0) + nVolume = 0; + if (nVolume > 255) + nVolume = 255; + + AudioQueueSetParameter(m_haq, kAudioQueueParam_Volume, + (Float32)nVolume / (Float32)255.0); +} + +int IPhoneSoundDevice::GetVolume() +{ + AudioQueueParameterValue value; + AudioQueueGetParameter(m_haq, kAudioQueueParam_Volume, + &value); + return (int)(value * 255.0); +} + +void AudioCallback(void *pvUser, AudioQueueRef haq, AudioQueueBuffer *paqb) +{ + IPhoneSoundDevice *psndd = (IPhoneSoundDevice *)pvUser; + psndd->InitAudioBuffer(paqb); +} + +void IPhoneSoundDevice::InitAudioBuffer(AudioQueueBuffer *paqb) +{ + base::CritScope cs(&m_crit); + + // Don't send buffers to the device if not enabled + + if (!m_fEnable) + return; + + // Fill the buffer and queue it for playing + + paqb->mAudioDataByteSize = kcbBuffer; + MixChannels(m_achnl, ARRAYSIZE(m_achnl), (byte *)paqb->mAudioData, + kcbBuffer); + AudioQueueEnqueueBuffer(m_haq, paqb, 0, NULL); +} + +} // namespace wi diff --git a/game/iphone/layerdib.cpp b/game/iphone/layerdib.cpp new file mode 100644 index 0000000..f590df7 --- /dev/null +++ b/game/iphone/layerdib.cpp @@ -0,0 +1,101 @@ +#include "game/iphone/layerdib.h" + +namespace wi { + +LayerDib *CreateLayerDib(int cx, int cy) { + LayerDib *pbm = new LayerDib; + if (pbm == NULL) { + return NULL; + } + if (!pbm->Init(cx, cy)) { + delete pbm; + return NULL; + } + return pbm; +} + +LayerDib::LayerDib() { + palette_ = NULL; +} + +LayerDib::~LayerDib() { + if (palette_ != NULL) { + free(palette_); + } +} + +bool LayerDib::Init(int cx, int cy) { + m_pb = NULL; + m_cbRow = 0; + m_siz.cx = cx; + m_siz.cy = cy; + m_wf |= kfDibWantScrolls; + return true; +} + +bool LayerDib::InitLayerMap(int ilmap, const Rect *prc, const Size *psizMap) { + switch (ilmap) { + case 0: + return lmap0_.Init(prc, psizMap); + case 1: + return lmap1_.Init(prc, psizMap); + } + return false; +} + +void LayerDib::MarkUpdate() { + lmap0_.MarkUpdateAll(); + lmap1_.MarkUpdateAll(); +} + +void LayerDib::SetPalette(void *palette, int cb) { + if (palette_ != NULL) + free(palette_); + palette_ = malloc(cb); + memcpy((byte *)palette_, (byte *)palette, cb); +} + +void LayerDib::BltTiles(DibBitmap *pbmSrc, UpdateMap *pupd, int yTopDst) { + if (palette_ == NULL) { + return; + } + +#ifdef UPDATE_ALL + changed_ = true; +#endif + + // Which layer? + if (yTopDst == lmap0_.GetTop()) { + if (lmap0_.UpdateTiles(pbmSrc, pupd, palette_)) { + changed_ = true; + } + return; + } + if (yTopDst == lmap1_.GetTop()) { + if (lmap1_.UpdateTiles(pbmSrc, pupd, palette_)) { + changed_ = true; + } + return; + } +} + +void LayerDib::Scroll(Rect *prcSrc, int xDst, int yDst) { + int dx = xDst - prcSrc->left; + int dy = yDst - prcSrc->top; + if (dx != 0 || dy != 0) { + lmap0_.Scroll(dx, dy); + changed_ = true; + } +} + +void LayerDib::ResetScrollOffset() { + lmap0_.ResetScrollOffset(); + changed_ = true; +} + +void LayerDib::Draw(CGContextRef ctx, const CGRect& rcInvalid) { + lmap0_.Draw(ctx, m_siz.cy, rcInvalid); + lmap1_.Draw(ctx, m_siz.cy, rcInvalid); +} + +} // namespace wi diff --git a/game/iphone/layerdib.h b/game/iphone/layerdib.h new file mode 100644 index 0000000..e785493 --- /dev/null +++ b/game/iphone/layerdib.h @@ -0,0 +1,38 @@ +#ifndef __LAYERDIB_H__ +#define __LAYERDIB_H__ + +#include "game/ht.h" +#include "game/iphone/layermap.h" +#include + +namespace wi { + +class LayerDib : public DibBitmap +{ +public: + LayerDib(); + ~LayerDib(); + + virtual bool Init(int cx, int cy); + virtual void SetPalette(void *palette, int cb); + virtual void BltTiles(DibBitmap *pbmSrc, UpdateMap *pupd, int yTopDst); + virtual void Scroll(Rect *prcSrc, int xDst, int yDst); + + bool InitLayerMap(int ilmap, const Rect *prc, const Size *psizMap); + void MarkUpdate(); + void ResetScrollOffset(); + void Draw(CGContextRef ctx, const CGRect& rcInvalid); + bool HasChanged() { return changed_; } + void ResetChanged() { changed_ = false; } + +protected: + bool changed_; + void *palette_; + LayerMap lmap0_; + LayerMap lmap1_; +}; +LayerDib *CreateLayerDib(int cx, int cy); + +} // namespace wi + +#endif // __LAYERDIB_H__ diff --git a/game/iphone/layermap.h b/game/iphone/layermap.h new file mode 100644 index 0000000..8b06bf0 --- /dev/null +++ b/game/iphone/layermap.h @@ -0,0 +1,39 @@ +#ifndef __LAYERMAP_H__ +#define __LAYERMAP_H__ + +#include "game/ht.h" +#include "game/iphone/layertile.h" +#include + +namespace wi { + +class LayerMap { +public: + LayerMap(); + ~LayerMap(); + + bool Init(const Rect *prc, const Size *psizMap); + bool UpdateTiles(DibBitmap *pbmSrc, UpdateMap *pupd, void *palette); + void Draw(CGContextRef ctx, int cy, const CGRect& rcInvalid); + void Scroll(int dx, int dy); + void ResetScrollOffset(); + int GetTop() { return rc_.top; } + void MarkUpdateAll(); + +private: + void UpdateTile(LayerTile *pltile, DibBitmap *pbmSrc, Rect *prcSrc, + void *palette); + void LRTBExchange(TRect *ptrc, int txDst, int tyDst); + void RLBTExchange(TRect *ptrc, int txDst, int tyDst); + + CGContextRef bmCtx_; + dword *pdwTile_; + LayerTile **apltile_; + int ctx_, cty_; + int xOffset_, yOffset_; + Rect rc_; +}; + +} // namespace wi + +#endif // __LAYERMAP_H__ diff --git a/game/iphone/layermap.mm b/game/iphone/layermap.mm new file mode 100644 index 0000000..697dd94 --- /dev/null +++ b/game/iphone/layermap.mm @@ -0,0 +1,371 @@ +#include "game/iphone/layermap.h" +#include +#include +#include +#include + +namespace wi { + +LayerMap::LayerMap() { + apltile_ = NULL; + ctx_ = 0; + cty_ = 0; + xOffset_ = 0; + yOffset_ = 0; + memset(&rc_, 0, sizeof(rc_)); + pdwTile_ = NULL; + bmCtx_ = NULL; +} + +LayerMap::~LayerMap() { + if (apltile_ != NULL) { + int cltiles = ctx_ * cty_; + for (int i = 0; i < cltiles; i++) { + delete apltile_[i]; + } + delete apltile_; + } + delete pdwTile_; + if (bmCtx_ != NULL) { + CGContextRelease(bmCtx_); + } +} + +bool LayerMap::Init(const Rect *prc, const Size *psizMap) { + rc_ = *prc; + ctx_ = psizMap->cx; + cty_ = psizMap->cy; + + // Alloc the BitmapContext + pdwTile_ = new dword[gcxTile * gcyTile]; + if (pdwTile_ == NULL) { + return false; + } + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + bmCtx_ = CGBitmapContextCreate(pdwTile_, gcxTile, gcyTile, + 8, gcxTile * 4, colorSpace, + kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrderDefault); + CGColorSpaceRelease(colorSpace); + if (bmCtx_ == NULL) { + return false; + } + + // Alloc the layertile map + apltile_ = new LayerTile *[ctx_ * cty_]; + if (apltile_ == NULL) { + return false; + } + memset(apltile_, 0, sizeof(apltile_[0]) * ctx_ * cty_); + + // Allocate layer objects + LayerTile **ppltile = apltile_; + for (int ty = 0; ty < cty_; ty++) { + for (int tx = 0; tx < ctx_; tx++) { + LayerTile *pltile = new LayerTile(bmCtx_); + if (pltile == NULL) { + return false; + } + *ppltile++ = pltile; + } + } + return true; +} + +bool LayerMap::UpdateTiles(DibBitmap *pbmSrc, UpdateMap *pupd, void *palette) { + Size sizMap; + pupd->GetMapSize(&sizMap); + if (sizMap.cx != ctx_ || sizMap.cy != cty_) { + return false; + } + + // Update layer tiles from pbmSrc. + + bool *pfInvalid = pupd->GetInvalidMap(); + LayerTile **ppltile = apltile_; + + bool changed = false; + for (int ty = 0; ty < cty_; ty++) { + for (int tx = 0; tx < ctx_; tx++, pfInvalid++, ppltile++) { + if (!(*pfInvalid)) { + continue; + } + Rect rc; + rc.left = tx * gcxTile - xOffset_; + rc.top = ty * gcyTile - yOffset_; + rc.right = rc.left + gcxTile; + rc.bottom = rc.top + gcyTile; + UpdateTile(*ppltile, pbmSrc, &rc, palette); + changed = true; + } + } + return changed; +} + +void LayerMap::UpdateTile(LayerTile *pltile, DibBitmap *pbmSrc, Rect *prcSrc, + void *palette) { + + // Clip prcSrc to the dib + int xDst = 0; + if (prcSrc->left < 0) { + xDst = -prcSrc->left; + prcSrc->left = 0; + } + int yDst = 0; + if (prcSrc->top < 0) { + yDst = -prcSrc->top; + prcSrc->top = 0; + } + Size sizDib; + pbmSrc->GetSize(&sizDib); + if (prcSrc->right > sizDib.cx) { + prcSrc->right = sizDib.cx; + } + if (prcSrc->bottom > sizDib.cy) { + prcSrc->bottom = sizDib.cy; + } + if (prcSrc->IsEmpty()) { + return; + } + + // This code assumes even alignment + if ((prcSrc->left & 1) || (prcSrc->right & 1)) { + return; + } + + // Grab the tile, and convert to 32bpp + byte *pbSrc = pbmSrc->GetBits() + prcSrc->top * sizDib.cx + prcSrc->left; + int cbSrcReturn = sizDib.cx - prcSrc->Width(); + dword *pdwDst = pdwTile_ + yDst * gcxTile + xDst; + int cdwDstReturn = gcxTile - prcSrc->Width(); + + for (int y = prcSrc->top; y < prcSrc->bottom; y++) { + for (int x = prcSrc->left; x < prcSrc->right; x += 2) { + word w = *((word *)pbSrc); + pbSrc += 2; + *pdwDst++ = ((dword *)palette)[w & 0xff]; + *pdwDst++ = ((dword *)palette)[(w >> 8) & 0xff]; + } + pbSrc += cbSrcReturn; + pdwDst += cdwDstReturn; + } + + // Update the layer. Note could clip the dest as a little speedup. + + CGImageRef image = CGBitmapContextCreateImage(bmCtx_); + pltile->UpdateImage(image); + CGImageRelease(image); +} + +void LayerMap::Draw(CGContextRef ctx, int cy, const CGRect& rcInvalid) { + // Set clipping rect + + CGContextSaveGState(ctx); + CGRect rcClip; + rcClip.origin.y = cy - rc_.top - rc_.Height(); + rcClip.origin.x = rc_.left; + rcClip.size.width = rc_.Width(); + rcClip.size.height = rc_.Height(); + CGContextClipToRect(ctx, rcClip); + + // Draw layers + + LayerTile **apltile = apltile_; + int y = rc_.top - yOffset_; + for (int ty = 0; ty < cty_; ty++, y += gcyTile) { + int x = rc_.left - xOffset_; + for (int tx = 0; tx < ctx_; tx++, x += gcxTile, apltile++) { + LayerTile *pltile = *apltile; + CGRect rc; + rc.origin.x = x; + rc.origin.y = cy - y - gcyTile; + rc.size.width = gcxTile; + rc.size.height = gcyTile; +#if 0 + if (!pltile->IsUpdated()) { + continue; + } +#else + // Unfortunately, this must be done because iPhone extends + // the invalid region by itself, so the game can't track it + // reliably. + if (!CGRectIntersectsRect(rcInvalid, rc)) { + continue; + } +#endif + pltile->Draw(ctx, rc); + } + } + + // Restore clipping rect + CGContextRestoreGState(ctx); +} + +void LayerMap::Scroll(int dx, int dy) { + if (dx == 0 && dy == 0) { + return; + } + + // Calc new sub-tile offsets + + int xOffsetNew, yOffsetNew; + if (dx <= 0) { + xOffsetNew = PcFracFromUpc(xOffset_ - dx); + } else { + xOffsetNew = PcFracFromUpc(gcxTile - + PcFracFromUpc(gcxTile - xOffset_ + dx)); + } + if (dy <= 0) { + yOffsetNew = PcFracFromUpc(yOffset_ - dy); + } else { + yOffsetNew = PcFracFromUpc(gcyTile - + PcFracFromUpc(gcyTile - yOffset_ + dy)); + } + + // Figure out the number of whole tiles to scroll + + int dtx; + if (dx <= 0) { + dtx = -TcFromPc(xOffset_ - dx); + } else { + dtx = TcFromPc((gcxTile - 1) - xOffset_ + dx); + } + + int dty; + if (dy <= 0) { + dty = -TcFromPc(yOffset_ - dy); + } else { + dty = TcFromPc((gcyTile - 1) - yOffset_ + dy); + } + + // Update new offset + + xOffset_ = xOffsetNew; + yOffset_ = yOffsetNew; + if (dtx == 0 && dty == 0) { + MarkUpdateAll(); + return; + } + + // Actually need to scroll the tiles. Figure out the scrolling rect + + TRect trcSrc; + trcSrc.Set(0, 0, ctx_, cty_); + int txDst = dtx; + int tyDst = dty; + + // Clip + + if (trcSrc.left < 0) { + trcSrc.left = 0; + } + if (trcSrc.top < 0) { + trcSrc.top = 0; + } + if (trcSrc.right > ctx_) { + trcSrc.right = ctx_; + } + if (trcSrc.bottom > cty_) { + trcSrc.bottom = cty_; + } + + // Figure out dst + + if (txDst < 0) { + trcSrc.left -= txDst; + txDst = 0; + } + if (tyDst < 0) { + trcSrc.top -= tyDst; + tyDst = 0; + } + + // Clip right and bottom edges + + int txRightDst = txDst + trcSrc.Width(); + if (txRightDst > ctx_) { + trcSrc.right -= txRightDst - ctx_; + } + int tyBottomDst = tyDst + trcSrc.Height(); + if (tyBottomDst > cty_) { + trcSrc.bottom -= tyBottomDst - cty_; + } + + // Anything left to copy? If not, all done + + if (trcSrc.IsEmpty()) { + MarkUpdateAll(); + return; + } + + // Scroll the tiles in a non-destructive direction + + if (trcSrc.top == tyDst) { + if (txDst < trcSrc.left) { + LRTBExchange(&trcSrc, txDst, tyDst); + } else { + RLBTExchange(&trcSrc, txDst, tyDst); + } + } else { + if (tyDst < trcSrc.top) { + LRTBExchange(&trcSrc, txDst, tyDst); + } else { + RLBTExchange(&trcSrc, txDst, tyDst); + } + } +} + +void LayerMap::ResetScrollOffset() { + xOffset_ = 0; + yOffset_ = 0; + MarkUpdateAll(); +} + +void LayerMap::MarkUpdateAll() { + for (int ty = 0; ty < cty_; ty++) { + for (int tx = 0; tx < ctx_; tx++) { + apltile_[ty * ctx_ + tx]->MarkUpdated(); + } + } +} + +void LayerMap::LRTBExchange(TRect *ptrc, int txDst, int tyDst) { + LayerTile **ppltileSrc = &apltile_[ptrc->top * ctx_ + ptrc->left]; + LayerTile **ppltileDst = &apltile_[tyDst * ctx_ + txDst]; + int ctxReturn = ctx_ - ptrc->Width(); + + int ctyT = ptrc->Height(); + while (ctyT-- != 0) { + int ctxT = ptrc->Width(); + while (ctxT-- != 0) { + LayerTile *pltileT = *ppltileDst; + *ppltileDst++ = *ppltileSrc; + (*ppltileSrc)->MarkUpdated(); + *ppltileSrc++ = pltileT; + } + ppltileDst += ctxReturn; + ppltileSrc += ctxReturn; + } +} + +void LayerMap::RLBTExchange(TRect *ptrc, int txDst, int tyDst) { + LayerTile **ppltileSrc = &apltile_[(ptrc->bottom - 1) * ctx_ + + ptrc->right - 1]; + LayerTile **ppltileDst = &apltile_[(tyDst + ptrc->Height() - 1) * ctx_ + + txDst + ptrc->Width() - 1]; + int ctxReturn = ctx_ - ptrc->Width(); + + int ctyT = ptrc->Height(); + while (ctyT-- != 0) { + int ctxT = ptrc->Width(); + while (ctxT-- != 0) { + LayerTile *pltileT = *ppltileDst; + *ppltileDst-- = *ppltileSrc; + (*ppltileSrc)->MarkUpdated(); + *ppltileSrc-- = pltileT; + } + ppltileDst -= ctxReturn; + ppltileSrc -= ctxReturn; + } +} + +} // namespace wi diff --git a/game/iphone/layertile.h b/game/iphone/layertile.h new file mode 100644 index 0000000..0819c3f --- /dev/null +++ b/game/iphone/layertile.h @@ -0,0 +1,102 @@ +#ifndef __LAYERTILE_H__ +#define __LAYERTILE_H__ + +#include + +namespace wi { + +#define USE_IMAGE +//#define USE_LAYER + +class LayerTile { +public: + LayerTile(CGContextRef ctx) { +#ifdef USE_LAYER + CGSize size; + size.width = gcxTile; + size.height = gcyTile; + layer_ = CGLayerCreateWithContext(ctx, size, NULL); +#endif +#ifdef USE_IMAGE + image_ = NULL; +#endif + updated_ = true; + } + ~LayerTile() { +#ifdef USE_LAYER + if (layer_ != NULL) { + CGLayerRelease(layer_); + } +#endif +#ifdef USE_IMAGE + if (image_ != NULL) { + CGImageRelease(image_); + } +#endif + } + + void UpdateImage(CGImageRef image) { +#ifdef USE_LAYER + if (layer_ != NULL) { + CGContextRef ctx = CGLayerGetContext(layer_); + CGRect rc = CGRectMake(0, 0, gcxTile, gcyTile); + CGContextDrawImage(ctx, rc, image); + } +#endif +#ifdef USE_IMAGE + if (image_ != NULL) { + CGImageRelease(image_); + } + image_ = image; + if (image_ != NULL) { + CGImageRetain(image_); + } +#endif + MarkUpdated(); + } + + void Draw(CGContextRef ctx, const CGRect& rc) { +#ifdef USE_LAYER + if (layer_ != NULL) { + CGContextDrawLayerInRect(ctx, rc, layer_); + } +#endif +#ifdef USE_IMAGE + if (image_ != NULL) { + CGContextDrawImage(ctx, rc, image_); + } +#endif + ClearUpdated(); + } + + bool IsUpdated() { +#ifdef UPDATE_ALL + return true; +#else + return updated_; +#endif + } + + void MarkUpdated() { + updated_ = true; + } + + void ClearUpdated() { + updated_ = false; + } + +private: +#ifdef USE_LAYER + CGLayerRef layer_; +#endif +#ifdef USE_IMAGE + CGImageRef image_; +#endif + bool updated_; + + friend class LayerMap; +}; + +} // namespace wi + +#endif // __LAYERTILE_H__ diff --git a/game/iphone/layerview.h b/game/iphone/layerview.h new file mode 100644 index 0000000..c4f6f86 --- /dev/null +++ b/game/iphone/layerview.h @@ -0,0 +1,35 @@ +#ifndef __LAYERVIEW_H__ +#define __LAYERVIEW_H__ + +#import +#import +#import +#import +#import +#import +#include "game/ht.h" +#import "game/iphone/wiview.h" +#include "game/iphone/layerdib.h" +#include + +@interface LayerView : WiView { + CGContextRef bmCtx_; + wi::LayerDib *pbm_; + CGAffineTransform tm_; + bool initialized_; + CGRect *arcInvalid_; + int crcInvalid_; +} +- (id)initWithFrame:(CGRect)rect; +- (void)dealloc; +- (void)frameStart; +- (void)frameComplete:(int)cfrmm maps:(wi::UpdateMap **)apupd + rects:(wi::Rect *)arc scrolled:(bool)fScrolled; +- (wi::DibBitmap *)createFrontDibWithOrientation:(int)nDegreeOrientation + width:(int)cx height:(int)cy; +- (void)setPalette:(wi::Palette *)ppal; +- (void)setFormMgrs:(wi::FormMgr *)pfrmmSimUI input:(wi::FormMgr *)pfrmmInput; +- (void)resetScrollOffset; +@end + +#endif // __LAYERVIEW_H__ diff --git a/game/iphone/layerview.mm b/game/iphone/layerview.mm new file mode 100644 index 0000000..19ae12d --- /dev/null +++ b/game/iphone/layerview.mm @@ -0,0 +1,203 @@ +#import +#import "game/iphone/layerview.h" +#import "game/iphone/iphone.h" +#include "base/log.h" + +@implementation LayerView + +- (id)initWithFrame:(struct CGRect)rect +{ + self = [super initWithFrame: rect]; + if (self == nil) { + return nil; + } + + //self.backgroundColor = [UIColor clearColor]; + [[self layer] setOpaque: YES]; + self.clearsContextBeforeDrawing = NO; + [[self.superview layer] setOpaque: YES]; + self.superview.clearsContextBeforeDrawing = NO; + rect_ = rect; + + // This produces a coordinate system that is like a landscape sector 0NO; + // (origin bottom left). This will make the OS rotate blts for us. + + tm_ = CGAffineTransformMake(1.0f, 0.0f, 0.0f, -1.0f, 0.0f, + rect.size.height); + tm_ = CGAffineTransformRotate(tm_, 4.7123889749); + tm_ = CGAffineTransformTranslate(tm_, -rect_.size.height, 0); + initialized_ = false; + + crcInvalid_ = 0; + arcInvalid_ = NULL; + + return self; +} + +- (void)dealloc +{ + if (bmCtx_ != NULL) { + CGContextRelease(bmCtx_); + } + delete arcInvalid_; + [super dealloc]; +} + +- (void)setFormMgrs:(wi::FormMgr *)pfrmmSimUI input:(wi::FormMgr *)pfrmmInput +{ + wi::Size sizMap0; + pfrmmSimUI->GetUpdateMap()->GetMapSize(&sizMap0); + wi::Size sizDib; + pfrmmSimUI->GetDib()->GetSize(&sizDib); + wi::Rect rc; + rc.Set(0, 0, sizDib.cx, sizDib.cy); + pbm_->InitLayerMap(0, &rc, &sizMap0); + wi::Size sizMap1; + pfrmmInput->GetUpdateMap()->GetMapSize(&sizMap1); + rc.top = sizDib.cy; + pfrmmInput->GetDib()->GetSize(&sizDib); + rc.bottom = rc.top + sizDib.cy; + pbm_->InitLayerMap(1, &rc, &sizMap1); + + // Now allocate the max # of invalid rects that may result + + int crcMax = (sizMap0.cx * sizMap0.cy + sizMap1.cx * sizMap1.cy) / 2; + arcInvalid_ = new CGRect[crcMax]; + initialized_ = true; +} + +- (void)drawRect:(CGRect)frame +{ + if (!initialized_) { + return; + } + CGContextRef ctx = UIGraphicsGetCurrentContext(); + base::CritScope cs(pcrit_); + CGContextSaveGState(ctx); + CGContextConcatCTM(ctx, tm_); + + // iPhone expands invalid areas in some mysterious cases, + // so it is not possible to rely solely on game invalid rects. + // This bounding rect is the best thing iPhone provides - however + // the ctx is clipped to the rects the game provided (plus the + // mysterious extra). + // Also: there appears to be no way to stop iPhone from erasing + // the invalid areas before drawRect gets control. Setting + // the clear property to NO does nothing. + + CGRect rcInvalid= CGRectMake(frame.origin.y, frame.origin.x, + frame.size.height, frame.size.width); + pbm_->Draw(ctx, rcInvalid); +// [self drawSprites: ctx]; + CGContextRestoreGState(ctx); +} + +- (void)frameStart +{ + // Hold the critical section around game drawing. This way, + // the graphics, and the invalidation of the view can be updated + // atomically. + + pcrit_->Enter(); + pbm_->ResetChanged(); +} + +- (void)invalidate +{ + // Runs on main thread + + base::CritScope cs(pcrit_); + for (int i = 0; i < crcInvalid_; i++) { + [self setNeedsDisplayInRect:arcInvalid_[i]]; + } + crcInvalid_ = 0; +} + +- (void)frameComplete:(int)cfrmm maps:(wi::UpdateMap **)apupd + rects:(wi::Rect *)arc scrolled:(bool)fScrolled +{ + + if (!havePalette_ || !pbm_->HasChanged()) { + pcrit_->Leave(); + return; + } + + // Everything needs to redraw if scrolling + + if (fScrolled) { + pbm_->MarkUpdate(); + arcInvalid_[0] = rect_; + crcInvalid_ = 1; + pcrit_->Leave(); + [self performSelectorOnMainThread:@selector(invalidate) + withObject:nil waitUntilDone: NO]; + return; + } + + // Collect the invalid rects, and send them over to the main + // thread + + CGRect *prc = arcInvalid_; + crcInvalid_ = 0; + int cy = (int)rect_.size.width; + + for (int i = 0; i < cfrmm; i++) { + wi::UpdateMap *pupd = apupd[i]; + int yTop = arc[i].top; + + bool fFirst = true; + wi::Rect rc; + while (pupd->EnumUpdateRects(fFirst, NULL, &rc)) { + fFirst = false; + prc->origin.x = cy - (rc.bottom + yTop); + prc->origin.y = rc.left; + prc->size.width = rc.Height(); + prc->size.height = rc.Width(); + prc++; + } + } + crcInvalid_ = prc - arcInvalid_; + pcrit_->Leave(); + + [self performSelectorOnMainThread:@selector(invalidate) + withObject:nil waitUntilDone: NO]; +} + +- (wi::DibBitmap *)createFrontDibWithOrientation:(int)nDegreeOrientation width:(int)cx height:(int)cy +{ + pbm_ = wi::CreateLayerDib(cx, cy); + return pbm_; +} + +- (void)resetScrollOffset +{ + pbm_->ResetScrollOffset(); +} + +- (void)setPalette:(wi::Palette *)ppal +{ + if (pbm_ != NULL) { + // 8->8888 mapping table. 3x faster than 555 format. + dword mp8bpp32bpp[256]; + for (int n = 0; n < BigWord(ppal->cEntries); n++) { + byte *pb = (byte *)&mp8bpp32bpp[n]; + *pb++ = 0; + *pb++ = ppal->argb[n][0]; + *pb++ = ppal->argb[n][1]; + *pb++ = ppal->argb[n][2]; + } + pbm_->SetPalette(mp8bpp32bpp, sizeof(mp8bpp32bpp)); + + havePalette_ = true; + } +} + +- (void)getSurfaceProperties:(wi::SurfaceProperties *)pprops +{ + pprops->cxWidth = (int)rect_.size.width; + pprops->cyHeight = (int)rect_.size.height; + pprops->cbxPitch = 4; + pprops->cbyPitch = (int)rect_.size.width * 4; + pprops->ffFormat = wi::kfDirect888; +} +@end diff --git a/game/iphone/main.cpp b/game/iphone/main.cpp new file mode 100644 index 0000000..1652e2a --- /dev/null +++ b/game/iphone/main.cpp @@ -0,0 +1,40 @@ +#include "game/ht.h" +#include "base/messagequeue.h" + +// C++ support + +#if 0 +void *operator new(size_t cb) +{ + void *pv = malloc(cb); + return pv; +} + +void operator delete(void *pv) +{ + if (pv != 0) + free(pv); +} + +void *operator new[](size_t cb) +{ + void *pv = malloc(cb); + return pv; +} + +void operator delete[](void *pv) +{ + if (pv != 0) + free(pv); +} + +extern "C" void __cxa_pure_virtual(void) +{ + IPhone::Log("pure virtual method called\n"); +} +#endif + +int main(int argc, char **argv) +{ + return wi::IPhone::main(argc, argv); +} diff --git a/game/iphone/ogldib.cpp b/game/iphone/ogldib.cpp new file mode 100644 index 0000000..80fa9d6 --- /dev/null +++ b/game/iphone/ogldib.cpp @@ -0,0 +1,133 @@ +#include "ogldib.h" +#import +#import + + +namespace wi { + +OglDib *CreateOglDib(byte *pb, const SurfaceProperties *pprops, int cx, + int cy, int nDegreeOrientation) +{ + OglDib *pbm = new OglDib; + if (pbm == NULL) + return NULL; + if (!pbm->Init(pb, pprops, cx, cy, nDegreeOrientation)) { + delete pbm; + return NULL; + } + return pbm; +} + +void OglDib::Blt(DibBitmap *pbmSrc, Rect *prcSrc, int xDst, int yDst) +{ + // The overall approach is to blt to a holding buffer, and then upload + // this via glTexSubImage2D to the texture. This api assumes the texture + // it is being sent is sequential. It assumes lines start on a given + // byte boundary (default 4 byte, which is what this code assumes). + // So, 8 bit data -> converted to 16 bit data in holding buffer -> + // uploaded to OpenGL ES. + + // Get src dib dimensions + + Size sizSrc; + pbmSrc->GetSize(&sizSrc); + + // Clip to source rect + // NOTE: Not exactly good form to change the caller's rect. + + if (prcSrc->left < 0) + prcSrc->left = 0; + if (prcSrc->top < 0) + prcSrc->top = 0; + if (prcSrc->right > sizSrc.cx) + prcSrc->right = sizSrc.cx; + if (prcSrc->bottom > sizSrc.cy) + prcSrc->bottom = sizSrc.cy; + + // Clip to dest + + if (xDst < 0) { + prcSrc->left -= xDst; + xDst = 0; + } + if (yDst < 0) { + prcSrc->top -= yDst; + yDst = 0; + } + int xRightDst = xDst + prcSrc->Width(); + if (xRightDst > m_siz.cx) + prcSrc->right -= xRightDst - m_siz.cx; + int yBottomDst = yDst + prcSrc->Height(); + if (yBottomDst > m_siz.cy) + prcSrc->bottom -= yBottomDst - m_siz.cy; + + // Anything to blt? + + if (prcSrc->IsEmpty()) + return; + + if (palette_ == NULL) + return; + + // Remember that bits have changed + + changed_ = true; + + // Simulator does portrait mode for now, due to how the simulator + // handles rotation. +#if defined(SIMULATOR) + // Fill the dest in backwards because gl texture coordinates have 0,0 + // in the lower left. + + word *pwDst = (word *)m_pb; + int cpDstStride = ((prcSrc->Width() + 1) & ~1) - prcSrc->Width(); + + byte *pbSrc = pbmSrc->GetBits() + (prcSrc->bottom - 1) * sizSrc.cx + prcSrc->left; + int cpSrcStride = -prcSrc->Width() - sizSrc.cx; + word *mp8bpp16bpp = (word *)palette_; + + for (int y = prcSrc->top; y < prcSrc->bottom; y++) { + for (int x = prcSrc->left; x < prcSrc->right; x++) { + *pwDst++ = mp8bpp16bpp[*pbSrc++]; + } + pbSrc += cpSrcStride; + pwDst += cpDstStride; + } + + glTexSubImage2D(GL_TEXTURE_2D, 0, xDst, m_siz.cy - yDst - prcSrc->Height(), + prcSrc->Width(), prcSrc->Height(), + GL_RGB, GL_UNSIGNED_SHORT_5_6_5, m_pb); +#endif + + // Real device does landscape mode because it is better, with the + // same orientation the youtube player uses, which is button on the + // right. +#if !defined(SIMULATOR) + // Fill the dest in backwards because gl texture coordinates have 0,0 + // in the lower left. + + word *pwDst = (word *)m_pb; + int cpDstStride = ((prcSrc->Height() + 1) & ~1) - prcSrc->Height(); + + byte *pbSrc = pbmSrc->GetBits() + (prcSrc->bottom - 1) * sizSrc.cx + prcSrc->right - 1; + int cpSrcStride = prcSrc->Height() * sizSrc.cx - 1; + word *palette = (word *)palette_; + + for (int x = prcSrc->left; x < prcSrc->right; x++) { + for (int y = prcSrc->top; y < prcSrc->bottom; y++) { + *pwDst++ = palette[*pbSrc]; + pbSrc -= sizSrc.cx; + } + pbSrc += cpSrcStride; + pwDst += cpDstStride; + } + + glTexSubImage2D(GL_TEXTURE_2D, 0, + m_siz.cy - (yDst + prcSrc->Height()), + m_siz.cx - (xDst + prcSrc->Width()), + prcSrc->Height(), prcSrc->Width(), + GL_RGB, GL_UNSIGNED_SHORT_5_6_5, m_pb); +#endif +} + +} // namespace wi diff --git a/game/iphone/ogldib.h b/game/iphone/ogldib.h new file mode 100644 index 0000000..a240fb1 --- /dev/null +++ b/game/iphone/ogldib.h @@ -0,0 +1,25 @@ +#ifndef __OGLDIB_H__ +#define __OGLDIB_H__ + +#include "../ht.h" +#include "cgdib.h" + +namespace wi { + +class OglDib : public CgDib +{ +public: + virtual void Blt(DibBitmap *pbmSrc, Rect *prcSrc, int xDst, int yDst); + + bool HasChanged() { return changed_; } + void ResetChanged() { changed_ = false; } + +private: + bool changed_; +}; +OglDib *CreateOglDib(byte *pb, const SurfaceProperties *pprops, int cx, + int cy, int nDegreeOrientation); + +} // namespace wi + +#endif // __OGLDIB_H__ diff --git a/game/iphone/oglview.h b/game/iphone/oglview.h new file mode 100644 index 0000000..e9a93d3 --- /dev/null +++ b/game/iphone/oglview.h @@ -0,0 +1,60 @@ +// iphone beta 5 sdk made this implementation obsolete +#if 0 + +// +// oglview.h +// wi +// +// Created by Scott Ludwig on 4/19/08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#ifndef __OGLVIEW_H__ +#define __OGLVIEW_H__ + +#include "../ht.h" + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import "wiview.h" + +namespace wi { +class OglDib; +} + +@interface OglView : WiView { + EAGLContext context_; + EAGLSurface surface_; + EAGLSurface surfacePBuffer_; + GLuint texName_; + wi::OglDib *pbm_; +} +- (id)initWithFrame:(struct CGRect)rect; +- (void)initSurfaceAndContext; +- (void)initGraphics; +- (void)dealloc; +- (void)frameStart; +- (void)frameComplete; +- (wi::DibBitmap *)createFrontDibWithOrientation:(int)nDegreeOrientation width:(int)cx height:(int)cy; +- (void)setPalette:(wi::Palette *)ppal; +@end + +/* EAGL and GL functions calling wrappers that log on error */ +#ifdef DEBUG +#define CALL_EAGL_FUNCTION(__FUNC__, ...) ({ EAGLError __error = __FUNC__( __VA_ARGS__ ); if(__error != kEAGLErrorSuccess) NSLog(@"%s() called from %s returned error %i", #__FUNC__, __FUNCTION__, __error); (__error ? NO : YES); }) +#define CHECK_GL_ERROR() ({ GLenum __error = glGetError(); if(__error) NSLog(@"OpenGL error 0x%04X in %s", __error, __FUNCTION__); (__error ? NO : YES); }) +#else +#define CALL_EAGL_FUNCTION(__FUNC__, ...) ({ __FUNC__( __VA_ARGS__ ); }) +#define CHECK_GL_ERROR() +#endif + +#endif // __OGLVIEW_H__ + +#endif // 0 \ No newline at end of file diff --git a/game/iphone/oglview.mm b/game/iphone/oglview.mm new file mode 100644 index 0000000..8932390 --- /dev/null +++ b/game/iphone/oglview.mm @@ -0,0 +1,208 @@ +// iphone beta 5 sdk made this implementation obsolete +#if 0 + +// +// oglview.m +// wi +// +// Created by Scott Ludwig on 4/19/08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import +#import "oglview.h" + +@implementation OglView + ++ (Class) layerClass +{ + return [CAEAGLLayer class]; +} + +- (id)initWithFrame:(struct CGRect)rect +{ + self = [super initWithFrame: rect]; + if (self != nil) { + [self initSurfaceAndContext]; + } + return self; +} + +- (void)dealloc +{ + [super dealloc]; +} + +- (void)initSurfaceAndContext { + // These properties make things faster + + self.opaque = TRUE; + self.clearsContextBeforeDrawing = NO; + + // Create the window surface, the context, make current. + // For an unknown reason, these need to be called on the main thread. + + CAEAGLLayer *layer = (CAEAGLLayer *)[self layer]; + CALL_EAGL_FUNCTION(eaglCreateWindowSurface, kEAGLPixelFormat_RGB565_D0, [layer nativeWindow], &surface_); + CALL_EAGL_FUNCTION(eaglCreateContext, NULL, &context_); +} + +- (void)initGraphics { + CALL_EAGL_FUNCTION(eaglMakeCurrent, surface_, context_); + + // Initialize the viewport and projection, modelview matrices + + glDisable(GL_DEPTH_TEST); + CHECK_GL_ERROR(); + glViewport(0, 0, rect_.size.width, rect_.size.height); + CHECK_GL_ERROR(); + glMatrixMode(GL_PROJECTION); + CHECK_GL_ERROR(); + glLoadIdentity(); + CHECK_GL_ERROR(); + glOrthof(0.0f, rect_.size.width, rect_.size.height, 0.0f, -1.0, 1.0); + CHECK_GL_ERROR(); + glMatrixMode(GL_MODELVIEW); + CHECK_GL_ERROR(); + glLoadIdentity(); + CHECK_GL_ERROR(); + + // Create a texture bound to a pbuffer. This is done because glTexSubImage2D + // is much faster changing this kind of texture than a regular texture. + // OpenGL ES reformats regular textures for optimal texture mapping speed, + // assuming they don't change much. OpenGL ES does not reformat pbuffer + // textures, since it assumes pbuffers change frequently. + + glEnable(GL_TEXTURE_2D); + CHECK_GL_ERROR(); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + CHECK_GL_ERROR(); + glGenTextures(1, &texName_); + CHECK_GL_ERROR(); + glBindTexture(GL_TEXTURE_2D, texName_); + CHECK_GL_ERROR(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + CHECK_GL_ERROR(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + CHECK_GL_ERROR(); + + // Need texture to be power of 2 dimensions + + int cx = (int)rect_.size.width; + if ((cx & (cx - 1)) != 0) { + cx <<= 1; + while ((cx & (cx - 1)) != 0) + cx &= cx - 1; + } + int cy = (int)rect_.size.height; + if ((cy & (cy - 1)) != 0) { + cy <<= 1; + while ((cy & (cy - 1)) != 0) + cy &= cy - 1; + } + + // The simulator doesn't support PBuffers (yet). Use a regular texture since it's fast + // enough on the simulator. +#if !defined(SIMULATOR) + CALL_EAGL_FUNCTION(eaglCreatePBufferSurface, kEAGLPixelFormat_RGB565_D0, cx, cy, &surfacePBuffer_); + CALL_EAGL_FUNCTION(eaglTexImagePBuffer, surfacePBuffer_); +#else + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, cx, cy, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, NULL); + CHECK_GL_ERROR(); +#endif + + // The simulator doesn't support GL_TEXTURE_CROP_RECT_OES. + // Use regular texture mapping instead. +#if !defined(SIMULATOR) + // Set crop rectangle in texels (l, b, w, h) + int rcCrop[4] = { 0, 0, (int)rect_.size.width, (int)rect_.size.height }; + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, rcCrop); + CHECK_GL_ERROR(); +#else + // On the simulator, regular texture mapping is required + glEnableClientState(GL_VERTEX_ARRAY); + CHECK_GL_ERROR(); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + CHECK_GL_ERROR(); +#endif + + // Clear the color buffer + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + CHECK_GL_ERROR(); + + // Allocate the memory OglDib uses + int w = rect_.size.width; + int h = rect_.size.height; + int cb = 2 * w * h; + pb_ = (unsigned char *)malloc(cb); + memset(pb_, 0, cb); + + // Don't have palette yet + havePalette_ = false; +} + +- (void)frameStart { + pbm_->ResetChanged(); +} + +- (void)frameComplete { + if (!havePalette_) + return; + if (!pbm_->HasChanged()) + return; + +#if !defined(SIMULATOR) + // Draw texture and show on screen + glDrawTexiOES(0, 0, 1, (int)rect_.size.width, (int)rect_.size.height); + CHECK_GL_ERROR(); + CALL_EAGL_FUNCTION(eaglSwapBuffers, surface_); +#else + // Draw texture. + GLfloat coords[] = { + 0.0f, 0.0f, + 320.0f / 512.0f, 0.0f, + 0.0f, 480.0f / 512.0f, + 320.0f / 512.0f, 480.0f / 512.0f + }; + + GLfloat verts[] = { + 0.0f, 480.0f, 1.0f, + 320.0f, 480.0f, 1.0f, + 0.0f, 0.0f, 1.0f, + 320.0f, 0.0f, 1.0f + }; + + glVertexPointer(3, GL_FLOAT, 0, verts); + CHECK_GL_ERROR(); + glTexCoordPointer(2, GL_FLOAT, 0, coords); + CHECK_GL_ERROR(); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + CHECK_GL_ERROR(); + CALL_EAGL_FUNCTION(eaglSwapBuffers, surface_); +#endif +} + +- (wi::DibBitmap *)createFrontDibWithOrientation:(int)nDegreeOrientation width:(int)cx height:(int)cy { + // This is called from the game thread. initGraphics here because the + // game thread will be doing the rendering. + + [self initGraphics]; + + pbm_ = wi::CreateOglDib(pb_, cx, cy, nDegreeOrientation); + return pbm_; +} + +- (void)setPalette:(wi::Palette *)ppal { + if (pbm_ != NULL) { + // 8->565 mapping table. OpenGL ES understands 565 format. + word mp8bpp16bpp[256]; + for (int n = 0; n < BigWord(ppal->cEntries); n++) { + mp8bpp16bpp[n] = ((ppal->argb[n][0] << 8) & 0xf800) | ((ppal->argb[n][1] << 3) & 0x07e0) | ((ppal->argb[n][2] >> 3) & 0x001f); + } + pbm_->SetPalette(mp8bpp16bpp); + havePalette_ = true; + } +} +@end + +#endif // 0 \ No newline at end of file diff --git a/game/iphone/oglview2.h b/game/iphone/oglview2.h new file mode 100644 index 0000000..893e0e9 --- /dev/null +++ b/game/iphone/oglview2.h @@ -0,0 +1,54 @@ +// +// oglview2.h +// wi +// +// Created by Scott Ludwig on 5/22/08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#ifndef __OGLVIEW2_H__ +#define __OGLVIEW2_H__ + +#include "../ht.h" + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#include "ogldib.h" +#import "wiview.h" + +@interface OglView2 : WiView { + EAGLContext *context_; + GLuint tex_; + GLuint framebufferTex_; + GLuint renderbuffer_; + GLuint framebufferRender_; + GLfloat coords_[8]; + GLfloat verts_[12]; + wi::OglDib *pbm_; +} +- (id)initWithFrame:(struct CGRect)rect; +- (void)initLayer; +- (void)initGraphics; +- (void)cleanupGraphics; +- (void)dealloc; +- (void)frameStart; +- (void)frameComplete; +- (wi::DibBitmap *)createFrontDibWithOrientation:(int)nDegreeOrientation width:(int)cx height:(int)cy; +- (void)setPalette:(wi::Palette *)ppal; +@end + +/* EAGL and GL functions calling wrappers that log on error */ +#ifdef DEBUG +#define CHECK_GL_ERROR() ({ GLenum __error = glGetError(); if(__error) NSLog(@"OpenGL error 0x%04X in %s", __error, __FUNCTION__); (__error ? NO : YES); }) +#else +#define CHECK_GL_ERROR() +#endif + +#endif // __OGLVIEW2_H__ \ No newline at end of file diff --git a/game/iphone/oglview2.mm b/game/iphone/oglview2.mm new file mode 100644 index 0000000..6ca8992 --- /dev/null +++ b/game/iphone/oglview2.mm @@ -0,0 +1,262 @@ +// +// oglview2.m +// wi +// +// Created by Scott Ludwig on 5/22/08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import +#import "oglview2.h" + +@implementation OglView2 + ++ (Class) layerClass +{ + return [CAEAGLLayer class]; +} + +- (id)initWithFrame:(struct CGRect)rect +{ + self = [super initWithFrame: rect]; + if (self == nil) + return nil; + + rect_ = rect; + pb_ = NULL; + tex_ = 0; + framebufferTex_ = 0; + renderbuffer_ = 0; + framebufferRender_ = 0; + context_ = NULL; + + [self initLayer]; + return self; +} + +- (void)dealloc { + [super dealloc]; + [self cleanupGraphics]; +} + + +- (void)initLayer +{ + // The basic approach is to update an ogl texture from back buffer + + // update map, then texture map that into a render buffer, then show + // that in this view. + // + // The contentious part is updating the ogl texture. It is regularly a + // very slow process, however when a texture is set up to be a rendering + // destination, updating the texture with glTexSubImage2D is much faster, + // because the texture bits are stored more efficiently for updating this + // way. + // + // Two framebuffer objects are created. One with the texture in the color + // buffer attach point, one with the destination renderbuffer in the color + // buffer attach point. For each frame, the texture is updated, then + // texture mapped into the render buffer, then the render buffer is shown + // on screen. + + // Clear before drawing set to NO is a little faster. The game doesn't + // use a drawing context (not sure if this matters for CAEAGLLayer). + self.clearsContextBeforeDrawing = NO; + + // Setting the view as opaque means the OS doesn't need to working + // about compositing layers underneath. + + CAEAGLLayer *layer = (CAEAGLLayer *)self.layer; + layer.opaque = YES; + + // No retained backing, and 565 format + layer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithBool:FALSE], + kEAGLDrawablePropertyRetainedBacking, + kEAGLColorFormatRGB565, kEAGLDrawablePropertyColorFormat, + (char *)NULL]; + + // Create and initialize an EAGL Context + context_ = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1]; + [EAGLContext setCurrentContext:context_]; + + // Now create the render buffer, use the CAEAGLLayer for storage, and + // attach it to another framebuffer. + glGenRenderbuffersOES(1, &renderbuffer_); + glBindRenderbufferOES(GL_RENDERBUFFER_OES, renderbuffer_); + [context_ renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(CAEAGLLayer*)self.layer]; + + // Bind the renderbuffer to a framebuffer object's color + // buffer attach point. This will be the rendering destination. + glGenFramebuffersOES(1, &framebufferRender_); + glBindFramebufferOES(GL_FRAMEBUFFER_OES, framebufferRender_); + glBindRenderbufferOES(GL_RENDERBUFFER_OES, renderbuffer_); + glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, renderbuffer_); + + // Create a texture and framebuffer. Attach texture to framebuffer. + // This is done so that the texture is internally stored in a format + // that makes glTexSubImage2D() fast (otherwise it is really slow). + + // Texture must be power of 2 dimensions + int cxTex = (int)rect_.size.width; + if ((cxTex & (cxTex - 1)) != 0) { + cxTex <<= 1; + while ((cxTex & (cxTex - 1)) != 0) + cxTex &= cxTex - 1; + } + int cyTex = (int)rect_.size.height; + if ((cyTex & (cyTex - 1)) != 0) { + cyTex <<= 1; + while ((cyTex & (cyTex - 1)) != 0) + cyTex &= cyTex - 1; + } + glEnable(GL_TEXTURE_2D); + glGenTextures(1, &tex_); + glBindTexture(GL_TEXTURE_2D, tex_); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, cxTex, cyTex, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, NULL); + glGenFramebuffersOES(1, &framebufferTex_); + glBindFramebufferOES(GL_FRAMEBUFFER_OES, framebufferTex_); + glFramebufferTexture2DOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_TEXTURE_2D, tex_, 0); + + // Bind framebufferRender_ and tex_ here, since this is what + // draw code needs. + glBindTexture(GL_TEXTURE_2D, tex_); + glBindFramebufferOES(GL_FRAMEBUFFER_OES, framebufferRender_); + + // Perform the rest of misc gl initialization. + glDisable(GL_DEPTH_TEST); + glViewport(0, 0, rect_.size.width, rect_.size.height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrthof(0.0f, rect_.size.width, rect_.size.height, 0.0f, -1.0, 1.0); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + // Texture map parameters + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + // Pre-initialize so this isn't done during drawing + coords_[0] = 0.0f; + coords_[1] = 0.0f; + + coords_[2] = rect_.size.width / cxTex; + coords_[3] = 0.0f; + + coords_[4] = 0.0f; + coords_[5] = rect_.size.height / cyTex; + + coords_[6] = rect_.size.width / cxTex; + coords_[7] = rect_.size.height / cyTex; + + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(2, GL_FLOAT, 0, coords_); + + verts_[0] = 0.0f; + verts_[1] = rect_.size.height; + verts_[2] = 1.0f; + + verts_[3] = rect_.size.width; + verts_[4] = rect_.size.height; + verts_[5] = 1.0f; + + verts_[6] = 0.0f; + verts_[7] = 0.0f; + verts_[8] = 1.0f; + + verts_[9] = rect_.size.width; + verts_[10] = 0.0f; + verts_[11] = 1.0f; + + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(3, GL_FLOAT, 0, verts_); + + // Clear the color buffer + glClear(GL_COLOR_BUFFER_BIT); + + // Don't have palette yet + havePalette_ = false; +} + +- (void)initGraphics { + // NOTE: initGraphics is called from the game thread + [EAGLContext setCurrentContext:context_]; +} + +- (void)cleanupGraphics { + // NOTE: called on game thread + + // Detach texture from framebufferTex_, unbind and delete both + glBindFramebufferOES(GL_FRAMEBUFFER_OES, framebufferTex_); + glFramebufferTexture2DOES(GL_FRAMEBUFFER_OES, 0, GL_TEXTURE_2D, 0, 0); + glBindTexture(GL_TEXTURE_2D, 0); + glDeleteTextures(1, &tex_); + glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0); + glDeleteFramebuffersOES(1, &framebufferTex_); + + // Detach renderbuffer from framebufferRender_, unbind and delete both + glBindFramebufferOES(GL_FRAMEBUFFER_OES, framebufferRender_); + glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, 0); + glBindRenderbufferOES(GL_RENDERBUFFER_OES, 0); + glDeleteRenderbuffersOES(1, &framebufferRender_); + glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0); + glDeleteFramebuffersOES(1, &framebufferRender_); + + [EAGLContext setCurrentContext:NULL]; + [context_ release]; + + if (pb_ != NULL) { + free(pb_); + pb_ = NULL; + } + + tex_ = 0; + framebufferTex_ = 0; + renderbuffer_ = 0; + framebufferRender_ = 0; + context_ = NULL; +} + +- (void)frameStart { + pbm_->ResetChanged(); +} + +- (void)frameComplete { + if (!havePalette_) + return; + if (!pbm_->HasChanged()) + return; + + // Draw texture. + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + [context_ presentRenderbuffer:renderbuffer_]; +} + +- (wi::DibBitmap *)createFrontDibWithOrientation:(int)nDegreeOrientation width:(int)cx height:(int)cy { + // This is called from the game thread. initGraphics here because the + // game thread will be doing the rendering. + + [self initGraphics]; + + // Allocate the memory OglDib uses + int cb = 2 * cx * cy; + pb_ = (unsigned char *)malloc(cb); + memset(pb_, 0, cb); + wi::SurfaceProperties props; + [self getSurfaceProperties:&props]; + pbm_ = wi::CreateOglDib(pb_, &props, cx, cy, nDegreeOrientation); + return pbm_; +} + +- (void)setPalette:(wi::Palette *)ppal { + if (pbm_ != NULL) { + // 8->565 mapping table. OpenGL ES understands 565 format. + word mp8bpp16bpp[256]; + for (int n = 0; n < BigWord(ppal->cEntries); n++) { + mp8bpp16bpp[n] = ((ppal->argb[n][0] << 8) & 0xf800) | ((ppal->argb[n][1] << 3) & 0x07e0) | ((ppal->argb[n][2] >> 3) & 0x001f); + } + pbm_->SetPalette(mp8bpp16bpp, sizeof(mp8bpp16bpp)); + havePalette_ = true; + } +} +@end diff --git a/game/iphone/savegame.cpp b/game/iphone/savegame.cpp new file mode 100644 index 0000000..bb4e847 --- /dev/null +++ b/game/iphone/savegame.cpp @@ -0,0 +1,329 @@ +#include "../ht.h" +#include +#include +#include +//#include +#include +#include +#include +#include +#include + +namespace wi { + +class FileStream : public Stream +{ +public: + FileStream(); + ~FileStream(); + bool Init(char *pszMode, char *pszFile, char *pszDelete, char *pszFinal); + + virtual void Close(); + virtual dword Read(void *pv, dword cb); + virtual dword Write(void *pv, dword cb); + virtual bool IsSuccess(); + +private: + bool m_fSuccess; + FILE *m_pf; + char m_szFile[PATH_MAX]; + char m_szDelete[PATH_MAX]; + char m_szFinal[PATH_MAX]; +}; + +FileStream::FileStream() +{ + m_fSuccess = true; + m_pf = NULL; + m_szFile[0] = 0; + m_szDelete[0] = 0; + m_szFinal[0] = 0; +} + +FileStream::~FileStream() +{ + Assert(m_pf == NULL); +} + +bool FileStream::Init(char *pszMode, char *pszFile, char *pszDelete, char *pszFinal) +{ + m_pf = fopen(pszFile, pszMode); + if (m_pf == NULL) + return false; + strcpy(m_szFile, pszFile); + if (pszDelete != NULL) + strcpy(m_szDelete, pszDelete); + if (pszFinal != NULL) + strcpy(m_szFinal, pszFinal); + return true; +} + +void FileStream::Close() +{ + fclose(m_pf); + m_pf = NULL; + + // Delete and rename file if no error + + if (m_fSuccess) { + if (m_szDelete[0] != 0) { + unlink(m_szDelete); + } + if (m_szFinal[0] != 0) { + rename(m_szFile, m_szFinal); + } + } +} + +dword FileStream::Read(void *pv, dword cb) +{ + size_t cbT = fread(pv, 1, cb, m_pf); + if (cb != cbT) + m_fSuccess = false; + return cbT; +} + +dword FileStream::Write(void *pv, dword cb) +{ + size_t cbT = fwrite(pv, 1, cb, m_pf); + if (cb != cbT) + m_fSuccess = false; + return cbT; +} + +bool FileStream::IsSuccess() +{ + return m_fSuccess; +} + +void PrependSavesDirectory(char *pszIn, char *pszOut) +{ + strcpy(pszOut, IPhone::GetSaveGamesDir()); + strcat(pszOut, "/"); + strcat(pszOut, pszIn); +} + +bool FindSaveGame(int nGame, char *psz, int cb, int *pc = NULL) +{ + if (pc != NULL) + *pc = 0; + + // This is the prefix of the file being looked for + + char szCompare[PATH_MAX]; + sprintf(szCompare, "htsave%d_", nGame); + int cchCompare = strlen(szCompare); + + // This is the special save game that is only used + // when the game exits and reloads right away + + char szReinitializeSave[20]; + sprintf(szReinitializeSave, "htsave%d_", knGameReinitializeSave); + int cchReinitializeSave = strlen(szReinitializeSave); + + // Enum files in this directory + + char szFileSpec[PATH_MAX]; + PrependSavesDirectory("", szFileSpec); + DIR *pdir = opendir(szFileSpec); + if (pdir == NULL) { + return false; + } + + int c = 0; + dirent *pdent; + while ((pdent = readdir(pdir)) != NULL) { + // Only consider save games, because if the desired save game is + // not found, we need to count "slots". + + if (strncmp("htsave", pdent->d_name, 6) != 0) { + continue; + } + + // Save game found? + + if (strncmp(szCompare, pdent->d_name, cchCompare) == 0) { + if (psz != NULL) + strncpyz(psz, pdent->d_name, cb); + closedir(pdir); + return true; + } + + // Count save games but don't count the temporary "Reinitialize" + // saved game + + if (strncmp(szReinitializeSave, pdent->d_name, + cchReinitializeSave) == 0) { + continue; + } + + // Count this save game as a slot + + c++; + } + closedir(pdir); + + // Didn't find the saved game, but did count the number of occupied slots + + if (pc != NULL) + *pc = c; + return false; +} + +int HostGetSaveGameCount() +{ + int c; + FindSaveGame(-1, NULL, 0, &c); + return c; +} + +bool HostGetSaveGameName(int nGame, char *psz, int cb, Date *pdate, int *pnHours24, int *pnMinutes, int *pnSeconds) +{ + // Find the game + + char szT[PATH_MAX]; + if (!FindSaveGame(nGame, szT, sizeof(szT))) { + strncpyz(psz, "-- Empty --", cb); + return false; + } + + char szPath[PATH_MAX]; + PrependSavesDirectory(szT, szPath); + + // Get the creation time in hours24, minutes + + struct stat st; + if (stat(szPath, &st) > 0) { + return false; + } + struct tm *ptm = localtime(&st.st_mtime); + *pnHours24 = ptm->tm_hour; + *pnMinutes = ptm->tm_min; + *pnSeconds = ptm->tm_sec; + pdate->nDay = ptm->tm_mday; + pdate->nMonth = ptm->tm_mon; + pdate->nYear = ptm->tm_year + 1900; + + // Copy over filename, lose prefix + + char *pszName = strchr(szT, '_') + 1; + int cbName = strlen(pszName) - 4 + 1; + strncpyz(psz, pszName, _min(cb, cbName)); + + // restore '#' to ':' + + char *pchInvalid = psz; + do { + pchInvalid = strchr(pchInvalid, '#'); + if (pchInvalid != 0) + *pchInvalid = ':'; + } while (pchInvalid != 0); + + return true; +} + +Stream *HostNewSaveGameStream(int nGame, char *pszName) +{ + // Get the old file name - we'll delete this if successful + + char szOld[PATH_MAX]; + char szOldFull[PATH_MAX]; + if (!FindSaveGame(nGame, szOld, sizeof(szOld))) { + szOldFull[0] = 0; + } else { + PrependSavesDirectory(szOld, szOldFull); + } + + // New file name + + char szNew[PATH_MAX]; + sprintf(szNew, "htsave%d_%s.bin", nGame, pszName); + + // windows disallows ':' in a filename, so sub those out + + char *pchInvalid = szNew; + do { + pchInvalid = strchr(pchInvalid, ':'); + if (pchInvalid != 0) + *pchInvalid = '#'; + } while (pchInvalid != 0); + + char szNewFull[PATH_MAX]; + PrependSavesDirectory(szNew, szNewFull); + + // Get stream over temp file + + FileStream *pstm = new FileStream(); + if (pstm == NULL) + return NULL; + + // If save is successful, szOld will be deleted and httempsave.bin + // will be renamed to szNew + + char szTempSaveFull[PATH_MAX]; + PrependSavesDirectory("httempsave.bin", szTempSaveFull); + + if (!pstm->Init("wb", szTempSaveFull, szOldFull, szNewFull)) { + delete pstm; + return NULL; + } + + return (Stream *)pstm; +} + +Stream *HostOpenSaveGameStream(int nGame, bool fDelete) +{ + char szT[PATH_MAX]; + if (!FindSaveGame(nGame, szT, sizeof(szT))) + return NULL; + + // rename to a temporary file before opening + + char szTFull[PATH_MAX]; + PrependSavesDirectory(szT, szTFull); + char szTempNameFull[PATH_MAX]; + PrependSavesDirectory(kszTempName, szTempNameFull); + rename(szTFull, szTempNameFull); + + // Get stream over temp file + + FileStream *pstm = new FileStream(); + if (pstm == NULL) + return NULL; + + // If load is successful, and fDelete is True szTempName will be deleted + // if fDelete is false szTempName will be renamed to szSaveGame + + if (!pstm->Init("rb", szTempNameFull, fDelete ? szTempNameFull : NULL, fDelete ? NULL : szTFull)) { + delete pstm; + return NULL; + } + + return (Stream *)pstm; +} + +bool HostDeleteSaveGame(char *psz, int nGame) +{ + // if nGame is > 0, delete that, otherwise if psz is non-null delete that + // return true if we deleted something + + char szSaveGame[PATH_MAX]; + + if (psz == NULL) { + if (!FindSaveGame(nGame, szSaveGame, sizeof(szSaveGame))) + return false; + } else { + strncpyz(szSaveGame, psz, sizeof(szSaveGame)); + } + + char szSaveGameFull[PATH_MAX]; + PrependSavesDirectory(szSaveGame, szSaveGameFull); + + if (psz != NULL) { + unlink(szSaveGameFull); + return true; + } + return false; +} + +} // namespace wi diff --git a/game/iphone/selectionsprite.h b/game/iphone/selectionsprite.h new file mode 100644 index 0000000..24f979b --- /dev/null +++ b/game/iphone/selectionsprite.h @@ -0,0 +1,43 @@ +#ifndef __IPHONESELECTIONSPRITE_H__ +#define __IPHONESELECTIONSPRITE_H__ + +#include "base/criticalsection.h" +#include "inc/basictypes.h" +#include "game/ht.h" +#include "game/dragrect.h" +#include "game/sprite.h" +#import +#import + +namespace wi { + +class IPhoneSelectionSprite : public SelectionSprite { +public: + IPhoneSelectionSprite(SpriteManager *psprm); + ~IPhoneSelectionSprite(); + + // SelectionSprite + virtual void SetDragRect(const DragRect& drc); + virtual const DragRect& GetDragRect(); + + // Sprite + virtual SpriteManager *GetManager() { return psprm_; } + virtual void SetScale(float scale) {} + virtual void SetPosition(int x, int y) {} + virtual void Show(bool fShow); + virtual bool IsVisible() { return fVisible_; } + virtual void GetBounds(Rect *prc) {} + + // PlatformSprite + virtual void Draw(void *pv, Size *psiz); + +private: + base::CriticalSection crit_; + SpriteManager *psprm_; + DragRect drc_; + bool fVisible_; +}; + +} // namespace wi + +#endif // __IPHONESELECTIONSPRITE_H__ diff --git a/game/iphone/selectionsprite.mm b/game/iphone/selectionsprite.mm new file mode 100644 index 0000000..13d1a1d --- /dev/null +++ b/game/iphone/selectionsprite.mm @@ -0,0 +1,74 @@ +#include "game/iphone/selectionsprite.h" +#import + +namespace wi { + +IPhoneSelectionSprite::IPhoneSelectionSprite(SpriteManager *psprm) { + fVisible_ = false; + psprm_ = psprm; + psprm_->Add(this); +} + +IPhoneSelectionSprite::~IPhoneSelectionSprite() { + psprm_->Remove(this); +} + +void IPhoneSelectionSprite::SetDragRect(const DragRect& drc) { + crit_.Enter(); + drc_ = drc; + crit_.Leave(); + psprm_->Update(this); +} + +const DragRect& IPhoneSelectionSprite::GetDragRect() { + return drc_; +} + +void IPhoneSelectionSprite::Show(bool fShow) { + if (fVisible_ == fShow) { + return; + } + fVisible_ = fShow; + psprm_->Update(this); +} + +void IPhoneSelectionSprite::Draw(void *pv, Size *psiz) { + if (!fVisible_) { + return; + } + + crit_.Enter(); + DPoint apt[4]; + drc_.GetPoints(apt); + + // x/y are swapped, and y needs to be origin adjusted + + CGContextRef ctx = (CGContextRef)pv; + CGContextSetRGBStrokeColor(ctx, 1.0, 1.0, 1.0, 1.0); + CGContextSetLineWidth(ctx, 2.0); + CGContextBeginPath(ctx); + CGContextMoveToPoint(ctx, apt[0].y, psiz->cy - apt[0].x); + for (int i = 1; i < ARRAYSIZE(apt); i++) { + CGContextAddLineToPoint(ctx, apt[i].y, psiz->cy - apt[i].x); + } + CGContextAddLineToPoint(ctx, apt[0].y, psiz->cy - apt[0].x); + CGContextStrokePath(ctx); + + // Draw circles at rect corners for "grabbies". White stroked, + // black filled. + CGContextSetRGBFillColor(ctx, 0.0, 0.0, 0.0, 1.0); + +#define kcpRectHalf 4 + + for (int i = 0; i < ARRAYSIZE(apt); i++) { + CGRect rc = CGRectMake(apt[i].y - kcpRectHalf, + psiz->cy - (apt[i].x + kcpRectHalf), + kcpRectHalf * 2, kcpRectHalf * 2); + CGContextFillEllipseInRect(ctx, rc); + CGContextStrokeEllipseInRect(ctx, rc); + } + + crit_.Leave(); +} + +} // namespace wi diff --git a/game/iphone/spritemgradapter.h b/game/iphone/spritemgradapter.h new file mode 100644 index 0000000..1e9c983 --- /dev/null +++ b/game/iphone/spritemgradapter.h @@ -0,0 +1,39 @@ +#ifndef __SPRITEMGRADAPTER_H__ +#define __SPRITEMGRADAPTER_H__ + +#include "game/sprite.h" + +// C++ SpriteManager wrapper + +namespace wi { + +class SpriteManagerAdapter : public SpriteManager { +public: + SpriteManagerAdapter(WiView *view) : view_(view) { } + + virtual void SetClipRects(wi::Rect *prc1, wi::Rect *prc2) { + return [view_ setClipRects:prc1 prc2:prc2]; + } + virtual wi::AnimSprite *CreateAnimSprite() { + return [view_ createAnimSprite]; + } + virtual wi::SelectionSprite *CreateSelectionSprite() { + return [view_ createSelectionSprite]; + } + virtual void Add(wi::Sprite *pspr) { + return [view_ addSprite:pspr]; + } + virtual void Remove(wi::Sprite *pspr) { + return [view_ removeSprite:pspr]; + } + virtual void Update(wi::Sprite *pspr) { + return [view_ updateSprite:pspr]; + } + +private: + WiView *view_; +}; + +} // namespace wi + +#endif // __SPRITEMGRADAPTER_H__ diff --git a/game/iphone/textalertview.h b/game/iphone/textalertview.h new file mode 100644 index 0000000..acf641a --- /dev/null +++ b/game/iphone/textalertview.h @@ -0,0 +1,31 @@ +/* + * Text Alert View + * + * File: TextAlertView.h + * Abstract: UIAlertView extension with UITextField (Interface Declaration). + * + */ + +#ifndef __TEXTALERTVIEW_H__ +#define __TEXTALERTVIEW_H__ + +#import + +#define kUITextFieldHeight 25.0 +#define kUITextFieldXPadding 12.0 +#define kUITextFieldYPadding 10.0 +#define kUIAlertOffset 100.0 + +@interface TextAlertView : UIAlertView { + UITextField *textField_; + BOOL layoutDone_; +} +@property (nonatomic, retain) UITextField *textField_; + +- (id)initSpecial:(NSString *)title default:(NSString *)def + keyboardType:(UIKeyboardType)keyboardType secure:(BOOL)secure + delegate:(id)delegate cancelButtonTitle:(NSString *)cancelButtonTitle + otherButtonTitles:(NSString *)otherButtonTitles, ...; +@end + +#endif // __TEXTALERTVIEW_H__ diff --git a/game/iphone/textalertview.m b/game/iphone/textalertview.m new file mode 100644 index 0000000..4c11cf1 --- /dev/null +++ b/game/iphone/textalertview.m @@ -0,0 +1,150 @@ +/* + * Text Alert View + * + * File: TextAlertView.m + * Abstract: UIAlertView extension with UITextField (Implementation). + * + * Pulled off the 'tubes. It's a hack because it subclasses the built in + * AlertView and makes all sorts of assumptions. + */ + +#import "game/iphone/textalertview.h" + +@implementation TextAlertView + +@synthesize textField_; + +/* + * Initialize view with maximum of two buttons + */ +- (id)initSpecial:(NSString *)title default:(NSString *)def + keyboardType:(UIKeyboardType)keyboard secure:(BOOL)secure + delegate:(id)delegate cancelButtonTitle:(NSString *)cancelButtonTitle + otherButtonTitles:(NSString *)otherButtonTitles, ... { + + self = [super initWithTitle:title message:@"hideme" delegate:delegate + cancelButtonTitle:cancelButtonTitle + otherButtonTitles:otherButtonTitles, nil]; + + if (self) { + // Create and add UITextField to UIAlertView + UITextField *myTextField = [[[UITextField alloc] initWithFrame:CGRectZero] retain]; + myTextField.autocorrectionType = UITextAutocorrectionTypeNo; + myTextField.autocapitalizationType = UITextAutocapitalizationTypeNone; + myTextField.keyboardAppearance = UIKeyboardAppearanceAlert; + myTextField.keyboardType = keyboard; + //myTextField.alpha = 0.75; + myTextField.alpha = 1.0; + myTextField.borderStyle = UITextBorderStyleRoundedRect; + myTextField.delegate = delegate; + myTextField.text = def; + myTextField.secureTextEntry = secure; + [self setTextField_:myTextField]; + // insert UITextField before first button + for (UIView *view in self.subviews) { + if (![view isKindOfClass:[UILabel class]]) { + [self insertSubview:myTextField aboveSubview:view]; + break; + } + } + + // hide the message label - so only the title is visible + for (UIView *view in self.subviews) { + if ([view isKindOfClass:[UILabel class]]) { + UILabel *label = (UILabel *)view; + if (label.text == @"hideme") { + label.hidden = YES; + break; + } + } + } + + layoutDone_ = NO; + + // add a transform to move the UIAlertView above the keyboard + CGAffineTransform myTransform = CGAffineTransformMakeTranslation(0.0, kUIAlertOffset); + [self setTransform:myTransform]; + } + return self; +} + +/* + * Show alert view and make keyboard visible + */ +- (void) show { + [super show]; + [[self textField_] becomeFirstResponder]; +} + +/* + * Determine maximum y-coordinate of UILabel objects. This method assumes + * that only following objects are contained in subview list: + * - UILabel + * - UITextField + * - UIThreePartButton (Private Class) + */ +- (CGFloat) maxLabelYCoordinate { + // Determine maximum y-coordinate of labels + CGFloat maxY = 0; + for (UIView *view in self.subviews) { + if ([view isKindOfClass:[UILabel class]]) { + if (view.hidden == YES) { + continue; + } + CGRect viewFrame = [view frame]; + CGFloat lowerY = viewFrame.origin.y + viewFrame.size.height; + if (lowerY > maxY) { + maxY = lowerY; + } + } + } + return maxY; +} + +/* + * Override layoutSubviews to correctly handle the UITextField + */ +- (void)layoutSubviews { + [super layoutSubviews]; + CGRect frame = [self frame]; + CGFloat alertWidth = frame.size.width; + + // Perform layout of subviews just once + if (!layoutDone_) { + CGFloat labelMaxY = [self maxLabelYCoordinate]; + + // Size the textField below labels + CGRect textFieldFrame; + for (UIView *view in self.subviews) { + if ([view isKindOfClass:[UITextField class]]) { + textFieldFrame = CGRectMake( + kUITextFieldXPadding, + labelMaxY + kUITextFieldYPadding, + alertWidth - 2.0*kUITextFieldXPadding, + kUITextFieldHeight); + [view setFrame:textFieldFrame]; + break; + } + } + + // Keyboard is 180 high according to iPhone HIG. Center view within + // the upper 180. + + frame.origin.y += 15; + [self setFrame:frame]; + layoutDone_ = YES; + } else { + // reduce the x placement and width of the UITextField based on + // UIAlertView width + + for (UIView *view in self.subviews) { + if ([view isKindOfClass:[UITextField class]]) { + CGRect viewFrame = [view frame]; + viewFrame.origin.x = kUITextFieldXPadding; + viewFrame.size.width = alertWidth - 2.0*kUITextFieldXPadding; + [view setFrame:viewFrame]; + } + } + } +} +@end diff --git a/game/iphone/transportmgr.cpp b/game/iphone/transportmgr.cpp new file mode 100644 index 0000000..d29cb5c --- /dev/null +++ b/game/iphone/transportmgr.cpp @@ -0,0 +1,16 @@ +#include "../ht.h" +#include "xtransport.h" + +namespace wi { + +TransportMgr gtram; + +//--------------------------------------------------------------------------- +// TransportMgr implementation + +int TransportMgr::GetTransportDescriptions(TransportDescription *atrad, int ctradMax) +{ + return XTransport::GetTransportDescriptions(atrad, ctradMax); +} + +} // namespace wi \ No newline at end of file diff --git a/game/iphone/webviewcontroller.h b/game/iphone/webviewcontroller.h new file mode 100644 index 0000000..eba4745 --- /dev/null +++ b/game/iphone/webviewcontroller.h @@ -0,0 +1,32 @@ +#ifndef __WEBVIEWCONTROLLER_H__ +#define __WEBVIEWCONTROLLER_H__ + +#import +#import +#import +#import +#import + +@class WebViewController; + +@protocol WebViewControllerDelegate +- (void)onDone:(WebViewController *)c; +@end + +@interface WebViewController : NSObject { + UIView *parent_; + UIView *view_; + UIWebView *webView_; + UIActivityIndicatorView *activityView_; + UIButton *backButton_; + UIButton *forwardButton_; + id delegate_; +} +- (void)loadView; +- (void)show; +- (void)loadDocument:(NSString *)url withTitle:(NSString *)title; +- (void)setDocument:(NSString *)doc; +- (id)init:(id)delegate parent:(UIView *)parent; +@end + +#endif // __WEBVIEWCONTROLLER_H__ diff --git a/game/iphone/webviewcontroller.mm b/game/iphone/webviewcontroller.mm new file mode 100644 index 0000000..45e9b5e --- /dev/null +++ b/game/iphone/webviewcontroller.mm @@ -0,0 +1,175 @@ +#import "game/iphone/webviewcontroller.h" + +@implementation WebViewController + +- (id)init:(id)delegate parent:(UIView *)parent { + parent_ = parent; + [parent_ retain]; + delegate_ = delegate; + [delegate_ retain]; + view_ = nil; + webView_ = nil; + activityView_ = nil; + backButton_ = nil; + forwardButton_ = nil; + + [self loadView]; + + return self; +} + +- (void)dealloc { + [webView_ release]; + [activityView_ release]; + [backButton_ release]; + [forwardButton_ release]; + [view_ release]; + [delegate_ release]; + [parent_ release]; + [super dealloc]; +} + +- (void)loadView { + // Create parent view for web view and toolbar + CGRect frame = CGRectMake(0, 0, parent_.frame.size.height, + parent_.frame.size.width); + UIView *parentView = [[[UIView alloc] initWithFrame:frame] autorelease]; + parentView.autoresizesSubviews = YES; + parentView.autoresizingMask = UIViewAutoresizingFlexibleHeight | + UIViewAutoresizingFlexibleWidth; + + // Create toolbar + UIToolbar *toolbar = [[[UIToolbar alloc] initWithFrame:CGRectZero] autorelease]; + toolbar.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | + UIViewAutoresizingFlexibleWidth; + toolbar.autoresizesSubviews = YES; + [parentView addSubview: toolbar]; + + // Add buttons to toolbar + UIBarButtonItem *spacer0 = [[[UIBarButtonItem alloc] + initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace + target:nil action:nil] autorelease]; + spacer0.width = 10; + UIImage *backImage = [UIImage imageNamed:@"NavBackSmall.png"]; + UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom]; + [backButton setImage:backImage forState:UIControlStateNormal]; + [backButton addTarget:self action:@selector(onBack) + forControlEvents:UIControlEventTouchUpInside]; + backButton.enabled = NO; + backButton.showsTouchWhenHighlighted = YES; + UIBarButtonItem *backBarButton = [[[UIBarButtonItem alloc] + initWithCustomView:backButton] autorelease]; + UIBarButtonItem *spacer1 = [[[UIBarButtonItem alloc] + initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace + target:nil action:nil] autorelease]; + UIImage *forwardImage = [UIImage imageNamed:@"NavForwardSmall.png"]; + UIButton *forwardButton = [UIButton buttonWithType:UIButtonTypeCustom]; + [forwardButton setImage:forwardImage forState:UIControlStateNormal]; + [forwardButton addTarget:self action:@selector(onForward) + forControlEvents:UIControlEventTouchUpInside]; + forwardButton.enabled = NO; + forwardButton.showsTouchWhenHighlighted = YES; + UIBarButtonItem *forwardBarButton = [[[UIBarButtonItem alloc] + initWithCustomView:forwardButton] autorelease]; + UIBarButtonItem *spacer2 = [[[UIBarButtonItem alloc] + initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace + target:nil action:nil] autorelease]; + UIActivityIndicatorView *activityView = [[[UIActivityIndicatorView alloc] + initWithActivityIndicatorStyle: UIActivityIndicatorViewStyleWhite] + autorelease]; + UIBarButtonItem *activityBarButton = [[[UIBarButtonItem alloc] + initWithCustomView:activityView] autorelease]; + UIBarButtonItem *spacer3 = [[[UIBarButtonItem alloc] + initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace + target:nil action:nil] autorelease]; + UIBarButtonItem *doneBarButton = [[[UIBarButtonItem alloc] + initWithBarButtonSystemItem:UIBarButtonSystemItemDone + target:self action:@selector(onDone)] autorelease]; + NSArray *array = [NSArray arrayWithObjects: spacer0, backBarButton, spacer1, + forwardBarButton, spacer2, activityBarButton, spacer3, + doneBarButton, nil]; + [toolbar setItems:array animated:NO]; + [toolbar sizeToFit]; + + // Need to set bounds for these or they won't hittest + backButton.bounds = CGRectMake(0, 0, backImage.size.width, + toolbar.frame.size.height); + forwardButton.bounds = CGRectMake(0, 0, forwardImage.size.width, + toolbar.frame.size.height); + + // Create web view + frame.size.height -= toolbar.frame.size.height; + UIWebView *webView = [[[UIWebView alloc] initWithFrame:frame] autorelease]; + webView.backgroundColor = [UIColor whiteColor]; + webView.scalesPageToFit = YES; + webView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | + UIViewAutoresizingFlexibleHeight); + webView.delegate = self; + [parentView addSubview: webView]; + + // Position toolbar + CGRect toolbar_frame = toolbar.frame; + CGRect parent_frame = parentView.bounds; + toolbar_frame.origin.y = parent_frame.size.height - + toolbar_frame.size.height; + toolbar.frame = toolbar_frame; + + // Keep these around + view_ = parentView; + [view_ retain]; + webView_ = webView; + [webView_ retain]; + activityView_ = activityView; + [activityView_ retain]; + backButton_ = backButton; + [backButton_ retain]; + forwardButton_ = forwardButton; + [forwardButton_ retain]; +} + +- (void)show { + [parent_ addSubview:view_]; +} + +- (void)onDone { + [view_ removeFromSuperview]; + [delegate_ onDone:self]; +} + +- (void)loadDocument:(NSString *)url withTitle:(NSString *)title { + NSURLRequest *req = [NSURLRequest requestWithURL: + [NSURL URLWithString:url]]; + [webView_ loadRequest:req]; +} + +- (void)setDocument:(NSString *)doc { + [webView_ loadHTMLString:doc baseURL:nil]; +} + +- (void)updateNavButtons { + backButton_.enabled = webView_.canGoBack; + forwardButton_.enabled = webView_.canGoForward; +} + +- (void)webViewDidStartLoad:(UIWebView *)webview { + [activityView_ startAnimating]; + [self updateNavButtons]; +} + +- (void)webViewDidFinishLoad:(UIWebView *)webview { + [activityView_ stopAnimating]; + [self updateNavButtons]; +} + +- (void)onBack { + [webView_ goBack]; +} + +- (void)onForward { + [webView_ goForward]; +} + +- (void)webView:(UIWebView *)webview didFailLoadWithError:(NSError *)error { + [webView_ loadHTMLString:@"Timed out loading page." baseURL:nil]; +} +@end diff --git a/game/iphone/wiview.h b/game/iphone/wiview.h new file mode 100644 index 0000000..5adf02d --- /dev/null +++ b/game/iphone/wiview.h @@ -0,0 +1,55 @@ +// +// wiview.h +// wi +// +// Created by Scott Ludwig on 4/21/08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import +#import +#import +#import +#import +#include "game/ht.h" +#include "game/sprite.h" +#include "base/criticalsection.h" +#include "base/thread.h" + +namespace wi { +class SpriteManagerAdapter; +} + +@interface WiView : UIView { + UITouch *atouch_[2]; + CGRect rect_; + byte *pb_; + bool havePalette_; + wi::Rect rcClip1_; + wi::Rect rcClip2_; + wi::SpriteManagerAdapter *psprm_; + wi::Sprite *apspr_[16]; + int cpspr_; + base::CriticalSection *pcrit_; + base::Thread *game_thread_; + bool fSpriteDirty_; + wi::Point aptLast_[2]; +} +- (id)initWithFrame:(CGRect)rect; +- (void)dealloc; +- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event; +- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event; +- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event; +- (void)getSurfaceProperties:(wi::SurfaceProperties *)pprops; +- (wi::SpriteManager *)getSpriteManager; +- (void)setClipRects:(wi::Rect *)prc1 prc2:(wi::Rect *)prc2; +- (wi::AnimSprite *)createAnimSprite; +- (wi::SelectionSprite *)createSelectionSprite; +- (void)addSprite:(wi::Sprite *)pspr; +- (void)removeSprite:(wi::Sprite *)pspr; +- (void)updateSprite:(wi::Sprite *)pspr; +- (void)drawSprites:(CGContextRef)ctx; +- (void)setFormMgrs:(wi::FormMgr *)pfrmmSimUI input:(wi::FormMgr *)pfrmmInput; +- (void)resetScrollOffset; +- (void)setGameThread:(base::Thread *)thread; +@end diff --git a/game/iphone/wiview.mm b/game/iphone/wiview.mm new file mode 100644 index 0000000..6ec093d --- /dev/null +++ b/game/iphone/wiview.mm @@ -0,0 +1,346 @@ +// +// wiview.mm +// wi +// +// Created by Scott Ludwig on 4/21/08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import "game/iphone/wiview.h" +#import "game/iphone/spritemgradapter.h" +#import "game/iphone/iphoneanimsprite.h" +#import "game/iphone/selectionsprite.h" +#import "game/iphone/webviewcontroller.h" +#include "game/iphone/input.h" +#include "mpshared/misc.h" + +@implementation WiView + +- (id)initWithFrame:(struct CGRect)rect +{ + self = [super initWithFrame: rect]; + if (self == nil) { + return nil; + } + + rect_ = CGRectMake(0, 0, rect.size.height, rect.size.width); + + [self setMultipleTouchEnabled: YES]; + atouch_[0] = NULL; + atouch_[1] = NULL; + havePalette_ = false; + pb_ = NULL; + memset(&rcClip1_, 0, sizeof(rcClip1_)); + memset(&rcClip2_, 0, sizeof(rcClip2_)); + pcrit_ = new base::CriticalSection(); + psprm_ = new wi::SpriteManagerAdapter(self); + cpspr_ = 0; + fSpriteDirty_ = false; + game_thread_ = NULL; + + return self; +} + +- (void)dealloc +{ + if (pb_ != NULL) { + free(pb_); + } + delete psprm_; + delete pcrit_; + [super dealloc]; +} + +- (void)setGameThread:(base::Thread *)thread +{ + game_thread_ = thread; +} + +- (void)checkTracking:(UIEvent *)event +{ + // This is just in case - if we lose an up, this detects it and resets + // things. So far touchesCancelled appears to be working as advertised. + + int cTouchesTracking = 0; + for (int i = 0; i < 2; i++) { + if (atouch_[i] != NULL) { + cTouchesTracking++; + } + } + int cTouchesFound = 0; + NSSet *allTouches = [event allTouches]; + for (UITouch *touch in allTouches) { + for (int i = 0; i < 2; i++) { + if (touch == atouch_[i]) { + cTouchesFound++; + } + } + } + + // If it's whacked, post an up and set the slot to NULL. + + if (cTouchesFound != cTouchesTracking) { + for (int i = 0; i < 2; i++) { + if (atouch_[i] != NULL) { + atouch_[i] = NULL; + base::Message msg; + msg.id = (i == 0) ? kidmMouseUp : kidmMouseUp2; + msg.x = aptLast_[i].x; + msg.y = aptLast_[i].y; + msg.ff = 0; + msg.ms = wi::HostGetMillisecondCount(); + if (game_thread_ != NULL) { + game_thread_->Post(&msg); + } + } + } + } +} + +- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event +{ + // Sometimes we won't get an up. Handle this case. + + [self checkTracking:event]; + + // Keep track of touches. According to the documentation, the touch * + // pointer value is constant throughout the life of the touch, so it + // can be used as sort of an id. + + + // Only process touches for this view. WebView and ChatView are + // handled as children, and events in their toolbar area somehow + // get sent here. However this parent only wants touches for its + // own view, not for the whole window. + + for (UITouch *touch in touches) { + if (touch.view != self) { + continue; + } + for (int i = 0; i < 2; i++) { + if (atouch_[i] == NULL) { + atouch_[i] = touch; + CGPoint point = [touch locationInView: nil]; + base::Message msg; + msg.id = (i == 0) ? kidmMouseDown : kidmMouseDown2; + msg.x = (int)point.x; + msg.y = (int)point.y; + msg.ff = 0; + msg.ms = wi::HostGetMillisecondCount(); + if (game_thread_ != NULL) { + game_thread_->Post(&msg); + } + aptLast_[i].x = (int)point.x; + aptLast_[i].y = (int)point.y; + break; + } + } + } +} + +- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event +{ + for (UITouch *touch in touches) { + if (touch.view != self) { + continue; + } + for (int i = 0; i < 2; i++) { + if (atouch_[i] == touch) { + CGPoint point = [touch locationInView: nil]; + base::Message msg; + msg.id = (i == 0) ? kidmMouseMove : kidmMouseMove2; + msg.x = (int)point.x; + msg.y = (int)point.y; + msg.ff = 0; + msg.ms = wi::HostGetMillisecondCount(); + if (game_thread_ != NULL) { + game_thread_->Post(&msg, + (i == 0) ? kidmMouseMove2 : kidmMouseMove); + } + aptLast_[i].x = (int)point.x; + aptLast_[i].y = (int)point.y; + break; + } + } + } +} + +- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event +{ + for (UITouch *touch in touches) { + if (touch.view != self) { + continue; + } + for (int i = 0; i < 2; i++) { + if (atouch_[i] == touch) { + atouch_[i] = NULL; + CGPoint point = [touch locationInView: nil]; + base::Message msg; + msg.id = (i == 0) ? kidmMouseUp : kidmMouseUp2; + msg.x = (int)point.x; + msg.y = (int)point.y; + msg.ff = 0; + msg.ms = wi::HostGetMillisecondCount(); + if (game_thread_ != NULL) { + game_thread_->Post(&msg); + } + aptLast_[i].x = (int)point.x; + aptLast_[i].y = (int)point.y; + break; + } + } + } +} + +- (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event +{ + for (int i = 0; i < 2; i++) { + if (atouch_[i] != NULL) { + atouch_[i] = NULL; + base::Message msg; + msg.id = (i == 0) ? kidmMouseUp : kidmMouseUp2; + msg.x = aptLast_[i].x; + msg.y = aptLast_[i].y; + msg.ff = 0; + msg.ms = wi::HostGetMillisecondCount(); + if (game_thread_ != NULL) { + game_thread_->Post(&msg); + } + } + } +} + +- (void)getSurfaceProperties:(wi::SurfaceProperties *)pprops +{ + pprops->cxWidth = (int)rect_.size.width; + pprops->cyHeight = (int)rect_.size.height; + pprops->cbxPitch = 2; + pprops->cbyPitch = (int)rect_.size.width * 2; + pprops->ffFormat = wi::kfDirect565; +} + +- (wi::SpriteManager *)getSpriteManager +{ + return psprm_; +} + +- (void)setClipRects:(wi::Rect *)prc1 prc2:(wi::Rect *)prc2 +{ + rcClip1_ = *prc1; + rcClip2_ = *prc2; +} + +- (wi::AnimSprite *)createAnimSprite +{ + return new wi::IPhoneAnimSprite(psprm_); +} + +- (wi::SelectionSprite *)createSelectionSprite +{ + return new wi::IPhoneSelectionSprite(psprm_); +} + +- (void)addSprite:(wi::Sprite *)pspr +{ + base::CritScope cs(pcrit_); + + // If already added, just recreate the layer + + bool fFound = false; + for (int i = 0; i < cpspr_; i++) { + if (pspr == apspr_[i]) { + fFound = true; + } + } + if (!fFound && cpspr_ < ARRAYSIZE(apspr_) - 1) { + apspr_[cpspr_] = pspr; + cpspr_++; + } + + fSpriteDirty_ = true; +} + +- (void)removeSprite:(wi::Sprite *)pspr +{ + base::CritScope cs(pcrit_); + + bool fFound = false; + for (int i = 0; i < cpspr_; i++) { + if (pspr == apspr_[i]) { + cpspr_--; + if (i < ARRAYSIZE(apspr_) - 1) { + memmove(&apspr_[i], &apspr_[i + 1], + (ARRAYSIZE(apspr_) - 1 - i) * ELEMENTSIZE(apspr_)); + } + fFound = true; + break; + } + } + + fSpriteDirty_ = true; +} + +- (void)updateSprite:(wi::Sprite *)pspr +{ + fSpriteDirty_ = true; +} + +- (void)drawSprites:(CGContextRef)ctx +{ + base::CritScope cs(pcrit_); + + if (cpspr_ == 0) { + return; + } + + // Save context before setting clip rects + CGContextSaveGState(ctx); + + // First clip to the playfield up to just before the left edge of the + // minimap, then the area above the minimap. + +#if 0 + CGRect arcClip[2]; + arcClip[0].origin.x = rcClip1_.left; + arcClip[0].origin.y = (int)rect_.size.width - rcClip1_.top - + rcClip1_.Height(); + arcClip[0].size.width = rcClip1_.Width(); + arcClip[0].size.height = rcClip1_.Height(); + arcClip[1].origin.x = rcClip2_.left; + arcClip[1].origin.y = (int)rect_.size.width - rcClip2_.top - + rcClip2_.Height(); + arcClip[1].size.width = rcClip2_.Width(); + arcClip[1].size.height = rcClip2_.Height(); +#else + CGRect arcClip[2]; + arcClip[0].origin.x = rcClip1_.left; + arcClip[0].origin.y = rect_.size.height - rcClip1_.bottom; + arcClip[0].size.width = rcClip1_.Width(); + arcClip[0].size.height = rcClip1_.Height(); + arcClip[1].origin.x = rcClip2_.left; + arcClip[1].origin.y = rect_.size.height - rcClip2_.bottom; + arcClip[1].size.width = rcClip2_.Width(); + arcClip[1].size.height = rcClip2_.Height(); +#endif + CGContextClipToRects(ctx, arcClip, 2); + + // Draw sprites + wi::Size siz; + siz.cx = (int)rect_.size.width; + siz.cy = (int)rect_.size.height; + for (int i = 0; i < cpspr_; i++) { + apspr_[i]->Draw(ctx, &siz); + } + + // Restore context + CGContextRestoreGState(ctx); +} + +- (void)setFormMgrs:(wi::FormMgr *)pfrmmSimUI input:(wi::FormMgr *)pfrmmInput +{ +} + +- (void)resetScrollOffset +{ +} +@end diff --git a/game/iphone/wiviewcontroller.h b/game/iphone/wiviewcontroller.h new file mode 100644 index 0000000..305a04e --- /dev/null +++ b/game/iphone/wiviewcontroller.h @@ -0,0 +1,33 @@ +#ifndef __WIVIEWCONTROLLER_H__ +#define __WIVIEWCONTROLLER_H__ + +#import +#import +#import +#import +#import +#import +#import "game/iphone/inputcontroller.h" +#import "game/iphone/webviewcontroller.h" +#include "base/thread.h" + +@interface WiViewController : UIViewController { + char titleAsk_[512]; + char ask_[512]; + int keyboardAsk_; + int maxAsk_; + BOOL secureAsk_; + base::Thread *game_thread_; + InputController *input_controller_; + WebViewController *web_controller_; +} +- (void)setGameThread:(base::Thread *)thread; +- (void)initiateWebView:(const char *)title withUrl:(const char *)url; +- (void)initiateAsk:(const char *)title max:(int)max default:(const char *)def + keyboard:(int)keyboard secure:(BOOL)secure; +- (void)getAskString:(char *)psz size:(int)cb; +- (void)setGameThread:(base::Thread *)thread; +@end + +#endif // __WIVIEWCONTROLLER_H__ diff --git a/game/iphone/wiviewcontroller.mm b/game/iphone/wiviewcontroller.mm new file mode 100644 index 0000000..e0285e7 --- /dev/null +++ b/game/iphone/wiviewcontroller.mm @@ -0,0 +1,170 @@ +#import "game/iphone/wiviewcontroller.h" +#import "game/iphone/cgview.h" +#import "game/iphone/chatviewcontroller.h" +#import "game/iphone/webviewcontroller.h" +#include "game/iphone/input.h" + +@implementation WiViewController + +- (id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)bundle +{ + self = [super initWithNibName:nibName bundle:bundle]; + if (self == nil) { + return nil; + } + input_controller_ = nil; + game_thread_ = NULL; + web_controller_ = nil; + return self; +} + +- (void)loadView +{ +#if 1 + // As of iOS7 applicationFrame subtracts the status bar height. So we use the full screen bounds instead. + CGRect frame = [[UIScreen mainScreen] bounds]; +#else + CGRect frame = [[UIScreen mainScreen] applicationFrame]; +#endif + UIView *view = [[CgView alloc] initWithFrame: frame]; + view.autoresizesSubviews = YES; + view.autoresizingMask = (UIViewAutoresizingFlexibleWidth | + UIViewAutoresizingFlexibleHeight); + self.view = view; + +#if 0 + UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect]; + [button setTitle:@"Hello world!" forState:UIControlStateNormal]; + frame = button.frame; + frame.origin.x = 0; + frame.origin.y = 0; + frame.size.width = 100; + frame.size.height = 100; + button.frame = frame; + [self.view addSubview:button]; +#endif +} + +// DEPRECATED in iOS 6.0. No longer sufficient to get correct orientation on iOS 7. +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)way +{ + return way == UIInterfaceOrientationLandscapeRight; +} + +- (BOOL)shouldAutorotate { + return YES; +} + +- (NSUInteger)supportedInterfaceOrientations { + return UIInterfaceOrientationMaskLandscape; +} + +- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation { + return UIInterfaceOrientationLandscapeRight; +} + +- (BOOL)prefersStatusBarHidden { + return YES; +} + +- (void)setGameThread:(base::Thread *)thread +{ + game_thread_ = thread; + [(WiView *)(self.view) setGameThread:thread]; +} + +- (void)doShowWebView:(NSDictionary *)dict +{ + // Alloc web view controller + web_controller_ = [[WebViewController alloc] init:self parent:self.view]; + + [web_controller_ show]; + + // Tell it to start loading the document + NSString *url = [dict objectForKey:@"url"]; + NSString *title = [dict objectForKey:@"title"]; + [web_controller_ loadDocument:url withTitle:title]; +} + +- (void)onDone:(WebViewController *)c { + [web_controller_ autorelease]; + web_controller_ = nil; +} + +- (void)initiateWebView:(const char *)title withUrl:(const char *)url +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + NSString *title_s = [NSString stringWithCString:title + encoding:[NSString defaultCStringEncoding]]; + NSString *url_s = [NSString stringWithCString:url + encoding:[NSString defaultCStringEncoding]]; + NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys: + title_s, @"title", url_s, @"url", (char *)NULL]; + [self performSelectorOnMainThread:@selector(doShowWebView:) + withObject:dict waitUntilDone:NO]; + [pool release]; +} + +- (void)doAsk +{ + NSString *title = [NSString stringWithCString:titleAsk_ + encoding:[NSString defaultCStringEncoding]]; + NSString *def = [NSString stringWithCString:ask_ + encoding:[NSString defaultCStringEncoding]]; + + UIKeyboardType keyboardType; + switch (keyboardAsk_) { + //case knKeyboardAskURL: + case 1: + keyboardType = UIKeyboardTypeURL; + break; + + // case knKeyboardAskDefault: + case 0: + default: + keyboardType = UIKeyboardTypeASCIICapable; + break; + } + + InputController *input_controller = [[InputController alloc] + init:title default:def keyboardType:keyboardType + delegate:self maxChars:maxAsk_ secure:secureAsk_]; + input_controller_ = input_controller; +} + +- (void)onDone:(InputController *)c text:(NSString *)text { + const char *pszText = [text cStringUsingEncoding: + [NSString defaultCStringEncoding]]; + wi::strncpyz(ask_, pszText, sizeof(ask_)); + [input_controller_ autorelease]; + input_controller_ = nil; + + // Alerts are modeless, so this hack is needed to notify + // the game thread + if (game_thread_ != NULL) { + game_thread_->Post(kidmAskStringEvent, NULL); + } +} + +- (void)getAskString:(char *)psz size:(int)cb +{ + wi::strncpyz(psz, ask_, cb); +} + +- (void)initiateAsk:(const char *)title max:(int)max default:(const char *)def + keyboard:(int)keyboard secure:(BOOL)secure +{ + // Called by the game to retrieve an URL from the user using native UI. + // Ask the main thread to show the UI. The UI isn't modal so the result + // needs to be retrieved later + + wi::strncpyz(titleAsk_, title, sizeof(titleAsk_)); + wi::strncpyz(ask_, def, sizeof(ask_)); + keyboardAsk_ = keyboard; + maxAsk_ = max; + secureAsk_ = secure; + + [self performSelectorOnMainThread:@selector(doAsk) + withObject:nil waitUntilDone: NO]; +} +@end diff --git a/game/iphone/xconnection.cpp b/game/iphone/xconnection.cpp new file mode 100644 index 0000000..dc7f71e --- /dev/null +++ b/game/iphone/xconnection.cpp @@ -0,0 +1,67 @@ +#include "game/iphone/xconnection.h" +#include "game/iphone/input.h" +#include "base/socket.h" + +const int kidmConnect = 1; +const int kidmDisconnect = 2; + +namespace wi { + +XConnection::XConnection(XTransport *xtrans, dword gameid) : xtrans_(xtrans), + gameid_(gameid) { + thread_.Post(kidmConnect, this); +} + +XConnection::~XConnection() { + // When the game exits, the connection gets deleted. When this happens, + // it is necessary for the transport to disconnect from the game. + LOG(); + xtrans_->DisconnectGame(gameid_); +} + +bool XConnection::AsyncSend(NetMessage *pnm) { + return xtrans_->SendNetMessage(pnm); +} + +void XConnection::OnMessage(base::Message *pmsg) { + LOG() << pmsg->id; + + switch (pmsg->id) { + case kidmConnect: + // Callers to AsyncConnect expect to get called back + // asynchronously. + if (m_pccb != NULL) { + m_pccb->OnConnectComplete(this); + } + break; + + case kidmDisconnect: + // This will cause the game to exit, then this connection + // will get deleted. + if (m_pccb != NULL) { + m_pccb->OnDisconnect(this); + } + break; + } + + // Cause a message to be pumped through the game input loop to cause it + // to wake up, since processing here was done in MessageQueue dispatch. + + thread_.Post(kidmNullEvent, NULL); +} + +void XConnection::AsyncDisconnect() { + LOG(); + thread_.Post(kidmDisconnect, this); +} + +void XConnection::OnNetMessage(NetMessage **ppnm) { +#ifdef LOGGING + LOG() << PszFromNetMessage(*ppnm); +#endif + if (m_pccb != NULL) { + m_pccb->OnReceive(this, ppnm); + } +} + +} // namespace wi diff --git a/game/iphone/xconnection.h b/game/iphone/xconnection.h new file mode 100644 index 0000000..1f85d85 --- /dev/null +++ b/game/iphone/xconnection.h @@ -0,0 +1,33 @@ +#ifndef __XCONNECTION_H__ +#define __XCONNECTION_H__ + +#include "base/messagequeue.h" +#include "game/ht.h" +#include "game/iphone/xtransport.h" + +namespace wi { + +class XTransport; + +class XConnection : public Connection, base::MessageHandler { +public: + XConnection(XTransport *xtrans, dword gameid); + ~XConnection(); + virtual dword gameid() { return gameid_; } + + // Connection overrides + virtual bool AsyncSend(NetMessage *pnm); + virtual void AsyncDisconnect(); + + void OnNetMessage(NetMessage **ppnm); + +private: + virtual void OnMessage(base::Message *pmsg); + + XTransport *xtrans_; + dword gameid_; +}; + +} // namespace wi + +#endif // __XCONNECTION_H__ diff --git a/game/license.h b/game/license.h new file mode 100644 index 0000000..6975af9 --- /dev/null +++ b/game/license.h @@ -0,0 +1,7 @@ +// On-screen licensing strings. Requires INCL_DRAWREVISIONNUMBER to draw +// pszLicenseLine1 is shared with the revision number +// pszLicenseLine2 is the next line. +// Either can be NULL in which case smart formatting occurs. + +char *pszLicenseLine1 = NULL; //"Licensed to:"; +char *pszLicenseLine2 = NULL; //"Omni Interactive Audio"; diff --git a/game/loadsave.cpp b/game/loadsave.cpp new file mode 100644 index 0000000..2ac1b6b --- /dev/null +++ b/game/loadsave.cpp @@ -0,0 +1,210 @@ +#include "ht.h" + +namespace wi { + +Stream *PickLoadGameStream() +{ + LoadGameForm *pfrm = (LoadGameForm *)gpmfrmm->LoadForm(gpiniForms, kidfLoadGame, new LoadGameForm()); + if (pfrm == NULL) + return NULL; + pfrm->SelectLast(true); + + Stream *pstm = NULL; + while (true) { + int nGame = -1; + if (!pfrm->DoModal(&nGame)) { + delete pfrm; + return NULL; + } + + pstm = HostOpenSaveGameStream(nGame); + if (pstm != NULL) + break; + pfrm->Show(true); + HtMessageBox(kfMbWhiteBorder, "Load Saved Game", "Please select a valid saved game to load first!"); + } + + delete pfrm; + return pstm; +} + +bool PickSaveGameStream(Stream **ppstm) +{ + LoadGameForm *pfrm = (LoadGameForm *)gpmfrmm->LoadForm(gpiniForms, kidfSaveGame, new LoadGameForm()); + if (pfrm == NULL) + return false; + pfrm->SelectLast(false); + int nGame = -1; + if (!pfrm->DoModal(&nGame)) { + delete pfrm; + return false; + } + delete pfrm; + *ppstm = HostNewSaveGameStream(nGame, (char *)gsim.GetLevel()->GetTitle()); + return true; +} + +void DeleteStaleSaveGames() +{ + for (int nGame = knGameReinitializeSave; nGame < 20;) { + Stream *pstm = HostOpenSaveGameStream(nGame, false); + if (pstm != NULL) { + char szVersion[32]; + szVersion[0] = 0; + pstm->ReadString(szVersion, sizeof(szVersion)); + byte bVer = pstm->ReadByte(); + byte bPlatform = 0; + if (bVer >= 6) + bPlatform = pstm->ReadByte(); + pstm->Close(); + delete pstm; + + if (!CheckSaveGameVersion(szVersion, bPlatform)) + HostDeleteSaveGame(NULL, nGame); + } + + // Ugly + + if (nGame == knGameReinitializeSave) { + nGame = 0; + } else { + nGame++; + } + } + + // delete any crashed save games + + HostDeleteSaveGame(kszTempName, 0); +} + +bool CheckSaveGameVersion(char *pszVersion, byte bPlatform) +{ +#ifndef DEV_BUILD +#ifdef PIL +#ifdef PNO + if (bPlatform != 1) + return false; +#else + if (bPlatform != 0) + return false; +#endif +#endif +#endif + + // If not upwardly compatible with ship version and major version, delete the save game. + // This is so v1.0a allows reading save game files from v1.0, etc. + + int nShipVersionSaveGame; + int nMajorVersionSaveGame; + char chMinorVersionSaveGame; + ggame.ParseVersion(pszVersion, &nShipVersionSaveGame, &nMajorVersionSaveGame, &chMinorVersionSaveGame); + return ggame.IsVersionCompatibleWithExe(nShipVersionSaveGame, nMajorVersionSaveGame, chMinorVersionSaveGame, true); +} + +// +// LoadGameFrom +// + +LoadGameForm::LoadGameForm() +{ + m_nGameLast = -1; +} + +bool LoadGameForm::Init(FormMgr *pfrmm, IniReader *pini, word idf) +{ + if (!ShellForm::Init(pfrmm, pini, idf)) + return false; + + ListControl *plstc = (ListControl *)GetControlPtr(kidcGameList); + Rect rc; + plstc->GetSubRects(&rc); + int cEntries = rc.Height() / (gapfnt[kifntDefault]->GetHeight() + gcxyBorder); + Date dateLast; + dateLast.nYear = 0; + int nHours24Last = -1; + int nMinutesLast = -1; + int nSecondsLast = -1; + int nGameLast = -1; + int nGame; + for (nGame = 0; nGame < cEntries; nGame++) { + int nHours24; + int nMinutes; + int nSeconds; + char szLevel[64]; + char szT[64]; + Date date; + if (HostGetSaveGameName(nGame, szLevel, sizeof(szLevel), &date, &nHours24, &nMinutes, &nSeconds)) { + sprintf(szT, "%02d:%02d %s", nHours24, nMinutes, szLevel); + + // Remember last saved game + + bool fGreater = false; + switch (CompareDates(&dateLast, &date)) { + case 1: + fGreater = false; + break; + + case -1: + fGreater = true; + break; + + case 0: + if (nHours24 > nHours24Last) + fGreater = true; + if (nHours24 == nHours24Last && nMinutes > nMinutesLast) + fGreater = true; + if (nHours24 == nHours24Last && nMinutes == nMinutesLast && nSeconds >= nSecondsLast) + fGreater = true; + break; + } + if (fGreater) { + nGameLast = nGame; + dateLast = date; + nHours24Last = nHours24; + nMinutesLast = nMinutes; + nSecondsLast = nSeconds; + } + } else { + strcpy(szT, szLevel); + } + plstc->Add(szT, (void *)(nGame + 1)); + } + m_nGameLast = nGameLast; + + return true; +} + +void LoadGameForm::SelectLast(bool fLast) +{ + ListControl *plstc = (ListControl *)GetControlPtr(kidcGameList); + int nGameSelect; + if (fLast) { + nGameSelect = m_nGameLast; + if (nGameSelect == -1) + nGameSelect = 0; + } else { + nGameSelect = m_nGameLast + 1; + if (nGameSelect == plstc->GetCount()) + nGameSelect = 0; + } + plstc->Select(nGameSelect); +} + +void LoadGameForm::OnControlSelected(word idc) +{ + switch (idc) { + case kidcOk: + { + ListControl *plstc = (ListControl *)GetControlPtr(kidcGameList); + int nGame = ((int)plstc->GetSelectedItemData()) - 1; + EndForm(nGame); + } + return; + + case kidcCancel: + ShellForm::OnControlSelected(idc); + break; + } +} + +} // namespace wi \ No newline at end of file diff --git a/game/lobby.cpp b/game/lobby.cpp new file mode 100644 index 0000000..ef9c90e --- /dev/null +++ b/game/lobby.cpp @@ -0,0 +1,202 @@ +#include "game/lobby.h" +#include "game/loginform.h" +#include "game/lobbyform.h" +#include "game/roomform.h" +#include "game/httppackmanager.h" +#include "game/chooseserverform.h" + +namespace wi { + +Lobby::Lobby() { +} + +Lobby::~Lobby() { +} + +dword Lobby::Shell(const PackId *ppackidFind) { +#ifndef MULTIPLAYER + return knShellResultSuccess; +#endif + + // Connect to the service + std::string server_name; + dword result = Connect(&server_name); + if (result == knConnectResultAppStop) { + Disconnect(); + return knShellResultAppStop; + } + + if (result != knConnectResultSuccess) { + Disconnect(); + return knShellResultError; + } + + // Run the shell handler + result = ShellHandler(ppackidFind, server_name); + Disconnect(); + return result; +} + +dword Lobby::Connect(std::string *server_name) { + dword result = ChooseServerForm::DoForm(&gptra, server_name); + if (result == knChooseServerResultAppStop) { + return knConnectResultAppStop; + } + if (gptra == NULL) { + return knConnectResultError; + } + return knConnectResultSuccess; +} + +void Lobby::Disconnect() { + if (gptra != NULL) { + TransportWaitingUI twui(knWaitStrClosingTransport); + gptra->Close(); + delete gptra; + gptra = NULL; + } +} + +dword Lobby::ShellHandler(const PackId *ppackidFind, + const std::string& server_name) { + LoginHandler handler; + + bool fAutoLogin = true; + while (true) { + // If the app is exiting, return + if (gevm.IsAppStopping()) { + return knShellResultSuccess; + } + + // First, login if not already logged in + while (!handler.loggedin()) { + if (!LoginForm::DoForm(handler, fAutoLogin)) { + return knShellResultSuccess; + } + fAutoLogin = true; + } + + // The user has logged in. Now enter the lobby. + RoomInfo roominfo; + dword result = LobbyForm::DoForm(handler, server_name, &roominfo); + + // If logging off, logoff and go back to the login form + if (result == knLobbyResultSignOut) { + result = handler.SignOut(); + if (result == knSignOutResultFail) { + return knShellResultError; + } + fAutoLogin = false; + continue; + } + + if (result == knLobbyResultDone) { + return knShellResultSuccess; + } + + // If not entering a room go back to the main menu + if (result != knLobbyResultEnterRoom) { + return knShellResultError; + } + + // Entering room + Chatter chatter(handler); + while (true) { + // If the app is exiting, return + if (gevm.IsAppStopping()) { + return knShellResultSuccess; + } + + // Enter room. + GameInfo gameinfo; + result = RoomForm::DoForm(handler, roominfo, chatter, &gameinfo); + + // Go back to lobby + if (result == knRoomResultDone || result == knRoomResultFail) { + break; + } + + // Just go back to the room if it's not one of these cases + if (result != knRoomResultJoin && result != knRoomResultCreated) { + continue; + } + + // Show the GameStart form. + bool creator = (result == knRoomResultCreated); + result = GameForm::DoForm(handler, gameinfo, creator, chatter); + if (result == knGameStartResultDone) { + // Go back to the room + continue; + } + + // If not beginning a game, go back to the room. This case + // shouldn't happen. + if (result != knGameStartResultStart) { + // Go back to the room + continue; + } + + // Begin the game! After playing, return to the room. + result = BeginGame(gameinfo, creator, chatter); + + if (result == knBeginResultAppStop) { + return knShellResultAppStop; + } + + if (result == knBeginResultTransportClosed) { + return knShellResultSuccess; + } + + if (result == knBeginResultSuccess) { + // TODO: Here is a good place to post game results back to the + // www server. Use a LoginHandler method. + } + } + } +} + +dword Lobby::BeginGame(const GameInfo& info, bool creator, Chatter& chatter) { + // Force certain game options that need to be the same across clients + word wfPerfOptionsSave = gwfPerfOptions; + gwfPerfOptions = kfPerfMax; + long tGameSpeedSave = gtGameSpeed; + gtGameSpeed = info.params.tGameSpeed; + + // Play the game! + word wfRole = kfRoleMultiplayer; + if (creator) { + wfRole |= kfRoleCreator; + } + + // The player has already joined the game, and it is starting. + gppackm->Mount(gpakr, &info.params.packid); + int nRet = ggame.RunSimulation(NULL, (char *)info.params.szLvlFilename, + wfRole, info.gameid, &chatter); + gppackm->Unmount(gpakr, &info.params.packid); + + // Leave the game + if (gptra != NULL) { + gptra->LeaveGame(); + } + + // Set these options back + gwfPerfOptions = wfPerfOptionsSave; + gtGameSpeed = tGameSpeedSave; + + if (nRet == knGoAppStop) { + return knBeginResultAppStop; + } + + if (gptra != NULL && gptra->IsClosed()) { + return knBeginResultTransportClosed; + } + + if (nRet == knGoInitFailure) { + HtMessageBox(kfMbWhiteBorder, "Launch Error", "Unable to initialize the game!"); + return knBeginResultError; + } + + return knBeginResultSuccess; +} + +} // namespace wi diff --git a/game/lobby.h b/game/lobby.h new file mode 100644 index 0000000..00ebf11 --- /dev/null +++ b/game/lobby.h @@ -0,0 +1,60 @@ +#ifndef __LOBBY__ +#define __LOBBY__ + +#include "game/ht.h" +#include "game/loginhandler.h" +#include "game/gameform.h" +#include + +namespace wi { + +const dword knConnectResultSuccess = 0; +const dword knConnectResultError = 1; +const dword knConnectResultAppStop = 2; + +STARTLABEL(ConnectResults) + LABEL(knConnectResultSuccess) + LABEL(knConnectResultError) + LABEL(knConnectResultAppStop) +ENDLABEL(ConnectResults) + +const dword knShellResultSuccess = 0; +const dword knShellResultError = 1; +const dword knShellResultAppStop = 2; + +STARTLABEL(ShellResults) + LABEL(knShellResultSuccess) + LABEL(knShellResultError) + LABEL(knShellResultAppStop) +ENDLABEL(ShellResults) + +const dword knBeginResultSuccess = 0; +const dword knBeginResultError = 1; +const dword knBeginResultAppStop = 2; +const dword knBeginResultTransportClosed = 3; + +STARTLABEL(BeginResults) + LABEL(knBeginResultSuccess) + LABEL(knBeginResultError) + LABEL(knBeginResultAppStop) + LABEL(knBeginResultTransportClosed) +ENDLABEL(BeginResults) + +class Lobby { +public: + Lobby(); + ~Lobby(); + + dword Shell(const PackId *ppackidFind); + +private: + dword Connect(std::string *server_name); + void Disconnect(); + dword ShellHandler(const PackId *ppackidFind, + const std::string& server_name); + dword BeginGame(const GameInfo& info, bool creator, Chatter& chatter); +}; + +} // namespace wi + +#endif // __LOBBY__ diff --git a/game/lobbyform.cpp b/game/lobbyform.cpp new file mode 100644 index 0000000..466ea6b --- /dev/null +++ b/game/lobbyform.cpp @@ -0,0 +1,383 @@ +#include "game/lobbyform.h" +#include "game/createroomform.h" +#include "game/roomform.h" +#include "server/room.h" + +namespace wi { + +LobbyForm::LobbyForm(LoginHandler& handler, const std::string& server_name) : + handler_(handler), server_name_(server_name), refresh_(true), + lurkers_(0), zipdone_(false), selected_main_(false) { +} + +LobbyForm::~LobbyForm() { +} + +void LobbyForm::ShowJoinMessage(dword result) { + // Show an appropriate message if it was not a success + const char *message = NULL; + switch (result) { + case knLobbyJoinResultSuccess: + break; + case knLobbyJoinResultNotLoggedIn: + message = "Not logged in"; + break; + case knLobbyJoinResultFull: + message = "Lobby is full, try again later."; + break; + case knLobbyJoinResultFail: + default: + message = "Failure entering lobby"; + break; + } + + if (message != NULL) { + HtMessageBox(kfMbWhiteBorder, "Enter Lobby Problem", message); + } +} + +dword LobbyForm::DoForm(LoginHandler &handler, const std::string& server_name, + RoomInfo *joininfo) { + // The user is logged in by the time this gets called. + LobbyForm *pfrm = (LobbyForm *)gpmfrmm->LoadForm(gpiniForms, kidfLobby, + new LobbyForm(handler, server_name)); + if (pfrm == NULL) { + return false; + } + int result = 0; + pfrm->DoModal(&result); + *joininfo = pfrm->joininfo(); + delete pfrm; + + if (result == kidcSignOut) { + return knLobbyResultSignOut; + } + if (result == kidcCancel) { + return knLobbyResultDone; + } + if (result == kidcJoinRoom) { + return knLobbyResultEnterRoom; + } + return knLobbyResultDone; +} + +bool LobbyForm::DoModal(int *presult, Sfx sfxShow, Sfx sfxHide) { + char name[kcbPlayerName*2]; + handler_.GetPlayerName(name, sizeof(name)); + LabelControl *plbl = (LabelControl *)GetControlPtr(kidcPlayerName); + plbl->Show(false); + plbl = (LabelControl *)GetControlPtr(kidcPlayerNameLabel); + const char *s = base::Format::ToString("%s / %s", name, + server_name_.c_str()); + plbl->SetText(s); + + ListControl *plstc = (ListControl *)GetControlPtr(kidcRoomList); + Rect rcList; + plstc->GetRect(&rcList); + Font *pfnt = plstc->GetFont(); + int cxPrivate = pfnt->GetTextExtent("PRIVATE"); + int cxStart = rcList.Width() / 20; // 10; + plstc->SetTabStops(0, cxStart, rcList.Width() - cxStart - cxPrivate); + plstc->SetTabFlags(0, kfLstTabEllipsis, 0); + GetControlPtr(kidcJoinRoom)->Show(false); + + Show(true); + gptra->SetCallback(this); + dword result = gptra->JoinLobby(this); + if (result != knLobbyJoinResultSuccess) { + gptra->SetCallback(NULL); + ShowJoinMessage(result); + *presult = 0; + return false; + } + bool success = ShellForm::DoModal(presult, sfxShow, sfxHide); + gptra->LeaveLobby(); + gptra->SetCallback(NULL); + + return success; +} + +void LobbyForm::OnControlSelected(word idc) { + switch (idc) { + case kidcNewRoom: + OnCreateRoom(); + break; + + case kidcJoinRoom: + OnJoinRoom(); + break; + + case kidcCancel: + case kidcSignOut: + EndForm(idc); + break; + } +} + +void LobbyForm::OnControlNotify(word idc, int nNotify) { + if (idc == kidcRoomList && nNotify == knNotifySelectionChange) { + Refresh(0); + } + Form::OnControlNotify(idc, nNotify); +} + +void LobbyForm::OnJoinRoom() { + ListControl *plstc = (ListControl *)GetControlPtr(kidcRoomList); + dword roomid = (dword)plstc->GetSelectedItemData(); + RoomMap::iterator it = map_.find(roomid); + if (it == map_.end()) { + return; + } + if (it->second.priv) { + HostInitiateAsk("Enter Password:", kcbPassword - 1, + it->second.password.c_str(), knKeyboardAskDefault, + true); + return; + } + InitiateJoinRoom(it->second); +} + +void LobbyForm::OnCreateRoom() { + char roomname[kcbRoomname]; + char password[kcbPassword]; + if (!CreateRoomForm::DoForm(roomname, sizeof(roomname), password, + sizeof(password))) { + return; + } + + if (gptra == NULL) { + return; + } + + dword roomid; + dword result = gptra->CreateRoom(roomname, password, &roomid); + switch (result) { + case knLobbyCreateRoomResultSuccess: + { + // Update the list and select the created room, in case + // the join fails. + Refresh(-1); + ListControl *plstc = (ListControl *)GetControlPtr(kidcRoomList); + refresh_ = false; + plstc->Select(FindIndex(roomid), true, true); + refresh_ = true; + + // OnAddRoom has been called already. Shove the password + // into the info and join the room. + RoomMap::iterator it = map_.find(roomid); + if (it != map_.end()) { + it->second.password = password; + InitiateJoinRoom(it->second); + } + } + break; + + case knLobbyCreateRoomResultFail: + HtMessageBox(kfMbWhiteBorder, "Create Room", + "Error creating this room."); + break; + + case knLobbyCreateRoomResultFull: + HtMessageBox(kfMbWhiteBorder, "Create Room", "Too many rooms. Join an existing room"); + break; + + case knLobbyCreateRoomResultExists: + HtMessageBox(kfMbWhiteBorder, "Create Room", + "A room with this name already exists."); + break; + + } +} + +int LobbyForm::FindIndex(dword roomid) { + int index = 0; + RoomMap::iterator it = map_.begin(); + for (; it != map_.end(); it++) { + if (it->first == roomid) { + return index; + } + index++; + } + return -1; +} + +void LobbyForm::InitiateJoinRoom(const RoomInfo& info) { + if (gptra == NULL) { + return; + } + + // Before joining, check to see if this room is joinable. + // Actually joining also checks, but this is a way to provide the user + // with feedback before exiting this form (actual joining happens + // in RoomForm). + dword result = gptra->CanJoinRoom(info.roomid, info.password.c_str()); + RoomForm::ShowJoinMessage(result); + switch (result) { + case knRoomJoinResultWrongPassword: + OnJoinRoom(); + break; + + case knRoomJoinResultSuccess: + joininfo_ = info; + EndForm(kidcJoinRoom); + break; + } +} + +void LobbyForm::Refresh(int ct) { + if (!refresh_) { + return; + } + if (ct == -1) { + timer_.Stop(); + OnTimeout(0); + return; + } + if (ct == 0) { + timer_.Stop(); + } + if (timer_.IsStarted()) { + return; + } + timer_.Start(this, ct * 10); +} + +void LobbyForm::OnTimeout(int id) { + refresh_ = false; + ListControl *plstc = (ListControl *)GetControlPtr(kidcRoomList); + dword roomid = (dword)plstc->GetSelectedItemData(); + if (!selected_main_) { + roomid = kroomidMain; + } + plstc->Clear(); + RoomMap::iterator it = map_.begin(); + for (; it != map_.end(); it++) { + RoomInfo& info = it->second; + const char *s; + if (info.priv) { + s = base::Format::ToString("\t%s (%d %s, %d %s)\tPRIVATE", + info.name.c_str(), info.cPlayers, + info.cPlayers == 1 ? "player" : "players", + info.cGames, + info.cGames == 1 ? "game" : "games"); + } else { + s = base::Format::ToString("\t%s (%d %s, %d %s)", + info.name.c_str(), info.cPlayers, + info.cPlayers == 1 ? "player" : "players", + info.cGames, + info.cGames == 1 ? "game" : "games"); + } + plstc->Add(s, (void *)info.roomid); + } + int selected = FindIndex(roomid); + if (selected < 0 && map_.size() != 0) { + plstc->Select(0, true, true); + } else { + plstc->Select(selected, true); + selected_main_ = true; + } + Control *pctl = GetControlPtr(kidcJoinRoom); + refresh_ = true; + pctl->Show(plstc->GetSelectedItemIndex() >= 0); +} + +void LobbyForm::OnLurkerCount(dword count) { + lurkers_ = count; + if (zipdone_) { + LabelControl *plbl = (LabelControl *)GetControlPtr(kidcLurkerCount); + plbl->SetText(base::Format::ToString("Lurkers: %d", count)); + } +} + +void LobbyForm::OnZipDone() { + zipdone_ = true; + OnLurkerCount(lurkers_); +} + +void LobbyForm::OnAddRoom(const char *name, dword roomid, bool priv, + dword cPlayers, dword cGames) { + RoomInfo info; + info.name = name; + info.roomid = roomid; + info.priv = priv; + info.cPlayers = cPlayers; + info.cGames = cGames; + map_.insert(RoomMap::value_type(roomid, info)); + Refresh(); +} + +void LobbyForm::OnRemoveRoom(dword roomid) { + RoomMap::iterator it = map_.find(roomid); + if (it == map_.end()) { + LOG() << "Couldn't find room!"; + return; + } + map_.erase(it); + Refresh(); +} + +void LobbyForm::OnUpdateRoom(dword roomid, dword cPlayers, dword cGames) { + RoomMap::iterator it = map_.find(roomid); + if (it == map_.end()) { + LOG() << "Couldn't find room!"; + return; + } + it->second.cPlayers = cPlayers; + it->second.cGames = cGames; + Refresh(); +} + +void LobbyForm::OnStatusUpdate(char *pszStatus) { +} + +void LobbyForm::OnConnectionClose() { + Event evt; + memset(&evt, 0, sizeof(evt)); + evt.idf = m_idf; + evt.eType = connectionCloseEvent; + gevm.PostEvent(&evt); +} + +void LobbyForm::OnShowMessage(const char *message) { + message_ = message; + Event evt; + memset(&evt, 0, sizeof(evt)); + evt.idf = m_idf; + evt.eType = showMessageEvent; + gevm.PostEvent(&evt); +} + +bool LobbyForm::OnFilterEvent(Event *pevt) { + if (pevt->eType == askStringEvent) { + if (gptra == NULL) { + return true; + } + + char s[512]; + HostGetAskString(s, sizeof(s)); + + ListControl *plstc = (ListControl *)GetControlPtr(kidcRoomList); + dword roomid = (dword)plstc->GetSelectedItemData(); + RoomMap::iterator it = map_.find(roomid); + if (it != map_.end()) { + it->second.password = s; + InitiateJoinRoom(it->second); + } + return true; + } + + if (pevt->eType == connectionCloseEvent) { + HtMessageBox(kfMbWhiteBorder, "Comm Problem", "The server has closed your connection."); + EndForm(kidcCancel); + return true; + } + + if (pevt->eType == showMessageEvent) { + HtMessageBox(kfMbWhiteBorder, "Server Message", message_.c_str()); + message_ = ""; + return true; + } + return false; +} + +} // namespace wi diff --git a/game/lobbyform.h b/game/lobbyform.h new file mode 100644 index 0000000..2d45690 --- /dev/null +++ b/game/lobbyform.h @@ -0,0 +1,95 @@ +#ifndef __LOBBYFORM_H__ +#define __LOBBYFORM_H__ + +#include "game/ht.h" +#include "game/loginform.h" +#include "game/loginhandler.h" +#include +#include + +namespace wi { + +const dword knLobbyResultSignOut = 0; +const dword knLobbyResultEnterRoom = 1; +const dword knLobbyResultDone = 2; + +STARTLABEL(LobbyResults) + LABEL(knLobbyResultSignOut) + LABEL(knLobbyResultEnterRoom) + LABEL(knLobbyResultDone) +ENDLABEL(LobbyResults) + +struct RoomSorter { + bool operator()(dword a, dword b) const { + return a > b; + } +}; + +struct RoomInfo { + std::string name; + std::string password; + dword roomid; + bool priv; + dword cPlayers; + dword cGames; +}; +typedef std::map RoomMap; + +class LobbyForm : public ShellForm, ILobbyCallback, ITransportCallback, + ITimeout { +public: + LobbyForm(LoginHandler& handler, const std::string& server_name); + ~LobbyForm(); + + static void ShowJoinMessage(dword result); + static dword DoForm(LoginHandler& handler, const std::string& server_name, + RoomInfo *joininfo); + const RoomInfo& joininfo() { return joininfo_; } + + // Form overrides + virtual bool DoModal(int *pnResult = NULL, Sfx sfxShow = ksfxGuiFormShow, + Sfx sfxHide = ksfxGuiFormHide); + virtual void OnControlSelected(word idc); + virtual void OnControlNotify(word idc, int nNotify); + virtual bool OnFilterEvent(Event *pevt); + + // ShellForm overrides + virtual void OnZipDone(); + +private: + void Refresh(int ct = 20); + void OnCreateRoom(); + void OnJoinRoom(); + void InitiateJoinRoom(const RoomInfo& info); + int FindIndex(dword roomid); + + // ILobbyCallback + virtual void OnLurkerCount(dword count); + virtual void OnAddRoom(const char *name, dword roomid, bool priv, + dword cPlayers, dword cGames); + virtual void OnRemoveRoom(dword roomid); + virtual void OnUpdateRoom(dword roomid, dword cPlayers, dword cGames); + + // ITransportCallback + virtual void OnStatusUpdate(char *pszStatus); + virtual void OnConnectionClose(); + virtual void OnShowMessage(const char *message); + + // ITimeout + virtual void OnTimeout(int id); + + dword lurkers_; + TimeoutTimer timer_; + LoginHandler& handler_; + RoomMap map_; + RoomInfo joininfo_; + bool refresh_; + bool zipdone_; + bool selected_main_; + std::string message_; + std::string server_name_; +}; + +} + +#endif // __LOBBYFORM_H__ diff --git a/game/loginform.cpp b/game/loginform.cpp new file mode 100644 index 0000000..e04d738 --- /dev/null +++ b/game/loginform.cpp @@ -0,0 +1,280 @@ +#include "game/loginform.h" +#include "game/serviceurls.h" + +namespace wi { + +LoginForm::LoginForm(LoginHandler& handler) : handler_(handler), + ask_(knLoginAskNone) { +} + +LoginForm::~LoginForm() { +} + +bool LoginForm::DoForm(LoginHandler& handler, bool fAutoLogin) { + LoginForm *pfrm = (LoginForm *)gpmfrmm->LoadForm(gpiniForms, kidfLogin, + new LoginForm(handler)); + if (pfrm == NULL) { + return false; + } + + int result = 0; + pfrm->DoModal(&result, fAutoLogin); + delete pfrm; + return result == kidcLogin; +} + +bool LoginForm::DoModal(int *pnResult, bool fAutoLogin) { + // Reflect the state of LoginHandler in the UI + ReflectHandlerState(); + + // If enough credentials to attempt login, then do it and return + // if successful. Always show if anonymous login + if (fAutoLogin) { + if (!handler_.anonymous() && handler_.ShouldAttemptLogin()) { + if (AttemptLogin()) { + *pnResult = kidcLogin; + return true; + } + } + } + + // Show the form + return ShellForm::DoModal(pnResult); +} + +void LoginForm::ReflectHandlerState() { + // Initialize controls + EditControl *pedc = (EditControl *)GetControlPtr(kidcPlayerName); + char name[kcbPlayerName*2]; + handler_.GetPlayerName(name, sizeof(name)); + pedc->SetText(name); + pedc = (EditControl *)GetControlPtr(kidcPassword); + char password[kcbPassword]; + strncpyz(password, handler_.password(), sizeof(password)); + for (int i = 0; i < sizeof(password); i++) { + if (password[i] == 0) { + break; + } + password[i] = '*'; + } + pedc->SetText(password); + CheckBoxControl *pcbc = (CheckBoxControl *)GetControlPtr(kidcAnonymous); + pcbc->SetChecked(handler_.anonymous()); + + // Hide the login button until it is deemed ok to attempt + // login + ButtonControl *pbtn = (ButtonControl *)GetControlPtr(kidcLogin); + pbtn->Show(handler_.ShouldAttemptLogin()); + + // Hide input panel and password controls if login anonymously selected + bool fShow = !handler_.anonymous(); + Control *pctl = GetControlPtr(kidcPlayerNamePanel); + pctl->Show(fShow); + pctl = GetControlPtr(kidcPasswordLabel); + pctl->Show(fShow); + pctl = GetControlPtr(kidcPassword); + pctl->Show(fShow); + pctl = GetControlPtr(kidcPasswordPanel); + pctl->Show(fShow); + pctl = GetControlPtr(kidcForums); + pctl->Show(fShow); + + // Arrange buttons + word aid0[] = { kidcLogin, kidcRegister, kidcUpdateAccount, + kidcCancel, 0 }; + word aid1[] = { kidcLogin, kidcCancel, 0 }; + + word *aidShow; + word *aidHide; + if (handler_.anonymous()) { + aidShow = aid1; + aidHide = aid0; + } else { + aidShow = aid0; + aidHide = aid1; + } + + // Hide first + for (int i = 0; aidHide[i] != 0; i++) { + pctl = GetControlPtr(aidHide[i]); + pctl->Show(false); + } + + // Calc width of visible buttons + int cx = 0; + for (int i = 0; aidShow[i] != 0; i++) { + pctl = GetControlPtr(aidShow[i]); + Rect rc; + pctl->GetRect(&rc); + cx += rc.Width(); + if (aidShow[i + 1] != 0) { + cx += 10; + } + } + + // Lay them out + int x = (m_rc.Width() - cx) / 2; + for (int i = 0; aidShow[i] != 0; i++) { + pctl = GetControlPtr(aidShow[i]); + Rect rc; + pctl->GetRect(&rc); + pctl->SetPosition(x, rc.top); + x += rc.Width() + 10; + pctl->Show(true); + } +} + +void LoginForm::OnControlSelected(word idc) { + switch (idc) { + case kidcAnonymous: + { + CheckBoxControl *pcbc = (CheckBoxControl *) + GetControlPtr(kidcAnonymous); + handler_.SetAnonymous(pcbc->IsChecked()); + ReflectHandlerState(); + } + break; + + case kidcLogin: + AttemptLogin(); + if (handler_.loggedin()) { + EndForm(kidcLogin); + } + break; + + case kidcPlayerNamePanel: + case kidcPlayerName: + // This brings up native UI asking for the player's name. + // This UI is modeless and executes on the main thread. This + // form will get notified when this native UI goes away, at + // which point it can get the string. + if (!handler_.anonymous()) { + ask_ = knLoginAskPlayerName; + HostInitiateAsk("Enter Player Name", kcbPlayerName - 1, + handler_.username()); + } + break; + + case kidcPasswordPanel: + case kidcPassword: + if (!handler_.anonymous()) { + ask_ = knLoginAskPassword; + HostInitiateAsk("Enter Password", kcbPassword - 1, + handler_.password(), knKeyboardAskDefault, + true); + } + break; + + case kidcRegister: + HostInitiateWebView("", kszRegisterUrl); + //HostOpenUrl(kszRegisterUrl); + break; + + case kidcUpdateAccount: + HostInitiateWebView("", kszUpdateAccountUrl); + //HostOpenUrl(kszUpdateAccountUrl); + break; + + case kidcCancel: + EndForm(kidcCancel); + break; + } +} + +bool LoginForm::AttemptLogin() { + // Want the form to be visible when doing this since Login is + // synchronous. + Show(true); + + // Attempt to login. Note WaitingUI forces a synchronous repaint + dword result; + { + TransportWaitingUI twui("Logging in..."); + result = handler_.Login(); + } + + // Show messages here as necessary + switch (result) { + case knLoginResultSuccess: + case knLoginResultAnonymousSuccess: + return true; + + case knLoginResultNoPassword: + HtMessageBox(kfMbWhiteBorder, "Login Failure", + "The password field is empty. Please enter your password and try again."); + return false; + + case knLoginResultAuthDown: + HtMessageBox(kfMbWhiteBorder, "Login Failure", + "The player authentication server is not responding. Please try to login as an anonymous player."); + return false; + + case knLoginResultFail: + default: + if (!handler_.anonymous()) { + HtMessageBox(kfMbWhiteBorder, "Login Failure", + "Your login attempt failed. Please check your username and password, or log in anonymously."); + } else { + HtMessageBox(kfMbWhiteBorder, "Login Failure", + "An unknown error occured while logging in. Please try again."); + } + return false; + } +} + +void LoginForm::OnStatusUpdate(char *pszStatus) { +} + +void LoginForm::OnConnectionClose() { + Event evt; + memset(&evt, 0, sizeof(evt)); + evt.idf = m_idf; + evt.eType = connectionCloseEvent; + gevm.PostEvent(&evt); +} + +void LoginForm::OnShowMessage(const char *message) { + message_ = message; + Event evt; + memset(&evt, 0, sizeof(evt)); + evt.idf = m_idf; + evt.eType = showMessageEvent; + gevm.PostEvent(&evt); +} + +bool LoginForm::OnFilterEvent(Event *pevt) { + if (pevt->eType == askStringEvent) { + char s[512]; + HostGetAskString(s, sizeof(s)); + + switch (ask_) { + case knLoginAskPlayerName: + handler_.SetUsername(s); + ReflectHandlerState(); + break; + + case knLoginAskPassword: + handler_.SetPassword(s); + ReflectHandlerState(); + break; + } + return true; + } + + if (pevt->eType == connectionCloseEvent) { + HtMessageBox(kfMbWhiteBorder, "Comm Problem", "The server has closed your connection."); + EndForm(kidcCancel); + return true; + } + + if (pevt->eType == showMessageEvent) { + HtMessageBox(kfMbWhiteBorder, "Server Message", message_.c_str()); + message_ = ""; + return true; + } + + return false; +} + + +} // namespace wi diff --git a/game/loginform.h b/game/loginform.h new file mode 100644 index 0000000..22d81f7 --- /dev/null +++ b/game/loginform.h @@ -0,0 +1,43 @@ +#ifndef __LOGINFORM_H__ +#define __LOGINFORM_H__ + +#include "game/ht.h" +#include "game/loginhandler.h" +#include "mpshared/messages.h" +#include + +namespace wi { + +const int knLoginAskNone = 0; +const int knLoginAskPlayerName = 1; +const int knLoginAskPassword = 2; + +class LoginForm : public ShellForm, ITransportCallback { +public: + LoginForm(LoginHandler& handler); + ~LoginForm(); + + static bool DoForm(LoginHandler& handler, bool fAutoLogin); + bool DoModal(int *pnResult, bool fAutoLogin); + + // Form callbacks + virtual void OnControlSelected(word idc); + virtual bool OnFilterEvent(Event *pevt); + +private: + void ReflectHandlerState(); + bool AttemptLogin(); + + // ITransportCallback + virtual void OnStatusUpdate(char *pszStatus); + virtual void OnConnectionClose(); + virtual void OnShowMessage(const char *message); + + LoginHandler& handler_; + int ask_; + std::string message_; +}; + +} // namespace wi + +#endif // __LOGINFORM_H__ diff --git a/game/loginhandler.cpp b/game/loginhandler.cpp new file mode 100644 index 0000000..545dbcb --- /dev/null +++ b/game/loginhandler.cpp @@ -0,0 +1,232 @@ +#include "game/loginhandler.h" +#include "game/simplerequest.h" +#include "game/serviceurls.h" +#include "game/httpservice.h" +#include "mpshared/messages.h" +#include "base/base64.h" +#include + +namespace wi { + +const dword knGetTokenResultSuccess = 0; +const dword knGetTokenResultNoPassword = 1; +const dword knGetTokenResultTokenInvalid = 2; +const dword knGetTokenResultCreateRequestError = 3; +const dword knGetTokenResultServiceUnavailable = 4; + +LoginHandler::LoginHandler() : loggedin_(false) { + InitFromGlobals(); +} + +LoginHandler::~LoginHandler() { +} + +void LoginHandler::InitFromGlobals() { + strncpyz(token_, gszToken, sizeof(token_)); + strncpyz(password_, gszPassword, sizeof(password_)); + strncpyz(username_, gszUsername, sizeof(username_)); + SetAnonymous(gfAnonymous); +} + +void LoginHandler::SaveToGlobals() { + strncpyz(gszToken, token_, sizeof(gszToken)); + strncpyz(gszPassword, password_, sizeof(gszPassword)); + strncpyz(gszUsername, username_, sizeof(gszUsername)); + gfAnonymous = anonymous_; + ggame.SavePreferences(); +} + +void LoginHandler::SetAnonymous(bool anonymous) { + anonymous_ = anonymous; +} + +void LoginHandler::SetUsername(const char *username) { + strncpyz(username_, username, sizeof(username_)); +} + +void LoginHandler::SetPassword(const char *password) { + strncpyz(password_, password, sizeof(password_)); +} + +void LoginHandler::GetPlayerName(char *psz, int cb) { + if (anonymous_) { + if (gptra != NULL) { + strncpyz(psz, gptra->GetAnonymousUsername(), cb); + } else { + strncpyz(psz, "anonymous", cb); + } + } else { + strncpyz(psz, username_, cb); + } +} + +dword LoginHandler::Login() { + if (gptra == NULL) { + return knLoginResultFail; + } + + if (!anonymous_ && token_[0] == 0) { + switch (GetToken()) { + case knGetTokenResultSuccess: + break; + case knGetTokenResultNoPassword: + return knLoginResultNoPassword; + case knGetTokenResultTokenInvalid: + case knGetTokenResultCreateRequestError: + return knLoginResultFail; + case knGetTokenResultServiceUnavailable: + return knLoginResultAuthDown; + } + } + + dword result = gptra->Login(username_, token_); + + // If successful, return and keep the token + if (result == knLoginResultSuccess || + result == knLoginResultAnonymousSuccess) { + loggedin_ = true; + SaveToGlobals(); + return result; + } + + // Otherwise don't save the token + token_[0] = 0; + + // If token stale, try again otherwise return + if (result != knLoginResultStaleToken) { + return result; + } + + // Get a new token + if (!GetToken()) { + switch (GetToken()) { + case knGetTokenResultSuccess: + break; + case knGetTokenResultNoPassword: + return knLoginResultNoPassword; + case knGetTokenResultTokenInvalid: + case knGetTokenResultCreateRequestError: + return knLoginResultFail; + case knGetTokenResultServiceUnavailable: + return knLoginResultAuthDown; + } + } + + result = gptra->Login(username_, token_); + + // If successful, return and keep the token + if (result == knLoginResultSuccess || + result == knLoginResultAnonymousSuccess) { + loggedin_ = true; + SaveToGlobals(); + return result; + } + + // Otherwise don't save the token + token_[0] = 0; + + // If this token is stale for some reason, there is a bug. + // Convert into "fail". + if (result == knLoginResultStaleToken) { + return knLoginResultFail; + } + + return result; +} + +dword LoginHandler::SignOut() { + loggedin_ = false; + token_[0] = 0; + + if (!anonymous_) { + // Sign out user - erase password, keep username + password_[0] = 0; + } + SaveToGlobals(); + if (gptra == NULL) { + return knSignOutResultFail; + } + return gptra->SignOut(); +} + +dword LoginHandler::GetToken() { + // Wipe out the current token, if there is one + token_[0] = 0; + + // Don't send a request for a token if the user has no password + if (password_[0] == 0) { + return knGetTokenResultNoPassword; + } + + // Ask the authentication server for an auth token. Use https + // so ok to send password cleartext. Encode base64 to avoid + // url encoding parameters. + + const char *s = base::Format::ToString("%s#%s", username_, password_); + char input[sizeof(username_) + sizeof(password_)]; + strncpyz(input, s, sizeof(input)); + int cchInput = strlen(input); + input[strlen(username_)] = 0; + char output[(sizeof(username_) + sizeof(password_)) * 2]; + int cb = base::base64encode((const byte *)input, cchInput, (byte *)output, + sizeof(output)); + if (cb == -1) { + return knGetTokenResultCreateRequestError; + } + output[cb] = 0; + + // Submit a simple blocking request to get the token + + SimpleRequest req(gphttp); + const char *url = base::Format::ToString("%s?a=%s&d=%s", kszAuthUrl, output, gszDeviceId); + req.SetTimeout(60); + + char result[kcbTokenMax]; + if (!req.Get(url, result, sizeof(result))) { + return knGetTokenResultServiceUnavailable; + } + int cchResult = strlen(result); + + // Strip whitespace from the start and end. + char *start = result; + for (; start < &result[cchResult]; start++) { + if (!isspace(*start)) { + break; + } + } + char *end = &result[cchResult - 1]; + for (; end > start; end--) { + if (!isspace(*end)) { + end++; + break; + } + } + + if (end <= start) { + return knGetTokenResultTokenInvalid; + } + strncpyz(token_, start, end - start + 1); + return knGetTokenResultSuccess; +} + +bool LoginHandler::ShouldAttemptLogin() { + if (anonymous_) { + return true; + } + return username_[0] != 0 && password_[0] != 0; +} + +const char *LoginHandler::StatsUsername() { + // Return the username to use for stats. Only return username if: + // 1. The user is not anonymous + // 2. It has been used to login successfully before + if (anonymous_) { + return ""; + } + if (token_[0] == 0) { + return ""; + } + return username_; +} + +} // namespace wi diff --git a/game/loginhandler.h b/game/loginhandler.h new file mode 100644 index 0000000..209f60a --- /dev/null +++ b/game/loginhandler.h @@ -0,0 +1,41 @@ +#ifndef __LOGINHANDLER_H__ +#define __LOGINHANDLER_H__ + +#include "game/ht.h" + +namespace wi { + +class LoginHandler { +public: + LoginHandler(); + ~LoginHandler(); + + dword Login(); + dword SignOut(); + + void SetUsername(const char *username); + const char *username() { return username_; } + void SetPassword(const char *password); + const char *password() { return password_; } + void SetAnonymous(bool anonymous); + bool anonymous() { return anonymous_; } + bool loggedin() { return loggedin_; } + bool ShouldAttemptLogin(); + void GetPlayerName(char *psz, int cb); + void SaveToGlobals(); + const char *StatsUsername(); + +private: + void InitFromGlobals(); + dword GetToken(); + + char username_[kcbPlayerName]; + char password_[kcbPlayerName]; + char token_[kcbTokenMax]; + bool anonymous_; + bool loggedin_; +}; + +} + +#endif // __LOGINHANDLER_H__ diff --git a/game/makefile b/game/makefile new file mode 100644 index 0000000..77d1b5c --- /dev/null +++ b/game/makefile @@ -0,0 +1,148 @@ +OUTDIR= +PRC=Warfare.prc +CC = m68k-palmos-gcc +CPP_DEFINES_GLOBAL=-fcheck-new -D__CPU_68K +CPP_INCS=-I ../inc -I palm +TOOLDIR=../bin +TARGETDEFINED=0 +PRC_VERSION=12 +BUILD-PRC-FLAGS= + +ifdef MAP +MAPFLAGS=-Xlinker -Map -Xlinker ht.map +endif + +# Debug is default + +ifdef REL +CPP_DEBUG= +else +CPP_DEBUG=-DDEBUG -g +BUILD-PRC-FLAGS= +endif + +ifdef ESD_BUILD +ifdef MERGE +BUILD-PRC-FLAGS=--backup --copy-prevention --version-number $(PRC_VERSION) +else +BUILD-PRC-FLAGS=--backup --version-number $(PRC_VERSION) +endif +CPP_DEFINES=$(CPP_DEFINES_GLOBAL) -DESD_BUILD +ifdef REL +OUTDIR=Palm_ESD_Release +else +OUTDIR=Palm_ESD_Debug +endif +TARGETDEFINED=1 +endif + +ifdef RETAIL_BUILD +BUILD-PRC-FLAGS=--backup --copy-prevention --version-number $(PRC_VERSION) +CPP_DEFINES=$(CPP_DEFINES_GLOBAL) -DRETAIL_BUILD +ifdef REL +OUTDIR=Palm_Retail_Release +else +OUTDIR=Palm_Retail_Debug +endif +TARGETDEFINED=1 +endif + +ifdef PROFILE +CPP_DEFINES=$(CPP_DEFINES_GLOBAL) -DDEV_BUILD +CPP_DEBUG=-mdebug-labels +OUTDIR=Palm_Profile +TARGETDEFINED=1 +endif + +# DEV_BUILD is default + +ifeq ($(TARGETDEFINED),0) +DEV_BUILD=1 +endif + +ifdef DEV_BUILD +CPP_DEFINES=$(CPP_DEFINES_GLOBAL) -DDEV_BUILD +ifdef REL +OUTDIR=Palm_Release +else +OUTDIR=Palm_Debug +endif +TARGETDEFINED=1 +endif + +# Composite the flags together + +CFLAGS=-fvtable-thunks -Os -DPIL $(CPP_DEFINES) $(CPP_DEBUG) $(CPP_INCS) -fno-rtti -fno-exceptions +CFLAGSARM=-Os -DPIL $(CPP_DEFINES) $(CPP_INCS) -fno-rtti -fno-exceptions + +OBJS=game.o main.o host.o misc.o bitmap.o packfile.o rip.o form.o ini.o \ +misccontrols.o tests.o timer.o event.o font.o ht-sections.o 68k.o display.o mqdisplay.o \ +GameObjects.o Level.o Simulation.o tilemap.o StateMachine.o Animation.o \ +fogmap.o SRInfantry.o LRInfantry.o terrainmap.o HRC.o \ +Reactor.o Processor.o Miner.o Headquarters.o Research.o Radar.o VTS.o \ +MobileBuilder.o Tank.o SpInfantry.o Overmind.o Builder.o MobileUnit.o \ +memmgr.o tbitmap.o tbltcode.o compression.o decompress.o \ +cachemgr.o copybits.o Multiplayer.o comm.o Player.o \ +TransportMgr.o Warehouse.o Tower.o MobileHQ.o Unit.o Struct.o SimUI.o \ +pwm2.o soundmgr.o pwmsounddev.o sounddev.o pa1lib.o pa1sounddev.o pa1encode.o \ +os5sounddev.o updatemap.o mapdraw824.o mapdraw816.o updatescreen824.o updatescreen816.o blt.o \ +GameOptions.o InputUI.o formmgr.o updatescreen416.o fill.o thunks.o armthunks.o loadsave.o savegame.o \ +triggermgr.o TriggerConditions.o TriggerActions.o UnitGroupMgr.o BuildMgr.o \ +RawBitmap.o Shell.o Ecom.o alertcontrol.o stringtable.o selection.o Andy.o \ +Artillery.o Replicator.o Help.o CutScene.o drm.o PalmPins.o twdisplay.o + +all: + make $(OUTDIR)/$(PRC) + +clean: + rm -f $(OUTDIR)/* + +$(OUTDIR)/$(PRC): $(OUTDIR)/ht palm/ht.def palm/ht.rcp $(OUTDIR)/tAIB03e8.bin $(OUTDIR)/armcode + cp -f palm/ht.def $(OUTDIR)/ht.def + cd $(OUTDIR) && build-prc $(BUILD-PRC-FLAGS) -o $(PRC) ht.def ht armcode *.bin && cp $(PRC) "Warfare Incorporated.prc" && cd .. +ifdef MERGE + cd $(OUTDIR) && ../$(TOOLDIR)/packpdb2 -m foo.prc $(PRC) ..\\$(MERGE) ..\\htsfx.pdb && mv foo.prc "Warfare Incorporated_merged.prc" && cd .. +endif + +$(OUTDIR)/tAIB03e8.bin: palm/ht.rcp palm/resource.h + rm -f $(OUTDIR)/*.bin +ifdef MERGE + echo $(MERGE) > $(OUTDIR)/FILR0000.bin +endif + pilrc $(CPP_INCS) -V palm/ht.rcp $(OUTDIR) + +$(OUTDIR)/ht-sections.o: $(OUTDIR)/ht-sections.s + $(CC) -c $(OUTDIR)/ht-sections.s -o $(OUTDIR)/ht-sections.o + +$(OUTDIR)/ht-sections.s $(OUTDIR)/ht-sections.ld: palm/ht.def + cd $(OUTDIR) && m68k-palmos-multigen ../palm/ht.def + +$(OUTDIR)/ht: $(OBJS:%=$(OUTDIR)/%) $(OUTDIR)/ht-sections.ld + $(CC) $(CFLAGS) -lnoexcept -o $(OUTDIR)/ht $(OBJS:%=$(OUTDIR)/%) libgcc.a $(OUTDIR)/ht-sections.ld $(MAPFLAGS) + +$(OUTDIR)/%.o: palm/%.s + $(CC) -c palm/$(*F).s -o $@ + +$(OUTDIR)/packfile.o: packfile.cpp packfile.h + $(CC) $(CFLAGS) -c packfile.cpp -o $(OUTDIR)/packfile.o + +$(OUTDIR)/misc.o: misc.cpp ht.h res.h palm/htplatform.h license.h + $(CC) $(CFLAGS) -c misc.cpp -o $(OUTDIR)/misc.o + +$(OUTDIR)/%.o: %.cpp ht.h res.h palm/htplatform.h + $(CC) $(CFLAGS) -c $(*F).cpp -o $@ + +$(OUTDIR)/armcode: palm/armcode.cpp palm/arm.h res.h miscgraphics.cpp + arm-palmos-gcc $(CFLAGSARM) -c palm/armcode.cpp -o $(OUTDIR)/armcode.o + arm-palmos-gcc -nostartfiles -o $(OUTDIR)/armcode $(OUTDIR)/armcode.o + +$(OUTDIR)/%.o: palm/%.cpp ht.h res.h palm/htplatform.h + $(CC) $(CFLAGS) -c palm/$(*F).cpp -o $@ + +$(OUTDIR)/%.o: palm/%.c ht.h res.h palm/htplatform.h + $(CC) $(CFLAGS) -c palm/$(*F).c -o $@ + +$(OUTDIR)/rip.o: ../inc/rip.cpp + $(CC) $(CFLAGS) -c ../inc/rip.cpp -o $(OUTDIR)/rip.o + + diff --git a/game/makefile.dsp b/game/makefile.dsp new file mode 100644 index 0000000..1a608d2 --- /dev/null +++ b/game/makefile.dsp @@ -0,0 +1,93 @@ +# Microsoft Developer Studio Project File - Name="makefile" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) External Target" 0x0106 + +CFG=makefile - Win32 Palm_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 "makefile.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 "makefile.mak" CFG="makefile - Win32 Palm_Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "makefile - Win32 Palm_Release" (based on "Win32 (x86) External Target") +!MESSAGE "makefile - Win32 Palm_Debug" (based on "Win32 (x86) External Target") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "makefile" +# PROP Scc_LocalPath "." + +!IF "$(CFG)" == "makefile - Win32 Palm_Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Palm_Release" +# PROP BASE Intermediate_Dir "Palm_Release" +# PROP BASE Cmd_Line "htmake REL=1 -j 2" +# PROP BASE Rebuild_Opt "clean" +# PROP BASE Target_File "ht.prc" +# PROP BASE Bsc_Name "" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Palm_Release" +# PROP Intermediate_Dir "Palm_Release" +# PROP Cmd_Line "htmake.bat REL" +# PROP Rebuild_Opt "clean" +# PROP Target_File "ht.prc" +# PROP Bsc_Name "" +# PROP Target_Dir "" + +!ELSEIF "$(CFG)" == "makefile - Win32 Palm_Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Palm_Debug" +# PROP BASE Intermediate_Dir "Palm_Debug" +# PROP BASE Cmd_Line "htmake.bat -j 2" +# PROP BASE Rebuild_Opt "clean" +# PROP BASE Target_File "ht.prc" +# PROP BASE Bsc_Name "" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Palm_Debug" +# PROP Intermediate_Dir "Palm_Debug" +# PROP Cmd_Line "htmake.bat" +# PROP Rebuild_Opt "clean" +# PROP Target_File "htd.prc" +# PROP Bsc_Name "" +# PROP Target_Dir "" + +!ENDIF + +# Begin Target + +# Name "makefile - Win32 Palm_Release" +# Name "makefile - Win32 Palm_Debug" + +!IF "$(CFG)" == "makefile - Win32 Palm_Release" + +!ELSEIF "$(CFG)" == "makefile - Win32 Palm_Debug" + +!ENDIF + +# Begin Source File + +SOURCE=.\htmake.bat +# End Source File +# Begin Source File + +SOURCE=.\makefile +# End Source File +# End Target +# End Project diff --git a/game/makefile.nacl b/game/makefile.nacl new file mode 100644 index 0000000..9a684d7 --- /dev/null +++ b/game/makefile.nacl @@ -0,0 +1,62 @@ +# Makefile for the wi project. + +OUTDIR = nacl_debug +VPATH = sdl/nacl + +.PHONY: all clean + +# The files the project starts with. Add your code files here! +CCFILES = wi.cpp + +#Add your includes here. +INCLUDES = +# These libraries are necessary for PPAPI (Pepper 2) and NaCl. +LDFLAGS = -lppruntime \ + -lppapi_cpp \ + -lplatform \ + -lgio \ + -lpthread \ + -lsrpc \ + $(ARCH_FLAGS) + +# Add your .nmf files for each nexe you build here. +all: check_variables $(OUTDIR)/wi.nmf $(OUTDIR)/wi_dbg.nmf $(OUTDIR)/wi.html + +# common.mk has rules to build .o files from .cc/.c/.cpp files. +-include sdl/nacl/common.mk + +$(OUTDIR)/wi.html: sdl/nacl/wi.html + cp $< $@ + +# Targets to create the manifests. +$(OUTDIR)/wi.nmf: $(OUTDIR)/wi_x86_32.nexe $(OUTDIR)/wi_x86_64.nexe + @echo "Creating wi.nmf..." + $(PYTHON) sdl/nacl/generate_nmf.py --nmf $@ \ + --x86-64 $(OUTDIR)/wi_x86_64.nexe \ + --x86-32 $(OUTDIR)/wi_x86_32.nexe + +$(OUTDIR)/wi_dbg.nmf: $(OUTDIR)/wi_x86_32_dbg.nexe \ + $(OUTDIR)/wi_x86_64_dbg.nexe + @echo "Creating wi_dbg.nmf..." + $(PYTHON) sdl/nacl/generate_nmf.py --nmf $@ \ + --x86-64 $(OUTDIR)/wi_x86_64_dbg.nexe \ + --x86-32 $(OUTDIR)/wi_x86_32_dbg.nexe + +# Targets to create the nexes. +$(OUTDIR)/wi_x86_32.nexe: $(OBJECTS_X86_32:%.o=$(OUTDIR)/%.o) + $(CPP) $^ $(LDFLAGS) -m32 -o $@ + $(NACL_STRIP) $@ -o $@ + +$(OUTDIR)/wi_x86_64.nexe: $(OBJECTS_X86_64:%.o=$(OUTDIR)/%.o) + $(CPP) $^ $(LDFLAGS) -m64 -o $@ + $(NACL_STRIP) $@ -o $@ + +$(OUTDIR)/wi_x86_32_dbg.nexe: $(OBJECTS_X86_32_DBG:%.o=$(OUTDIR)/%.o) + $(CPP) $^ $(LDFLAGS) -m32 -o $@ + +$(OUTDIR)/wi_x86_64_dbg.nexe: $(OBJECTS_X86_64_DBG:%.o=$(OUTDIR)/%.o) + $(CPP) $^ $(LDFLAGS) -m64 -o $@ + +# Target to clean up +clean: + -$(RM) -f $(OUTDIR)/* diff --git a/game/makefile.sdl b/game/makefile.sdl new file mode 100644 index 0000000..1ac0c85 --- /dev/null +++ b/game/makefile.sdl @@ -0,0 +1,117 @@ +# TODO(darrinm): +# - make output dir +# - produce OUTDIR from platform (mac, win, linux, nacl), release type (debug, rel) +# - build on Windows +# - static/dynamic link switch + +PLATFORM=mac + +# Debug is default + +ifdef REL +CPP_DEBUG = +OUTDIR = $(PLATFORM)_rel +else +CPP_DEBUG = -DDEV_BUILD -DDEBUG -DDEBUG_LOGGING -g +OUTDIR = $(PLATFORM)_debug +endif + +EXE_NAME = $(OUTDIR)/WarfareIncorporated$(EXE) + +ifeq ($(PLATFORM), 'mac') +APP_NAME=WarfareIncorporated + +BUNDLE_CONTENTS = $(OUTDIR)/$(APP_NAME).app/Contents +$(APP_NAME)_bundle: $(EXE_NAME) + mkdir -p $(BUNDLE_CONTENTS)/MacOS + mkdir -p $(BUNDLE_CONTENTS)/Resources + echo "APPL????" > $(BUNDLE_CONTENTS)/PkgInfo +# $(INSTALL_PROGRAM) $< $(BUNDLE_CONTENTS)/MacOS/ +endif + + +CC = g++ +EXE = +CPP_INCS = -I .. -I ../inc -I sdl -I ../SDL +CFLAGS = -m32 -O2 -Wno-write-strings -DSDL -DMULTIPLAYER -DTRACKSTATE -D_THREAD_SAFE -DHAVE_OPENGL $(CPP_DEBUG) $(CPP_INCS) +LIBS = -L/usr/local/lib -lSDL +#LIBS = -L/usr/local/lib /usr/local/lib/libSDL.a -lm -liconv -Wl,-framework,OpenGL -Wl,-framework,ForceFeedback -lobjc -Wl,-framework,Cocoa -Wl,-framework,Carbon -Wl,-framework,IOKit -Wl,-framework,CoreAudio -Wl,-framework,AudioToolbox -Wl,-framework,AudioUnit + +OBJS=game.o main.o host.o misc.o bitmap.o rip.o form.o packfile.o ini.o \ + misccontrols.o tests.o timer.o event.o font.o display.o \ + GameObjects.o Level.o Simulation.o tilemap.o StateMachine.o Animation.o \ + fogmap.o SRInfantry.o LRInfantry.o terrainmap.o HRC.o \ + Reactor.o Processor.o Miner.o Headquarters.o Research.o Radar.o VTS.o \ + MobileBuilder.o Tank.o SpInfantry.o Overmind.o Builder.o MobileUnit.o \ + memmgr.o tbitmap.o compression.o decompress.o \ + cachemgr.o Multiplayer.o comm.o Player.o \ + Warehouse.o Tower.o MobileHQ.o Unit.o Struct.o SimUI.o \ + soundmgr.o \ + updatemap.o \ + GameOptions.o InputUI.o formmgr.o thunks.o loadsave.o savegame.o \ + triggermgr.o TriggerConditions.o TriggerActions.o UnitGroupMgr.o BuildMgr.o \ + RawBitmap.o Shell.o Ecom.o alertcontrol.o stringtable.o Andy.o \ + Artillery.o Replicator.o Help.o CutScene.o drm.o \ + sdlpackfile.o tick.o mempdbreader.o loginhandler.o simplerequest.o \ + bytebuffer.o base64.o httprequest.o basemisc.o selectmission.o \ + missionlist.o chooseserverform.o creategameform.o serviceurls.o \ + dlmissionpack.o downloadbox.o httppackinfomanager.o packinfomanager.o \ + format.o httppackmanager.o httpindexloader.o indexloader.o packmanager.o \ + thread.o messagequeue.o messagehandler.o jsonbuilder.o jsontypes.o \ + yajl_parser.o yajl.o yajl_buf.o yajl_lex.o yajl_encode.o \ + socketserver.o socketaddress.o socket.o deletetracker.o selectserver.o \ + lobby.o chatter.o gameform.o lobbyform.o eventer.o completemanager.o \ + statetracker.o stateframe.o roomform.o createroomform.o loginform.o \ + map.o fingerhandler.o dragrect.o flickscroller.o mpsharedmisc.o md5c.o \ + uploader.o stylushandler.o drawscan.o netmessage.o hosthelpers.o \ + sdlhttpservice.o sdlhttprequest.o + +# TODO(darrinm): sdl/transportmgr.cpp [from iphone? from win?] + + +all: $(EXE_NAME) + +clean: + rm -f $(OUTDIR)/* + +$(EXE_NAME): $(OUTDIR)/ht + cp $(OUTDIR)/ht $(EXE_NAME) + +#$(EXE_NAME): sdl/main.cpp game.cpp misccontrols.cpp form.cpp GameObjects.cpp +# $(CC) -o $@ $? $(CFLAGS) $(LIBS) + +$(OUTDIR)/ht: $(OBJS:%=$(OUTDIR)/%) + $(CC) $(CFLAGS) -o $(OUTDIR)/ht $(OBJS:%=$(OUTDIR)/%) + +#$(OUTDIR)/packfile.o: mpshared/packfile.cpp mpshared/packfile.h +# $(CC) $(CFLAGS) -c mpshared/packfile.cpp -o $(OUTDIR)/packfile.o + +$(OUTDIR)/misc.o: misc.cpp ht.h res.h sdl/htplatform.h license.h + $(CC) $(CFLAGS) -c misc.cpp -o $(OUTDIR)/misc.o + +$(OUTDIR)/%.o: %.cpp ht.h res.h sdl/htplatform.h + $(CC) $(CFLAGS) -c $(*F).cpp -o $@ + +$(OUTDIR)/%.o: sdl/%.cpp ht.h res.h sdl/htplatform.h + $(CC) $(CFLAGS) -c sdl/$(*F).cpp -o $@ + +$(OUTDIR)/%.o: ../mpshared/%.cpp ht.h res.h sdl/htplatform.h + $(CC) $(CFLAGS) -c ../mpshared/$(*F).cpp -o $@ + +$(OUTDIR)/%.o: ../yajl/wrapper/%.cpp + $(CC) $(CFLAGS) -c ../yajl/wrapper/$(*F).cpp -o $@ + +$(OUTDIR)/%.o: ../yajl/src/%.c + $(CC) $(CFLAGS) -c ../yajl/src/$(*F).c -o $@ + +$(OUTDIR)/%.o: ../base/%.cpp + $(CC) $(CFLAGS) -c ../base/$(*F).cpp -o $@ + +$(OUTDIR)/basemisc.o: ../base/misc.cpp ../base/misc.h + $(CC) $(CFLAGS) -c ../base/misc.cpp -o $@ + +$(OUTDIR)/mpsharedmisc.o: ../mpshared/misc.cpp ../mpshared/misc.h + $(CC) $(CFLAGS) -c ../mpshared/misc.cpp -o $@ + +$(OUTDIR)/rip.o: ../inc/rip.cpp ../inc/rip.h + $(CC) $(CFLAGS) -c ../inc/rip.cpp -o $(OUTDIR)/rip.o diff --git a/game/map.cpp b/game/map.cpp new file mode 100644 index 0000000..c9740a4 --- /dev/null +++ b/game/map.cpp @@ -0,0 +1,45 @@ +#include "game/map.h" + +namespace wi { + +bool Map::GetValue(const char *pszKey, char *pszValue, + int cbValue) const { + + // Find key + KeyMap::const_iterator it = map_.find(pszKey); + if (it == map_.end()) { + return false; + } + strncpyz(pszValue, it->second.c_str(), cbValue); + return true; +} + +bool Map::SetValue(const char *pszKey, const char *pszValue) { + // Erase old key of this value, if it exists + KeyMap::iterator it = map_.find(pszKey); + if (it != map_.end()) { + map_.erase(it); + } + + // Insert new + map_.insert(KeyMap::value_type(pszKey, pszValue)); + return true; +} + +bool Map::EnumKeys(Enum *penm, char *pszKey, int cbKey) const { + if (penm->m_dwUser == kEnmFirst) { + penm->m_dwUser = 0; + } + + KeyMap::const_iterator it = map_.begin(); + for (int i = 0; it != map_.end(); i++, it++) { + if (penm->m_dwUser == i) { + strncpyz(pszKey, it->first.c_str(), cbKey); + penm->m_dwUser++; + return true; + } + } + return false; +} + +} // namespace wi diff --git a/game/map.h b/game/map.h new file mode 100644 index 0000000..a2200e6 --- /dev/null +++ b/game/map.h @@ -0,0 +1,25 @@ +#ifndef __MAP_H__ +#define __MAP_H__ + +#include "inc/basictypes.h" +#include "mpshared/enum.h" +#include "mpshared/misc.h" +#include +#include + +namespace wi { + +class Map { +public: + bool GetValue(const char *pszKey, char *pszValue, int cbValue) const; + bool SetValue(const char *pszKey, const char *pszValue); + bool EnumKeys(Enum *penm, char *pszKey, int cbKey) const; + +protected: + typedef std::map KeyMap; + KeyMap map_; +}; + +} // namespace wi + +#endif // __MAP_H__ diff --git a/game/memmgr.cpp b/game/memmgr.cpp new file mode 100644 index 0000000..6cd6794 --- /dev/null +++ b/game/memmgr.cpp @@ -0,0 +1,1238 @@ +// Heap support. Chunks are allocated by MemMgr, heaps are initialized into these chunks (can be in +// dyn ram or db ram). The MemMgr delegates requests to the appropriate heap. Allocs out of dyn ram first. + +// "chunk" are the chunks of memory allocated from the host os in which we create heaps +// "block" are the blocks of memory we reserve in our heaps for allocs +// "alloc" are the allocs requested. cbBlock == ((cbAlloc + 1) & ~1) + sizeof(MemHeader) + +#if defined(DEBUG) && !defined(PIL) +#define INCL_FORCECOMPACTION +#endif + +#include "ht.h" + +namespace wi { + +MemMgr gmmgr; +dword gcbDynMemAtStart; +dword gcbDbMemAtStart; + +// MemHeader w field +// bit 0: busy flag. 1 if busy, 0 if free. If free, bits 1-15 are the offset to the next free. +// bit 1: locked flag. 1 if locked, 0 if unlocked. Valid only if busy is 1 +// bits 2-7: handle table entry index +// bits 8-15: handle table index + +#define kfMhdrBusy 0x01 +#define kfMhdrLocked 0x02 + +#define IsFree(pmhdr) ((pmhdr->w & kfMhdrBusy) == 0) +#define IsLocked(pmhdr) ((pmhdr->w & kfMhdrLocked) != 0) +#define GetNextFree(pmhdr) (pmhdr->w == 0 ? NULL : (MemHeader *)(pheap->pbHeap + pmhdr->w)) +#define GetNext(pmhdr) ((MemHeader *)((byte *)pmhdr + pmhdr->cbBlock)) +#define GetHandleTableIndex(her) (her >> 8) +#define GetHandleEntryIndex(her) ((her & 0xfc) >> 2) +#define GetHandleEntryPtr(her) (&m_aphtab[GetHandleTableIndex(her)]->ahe[GetHandleEntryIndex(her)]) +#define GetHandleEntryRef(ihtab, ihe) ((ihtab << 8) | (ihe << 2)) +#define UpdateHandleEntry(pheap, her, pmhdr) *GetHandleEntryPtr(her) = (word)((byte *)pmhdr - pheap->pbHeap) +#define GetHeap(hmem) m_apheap[((HMemStruct *)&hmem)->iheap] +#define GetMemHeader(hmem) (MemHeader *)(*GetHandleEntryPtr(((HMemStruct *)&hmem)->her) + GetHeap(hmem)->pbHeap) + +// Heap flags + +#define kfHeapDbRam 1 +#define kfHeapLargestFreeBlockSizeValid 2 +#define kfHeapFullyCompacted 4 + +// MemMgr + +#define kcbChunkAllocBig ((word)63 * 1024) +#define kcbChunkAllocSmall ((word)32 * 1024) +#define kcbChunkTooSmall ((word)4 * 1024) + +#define kszDbMemMgr "HtDbMemMgr" +#define kdwType 'temp' + +MemMgr::MemMgr() +{ + m_cheap = 0; + m_chtab = 0; + m_chtabAlloced = 0; + m_aphtab = NULL; + m_cbTotalFreeBlocks = 0; +#ifdef PIL + m_lid = 0; + m_pdb = NULL; +#endif +} + +MemMgr::~MemMgr() +{ + Assert(m_cheap == 0); + Assert(m_chtab == 0); +#ifdef PIL + Assert(m_pdb == NULL); +#endif +} + +void MemMgr::OnRebootFreeStorageDatabase() +{ +#ifdef PIL + LocalID lid = DmFindDatabase(0, kszDbMemMgr); + if (lid != 0) + DmDeleteDatabase(0, lid); +#endif +} + +#if defined(WIN) || defined(IPHONE) || defined(SDL) +#define kcbDynMemWindows (3 * 1024 * 1024) +#endif + +void MemMgr::GetInitSize(dword cbDynReserve, dword *pcbDyn, dword *pcbStorage) +{ +#ifdef PIL + UInt32 cbDyn, cbStorage; + UInt32 cbMaxT; + MemHeapFreeBytes(0, (UInt32 *)&cbDyn, &cbMaxT); + MemHeapFreeBytes(1, (UInt32 *)&cbStorage, &cbMaxT); + + dword cbDynRemaining = 0; + if (cbDynReserve < cbDyn) + cbDynRemaining = cbDyn - cbDynReserve; + + *pcbDyn = cbDynRemaining; + *pcbStorage = cbStorage; +#else + *pcbDyn = kcbDynMemWindows; + *pcbStorage = 0; +#endif +} + +void MemMgr::Init(dword cbDynReserve, dword cbMaxNeeded, dword *pcbTotal) +{ + // Remember how much memory is available in dyn, db heaps before doing anything. + +#ifdef PIL + UInt32 cbMaxT; + MemHeapFreeBytes(0, (UInt32 *)&gcbDynMemAtStart, &cbMaxT); + MemHeapFreeBytes(1, (UInt32 *)&gcbDbMemAtStart, &cbMaxT); +#else + gcbDynMemAtStart = kcbDynMemWindows; + gcbDbMemAtStart = 0; +#endif + + // First, dyn allocs. Grab everything we can, in the largest chunks we can. + + m_cheap = 0; + *pcbTotal = 0; + while (*pcbTotal < cbMaxNeeded) { + // Bounds check + + if (m_cheap == ARRAYSIZE(m_apheap)) + break; + + // Find out how much dyn ram we have to alloc. If we're at our + // reserve limit, break out. + +#ifdef PIL + // Call the mem api for Palm + + UInt32 cbFree; + MemHeapFreeBytes(0, &cbFree, &cbMaxT); +#else + // For non-Palm, just assume a big size + + dword cbFree = gcbDynMemAtStart - *pcbTotal; +#endif + if (cbFree <= cbDynReserve) + break; + + // If the chunk is too small to bother, break out + + dword cbAlloc = cbFree - cbDynReserve; + if (cbAlloc < kcbChunkTooSmall) + break; + + // Hack: On simulator, MemHeapFreeBytes() lies and doesn't change across + // successive allocs. If we can't alloc the size we want, skip the dyn heap + // and go for the storage heap + + if (m_cheap == 0) { + // Don't waste a heap if marginal space is available + + if (cbAlloc < kcbChunkAllocBig) + break; + } else { + // If the last heap was fractional, skip to storage mem + + if (m_apheap[m_cheap - 1]->cbHeap < kcbChunkAllocBig) + break; + } + + // Palm MemMgr is funky. Try big chunk size first (bigger the better). If it fails, + // try the small size. If that fails, break out. + + if (cbAlloc > kcbChunkAllocBig) + cbAlloc = kcbChunkAllocBig; + void *pv = (void *)new byte[cbAlloc]; + if (pv == NULL && cbAlloc > kcbChunkAllocSmall) { + cbAlloc = kcbChunkAllocSmall; + pv = (void *)new byte[cbAlloc]; + } + if (pv == NULL) + break; + + // Create a heap in this chunk and continue + + Heap *pheap = NewHeap(pv, (word)cbAlloc, false, 0, NULL); + if (pheap == NULL) { + delete (byte *)pv; + break; + } + m_apheap[m_cheap++] = pheap; + *pcbTotal += cbAlloc; + } + + // If our heap space is big enough, we're done + + if (*pcbTotal >= cbMaxNeeded) + return; + +#ifdef PIL + // Start allocing out of db ram (Palm only) + + LocalID lid = DmFindDatabase(0, kszDbMemMgr); + if (lid != 0) + DmDeleteDatabase(0, lid); + if (DmCreateDatabase(0, kszDbMemMgr, kdwCreator, kdwType, false) != 0) + return; + m_lid = DmFindDatabase(0, kszDbMemMgr); + if (m_lid == 0) + return; + m_pdb = DmOpenDatabase(0, m_lid, dmModeReadWrite); + if (m_pdb == NULL) { + DmDeleteDatabase(0, m_lid); + return; + } + + // Start creating heaps + + while (*pcbTotal < cbMaxNeeded) { + // Bounds check + + if (m_cheap == ARRAYSIZE(m_apheap)) + break; + + // Try to allocate a chunk + + UInt32 cbAlloc = kcbChunkAllocBig; + UInt16 irec = dmMaxRecordIndex; + MemHandle hNew = DmNewRecord(m_pdb, &irec, cbAlloc); + if (hNew == NULL) { + cbAlloc = kcbChunkAllocSmall; + hNew = DmNewRecord(m_pdb, &irec, cbAlloc); + } + if (hNew == NULL) + break; + void *pv = MemHandleLock(hNew); + + // Make a heap in this chunk. If this fails, break out. The db records + // will get removed in later cleanup + + Heap *pheap = NewHeap(pv, (word)cbAlloc, true, irec, hNew); + if (pheap == NULL) + break; + m_apheap[m_cheap++] = pheap; + *pcbTotal += cbAlloc; + } +#endif +} + +void MemMgr::Exit() +{ + // Free heaps first + + for (int iheap = 0; iheap < m_cheap; iheap++) { + Heap *pheap = m_apheap[iheap]; + + // Check for one free block the size of the heap. If anything less, something hasn't been freed + + Assert(((MemHeader *)pheap->pbHeap)->cbBlock == pheap->cbHeap && IsFree(((MemHeader *)pheap->pbHeap))); + + // dyn heap or db heap? + + if (pheap->wf & kfHeapDbRam) { +#ifdef PIL + // Just unlock the record; it'll get freed below all at once + + MemHandleUnlock((MemHandle)pheap->hMem); + DmReleaseRecord(m_pdb, pheap->nRec, false); +#else + Assert(false); +#endif + } else { + // Free the dyn chunk + + delete pheap->pbHeap; + } + delete pheap; + } + m_cheap = 0; + +#ifdef PIL + // Now free database which'll free all db chunks at once. + + if (m_pdb != NULL) { + Err err = DmCloseDatabase(m_pdb); + Assert(err == 0); + DmDeleteDatabase(0, m_lid); + m_pdb = NULL; + m_lid = 0; + } +#endif + + // Free handle tables + + for (int ihtab = 0; ihtab < m_chtab; ihtab++) + delete m_aphtab[ihtab]; + delete m_aphtab; + m_chtab = 0; + m_chtabAlloced = 0; + m_aphtab = NULL; + m_cbTotalFreeBlocks = 0; +} + +Heap *MemMgr::NewHeap(void *pvHeap, word cbHeap, bool fDbRam, word nRec, void *hMem) +{ + Heap *pheap = new Heap; + if (pheap == NULL) + return NULL; + + pheap->pbHeap = (byte *)pvHeap; + pheap->cbHeap = cbHeap; + pheap->wf = fDbRam ? kfHeapDbRam : 0; + pheap->nRec = nRec; + pheap->hMem = hMem; + + MemHeader mhdr; + mhdr.w = 0; + mhdr.cbBlock = cbHeap; + WriteHeader(pheap, (MemHeader *)pvHeap, &mhdr); + pheap->pmhdrFreeFirst = (MemHeader *)pvHeap; + + m_cbTotalFreeBlocks += cbHeap; + + return pheap; +} + +#define kchtabGrow 32 + +HandleTable *MemMgr::NewHandleTable() +{ + // Make space in the list + + if (m_chtab == m_chtabAlloced) { + HandleTable **aphtabT = new HandleTable *[m_chtabAlloced + kchtabGrow]; + if (aphtabT == NULL) + return NULL; + if (m_aphtab != NULL) { + memcpy(aphtabT, m_aphtab, sizeof(HandleTable *) * m_chtabAlloced); + delete m_aphtab; + } + m_aphtab = aphtabT; + m_chtabAlloced += kchtabGrow; + } + + // Alloc new + + HandleTable *phtab = new HandleTable; + if (phtab == NULL) + return NULL; + memset(phtab, 0, sizeof(HandleTable)); + + // Link into free list + + for (int ihe = 0; ihe < kcHandlesPerTable; ihe++) { + if (ihe < kcHandlesPerTable - 1) { + phtab->ahe[ihe] = (HandleEntry)(ihe + 1); + } else { + phtab->ahe[ihe]= (HandleEntry)-1; + } + } + + // Done + + m_aphtab[m_chtab++] = phtab; + return phtab; +} + +bool MemMgr::AllocHandleEntry(word *pher) +{ +#ifdef INCL_VALIDATEHEAP + ValidateHandleTableFreeEntries(); +#endif + + // First find a handle table with a free entry + + int ihtab; + for (ihtab = 0; ihtab < m_chtab; ihtab++) { + if (m_aphtab[ihtab]->iheFree != -1) + break; + } + + // If we couldn't find a suitable handle table, make a new one + + if (ihtab >= m_chtab) { + if (NewHandleTable() == NULL) + return false; + ihtab = m_chtab - 1; + } + + // Grab the next free handle entry + + HandleTable *phtab = m_aphtab[ihtab]; + Assert(phtab->iheFree >= 0 && phtab->iheFree < ARRAYSIZE(phtab->ahe)); + int ihe = phtab->iheFree; + int iheFree = (int)(short)phtab->ahe[ihe]; + Assert(iheFree == -1 || iheFree < ARRAYSIZE(phtab->ahe)); + Assert(phtab->ahe[iheFree] == (word)-1 || phtab->ahe[iheFree] < ARRAYSIZE(phtab->ahe)); + phtab->iheFree = iheFree; + + // Return a cookie reference + + *pher = GetHandleEntryRef(ihtab, ihe); + +#ifdef INCL_VALIDATEHEAP + phtab->ahe[ihe] = 0; + ValidateHandleTableFreeEntries(); +#endif + + return true; +} + +void MemMgr::FreeHandleEntry(word her) +{ +#ifdef INCL_VALIDATEHEAP + ValidateHandleTableFreeEntries(); +#endif + + int ihtab = GetHandleTableIndex(her); + int ihe = GetHandleEntryIndex(her); + Assert(ihtab < m_chtab); + HandleTable *phtab = m_aphtab[ihtab]; + Assert(ihe < ARRAYSIZE(phtab->ahe)); // should be in table + Assert((phtab->ahe[ihe] & 1) == 0); // hack to check it's not allocated + Assert(phtab->iheFree == -1 || phtab->iheFree < ARRAYSIZE(phtab->ahe)); // validate free + Assert(phtab->ahe[phtab->iheFree] == (word)-1 || phtab->ahe[phtab->iheFree] < ARRAYSIZE(phtab->ahe)); // validate free points to free + phtab->ahe[ihe] = (HandleEntry)phtab->iheFree; + phtab->iheFree = ihe; + +#ifdef INCL_VALIDATEHEAP + ValidateHandleTableFreeEntries(); +#endif +} + +dword MemMgr::GetFreeSize(word *pcbLargestFree) +{ + if (pcbLargestFree != NULL) { + word cbLargestFreeBlock = 0; + for (int iheap = 0; iheap < m_cheap; iheap++) { + Heap *pheap = m_apheap[iheap]; + word cbT = GetLargestFreeBlockSize(pheap); + if (cbT > cbLargestFreeBlock) + cbLargestFreeBlock = cbT; + } + if (cbLargestFreeBlock > sizeof(MemHeader)) { + *pcbLargestFree = cbLargestFreeBlock - sizeof(MemHeader); + } else { + *pcbLargestFree = 0; + } + } + + return m_cbTotalFreeBlocks - sizeof(MemHeader); +} + +word MemMgr::GetLargestFreeBlockSize(Heap *pheap) +{ + // Compact to determine the largest free block + + if ((pheap->wf & (kfHeapFullyCompacted | kfHeapLargestFreeBlockSizeValid)) != (kfHeapFullyCompacted | kfHeapLargestFreeBlockSizeValid)) { + // We want to get the large free block size which we'll do by + // compaction even if the heap is fully compacted already (rare). + + pheap->wf &= ~kfHeapFullyCompacted; + Compact(pheap, 0xffff, NULL, 0); + } + + // Now these bits should be valid + + Assert((pheap->wf & (kfHeapFullyCompacted | kfHeapLargestFreeBlockSizeValid)) == (kfHeapFullyCompacted | kfHeapLargestFreeBlockSizeValid)); + return pheap->cbLargestFreeBlock; +} + +dword MemMgr::GetTotalSize(dword *pcbDyn, dword *pcbDb) +{ + *pcbDyn = 0; + *pcbDb = 0; + for (int iheap = 0; iheap < m_cheap; iheap++) { + Heap *pheap = m_apheap[iheap]; + if (pheap->wf & kfHeapDbRam) { + *pcbDb += pheap->cbHeap; + } else { + *pcbDyn += pheap->cbHeap; + } + } + return *pcbDb + *pcbDyn; +} + +HMem MemMgr::AllocHandle(word cbAlloc, word wfHints) +{ + // CE is ARM which wants dword aligned allocs because of alignment issues + +#ifdef __CPU_68K + word cbBlock = ((cbAlloc + 1) & ~1) + sizeof(MemHeader); +#else + word cbBlock = ((cbAlloc + 3) & ~3) + sizeof(MemHeader); +#endif + bool fLastCacheDiscardFailed = false; + + // Try to alloc from one of our heaps. If this fails, free space from the cache and try again + + while (true) { + // Try each heap + + for (int iheap = 0; iheap < m_cheap; iheap++) { + Heap *pheap = m_apheap[iheap]; + + // Do we have a block large enough to contain this alloc? We may be able to know + // that up front and save some time. + + if ((pheap->wf & (kfHeapFullyCompacted | kfHeapLargestFreeBlockSizeValid)) == (kfHeapFullyCompacted | kfHeapLargestFreeBlockSizeValid)) { + if (cbBlock > pheap->cbLargestFreeBlock) + continue; + } + + // Find any that fits. + + word cbLargestFreeBlock = 0; + MemHeader *pmhdrFreeLast = NULL; + MemHeader *pmhdrFree = pheap->pmhdrFreeFirst; + MemHeader *pmhdrUse = NULL; + MemHeader *pmhdrUseFreeLast = NULL; + while (pmhdrFree != NULL) { + // This block large enough? + + Assert(IsFree(pmhdrFree)); + if (pmhdrFree->cbBlock >= cbBlock) { + // If this block will get locked, alloc it at the end of the heap to help + // fragmentation + + pmhdrUse = pmhdrFree; + pmhdrUseFreeLast = pmhdrFreeLast; + if (!(wfHints & kfHintWillLock)) + break; + } + + // Track largest free + + if (pmhdrFree->cbBlock > cbLargestFreeBlock) + cbLargestFreeBlock = pmhdrFree->cbBlock; + + // Next free + + pmhdrFreeLast = pmhdrFree; + pmhdrFree = GetNextFree(pmhdrFree); + } + + // If we scanned the whole list, update largest free + + if (pmhdrFree == NULL) { + pheap->cbLargestFreeBlock = cbLargestFreeBlock; + pheap->wf |= kfHeapLargestFreeBlockSizeValid; + } + + // If we found something, use it + + if (pmhdrUse != NULL) + return AllocBlock(iheap, pmhdrUse, pmhdrUseFreeLast, cbBlock, wfHints); + + // If already compacted, nothing more to do for this heap. + + if (pheap->wf & kfHeapFullyCompacted) + continue; + + // Compact to try to create the space + + pmhdrFree = Compact(pheap, cbBlock, &pmhdrFreeLast, wfHints); + if (pmhdrFree != NULL) + return AllocBlock(iheap, pmhdrFree, pmhdrFreeLast, cbBlock, wfHints); + } + + // No space in any of the heaps. Free space from the cache. + // If the last time we tried to make space it failed, there is nothing left + // to discard, so we have a total failure. + + if (fLastCacheDiscardFailed) + return NULL; + fLastCacheDiscardFailed = !gcam.MakeSpace(cbAlloc); + } +} + +HMem MemMgr::AllocBlock(int iheap, MemHeader *pmhdr, MemHeader *pmhdrFreeLast, word cbBlock, word wfHints) +{ +#ifdef INCL_VALIDATEHEAP + Validate(); +#endif + + // First need to alloc a new handle. This can fail. + + word her; + if (!AllocHandleEntry(&her)) + return NULL; + + MemHeader mhdr; + mhdr.w = her | kfMhdrBusy; + mhdr.cbBlock = cbBlock; + + // Have a block to use, now officially alloc it for use. + // Should we split this block or use it as is? + + Heap *pheap = m_apheap[iheap]; + if (pmhdr->cbBlock - cbBlock > sizeof(MemHeader)) { + // Enough room to split the existing block + + pmhdr = SplitFreeBlock(pheap, pmhdr, pmhdrFreeLast, &mhdr, wfHints); + } else { + // Not enough room to split so use this block as is. May have up to + // MemHeader+1 extra bytes. Remove this block from the free list. + + if (pmhdrFreeLast != NULL) { + MemHeader mhdrFreeLast = *pmhdrFreeLast; + mhdrFreeLast.w = pmhdr->w; + WriteHeader(pheap, pmhdrFreeLast, &mhdrFreeLast); + } else { + pheap->pmhdrFreeFirst = GetNextFree(pmhdr); + } + + // Init the new block header + + mhdr.cbBlock = pmhdr->cbBlock; + WriteHeader(m_apheap[iheap], pmhdr, &mhdr); + + // If we're using the largest block, we don't know what the next largest block is + + if (mhdr.cbBlock == pheap->cbLargestFreeBlock) + pheap->wf &= ~kfHeapLargestFreeBlockSizeValid; + } + + // Update the handle to this object + + UpdateHandleEntry(pheap, pmhdr->w, pmhdr); + + // We just used this much space + + m_cbTotalFreeBlocks -= pmhdr->cbBlock; + +#ifdef INCL_VALIDATEHEAP + Validate(); +#endif + + // We're done. + + HMemStruct hms; + hms.her = mhdr.w; + hms.iheap = iheap; + + // Easiest way to handle alignment + + HMem hMem; + memcpy(&hMem, &hms, _min(sizeof(hMem), sizeof(hms))); + return hMem; +} + +MemHeader *MemMgr::SplitFreeBlock(Heap *pheap, MemHeader *pmhdr, MemHeader *pmhdrFreeLast, MemHeader *pmhdrNew, word wfHints) +{ + Assert(pmhdr->cbBlock - pmhdrNew->cbBlock > sizeof(MemHeader)); + Assert(IsFree(pmhdr)); + + // If we're splitting the largest block, we don't know what the next + // biggest block is + + if (pmhdr->cbBlock == pheap->cbLargestFreeBlock) + pheap->wf &= ~kfHeapLargestFreeBlockSizeValid; + + if (wfHints & kfHintWillLock) { + // Locked objects go at the end of the heap. So when splitting a free block, place the new object + // at the end of the block, new free block at the start. This method doesn't require adjusting + // the free list since the free block doesn't move. + + MemHeader mhdrFreeNew = *pmhdr; + mhdrFreeNew.cbBlock -= pmhdrNew->cbBlock; + WriteHeader(pheap, pmhdr, &mhdrFreeNew); + pmhdr = GetNext(pmhdr); + WriteHeader(pheap, pmhdr, pmhdrNew); + } else { + // Split the block so the new free chunk goes at the end. This tends to alloc blocks + // towards the front of the heap (at the penalty of having to update the free list) + + MemHeader mhdrFreeNew = *pmhdr; + WriteHeader(pheap, pmhdr, pmhdrNew); + mhdrFreeNew.cbBlock -= pmhdrNew->cbBlock; + MemHeader *pmhdrFreeNew = GetNext(pmhdr); + WriteHeader(pheap, pmhdrFreeNew, &mhdrFreeNew); + + // Update the free list + + if (pmhdrFreeLast != NULL) { + MemHeader mhdrFreeLast = *pmhdrFreeLast; + mhdrFreeLast.w = (word)((byte *)pmhdrFreeNew - pheap->pbHeap); + WriteHeader(pheap, pmhdrFreeLast, &mhdrFreeLast); + } else { + pheap->pmhdrFreeFirst = pmhdrFreeNew; + } + Assert(IsFree(pmhdrFreeNew)); + } + + // Return the new block + + return pmhdr; +} + +MemHeader *MemMgr::Compact(Heap *pheap, word cbBlock, MemHeader **ppmhdrFreeLast, word wfHints) +{ +#ifdef INCL_VALIDATEHEAP + Validate(); +#endif + + // We're here to see if a block at least as big as cbBlock can be created by heap compaction. + + Assert(!(pheap->wf & kfHeapFullyCompacted)); + + // Use a bubble up approach. Try to move free space upwards to create larger free chunks towards the middle + // of the heap. Since everything is handle based, works great on everything except locked objects. + + word cbLargestFreeBlock = 0; + MemHeader *pmhdrUse = NULL; + MemHeader *pmhdr = (MemHeader *)pheap->pbHeap; + MemHeader *pmhdrHeapEnd = (MemHeader *)&pheap->pbHeap[pheap->cbHeap]; + MemHeader *pmhdrLast = NULL; + MemHeader *pmhdrFreeLast = NULL; + MemHeader *pmhdrFreeLastNext = NULL; + while (pmhdr != pmhdrHeapEnd) { + // We do compacting by comparing the current and last blocks + + if (pmhdrLast != NULL) { + // Remember the last free block so the free list can be maintained + + if (IsFree(pmhdrLast)) + pmhdrFreeLastNext = pmhdrLast; + + // If this block is free, we can merge two free blocks, or if not free, bubble up a + // free block + + if (IsFree(pmhdr)) { + // This block is free. + + if (IsFree(pmhdrLast)) { + // Last block is free too; coalesce these two + + MemHeader mhdr = *pmhdr; + mhdr.cbBlock += pmhdrLast->cbBlock; + WriteHeader(pheap, pmhdrLast, &mhdr); + + // pmhdr has disappeared. It's now the previous block. + + pmhdr = pmhdrLast; + + // Update the largest free if necessary + + if (pmhdr->cbBlock > pheap->cbLargestFreeBlock) + pheap->cbLargestFreeBlock = pmhdr->cbBlock; + + // Keep the last "free last" since we just merged pmhdrLast + + pmhdrFreeLastNext = pmhdrFreeLast; + } + } else { + // This block is alloced. If it is locked, we really can't do anything + // with this block and the last block. + + if (!IsLocked(pmhdr)) { + // This block is unlocked. + + if (IsFree(pmhdrLast)) { + // Previous block is free. Move the current block into that free block, + // thereby 'bubbling up' the free block. + // Save the free header + + MemHeader mhdrFree = *pmhdrLast; + + // First update handle table for this block about to move + + UpdateHandleEntry(pheap, pmhdr->w, pmhdrLast); + + // Now move it + + WriteHeader(pheap, pmhdrLast, pmhdr, pmhdr->cbBlock); + pmhdr = GetNext(pmhdrLast); + + // Now write the new free block + + WriteHeader(pheap, pmhdr, &mhdrFree); + + // Update the free list link + + if (pmhdrFreeLast != NULL) { + MemHeader mhdr = *pmhdrFreeLast; + mhdr.w = (word)((byte *)pmhdr - pheap->pbHeap); + WriteHeader(pheap, pmhdrFreeLast, &mhdr); + } else { + pheap->pmhdrFreeFirst = pmhdr; + } + + // Keep the last "free last" since we just moved pmhdrLast + + pmhdrFreeLastNext = pmhdrFreeLast; + } + } + } + } + + // Track largest free + + if (IsFree(pmhdr)) { + if (pmhdr->cbBlock > cbLargestFreeBlock) + cbLargestFreeBlock = pmhdr->cbBlock; + } + + // If this block is free and is large enough, return with it + + if (IsFree(pmhdr) && pmhdr->cbBlock >= cbBlock) { + Assert((pmhdrFreeLast == NULL && pheap->pmhdrFreeFirst == pmhdr) || GetNextFree(pmhdrFreeLast) == pmhdr); + pmhdrUse = pmhdr; + *ppmhdrFreeLast = pmhdrFreeLast; + + // If this object will get not get locked, then break now to alloc it towards the front + + if (!(wfHints & kfHintWillLock)) + break; + } + + // Next block... + + pmhdrFreeLast = pmhdrFreeLastNext; + pmhdrLast = pmhdr; + pmhdr = GetNext(pmhdr); + Assert(pmhdr > pmhdrLast); + } + + // If we completed, this info is up to date + + if (pmhdr == pmhdrHeapEnd) { + pheap->cbLargestFreeBlock = cbLargestFreeBlock; + pheap->wf |= kfHeapFullyCompacted | kfHeapLargestFreeBlockSizeValid; + } + +#ifdef INCL_VALIDATEHEAP + Validate(); +#endif + + // Return what we found + + return pmhdrUse; +} + +void MemMgr::FreeHandle(HMem hmem) +{ +#ifdef INCL_VALIDATEHEAP + ValidateHandle(hmem); + Validate(); +#endif + + Heap *pheap = GetHeap(hmem); + MemHeader *pmhdr = GetMemHeader(hmem); + Assert(!IsFree(pmhdr)); + + // Free this handle + + FreeHandleEntry(pmhdr->w); + + // The free list is sorted. This makes it possible to compact easily, as well as + // coalesce free blocks during freeing. Find the insertion point. + + MemHeader *pmhdrInsertAfter = NULL; + if (pmhdr > pheap->pmhdrFreeFirst && pheap->pmhdrFreeFirst != NULL) { + pmhdrInsertAfter = pheap->pmhdrFreeFirst; + while (pmhdrInsertAfter != NULL) { + Assert(IsFree(pmhdrInsertAfter)); + MemHeader *pmhdrT = GetNextFree(pmhdrInsertAfter); + if (pmhdrT == NULL || pmhdrT > pmhdr) + break; + pmhdrInsertAfter = pmhdrT; + } + } + + // See if we have a free block immediately before + + MemHeader *pmhdrFreeBefore = NULL; + if (pmhdrInsertAfter != NULL && GetNext(pmhdrInsertAfter) == pmhdr) + pmhdrFreeBefore = pmhdrInsertAfter; + + // Now check to see if we have a free block immediately after + + MemHeader *pmhdrFreeAfter = NULL; + MemHeader *pmhdrT = GetNext(pmhdr); + if (pmhdrT != NULL && (byte *)pmhdrT < &pheap->pbHeap[pheap->cbHeap] && IsFree(pmhdrT)) { + pmhdrFreeAfter = pmhdrT; + Assert(pmhdrFreeAfter > pmhdrFreeBefore); + } + + // About to free this much space + + m_cbTotalFreeBlocks += pmhdr->cbBlock; + + // Case out coalescing + + if (pmhdrFreeBefore == NULL) { + if (pmhdrFreeAfter == NULL) { + // No empty space surrounding + + if (pmhdrInsertAfter == NULL) { + // This is the first sorted free block + + MemHeader mhdr; + mhdr.w = 0; + if (pheap->pmhdrFreeFirst != NULL) + mhdr.w = (word)((byte *)pheap->pmhdrFreeFirst - pheap->pbHeap); + mhdr.cbBlock = pmhdr->cbBlock; + WriteHeader(pheap, pmhdr, &mhdr); + pheap->pmhdrFreeFirst = pmhdr; + } else { + // This is not the first free block, but somewhere in the middle + + MemHeader mhdr; + mhdr.w = pmhdrInsertAfter->w; + mhdr.cbBlock = pmhdr->cbBlock; + WriteHeader(pheap, pmhdr, &mhdr); + mhdr.w = (word)((byte *)pmhdr - pheap->pbHeap); + mhdr.cbBlock = pmhdrInsertAfter->cbBlock; + WriteHeader(pheap, pmhdrInsertAfter, &mhdr); + } + + // Update the largest free block if this block is even larger + + if (pmhdr->cbBlock > pheap->cbLargestFreeBlock) + pheap->cbLargestFreeBlock = pmhdr->cbBlock; + } else { + // No empty space before, but have empty space after + // First merge block after + + MemHeader mhdr = *pmhdrFreeAfter; + mhdr.cbBlock += pmhdr->cbBlock; + WriteHeader(pheap, pmhdr, &mhdr); + + // Now update the pmhdrInsertAfter + + if (pmhdrInsertAfter == NULL) { + pheap->pmhdrFreeFirst = pmhdr; + } else { + mhdr = *pmhdrInsertAfter; + mhdr.w = (word)((byte *)pmhdr - pheap->pbHeap); + WriteHeader(pheap, pmhdrInsertAfter, &mhdr); + } + + // Update largest block if this new block is larger + + if (pmhdr->cbBlock > pheap->cbLargestFreeBlock) + pheap->cbLargestFreeBlock = pmhdr->cbBlock; + } + } else { + if (pmhdrFreeAfter == NULL) { + // Empty space before but none after + // No free list fix up required + + MemHeader mhdrBefore = *pmhdrFreeBefore; + mhdrBefore.cbBlock += pmhdr->cbBlock; + WriteHeader(pheap, pmhdrFreeBefore, &mhdrBefore); + + // Update largest block if this new block is larger + + if (mhdrBefore.cbBlock > pheap->cbLargestFreeBlock) + pheap->cbLargestFreeBlock = mhdrBefore.cbBlock; + } else { + // Empty space both behind and forwards + // Minor free list fix up required + + MemHeader mhdrBefore = *pmhdrFreeBefore; + mhdrBefore.w = pmhdrFreeAfter->w; + mhdrBefore.cbBlock += pmhdr->cbBlock + pmhdrFreeAfter->cbBlock; + WriteHeader(pheap, pmhdrFreeBefore, &mhdrBefore); + + // Update largest block if this new block is larger + + if (mhdrBefore.cbBlock > pheap->cbLargestFreeBlock) + pheap->cbLargestFreeBlock = mhdrBefore.cbBlock; + } + } + + // The heap isn't fully compacted now + + pheap->wf &= ~kfHeapFullyCompacted; + +#ifdef INCL_VALIDATEHEAP + Validate(); +#endif + +#ifdef INCL_FORCECOMPACTION + // Force compaction for validation purposes + // This will validate again after compaction. + + Compact(pheap, 0xffff, NULL, 0); +#endif +} + +void MemMgr::SetLocked(HMem hmem) +{ +#ifdef INCL_VALIDATEHEAP + ValidateHandle(hmem); +#endif + // Set the locked bit + + MemHeader *pmhdr = GetMemHeader(hmem); + Assert(!IsFree(pmhdr) && !IsLocked(pmhdr)); + MemHeader mhdr = *pmhdr; + mhdr.w |= kfMhdrLocked; + WriteHeader(GetHeap(hmem), pmhdr, &mhdr); +} + +void MemMgr::ClearLocked(HMem hmem) +{ +#ifdef INCL_VALIDATEHEAP + ValidateHandle(hmem); +#endif + // Clear the locked bit + + MemHeader *pmhdr = GetMemHeader(hmem); + Assert(!IsFree(pmhdr) && IsLocked(pmhdr)); + MemHeader mhdr = *pmhdr; + mhdr.w &= ~kfMhdrLocked; + WriteHeader(GetHeap(hmem), pmhdr, &mhdr); + + // This block can move again; the heap potentially isn't fully compacted now + + GetHeap(hmem)->wf &= ~kfHeapFullyCompacted; +} + +void *MemMgr::GetPtr(HMem hmem) +{ +#ifdef INCL_VALIDATEHEAP + ValidateHandle(hmem); +#endif + MemHeader *pmhdr = GetMemHeader(hmem); + return (void *)(pmhdr + 1); +} + +void MemMgr::WriteHeader(Heap *pheap, MemHeader *pmhdrDst, MemHeader *pmhdrSrc, word cb) +{ +#ifdef PIL + if (pheap->wf & kfHeapDbRam) { + DmWrite(pheap->pbHeap, (byte *)pmhdrDst - pheap->pbHeap, pmhdrSrc, cb); + } else { + memcpy(pmhdrDst, pmhdrSrc, cb); + } +#else + memcpy(pmhdrDst, pmhdrSrc, cb); +#endif +} + +void MemMgr::WriteHandle(HMem hmem, word ib, void *pvSrc, word cb) +{ +#ifdef INCL_VALIDATEHEAP + ValidateHandle(hmem); +#endif + MemHeader *pmhdr = GetMemHeader(hmem); + Assert(ib + cb <= pmhdr->cbBlock); +#ifdef PIL + Heap *pheap = GetHeap(hmem); + if (pheap->wf & kfHeapDbRam) { + DmWrite(pheap->pbHeap, (byte *)(pmhdr + 1) - pheap->pbHeap + ib, pvSrc, cb); + } else { + memcpy(((byte *)(pmhdr + 1)) + ib, pvSrc, cb); + } +#else + memcpy(((byte *)(pmhdr + 1)) + ib, pvSrc, cb); +#endif +} + +void *MemMgr::AllocPtr(word cb) +{ + // Alloc as a handle. Only reason we do this is so we know what heap this is stored in, + // which we need in order to allow calls to WritePtr() which needs to know if it is + // in dyn ram or db ram. + + HMem hmem = AllocHandle(cb + sizeof(hmem), kfHintWillLock); + if (hmem == NULL) + return NULL; + SetLocked(hmem); + WriteHandle(hmem, 0, &hmem, sizeof(hmem)); + return (void *)((HMem *)GetPtr(hmem) + 1); +} + +void MemMgr::FreePtr(void *pv) +{ + // Copy handle byte at a time to satisfy ARM + + HMem hmem; + byte *pbSrc = (byte *)((HMem *)pv - 1); + byte *pbDst = (byte *)&hmem; + *pbDst++ = *pbSrc++; + *pbDst++ = *pbSrc++; + *pbDst++ = *pbSrc++; + *pbDst++ = *pbSrc++; + ClearLocked(hmem); + FreeHandle(hmem); +} + +void MemMgr::WritePtr(void *pvDst, word ib, void *pvSrc, word cb) +{ + // Copy handle byte at a time to satisfy ARM + + HMem hmem; + byte *pbSrc = (byte *)((HMem *)pvDst - 1); + byte *pbDst = (byte *)&hmem; + *pbDst++ = *pbSrc++; + *pbDst++ = *pbSrc++; + *pbDst++ = *pbSrc++; + *pbDst++ = *pbSrc++; + WriteHandle(hmem, ib + sizeof(HMem), pvSrc, cb); +} + +#ifdef INCL_VALIDATEHEAP +void MemMgr::Validate() +{ + // Make sure all free objects are on the free list + + int iheap; + for (iheap = 0; iheap < m_cheap; iheap++) { + Heap *pheap = m_apheap[iheap]; + MemHeader *pmhdr = (MemHeader *)pheap->pbHeap; + MemHeader *pmhdrHeapEnd = (MemHeader *)(pheap->pbHeap + pheap->cbHeap); + for (; pmhdr < pmhdrHeapEnd; pmhdr = GetNext(pmhdr)) { + if (IsFree(pmhdr)) { + MemHeader *pmhdrT = pheap->pmhdrFreeFirst; + for (; pmhdrT != NULL; pmhdrT = GetNextFree(pmhdrT)) { + Assert(IsFree(pmhdrT)); + if (pmhdrT == pmhdr) + break; + } + Assert(pmhdrT != NULL); + } + } + Assert(pmhdr == pmhdrHeapEnd); + } + + // Ensure all alloced objects have HandleEntries that are valid + + for (iheap = 0; iheap < m_cheap; iheap++) { + Heap *pheap = m_apheap[iheap]; + MemHeader *pmhdr = (MemHeader *)pheap->pbHeap; + MemHeader *pmhdrHeapEnd = (MemHeader *)(pheap->pbHeap + pheap->cbHeap); + for (; pmhdr < pmhdrHeapEnd; pmhdr = GetNext(pmhdr)) { + if (!IsFree(pmhdr)) { + int ihtab = GetHandleTableIndex(pmhdr->w); + int ihe = GetHandleEntryIndex(pmhdr->w); + Assert(ihtab < m_chtab); + word *pw = GetHandleEntryPtr(pmhdr->w); + Assert(*pw == (word)((byte *)pmhdr - pheap->pbHeap)); + } + } + Assert(pmhdr == pmhdrHeapEnd); + } + + // Ensure largest count / bit is valid, and that total count is valid + + dword cbTotal = 0; + for (iheap = 0; iheap < m_cheap; iheap++) { + dword cbLargest = 0; + Heap *pheap = m_apheap[iheap]; + MemHeader *pmhdr = (MemHeader *)pheap->pbHeap; + MemHeader *pmhdrHeapEnd = (MemHeader *)(pheap->pbHeap + pheap->cbHeap); + for (; pmhdr < pmhdrHeapEnd; pmhdr = GetNext(pmhdr)) { + if (!IsFree(pmhdr)) + continue; + if (pmhdr->cbBlock > cbLargest) + cbLargest = pmhdr->cbBlock; + cbTotal += pmhdr->cbBlock; + } + Assert(pmhdr == pmhdrHeapEnd); + if (pheap->wf & kfHeapLargestFreeBlockSizeValid) + Assert(cbLargest == pheap->cbLargestFreeBlock); + Assert(cbLargest <= pheap->cbHeap); + } + Assert(cbTotal == m_cbTotalFreeBlocks); + + // Ensure the compacted bit is valid. Meaning there are not two contiguous free blocks, and that + // there are no free blocks followed by unlocked blocks. + + for (iheap = 0; iheap < m_cheap; iheap++) { + Heap *pheap = m_apheap[iheap]; + if (!(pheap->wf & kfHeapFullyCompacted)) + continue; + MemHeader *pmhdrLast = NULL; + MemHeader *pmhdr = (MemHeader *)pheap->pbHeap; + MemHeader *pmhdrHeapEnd = (MemHeader *)(pheap->pbHeap + pheap->cbHeap); + for (; pmhdr < pmhdrHeapEnd; pmhdr = GetNext(pmhdr)) { + if (pmhdrLast != NULL) { + if (IsFree(pmhdrLast) && IsFree(pmhdr)) + Assert(false); + if (IsFree(pmhdrLast) && !IsLocked(pmhdr)) + Assert(false); + } + } + Assert(pmhdr == pmhdrHeapEnd); + } + + ValidateHandleTableFreeEntries(); +} + +void MemMgr::ValidateHandle(HMem hmem) +{ + Assert(hmem != 0); + HMemStruct *phms = (HMemStruct *)&hmem; + Assert(phms->iheap < m_cheap); + Assert(GetHandleTableIndex(phms->her) < m_chtab); + MemHeader *pmhdr = GetMemHeader(hmem); + Heap *pheap = GetHeap(hmem); + Assert((byte *)pmhdr >= pheap->pbHeap && ((byte *)GetNext(pmhdr)) <= pheap->pbHeap + pheap->cbHeap); + Assert((pmhdr->w & ~(kfMhdrBusy | kfMhdrLocked)) == (phms->her & ~(kfMhdrBusy | kfMhdrLocked))); +} + +void MemMgr::ValidateHandleTableFreeEntries() +{ + for (int ihtab = 0; ihtab < m_chtab; ihtab++) { + // Make sure the free list is good + + bool afVisited[kcHandlesPerTable]; + memset(afVisited, 0, sizeof(afVisited)); + HandleTable *phtab = m_aphtab[ihtab]; + word iheFree = (word)phtab->iheFree; + while (iheFree != (word)-1) { + Assert(iheFree < ARRAYSIZE(phtab->ahe)); + Assert(!afVisited[iheFree]); + afVisited[iheFree] = true; + iheFree = phtab->ahe[iheFree]; + } + + // Make sure the rest of them could be valid offsets (even) + + for (int ihe = 0; ihe < ARRAYSIZE(phtab->ahe); ihe++) { + if (afVisited[ihe] == false) + Assert((phtab->ahe[ihe] & 1) == 0); + } + } + + +} +#endif + +} // namespace wi diff --git a/game/mempdbreader.cpp b/game/mempdbreader.cpp new file mode 100644 index 0000000..526aeb8 --- /dev/null +++ b/game/mempdbreader.cpp @@ -0,0 +1,245 @@ +#include "game/mempdbreader.h" + +namespace wi { + +MemPdbReader::MemPdbReader() +{ + m_cb = 0; + m_pb = NULL; + m_cRecs = 0; + m_cMapped = 0; + m_aphcRecordData = NULL; +} + +MemPdbReader::~MemPdbReader() +{ + Assert(m_pb == NULL); + Assert(m_cMapped == 0); + Assert(m_aphcRecordData == NULL); +} + +bool MemPdbReader::Open(char *pszFn) +{ + // Attempt to open + + Assert(m_pb == NULL); + FILE *pfil = fopen(pszFn, "rb"); + if (pfil == NULL) { + Trace("fopen(\"%s\", \"rb\"); failed", pszFn); + return false; + } + + // Read in the entire thing + + fseek(pfil, 0, SEEK_END); + m_cb = ftell(pfil); + fseek(pfil, 0, SEEK_SET); + + m_pb = new byte[m_cb]; + if (m_pb == NULL) { + fclose(pfil); + return false; + } + if (fread(m_pb, m_cb, 1, pfil) != 1) { + fclose(pfil); + return false; + } + fclose(pfil); + + // Alloc cache handle array + + DatabaseHdrType *phdr = (DatabaseHdrType *)m_pb; + m_cRecs = BigWord(phdr->recordList.numRecords); + m_aphcRecordData = new CacheHandle[m_cRecs]; + if (m_aphcRecordData == NULL) + return false; + memset(m_aphcRecordData, 0, sizeof(CacheHandle) * m_cRecs); + + return true; +} + +void MemPdbReader::Close() +{ + Assert(m_cMapped == 0); + Assert(m_pb != NULL); + delete m_pb; + m_pb = NULL; + delete m_aphcRecordData; + m_aphcRecordData = NULL; +} + +bool MemPdbReader::GetRecordSize(word nRec, word *pcb) +{ + CompressionHeader coh; + if (GetCompressionHeader(nRec, &coh) == 0) + return false; + *pcb = BigWord(coh.cbUncompressed); + return true; +} + +bool MemPdbReader::GetRecordEntry(word nRec, int cRec, RecordEntryType *prece, dword *pcb) +{ + // Valid? + + Assert(cRec != 0); + Assert(nRec + cRec <= m_cRecs); + if (nRec + cRec > m_cRecs) + return false; + + // Seek to record header offset + + DatabaseHdrType hdr; + dword off = (sizeof(hdr) - sizeof(hdr.recordList.firstEntry)) + nRec * sizeof(RecordEntryType); + + // End of records case? + + if (nRec + cRec == m_cRecs) { + memcpy(prece, &m_pb[off], sizeof(RecordEntryType) * cRec); + if (pcb != NULL) + *pcb = m_cb - BigDword(prece[cRec - 1].localChunkID); + return true; + } + + // 1 record case and not end of records + + if (cRec == 1) { + RecordEntryType arece[2]; + memcpy(arece, &m_pb[off], sizeof(RecordEntryType) * 2); + *prece = arece[0]; + if (pcb != NULL) + *pcb = BigDword(arece[1].localChunkID) - BigDword(arece[0].localChunkID); + return true; + } + + // N record case and not end of records + + memcpy(prece, &m_pb[off], sizeof(RecordEntryType) * cRec); + if (pcb != NULL) + *pcb = BigDword(prece[cRec - 1].localChunkID) - BigDword(prece[0].localChunkID); + return true; +} + +dword MemPdbReader::GetCompressionHeader(word nRec, CompressionHeader *pcoh) +{ + dword cbRecord; + RecordEntryType rece; + if (!GetRecordEntry(nRec, 1, &rece, &cbRecord)) + return 0; + memcpy(pcoh, &m_pb[BigDword(rece.localChunkID)], kcbCompressionHeader); + return BigDword(rece.localChunkID) + kcbCompressionHeader; +} + +bool MemPdbReader::ReadRecord(word nRec, word n, word cb, void *pv) +{ + // See if the data is cached + + Assert(nRec < m_cRecs); + CacheHandle hc = m_aphcRecordData[nRec]; + if (!gcam.IsValid(hc)) { + // Not cached, get the compression header + + CompressionHeader coh; + dword offBytes = GetCompressionHeader(nRec, &coh); + if (offBytes == 0) + return false; + + // If not compressed, read data straight from file + + if (!BigWord(coh.fCompressed)) { + if (n + cb > BigWord(coh.cbUncompressed)) + return false; + memcpy(pv, &m_pb[offBytes + n], cb); + return true; + } + + // It is compressed, but not cached. + + hc = gcam.NewObject(NULL, BigWord(coh.cbUncompressed)); + if (hc == 0) + return false; + m_aphcRecordData[nRec] = hc; + DecompressToCache(&m_pb[offBytes], hc, &coh); + } + + // Read from cached data + + word cbUncompressed = gcam.GetSize(hc); + byte *pbUncompressed = (byte *)gcam.GetPtr(hc); + if (n + cb > cbUncompressed) + return false; + memcpy(pv, pbUncompressed + n, cb); + return true; +} + +byte *MemPdbReader::MapRecord(word nRec, dword *pdwCookie, word *pcb) +{ + // First see if the record is cached + + Assert(m_pb != NULL); + Assert(nRec < m_cRecs); + CacheHandle hc = m_aphcRecordData[nRec]; + if (!gcam.IsValid(hc)) { + // Not cached, get the compression header + + CompressionHeader coh; + dword offBytes = GetCompressionHeader(nRec, &coh); + if (offBytes == 0) + return NULL; + + // If it is not compressed, we can't just point to it; it may + // be unaligned + + if (!BigWord(coh.fCompressed)) { + word cbT = BigWord(coh.cbUncompressed); + byte *pbT = new byte[cbT]; + if (pbT == NULL) + return NULL; + memcpy(pbT, &m_pb[offBytes], cbT); + m_cMapped++; + *pdwCookie = (dword)pbT; + *pcb = cbT; + return pbT; + } + + // It is compressed, decompress it and lock it + + hc = gcam.NewObject(NULL, BigWord(coh.cbUncompressed), kfHintWillLock); + if (hc == 0) + return NULL; + m_aphcRecordData[nRec] = hc; + DecompressToCache(&m_pb[offBytes], hc, &coh); + } + + // We have cache access to this record and it is locked. Return a pointer. + + byte *pbUncompressed = (byte *)gcam.Lock(hc); + if (pcb != NULL) + *pcb = gcam.GetSize(hc); + *pdwCookie = 0; + m_cMapped++; + return pbUncompressed; +} + +void MemPdbReader::UnmapRecord(word nRec, dword dwCookie) +{ + Assert(m_pb != NULL); + Assert(nRec < m_cRecs); + Assert(m_cMapped > 0); + m_cMapped--; + if (dwCookie == 0) { + // Pointed to cache object; unlock it + + if (m_aphcRecordData[nRec] != 0) { + // It was compressed and cached so unlock the cache handle + + gcam.Unlock(m_aphcRecordData[nRec]); + } + } else { + // Pointed to alloced object; unlock it + + byte *pb = (byte *)dwCookie; + delete pb; + } +} + +} // namespace wi diff --git a/game/mempdbreader.h b/game/mempdbreader.h new file mode 100644 index 0000000..66308cf --- /dev/null +++ b/game/mempdbreader.h @@ -0,0 +1,39 @@ +#ifndef __MEMPDBREADER_H__ +#define __MEMPDBREADER_H__ + +#include "game/ht.h" +#include "mpshared/pdbreader.h" + +namespace wi { + +// +// Reads pdbs from memory +// + +class MemPdbReader : public PdbReader +{ +public: + MemPdbReader() secPackFile; + ~MemPdbReader() secPackFile; + bool Open(char *pszFn) secPackFile; + + virtual void Close() secPackFile; + virtual bool GetRecordSize(word nRec, word *pcb) secPackFile; + virtual bool ReadRecord(word nRec, word n, word cb, void *pv) secPackFile; + virtual byte *MapRecord(word nRec, dword *pdwCookie, word *pcb = NULL) secPackFile; + virtual void UnmapRecord(word nRec, dword dwCookie) secPackFile; + +private: + bool GetRecordEntry(word nRec, int cRec, RecordEntryType *prece, dword *pcb) secPackFile; + dword GetCompressionHeader(word nRec, CompressionHeader *pcoh) secPackFile; + + dword m_cb; + byte *m_pb; + word m_cRecs; + word m_cMapped; + CacheHandle *m_aphcRecordData; +}; + +} // namespace wi + +#endif // __MEMPDBREADER_H__ diff --git a/game/misc.cpp b/game/misc.cpp new file mode 100644 index 0000000..c161a41 --- /dev/null +++ b/game/misc.cpp @@ -0,0 +1,1859 @@ +#include "ht.h" +#include "strings.h" + +namespace wi { + +// GCCBUG: We must not have this inline because when Gcc inlines the call to +// the base classes' constructor (Timer::Timer) it gets the reference wrong. + +TimeoutTimer::TimeoutTimer() +{ + m_ptimo = NULL; +} + +// +// CommandQueue implementation +// + +CommandQueue::CommandQueue() +{ + m_amsg = NULL; + m_cmsg = 0; +} + +CommandQueue::~CommandQueue() +{ + Assert(m_amsg == NULL); +} + +bool CommandQueue::Init(int cmsgMax) +{ + m_cmsg = 0; + m_cmsgMax = cmsgMax; + Assert(m_amsg == NULL); + m_amsg = (Message *)gmmgr.AllocPtr(sizeof(Message) * m_cmsgMax); + return m_amsg != NULL; +} + +void CommandQueue::Exit() +{ + if (m_amsg != NULL) { + gmmgr.FreePtr(m_amsg); + m_amsg = NULL; + } +} + +#define knVerCommandQueueState 1 +bool CommandQueue::LoadState(Stream *pstm) +{ + // Version check + + byte nVer = pstm->ReadByte(); + if (nVer != knVerCommandQueueState) + return false; + + // Read msg count + + int cmsgs = pstm->ReadWord(); + + // Read in messages + + while (cmsgs-- != 0) { + Message msg; + if (pstm->Read(&msg, sizeof(msg)) == 0) + return false; + Enqueue(&msg); + } + + return pstm->IsSuccess(); +} + +bool CommandQueue::SaveState(Stream *pstm) +{ + // Save version + + pstm->WriteByte(knVerCommandQueueState); + + // Save count of messages + + pstm->WriteWord(m_cmsg); + + // Save messages + + for (int nmsg = 0; nmsg < m_cmsg; nmsg++) + pstm->Write(&m_amsg[nmsg], sizeof(m_amsg[0])); + + return pstm->IsSuccess(); +} + +void CommandQueue::Enqueue(MessageId mid, StateMachineId smidReceiver) +{ + Message msg; + memset(&msg, 0, sizeof(msg)); + msg.mid = mid; + msg.smidReceiver = smidReceiver; + SetEntry(&msg); +} + +void CommandQueue::Enqueue(Message *pmsg) +{ + pmsg->tDelivery = 0; + SetEntry(pmsg); +} + +void CommandQueue::SetEntry(Message *pmsg) +{ + if (m_cmsg < m_cmsgMax) { + gmmgr.WritePtr(m_amsg, m_cmsg * sizeof(Message), pmsg, sizeof(Message)); + m_cmsg++; + } +} + +bool IsTileFree(TCoord tx, TCoord ty, byte bf, Gob **ppgob) +{ + // Gob to return if there is one + + if (ppgob != NULL) + *ppgob = NULL; + + // TCoord needs to be on the map + + Size sizMap; + gsim.GetLevel()->GetTileMap()->GetTCoordMapSize(&sizMap); + if (tx < 0 || tx >= sizMap.cx || ty < 0 || ty >= sizMap.cy) + return false; + + // Needs to be free of gobs + + if (bf & (kbfMobileUnit | kbfStructure)) { + for (Gid gid = ggobm.GetFirstGid(tx, ty); gid != kgidNull; gid = ggobm.GetNextGid(gid)) { + Gob *pgob = ggobm.GetGob(gid); + if (pgob != NULL) { + if (ppgob != NULL) + *ppgob = pgob; + return false; + } + } + } + + // No gobs can be transitioning into this tile + + if (bf & kbfMobileUnit) { + Gob *pgob = MobileUnitGob::AnyTransitionsIntoTile(tx, ty, NULL); + if (pgob != NULL) { + if (ppgob != NULL) + *ppgob = pgob; + return false; + } + } + + // Tile can't be occupied + + if (gsim.GetLevel()->GetTerrainMap()->IsBlocked(tx, ty, bf)) + return false; + + // Looks free! + + return true; +} + +void BringInBounds(WCoord *pwx, WCoord *pwy) +{ + Size sizMap; + gsim.GetLevel()->GetTileMap()->GetTCoordMapSize(&sizMap); + WCoord cwxMap = WcFromTc(sizMap.cx); + WCoord cwyMap = WcFromTc(sizMap.cy); + if (*pwx < 0) + *pwx = 0; + else if (*pwx >= cwxMap) + *pwx = cwxMap - 1; + if (*pwy < 0) + *pwy = 0; + else if (*pwy >= cwyMap) + *pwy = cwyMap - 1; +} + +// +// Rect implementation +// + +bool Rect::Intersect(Rect *prcSrc1, Rect *prcSrc2) +{ + left = _max(prcSrc1->left, prcSrc2->left); + right = _min(prcSrc1->right, prcSrc2->right); + + // Check for empty rect + + if (left < right) { + + top = _max(prcSrc1->top, prcSrc2->top); + bottom = _min(prcSrc1->bottom, prcSrc2->bottom); + + // Check for empty rect + + if (top < bottom) + return true; + } + + SetEmpty(); + return false; +} + +void Rect::Set(Point pt1, Point pt2) +{ + if (pt1.x < pt2.x) { + left = pt1.x; + right = pt2.x; + } else { + left = pt2.x; + right = pt1.x; + } + if (pt1.y < pt2.y) { + top = pt1.y; + bottom = pt2.y; + } else { + top = pt2.y; + bottom = pt1.y; + } +} + +bool Rect::Subtract(Rect *prcSrc1, Rect *prcSrc2) +{ + // prcSrc1 - prcSrc2 + + int xLeft = prcSrc1->left; + int yTop = prcSrc1->top; + int xRight = prcSrc1->right; + int yBottom = prcSrc1->bottom; + + // Vert edges + + if (prcSrc2->top <= prcSrc1->top && prcSrc2->bottom >= prcSrc1->bottom) { + // left edge + + if (prcSrc2->left <= prcSrc1->left && prcSrc2->right > prcSrc1->left) + xLeft = prcSrc2->right; + + // right edge + + if (prcSrc2->left < prcSrc1->right && prcSrc2->right >= prcSrc1->right) + xRight = prcSrc2->left; + } + + // Horz edges + + if (prcSrc2->left <= prcSrc1->left && prcSrc2->right >= prcSrc1->right) { + // top edge + + if (prcSrc2->top <= prcSrc1->top && prcSrc2->bottom > prcSrc1->top) + yTop = prcSrc2->bottom; + + // bottom edge + + if (prcSrc2->top < prcSrc1->bottom && prcSrc2->bottom >= prcSrc1->bottom) + yBottom = prcSrc2->top; + } + + Set(xLeft, yTop, xRight, yBottom); + return !IsEmpty(); +} + +void Rect::Add(Rect *prcSrc1, Rect *prcSrc2) +{ + if (prcSrc1->IsEmpty()) { + *this = *prcSrc2; + return; + } + if (prcSrc2->IsEmpty()) { + *this = *prcSrc1; + return; + } + + // prcSrc1 + prcSrc2 (not Union) + + int xLeft = prcSrc1->left; + int yTop = prcSrc1->top; + int xRight = prcSrc1->right; + int yBottom = prcSrc1->bottom; + + // Vert edges + + if (prcSrc2->top <= prcSrc1->top && prcSrc2->bottom >= prcSrc1->bottom) { + // left edge + + if (prcSrc2->left < prcSrc1->left && prcSrc2->right >= prcSrc1->left) + xLeft = prcSrc2->left; + + // right edge + + if (prcSrc2->left <= prcSrc1->right && prcSrc2->right > prcSrc1->right) + xRight = prcSrc2->right; + } + + // Horz edges + + if (prcSrc2->left <= prcSrc1->left && prcSrc2->right >= prcSrc1->right) { + // top edge + + if (prcSrc2->top < prcSrc1->top && prcSrc2->bottom >= prcSrc1->top) + yTop = prcSrc2->top; + + // bottom edge + + if (prcSrc2->top <= prcSrc1->bottom && prcSrc2->bottom > prcSrc1->bottom) + yBottom = prcSrc2->bottom; + } + + Set(xLeft, yTop, xRight, yBottom); +} + +void Rect::FromWorldRect(WRect *pwrc) +{ + left = PcFromWc(pwrc->left); + right = PcFromWc(pwrc->right); + top = PcFromWc(pwrc->top); + bottom = PcFromWc(pwrc->bottom); +} + +void Rect::FromTileRect(TRect *ptrc) +{ + left = PcFromTc(ptrc->left); + right = PcFromTc(ptrc->right); + top = PcFromTc(ptrc->top); + bottom = PcFromTc(ptrc->bottom); +} + +void Rect::Union(Rect *prc) +{ + if (prc->left >= prc->right || prc->top >= prc->bottom) + return; + if (prc->left < left) + left = prc->left; + if (prc->top < top) + top = prc->top; + if (prc->right > right) + right = prc->right; + if (prc->bottom > bottom) + bottom = prc->bottom; +} + +bool Rect::Equal(Rect *prc) +{ + if (left == prc->left && top == prc->top && right == prc->right && bottom == prc->bottom) + return true; + return false; +} + +void Rect::GetCenter(Point *ppt) +{ + ppt->x = left + (right - left) / 2; + ppt->y = top + (bottom - top) / 2; +} + +int Rect::GetDistance(int x, int y) +{ + // Use isqrt. If too expensive, could change this to + // GetSquaredDistance and change callers. + + if (y >= bottom) { + if (x >= right) { + return isqrt(x - right, y - bottom); + } + if (x >= left) { + return y - bottom; + } + return isqrt(left - x, y - bottom); + } else if (y >= top) { + if (x >= right) { + return x - right; + } + if (x >= left) { + return 0; + } + return left - x; + } else { + if (x >= right) { + return isqrt(x - right, top - y); + } + if (x >= left) { + return top - y; + } + return isqrt(left - x, top - y); + } +} + +// +// WRect implementation +// + +void WRect::Set(WPoint wpt1, WPoint wpt2) +{ + if (wpt1.wx < wpt2.wx) { + left = wpt1.wx; + right = wpt2.wx; + } else { + left = wpt2.wx; + right = wpt1.wx; + } + if (wpt1.wy < wpt2.wy) { + top = wpt1.wy; + bottom = wpt2.wy; + } else { + top = wpt2.wy; + bottom = wpt1.wy; + } +} + +#if 0 // UNUSED +void WRect::FromPixelRect(Rect *prc) +{ + left = WcFromPc(prc->left); + right = WcFromPc(prc->right); + top = WcFromPc(prc->top); + bottom = WcFromPc(prc->bottom); +} +#endif + +void WRect::FromTileRect(TRect *ptrc) +{ + left = WcFromTc(ptrc->left); + right = WcFromTc(ptrc->right); + top = WcFromTc(ptrc->top); + bottom = WcFromTc(ptrc->bottom); +} + +// fgets + +char *_fgets(char *psz, int cch, File *pfil) +{ + // Read into the buffer + + dword cbRead = gpakr.fread(psz, 1, cch, pfil); + if (cbRead == 0) + return NULL; + psz[cbRead] = 0; + + // Look for a newline, and end there + + int cchOld = strlen(psz); + int nchNew; + for (nchNew = 0; nchNew < cchOld; nchNew++) { + if (psz[nchNew] == '\n') { + nchNew++; + psz[nchNew] = 0; + break; + } + } + + // Set the file pointer backwards + + int cchNew = nchNew; + gpakr.fseek(pfil, cchNew - cchOld, SEEK_CUR); + return psz; +} + +// String uppercasing function because PalmOS doesn't have one + +void HtStrupr(char *psz) +{ + while (true) { + char ch = *psz; + if (ch == 0) + break; + if (ch >= 'a' && ch <= 'z') + *psz = ch + 'A' - 'a'; + psz++; + } +} + +// Bring up an Hostile Takeover-style message box with a title and a formatted message + +bool HtMessageBox(word wf, const char *pszTitle, const char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + char szBody[512]; + vsprintf(szBody, pszFormat, va); + va_end(va); + + return HtMessageBox(kidfMessageBox, wf, pszTitle, szBody); +} + +bool HtMessageBox(word idf, word wf, const char *pszTitle, const char *pszBody) +{ + DialogForm *pfrm = CreateHtMessageBox(idf, wf, pszTitle, pszBody); + + if (!(wf & kfMbKeepTimersEnabled)) { + gtimm.Enable(false); + } + + // Bring up form + + bool f = pfrm->DoModal(); + delete pfrm; + + if (!(wf & kfMbKeepTimersEnabled)) { + gtimm.Enable(true); + } + + return f; +} + +DialogForm *CreateHtMessageBox(word idf, word wf, const char *pszTitle, + const char *pszBody) { + DialogForm *pfrm = (DialogForm *)gpmfrmm->LoadForm(gpiniForms, idf, new DialogForm()); + Assert(pfrm != NULL, "Unable to load HtMessageBox form"); + + if (wf & kfMbWhiteBorder) + pfrm->SetBorderColorIndex(kiclrWhite); + + pfrm->SetTitleColor(GetColor(kiclrRed)); + pfrm->SetBackgroundColorIndex(kiclrShadow2x); + + LabelControl *plbl = (LabelControl *)pfrm->GetControlPtr(kidcTitle); + plbl->SetText(pszTitle); + + // The label self-adjusts its height. Use that to format the messagebox as well + + plbl = (LabelControl *)pfrm->GetControlPtr(kidcMessage); + Rect rcOld; + plbl->GetRect(&rcOld); + plbl->SetText(pszBody); + Rect rcNew; + plbl->GetRect(&rcNew); + + // Move the buttons + + ButtonControl *pbtn = (ButtonControl *)pfrm->GetControlPtr(kidcOk); + Rect rcButton; + pbtn->GetRect(&rcButton); + rcButton.Offset(0, rcNew.Height() - rcOld.Height()); + pbtn->SetRect(&rcButton); + + pbtn = (ButtonControl *)pfrm->GetControlPtr(kidcCancel); + if (pbtn != NULL) { + pbtn->GetRect(&rcButton); + rcButton.Offset(0, rcNew.Height() - rcOld.Height()); + pbtn->SetRect(&rcButton); + } + + // Resize the form + + Rect rcForm; + pfrm->GetRect(&rcForm); + rcForm.bottom += rcNew.Height() - rcOld.Height(); + + // Now center the form again + + DibBitmap *pbm = pfrm->GetFormMgr()->GetDib(); + Size siz; + pbm->GetSize(&siz); + int yNew = (siz.cy - rcForm.Height()) / 2; + rcForm.Offset(0, yNew - rcForm.top); + pfrm->SetRect(&rcForm); + + // Clear if asked + + if (wf & kfMbClearDib) { + pfrm->SetClearDibFlag(); + } + + return pfrm; +} + +#ifdef STATUS_LINE +void Status(const char *psz) +{ + // Display::DrawText draws directly to the screen, no buffering + + if (gpdisp != NULL) { + gpdisp->DrawText(psz, 0, -1, kfDtClearLine); + Trace((char *)psz); + } +} +#endif + +// Returns a pseudo-random number 0 through 32767. +// This random number generator must be kept in sync across all +// machines in a multiplayer game. Use it in response to game +// events that are seen by all players. + +static long glSeed = 1L; + +#ifdef DEBUG +int _GetRandom(char *pszFile, int nLine) +#else +int _GetRandom() +#endif +{ +#ifdef DEBUG +// MpTrace("- GetRandom() [%s, %d]", pszFile, nLine); +#endif + return ((glSeed = glSeed * 214013L + 2531011L) >> 16) & 0x7fff; +} + +void SetRandomSeed(unsigned long nSeed) +{ + glSeed = (long)nSeed; +} + +unsigned long GetRandomSeed() +{ + return glSeed; +} + +// This random number generator does not need to be in sync across +// all machines in a multiplayer game. Use it when you want a random +// number that is only relevent to the local player (e.g., picking +// a random voice response for a unit). + +static long glAsyncSeed = 1L; + +int GetAsyncRandom() +{ + return ((glAsyncSeed = glAsyncSeed * 214013L + 2531011L) >> 16) & 0x7fff; +} + +Direction TurnToward(Direction dirTo, Direction dirFrom) +{ + int d = dirTo - dirFrom; + if (d == 0) + return dirTo; + + if (d < -4) + d = 1; + else if (d > 4) + d = -1; + if (d < 0) + dirFrom--; + else + dirFrom++; + return ((unsigned int)dirFrom) % 8; +} + +int isqrt(int val1, int val2) +{ + return (int)isqrt((dword)val1 * (dword)val1 + (dword)val2 * (dword)val2); +} + +unsigned int isqrt(unsigned long val) +{ + unsigned long temp, g=0; + + if (val >= 0x40000000) { + g = 0x8000; + val -= 0x40000000; + } + +#define INNER_MBGSQRT(s) \ + temp = (g << (s)) + (1L << ((s) * 2L - 2L)); \ + if (val >= temp) { \ + g += 1L << ((s)-1L); \ + val -= temp; \ + } + + INNER_MBGSQRT (15) + INNER_MBGSQRT (14) + INNER_MBGSQRT (13) + INNER_MBGSQRT (12) + INNER_MBGSQRT (11) + INNER_MBGSQRT (10) + INNER_MBGSQRT ( 9) + INNER_MBGSQRT ( 8) + INNER_MBGSQRT ( 7) + INNER_MBGSQRT ( 6) + INNER_MBGSQRT ( 5) + INNER_MBGSQRT ( 4) + INNER_MBGSQRT ( 3) + INNER_MBGSQRT ( 2) + +#undef INNER_MBGSQRT + + temp = g+g+1; + if (val >= temp) g++; + return g; +} + +// LineIterator implementation +// OPT: expensive! Lots of multiplies, divides and that fixed-point square root + +void WLineIterator::Init(WCoord wx1, WCoord wy1, WCoord wx2, WCoord wy2, int nIncr) +{ + Assert(nIncr != 0); + + m_wx = wx1; + m_wy = wy1; + + // Calc the number of nSteps needed to step the line: + // cSteps = sqrt(dx^2 + dy^2) / nStep + + WCoord wdx = wx2 - wx1; + WCoord wdy = wy2 - wy1; + int nLen = isqrt(((long)wdx * (long)wdx) + ((long)wdy * (long)wdy)); + + // Handle case where destination is less than one step away from the start. + + if (nLen < nIncr) { + m_cStepsRemaining = 0; + return; + } + + int cSteps = nLen / nIncr; + Assert(cSteps != 0); + + m_wdx = wdx / cSteps; + m_wdy = wdy / cSteps; + m_cStepsRemaining = cSteps; + +#if 0 // doing without for now + // Some fraction of a step is lost when we convert cSteps to an integer. + // Add this fractional step to m_fx, m_fy to start things off so the + // final step will leave us at the desired destination. + + fix fxFrac = fracfx(cfxSteps); + m_fx = addfx(m_fx, (fix)mulfx(m_fdx, fxFrac)); + m_fy = addfx(m_fy, (fix)mulfx(m_fdy, fxFrac)); +#endif +} + +// +// Palette and color helpers +// + +const short SCALEFACTOR = 128; +const word SCALEMAX = 256 * (word)SCALEFACTOR; + +// nHueOffset is in the range from -100 to +100 +// nLumOffset is in the range from -100 to +100 +// nSatMultiplier is in the range from -100 to +100 and is scaled non-linearly +// to cover the desired (finely tuned) range. + +void SetHslAdjustedPalette(Palette *ppal, short nHueOffset, short nSatMultiplier, short nLumOffset) +{ + // Incorporate hardware-correcting values + + short nHueT, nSatT, nLumT; + gpdisp->GetHslAdjustments(&nHueT, &nSatT, &nLumT); + nHueOffset += nHueT; + if (nHueT < -100) + nHueT = -100; + else if (nHueT > 100) + nHueT = 100; + nSatMultiplier += nSatT; + if (nSatMultiplier < -100) + nSatMultiplier = -100; + else if (nSatMultiplier > 100) + nSatMultiplier = 100; + nLumOffset += nLumT; + if (nLumOffset < -100) + nLumOffset = -100; + else if (nLumOffset > 100) + nLumOffset = 100; + + short nHueAdd = (nHueOffset * (3 * SCALEFACTOR)) / 100; + short nLumAdd = (short)((nLumOffset * 10000L) / 100); + + // maps +/-100 to 128-384 + long nT = 128 + (((nSatMultiplier + 100L) * 256) / 200); + + // non-linearly transforms to the range 0-1024 (actually 64-576) + short nSatMult = (short)((nT * nT) / 256); + + int cEntries = BigWord(ppal->cEntries); + Palette *ppalMod = (Palette *)new byte[sizeof(word) + (cEntries * sizeof(ppal->argb))]; + ppalMod->cEntries = ppal->cEntries; + + word nH, nS, nL; + + for (int i = 0; i < cEntries; i++) { + RgbToHsl(ppal->argb[i][0], ppal->argb[i][1], ppal->argb[i][2], &nH, &nS, &nL); + + int nT = nH + nHueAdd; + if (nT >= 6 * SCALEFACTOR) + nT -= 6 * SCALEFACTOR; + else if (nT < 0) + nT += 6 * SCALEFACTOR; + + long lS = (nS * (long)nSatMult) / 256L; + if (lS > SCALEMAX) + lS = (long)SCALEMAX; + + long lL = nL + (long)nLumAdd; + if (lL > SCALEMAX) + lL = (long)SCALEMAX; + else if (lL < 0) + lL = 0; + + HslToRgb((word)nT, (word)lS, (word)lL, &ppalMod->argb[i][0], &ppalMod->argb[i][1], &ppalMod->argb[i][2]); + } + + gpdisp->SetPalette(ppalMod); + delete ppalMod; +} + +// Takes byte-sized RGB values in the range from 0-255 and returns +// word-sized HSL values in the range from 0-32768 (H, S, L). +// H is special and ranges from 0 to SCALEFACTOR (128) * 6 + +void RgbToHsl(byte bR, byte bG, byte bB, word *pnH, word *pnS, word *pnL) +{ + word nR = bR * SCALEFACTOR; + word nG = bG * SCALEFACTOR; + word nB = bB * SCALEFACTOR; + + word nMax = _max(nR, nG); + nMax = _max(nMax, nB); + + word nMin = _min(nR, nG); + nMin = _min(nMin, nB); + + *pnH = 0; + *pnS = 0; + *pnL = (word)((nMin + nMax) / 2); + if (*pnL == 0) + return; + + word nDelta = nMax - nMin; + *pnS = nDelta; + if (nDelta == 0) + return; + + word n; + if (*pnL < SCALEMAX / 2) + n = nMax + nMin; + else + n = (word)(((long)SCALEMAX * 2) - nMax - nMin); + *pnS = (word)((nDelta * (long)SCALEMAX) / n); + + word nDeltaDiv = nDelta / SCALEFACTOR; + + word r2 = (nMax - nR) / nDeltaDiv; + word g2 = (nMax - nG) / nDeltaDiv; + word b2 = (nMax - nB) / nDeltaDiv; + + if (nR == nMax) { + if (nG == nMin) { + if (b2 == SCALEFACTOR) + *pnH = 0; + else + *pnH = (5 * SCALEFACTOR) + b2; + } else { + *pnH = (1 * SCALEFACTOR) - g2; + } + } else if (nG == nMax) { + *pnH = (nB == nMin ? (1 * SCALEFACTOR) + r2 : (3 * SCALEFACTOR) - b2); + } else { + *pnH = (nR == nMin ? (3 * SCALEFACTOR) + g2 : (5 * SCALEFACTOR) - r2); + } +} + +void HslToRgb(word nH, word nS, word nL, byte *pbR, byte *pbG, byte *pbB) +{ + word v; + + if (nL <= SCALEMAX / 2) { + Assert((nL * (SCALEMAX + (long)nS)) / SCALEMAX < 65536); + v = (word)((nL * (SCALEMAX + (long)nS)) / SCALEMAX); + } else { + Assert((nL + nS - ((nL * (long)nS) / SCALEMAX)) < 65536); + v = (word)(nL + nS - ((nL * (long)nS) / SCALEMAX)); + } + + if (v == 0) { + *pbR = *pbG = *pbB = 0; + return; + } + + word m = nL + nL - v; + word sv = (word)(((v - m) * (long)SCALEMAX) / v); + + word sextant = nH / SCALEFACTOR; + word fract = nH & (SCALEFACTOR - 1); + word vsf = (word)((((v * (long)sv) / SCALEMAX) * fract) / SCALEFACTOR); + word mid1 = m + vsf; + word mid2 = v - vsf; + + // UNDONE: remove + if (v == SCALEMAX) + v = SCALEMAX - 1; + if (m == SCALEMAX) + m = SCALEMAX - 1; + if (mid1 == SCALEMAX) + mid1 = SCALEMAX - 1; + if (mid2 == SCALEMAX) + mid2 = SCALEMAX - 1; + + Assert(v / SCALEFACTOR < 256); + Assert(m / SCALEFACTOR < 256); + Assert(mid1 / SCALEFACTOR < 256); + Assert(mid2 / SCALEFACTOR < 256); + + switch (sextant) { + case 0: *pbR = v / SCALEFACTOR; *pbG = mid1 / SCALEFACTOR; *pbB = m / SCALEFACTOR; break; + case 1: *pbR = mid2 / SCALEFACTOR; *pbG = v / SCALEFACTOR; *pbB = m / SCALEFACTOR; break; + case 2: *pbR = m / SCALEFACTOR; *pbG = v / SCALEFACTOR; *pbB = mid1 / SCALEFACTOR; break; + case 3: *pbR = m / SCALEFACTOR; *pbG = mid2 / SCALEFACTOR; *pbB = v / SCALEFACTOR; break; + case 4: *pbR = mid1 / SCALEFACTOR; *pbG = m / SCALEFACTOR; *pbB = v / SCALEFACTOR; break; + case 5: *pbR = v / SCALEFACTOR; *pbG = m / SCALEFACTOR; *pbB = mid2 / SCALEFACTOR; break; + } +} + +int CompareDates(Date *pdate1, Date *pdate2) +{ + // Check years + + if (pdate1->nYear < pdate2->nYear) + return -1; + if (pdate1->nYear > pdate2->nYear) + return 1; + + // Years are equal. Check months. + + if (pdate1->nMonth < pdate2->nMonth) + return -1; + if (pdate1->nMonth > pdate2->nMonth) + return 1; + + // Months are equal. Check days + + if (pdate1->nDay < pdate2->nDay) + return -1; + if (pdate1->nDay > pdate2->nDay) + return 1; + + // All equal + + return 0; +} + +// CheckBetaTimeout +// false means beta has timed out +// true means beta has not timed out + +#ifdef BETA_TIMEOUT +#define knMonthBetaStart 10 +#define knDayBetaStart 1 +#define knYearBetaStart 2009 + +#define knMonthBetaEnd 12 +#define knDayBetaEnd 1 +#define knYearBetaEnd 2009 + +bool CheckBetaTimeout() +{ + // Get current date + + Date date; + HostGetCurrentDate(&date); + + // Get prefs. If we have prefs, get the last date run from there + + Date dateLast; + dateLast.nYear = gprefsInit.nYearLastRun; + dateLast.nMonth = gprefsInit.nMonthLastRun; + dateLast.nDay = gprefsInit.nDayLastRun; + + // See if the date has been set backwards to trick us :) + + if (CompareDates(&date, &dateLast) < 0) { +TimeOut: + HtMessageBox(kfMbWhiteBorder | kfMbClearDib, "Test Release", "Thank you for playing Hostile Takeover. Unfortunately this test release has expired! Please contact us at:\n\nhttp://www.spiffcode.com"); + return false; + } + + // See if the current date is ahead of the demo start date + + Date dateStart; + dateStart.nYear = knYearBetaStart; + dateStart.nMonth = knMonthBetaStart; + dateStart.nDay = knDayBetaStart; + if (CompareDates(&date, &dateStart) < 0) + goto TimeOut; + + // See if the current date is before the demo end date + + Date dateEnd; + dateEnd.nYear = knYearBetaEnd; + dateEnd.nMonth = knMonthBetaEnd; + dateEnd.nDay = knDayBetaEnd; + if (CompareDates(&date, &dateEnd) > 0) + goto TimeOut; + + // All looks ok. Save the prefs which'll save the current date + + ggame.SavePreferences(); + + return true; +} +#endif + +// Hardwired sfx categories + +// Infantry destroyed + +Sfx gasfxInfantryDestroyed[] = { + ksfxInfantryDestroyed0, + ksfxInfantryDestroyed1, + ksfxInfantryDestroyed2, + ksfxInfantryDestroyed3, + ksfxInfantryDestroyed4, +}; + +// Vehicle destroyed + +Sfx gasfxVehicleDestroyed[] = { + ksfxVehicleDestroyed, +}; + +// Male01 + +Sfx gasfxMale01Select[] = { + ksfxMale01Select0, + ksfxMale01Select1, + ksfxMale01Select2, + ksfxMale01Select3, +}; + +Sfx gasfxMale01Move[] = { + ksfxMale01Move0, + ksfxMale01Move1, + ksfxMale01Move2, + ksfxMale01Move3, +}; + +Sfx gasfxMale01Attack[] = { + ksfxMale01Attack0, + ksfxMale01Attack1, + ksfxMale01Attack2, + ksfxMale01Attack3, +}; + +// Male03 + +Sfx gasfxMale03Select[] = { + ksfxMale03Select0, + ksfxMale03Select1, + ksfxMale03Select2, + ksfxMale03Select3, +}; + +Sfx gasfxMale03Move[] = { + ksfxMale03Move0, + ksfxMale03Move1, + ksfxMale03Move2, + ksfxMale03Move3, +}; + +Sfx gasfxMale03Attack[] = { + ksfxMale03Attack0, + ksfxMale03Attack1, + ksfxMale03Attack2, + ksfxMale03Attack3, +}; + +// Male06 + +Sfx gasfxMale06Select[] = { + ksfxMale06Select0, + ksfxMale06Select1, + ksfxMale06Select2, + ksfxMale06Select3, +}; + +Sfx gasfxMale06Move[] = { + ksfxMale06Move0, + ksfxMale06Move1, + ksfxMale06Move2, + ksfxMale06Move3, +}; + +Sfx gasfxMale06Attack[] = { + ksfxMale06Attack0, + ksfxMale06Attack1, + ksfxMale06Attack2, + ksfxMale06Attack3, +}; + +// Major01 + +Sfx gasfxMajor01Select[] = { + ksfxMajor01Select0, + ksfxMajor01Select1, + ksfxMajor01Select2, + ksfxMajor01Select3, +}; + +Sfx gasfxMajor01Move[] = { + ksfxMajor01Move0, + ksfxMajor01Move1, + ksfxMajor01Move2, + ksfxMajor01Move3, +}; + +Sfx gasfxMajor01Attack[] = { + ksfxMajor01Attack0, + ksfxMajor01Attack1, + ksfxMajor01Attack2, + ksfxMajor01Attack3, +}; + +// Major02 + +Sfx gasfxMajor02Select[] = { + ksfxMajor02Select0, + ksfxMajor02Select1, + ksfxMajor02Select2, + ksfxMajor02Select3, +}; + +Sfx gasfxMajor02Move[] = { + ksfxMajor02Move0, + ksfxMajor02Move1, + ksfxMajor02Move2, + ksfxMajor02Move3, +}; + +Sfx gasfxMajor02Attack[] = { + ksfxMajor02Attack0, + ksfxMajor02Attack1, + ksfxMajor02Attack2, + ksfxMajor02Attack3, +}; + +// Andy + +Sfx gasfxAndySelect[] = { + ksfxAndySelect, +}; + +Sfx gasfxAndyMove[] = { + ksfxAndyMove, +}; + +Sfx gasfxAndyAttack[] = { + ksfxAndyAttack, +}; + +Sfx gasfxAndyDestroyed[] = { + ksfxAndyDestroyed, +}; + +// Fox + +Sfx gasfxFoxDestroyed[] = { + ksfxFoxDestroyed, +}; + +struct SfxCategoryEntry // sfxce +{ + Sfx *asfx; + int csfx; +}; + +SfxCategoryEntry gasfxce[] = { + { gasfxInfantryDestroyed, ARRAYSIZE(gasfxInfantryDestroyed) }, + { gasfxVehicleDestroyed, ARRAYSIZE(gasfxVehicleDestroyed) }, + { gasfxMale01Select, ARRAYSIZE(gasfxMale01Select) }, + { gasfxMale01Move, ARRAYSIZE(gasfxMale01Move) }, + { gasfxMale01Attack, ARRAYSIZE(gasfxMale01Attack) }, + { gasfxMale03Select, ARRAYSIZE(gasfxMale03Select) }, + { gasfxMale03Move, ARRAYSIZE(gasfxMale03Move) }, + { gasfxMale03Attack, ARRAYSIZE(gasfxMale03Attack) }, + { gasfxMale06Select, ARRAYSIZE(gasfxMale06Select) }, + { gasfxMale06Move, ARRAYSIZE(gasfxMale06Move) }, + { gasfxMale06Attack, ARRAYSIZE(gasfxMale06Attack) }, + { gasfxMajor01Select, ARRAYSIZE(gasfxMajor01Select) }, + { gasfxMajor01Move, ARRAYSIZE(gasfxMajor01Move) }, + { gasfxMajor01Attack, ARRAYSIZE(gasfxMajor01Attack) }, + { gasfxMajor02Select, ARRAYSIZE(gasfxMajor02Select) }, + { gasfxMajor02Move, ARRAYSIZE(gasfxMajor02Move) }, + { gasfxMajor02Attack, ARRAYSIZE(gasfxMajor02Attack) }, + { gasfxAndySelect, ARRAYSIZE(gasfxAndySelect) }, + { gasfxAndyMove, ARRAYSIZE(gasfxAndyMove) }, + { gasfxAndyAttack, ARRAYSIZE(gasfxAndyAttack) }, + { gasfxAndyDestroyed, ARRAYSIZE(gasfxAndyDestroyed) }, + { gasfxFoxDestroyed, ARRAYSIZE(gasfxFoxDestroyed) }, +}; + +Sfx SfxFromCategory(SfxCategory sfxc) +{ + if (sfxc < 0 || sfxc >= ARRAYSIZE(gasfxce)) + return (Sfx)-1; + SfxCategoryEntry *psfxce = &gasfxce[sfxc]; + + // NOTE: We use GetAsyncRandom() to avoid disturbing the in-sync random + // number generator when we're performing this operation which is meant + // only for the local player. + + return psfxce->asfx[GetAsyncRandom() % psfxce->csfx]; +} + +// Stream helpers + +void Stream::ReadString(char *psz, int cb) +{ + // Fill the passed buffer + + *psz = 0; + while (cb-- != 0) { + char ch = (char)ReadByte(); + *psz++ = ch; + if (ch == 0) + return; + } + + // More string in the stream than size of psz. Cap off psz, read till 0 + + psz[cb - 1] = 0; + while (ReadByte() != 0) + ; +} + +void Stream::WriteString(char *psz) +{ + Write(psz, strlen(psz) + 1); +} + +void Stream::WriteBytesRLE(byte *pb, int cb) +{ + byte *pbMax = &pb[cb]; + for (byte *pbChunk = pb; pbChunk < pbMax; ) { + // Literal runs are separated by repeats of at least 3 or more. + + byte *pbRepeat = FindRLERepeat(pbChunk, pbMax, 3); + + // If there is a literal to write, do it + + int cbLiteral = 0; + if (pbRepeat == NULL) { + cbLiteral = pbMax - pbChunk; + } else if (pbRepeat > pbChunk) { + cbLiteral = pbRepeat - pbChunk; + } + if (cbLiteral != 0) { + WriteRLEChunk(pbChunk, cbLiteral, false); + pbChunk += cbLiteral; + continue; + } + + // There should be a repeat to write + + Assert(pbRepeat != NULL && pbRepeat == pbChunk); + byte *pbT; + for (pbT = pbRepeat + 1; pbT < pbMax; pbT++) { + if (*pbT != *pbRepeat) + break; + } + int cbRepeat = pbT - pbRepeat; + Assert(cbRepeat >= 3); + WriteRLEChunk(pbChunk, cbRepeat, true); + pbChunk += cbRepeat; + } +} + +byte *Stream::FindRLERepeat(byte *pbStart, byte *pbMax, int cbMin) +{ + for (; pbStart < pbMax; pbStart++) { + byte bFind = *pbStart; + + int cRepeat = 1; + for (byte *pbT = pbStart + 1; pbT < pbMax; pbT++) { + if (*pbT != bFind) + break; + cRepeat++; + if (cRepeat >= cbMin) + return pbStart; + } + } + return 0; +} + +void Stream::WriteRLEChunk(byte *pb, int cb, bool fRepeat) +{ + if (!fRepeat) { + // Literal + + while (cb != 0) { + int cbWrite = cb < 128 ? cb : 128; + WriteByte(128 | (cbWrite - 1)); + Write(pb, cbWrite); + pb += cbWrite; + cb -= cbWrite; + if (!IsSuccess()) + break; + } + } else { + // Repeat + + while (cb != 0) { + int cbWrite = cb < 128 ? cb : 128; + WriteByte(0 | (cbWrite - 1)); + WriteByte(*pb); + cb -= cbWrite; + if (!IsSuccess()) + break; + } + } +} + +void Stream::ReadBytesRLE(byte *pb, int cb) +{ + while (cb != 0) { + byte bT = ReadByte(); + int cbRun = (bT & ~128) + 1; + if (bT & 128) { + Read(pb, cbRun); + } else { + byte bRepeat = ReadByte(); + memset(pb, bRepeat, cbRun); + } + pb += cbRun; + cb -= cbRun; + if (!IsSuccess()) + break; + } +} + +// +// Return the appropriate color for the current device +// + +Color gaclr4bpp[] = { + 0x000f, // black + 0x0000, // white + 0x0007, // red + 0x0000, // green + 0x0003, // yellow + 0x0008, // side 1 color + 0x000d, // side 2 color + 0x0004, // side 3 color + 0x000f, // side 4 color + 0x0008, // button fill + 0x000a, // button border + 0x000f, // menu background + 0x000f, // form background + 0x000d, // mini map border area + 0x000b, // Galaxite + 0x0007, // button fill highlight + 0x0008, // medium gray + 0x0004, // blue side 0 + 0x0006, // blue side 1 + 0x0008, // blue side 2 + 0x000a, // blue side 3 + 0x000c, // blue side 4 + 0x000b, // red side 0 + 0x000c, // red side 1 + 0x000d, // red side 2 + 0x000e, // red side 3 + 0x000f, // red side 4 + 0x0001, // yellow side 0 + 0x0003, // yellow side 1 + 0x0004, // yellow side 2 + 0x0005, // yellow side 3 + 0x0005, // yellow side 4 + 0x0000, // cyan side 0 + 0x000c, // cyan side 1 + 0x0002, // cyan side 2 + 0x000e, // cyan side 3 + 0x0004, // cyan side 4 + 0x000e, // list background + 0x0003, // list border + 0x0000, // Jana Font + 0x0000, // Andy Font + 0x0000, // Olstrom Font + 0x0000, // Fox Font + 0x000f, // neutral side 0 + 0x000f, // neutral side 1 + 0x000f, // neutral side 2 + 0x000f, // neutral side 3 + 0x000f, // neutral side 4 +}; + +Color gaclr8bpp[] = { + 0x0000, // black + 0x0001, // white + 0x0002, // red + 0x0003, // green + 0x0004, // yellow + 0x0010, // side 1 color + 0x0015, // side 2 color + 0x001a, // side 3 color + 0x000b, // side 4 color + 0x0005, // button fill + 0x0011, // button border + 0x0000, // menu background + 0x0000, // form background + 0x000f, // mini map border area + 0x001f, // Galaxite minimap color + 0x000c, // button fill highlight + 35, // medium gray + 0x0010, // blue side 0 + 0x0011, // blue side 1 + 0x0012, // blue side 2 + 0x0013, // blue side 3 + 0x0014, // blue side 4 + 0x0015, // red side 0 + 0x0016, // red side 1 + 0x0017, // red side 2 + 0x0018, // red side 3 + 0x0019, // red side 4 + 0x001a, // yellow side 0 + 0x001b, // yellow side 1 + 0x001c, // yellow side 2 + 0x001d, // yellow side 3 + 0x001e, // yellow side 4 + 0x000b, // cyan side 0 + 0x000c, // cyan side 1 + 0x000d, // cyan side 2 + 0x000e, // cyan side 3 + 0x000f, // cyan side 4 + 0x000a, // list background + 0x000c, // list border + 0x0006, // Jana font + 0x000b, // Andy font + 0x001a, // Olstrom font + 0x0015, // Fox font + 32, // neutral side 0 + 33, // neutral side 1 + 36, // neutral side 2 + 40, // neutral side 3 + 45, // neutral side 4 + 52, // Galaxite fullness indicator/neon pink +}; + +int gaiclrSide[kcSides] = { kiclrSideNeutral, kiclrSide1, kiclrSide2, kiclrSide3, kiclrSide4 }; + +void DrawBorder(DibBitmap *pbm, Rect *prc, int nThickness, Color clr, UpdateMap *pupd) +{ + if (pupd == NULL) { + pbm->Fill(prc->left, prc->top, prc->Width(), nThickness, clr); + pbm->Fill(prc->left, prc->top, nThickness, prc->Height(), clr); + pbm->Fill(prc->right - nThickness, prc->top, nThickness, prc->Height(), clr); + pbm->Fill(prc->left, prc->bottom - nThickness, prc->Width(), nThickness, clr); + } else { + Rect rcT; + rcT.Set(prc->left, prc->top, prc->right, prc->top + nThickness); + FillHelper(pbm, pupd, &rcT, clr); + rcT.Set(prc->left, prc->top, prc->left + nThickness, prc->bottom); + FillHelper(pbm, pupd, &rcT, clr); + rcT.Set(prc->right - nThickness, prc->top, prc->right, prc->bottom); + FillHelper(pbm, pupd, &rcT, clr); + rcT.Set(prc->left, prc->bottom - nThickness, prc->right, prc->bottom); + FillHelper(pbm, pupd, &rcT, clr); + } +} + +#if 0 +void DrawBitmapBorder(DibBitmap *pbm, UpdateMap *pupd, Rect *prc, AnimationData *panid, int ifrm, Side side) +{ + // Get piece bounds + + Rect rcTL; + panid->GetBounds(0, ifrm, &rcTL); + Rect rcT; + panid->GetBounds(1, ifrm, &rcT); + Rect rcTR; + panid->GetBounds(2, ifrm, &rcTR); + Rect rcR; + panid->GetBounds(3, ifrm, &rcR); + Rect rcBR; + panid->GetBounds(4, ifrm, &rcBR); + Rect rcB; + panid->GetBounds(5, ifrm, &rcB); + Rect rcBL; + panid->GetBounds(6, ifrm, &rcBL); + Rect rcL; + panid->GetBounds(7, ifrm, &rcL); + + // Draw repeating top edge + + Rect rcBounds; + int xL = prc->left + rcTL.Width(); + int xR = prc->right - rcTR.Width(); + int cxPiece = rcT.Width(); + for (; xL < xR; xL += cxPiece) { + rcBounds = rcT; + rcBounds.Offset(xL, prc->top); + if (pupd->IsRectInvalid(&rcBounds)) + panid->DrawFrame(1, ifrm, pbm, xL, prc->top, side); + } + + // Draw repeating bottom edge + + xL = prc->left + rcBL.Width(); + xR = prc->right - rcBR.Width(); + cxPiece = rcB.Width(); + for (; xL < xR; xL += cxPiece) { + rcBounds = rcB; + rcBounds.Offset(xL, prc->bottom); + if (pupd->IsRectInvalid(&rcBounds)) + panid->DrawFrame(5, ifrm, pbm, xL, prc->bottom, side); + } + + // Draw repeating left edge + + int yT = prc->top + rcTL.Height(); + int yB = prc->bottom - rcBL.Height(); + int cyPiece = rcL.Height(); + for (; yT < yB; yT += cyPiece) { + rcBounds = rcL; + rcBounds.Offset(prc->left, yT); + if (pupd->IsRectInvalid(&rcBounds)) + panid->DrawFrame(7, ifrm, pbm, prc->left, yT, side); + } + + // Draw repeating right edge + + yT = prc->top + rcTR.Height(); + yB = prc->bottom - rcBR.Height(); + cyPiece = rcR.Height(); + for (; yT < yB; yT += cyPiece) { + rcBounds = rcR; + rcBounds.Offset(prc->right, yT); + if (pupd->IsRectInvalid(&rcBounds)) + panid->DrawFrame(3, ifrm, pbm, prc->right, yT, side); + } + + // Draw the corners + + rcBounds = rcTL; + rcBounds.Offset(prc->left, prc->top); + if (pupd->IsRectInvalid(&rcBounds)) + panid->DrawFrame(0, ifrm, pbm, prc->left, prc->top, side); + + rcBounds = rcTR; + rcBounds.Offset(prc->right, prc->top); + if (pupd->IsRectInvalid(&rcBounds)) + panid->DrawFrame(2, ifrm, pbm, prc->right, prc->top, side); + + rcBounds = rcBR; + rcBounds.Offset(prc->right, prc->bottom); + if (pupd->IsRectInvalid(&rcBounds)) + panid->DrawFrame(4, ifrm, pbm, prc->right, prc->bottom, side); + + rcBounds = rcBL; + rcBounds.Offset(prc->left, prc->bottom); + if (pupd->IsRectInvalid(&rcBounds)) + panid->DrawFrame(6, ifrm, pbm, prc->left, prc->bottom, side); +} +#endif + +// +// Basic string dictionary class. Limited to short keys and values. +// + +Dictionary::Dictionary() +{ + m_cde = 0; + m_pdeHead = NULL; +} + +Dictionary::~Dictionary() +{ + Clear(); +} + +// Clone the passed-in dictionary over self + +bool Dictionary::Init(Dictionary *pdict) +{ + Clear(); + + DictionaryEntry *pdeSrc = pdict->m_pdeHead; + DictionaryEntry **ppdeDst = &m_pdeHead; + while (pdeSrc != NULL) { + DictionaryEntry *pdeDst = new DictionaryEntry; + Assert(pdeDst != NULL, "out of memory!"); + if (pdeDst == NULL) { + Clear(); + return false; + } + pdeDst->pdeNext = NULL; + strcpy(pdeDst->szName, pdeSrc->szName); + strcpy(pdeDst->szValue, pdeSrc->szValue); + *ppdeDst = pdeDst; + ppdeDst = &pdeDst->pdeNext; + pdeSrc = pdeSrc->pdeNext; + m_cde++; + } + + return true; +} + +void Dictionary::Clear() +{ + DictionaryEntry *pde = m_pdeHead; + while (pde != NULL) { + DictionaryEntry *pdeNext = pde->pdeNext; + delete pde; + pde = pdeNext; + } + m_cde = 0; + m_pdeHead = NULL; +} + +const char *Dictionary::Get(const char *pszName) +{ + DictionaryEntry **ppde = Find(pszName); + if (*ppde == NULL) + return NULL; + + // Relink at head of the list (if not already there) + + DictionaryEntry *pde = *ppde; + if (m_pdeHead != pde) { + *ppde = pde->pdeNext; + pde->pdeNext = m_pdeHead; + m_pdeHead = pde; + } + return pde->szValue; +} + +bool Dictionary::Set(const char *pszName, const char *pszValue) +{ + // Is it already in the dictionary? + + DictionaryEntry **ppde = Find(pszName); + + // Create new entry if needed + + DictionaryEntry *pde; + if (*ppde == NULL) { + pde = new DictionaryEntry; + Assert(pde != NULL, "out of memory!"); + if (pde == NULL) + return false; + m_cde++; + } else { + pde = *ppde; + *ppde = pde->pdeNext; + } + + // Set its name/value and link it to the head of the list + + pde->pdeNext = m_pdeHead; + m_pdeHead = pde; + strcpy(pde->szName, pszName); + strcpy(pde->szValue, pszValue); + + return true; +} + +bool Dictionary::Remove(const char *pszName) +{ + DictionaryEntry **ppde = Find(pszName); + if (*ppde == NULL) + return false; + + m_cde--; + DictionaryEntry *pde = *ppde; + *ppde = pde->pdeNext; + delete pde; + return true; +} + +#define knVerDictionaryState 0 +bool Dictionary::LoadState(Stream *pstm) +{ + Clear(); + + // Do version handling + + byte nVer = pstm->ReadByte(); + if (nVer != knVerDictionaryState) + return false; + + DictionaryEntry **ppde = &m_pdeHead; + m_cde = pstm->ReadByte(); + for (int i = 0; i < m_cde; i++) { + DictionaryEntry *pde = new DictionaryEntry; + Assert(pde != NULL, "out of memory!"); + if (pde == NULL) + return false; + pstm->ReadString(pde->szName, sizeof(pde->szName)); + pstm->ReadString(pde->szValue, sizeof(pde->szValue)); + + pde->pdeNext = NULL; + *ppde = pde; + ppde = &pde->pdeNext; + } + + return pstm->IsSuccess(); +} + +bool Dictionary::SaveState(Stream *pstm) +{ + pstm->WriteByte(knVerDictionaryState); + pstm->WriteByte(m_cde); + + DictionaryEntry *pde = m_pdeHead; + while (pde != NULL) { + pstm->WriteString(pde->szName); + pstm->WriteString(pde->szValue); + pde = pde->pdeNext; + } + + return pstm->IsSuccess(); +} + +DictionaryEntry **Dictionary::Find(const char *pszName) +{ + DictionaryEntry **ppde = &m_pdeHead; + while (*ppde != NULL) { + // OPT: upcase entries as they're added to the dictionary + if (stricmp((*ppde)->szName, pszName) == 0) + break; + ppde = &(*ppde)->pdeNext; + } + + return ppde; +} + +// Takes a string with embedded variable references in the form of "{pvar}" +// and expands them to the appropriate values. + +void ExpandVars(char *pszSrc, char *pszBuff, int cbBuff) +{ + while (*pszSrc != 0 && cbBuff > 1) { + char ch = *pszSrc++; + + // Variable? + + if (ch == '{') { + + // yes + + char szT[kcbPvarNameMax]; + char *pszT = szT; + *pszT = 0; + + int cbT = sizeof(szT); + char *pszBeforeVar = pszSrc; + while (*pszSrc != 0 && *pszSrc != '}' && cbT-- > 0) + *pszT++ = *pszSrc++; + + if (*pszSrc != 0) { + *pszT = 0; + ggame.GetVar(szT, pszBuff, cbBuff); + + int cb = strlen(pszBuff); + cbBuff -= cb; + pszBuff += cb; + pszSrc++; + } else { + + // Variable not terminated properly + + pszSrc = pszBeforeVar; + *pszBuff++ = ch; + cbBuff--; + } + } else { + + // No, just copy it + + *pszBuff++ = ch; + cbBuff--; + } + } + + *pszBuff = 0; +} + +void GetRankTitle(char *psz, int cb) +{ + // Note: rank "Challenger" is hardcoded in the level files as -1 + + int nRank = 0; + char szT[20]; + if (ggame.GetVar("rank", szT, sizeof(szT))) + nRank = atoi(szT); + if (!gpstrtbl->GetString(kidsRank0 + nRank, psz, cb)) + *psz = 0; +} + +// Helpers - no need to call runtime for these +// These are here because Metrowerks doesn't have ARM runtime support for them, and we +// don't want to create thunks + +int strnicmp(const char *psz1, const char *psz2, int cch) +{ + while (cch-- != 0) { + byte b1 = *psz1++; + if (b1 >= 'A' && b1 <= 'Z') + b1 += 'a' - 'A'; + byte b2 = *psz2++; + if (b2 >= 'A' && b2 <= 'Z') + b2 += 'a' - 'A'; + if (b1 != b2) + return b1 - b2; + if (b1 == 0) + return 0; + } + return 0; +} + +static void xtoa ( + unsigned long val, + char *buf, + unsigned radix, + int is_neg + ) +{ + char *p; /* pointer to traverse string */ + char *firstdig; /* pointer to first digit */ + char temp; /* temp char */ + unsigned digval; /* value of digit */ + + p = buf; + + if (is_neg) { + /* negative, so output '-' and negate */ + *p++ = '-'; + val = (unsigned long)(-(long)val); + } + + firstdig = p; /* save pointer to first digit */ + + do { + digval = (unsigned) (val % radix); + val /= radix; /* get next digit */ + + /* convert to ascii and store */ + if (digval > 9) + *p++ = (char) (digval - 10 + 'a'); /* a letter */ + else + *p++ = (char) (digval + '0'); /* a digit */ + } while (val > 0); + + /* We now have the digit of the number in the buffer, but in reverse + order. Thus we reverse them now. */ + + *p-- = '\0'; /* terminate string; p points to last digit */ + + do { + temp = *p; + *p = *firstdig; + *firstdig = temp; /* swap *p and *firstdig */ + --p; + ++firstdig; /* advance to next two digits */ + } while (firstdig < p); /* repeat until halfway */ +} + +/* Actual functions just call conversion helper with neg flag set correctly, + and return pointer to buffer. */ + +char *itoa ( + int val, + char *buf, + int radix + ) +{ + if (radix == 10 && val < 0) + xtoa((unsigned long)val, buf, radix, 1); + else + xtoa((unsigned long)(unsigned int)val, buf, radix, 0); + return buf; +} + +} // namespace wi diff --git a/game/misccontrols.cpp b/game/misccontrols.cpp new file mode 100644 index 0000000..3090178 --- /dev/null +++ b/game/misccontrols.cpp @@ -0,0 +1,2147 @@ +#include "ht.h" + +namespace wi { + +// Button control + +TBitmap *ButtonControl::s_ptbmLeftUp; +TBitmap *ButtonControl::s_ptbmMidUp; +TBitmap *ButtonControl::s_ptbmRightUp; +TBitmap *ButtonControl::s_ptbmLeftDown; +TBitmap *ButtonControl::s_ptbmMidDown; +TBitmap *ButtonControl::s_ptbmRightDown; + +bool ButtonControl::InitClass() +{ + s_ptbmLeftUp = GetSharedTBitmap("buttonleftup.tbm"); + s_ptbmMidUp = GetSharedTBitmap("buttonmidup.tbm"); + s_ptbmRightUp = GetSharedTBitmap("buttonrightup.tbm"); + s_ptbmLeftDown = GetSharedTBitmap("buttonleftdown.tbm"); + s_ptbmMidDown = GetSharedTBitmap("buttonmiddown.tbm"); + s_ptbmRightDown = GetSharedTBitmap("buttonrightdown.tbm"); + return true; +} + +void ButtonControl::ExitClass() +{ +} + +ButtonControl::ButtonControl() +{ + m_nfnt = 0; + m_ptbmUp = NULL; + m_ptbmDown = NULL; + m_szLabel = NULL; +} + +ButtonControl::~ButtonControl() +{ + if (m_szLabel != NULL) + gmmgr.FreePtr(m_szLabel); +} + +bool ButtonControl::Init(Form *pfrm, word idc, int x, int y, int cx, int cy, + char *pszLabel, int nfnt, char *szFnUp, char *szFnDown, char *szFnDisabled) +{ + if (!Control::Init(pfrm, idc, x, y, cx, cy)) + return false; + + return Init(pszLabel, nfnt, szFnUp, szFnDown, szFnDisabled, false); +} + +bool ButtonControl::Init(char *pszLabel, int nfnt, char *szFnUp, char *szFnDown, char *szFnDisabled, bool fCenter) +{ + m_nfnt = nfnt; + m_szLabel = (char *)gmmgr.AllocPtr(strlen(pszLabel) + 1); + gmmgr.WritePtr(m_szLabel, 0, pszLabel, strlen(pszLabel) + 1); + + if (szFnUp != NULL) + m_ptbmUp = GetSharedTBitmap(szFnUp); + if (szFnDown != NULL) + m_ptbmDown = GetSharedTBitmap(szFnDown); + if (szFnDisabled != NULL) + m_ptbmDisabled = GetSharedTBitmap(szFnDisabled); + + if (m_ptbmUp != NULL && m_ptbmDown != NULL) { + Size siz1 = { 0, 0 }; + m_ptbmUp->GetSize(&siz1); + Size siz2 = { 0, 0 }; + m_ptbmDown->GetSize(&siz2); + int cx = _max(siz1.cx, siz2.cx); + int cy = _max(siz1.cy, siz2.cy); + if (cx != 0 && cy != 0) { + m_rc.right = m_rc.left + _max(siz1.cx, siz2.cx); + m_rc.bottom = m_rc.top + _max(siz1.cy, siz2.cy); + } + } else { +#if 0 // old-style filled-rect buttons + Font *pfnt = gapfnt[m_nfnt]; + int cx = pfnt->GetTextExtent(m_szLabel); + int cy = pfnt->GetHeight(); + if (m_rc.right == m_rc.left) + m_rc.right = m_rc.left + cx + PcFromFc(3 + 3); + if (m_rc.bottom == m_rc.top) + m_rc.bottom = m_rc.top + cy + PcFromFc(1 + 2); +#else + Size sizMid; + s_ptbmMidUp->GetSize(&sizMid); + + // Always set the height correctly so that updatemap is correct + // and TBitmap::Fill() doesn't need to clip. + + m_rc.bottom = m_rc.top + sizMid.cy; + + // Default width + + if (m_rc.right == m_rc.left) + m_rc.right = m_rc.left + (sizMid.cx * 3); +#endif + } + + if (fCenter) + m_rc.Offset(-m_rc.Width() / 2, 0); + + return true; +} + +bool ButtonControl::Init(Form *pfrm, IniReader *pini, FindProp *pfind) +{ + // Base initialization + + if (!Control::Init(pfrm, pini, pfind)) + return false; + + // idc (x y cx cy) "label" nfnt center buttonup.tbm buttondown.tbm + + char szFnUp[kcbFilename]; + char szFnDown[kcbFilename]; + char szFnDisabled[kcbFilename]; + + char szT[32]; + char szLabel[64]; + int cArgs = pini->GetPropertyValue(pfind, "%*d (%*d %*d %*d %*d) \"%s\" %d %s %s %s %s", + szLabel, &m_nfnt, szT, szFnUp, szFnDown, szFnDisabled); + switch (cArgs) { + case 2: + return Init(szLabel, m_nfnt, NULL, NULL, NULL, false); + case 3: + return Init(szLabel, m_nfnt, NULL, NULL, NULL, strcmp(szT, "center") == 0); + case 5: + return Init(szLabel, m_nfnt, szFnUp, szFnDown, NULL, strcmp(szT, "center") == 0); + case 6: + return Init(szLabel, m_nfnt, szFnUp, szFnDown, szFnDisabled, strcmp(szT, "center") == 0); + } + + return false; +} + +void ButtonControl::OnPaint(DibBitmap *pbm) +{ + Rect rcForm; + m_pfrm->GetRect(&rcForm); + + bool fSelected = m_pfrm->IsControlInside(this); + if (m_ptbmDown != NULL && m_ptbmUp != NULL) { + // Draw up / down image (if present) + + TBitmap *ptbm; + if (m_wf & kfCtlDisabled) + ptbm = m_ptbmDisabled; + else + ptbm = fSelected ? m_ptbmDown : m_ptbmUp; + + ptbm->BltTo(pbm, m_rc.left + rcForm.left, m_rc.top + rcForm.top, m_wf & kfCtlUseSide1Colors ? kside1 : ksideNeutral); + + // Center the text (if present) + + if (m_szLabel[0] != 0) { + Font *pfnt = gapfnt[m_nfnt]; + int cx = pfnt->GetTextExtent(m_szLabel); + int cy = pfnt->GetHeight(); + int x = m_rc.left + (m_rc.Width() - cx + 1) / 2; + int y = m_rc.top + (m_rc.Height() - cy + gcxyBorder) / 2; + if (fSelected) { + x++; + y++; + } + gapfnt[m_nfnt]->DrawText(pbm, m_szLabel, x + rcForm.left, y + rcForm.top); + } + } else { +#if 0 // old-style filled-rect buttons + Rect rcT = m_rc; + rcT.Offset(rcForm.left, rcForm.top); + + int cxyBorder2x = gcxyBorder * 2; + pbm->Fill(rcT.left + gcxyBorder, rcT.top + gcxyBorder, rcT.Width() - cxyBorder2x, rcT.Height() - cxyBorder2x, GetColor(fSelected ? kiclrButtonFillHighlight : kiclrButtonFill)); + + int iclr = GetColor(fSelected ? kiclrWhite : kiclrButtonBorder); + pbm->Fill(rcT.left + gcxyBorder, rcT.top, rcT.Width() - cxyBorder2x, gcxyBorder, iclr); + pbm->Fill(rcT.left, rcT.top + gcxyBorder, gcxyBorder, rcT.Height() - cxyBorder2x, iclr); + pbm->Fill(rcT.right - gcxyBorder, rcT.top + gcxyBorder, gcxyBorder, rcT.Height() - cxyBorder2x, iclr); + pbm->Fill(rcT.left + gcxyBorder, rcT.bottom - gcxyBorder, rcT.Width() - cxyBorder2x, gcxyBorder, iclr); + + if (m_szLabel[0] != 0) { + Font *pfnt = gapfnt[m_nfnt]; + int cx = pfnt->GetTextExtent(m_szLabel); + int cy = pfnt->GetHeight(); + int x = m_rc.left + (m_rc.Width() - cx + 1) / 2; + int y = m_rc.top + (m_rc.Height() - cy + gcxyBorder) / 2; + gapfnt[m_nfnt]->DrawText(pbm, m_szLabel, x + rcForm.left, y + rcForm.top); + } +#else + Rect rcT = m_rc; + rcT.Offset(rcForm.left, rcForm.top); + + TBitmap *ptbmLeft, *ptbmMid, *ptbmRight; + if (!fSelected) { + ptbmLeft = s_ptbmLeftUp; + ptbmMid = s_ptbmMidUp; + ptbmRight = s_ptbmRightUp; + } else { + ptbmLeft = s_ptbmLeftDown; + ptbmMid = s_ptbmMidDown; + ptbmRight = s_ptbmRightDown; + } + + Size sizLeft, sizMid, sizRight; + ptbmLeft->GetSize(&sizLeft); + ptbmMid->GetSize(&sizMid); + ptbmRight->GetSize(&sizRight); + + int x = rcT.left; + int xRight = rcT.right - sizRight.cx; + + ptbmLeft->BltTo(pbm, x, rcT.top); + x += sizLeft.cx; +#if 0 + while (x < xRight) { + ptbmMid->BltTo(pbm, x, rcT.top); + x += sizMid.cx; + } +#else + ptbmMid->FillTo(0, pbm, x, rcT.top, xRight - x, rcT.Height()); + x = xRight; +#endif + + ptbmRight->BltTo(pbm, xRight, rcT.top); + + if (m_szLabel[0] != 0) { + Font *pfnt = gapfnt[m_nfnt]; + int cx = pfnt->GetTextExtent(m_szLabel); + int cy = pfnt->GetHeight(); + int x = m_rc.left + (m_rc.Width() - cx + 1) / 2; + int y = m_rc.top + ((sizMid.cy - gapfnt[m_nfnt]->GetHeight() + 1) / 2); + gapfnt[m_nfnt]->DrawText(pbm, m_szLabel, x + rcForm.left, y + rcForm.top); + } +#endif + } +} + +void ButtonControl::OnSelect(int nSelect) +{ + if (m_wf & kfCtlDisabled) + return; + + switch (nSelect) { + case knSelMoveInside: + case knSelDownInside: + gsndm.PlaySfx(ksfxGuiButtonTap); + Invalidate(); + break; + + case knSelUpInside: + case knSelMoveOutside: + Invalidate(); + break; + } + + Control::OnSelect(nSelect); +} + +void ButtonControl::SetText(char *psz) +{ + if (strcmp(m_szLabel, psz) == 0) + return; + + if (m_szLabel != NULL) + gmmgr.FreePtr(m_szLabel); + m_szLabel = (char *)gmmgr.AllocPtr(strlen(psz) + 1); + gmmgr.WritePtr(m_szLabel, 0, psz, strlen(psz) + 1); + + Invalidate(); +} + +#if 0 // not used now +// Preset button control + +bool PresetButtonControl::Init(char *pszLabel, int nfnt, char *szFnUp, char *szFnDown, bool fCenter) +{ + if (szFnUp != NULL) + m_ptbmUp = GetSharedTBitmap(szFnUp); + if (szFnDown != NULL) + m_ptbmDown = GetSharedTBitmap(szFnDown); + + if (m_ptbmUp != NULL) { + Size siz1 = { 0, 0 }; + m_ptbmUp->GetSize(&siz1); + if (siz1.cx != 0 && siz1.cy != 0) { + m_rc.right = m_rc.left + siz1.cx + 2; + m_rc.bottom = m_rc.top + siz1.cy + 2; + } + } + if (fCenter) + m_rc.Offset(-m_rc.Width() / 2, 0); + + return true; +} + +void PresetButtonControl::OnPaint(DibBitmap *pbm) +{ + bool fSet = (m_wf & kfCtlSet) != 0; + + Rect rcForm; + m_pfrm->GetRect(&rcForm); + + bool fSelected = m_pfrm->IsControlInside(this); + Rect rcT = m_rc; + rcT.Offset(rcForm.left, rcForm.top); + + if (fSelected) { + pbm->Fill(rcT.left + 1, rcT.top + 1, rcT.Width() - 2, rcT.Height() - 2, GetColor(kiclrButtonFillHighlight)); + } + + TBitmap *ptbm; + if (fSet) + ptbm = m_ptbmDown; + else + ptbm = m_ptbmUp; + + if (ptbm != NULL) + ptbm->BltTo(pbm, m_rc.left + rcForm.left + 1, m_rc.top + rcForm.top + 1); +} +#endif + +// CheckBox Control + +TBitmap *CheckBoxControl::s_ptbmOnUp; +TBitmap *CheckBoxControl::s_ptbmOnDown; +TBitmap *CheckBoxControl::s_ptbmOffUp; +TBitmap *CheckBoxControl::s_ptbmOffDown; + +CheckBoxControl::CheckBoxControl() +{ + m_fChecked = false; + m_ifnt = 0; + m_szLabel[0] = 0; +} + +CheckBoxControl::~CheckBoxControl() +{ +} + +bool CheckBoxControl::Init(Form *pfrm, word idc, int x, int y, char *pszLabel, int ifnt, bool fChecked) +{ + if (!Control::Init(pfrm, idc, x, y, 0, 0)) + return false; + + return Init(pszLabel, ifnt, fChecked); +} + +bool CheckBoxControl::InitClass() +{ + s_ptbmOnUp = GetSharedTBitmap("checkboxonup.tbm"); + s_ptbmOnDown = GetSharedTBitmap("checkboxondown.tbm"); + s_ptbmOffUp = GetSharedTBitmap("checkboxoffup.tbm"); + s_ptbmOffDown = GetSharedTBitmap("checkboxoffdown.tbm"); + + return true; +} + +void CheckBoxControl::ExitClass() +{ +} + +bool CheckBoxControl::Init(char *pszLabel, int ifnt, bool fChecked) +{ + m_fChecked = fChecked; + m_ifnt = ifnt; + SetText(pszLabel); + return true; +} + +bool CheckBoxControl::Init(Form *pfrm, IniReader *pini, FindProp *pfind) +{ + // Base initialization + + if (!Control::Init(pfrm, pini, pfind)) + return false; + + // idc (x y cx cy) "label" ifnt [checked] + + char szChecked[64]; + szChecked[0] = 0; + int cArgs = pini->GetPropertyValue(pfind, "%*d (%*d %*d %*d %*d) \"%s\" %d %s", + m_szLabel, &m_ifnt, &szChecked); + if (cArgs == 2) + return Init(m_szLabel, m_ifnt, false); + if (cArgs != 3) + return false; + + return Init(m_szLabel, m_ifnt, strcmp(szChecked, "checked") == 0); +} + +void CheckBoxControl::OnPaint(DibBitmap *pbm) +{ + Rect rcForm; + m_pfrm->GetRect(&rcForm); + + // Draw up / down image (if present) + + TBitmap *ptbm; + bool fPenDownInside = m_pfrm->IsControlInside(this); + if (fPenDownInside) { + ptbm = m_fChecked ? s_ptbmOnDown : s_ptbmOffDown; + } else { + ptbm = m_fChecked ? s_ptbmOnUp : s_ptbmOffUp; + } + + // Center the text horizontally and draw to the right of the checkbox + + Font *pfnt = gapfnt[m_ifnt]; + int cy = pfnt->GetHeight(); + Size siz; + ptbm->GetSize(&siz); + ptbm->BltTo(pbm, m_rc.left + rcForm.left, m_rc.top + (cy - siz.cy) / 2 + rcForm.top - 1); + gapfnt[m_ifnt]->DrawText(pbm, m_szLabel, m_rc.left + rcForm.left + siz.cx, m_rc.top + rcForm.top); +} + +void CheckBoxControl::OnSelect(int nSelect) +{ + switch (nSelect) { + case knSelMoveInside: + case knSelDownInside: + gsndm.PlaySfx(ksfxGuiCheckBoxTap); + Invalidate(); + break; + + case knSelUpInside: + m_fChecked ^= 1; + Invalidate(); + break; + + case knSelMoveOutside: + Invalidate(); + break; + } + + Control::OnSelect(nSelect); +} + +void CheckBoxControl::SetText(char *psz) +{ + strncpyz(m_szLabel, psz, sizeof(m_szLabel)); + Size siz; + s_ptbmOnUp->GetSize(&siz); + Font *pfnt = gapfnt[m_ifnt]; + int cx = pfnt->GetTextExtent(m_szLabel); + m_rc.right = m_rc.left + siz.cx + pfnt->GetTextExtent(m_szLabel); + m_rc.bottom = m_rc.top + pfnt->GetHeight(); + Invalidate(); +} + +bool CheckBoxControl::IsChecked() +{ + return m_fChecked; +} + +void CheckBoxControl::SetChecked(bool fChecked) +{ + m_fChecked = fChecked; + Invalidate(); +} + +// Label Control + +LabelControl::LabelControl() +{ + m_nfnt = 0; + m_szLabel = NULL; +} + +LabelControl::~LabelControl() +{ + if (m_szLabel != NULL) + gmmgr.FreePtr(m_szLabel); +} + +bool LabelControl::Init(Form *pfrm, IniReader *pini, FindProp *pfind) +{ + // Base initialization + + if (!Control::Init(pfrm, pini, pfind)) + return false; + + // idc (x y cx cy) "label" nfnt + + char szLabel[256]; + char szFlag1[32], szFlag2[32]; + szFlag1[0] = 0; + szFlag2[0] = 0; + int nfnt; + int cArgs = pini->GetPropertyValue(pfind, "%*d (%*d %*d %*d %*d) \"%s\" %d %s %s", + szLabel, &nfnt, szFlag1, szFlag2); + if (cArgs < 2 || cArgs > 4) + return false; + + return Init(nfnt, szLabel, szFlag1, szFlag2); +} + +bool LabelControl::Init(int nfnt, char *pszLabel, char *pszFlag1, char *pszFlag2) +{ + m_nfnt = nfnt; + char *pszT = (char *)gpbScratch + (gcbScratch / 2); + ExpandVars(pszLabel, pszT, kcbLabelTextMax); + m_szLabel = (char *)gmmgr.AllocPtr(strlen(pszT) + 1); + gmmgr.WritePtr(m_szLabel, 0, pszT, strlen(pszT) + 1); + + if (strcmp(pszFlag1, "center") == 0) + m_wf |= kfLblCenterText; + else if (strcmp(pszFlag1, "multiline") == 0) + m_wf |= kfLblMultiLine; + else if (strcmp(pszFlag1, "right") == 0) + m_wf |= kfLblRightText; + + if (strcmp(pszFlag2, "center") == 0) + m_wf |= kfLblCenterText; + else if (strcmp(pszFlag2, "multiline") == 0) + m_wf |= kfLblMultiLine; + else if (strcmp(pszFlag2, "right") == 0) + m_wf |= kfLblRightText; + else if (strcmp(pszFlag2, "clipvert") == 0) + m_wf |= kfLblClipVertical; + + CalcRect(); + return true; +} + +void LabelControl::OnPaint(DibBitmap *pbm) +{ + if (m_szLabel == NULL) + return; + + Rect rcForm; + m_pfrm->GetRect(&rcForm); + Font *pfnt = gapfnt[m_nfnt]; + if (m_wf & kfLblMultiLine) { + Font *pfnt = gapfnt[m_nfnt]; + int cyClip = -1; + if (m_wf & kfLblClipVertical) { + cyClip = m_rc.Height(); + } + pfnt->DrawText(pbm, m_szLabel, m_rc.left + rcForm.left, + m_rc.top + rcForm.top, m_rc.Width(), cyClip, + (m_wf & kfLblEllipsis) != 0); + } else { + DrawFancyText(pbm, pfnt, m_szLabel, m_rc.left + rcForm.left, + m_rc.top + rcForm.top); + } +} + +int LabelControl::OnHitTest(Event *pevt) +{ + if (m_wf & kfLblHitTest) { + return Control::OnHitTest(pevt); + } + + // Assuming that passing Label selected events through to Forms does + // more harm than good (e.g., Forms have to case out labels) we'll + // just nip this in the bud. + + return -1; +} + +void LabelControl::SetText(const char *psz) +{ + if (psz == NULL) { + if (m_szLabel != NULL) { + gmmgr.FreePtr(m_szLabel); + m_szLabel = NULL; + } + } else { + if (strcmp(psz, m_szLabel) == 0) + return; + if (m_szLabel != NULL) + gmmgr.FreePtr(m_szLabel); + + // Use the tail end of the scratch buffer because ExpandVars + // (potentially) calls StringTable::GetString which reads from the + // database, decompressing to the front of the scratch buffer as part + // of the process. + + char *pszT = (char *)gpbScratch + (gcbScratch / 2); + ExpandVars((char *)psz, pszT, kcbLabelTextMax); + + m_szLabel = (char *)gmmgr.AllocPtr(strlen(pszT) + 1); + gmmgr.WritePtr(m_szLabel, 0, pszT, strlen(pszT) + 1); + } + CalcRect(); +} + +void LabelControl::CalcRect() +{ + Rect rcNew = m_rc; + int cx, cy; + + if (m_szLabel == NULL) { + cx = cy = 0; + } else { + Font *pfnt = gapfnt[m_nfnt]; + if (m_wf & kfLblMultiLine) { + cx = m_rc.Width(); + if (m_wf & kfLblClipVertical) { + cy = m_rc.Height(); + } else { + cy = pfnt->CalcMultilineHeight(m_szLabel, m_rc.Width()); + } + } else { + cx = GetFancyTextExtent(pfnt, m_szLabel); + cy = pfnt->GetHeight(); + } + } + + if (m_wf & kfLblCenterText) { + rcNew.left = (m_rc.left + m_rc.Width() / 2) - cx / 2; + } else if (m_wf & kfLblRightText) { + rcNew.left = m_rc.right - cx; + } else { + rcNew.left = m_rc.left; + } + rcNew.right = rcNew.left + cx; + rcNew.bottom = rcNew.top + cy; + SetRect(&rcNew, false); +} + +// FancyText supports special character sequences to control the formatting +// of the text as it is displayed. Callers should also note that the default +// font has a number of special symbol characters in it. +// +// Special characters +// ------------------ +// @ - Galaxite icon +// \ - Power (Reactor) icon +// +// Control sequences +// ----------------- +// @S. - subsequent characters' side colors are mapped to the local player's side +// @@ - escaped '@'. Use to output an '@' + +int FancyTextCore(DibBitmap *pbm, Font *pfntDefault, char *psz, int x, int y, int cch, bool fGetTextExtent) secControl; + +int DrawFancyText(DibBitmap *pbm, Font *pfntDefault, char *psz, int x, int y, int cch) +{ + return FancyTextCore(pbm, pfntDefault, psz, x, y, cch, false); +} + +int GetFancyTextExtent(Font *pfntDefault, char *psz, int cch) +{ + return FancyTextCore(NULL, pfntDefault, psz, 0, 0, cch, true); +} + +int FancyTextCore(DibBitmap *pbm, Font *pfntDefault, char *psz, int x, int y, int cch, bool fGetExtent) +{ + dword *mpscaiclr = NULL; + Font *pfnt = pfntDefault; + + if (cch == 0) + cch = strlen(psz); + + int cx = 0; + + int cchT = cch; + char *pchT = psz; + while (cchT > 0) { + char ch = *pchT++; + cchT--; + + if (ch == '@') { + if (pchT != psz + 1) { + if (fGetExtent) { + cx += pfnt->GetTextExtent(psz, pchT - psz - 1); + } else { + cx += pfnt->DrawText(pbm, psz, x, y, pchT - psz - 1, mpscaiclr); + x += cx; + } + psz = pchT; + } + + switch (*pchT) { + // "@@" outputs a single @ + case '@': + cchT--; + psz = pchT++; + continue; + + // "@S." turns on mapping to local player's side color + case 'S': + mpscaiclr = TBitmap::s_ampscaiclrSide[gpplrLocal->GetSide()]; + cchT--; + pchT++; + break; + } + + Assert(*pchT == '.'); + + // Skip past '.' + cchT--; + pchT++; + psz = pchT; + } + + } + + if (pchT != psz) { + if (fGetExtent) + cx += pfnt->GetTextExtent(psz, pchT - psz); + else + cx += pfnt->DrawText(pbm, psz, x, y, pchT - psz, mpscaiclr); + } + + return cx; +} + +// Bitmap Control + +BitmapControl::BitmapControl() +{ + m_phtbm = NULL; +} + +BitmapControl::~BitmapControl() +{ + delete m_phtbm; +} + +bool BitmapControl::Init(Form *pfrm, IniReader *pini, FindProp *pfind) +{ + // Base initialization + + if (!Control::Init(pfrm, pini, pfind)) + return false; + + // idc (x y cx cy) bitmap.tbm + + char szBitmap[kcbFilename]; + int cArgs = pini->GetPropertyValue(pfind, "%*d (%*d %*d %*d %*d) %s", szBitmap); + if (cArgs == 0) { + m_rc.right = m_rc.left; + m_rc.bottom = m_rc.top; + return true; + } + + if (cArgs != 1) + return false; + + // Distinguish between RawBitmaps and TBitmaps + // UNDONE: this should probably be handled by an HtBitmap::Init. + + int cch = strlen(szBitmap); + if (szBitmap[cch - 3] == 'r') { + Assert(szBitmap[cch - 4] == '.' && szBitmap[cch - 2] == 'b' && szBitmap[cch - 1] == 'm'); + m_phtbm = new RawBitmap(); + } else { + m_phtbm = new TBitmap(); + } + if (m_phtbm == NULL) + return false; + + if (!m_phtbm->Init(szBitmap)) + return false; + + Size siz = { 0, 0 }; + m_phtbm->GetSize(&siz); + m_rc.right = m_rc.left + siz.cx; + m_rc.bottom = m_rc.top + siz.cy; + return true; +} + +void BitmapControl::OnPaint(DibBitmap *pbm) +{ + Rect rcForm; + m_pfrm->GetRect(&rcForm); + + // Draw image + + if (m_phtbm != NULL) + + // HACK: this "& ~1" is to force word alignment on CE as required by RawBitmap::BltTo + // Character portrats have some side color in them that must be mapped to blue + + m_phtbm->BltTo(pbm, (m_rc.left + rcForm.left) & ~1, m_rc.top + rcForm.top, kside1); +} + +void BitmapControl::SetBitmap(HtBitmap *phtbm) +{ + if (m_phtbm != NULL) + delete m_phtbm; + + m_phtbm = phtbm; + + if (m_phtbm != NULL) { + Size siz; + m_phtbm->GetSize(&siz); + if (m_rc.Width() < siz.cx) + m_rc.right = m_rc.left + siz.cx; + if (m_rc.Height() < siz.cy) + m_rc.bottom = m_rc.top + siz.cy; + } + + Invalidate(); +} + +// Edit Control + +EditControl::EditControl() +{ + m_nfnt = 0; + m_szText[0] = 0; +} + +bool EditControl::Init(Form *pfrm, IniReader *pini, FindProp *pfind) +{ + // Base initialization + + if (!Control::Init(pfrm, pini, pfind)) + return false; + + // idc (x y cx cy) "Text" nfnt + + int cArgs = pini->GetPropertyValue(pfind, "%*d (%*d %*d %*d %*d) \"%s\" %d", + m_szText, &m_nfnt); + if (cArgs != 2) + return false; + + if (m_rc.Height() == 0) + m_rc.bottom = m_rc.top + gapfnt[m_nfnt]->GetHeight(); + + return true; +} + +void EditControl::OnPaint(DibBitmap *pbm) +{ + Font *pfnt = gapfnt[m_nfnt]; + Rect rcForm; + m_pfrm->GetRect(&rcForm); + int x = m_rc.left + rcForm.left; + int y = m_rc.top + rcForm.top; + pbm->Fill(x, y + m_rc.Height() - 1, m_rc.Width(), 1, GetColor(kiclrButtonBorder)); + pfnt->DrawText(pbm, m_szText, x + 1, y); +} + +void EditControl::SetText(const char *psz) +{ + strncpyz(m_szText, psz, sizeof(m_szText)); + Invalidate(); +} + +void EditControl::GetText(char *psz, int cb) +{ + strncpyz(psz, m_szText, cb); +} + +// +// List Control +// + +#define kcxyListBorder gcxyBorder +#define kcxListIntMargin PcFromFc(2) +#define kcyListLineSpace gcxyBorder + +TBitmap *ListControl::s_ptbmScrollUpUp; +TBitmap *ListControl::s_ptbmScrollUpDown; +TBitmap *ListControl::s_ptbmScrollDownUp; +TBitmap *ListControl::s_ptbmScrollDownDown; + +bool ListControl::InitClass() +{ + s_ptbmScrollUpUp = GetSharedTBitmap("scrollupup.tbm"); + s_ptbmScrollUpDown = GetSharedTBitmap("scrollupdown.tbm"); + s_ptbmScrollDownUp = GetSharedTBitmap("scrolldownup.tbm"); + s_ptbmScrollDownDown = GetSharedTBitmap("scrolldowndown.tbm"); + return true; +} + +void ListControl::ExitClass() +{ +} + +ListControl::ListControl() +{ + m_nfnt = 0; + m_pliFirst = NULL; + m_pliLast = NULL; + m_cyItem = 0; + m_iliTop = 0; + m_cli = 0; + memset(m_awfTab, 0, sizeof(m_awfTab)); + memset(m_axTab, 0xff, sizeof(m_axTab)); + m_axTab[0] = 0; + m_fDrag = false; + m_fTimerAdded = false; + m_iclrScrollPos = kiclrMediumGray; + m_fPenDown = false; +} + +ListControl::~ListControl() +{ + // Do this to free any allocated ListItem structures + + Clear(); + +#if 0 // Don't delete these because the form will do it + if (m_pbtnUp != NULL) + delete m_pbtnUp; + if (m_pbtnDown != NULL) + delete m_pbtnDown; +#endif + + if (m_fTimerAdded) { + gtimm.RemoveTimer(this); + } +} + +bool ListControl::Init(Form *pfrm, IniReader *pini, FindProp *pfind) +{ + // Base initialization + + if (!Control::Init(pfrm, pini, pfind)) + return false; + + // idc (x y cx cy) "Text" nfnt + + int cArgs = pini->GetPropertyValue(pfind, "%*d (%*d %*d %*d %*d) %d", + &m_nfnt); + if (cArgs != 1) + return false; + + Font *pfnt = gapfnt[m_nfnt]; + m_cyItem = pfnt->GetHeight() + kcyListLineSpace - pfnt->GetLineOverlap(); + m_cxEllipsis = pfnt->GetTextExtent("..."); + +#ifdef IPHONE + m_wf |= kfLstcScrollPosition; +#endif + + return true; +} + +void ListControl::GetSubRects(Rect *prcInterior, Rect *prcUpArrow, + Rect *prcDownArrow, Rect *prcScrollPosition) +{ + Rect rc = m_rc; + if (m_wf & kfLstcBorder) { + rc.Inflate(-gcxyBorder * 2, -gcxyBorder * 2); + } + + if (prcScrollPosition != NULL) { + prcScrollPosition->SetEmpty(); + } + if (prcUpArrow != NULL) { + prcUpArrow->SetEmpty(); + } + if (prcDownArrow != NULL) { + prcDownArrow->SetEmpty(); + } + + // If all the elements can be displayed without scrolling than the interior + // area can fill the whole ListControl + + Size sizArrow; + s_ptbmScrollUpUp->GetSize(&sizArrow); + if (rc.Height() / m_cyItem >= m_cli) { + if (m_wf & kfLstcKeepInteriorPositioning) { + *prcInterior = rc; + prcInterior->Inflate(0, -sizArrow.cy); + } else { + *prcInterior = rc; + } + return; + } + + if (m_wf & kfLstcScrollPosition) { + // Maintain interior positioning (with arrows) so that form + // layout doesn't change if this bit is on. + + prcInterior->Set(rc.left, rc.top + sizArrow.cy, + rc.right - gcxyBorder * 3, rc.bottom - sizArrow.cy); + + if (prcScrollPosition != NULL) { + prcScrollPosition->left = rc.right - gcxyBorder * 2; + prcScrollPosition->top = prcInterior->top; + prcScrollPosition->right = rc.right; + prcScrollPosition->bottom = prcInterior->bottom - + prcInterior->Height() % m_cyItem; + } + } else { + int xArrow = rc.left + ((rc.Width() - sizArrow.cx) / 2); + + if (prcUpArrow != NULL) { + prcUpArrow->Set(xArrow - sizArrow.cx, rc.top, + xArrow + sizArrow.cx * 2, rc.top + sizArrow.cy); + } + if (prcDownArrow != NULL) { + prcDownArrow->Set(xArrow - sizArrow.cx, rc.bottom - sizArrow.cy, + xArrow + sizArrow.cx * 2, rc.bottom); + } + if (prcInterior != NULL) { + prcInterior->Set(rc.left, rc.top + sizArrow.cy, rc.right, + rc.bottom - sizArrow.cy); + } + } +} + +void ListControl::OnPaint(DibBitmap *pbm) +{ + Rect rcForm; + m_pfrm->GetRect(&rcForm); + + Rect rc; + Rect rcScrollPos; + GetSubRects(&rc, NULL, NULL, &rcScrollPos); + rc.Offset(rcForm.left, rcForm.top); + rcScrollPos.Offset(rcForm.left, rcForm.top); + + Size sizArrow; + s_ptbmScrollUpUp->GetSize(&sizArrow); + + int cliDraw = GetVisibleItemCount(); + int cx = rc.Width(); + int x = rc.left; + int y = rc.top; + int xArrow = rc.left + ((cx - sizArrow.cx) / 2); + + // Walk through the list to the item that should be displayed first + + ListItem *pli; + int ili = 0; + for (pli = m_pliFirst; ili < m_iliTop; pli = pli->pliNext, ili++); + + int cliDrawn = 0; + for (; pli != NULL && cliDraw > 0; + pli = pli->pliNext, y += m_cyItem, cliDraw--) { + DrawItem(pbm, pli, x, y, cx, m_cyItem); + cliDrawn++; + } + + bool fNeedsScrollUp = NeedsScrollUpArrow(); + bool fNeedsScrollDown = NeedsScrollDownArrow(); + + if (m_wf & kfLstcScrollPosition) { + if (fNeedsScrollUp || fNeedsScrollDown) { + pbm->Shadow(rcScrollPos.left, rcScrollPos.top, rcScrollPos.Width(), + rcScrollPos.Height()); + pbm->Shadow(rcScrollPos.left, rcScrollPos.top, rcScrollPos.Width(), + rcScrollPos.Height()); + int y1 = rcScrollPos.Height() * m_iliTop / m_cli; + int y2 = rcScrollPos.Height() * (m_iliTop + cliDrawn) / m_cli; + pbm->Fill(rcScrollPos.left, rcScrollPos.top + y1, + rcScrollPos.Width(), y2 - y1, +#if 0 + m_fPenDown ? GetColor(kiclrWhite) : + GetColor(m_iclrScrollPos)); +#else + GetColor(m_iclrScrollPos)); +#endif + } + } else { + if (fNeedsScrollUp) { + s_ptbmScrollUpUp->BltTo(pbm, xArrow, rc.top - sizArrow.cy); + } + if (fNeedsScrollDown) { + s_ptbmScrollDownUp->BltTo(pbm, xArrow, rc.bottom); + } + } + + if (m_wf & kfLstcBorder) { + Rect rcT; + rcT = m_rc; + rcT.Offset(rcForm.left, rcForm.top); + DrawBorder(pbm, &rcT, 1, GetColor(kiclrListBorder)); + } +} + +bool ListControl::NeedsScrollUpArrow() +{ + return m_iliTop != 0; +} + +bool ListControl::NeedsScrollDownArrow() +{ + int ciliVisible = GetVisibleItemCount(); + return m_cli > m_iliTop + ciliVisible; +} + +void ListControl::OnControlSelected(word idc) +{ + int iliVisible = GetVisibleItemCount(); + if (idc == kidcScrollUpButton) { + m_iliTop -= iliVisible; + } else { + m_iliTop += iliVisible; + } + Invalidate(); +} + +void ListControl::OnControlNotify(word idc, int nNotify) +{ +} + +bool ListControl::OnControlHeld(word idc) +{ + return false; +} + +void ListControl::SetTabStops(int x0, int x1, int x2, int x3) +{ + m_axTab[0] = x0; + m_axTab[1] = x1; + m_axTab[2] = x2; + m_axTab[3] = x3; +} + +void ListControl::SetTabFlags(word wf0, word wf1, word wf2, word wf3) +{ + m_awfTab[0] = wf0; + m_awfTab[1] = wf1; + m_awfTab[2] = wf2; + m_awfTab[3] = wf3; +} + +void ListControl::DrawItem(DibBitmap *pbm, ListItem *pli, int x, int y, + int cx, int cy) +{ + if (pli->fSelected) { + pbm->Fill(x, y, cx, cy, GetColor(kiclrButtonFillHighlight)); + } + + char *pszTabNext = pli->szText - 1; + int iTab = 0; + do { + char *pszAfterTab = pszTabNext + 1; + pszTabNext = strchr(pszAfterTab, '\t'); + + // Figure out what to draw - up to the next tab + + char szT[80]; + char *pszDraw; + if (pszTabNext == NULL) { + pszDraw = pszAfterTab; + } else { + strncpyz(szT, pszAfterTab, + _min((int)sizeof(szT), pszTabNext - pszAfterTab + 1)); + pszDraw = szT; + } + + // Figure out the width of this tab column + + int cxT; + if (iTab < ARRAYSIZE(m_axTab) - 1 && m_axTab[iTab + 1] >= 0) { + cxT = m_axTab[iTab + 1] - m_axTab[iTab]; + } else { + cxT = cx - m_axTab[iTab]; + } + + // Draw + DrawText(pbm, pszDraw, x + m_axTab[iTab], y, cxT, cy, + m_awfTab[iTab]); + iTab++; + } while (pszTabNext != NULL && iTab < ARRAYSIZE(m_axTab)); +} + +void ListControl::DrawText(DibBitmap *pbm, char *psz, int x, int y, + int cx, int cy, word wf) +{ + Font *pfnt = gapfnt[m_nfnt]; + int dx = 0; + if (wf & kfLstTabCenter) { + int cxT = pfnt->GetTextExtent(psz); + if (cxT > cx) { + Rect rc; + GetSubRects(&rc); + int xT = x + (cx - cxT); + if (xT <= rc.left) { + dx = rc.left - x; + } else { + dx = xT - x; + wf &= ~kfLstTabEllipsis; + } + } else { + dx = (cx - cxT) / 2; + wf &= ~kfLstTabEllipsis; + } + } + if (wf & kfLstTabCenterOn) { + int cxT = pfnt->GetTextExtent(psz); + dx = -cxT / 2; + + } + if (wf & kfLstTabRight) { + int cxT = pfnt->GetTextExtent(psz); + dx = -cxT; + } + x += dx; + cx -= dx; + + // Make room before the next tab stop for ellipsis + + if (wf & kfLstTabEllipsis) { + pfnt->DrawTextWithEllipsis(pbm, psz, strlen(psz), x, + y + kcyListLineSpace, cx); + } else { + pfnt->DrawText(pbm, psz, x, y + kcyListLineSpace, cx, cy, false); + } +} + +void ListControl::OnPenEvent(Event *pevt) +{ + switch (pevt->eType) { + case penDownEvent: + m_fDrag = false; + m_yDrag = pevt->y; + m_iliTopDrag = m_iliTop; + m_fPenDown = true; + Invalidate(); + break; + + case penMoveEvent: + if (!m_fDrag) { + if (abs(m_yDrag - pevt->y) >= m_cyItem / 2) { + m_fDrag = true; + } + } + break; + + case penUpEvent: + m_fPenDown = false; + Invalidate(); + if (m_fDrag) { + if (m_flics.Init(1, 1.0f, 0.12f, 33, false)) { + m_yDragUp = pevt->y; + gtimm.AddTimer(this, 10); + m_fTimerAdded = true; + } + m_fDrag = false; + return; + } + break; + + default: + m_fDrag = false; + break; + } + + if (!m_fDrag) { + m_flics.Clear(); + OnPenEvent2(pevt); + return; + } + + DragScroll(pevt->y); +} + +void ListControl::DragScroll(int y) +{ + int dy = m_yDrag - y; + int ciScroll = (abs(dy) + m_cyItem / 2) / m_cyItem; + if (ciScroll == 0) { + return; + } + if (dy < 0) { + ciScroll = -ciScroll; + } + + int iliTopNew = m_iliTopDrag + ciScroll; + if (iliTopNew < 0) { + iliTopNew = 0; + } + + int cVisible = GetVisibleItemCount(); + if (iliTopNew > m_cli - cVisible) { + iliTopNew = m_cli - cVisible; + if (iliTopNew < 0) { + iliTopNew = 0; + } + } + + if (iliTopNew == m_iliTop) { + return; + } + m_iliTop = iliTopNew; + Invalidate(); + + //Trace("DragScroll: m_yDrag=%d y=%d dy=%d", m_yDrag, y, dy); +} + +void ListControl::OnTimer(long tCurrent) +{ + if (!m_flics.HasMagnitude()) { + gtimm.RemoveTimer(this); + m_fTimerAdded = false; + return; + } + + // GetPosition returns a delta. + Point pt; + m_flics.GetPosition(&pt); + DragScroll(pt.y + m_yDragUp); +} + +void ListControl::OnPenEvent2(Event *pevt) +{ + // UNDONE: track pen inside/outside of arrows and arrow pressed state + + Rect rcForm; + m_pfrm->GetRect(&rcForm); + + // Finger hittesting works as follows: + // 1. Out of the desired hit test rects, find the "closest" one + // 2. See if the event is within the "finger rect", an expansion of the + // regular rect. + + Rect rcInterior, rcUpArrow, rcDownArrow; + GetSubRects(&rcInterior, &rcUpArrow, &rcDownArrow); + rcInterior.Offset(rcForm.left, rcForm.top); + rcUpArrow.Offset(rcForm.left, rcForm.top); + rcDownArrow.Offset(rcForm.left, rcForm.top); + + // Find the closest selectable item + + int iliBottom = (m_iliTop + (rcInterior.Height() / m_cyItem)) - 1; + if (iliBottom >= m_cli) + iliBottom = m_cli - 1; + + int nDistClosest = 99999; + int iClosest = -1; + for (int ili = m_iliTop; ili <= iliBottom; ili++) { + Rect rc; + rc.left = rcInterior.left; + rc.top = rcInterior.top + (ili - m_iliTop) * m_cyItem; + rc.right = rcInterior.right; + rc.bottom = rc.top + m_cyItem; + int nDist = rc.GetDistance(pevt->x, pevt->y); + if (nDist < nDistClosest) { + iClosest = ili - m_iliTop; + nDistClosest = nDist; + } + } + + int nDistHit = 0; + if (pevt->ff & kfEvtFinger) { + nDistHit = 20; // Need a constant here + } + + // Compare this best distance against the up and down arrows. + if (!(m_wf & kfLstcScrollPosition)) { + int nDistUp = rcUpArrow.GetDistance(pevt->x, pevt->y); + int nDistDown = rcDownArrow.GetDistance(pevt->x, pevt->y); + if (nDistUp < nDistDown) { + if (nDistUp < nDistClosest) { + if (nDistUp <= nDistHit) { + if (pevt->eType == penUpEvent && NeedsScrollUpArrow()) { + gsndm.PlaySfx(ksfxGuiScrollingListSelectItem); + OnControlSelected(kidcScrollUpButton); + } + } + return; + } + } else { + if (nDistDown < nDistClosest) { + if (nDistDown <= nDistHit) { + if (pevt->eType == penUpEvent && NeedsScrollDownArrow()) { + gsndm.PlaySfx(ksfxGuiScrollingListSelectItem); + OnControlSelected(kidcScrollDownButton); + } + } + return; + } + } + } + + if (pevt->eType != penDownEvent) { + return; + } + + // Pin selection to min/max entry + + Assert(iClosest >= 0 && iClosest <= iliBottom - m_iliTop); + if (iClosest < 0) + return; + + int iliSel; + bool fOnItem; + if (nDistClosest <= nDistHit) { + iliSel = m_iliTop + iClosest; + fOnItem = true; + } else { + iliSel = -1; + fOnItem = false; + } + + int ili = 0; + bool fChange = false; + for (ListItem *pli = m_pliFirst; pli != NULL; pli = pli->pliNext, ili++) { + if (pli->fSelected != (ili == iliSel)) { + pli->fSelected = (ili == iliSel); + fChange = true; + } + } + + if (fChange) { + m_pceh->OnControlNotify(m_idc, knNotifySelectionChange); + gsndm.PlaySfx(ksfxGuiScrollingListSelectItem); + Invalidate(); + } else { + if (fOnItem && pevt->eType == penDownEvent) + m_pceh->OnControlNotify(m_idc, knNotifySelectionTap); + } +} + +void ListControl::Clear() +{ + ListItem *pli = m_pliFirst; + while (pli != NULL) { + ListItem *pliDel = pli; + pli = pli->pliNext; + delete pliDel; + } + m_pliFirst = NULL; + m_pliLast = NULL; + m_cli = 0; + m_iliTop = 0; + + Invalidate(); +} + +Font *ListControl::GetFont() +{ + return gapfnt[m_nfnt]; +} + +int ListControl::GetCount() +{ + return m_cli; +} + +void ListControl::OnSelect(int nSelect) +{ + Control::OnSelect(nSelect); +} + +int ListControl::GetVisibleItemCount() +{ + Rect rc; + GetSubRects(&rc); + return rc.Height() / m_cyItem; +} + +bool ListControl::Add(ListItem *pli) +{ + pli->fSelected = false; + pli->pliNext = NULL; + + if (m_pliLast != NULL) + m_pliLast->pliNext = pli; + else + m_pliFirst = pli; + m_pliLast = pli; + m_cli++; + + Invalidate(); + return true; +} + +bool ListControl::Add(const char *psz, void *pvData) +{ + ListItem *pli = new ListItem; + if (pli == NULL) + return false; + + strncpyz(pli->szText, psz, sizeof(pli->szText)); + pli->pvData = pvData; + + return Add(pli); +} + +void ListControl::Select(int iliSelect, bool fOnly, bool fMakeCenter) +{ + ListItem *pli = m_pliFirst; + bool fChange = false; + + // Single selection only + + if (fOnly) { + int ili = 0; + for (ListItem *pli = m_pliFirst; pli != NULL; pli = pli->pliNext, ili++) { + if (iliSelect != ili) { + if (pli->fSelected) + fChange = true; + pli->fSelected = false; + } else { + if (!pli->fSelected) + fChange = true; + pli->fSelected = true; + } + } + } + + int ili = 0; + pli = m_pliFirst; + for (; pli != NULL && ili < iliSelect; pli = pli->pliNext, ili++) + ; + + if (ili == iliSelect && pli != NULL) { + pli->fSelected = true; + + int ciliVisible = GetVisibleItemCount(); + int iliTop; + + if (fMakeCenter) { + // Almost center + iliTop = iliSelect - ciliVisible * 2 / 5; + if (iliTop < 0) { + iliTop = 0; + } + } else { + // Make sure the selected item is visible. + int ciliVisible = GetVisibleItemCount(); + iliTop = iliSelect - (iliSelect % ciliVisible); + } + + // Make sure a full list is shown if selected item is near the end. + + if (iliSelect > m_cli - ciliVisible) { + iliTop = m_cli - ciliVisible; + if (iliTop < 0) { + iliTop = 0; + } + } + + // If the selected item can be visible and the list be at + // the beginning, do that because it feels better. + + if (iliSelect < ciliVisible) { + iliTop = 0; + } + + m_iliTop = iliTop; + OnSelect(knSelUpInside); + m_pceh->OnControlNotify(m_idc, knNotifySelectionChange); + } + + if (fChange) + Invalidate(); +} + +#if 0 +// Enumerate the list Items + +ListItem *ListControl::EnumItemPtr(Enum *penm) +{ + if (penm->m_pvNext == (void *)kEnmFirst) + penm->m_pvNext = (void *)m_pliFirst; + else if (penm->m_pvNext != NULL) + penm->m_pvNext = (void *)((ListItem *)penm->m_pvNext)->pliNext; + + return (ListItem *)penm->m_pvNext; +} +#endif + +int ListControl::GetSelectedItemIndex() +{ + int i = 0; + for (ListItem *pli = m_pliFirst; pli != NULL; pli = pli->pliNext) { + if (pli->fSelected) + return i; + else + i++; + } + return -1; +} + +// Return first selected item's data pointer + +void *ListControl::GetSelectedItemData() +{ + for (ListItem *pli = m_pliFirst; pli != NULL; pli = pli->pliNext) + if (pli->fSelected) + return pli->pvData; + + return NULL; +} + +// Return first selected item's ListItem pointer + +ListItem *ListControl::GetSelectedItem() +{ + for (ListItem *pli = m_pliFirst; pli != NULL; pli = pli->pliNext) + if (pli->fSelected) + return pli; + + return NULL; +} + +// set same item's data + +void ListControl::SetSelectedItemData(void *pvData) +{ + for (ListItem *pli = m_pliFirst; pli != NULL; pli = pli->pliNext) + if (pli->fSelected) + pli->pvData = pvData; +} + +// Return first selected item's text + +bool ListControl::GetSelectedItemText(char *psz, int cb) +{ + for (ListItem *pli = m_pliFirst; pli != NULL; pli = pli->pliNext) { + if (pli->fSelected) { + strncpyz(psz, pli->szText, cb); + return true; + } + } + + return false; +} + +// Set first selected item's text + +bool ListControl::SetSelectedItemText(const char *psz) +{ + for (ListItem *pli = m_pliFirst; pli != NULL; pli = pli->pliNext) { + if (pli->fSelected) { + strncpyz(pli->szText, psz, sizeof(pli->szText)); + Invalidate(); + return true; + } + } + + return false; +} + +// +// SilkButtonControl +// + +void SilkButtonControl::OnSelect(int nSelect) +{ + switch (nSelect) { + case knSelMoveInside: + case knSelDownInside: + gsndm.PlaySfx(ksfxGuiButtonTap); + break; + } + + Control::OnSelect(nSelect); +} + +// +// SliderControl +// + +#define kcxyTrack gcxyBorder +#define kcxySlider PcFromFc(4) + +SliderControl::SliderControl() +{ + m_nMin = m_nMax = m_nValue = 0; +} + +void SliderControl::OnPaint(DibBitmap *pbm) +{ + int cx = m_rc.Width(); + int cy = m_rc.Height(); + + Rect rc; + m_pfrm->GetRect(&rc); + + rc.left += m_rc.left; + rc.top += m_rc.top; + rc.right = rc.left + cx; + rc.bottom = rc.top + cy; + + // Draw track + + pbm->Fill(rc.left, rc.top + (cy / 2), cx, kcxyTrack, GetColor(kiclrButtonBorder)); + + // Draw slider + + int x; + if (m_nMax - m_nMin <= 0) + x = 0; + else + x = (short)(((m_nValue - m_nMin) * (long)(cx - kcxySlider)) / (m_nMax - m_nMin)); + pbm->Fill(rc.left + x, rc.top, kcxySlider, cy, GetColor(kiclrButtonFill)); +} + +void SliderControl::OnSelect(int nSelect) +{ + if (nSelect == knSelDownInside) + gsndm.PlaySfx(ksfxGuiButtonTap); +} + +void SliderControl::OnPenEvent(Event *pevt) +{ + if (pevt->eType == penDownEvent || pevt->eType == penMoveEvent) { + Rect rcForm; + m_pfrm->GetRect(&rcForm); + + int x = pevt->x + (kcxySlider / 2) - (rcForm.left + m_rc.left); + + m_nValue = m_nMin + ((x * (long)(m_nMax - m_nMin)) / m_rc.Width()); + if (m_nValue < m_nMin) + m_nValue = m_nMin; + else if (m_nValue > m_nMax) + m_nValue = m_nMax; + + Invalidate(); + + m_pceh->OnControlSelected(m_idc); + } +} + +void SliderControl::SetRange(long nMin, long nMax) +{ + Assert(nMax >= nMin); + + m_nMin = nMin; + m_nMax = nMax; + if (m_nValue < m_nMin) + m_nValue = m_nMin; + if (m_nValue > m_nMax) + m_nValue = m_nMax; + + Invalidate(); +} + +void SliderControl::SetValue(long n) +{ + if (m_nValue < m_nMin) + m_nValue = m_nMin; + else if (m_nValue > m_nMax) + m_nValue = m_nMax; + else + m_nValue = n; + + Invalidate(); +} + +long SliderControl::GetValue() +{ + return m_nValue; +} + +// +// Pip Meter control +// + +TBitmap *PipMeterControl::s_ptbmPip; + +bool PipMeterControl::InitClass() +{ + s_ptbmPip = GetSharedTBitmap("pip.tbm"); + return true; +} + +void PipMeterControl::ExitClass() +{ +} + +PipMeterControl::PipMeterControl() +{ + m_ptbmPip = NULL; + m_nValue = 0; +} + +PipMeterControl::~PipMeterControl() +{ +} + +bool PipMeterControl::Init(Form *pfrm, word idc, int x, int y, int cx, int cy, + char *szPip) +{ + if (!Control::Init(pfrm, idc, x, y, cx, cy)) + return false; + + return Init(szPip); +} + +bool PipMeterControl::Init(char *szPip) +{ + if (szPip != NULL) + m_ptbmPip = GetSharedTBitmap(szPip); + else + m_ptbmPip = s_ptbmPip; + + return true; +} + +bool PipMeterControl::Init(Form *pfrm, IniReader *pini, FindProp *pfind) +{ + // Base initialization + + if (!Control::Init(pfrm, pini, pfind)) + return false; + + // idc (x y cx cy) [pip.tbm] + + char szPip[kcbFilename]; + + int cArgs = pini->GetPropertyValue(pfind, "%*d (%*d %*d %*d %*d) %s", szPip); + return Init(cArgs == 1 ? szPip : NULL); +} + +void PipMeterControl::OnPaint(DibBitmap *pbm) +{ + Rect rcForm; + m_pfrm->GetRect(&rcForm); + + Rect rcT = m_rc; + rcT.Offset(rcForm.left, rcForm.top); + + Size sizPip; + s_ptbmPip->GetSize(&sizPip); + int cxPips = (rcT.Width() * (long)m_nValue) / 100; + int cPips = (cxPips + (sizPip.cx / 2)) / sizPip.cx; + + int x = rcT.left; + for (int i = 0; i < cPips; i++) { + m_ptbmPip->BltTo(pbm, x, rcT.top); + x += sizPip.cx; + } +} + +void PipMeterControl::SetValue(int nValue) +{ + // Don't cause unnecessary invalidation + + if (nValue == m_nValue) + return; + + // for the bar to fit in the rect, nValue must be less than or equal to 100 + + Assert(nValue <= 100, "value out of PipMeter range"); + m_nValue = nValue; + Invalidate(); +} + +// +// Damage Meter control +// + +TBitmap *DamageMeterControl::s_ptbmInfantry; +TBitmap *DamageMeterControl::s_ptbmVehicle; +TBitmap *DamageMeterControl::s_ptbmStructure; + +bool DamageMeterControl::InitClass() +{ + s_ptbmInfantry = GetSharedTBitmap("damage_infantry.tbm"); + s_ptbmVehicle = GetSharedTBitmap("damage_vehicle.tbm"); + s_ptbmStructure = GetSharedTBitmap("damage_structure.tbm"); + return true; +} + +void DamageMeterControl::ExitClass() +{ +} + +DamageMeterControl::DamageMeterControl() +{ + m_puntc = NULL; +} + +DamageMeterControl::~DamageMeterControl() +{ +} + +#define kfxDamagePerSecMax itofx(16) + +void DrawPips(DibBitmap *pbm, TBitmap *ptbm, int *px, int y, int nDamage, int ctFiringRate) secControl; +void DrawPips(DibBitmap *pbm, TBitmap *ptbm, int *px, int y, int nDamage, int ctFiringRate) +{ + if (nDamage == 0) + return; + + fix fxDamagePerSec = (fix)mulfx(itofx(nDamage), divfx(itofx(100), itofx(ctFiringRate))); + int cPips = fxtoi(addfx((fix)divfx(8 * (long)fxDamagePerSec, kfxDamagePerSecMax), itofx(1) / itofx(2))); + if (cPips > 8) + cPips = 8; + + Size sizPip; + ptbm->GetSize(&sizPip); + + for (int i = 0; i < cPips; i++) { + ptbm->BltTo(pbm, *px, y); + *px += sizPip.cx + 1; + } +} + +void DamageMeterControl::OnPaint(DibBitmap *pbm) +{ + if (m_puntc == NULL) + return; + + Rect rcForm; + m_pfrm->GetRect(&rcForm); + + Rect rcT = m_rc; + rcT.Offset(rcForm.left, rcForm.top); + + int x = rcT.left; + + DrawPips(pbm, s_ptbmInfantry, &x, rcT.top, m_puntc->nInfantryDamage, m_puntc->ctFiringRate); + DrawPips(pbm, s_ptbmVehicle, &x, rcT.top, m_puntc->nVehicleDamage, m_puntc->ctFiringRate); + DrawPips(pbm, s_ptbmStructure, &x, rcT.top, m_puntc->nStructureDamage, m_puntc->ctFiringRate); +} + +void DamageMeterControl::SetUnitConsts(UnitConsts *puntc) +{ + // Don't cause unnecessary invalidation + + if (m_puntc == puntc) + return; + m_puntc = puntc; + Invalidate(); +} + +RadioButtonBarControl::RadioButtonBarControl() +{ + m_isel = 0; + m_ifnt = kifntButton; + m_cLabels = 0; + memset(m_apszLabels, 0, sizeof(m_apszLabels)); +} + +RadioButtonBarControl::~RadioButtonBarControl() +{ + for (int i = 0; i < m_cLabels; i++) { + delete m_apszLabels[i]; + } +} + +bool RadioButtonBarControl::Init(Form *pfrm, IniReader *pini, FindProp *pfind) +{ + // Base initialization + + if (!Control::Init(pfrm, pini, pfind)) { + return false; + } + + // idc (x y cx cy) "label" ifnt [isel] + + char szLabel[256]; + int ifnt; + int isel; + int cArgs = pini->GetPropertyValue(pfind, + "%*d (%*d %*d %*d %*d) \"%s\" %d %d", + szLabel, &ifnt, &isel); + if (cArgs == 2) { + return Init(szLabel, ifnt, 0); + } + if (cArgs == 3) { + return Init(szLabel, ifnt, isel); + } + + return false; +} + +bool RadioButtonBarControl::Init(const char *pszLabel, int ifnt, int isel) +{ + // Parse pszLabel into string pieces + + const char *pszNext = pszLabel; + while (true) { + const char *pszT = strchr(pszNext, '|'); + if (pszT == NULL) { + AddLabel(pszNext, strlen(pszNext)); + break; + } + AddLabel(pszNext, pszT - pszNext); + pszNext = pszT + 1; + } + + m_isel = 0; + if (isel >= 0 && isel < m_cLabels) { + m_isel = isel; + } + m_ifnt = ifnt; + + // Figure out positioning + // If cx == 0, then center horizontally + + if (m_cLabels == 0) { + return true; + } + Rect rcInner, rcOuter; + GetCellRects(0, &rcInner, &rcOuter); + for (int i = 1; i < m_cLabels; i++) { + Rect rcInnerT, rcOuterT; + GetCellRects(i, &rcInnerT, &rcOuterT); + rcOuter.Union(&rcOuterT); + } + if (m_rc.Width() == 0) { + Rect rcForm; + m_pfrm->GetRect(&rcForm); + m_rc.left = (rcForm.Width() - rcOuter.Width()) / 2; + // m_rc.top stays the same + m_rc.right = m_rc.left + rcOuter.Width(); + m_rc.bottom = m_rc.top + rcOuter.Height(); + } + return true; +} + +void RadioButtonBarControl::AddLabel(const char *psz, int cch) +{ + if (m_cLabels >= ARRAYSIZE(m_apszLabels)) { + return; + } + char *pszT = new char[cch + 1]; + if (pszT == NULL) { + return; + } + strncpyz(pszT, psz, cch + 1); + m_apszLabels[m_cLabels] = pszT; + m_cLabels++; +} + +void RadioButtonBarControl::GetCellRects(int icell, Rect *prcInner, + Rect *prcOuter) +{ + // First step through the cells up to icell, since a starting x has + // to be calculated. + + int xStart = m_rc.left; + for (int i = 0; i < icell; i++) { + Size sizT; + GetOuterCellSize(i, &sizT); + xStart += sizT.cx - gcxyBorder; + } + + Size sizT; + GetOuterCellSize(icell, &sizT); + prcOuter->Set(xStart, m_rc.top, xStart + sizT.cx, m_rc.top + sizT.cy); + *prcInner = *prcOuter; + prcInner->Inflate(-gcxyBorder, -gcxyBorder); +} + +void RadioButtonBarControl::GetOuterCellSize(int icell, Size *psiz) +{ +#define kcxLabelGap (gcxyBorder * 4) +#define kcyLabelGap (gcxyBorder * 1) + + // Pad around start and end, and border + + Font *pfnt = gapfnt[m_ifnt]; + psiz->cx = pfnt->GetTextExtent(m_apszLabels[icell]); + psiz->cx += kcxLabelGap * 2 + gcxyBorder * 2; + psiz->cy = pfnt->GetHeight(); + psiz->cy += kcyLabelGap * 2 + gcxyBorder * 2; +} + +void RadioButtonBarControl::OnPaint(DibBitmap *pbm) +{ + Rect rcForm; + m_pfrm->GetRect(&rcForm); + Font *pfnt = gapfnt[m_ifnt]; + + for (int icell = 0; icell < m_cLabels; icell++) { + Rect rcInner, rcOuter; + GetCellRects(icell, &rcInner, &rcOuter); + int xText = rcForm.left + rcInner.left + kcxLabelGap; + int yText = rcForm.top + rcInner.top + kcyLabelGap; + + if (icell < m_cLabels - 1) { + int cyDescender = gcxyBorder * 2; // hack + pbm->Fill(rcInner.right + rcForm.left, yText, + gcxyBorder, pfnt->GetHeight() - cyDescender, + GetColor(kiclrWhite)); + } + + if (icell == m_isel) { + pbm->Fill(xText, yText + pfnt->GetHeight(), + pfnt->GetTextExtent(m_apszLabels[icell]), 1, + GetColor(kiclrWhite)); + } + + pfnt->DrawText(pbm, (char *)m_apszLabels[icell], xText, yText); + } +} + +void RadioButtonBarControl::OnPenEvent(Event *pevt) +{ + // Change selection on down + + if (pevt->eType != penDownEvent) { + return; + } + + // Figure out which cell the down was in + + int isel = -1; + Rect rcForm; + m_pfrm->GetRect(&rcForm); + for (int i = 0; i < m_cLabels; i++) { + Rect rcInner, rcOuter; + GetCellRects(i, &rcInner, &rcOuter); + int x = pevt->x; + int y = rcForm.top + rcInner.top; + if (rcOuter.PtIn(x, y)) { + isel = i; + break; + } + } + + // Off the ends? + + Rect rcInner, rcOuter; + GetCellRects(0, &rcInner, &rcOuter); + if (pevt->x < rcOuter.left + rcForm.left) { + isel = 0; + } + if (m_cLabels > 0) { + GetCellRects(m_cLabels - 1, &rcInner, &rcOuter); + if (pevt->x >= rcOuter.right + rcForm.left) { + isel = m_cLabels - 1; + } + } + + // Any change? + + if (isel == m_isel) { + return; + } + + // Notify of change, invalidate to redraw + + m_isel = isel; + m_pceh->OnControlNotify(m_idc, knNotifySelectionChange); + gsndm.PlaySfx(ksfxGuiButtonTap); + Invalidate(); +} + +int RadioButtonBarControl::GetSelectionIndex() +{ + return m_isel; +} + +void RadioButtonBarControl::SetSelectionIndex(int isel) +{ + if (isel < 0 || isel >= m_cLabels) { + return; + } + if (isel == m_isel) { + return; + } + m_isel = isel; + Invalidate(); +} + +} // namespace wi diff --git a/game/miscgraphics.cpp b/game/miscgraphics.cpp new file mode 100644 index 0000000..b30ab5a --- /dev/null +++ b/game/miscgraphics.cpp @@ -0,0 +1,495 @@ +// This gets included in the windows build +// Also gets included by ARMlet code + +#define SwapDword(x) ((((x)&0xFF)<<24) | (((x)&0xFF00)<<8) | (((x)&0xFF0000)>>8) | (((x)&0xFF000000)>>24)) +#define SwapWord(x) ((((x)&0xFF)<<8) | (((x)&0xFF00)>>8)) + +void Shadow(byte *pbDst, int cbRowDst, int cx, int cy, byte *aclrMap) +{ +#if defined(__CPU_68K) && !defined(DEV_BUILD) + return; +#else + + byte *pbRow = pbDst; + while (cy-- != 0) { + int cxT = cx; + byte *pbRowT = pbRow; + while (cxT-- != 0) { +// *pbRowT++ = aclrMap[*pbRowT]; + // BUGBUG: if we post-increment pbRowT as above the CodeWarrior ARM + // compiler screws it up such that the RHS pbT is one byte ahead of + // the LHS pbT, the end result being that the shadowed area is + // shifted one pixel to the left! + + *pbRowT = aclrMap[*pbRowT]; + pbRowT++; + } + pbRow += cbRowDst; + } +#endif +} + +void Fill(byte *pbDst, int cx, int cy, int cbStride, byte bFill) +{ +#if defined(__CPU_68K) && !defined(DEV_BUILD) + return; +#else + + int cbReturn = cbStride - cx; + while (cy-- != 0) { + int cxT = cx; + while (cxT-- != 0) + *pbDst++ = bFill; + pbDst += cbReturn; + } +#endif +} + +void LeftToRightBlt(byte *pbSrc, int cxSrcStride, byte *pbDst, int cxDstStride, int cx, int cy) +{ +#if defined(__CPU_68K) && !defined(DEV_BUILD) + return; +#else + + int cwSrcReturn = (cxSrcStride - cx) / 2; + int cwDstReturn = (cxDstStride - cx) / 2; + word *pwSrc = (word *)pbSrc; + word *pwDst = (word *)pbDst; + while (cy-- != 0) { + int cw = cx / 2; + while (cw-- != 0) + *pwDst++ = *pwSrc++; + pwSrc += cwSrcReturn; + pwDst += cwDstReturn; + } +#endif +} + +void RightToLeftBlt(byte *pbSrc, int cxSrcStride, byte *pbDst, int cxDstStride, int cx, int cy) +{ +#if defined(__CPU_68K) && !defined(DEV_BUILD) + return; +#else + + if (cy <= 0) + return; + int cwSrcReturn = (cxSrcStride - cx) / 2; + int cwDstReturn = (cxDstStride - cx) / 2; + word *pwSrc = (word *)(pbSrc + (long)(cy - 1) * cxSrcStride + cx - 2); + word *pwDst = (word *)(pbDst + (long)(cy - 1) * cxDstStride + cx - 2); + while (cy-- != 0) { + int cw = cx / 2; + while (cw-- != 0) + *pwDst-- = *pwSrc--; + pwSrc -= cwSrcReturn; + pwDst -= cwDstReturn; + } +#endif +} + +void DrawTileMap(byte **ppbMap, int ctx, int cty, byte *pbDst, int cbDstStride, int cxLeftTile, int cyTopTile, int cxRightTile, int cyBottomTile, int ctxInside, int ctyInside, int cxTile, int cyTile) +{ +#if defined(__CPU_68K) && !defined(DEV_BUILD) + return; +#else + + int cxRightDst = cxLeftTile + ctxInside * cxTile + cxRightTile; + int cyBottomDst = cyTopTile + ctyInside * cyTile + cyBottomTile; + + int ctxT = ctxInside + ((cxLeftTile != 0) ? 1 : 0) + ((cxRightTile != 0) ? 1 : 0); + int ctyT = ctyInside + ((cyTopTile != 0) ? 1 : 0) + ((cyBottomTile != 0) ? 1 : 0); + + int xOffsetTile = cxLeftTile == 0 ? cxLeftTile : cxTile - cxLeftTile; + int yOffsetTile = cyTopTile == 0 ? cyTopTile : cyTile - cyTopTile; + + byte **ppbMapT = ppbMap; + int yTile = -yOffsetTile; + for (int ty = 0; ty < ctyT; ty++, yTile += cyTile) { + int xTile = -xOffsetTile; + word *pwDst = (word *)(pbDst + (long)((yTile < 0 ? 0 : yTile) * cbDstStride)); + for (int tx = 0; tx < ctxT; tx++, xTile += cxTile) { + int xLeft = xTile >= 0 ? xTile : 0; + int xRight = xTile + cxTile; + if (xRight > cxRightDst) + xRight = cxRightDst; + + if (*ppbMapT == NULL) { + ppbMapT++; + pwDst += (xRight - xLeft) / 2; + continue; + } + + int yTop = yTile >= 0 ? yTile : 0; + int yBottom = yTile + cyTile; + if (yBottom > cyBottomDst) + yBottom = cyBottomDst; + + int xSrc = xLeft - xTile; + int ySrc = yTop - yTile; + int cxSrc = xRight - xLeft; + int cySrc = yBottom - yTop; + // Swap if data is 68K but this code is armlet (v1.0 format) + +#if defined(PIL) && !defined(PNO) + dword dw = (dword)*ppbMapT; + word *pwSrcT = (word *)SwapDword(dw); +#else + word *pwSrcT = (word *)*ppbMapT; +#endif + word *pwDstT = pwDst; + + if (xSrc == 0 && ySrc == 0 && cxSrc == 16 && cySrc == 16) { + word cwSkip = (cbDstStride - 16) / 2; + for (int n = 0; n < 16; n++, pwDstT += cwSkip) { + *pwDstT++ = *pwSrcT++; + *pwDstT++ = *pwSrcT++; + *pwDstT++ = *pwSrcT++; + *pwDstT++ = *pwSrcT++; + *pwDstT++ = *pwSrcT++; + *pwDstT++ = *pwSrcT++; + *pwDstT++ = *pwSrcT++; + *pwDstT++ = *pwSrcT++; + } + } else if (xSrc == 0 && ySrc == 0 && cxSrc == 24 && cySrc == 24) { + word cwSkip = (cbDstStride - 24) / 2; + for (int n = 0; n < 24; n++, pwDstT += cwSkip) { + *pwDstT++ = *pwSrcT++; + *pwDstT++ = *pwSrcT++; + *pwDstT++ = *pwSrcT++; + *pwDstT++ = *pwSrcT++; + *pwDstT++ = *pwSrcT++; + *pwDstT++ = *pwSrcT++; + *pwDstT++ = *pwSrcT++; + *pwDstT++ = *pwSrcT++; + *pwDstT++ = *pwSrcT++; + *pwDstT++ = *pwSrcT++; + *pwDstT++ = *pwSrcT++; + *pwDstT++ = *pwSrcT++; + } + } else if (xSrc == 0 && ySrc == 0 && cxSrc == 32 && cySrc == 32) { + word cwSkip = (cbDstStride - 32) / 2; + for (int n = 0; n < 32; n++, pwDstT += cwSkip) { + *pwDstT++ = *pwSrcT++; + *pwDstT++ = *pwSrcT++; + *pwDstT++ = *pwSrcT++; + *pwDstT++ = *pwSrcT++; + *pwDstT++ = *pwSrcT++; + *pwDstT++ = *pwSrcT++; + *pwDstT++ = *pwSrcT++; + *pwDstT++ = *pwSrcT++; + *pwDstT++ = *pwSrcT++; + *pwDstT++ = *pwSrcT++; + *pwDstT++ = *pwSrcT++; + *pwDstT++ = *pwSrcT++; + *pwDstT++ = *pwSrcT++; + *pwDstT++ = *pwSrcT++; + *pwDstT++ = *pwSrcT++; + *pwDstT++ = *pwSrcT++; + } + } else { + int cwDstReturn = (cbDstStride - cxSrc) / 2; + int cwSrcReturn = (cxTile - cxSrc) / 2; + word *pwSrcT2 = (word *)(((byte *)pwSrcT) + ySrc * cxTile + xSrc); + word *pwDstT2 = pwDstT; + for (int y = 0; y < cySrc; y++) { + for (int x = 0; x < cxSrc / 2; x++) + *pwDstT2++ = *pwSrcT2++; + pwSrcT2 += cwSrcReturn; + pwDstT2 += cwDstReturn; + } + } + ppbMapT++; + pwDst += cxSrc / 2; + } + ppbMapT += ctx - ctxT; + } +#endif +} + +inline word GetSideColorOffset(int b0, int b1, int b2, int b3) +{ + return (word)((b0 * 125 + b1 * 25 + b2 * 5 + b3 * 1) * 4); +} + +word Compile8(byte *pbCompileBuffer, ScanData *psd, bool fOdd) +{ + // Make space for header + + byte *pb = pbCompileBuffer; + pb += sizeof(word); // ibasc; + pb += sizeof(word); // ibaiclr; + + // Copy over ops. Translate even / odd to asked alignment + // Filter out sc's (will go to another stream). + + byte *pop = (byte *)(psd + 1); + byte *pbT = pb; + if (fOdd) + *pbT++ = kopAlign; + while (true) { + int op = (int)(word)*pop++; + if (op >= kopEvenData1 && op <= kopEvenData48) { + if (fOdd) + op = op - kopEvenData1 + kopOddData1; + *pbT++ = op; + continue; + } + if (op >= kopOddData1 && op <= kopOddData48) { + if (fOdd) + op = op - kopOddData1 + kopEvenData1; + *pbT++ = op; + continue; + } + if (op >= kopEvenSide1 && op <= kopEvenSide16) { + int cb = op - kopEvenSide1 + 1; + pop += (cb + 1) / 2; + if (fOdd) + op = op - kopEvenSide1 + kopOddSide1; + *pbT++ = op; + continue; + } + if (op >= kopOddSide1 && op <= kopOddSide16) { + int cb = op - kopOddSide1 + 1; + pop += (cb + 1) / 2; + if (fOdd) + op = op - kopOddSide1 + kopEvenSide1; + *pbT++ = op; + continue; + } + *pbT++ = op; + if (op == kopEnd) + break; + } + if (((byte)(dword)pbT) & 1) + pbT++; + int cbOps = pbT - pb; + + // Transcode side codes into alignment sensitive mapping table offsets + + word *pwT = (word *)pbT; + pop = (byte *)(psd + 1); + while (true) { + int op = (int)(word)*pop++; + int cb; + bool fOddOp; + if (op >= kopEvenSide1 && op <= kopEvenSide16) { + cb = op - kopEvenSide1 + 1; + fOddOp = fOdd; + } else if (op >= kopOddSide1 && op <= kopOddSide16) { + cb = op - kopOddSide1 + 1; + fOddOp = !fOdd; + } else if (op == kopEnd) { + break; + } else { + continue; + } + + int cbT = cb; + byte abSideIndex[512]; + byte *pbSideIndex = abSideIndex; + while (true) { + byte sc = *pop++; + *pbSideIndex++ = ((sc >> 5) & 0x7); + cbT--; + if (cbT == 0) + break; + *pbSideIndex++ = ((sc >> 1) & 0x7); + cbT--; + if (cbT == 0) + break; + } + + pbSideIndex = abSideIndex; + cbT = cb; + if (fOddOp) { + byte b1 = *pbSideIndex++; + *pwT++ = GetSideColorOffset(b1, 0, 0, 0); + cbT--; + } + while (cbT != 0) { + byte b1, b2, b3, b4; + switch (cbT) { + case 1: + b1 = *pbSideIndex++; + *pwT++ = GetSideColorOffset(b1, 0, 0, 0); + cbT--; + break; + + case 2: + case 3: + b1 = *pbSideIndex++; + b2 = *pbSideIndex++; + *pwT++ = GetSideColorOffset(b1, b2, 0, 0); + cbT -= 2; + break; + + case 4: + default: + b1 = *pbSideIndex++; + b2 = *pbSideIndex++; + b3 = *pbSideIndex++; + b4 = *pbSideIndex++; + *pwT++ = GetSideColorOffset(b1, b2, b3, b4); + cbT -= 4; + break; + } + } + } + pbT = (byte *)pwT; + int cbScs = pbT - (pb + cbOps); + + // Now copy data and align as we go + + pop = pbCompileBuffer + 4; + byte *pbSrc = ((byte *)psd) + SwapWord(psd->ibaiclr); + while (true) { + int op = (int)(word)*pop++; + if (op == kopEnd) + break; + if (op == kopAlign) { + pbT++; + continue; + } + if (op >= kopEvenData1 && op <= kopEvenData48) { + int cb = op - kopEvenData1 + 1; + byte *pbDstT = pbT; + byte *pbSrcT = pbSrc; + while (pbDstT < &pbT[cb]) + *pbDstT++ = *pbSrcT++; + pbT += cb; + pbSrc += cb; + } + if (op >= kopOddData1 && op <= kopOddData48) { + int cb = op - kopOddData1 + 1; + byte *pbDstT = pbT; + byte *pbSrcT = pbSrc; + while (pbDstT < &pbT[cb]) + *pbDstT++ = *pbSrcT++; + pbT += cb; + pbSrc += cb; + } + } + + // Stuff in correct byte offsets + + pwT = (word *)pbCompileBuffer; + + // ibasc + *pwT++ = 4 + cbOps; + + // ibaiclr + *pwT = 4 + cbOps + cbScs; + + return pbT - pbCompileBuffer; +} + +void DrawDispatch(byte *pb, byte *pbSrc, byte *pbDst, int cbReturn, dword *mpscaiclr, byte *mpiclriclrShadow) +{ +#if defined(__CPU_68K) && !defined(DEV_BUILD) + return; +#else + + word *pw = (word *)pb; + word *psc = (word *)(pb + pw[0]); + if (pbSrc == NULL) + pbSrc = pb + pw[1]; + byte *pop = pb + sizeof(word) + sizeof(word); + + while (true) { + int op = (int)(word)*pop++; + if (op == kopAlign) { + pbSrc++; + continue; + } + if (op >= kopEvenData1 && op <= kopEvenData48) { + int cb = op - kopEvenData1 + 1; + for (; cb >= 2; cb -= 2) { + *((word *)pbDst) = *((word *)pbSrc); + pbDst += 2; + pbSrc += 2; + } + if (cb != 0) + *pbDst++ = *pbSrc++; + continue; + } + if (op >= kopOddData1 && op <= kopOddData48) { + int cb = op - kopOddData1 + 1; + *pbDst++ = *pbSrc++; + cb--; + for (; cb >= 2; cb -= 2) { + *((word *)pbDst) = *((word *)pbSrc); + pbDst += 2; + pbSrc += 2; + } + if (cb != 0) + *pbDst++ = *pbSrc++; + continue; + } + if (op >= kopEvenSide1 && op <= kopEvenSide16) { + int cb = op - kopEvenSide1 + 1; + for (; cb >= 4; cb -= 4) { + word *pwSrc = (word *)(((byte *)mpscaiclr) + (*psc)); + *((word *)pbDst) = *pwSrc++; + pbDst += 2; + *((word *)pbDst) = *pwSrc++; + pbDst += 2; + psc++; + } + for (; cb >= 2; cb -= 2) { + *((word *)pbDst) = *((word *)(((byte *)mpscaiclr) + (*psc))); + psc++; + pbDst += 2; + } + if (cb != 0) { + *pbDst++ = *((byte *)(((byte *)mpscaiclr) + (*psc))); + psc++; + } + continue; + } + if (op >= kopOddSide1 && op <= kopOddSide16) { + int cb = op - kopOddSide1 + 1; + *pbDst++ = *((byte *)(((byte *)mpscaiclr) + (*psc))); + psc++; + cb--; + for (; cb >= 4; cb -= 4) { + word *pwSrc = (word *)(((byte *)mpscaiclr) + (*psc)); + *((word *)pbDst) = *pwSrc++; + pbDst += 2; + *((word *)pbDst) = *pwSrc++; + pbDst += 2; + psc++; + } + for (; cb >= 2; cb -= 2) { + *((word *)pbDst) = *((word *)(((byte *)mpscaiclr) + (*psc))); + psc++; + pbDst += 2; + } + if (cb != 0) { + *pbDst++ = *((byte *)(((byte *)mpscaiclr) + (*psc))); + psc++; + } + continue; + } + if (op >= kopShadow1 && op <= kopShadow24) { + int cb = op - kopShadow1 + 1; + while (cb-- != 0) { + *pbDst = mpiclriclrShadow[*pbDst]; + pbDst++; + } + continue; + } + if (op >= kopTransparent1 && op <= kopTransparent32) { + int cb = op - kopTransparent1 + 1; + pbDst += cb; + continue; + } + if (op >= kopNextScan0 && op <= kopNextScan48) { + int cb = op - kopNextScan0; + pbDst += cb + cbReturn; + continue; + } + if (op == kopEnd) + return; + } +#endif +} diff --git a/game/missionlist.cpp b/game/missionlist.cpp new file mode 100644 index 0000000..cb12437 --- /dev/null +++ b/game/missionlist.cpp @@ -0,0 +1,438 @@ +#include "game/ht.h" +#include "game/httppackmanager.h" +#include "game/httppackinfomanager.h" +#include "yajl/wrapper/jsontypes.h" + +namespace wi { + +MissionList *CreateMissionList(const PackId *ppackid, MissionListType mlt) { + MissionList *pml = new MissionList; + if (pml == NULL) { + return NULL; + } + if (!pml->Init(ppackid, mlt)) { + delete pml; + return NULL; + } + return pml; +} + +MissionList::MissionList() { + m_ppdbiFirst = NULL; +} + +MissionList::~MissionList() { + // Delete the mission list + + while (m_ppdbiFirst != NULL) { + PdbItem *ppdbiT = m_ppdbiFirst; + m_ppdbiFirst = m_ppdbiFirst->ppdbiNext; + while (ppdbiT->plvliFirst != NULL) { + LvlItem *plvliT = ppdbiT->plvliFirst; + ppdbiT->plvliFirst = ppdbiT->plvliFirst->plvliNext; + delete plvliT->pszFilename; + delete plvliT; + } + delete ppdbiT->pszTitle; + delete ppdbiT; + } +} + +bool MissionList::Init(const PackId *ppackid, MissionListType mlt) { + if (m_ppdbiFirst != NULL) { + Assert(); + return true; + } + m_mlt = mlt; + + if (ppackid != NULL) { + // Add levels from just this pack + + AddLevelFiles(ppackid); + } else { + // First, enum the main game files + + PackId packidMain; + memset(&packidMain, 0, sizeof(packidMain)); + packidMain.id = PACKID_MAIN; + PdbItem *ppdbi = AddLevelFiles(&packidMain); + if (ppdbi == NULL) { + Assert(); + return false; + } + + // Now take this off the list for now, because the sorting will be + // forced later to be at the top + + PdbItem *ppdbiMain = ppdbi; + m_ppdbiFirst = NULL; + + // Fix up the main sort single player missions, so S_ goes before + // C_. + + if (m_mlt == kmltAll || m_mlt == kmltSinglePlayer) { + ResortMainMissions(ppdbi); + } + + // Now add addon missions + Enum enm; + PackId packid; + while (gppackm->EnumPacks(&enm, &packid)) { + AddLevelFiles(&packid); + } + + // Now re-insert main to be first + + ppdbiMain->ppdbiNext = m_ppdbiFirst; + m_ppdbiFirst = ppdbiMain; + } + + return true; +} + +int MissionList::GetCount() { + int clvli = 0; + for (PdbItem *ppdbi = m_ppdbiFirst; ppdbi != NULL; + ppdbi = ppdbi->ppdbiNext) { + clvli += ppdbi->clvli; + } + return clvli; +} + +bool MissionList::GetMissionDescription(int i, MissionDescription *pmd) { + PdbItem *ppdbi; + LvlItem *plvli = FindLevelItem(i, &ppdbi); + if (plvli == NULL) { + return false; + } + if (!gppackm->Mount(gpakr, &ppdbi->packid)) { + return false; + } + + const char *pszName; + switch (plvli->mt) { + case kmtStory: + pszName = "Story Missions"; + break; + + case kmtMultiplayerChallenge: + case kmtChallenge: + pszName = "Challenge Missions"; + break; + + case kmtDemo: + pszName = "Demo Missions"; + break; + + case kmtMultiplayerAddOn: + case kmtAddOn: + pszName = ppdbi->pszTitle; + if (strlen(pszName) == 0) { + pszName = ExtractPackTitle(&ppdbi->packid); + if (pszName == NULL) { + pszName = "Add-On Mission Pack"; + } + } + break; + + default: + pszName = ""; + break; + } + strncpyz(pmd->szPackName, pszName, sizeof(pmd->szPackName)); + pmd->mt = plvli->mt; + + IniReader *pini = LoadIniFile(gpakr, plvli->pszFilename); + if (pini == NULL) { + gppackm->Unmount(gpakr, &ppdbi->packid); + return false; + } + + strncpyz(pmd->szLvlTitle, "", sizeof(pmd->szLvlTitle)); + pini->GetPropertyValue("General", "Title", pmd->szLvlTitle, + sizeof(pmd->szLvlTitle)); + + if (IsMultiplayerMissionType(plvli->mt)) { + pmd->cPlayersMin = 2; + pini->GetPropertyValue("General", "MinPlayers", "%d", + &pmd->cPlayersMin); + if (pmd->cPlayersMin < 2) { + pmd->cPlayersMin = 2; + } + pmd->cPlayersMax = 2; + pini->GetPropertyValue("General", "MaxPlayers", "%d", + &pmd->cPlayersMax); + if (pmd->cPlayersMax > 4) { + pmd->cPlayersMax = 4; + } + } else { + pmd->cPlayersMin = 1; + pmd->cPlayersMax = 1; + } + + delete pini; + gppackm->Unmount(gpakr, &ppdbi->packid); + return true; +} + +const char *MissionList::ExtractPackTitle(const PackId *ppackid) { + const json::JsonMap *map = gppim->GetInfoMap(ppackid); + if (map == NULL) { + return NULL; + } + + const json::JsonObject *obj = map->GetObject("t"); + if (obj == NULL || obj->type() != json::JSONTYPE_STRING) { + delete map; + return NULL; + } + const json::JsonString *s = (json::JsonString *)obj; + + bool fWhitespace = true; + char ch; + const char *psz = s->GetString(); + while ((ch = *psz++) != 0) { + if (!isspace(ch)) { + fWhitespace = false; + break; + } + } + if (fWhitespace) { + delete map; + return NULL; + } + + const char *title = base::Format::ToString("%s", s->GetString()); + delete map; + return title; +} + +bool MissionList::GetMissionIdentifier(int i, MissionIdentifier *pmiid) { + PdbItem *ppdbi; + LvlItem *plvli = FindLevelItem(i, &ppdbi); + if (plvli == NULL) { + return false; + } + pmiid->packid = ppdbi->packid; + strncpyz(pmiid->szLvlFilename, plvli->pszFilename, + sizeof(pmiid->szLvlFilename)); + return true; +} + +MissionList::LvlItem *MissionList::FindLevelItem(int i, PdbItem **pppdbi) { + if (i < 0) { + return NULL; + } + bool fFound = false; + PdbItem *ppdbi = m_ppdbiFirst; + for (; ppdbi != NULL; ppdbi = ppdbi->ppdbiNext) { + if (i < ppdbi->clvli) { + fFound = true; + break; + } + i = i - ppdbi->clvli; + } + if (!fFound) { + return NULL; + } + for (LvlItem *plvli = ppdbi->plvliFirst; plvli != NULL; + plvli = plvli->plvliNext) { + if (i == 0) { + *pppdbi = ppdbi; + return plvli; + } + i--; + } + return NULL; +} + +MissionList::PdbItem *MissionList::AddLevelFiles(const PackId *ppackid) { + if (!gppackm->Mount(gpakr, ppackid)) { + return NULL; + } + + char szFn[kcbFilename]; + PdbItem *ppdbi = NULL; + int key = (ppackid->id == PACKID_MAIN) ? PACKENUM_FIRST : PACKENUM_LAST; + Enum enm; + while (gpakr.EnumFiles(&enm, key, szFn, sizeof(szFn))) { + int cch = strlen(szFn); + if (cch < 4) { + continue; + } + if (strcmp(&szFn[cch - 4], ".lvl") != 0) { + continue; + } + MissionListType mlt = kmltSinglePlayer; + if (IsMultiplayerMissionType(GetMissionType(ppackid, szFn))) { + mlt = kmltMultiplayer; + } else { + mlt = kmltSinglePlayer; + } + if (mlt != m_mlt && m_mlt != kmltAll) { + continue; + } + if (!PassesDemoFilter(szFn, ppackid->id != PACKID_MAIN)) { + continue; + } + + // Alloc PdbItem only if there are the desired missions + if (ppdbi == NULL) { + ppdbi = AddPdbItem(ppackid); + if (ppdbi == NULL) { + continue; + } + } + AddLvlItem(ppdbi, szFn); + } + gppackm->Unmount(gpakr, ppackid); + + return ppdbi; +} + +MissionList::PdbItem *MissionList::AddPdbItem(const PackId *ppackid) { + PdbItem *ppdbi = new PdbItem; + if (ppdbi == NULL) { + return NULL; + } + ppdbi->packid = *ppackid; + ppdbi->plvliFirst = NULL; + ppdbi->ppdbiNext = NULL; + ppdbi->clvli = 0; + ppdbi->pszTitle = NULL; + + if (ppackid->id == PACKID_MAIN) { + ppdbi->pszTitle = AllocString("Main Game"); + } else { + const json::JsonMap *map = gppim->GetInfoMap(ppackid); + if (map != NULL) { + const json::JsonObject *obj = map->GetObject("t"); + if (obj != NULL && obj->type() == json::JSONTYPE_STRING) { + const json::JsonString *title = (const json::JsonString *)obj; + ppdbi->pszTitle = AllocString(title->GetString()); + } + delete map; + } + if (ppdbi->pszTitle == NULL) { + ppdbi->pszTitle = AllocString(""); + } + } + + bool fInserted = false; + PdbItem **pppdbiT = &m_ppdbiFirst; + while ((*pppdbiT) != NULL) { + PdbItem *ppdbiT = *pppdbiT; + if (strcmp(ppdbi->pszTitle, ppdbiT->pszTitle) < 0) { + ppdbi->ppdbiNext = ppdbiT; + *pppdbiT = ppdbi; + fInserted = true; + break; + } + pppdbiT = &(*pppdbiT)->ppdbiNext; + } + if (!fInserted) { + *pppdbiT = ppdbi; + } + return ppdbi; +} + +MissionList::LvlItem *MissionList::AddLvlItem(PdbItem *ppdbi, char *pszLvl) { + LvlItem *plvli = new LvlItem; + if (plvli == NULL) { + return NULL; + } + plvli->pszFilename = AllocString(pszLvl); + if (plvli->pszFilename == NULL) { + delete plvli; + return NULL; + } + plvli->mt = GetMissionType(&ppdbi->packid, pszLvl); + plvli->plvliNext = NULL; + + bool fInserted = false; + LvlItem **pplvliT = &ppdbi->plvliFirst; + while ((*pplvliT) != NULL) { + LvlItem *plvliT = *pplvliT; + if (strcmp(plvli->pszFilename, plvliT->pszFilename) < 0) { + plvli->plvliNext = plvliT; + *pplvliT = plvli; + fInserted = true; + break; + } + pplvliT = &(*pplvliT)->plvliNext; + } + if (!fInserted) { + *pplvliT = plvli; + } + ppdbi->clvli++; + return plvli; +} + +void MissionList::ResortMainMissions(PdbItem *ppdbi) { + // Move the c_ missions after the s_ missions. + // Find the end of the list + + LvlItem **pplvliT = &ppdbi->plvliFirst; + while ((*pplvliT) != NULL) { + pplvliT = &(*pplvliT)->plvliNext; + } + + // Move all non-s_'s to the end. Keep relative order. + + while (strncmp(ppdbi->plvliFirst->pszFilename, "s_", 2) != 0) { + LvlItem *plvliT = ppdbi->plvliFirst; + ppdbi->plvliFirst = plvliT->plvliNext; + plvliT->plvliNext = NULL; + *pplvliT = plvliT; + pplvliT = &plvliT->plvliNext; + } +} + +bool MissionList::PassesDemoFilter(char *psz, bool fAddOn) { + // Everything is available if not demo + if (!gfDemo) { + return true; + } + // Addon's not available if demo + if (fAddOn) { + return false; + } + // Only m_12 available as multiplayer + if (m_mlt == kmltAll || m_mlt == kmltMultiplayer) { + return strcmp(psz, "m_12.lvl") == 0; + } + // Only s_00-03 available single player + return strcmp(psz, "s_00.lvl") == 0 || strcmp(psz, "s_01.lvl") == 0 || + strcmp(psz, "s_02.lvl") == 0 || strcmp(psz, "s_03.lvl") == 0; +} + +MissionType MissionList::GetMissionType(const PackId *ppackid, + const char *pszLvl) { + + if (ppackid->id == PACKID_MAIN) { + if (pszLvl[1] == '_') { + switch (pszLvl[0]) { + case 's': + return kmtStory; + + case 'c': + return kmtChallenge; + + case 'd': + return kmtDemo; + + case 'm': + return kmtMultiplayerChallenge; + } + } + return kmtUnknown; + } + + if (pszLvl[0] == 'm' && pszLvl[1] == '_') { + return kmtMultiplayerAddOn; + } + + return kmtAddOn; +} + +} // namespace wi diff --git a/game/missionlist.h b/game/missionlist.h new file mode 100644 index 0000000..fa64317 --- /dev/null +++ b/game/missionlist.h @@ -0,0 +1,73 @@ +#ifndef __MISSIONLIST_H__ +#define __MISSIONLIST_H__ + +#include "game/ht.h" + +namespace wi { + +enum MissionType { kmtStory, kmtChallenge, kmtAddOn, kmtDemo, + kmtMultiplayerChallenge, kmtMultiplayerAddOn, kmtUnknown }; + +struct MissionDescription { // md + char szPackName[kcbLevelTitle]; + char szLvlTitle[kcbLevelTitle]; + int cPlayersMin; + int cPlayersMax; + MissionType mt; +}; + +struct MissionIdentifier { // miid + PackId packid; + char szLvlFilename[kcbFilename]; +}; + +enum MissionListType { kmltAll, kmltSinglePlayer, kmltMultiplayer }; + +class MissionList +{ +public: + MissionList(); + ~MissionList(); + + bool Init(const PackId *ppackid, MissionListType type); + int GetCount(); + bool GetMissionDescription(int i, MissionDescription *pmd); + bool GetMissionIdentifier(int i, MissionIdentifier *pmiid); + bool IsMultiplayerMissionType(MissionType mt) { + return mt == kmtMultiplayerChallenge || mt == kmtMultiplayerAddOn; + } + +private: + struct LvlItem { // lvli + const char *pszFilename; + MissionType mt; + LvlItem *plvliNext; + }; + + struct PdbItem { // pdbi + PackId packid; + const char *pszTitle; + int clvli; + LvlItem *plvliFirst; + PdbItem *ppdbiNext; + }; + + LvlItem *FindLevelItem(int i, PdbItem **pppdbi); + PdbItem *AddLevelFiles(const PackId *ppackid); + PdbItem *AddPdbItem(const PackId *ppackid); + LvlItem *AddLvlItem(PdbItem *ppdbi, char *pszLvl); + void ResortMainMissions(PdbItem *ppdbi); + bool PassesDemoFilter(char *psz, bool fAddOn); + MissionType GetMissionType(const PackId *ppackid, const char *pszLvl); + const char *ExtractPackTitle(const PackId *ppackid); + + PdbItem *m_ppdbiFirst; + MissionListType m_mlt; +}; + +extern MissionList *CreateMissionList(const PackId *ppackid, + MissionListType mlt); + +} // namespace wi + +#endif // __MISSIONLIST_H__ diff --git a/game/mixer.cpp b/game/mixer.cpp new file mode 100644 index 0000000..56065ee --- /dev/null +++ b/game/mixer.cpp @@ -0,0 +1,232 @@ +#include "mixer.h" + +namespace wi { + +byte gabAdpcmSteppings[89][16] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 1, 1, 1, 2, 2, 0, 0, 255, 255, 255, 255, 254, 254, + 0, 0, 1, 1, 1, 1, 2, 2, 0, 0, 255, 255, 255, 255, 254, 254, + 0, 0, 1, 1, 1, 1, 2, 2, 0, 0, 255, 255, 255, 255, 254, 254, + 0, 0, 1, 1, 1, 1, 2, 2, 0, 0, 255, 255, 255, 255, 254, 254, + 0, 0, 1, 1, 1, 1, 2, 2, 0, 0, 255, 255, 255, 255, 254, 254, + 0, 0, 1, 1, 1, 1, 2, 2, 0, 0, 255, 255, 255, 255, 254, 254, + 0, 0, 1, 1, 1, 1, 2, 2, 0, 0, 255, 255, 255, 255, 254, 254, + 0, 0, 1, 1, 1, 1, 2, 2, 0, 0, 255, 255, 255, 255, 254, 254, + 0, 0, 1, 1, 1, 1, 2, 2, 0, 0, 255, 255, 255, 255, 254, 254, + 0, 0, 1, 1, 1, 1, 2, 2, 0, 0, 255, 255, 255, 255, 254, 254, + 0, 0, 1, 1, 1, 1, 2, 2, 0, 0, 255, 255, 255, 255, 254, 254, + 0, 0, 1, 1, 1, 1, 2, 2, 0, 0, 255, 255, 255, 255, 254, 254, + 0, 1, 1, 2, 2, 3, 3, 4, 0, 255, 255, 254, 254, 253, 253, 252, + 0, 1, 1, 2, 2, 3, 3, 4, 0, 255, 255, 254, 254, 253, 253, 252, + 0, 1, 1, 2, 2, 3, 3, 4, 0, 255, 255, 254, 254, 253, 253, 252, + 0, 1, 1, 2, 2, 3, 3, 4, 0, 255, 255, 254, 254, 253, 253, 252, + 0, 1, 1, 2, 2, 3, 3, 4, 0, 255, 255, 254, 254, 253, 253, 252, + 0, 1, 2, 2, 3, 4, 5, 5, 0, 255, 254, 254, 253, 252, 251, 251, + 0, 1, 2, 2, 3, 4, 5, 5, 0, 255, 254, 254, 253, 252, 251, 251, + 0, 1, 2, 2, 3, 4, 5, 5, 0, 255, 254, 254, 253, 252, 251, 251, + 0, 1, 2, 2, 3, 4, 5, 5, 0, 255, 254, 254, 253, 252, 251, 251, + 1, 1, 2, 3, 4, 5, 6, 7, 255, 255, 254, 253, 252, 251, 250, 249, + 1, 1, 2, 3, 4, 5, 6, 7, 255, 255, 254, 253, 252, 251, 250, 249, + 1, 1, 3, 4, 5, 6, 8, 9, 255, 255, 253, 252, 251, 250, 248, 247, + 1, 1, 3, 4, 5, 6, 8, 9, 255, 255, 253, 252, 251, 250, 248, 247, + 1, 2, 3, 5, 6, 8, 9, 11, 255, 254, 253, 251, 250, 248, 247, 245, + 1, 2, 3, 5, 6, 8, 9, 11, 255, 254, 253, 251, 250, 248, 247, 245, + 1, 2, 4, 5, 7, 9, 11, 12, 255, 254, 252, 251, 249, 247, 245, 244, + 1, 2, 4, 5, 7, 9, 11, 12, 255, 254, 252, 251, 249, 247, 245, 244, + 1, 2, 4, 6, 8, 10, 12, 14, 255, 254, 252, 250, 248, 246, 244, 242, + 1, 2, 5, 7, 9, 11, 14, 16, 255, 254, 251, 249, 247, 245, 242, 240, + 1, 3, 5, 8, 10, 13, 15, 18, 255, 253, 251, 248, 246, 243, 241, 238, + 1, 3, 6, 8, 11, 14, 17, 19, 255, 253, 250, 248, 245, 242, 239, 237, + 2, 3, 6, 9, 12, 15, 18, 21, 254, 253, 250, 247, 244, 241, 238, 235, + 2, 3, 7, 10, 13, 16, 20, 23, 254, 253, 249, 246, 243, 240, 236, 233, + 2, 4, 8, 11, 15, 19, 23, 26, 254, 252, 248, 245, 241, 237, 233, 230, + 2, 4, 8, 12, 16, 20, 24, 28, 254, 252, 248, 244, 240, 236, 232, 228, + 2, 5, 9, 14, 18, 23, 27, 32, 254, 251, 247, 242, 238, 233, 229, 224, + 2, 5, 10, 14, 19, 24, 29, 33, 254, 251, 246, 242, 237, 232, 227, 223, + 3, 5, 11, 16, 21, 26, 32, 37, 253, 251, 245, 240, 235, 230, 224, 219, + 3, 6, 12, 18, 24, 30, 36, 42, 253, 250, 244, 238, 232, 226, 220, 214, + 3, 7, 13, 20, 26, 33, 39, 46, 253, 249, 243, 236, 230, 223, 217, 210, + 4, 7, 14, 21, 28, 35, 42, 49, 252, 249, 242, 235, 228, 221, 214, 207, + 4, 8, 16, 23, 31, 39, 47, 54, 252, 248, 240, 233, 225, 217, 209, 202, + 4, 9, 17, 26, 34, 43, 51, 60, 252, 247, 239, 230, 222, 213, 205, 196, + 5, 10, 19, 29, 38, 48, 57, 67, 251, 246, 237, 227, 218, 208, 199, 189, + 5, 11, 21, 32, 42, 53, 63, 74, 251, 245, 235, 224, 214, 203, 193, 182, + 6, 12, 23, 35, 46, 58, 69, 81, 250, 244, 233, 221, 210, 198, 187, 175, + 6, 13, 25, 38, 50, 63, 75, 88, 250, 243, 231, 218, 206, 193, 181, 168, + 7, 14, 28, 42, 56, 70, 84, 98, 249, 242, 228, 214, 200, 186, 172, 158, + 8, 15, 31, 46, 61, 76, 92, 107, 248, 241, 225, 210, 195, 180, 164, 149, + 8, 17, 34, 50, 67, 84, 101, 117, 248, 239, 222, 206, 189, 172, 155, 139, + 9, 19, 37, 56, 74, 93, 111, 130, 247, 237, 219, 200, 182, 163, 145, 126, + 10, 20, 41, 61, 81, 101, 122, 142, 246, 236, 215, 195, 175, 155, 134, 114, + 11, 22, 45, 67, 89, 111, 134, 156, 245, 234, 211, 189, 167, 145, 122, 100, + 12, 25, 49, 74, 98, 123, 147, 172, 244, 231, 207, 182, 158, 133, 109, 84, + 14, 27, 54, 81, 108, 135, 162, 189, 242, 229, 202, 175, 148, 121, 94, 67, + 15, 30, 60, 89, 119, 149, 179, 208, 241, 226, 196, 167, 137, 107, 77, 48, + 16, 33, 66, 98, 131, 164, 197, 229, 240, 223, 190, 158, 125, 92, 59, 27, + 18, 36, 72, 108, 144, 180, 216, 252, 238, 220, 184, 148, 112, 76, 40, 4, + 20, 40, 79, 119, 158, 198, 237, 255, 236, 216, 177, 137, 98, 58, 19, 1, + 22, 44, 87, 131, 174, 218, 255, 255, 234, 212, 169, 125, 82, 38, 1, 1, + 24, 48, 96, 144, 192, 240, 255, 255, 232, 208, 160, 112, 64, 16, 1, 1, + 26, 53, 106, 158, 211, 255, 255, 255, 230, 203, 150, 98, 45, 1, 1, 1, + 29, 58, 116, 174, 232, 255, 255, 255, 227, 198, 140, 82, 24, 1, 1, 1, + 32, 64, 128, 191, 255, 255, 255, 255, 224, 192, 128, 65, 1, 1, 1, 1, +}; + +char ganStepIndexMap[89][16] = { + 0, 0, 0, 0, 2, 4, 6, 8, 0, 0, 0, 0, 2, 4, 6, 8, + 0, 0, 0, 0, 3, 5, 7, 9, 0, 0, 0, 0, 3, 5, 7, 9, + 1, 1, 1, 1, 4, 6, 8, 10, 1, 1, 1, 1, 4, 6, 8, 10, + 2, 2, 2, 2, 5, 7, 9, 11, 2, 2, 2, 2, 5, 7, 9, 11, + 3, 3, 3, 3, 6, 8, 10, 12, 3, 3, 3, 3, 6, 8, 10, 12, + 4, 4, 4, 4, 7, 9, 11, 13, 4, 4, 4, 4, 7, 9, 11, 13, + 5, 5, 5, 5, 8, 10, 12, 14, 5, 5, 5, 5, 8, 10, 12, 14, + 6, 6, 6, 6, 9, 11, 13, 15, 6, 6, 6, 6, 9, 11, 13, 15, + 7, 7, 7, 7, 10, 12, 14, 16, 7, 7, 7, 7, 10, 12, 14, 16, + 8, 8, 8, 8, 11, 13, 15, 17, 8, 8, 8, 8, 11, 13, 15, 17, + 9, 9, 9, 9, 12, 14, 16, 18, 9, 9, 9, 9, 12, 14, 16, 18, + 10, 10, 10, 10, 13, 15, 17, 19, 10, 10, 10, 10, 13, 15, 17, 19, + 11, 11, 11, 11, 14, 16, 18, 20, 11, 11, 11, 11, 14, 16, 18, 20, + 12, 12, 12, 12, 15, 17, 19, 21, 12, 12, 12, 12, 15, 17, 19, 21, + 13, 13, 13, 13, 16, 18, 20, 22, 13, 13, 13, 13, 16, 18, 20, 22, + 14, 14, 14, 14, 17, 19, 21, 23, 14, 14, 14, 14, 17, 19, 21, 23, + 15, 15, 15, 15, 18, 20, 22, 24, 15, 15, 15, 15, 18, 20, 22, 24, + 16, 16, 16, 16, 19, 21, 23, 25, 16, 16, 16, 16, 19, 21, 23, 25, + 17, 17, 17, 17, 20, 22, 24, 26, 17, 17, 17, 17, 20, 22, 24, 26, + 18, 18, 18, 18, 21, 23, 25, 27, 18, 18, 18, 18, 21, 23, 25, 27, + 19, 19, 19, 19, 22, 24, 26, 28, 19, 19, 19, 19, 22, 24, 26, 28, + 20, 20, 20, 20, 23, 25, 27, 29, 20, 20, 20, 20, 23, 25, 27, 29, + 21, 21, 21, 21, 24, 26, 28, 30, 21, 21, 21, 21, 24, 26, 28, 30, + 22, 22, 22, 22, 25, 27, 29, 31, 22, 22, 22, 22, 25, 27, 29, 31, + 23, 23, 23, 23, 26, 28, 30, 32, 23, 23, 23, 23, 26, 28, 30, 32, + 24, 24, 24, 24, 27, 29, 31, 33, 24, 24, 24, 24, 27, 29, 31, 33, + 25, 25, 25, 25, 28, 30, 32, 34, 25, 25, 25, 25, 28, 30, 32, 34, + 26, 26, 26, 26, 29, 31, 33, 35, 26, 26, 26, 26, 29, 31, 33, 35, + 27, 27, 27, 27, 30, 32, 34, 36, 27, 27, 27, 27, 30, 32, 34, 36, + 28, 28, 28, 28, 31, 33, 35, 37, 28, 28, 28, 28, 31, 33, 35, 37, + 29, 29, 29, 29, 32, 34, 36, 38, 29, 29, 29, 29, 32, 34, 36, 38, + 30, 30, 30, 30, 33, 35, 37, 39, 30, 30, 30, 30, 33, 35, 37, 39, + 31, 31, 31, 31, 34, 36, 38, 40, 31, 31, 31, 31, 34, 36, 38, 40, + 32, 32, 32, 32, 35, 37, 39, 41, 32, 32, 32, 32, 35, 37, 39, 41, + 33, 33, 33, 33, 36, 38, 40, 42, 33, 33, 33, 33, 36, 38, 40, 42, + 34, 34, 34, 34, 37, 39, 41, 43, 34, 34, 34, 34, 37, 39, 41, 43, + 35, 35, 35, 35, 38, 40, 42, 44, 35, 35, 35, 35, 38, 40, 42, 44, + 36, 36, 36, 36, 39, 41, 43, 45, 36, 36, 36, 36, 39, 41, 43, 45, + 37, 37, 37, 37, 40, 42, 44, 46, 37, 37, 37, 37, 40, 42, 44, 46, + 38, 38, 38, 38, 41, 43, 45, 47, 38, 38, 38, 38, 41, 43, 45, 47, + 39, 39, 39, 39, 42, 44, 46, 48, 39, 39, 39, 39, 42, 44, 46, 48, + 40, 40, 40, 40, 43, 45, 47, 49, 40, 40, 40, 40, 43, 45, 47, 49, + 41, 41, 41, 41, 44, 46, 48, 50, 41, 41, 41, 41, 44, 46, 48, 50, + 42, 42, 42, 42, 45, 47, 49, 51, 42, 42, 42, 42, 45, 47, 49, 51, + 43, 43, 43, 43, 46, 48, 50, 52, 43, 43, 43, 43, 46, 48, 50, 52, + 44, 44, 44, 44, 47, 49, 51, 53, 44, 44, 44, 44, 47, 49, 51, 53, + 45, 45, 45, 45, 48, 50, 52, 54, 45, 45, 45, 45, 48, 50, 52, 54, + 46, 46, 46, 46, 49, 51, 53, 55, 46, 46, 46, 46, 49, 51, 53, 55, + 47, 47, 47, 47, 50, 52, 54, 56, 47, 47, 47, 47, 50, 52, 54, 56, + 48, 48, 48, 48, 51, 53, 55, 57, 48, 48, 48, 48, 51, 53, 55, 57, + 49, 49, 49, 49, 52, 54, 56, 58, 49, 49, 49, 49, 52, 54, 56, 58, + 50, 50, 50, 50, 53, 55, 57, 59, 50, 50, 50, 50, 53, 55, 57, 59, + 51, 51, 51, 51, 54, 56, 58, 60, 51, 51, 51, 51, 54, 56, 58, 60, + 52, 52, 52, 52, 55, 57, 59, 61, 52, 52, 52, 52, 55, 57, 59, 61, + 53, 53, 53, 53, 56, 58, 60, 62, 53, 53, 53, 53, 56, 58, 60, 62, + 54, 54, 54, 54, 57, 59, 61, 63, 54, 54, 54, 54, 57, 59, 61, 63, + 55, 55, 55, 55, 58, 60, 62, 64, 55, 55, 55, 55, 58, 60, 62, 64, + 56, 56, 56, 56, 59, 61, 63, 65, 56, 56, 56, 56, 59, 61, 63, 65, + 57, 57, 57, 57, 60, 62, 64, 66, 57, 57, 57, 57, 60, 62, 64, 66, + 58, 58, 58, 58, 61, 63, 65, 67, 58, 58, 58, 58, 61, 63, 65, 67, + 59, 59, 59, 59, 62, 64, 66, 68, 59, 59, 59, 59, 62, 64, 66, 68, + 60, 60, 60, 60, 63, 65, 67, 69, 60, 60, 60, 60, 63, 65, 67, 69, + 61, 61, 61, 61, 64, 66, 68, 70, 61, 61, 61, 61, 64, 66, 68, 70, + 62, 62, 62, 62, 65, 67, 69, 71, 62, 62, 62, 62, 65, 67, 69, 71, + 63, 63, 63, 63, 66, 68, 70, 72, 63, 63, 63, 63, 66, 68, 70, 72, + 64, 64, 64, 64, 67, 69, 71, 73, 64, 64, 64, 64, 67, 69, 71, 73, + 65, 65, 65, 65, 68, 70, 72, 74, 65, 65, 65, 65, 68, 70, 72, 74, + 66, 66, 66, 66, 69, 71, 73, 75, 66, 66, 66, 66, 69, 71, 73, 75, + 67, 67, 67, 67, 70, 72, 74, 76, 67, 67, 67, 67, 70, 72, 74, 76, + 68, 68, 68, 68, 71, 73, 75, 77, 68, 68, 68, 68, 71, 73, 75, 77, + 69, 69, 69, 69, 72, 74, 76, 78, 69, 69, 69, 69, 72, 74, 76, 78, + 70, 70, 70, 70, 73, 75, 77, 79, 70, 70, 70, 70, 73, 75, 77, 79, + 71, 71, 71, 71, 74, 76, 78, 80, 71, 71, 71, 71, 74, 76, 78, 80, + 72, 72, 72, 72, 75, 77, 79, 81, 72, 72, 72, 72, 75, 77, 79, 81, + 73, 73, 73, 73, 76, 78, 80, 82, 73, 73, 73, 73, 76, 78, 80, 82, + 74, 74, 74, 74, 77, 79, 81, 83, 74, 74, 74, 74, 77, 79, 81, 83, + 75, 75, 75, 75, 78, 80, 82, 84, 75, 75, 75, 75, 78, 80, 82, 84, + 76, 76, 76, 76, 79, 81, 83, 85, 76, 76, 76, 76, 79, 81, 83, 85, + 77, 77, 77, 77, 80, 82, 84, 86, 77, 77, 77, 77, 80, 82, 84, 86, + 78, 78, 78, 78, 81, 83, 85, 87, 78, 78, 78, 78, 81, 83, 85, 87, + 79, 79, 79, 79, 82, 84, 86, 88, 79, 79, 79, 79, 82, 84, 86, 88, + 80, 80, 80, 80, 83, 85, 87, 88, 80, 80, 80, 80, 83, 85, 87, 88, + 81, 81, 81, 81, 84, 86, 88, 88, 81, 81, 81, 81, 84, 86, 88, 88, + 82, 82, 82, 82, 85, 87, 88, 88, 82, 82, 82, 82, 85, 87, 88, 88, + 83, 83, 83, 83, 86, 88, 88, 88, 83, 83, 83, 83, 86, 88, 88, 88, + 84, 84, 84, 84, 87, 88, 88, 88, 84, 84, 84, 84, 87, 88, 88, 88, + 85, 85, 85, 85, 88, 88, 88, 88, 85, 85, 85, 85, 88, 88, 88, 88, + 86, 86, 86, 86, 88, 88, 88, 88, 86, 86, 86, 86, 88, 88, 88, 88, + 87, 87, 87, 87, 88, 88, 88, 88, 87, 87, 87, 87, 88, 88, 88, 88, +}; + +#if defined(__MWERKS__) && defined(__CPU_ARM) +// The stepping tables are globals. This declspec puts in a prologue which loads up r10 with +// the globals pointer and then restores it on exit. Could also do this by passing in the +// tables as parameters. +#pragma thumb off // below declspec only works in non-thumb mode +__declspec(pace_native_callback) +#endif +void MixChannels(Channel *achnl, int cchnl, byte *pb, word cb) +{ + byte *pbT = pb; + while (pbT < &pb[cb]) { + word cMixed = 0; + word cSum1 = 0; + word cSum2 = 0; + + for (int ichnl = 0; ichnl < cchnl; ichnl++) { + Channel *pchnl = &achnl[ichnl]; + if (pchnl->pb < pchnl->pbEnd) { + int nDelta = (*pchnl->pb) >> 4; + pchnl->bSampleLast += gabAdpcmSteppings[pchnl->nStepIndex][nDelta]; + cSum1 += (word)pchnl->bSampleLast; + pchnl->nStepIndex = ganStepIndexMap[pchnl->nStepIndex][nDelta]; + nDelta = (*pchnl->pb) & 0xf; + pchnl->bSampleLast += gabAdpcmSteppings[pchnl->nStepIndex][nDelta]; + cSum2 += (word)pchnl->bSampleLast; + pchnl->nStepIndex = ganStepIndexMap[pchnl->nStepIndex][nDelta]; + pchnl->pb++; + cMixed++; + } + } + if (cMixed == 0) { + *pbT++ = 128; + *pbT++ = 128; + } else { + *pbT++ = cSum1 / cMixed; + *pbT++ = cSum2 / cMixed; + } + } +} +#if defined(__MWERKS__) && defined(__CPU_ARM) +#pragma thumb reset +#endif + +} // namespace wi \ No newline at end of file diff --git a/game/mixer.h b/game/mixer.h new file mode 100644 index 0000000..e331c38 --- /dev/null +++ b/game/mixer.h @@ -0,0 +1,20 @@ +#ifndef __MIXER_H__ +#define __MIXER_H__ + +#include "basictypes.h" + +namespace wi { + +struct Channel // chnl +{ + byte *pb; + byte *pbEnd; + byte bSampleLast; + int nStepIndex; +}; + +void MixChannels(Channel *achnl, int cchnl, byte *pb, word cb); + +} // namespace wi + +#endif // __MIXER_H__ diff --git a/game/mkwin.bat b/game/mkwin.bat new file mode 100644 index 0000000..dece507 --- /dev/null +++ b/game/mkwin.bat @@ -0,0 +1 @@ +@devenv ht.sln /rebuild Win_Debug diff --git a/game/mp_test.cpp b/game/mp_test.cpp new file mode 100644 index 0000000..de8c1f3 --- /dev/null +++ b/game/mp_test.cpp @@ -0,0 +1,584 @@ +#include "ht.h" + +#ifdef MP_STRESS + +bool gfMPStress; +int gnMPPos; + +int FindGobs(dword ffGob, WCoord wxNear, WCoord wyNear, State st, bool fAlly, int cgob, Gob **apgob) +{ + Gob *apgobSort[kcpgobMax]; + int anDistSort[kcpgobMax]; + int cgobSort = 0; + int cgobReturn = 0; + + for (Gob *pgob = ggobm.GetFirstGob(); pgob != NULL; pgob = ggobm.GetNextGob(pgob)) { + if ((pgob->GetFlags() & ffGob) != ffGob) + continue; + if (st != kstReservedNull && pgob->m_st != st) + continue; + if (pgob->GetFlags() & kfGobUnit) { + UnitGob *punt = (UnitGob *)pgob; + if (fAlly) { + if (!punt->IsAlly(gpplrLocal->GetSide())) + continue; + } else { + if (punt->IsAlly(gpplrLocal->GetSide())) + continue; + } + } + + if (wxNear == kwxInvalid) { + apgob[cgobReturn++] = pgob; + if (cgobReturn == cgob) + break; + } else { + // Remember so we can sort + + WPoint wpt; + pgob->GetPosition(&wpt); + apgobSort[cgobSort] = pgob; + anDistSort[cgobSort] = (wxNear - wpt.wx) * (wxNear - wpt.wx) + (wyNear - wpt.wy) * (wyNear - wpt.wy); + cgobSort++; + } + } + + if (wxNear == kwxInvalid) + return cgobReturn; + + // Sort what we've found + + for (int i = cgobSort - 1; i >= 0; i--) { + for (int j = 1; j <= i; j++) { + if (anDistSort[j - 1] > anDistSort[j]) { + Gob *pgobT = apgobSort[j]; + apgobSort[j] = apgobSort[j - 1]; + apgobSort[j - 1] = pgobT; + int nT = anDistSort[j]; + anDistSort[j] = anDistSort[j - 1]; + anDistSort[j - 1] = nT; + } + } + } + + // Copy the first cgob's worth + + int igob = 0; + for (; igob < cgob && igob < cgobSort; igob++) + apgob[igob] = apgobSort[igob]; + return igob; +} + +bool MPWait(int *pcupdWait, int cupdInterval) +{ + if (*pcupdWait == 0) { + *pcupdWait = cupdInterval; + return false; + } + (*pcupdWait)--; + return true; +} + +void MPMineStressUpdate() +{ + static int s_cupdWait; + if (MPWait(&s_cupdWait, 36)) + return; + + // Find all the miners, see what they are doing. If moving or guard state, give it a mine command + + for (Gob *pgob = ggobm.GetFirstGob(); pgob != NULL; pgob = ggobm.GetNextGob(pgob)) { + if (pgob->GetType() != kgtGalaxMiner || !(pgob->GetFlags() & kfGobActive)) + continue; + MinerGob *pgobMiner = (MinerGob *)pgob; + if (pgobMiner->m_fHidden) + continue; + if (pgobMiner->m_st != kstGuard && pgobMiner->m_st != kstMove) + continue; + + Message msg; + memset(&msg, 0, sizeof(msg)); + msg.mid = kmidMineCommand; + msg.MineCommand.gidTarget = kgidNull; + msg.MineCommand.wptTarget.wx = kwxInvalid; + msg.MineCommand.wptTarget.wy = kwxInvalid; + msg.smidReceiver = pgobMiner->GetId(); + gcmdq.Enqueue(&msg); + } +} + +void MPAttackStressUpdate() +{ + static int s_cupdWait; + if (MPWait(&s_cupdWait, 72)) + return; + + // Find an enemy - mobile first + +#if 0 + Gob *apgobEnemy[2]; + int cEnemies = FindGobs(kfGobUnit | kfGobActive | kfGobMobileUnit, kwxInvalid, kwxInvalid, kstReservedNull, false, ARRAYSIZE(apgobEnemy), apgobEnemy); + if (cEnemies == 0) { + // Find an enemy - anything + + cEnemies = FindGobs(kfGobUnit | kfGobActive, kwxInvalid, kwxInvalid, kstReservedNull, false, ARRAYSIZE(apgobEnemy), apgobEnemy); + if (cEnemies == 0) + return; + } +#else + Gob *apgobEnemy[2]; + int cEnemies = FindGobs(kfGobUnit | kfGobActive, kwxInvalid, kwxInvalid, kstReservedNull, false, ARRAYSIZE(apgobEnemy), apgobEnemy); + if (cEnemies == 0) + return; +#endif + + // Attack the headquarters only as a last resort + + UnitGob *puntEnemy = (UnitGob *)apgobEnemy[0]; + if (puntEnemy->GetUnitType() == kutHeadquarters) { + if (cEnemies > 1) + puntEnemy = (UnitGob *)apgobEnemy[1]; + } + + // Find a number of grouped allies that are idle + + WPoint wpt; + puntEnemy->GetPosition(&wpt); + Gob *apgob[kcpgobMax]; + int cUnits = FindGobs(kfGobMobileUnit | kfGobActive, wpt.wx, wpt.wy, kstGuard, true, ARRAYSIZE(apgob), apgob); + if (cUnits == 0) + return; + + // Send these units to attack this enemy + +#define kcpgobAttack 25 + + int cAttacking = 0; + for (int n = 0; n < cUnits && cAttacking <= kcpgobAttack; n++) { + // Limit # of attackers. Already attacking? + + Gob *pgob = apgob[n]; + if (pgob->m_st == kstAttack) { + cAttacking++; + continue; + } + + // Queue attack command + + Message msgT; + msgT.mid = kmidAttackCommand; + msgT.smidSender = apgob[n]->GetId(); + msgT.smidReceiver = apgob[n]->GetId(); + msgT.AttackCommand.wptTarget.wx = wpt.wx; + msgT.AttackCommand.wptTarget.wy = wpt.wy; + msgT.AttackCommand.gidTarget = puntEnemy->GetId(); + msgT.AttackCommand.wptTargetCenter.wx = wpt.wx; + msgT.AttackCommand.wptTargetCenter.wy = wpt.wy; + msgT.AttackCommand.tcTargetRadius = 1; + msgT.AttackCommand.wcMoveDistPerUpdate = 0; + gcmdq.Enqueue(&msgT); + cAttacking++; + } +} + +void MPMoveStressUpdate() +{ + static int s_cupdWait; + if (MPWait(&s_cupdWait, 24)) + return; + + // Find a number of grouped allies that are idle + + Gob *apgob[1]; + int cUnits = FindGobs(kfGobMobileUnit | kfGobActive, kwxInvalid, kwxInvalid, kstGuard, true, ARRAYSIZE(apgob), apgob); + if (cUnits == 0) + return; + + // Send this unit to some random place that won't cause an attack + + TCoord ctx, cty; + ggobm.GetMapSize(&ctx, &cty); + TCoord tx = GetAsyncRandom() % ctx; + TCoord ty = GetAsyncRandom() % cty; + WPoint wpt; + FindNearestFreeTile(tx, ty, &wpt); + + // Queue move command + + Message msgT; + msgT.mid = kmidMoveCommand; + msgT.smidSender = apgob[0]->GetId(); + msgT.smidReceiver = apgob[0]->GetId(); + msgT.MoveCommand.wptTarget.wx = wpt.wx; + msgT.MoveCommand.wptTarget.wy = wpt.wy; + msgT.MoveCommand.gidTarget = kgidNull; + msgT.MoveCommand.wptTargetCenter.wx = wpt.wx; + msgT.MoveCommand.wptTargetCenter.wy = wpt.wy; + msgT.MoveCommand.tcTargetRadius = 1; + msgT.MoveCommand.wcMoveDistPerUpdate = 0; + gcmdq.Enqueue(&msgT); +} + +struct UnitsMaintain { // unm + UnitType utBuilder; + int cUnits; +}; + +//Max unit counts +//#define kcStructGobsHumanMax 55 +//#define kcStructGobsComputerMax 72 +//#define kcMuntGobsHumanMax 88 +//#define kcMuntGobsComputerMax 117 + +UnitsMaintain gaunm[] = { + { kutHumanResourceCenter, 5}, // kutShortRangeInfantry + { kutHumanResourceCenter, 10}, // kutLongRangeInfantry + { kutHumanResourceCenter, 4}, // kutTakeoverSpecialist + { kutVehicleTransportStation, 5}, // kutMachineGunVehicle + { kutVehicleTransportStation, 10}, // kutLightTank + { kutVehicleTransportStation, 10}, // kutRocketVehicle + { kutVehicleTransportStation, 10}, // kutMediumTank + { kutVehicleTransportStation, 4}, // kutGalaxMiner + { kutVehicleTransportStation, 2}, // kutMobileHeadquarters + { kutHeadquarters, 10}, // kutReactor + { kutHeadquarters, 1}, // kutProcessor + { kutHeadquarters, 8}, // kutWarehouse + { kutHeadquarters, 2}, // kutHumanResourceCenter + { kutHeadquarters, 2}, // kutVehicleTransportStation + { kutHeadquarters, 1}, // kutRadar + { kutHeadquarters, 1}, // kutResearchCenter + { kutHeadquarters, 1}, // kutHeadquarters + { kutHeadquarters, 10}, // kutMachineGunTower + { kutHeadquarters, 10}, // kutRocketTower + { kutHumanResourceCenter, 1}, // kutAndy + { kutVehicleTransportStation, 5}, // kutArtillery + { kutHeadquarters, 0}, // kutReplicator + { kutHumanResourceCenter, 5}, // kutFox +}; + +WPoint FindInitPositionForStructure(BuilderGob *pbldr, int ctxReserve, int ctyReserve) +{ + WPoint wpt; + pbldr->GetPosition(&wpt); + TCoord txStart = TcFromWc(wpt.wx); + TCoord tyStart = TcFromWc(wpt.wy); + TCoord ctx, cty; + ggobm.GetMapSize(&ctx, &cty); + + for (TCoord ty = 0; ty < cty - ctyReserve; ty++) { + for (TCoord tx = 0; tx < ctx - ctxReserve; tx++) { + TCoord txT = (tx + txStart) % ctx; + TCoord tyT = (ty + tyStart) % cty; + + bool fOpen = true; + for (TCoord tyS = 0; tyS < ctyReserve; tyS++) { + for (TCoord txS = 0; txS < ctxReserve; txS++) { + if (!IsTileFree(txS + txT, tyS + tyT, kbfReserved | kbfStructure | kbfMobileUnit)) { + fOpen = false; + break; + } + } + if (!fOpen) + break; + } + if (fOpen) { + wpt.wx = WcFromTc(txT); + wpt.wy = WcFromTc(tyT); + return wpt; + } + } + } + + return wpt; +} + +void BuildUnits(UnitType ut, int cUnits, UnitType utBuilder, int *acUnitCapacityRemaining) +{ + // Find all instances of the desired unit type either built and active or queued to be built + + int cUnitsActive = gpplrLocal->GetUnitCount(ut); + if (cUnitsActive >= cUnits) + return; + + // Find all builders of type utBuilder + + int cUnitsQueued = 0; + UnitGob *apunt[kcpgobMax]; + UnitGob **ppuntT = apunt; + for (Gob *pgob = ggobm.GetFirstGob(); pgob != NULL; pgob = ggobm.GetNextGob(pgob)) { + if ((pgob->GetFlags() & (kfGobActive | kfGobStructure)) != (kfGobActive | kfGobStructure)) + continue; + UnitGob *punt = (UnitGob *)pgob; + if (punt->GetOwner() != gpplrLocal) + continue; + if (punt->GetUnitType() != utBuilder) + continue; + + // # of queue units? + + BuilderGob *pbldr = ((BuilderGob *)punt); + cUnitsQueued += pbldr->m_bq.GetUnitCount(ut); + *ppuntT++ = punt; + } + + // If # of active and queued units are met, return + + int cUnitsBuild = cUnits - (cUnitsActive + cUnitsQueued); + if (cUnitsBuild <= 0) + return; + + // If no builders for this unit type, wait for one to be built + + int cBuilders = ppuntT - apunt; + if (cBuilders == 0) + return; + + // Build! + + for (int n = 0; n < cBuilders; n++) { + // Is this builder full? + + BuilderGob *pbldr = (BuilderGob *)apunt[n]; + int *pcOpen = &acUnitCapacityRemaining[pbldr->GetId() / sizeof(Gob *)]; + if (*pcOpen == 0) + continue; + + while (*pcOpen > 0 && cUnitsBuild > 0) { + // Find coordinates for this unit + + WPoint wpt; + if (!((1UL << ut) & kumStructures)) { + pbldr->FindInitPosition(&wpt); + } else { + StructConsts *pstruc = (StructConsts *)gapuntc[ut]; + wpt = FindInitPositionForStructure(pbldr, pstruc->ctxReserve, pstruc->ctyReserve); + } + + Message msg; + memset(&msg, 0, sizeof(msg)); + msg.smidSender = ksmidNull; + msg.mid = kmidBuildOtherCommand; + msg.BuildOtherCommand.ut = (UnitType)ut; + msg.BuildOtherCommand.wpt.wx = wpt.wx; + msg.BuildOtherCommand.wpt.wy = wpt.wy; + msg.smidReceiver = pbldr->GetId(); + gcmdq.Enqueue(&msg); + + (*pcOpen)--; + cUnitsBuild--; + } + if (cUnitsBuild == 0) + break; + } +} + +void MPBuildStressUpdate() +{ + static int s_cupdWait; + if (MPWait(&s_cupdWait, 36)) + return; + + // Get the capacities of all builder gobs. Need to calc this up front since capacities don't change + // when build orders are queued + + int acUnitCapacityRemaining[kcpgobMax + 1]; + memset(acUnitCapacityRemaining, 0, sizeof(acUnitCapacityRemaining)); + for (Gob *pgob = ggobm.GetFirstGob(); pgob != NULL; pgob = ggobm.GetNextGob(pgob)) { + if ((pgob->GetFlags() & (kfGobActive | kfGobStructure)) != (kfGobActive | kfGobStructure)) + continue; + UnitGob *punt = (UnitGob *)pgob; + if (!(punt->GetConsts()->wf & (kfUntcStructureBuilder | kfUntcMobileUnitBuilder))) + continue; + BuilderGob *pbldr = (BuilderGob *)punt; + acUnitCapacityRemaining[pbldr->GetId() / sizeof(Gob *)] = pbldr->m_bq.GetRemainingCapacity(); + } + + // First ensure the builders are accounted for + + BuildUnits(kutHeadquarters, 1, kutHeadquarters, acUnitCapacityRemaining); + for (int ut = 0; ut < ARRAYSIZE(gaunm); ut++) { + if (gaunm[ut].cUnits == 0) + continue; + UnitType utBuilder = gaunm[ut].utBuilder; + BuildUnits(utBuilder, gaunm[utBuilder].cUnits, kutHeadquarters, acUnitCapacityRemaining); + } + + // Now ensure other units are accounted for. Enumerate in a random order + + UnitType autOrder[kutMax]; + memset(autOrder, 0xff, sizeof(autOrder)); + for (int ut = 0; ut < ARRAYSIZE(autOrder); ut++) { + int n; + while (true) { + n = GetAsyncRandom() % kutMax; + if (autOrder[n] == kutNone) + break; + } + autOrder[n] = ut; + } + + for (int n = 0; n < ARRAYSIZE(autOrder); n++) { + UnitType ut = autOrder[n]; + if (gaunm[ut].cUnits == 0) + continue; + if (ut == kutHeadquarters) + continue; + BuildUnits(ut, gaunm[ut].cUnits, gaunm[ut].utBuilder, acUnitCapacityRemaining); + } + + // UNDONE: kmidAbortBuildOtherCommand +} + +void MPStressUpdate() +{ + if (!gfMPStress) + return; + + MPMineStressUpdate(); // kmidMineCommand + MPAttackStressUpdate(); // kmidAttackCommand + MPMoveStressUpdate(); // kmidMoveCommand + MPBuildStressUpdate(); // kmidBuildOtherCommand, kmidAbortBuildOtherCommand + +#if 0 + MPUpgradeStressUpdate(); // kmidUpgradeCommand, kmidAbortUpgradeCommand + MPTransformStressUpdate(); // kmidTransformCommand + MPSelfDestructStressUpdate(); // kmidSelfDestructCommand + MPRepairStressUpdate(); // kmidRepairCommand +#endif +} +#endif + +#ifdef MP_DEBUG_SHAREDMEM + +HANDLE ghProcessServer; +HANDLE ghSharedMem; +SharedMemWindow *gpsmw; +bool gfMPServer; +Side gsideCurrent; +Gid ggidCurrent; +long gcupdCurrent; +GobType ggtCurrent; + +#ifndef DEBUG +#error Currently requires DEBUG since Assert is used functionally +#endif + +void MPInitSharedMemoryWindow(bool fServer) +{ + if (fServer) { + ghSharedMem = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 8192, "wi_shared_mem"); + if (ghSharedMem == NULL) + return; + gpsmw = (SharedMemWindow *)MapViewOfFile(ghSharedMem, FILE_MAP_ALL_ACCESS, 0, 0, 0); + gpsmw->pidProcess = GetCurrentProcessId(); +#ifdef DETECT_SYNC_ERRORS + gpsmw->fDetectSyncErrors = gfLockStep; +#else + gpsmw->fDetectSyncErrors = false; +#endif + } else { + ghSharedMem = OpenFileMapping(FILE_MAP_READ, FALSE, "wi_shared_mem"); + if (ghSharedMem == NULL) + return; + gpsmw = (SharedMemWindow *)MapViewOfFile(ghSharedMem, FILE_MAP_READ, 0, 0, 0); + ghProcessServer = OpenProcess(PROCESS_VM_READ, FALSE, gpsmw->pidProcess); + } + gfMPServer = fServer; +} + +void MPExitSharedMemoryWindow(bool fAmServer) +{ + UnmapViewOfFile(gpsmw); + gpsmw = NULL; + CloseHandle(ghSharedMem); + ghSharedMem = NULL; + if (!gfMPServer) { + CloseHandle(ghProcessServer); + ghProcessServer = NULL; + } +} + +void MPUpdateState() +{ + if (gfMPServer) + memcpy(gpsmw->apgobMaster, ggobm.m_apgobMaster, (kcpgobMax + 1) * sizeof(Gob *)); +} + +void MPValidateState() +{ + if (!gfMPServer && gpsmw->fDetectSyncErrors) { + for (Gob *pgob = ggobm.GetFirstGob(); pgob != NULL; pgob = ggobm.GetNextGob(pgob)) { + if (!(pgob->GetFlags() & kfGobUnit)) + continue; + UnitGob *punt = (UnitGob *)pgob; + punt->MPValidate(); + } + } +} + +void MPCopyMem(void *pvTo, void *pvFrom, int cb) +{ + Assert(!gfMPServer); + SIZE_T cbRead; + BOOL f = ReadProcessMemory(ghProcessServer, pvFrom, pvTo, cb, &cbRead); + Assert(f && cb == cbRead); +} + +Gob *MPGetGobPtr(Gid gid) +{ + Assert(!gfMPServer); + Assert(gid >= 0 && gid < (kcpgobMax + 1) * sizeof(Gob *)); + Gob *pgob = *((Gob **)((byte *)gpsmw->apgobMaster + (gid))); + Assert(((dword)pgob & 1) == 0); + return pgob; +} + +void MPValidateMemory(void *pvRemote, void *pvLocal, int cb) +{ + if (gfMPServer) + return; + + byte abT[512]; + Assert(cb <= sizeof(abT)); + + SIZE_T cbRead; + BOOL f = ReadProcessMemory(ghProcessServer, pvRemote, abT, cb, &cbRead); + Assert(f && cb == cbRead); + Assert(memcmp(abT, pvLocal, cb) == 0); +} + +byte gabRemoteCache[16 * 1024]; +void *gpvRemoteCache; +int gcbRemoteCache; +long gcupdRemoteCache; +bool gfInsideUpdate; + +void MPValidateMember2(void *pvLocal, void *pvRemote, int cbThis, int cbOffsetMem, int cbMem) +{ + if (gfMPServer) + return; + + // If inside an update, check state directly + + if (gfInsideUpdate) { + MPValidateMemory(((byte *)pvLocal) + cbOffsetMem, ((byte *)pvRemote) + cbOffsetMem, cbMem); + return; + } + + // Update our speedup cache + + if (gcupdRemoteCache == 0 || gcupdCurrent != gcupdRemoteCache || pvRemote != gpvRemoteCache || cbOffsetMem + cbMem > gcbRemoteCache) { + // Update the cache + + Assert(cbOffsetMem + cbMem <= cbThis); + MPCopyMem(gabRemoteCache, pvRemote, cbThis); + gpvRemoteCache = pvRemote; + gcbRemoteCache = cbThis; + gcupdRemoteCache = gcupdCurrent; + } + + // Check memory + + Assert(memcmp(&gabRemoteCache[cbOffsetMem], &((byte *)pvLocal)[cbOffsetMem], cbMem) == 0); +} +#endif diff --git a/game/multiplayer.h b/game/multiplayer.h new file mode 100644 index 0000000..c627fe7 --- /dev/null +++ b/game/multiplayer.h @@ -0,0 +1,10 @@ +#ifndef __MULTIPLAYER_H__ +#define __MULTIPLAYER_H__ + +namespace wi { + +extern Transport *gptra; + +} // namespace wi + +#endif // __MULTIPLAYER_H__ diff --git a/game/ops.h b/game/ops.h new file mode 100644 index 0000000..ebd41ab --- /dev/null +++ b/game/ops.h @@ -0,0 +1,41 @@ +#ifndef __OPS_H__ +#define __OPS_H__ + +namespace wi { + +enum Op { + kopEvenData1, kopEvenData2, kopEvenData3, kopEvenData4, kopEvenData5, kopEvenData6, kopEvenData7, kopEvenData8, kopEvenData9, + kopEvenData10, kopEvenData11, kopEvenData12, kopEvenData13, kopEvenData14, kopEvenData15, kopEvenData16, kopEvenData17, + kopEvenData18, kopEvenData19, kopEvenData20, kopEvenData21, kopEvenData22, kopEvenData23, kopEvenData24, kopEvenData25, + kopEvenData26, kopEvenData27, kopEvenData28, kopEvenData29, kopEvenData30, kopEvenData31, kopEvenData32, kopEvenData33, + kopEvenData34, kopEvenData35, kopEvenData36, kopEvenData37, kopEvenData38, kopEvenData39, kopEvenData40, kopEvenData41, + kopEvenData42, kopEvenData43, kopEvenData44, kopEvenData45, kopEvenData46, kopEvenData47, kopEvenData48, + kopOddData1, kopOddData2, kopOddData3, kopOddData4, kopOddData5, kopOddData6, kopOddData7, kopOddData8, kopOddData9, + kopOddData10, kopOddData11, kopOddData12, kopOddData13, kopOddData14, kopOddData15, kopOddData16, kopOddData17, + kopOddData18, kopOddData19, kopOddData20, kopOddData21, kopOddData22, kopOddData23, kopOddData24, kopOddData25, + kopOddData26, kopOddData27, kopOddData28, kopOddData29, kopOddData30, kopOddData31, kopOddData32, kopOddData33, + kopOddData34, kopOddData35, kopOddData36, kopOddData37, kopOddData38, kopOddData39, kopOddData40, kopOddData41, + kopOddData42, kopOddData43, kopOddData44, kopOddData45, kopOddData46, kopOddData47, kopOddData48, + kopEvenSide1, kopEvenSide2, kopEvenSide3, kopEvenSide4, kopEvenSide5, kopEvenSide6, kopEvenSide7, kopEvenSide8, kopEvenSide9, + kopEvenSide10, kopEvenSide11, kopEvenSide12, kopEvenSide13, kopEvenSide14, kopEvenSide15, kopEvenSide16, + kopOddSide1, kopOddSide2, kopOddSide3, kopOddSide4, kopOddSide5, kopOddSide6, kopOddSide7, kopOddSide8, kopOddSide9, + kopOddSide10, kopOddSide11, kopOddSide12, kopOddSide13, kopOddSide14, kopOddSide15, kopOddSide16, + kopShadow1, kopShadow2, kopShadow3, kopShadow4, kopShadow5, kopShadow6, kopShadow7, kopShadow8, kopShadow9, + kopShadow10, kopShadow11, kopShadow12, kopShadow13, kopShadow14, kopShadow15, kopShadow16, kopShadow17, + kopShadow18, kopShadow19, kopShadow20, kopShadow21, kopShadow22, kopShadow23, kopShadow24, + kopTransparent1, kopTransparent2, kopTransparent3, kopTransparent4, kopTransparent5, kopTransparent6, kopTransparent7, kopTransparent8, kopTransparent9, + kopTransparent10, kopTransparent11, kopTransparent12, kopTransparent13, kopTransparent14, kopTransparent15, kopTransparent16, kopTransparent17, + kopTransparent18, kopTransparent19, kopTransparent20, kopTransparent21, kopTransparent22, kopTransparent23, kopTransparent24, kopTransparent25, + kopTransparent26, kopTransparent27, kopTransparent28, kopTransparent29, kopTransparent30, kopTransparent31, kopTransparent32, + kopNextScan0, kopNextScan1, kopNextScan2, kopNextScan3, kopNextScan4, kopNextScan5, kopNextScan6, kopNextScan7, kopNextScan8, kopNextScan9, + kopNextScan10, kopNextScan11, kopNextScan12, kopNextScan13, kopNextScan14, kopNextScan15, kopNextScan16, kopNextScan17, kopNextScan18, + kopNextScan19, kopNextScan20, kopNextScan21, kopNextScan22, kopNextScan23, kopNextScan24, kopNextScan25, kopNextScan26, kopNextScan27, + kopNextScan28, kopNextScan29, kopNextScan30, kopNextScan31, kopNextScan32, kopNextScan33, kopNextScan34, kopNextScan35, kopNextScan36, + kopNextScan37, kopNextScan38, kopNextScan39, kopNextScan40, kopNextScan41, kopNextScan42, kopNextScan43, kopNextScan44, kopNextScan45, + kopNextScan46, kopNextScan47, kopNextScan48, + kopAlign, kopEnd +}; + +} // namespace wi + +#endif // __OPS_H__ diff --git a/game/picktransportform.cpp b/game/picktransportform.cpp new file mode 100644 index 0000000..c440544 --- /dev/null +++ b/game/picktransportform.cpp @@ -0,0 +1,133 @@ +#include "picktransportform.h" + +namespace wi { + +PickTransportForm::PickTransportForm() +{ + m_ptra = NULL; +} + +bool PickTransportForm::Init(FormMgr *pfrmm, IniReader *pini, word idf) +{ + if (!ShellForm::Init(pfrmm, pini, idf)) + return false; + + m_ctrad = gtram.GetTransportDescriptions(m_atrad, kctradMax); + LabelControl *plbl = + (LabelControl *)GetControlPtr(kidcNoTransportsAvailable); + if (m_ctrad != 0) + plbl->Show(false); + + for (int i = kidcTransport1; i <= kidcTransport6; i++) { + ButtonControl *pbtn = (ButtonControl *)GetControlPtr(i); + if ((i - kidcTransport1) < m_ctrad) { + pbtn->SetText(m_atrad[i - kidcTransport1].szName); + } else { + pbtn->Show(false); + } + } + + return true; +} + +bool PickTransportForm::DoModal(int *pnResult, bool fAnimate, bool fShowSound) +{ + // If there is only one valid transport, don't waste the user's time by + // making them choose it. + + if (m_ctrad != 1) + return ShellForm::DoModal(pnResult, fAnimate, fShowSound); + + dword result; + m_ptra = OpenTransport(&m_atrad[0], &result); + return m_ptra != NULL; +} + +Transport *PickTransportForm::OpenTransport(TransportDescription *ptrad, + dword *result) { + *result = knTransportOpenResultFail; + Transport *ptra = NULL; + { + char szT[64]; + sprintf(szT, "OPENING %s...", ptrad->szName); + TransportWaitingUI twui(szT); + *result = ptrad->pfnOpen(ptrad, &ptra); + } + + const char *message = NULL; + switch (*result) { + case knTransportOpenResultSuccess: + return ptra; + + default: + case knTransportOpenResultFail: + message = "Failure accessing network."; + break; + + case knTransportOpenResultNoNetwork: + message = "Please check for network connectivity."; + break; + + case knTransportOpenResultCantConnect: + message = "Could not connect to game server."; + break; + + case knTransportOpenResultNotResponding: + message = "Timed out waiting for Game server to respond."; + break; + + case knTransportOpenResultProtocolMismatch: + message = "Please upgrade to the latest version of Hostile Takeover before playing multiplayer."; + //message = "Please upgrade to the latest version of Hostile Takeover."; + break; + + case knTransportOpenResultServerFull: + message = "This server is full. Please try another server."; + break; + } + + if (message != NULL) { + HtMessageBox(kfMbWhiteBorder, "Error!", message); + } + return NULL; +} + +void PickTransportForm::OnControlSelected(word idc) +{ + if (idc == kidcCancel) { + EndForm(idc); + return; + } + + dword result; + m_ptra = OpenTransport(&m_atrad[idc - kidcTransport1], &result); + if (m_ptra != NULL) { + EndForm(idc); + } +} + +bool PickTransport(Transport **pptra) +{ + *pptra = NULL; + + PickTransportForm *pfrm = (PickTransportForm *)gpmfrmm->LoadForm( + gpiniForms, kidfPickTransport, new PickTransportForm()); + Assert(pfrm != NULL); + if (pfrm == NULL) + return true; + int idc; + pfrm->DoModal(&idc); + *pptra = pfrm->GetTransport(); + delete pfrm; + + if (gevm.IsAppStopping()) + return false; + + if (idc == kidcCancel) + return true; + + return true; +} + +} // namespace wi + diff --git a/game/picktransportform.h b/game/picktransportform.h new file mode 100644 index 0000000..48f341f --- /dev/null +++ b/game/picktransportform.h @@ -0,0 +1,35 @@ +#ifndef __MPPICKTRANSPORTFORM_H__ +#define __MPPICKTRANSPORTFORM_H__ + +#include "ht.h" + +namespace wi { + +class PickTransportForm : public ShellForm { +public: + PickTransportForm() secMultiplayer; + Transport *GetTransport() { + return m_ptra; + } + + // ShellForm overrides + + virtual bool DoModal(int *pnResult = NULL, bool fAnimate = true, + bool fShowSound = true); + + // Form overrides + + virtual bool Init(FormMgr *pfrmm, IniReader *pini, word idf); + virtual void OnControlSelected(word idc); + +private: + Transport *OpenTransport(TransportDescription *ptrad, dword *result); + + int m_ctrad; + TransportDescription m_atrad[kctradMax]; + Transport *m_ptra; +}; + +} // namespace wi + +#endif // __MPPICKTRANSPORTFORM_H__ diff --git a/game/pno_esd_release_68k.h b/game/pno_esd_release_68k.h new file mode 100644 index 0000000..ed13c15 --- /dev/null +++ b/game/pno_esd_release_68k.h @@ -0,0 +1,4 @@ +#define ESD_BUILD +#define __CPU_68K +#define PIL +#define PNO diff --git a/game/pno_esd_release_arm.h b/game/pno_esd_release_arm.h new file mode 100644 index 0000000..19d564f --- /dev/null +++ b/game/pno_esd_release_arm.h @@ -0,0 +1,4 @@ +#define ESD_BUILD +#define __CPU_ARM +#define PIL +#define PNO diff --git a/game/pno_release_68k.h b/game/pno_release_68k.h new file mode 100644 index 0000000..c6a7b31 --- /dev/null +++ b/game/pno_release_68k.h @@ -0,0 +1,5 @@ +#define DEBUG +#define DEV_BUILD +#define __CPU_68K +#define PIL +#define PNO \ No newline at end of file diff --git a/game/pno_release_arm.h b/game/pno_release_arm.h new file mode 100644 index 0000000..72af3a8 --- /dev/null +++ b/game/pno_release_arm.h @@ -0,0 +1,5 @@ +#define DEBUG +#define DEV_BUILD +#define __CPU_ARM +#define PIL +#define PNO \ No newline at end of file diff --git a/game/pno_retail_release_68k.h b/game/pno_retail_release_68k.h new file mode 100644 index 0000000..b2860ee --- /dev/null +++ b/game/pno_retail_release_68k.h @@ -0,0 +1,4 @@ +#define RETAIL_BUILD +#define __CPU_68K +#define PIL +#define PNO diff --git a/game/pno_retail_release_arm.h b/game/pno_retail_release_arm.h new file mode 100644 index 0000000..7b47807 --- /dev/null +++ b/game/pno_retail_release_arm.h @@ -0,0 +1,4 @@ +#define RETAIL_BUILD +#define __CPU_ARM +#define PIL +#define PNO diff --git a/game/progresscallback.h b/game/progresscallback.h new file mode 100644 index 0000000..2b460d8 --- /dev/null +++ b/game/progresscallback.h @@ -0,0 +1,16 @@ +#ifndef __PROGRESSCALLBACK_H__ +#define __PROGRESSCALLBACK_H__ + +namespace wi { + +class ProgressCallback { +public: + virtual void OnBegin(void *ctx, int cbLength) = 0; + virtual void OnProgress(void *ctx, int cbTotal, int cbLength) = 0; + virtual void OnFinished(void *ctx) = 0; + virtual void OnError(void *ctx, const char *pszError) = 0; +}; + +} // namespace wi + +#endif // __PROGRESSCALLBACK_H__ diff --git a/game/refmap.h b/game/refmap.h new file mode 100644 index 0000000..d41f4c2 --- /dev/null +++ b/game/refmap.h @@ -0,0 +1,43 @@ +#ifndef __REFMAP_H__ +#define __REFMAP_H__ + +#include "game/map.h" + +namespace wi { + +class RefMap : public Map { +public: + RefMap() : c_(0) { + } + + void AddRef() { + c_++; + } + + bool Release() { + c_--; + if (c_ <= 0) { + delete this; + return true; + } + return false; + } + + dword GetHash() { + dword h = 0; + KeyMap::iterator it = map_.begin(); + while (it != map_.end()) { + h ^= HashString(it->first.c_str()); + h += HashString(it->second.c_str()); + it++; + } + return h; + } + +private: + int c_; +}; + +} // namespace wi + +#endif // __REFMAP_H__ diff --git a/game/res.h b/game/res.h new file mode 100644 index 0000000..fc49c1e --- /dev/null +++ b/game/res.h @@ -0,0 +1,750 @@ +#define kidfDefault 1000 +#define kidfAreYouSure 1001 +#define kidfEmpty 1002 +#define kidfLoseSummary 1003 +#define kidfGameOptions 1004 +#define kidfTileMapTest 1005 +#define kidfContinueGame 1006 +#define kidfHrcMenu 1008 +#define kidfPickLevel 1009 +#define kidfWinSummary 1010 +#define kidfBuildInfantry 1011 +#define kidfResearchMenu 1012 +#define kidfStructMenu 1013 +#define kidfVtsMenu 1014 +#define kidfBuildVehicle 1015 +#define kidfHqMenu 1016 +#define kidfBuildStructure 1017 +#define kidfPlaceStructure 1018 +#define kidfTestOptions 1019 +#define kidfStartup 1021 +#define kidfPickTransport 1022 +#define kidfHostMultiplayer 1024 +#define kidfObjectives 1025 +#define kidfGameStart 1026 +#define kidfChangeDisplayMode 1028 +#define kidfMessageBox 1029 +#define kidfUnitMenu 1030 +#define kidfMobileHqMenu 1031 +#define kidfMinerMenu 1032 +#define kidfMemoryUse 1033 +#define kidfRoom 1034 +#define kidfCreateGame 1035 +#define kidfSimUI 1036 +#define kidfInputUI 1037 +#define kidfLoadGame 1038 +#define kidfSaveGame 1039 +#define kidfEcomLarge 1040 +#define kidfEcomSmall 1041 +#define kidfHelp 1042 +#define kidfPlay 1043 +#define kidfInGameMenu 1044 +#define kidfUpgrade 1045 +#define kidfMiniMap 1046 +#define kidfCutScene 1047 +#define kidfInGameOptions 1049 +#define kidfSoundOptions 1050 +#define kidfColorOptions 1051 +#define kidfDisplayOptions 1052 +#define kidfPerformanceOptions 1053 +#define kidfGobCount 1054 +#define kidfDrmCode 1055 +#define kidfDrmKey 1056 +#define kidfRegisterNow 1057 +#define kidfMessageBoxQuery 1058 +#define kidfInputPanel 1059 +#define kidfDeleteMissionPack 1060 +#define kidfLoading 1061 +#define kidfWaiting 1062 +#define kidfMultiplayerLoseSummary 1063 +#define kidfMultiplayerWinSummary 1064 +#define kidfMultiplayerObjectives 1065 +#define kidfBluetoothPatchQuery 1066 +#define kidfRoomWide 1067 +#define kidfCreateGameWide 1068 +#define kidfSelectMissionWide 1069 +#define kidfDownloadMissionPackWide 1070 +#define kidfDownloadBox 1071 +#define kidfHelpWide 1072 +#define kidfLobby 1073 +#define kidfLogin 1074 +#define kidfCreateRoom 1075 +#define kidfChooseServer 1076 +#define kidfAddOnSingleMulti 1077 + +#define kifntDefault 0 +#define kifntShadow 1 +#define kifntButton 2 +#define kifntTitle 3 +#define kifntEcom 4 +#define kifntHud 5 +#define kcFonts 6 + +#define kidcOk 2000 +#define kidcCancel 2001 +#define kidcRunTests 2002 +#define kidcMiniMap 2003 +#define kidcHelp 2004 +#define kidcDefault 2005 +#define kidcAlert 2006 + +// Gob types + +// WARNING: the value and order of these constants cannot be +// changed without updating 'M' + +#define kgtNone 0 +#define kgtShortRangeInfantry 1 +#define kgtLongRangeInfantry 2 +#define kgtHumanResourceCenter 3 +#define kgtSurfaceDecal 4 +#define kgtScenery 5 +#define kgtAnimation 6 +#define kgtReactor 7 +#define kgtProcessor 8 +#define kgtStructure 9 +#define kgtUnit 10 +#define kgtGalaxMiner 11 +#define kgtHeadquarters 12 +#define kgtResearchCenter 13 +#define kgtVehicleTransportStation 14 +#define kgtRadar 15 +#define kgtLightTank 16 +#define kgtMediumTank 17 +#define kgtMachineGunVehicle 18 +#define kgtRocketVehicle 19 +#define kgtTakeoverSpecialist 20 +#define kgtWarehouse 21 +#define kgtMobileHeadquarters 22 +#define kgtOvermind 23 +#define kgtTankShot 24 +#define kgtRocket 25 +#define kgtMachineGunTower 26 +#define kgtRocketTower 27 +#define kgtScorch 28 +#define kgtSmoke 29 +#define kgtPuff 30 +#define kgtBullet 31 +#define kgtArtillery 32 +#define kgtArtilleryShot 33 +#define kgtAndy 34 +#define kgtReplicator 35 +#define kgtActivator 36 +#define kgtFox 37 +#define kgtAndyShot 38 + +#include "../mpshared/side.h" + +// Startup form constants + +#define kidcPlaySinglePlayer 1001 +#define kidcPlayMultiPlayer 1002 +#define kidcPlay 1003 +#define kidcSetupGame 1004 +#define kidcBuyMe 1005 +#define kidcCredits 1006 +#define kidcExitGame 1007 +#define kidcLoadSavedGame 1008 +#define kidcPlayDemo 1500 +#define kidcPlayMission 1010 +#define kidcVersion 1011 +#define kidcDownloadMissions 1501 +#define kidcForums 1502 +#define kidcLeaderboard 1503 + +// Play Solo form constants + +#define kidcBeginNewGame 1012 +#define kidcPlayChallengeLevel 1013 +#define kidcPlayStoryMission 1014 +#define kidcPlaybackGame 1015 +//#define kidcLoadSavedGame 1008 + +// SimUIForm constants + +#define kidcStatusLabel 1001 +#define kidcFps 1004 +#define kidcObjective 1005 +#define kidcCountdown 1006 + +// InputUIForm constants + +#define kidcMenuButton 1002 +#define kidcGraffitiScroll 1012 +#define kidcAppsSilkButton 1013 +#define kidcMenuSilkButton 1014 +#define kidcCalcSilkButton 1015 +#define kidcFindSilkButton 1016 +#define kidcCreditsLabel 1018 +#define kidcPower 1019 + +// InGameMenuForm constants + +#define kidcSaveGame 1001 +#define kidcLoadGame 1002 +#define kidcOptions 1003 +#define kidcRestartMission 1004 +#define kidcAbortMission 1005 +#define kidcObjectives 1006 +//#define kidcHelp 1006 + +// TestOptionsForm constants + +#define kidcDrawPaths 1002 +#define kidcOvermind 1003 +#define kidcClearFog 1004 +#define kidcDrawLines 1005 +#define kidcShowStats 1006 +#define kidcShowFPS 1007 +#define kidcSuspendUpdates 1008 +#define kidcMaxRepaint 1009 +#define kidcAutosave 1010 +#define kidcMemoryUse 1011 +#define kidcLockStep 1012 +#define kidcDrawUpdateRects 1013 +//#define kidcGraphStats 1014 +#define kidcGodMode 1015 +//#define kidcFlashEnemyTargets 1016 +//#define kidcHelp 1017 +#define kidcNextPage 1018 +#define kidcPrevPage 1019 +#define kidcIndex 1020 +#define kidcBack 1021 +#define kidcGobCount 1022 +#define kidcBreak 1023 +#define kidcStylusUI 1024 +#define kidcMoveIndicator 1025 +#define kidcHoldSelect 1026 + +// MemoryUseForm constants + +#define kidcDynDbInitial 1001 +#define kidcMmgrDynDbReserve 1002 +#define kidcDynUse 1003 +#define kidcMmgrUse 1004 +#define kidcCacheUse 1005 +#define kidcClearCache 1006 +#define kidcLimitCache 1007 +#define kidcAdd10KCache 1008 +#define kidcSub10KCache 1009 +#define kidcCacheLimit 1010 + +// Game Over form constants + +#define kidcMessage 1001 + +// Login form constants + +#define kidcAnonymous 1200 +#define kidcLogin 1201 +#define kidcRegister 1202 +#define kidcUpdateAccount 1203 + +// Lobby form constants + +#define kidcRoomList 1200 +#define kidcJoinRoom 1201 +#define kidcNewRoom 1202 +#define kidcSignOut 1203 +#define kidcLurkerCount 1204 + +// CreateRoom constants + +#define kidcRoomNameLabel 1200 +#define kidcRoomName 1201 +#define kidcRoomNamePanel 1202 +#define kidcPrivate 1203 + +// PlayMultiplayer form constants + +#define kidcJoinGame 1001 +#define kidcNewGame 1002 +#define kidcGameList 1003 +#define kidcChat 1904 +#define kidcPlayerName 1005 +#define kidcSearching 1006 +#define kidcPlayerNamePanel 1007 +#define kidcPlayerNameLabel 1008 +#define kidcStatus 1009 +#define kidcSpecify 1010 +#define kidcNoGames 1011 + +// CreateNewMultiplayer form constants + +#define kidcPassword 1101 +#define kidcMapList 1102 + +// MeetingForm constants + +#define kidcGameName 1001 +#define kidcMapName 1002 +#define kidcNumPlayers 1003 +#define kidcPlayerList 1004 +#define kidcGameNameLabel 1005 +#define kidcGameNamePanel 1006 +#define kidcPasswordLabel 1107 +#define kidcPasswordPanel 1108 +#define kidcAddress 1009 +#define kidcAddressLabel 1010 + +// PickLevelForm constants + +#define kidcLevelList 1001 +#define kidcCategories 1500 +#define kidcAddOnMessage 1501 +#define kidcStoryList 1502 +#define kidcChallengeList 1503 +#define kidcAddOnList 1504 + +// DownloadMissionPack constants + +#define kidcMissionPackList 1001 +#define kidcMissionPackInfo 1002 +#define kidcNumMissions 1010 +#define kidcDiscuss 1011 + +// PickTransportForm constants + +#define kidcNoTransportsAvailable 1001 +#define kidcTransport1 1101 +#define kidcTransport2 1102 +#define kidcTransport3 1103 +#define kidcTransport4 1104 +#define kidcTransport5 1105 +#define kidcTransport6 1106 + +// UnitMenu constants + +#define kidcTitle 1200 +#define kidcCantTransform 1201 + +// Relocatable UnitMenu buttons + +#define kidcRelocButtonMin 1300 + +#define kidcRepair 1300 +#define kidcSelfDestruct 1301 +#define kidcAbortBuild 1302 +#define kidcAbortRepair 1303 +#define kidcBuild 1304 +#define kidcAbortUpgrade 1304 +#define kidcResearch 1305 +#define kidcTransform 1306 +#define kidcDeliver 1306 + +#define kidcRelocButtonMax 1307 + +// UnitBuildForm constants + +#define kidcName 1001 +#define kidcCost 1002 +#define kidcMoveRate 1003 +#define kidcMoveRateMeter 1103 +#define kidcArmorStrength 1004 +#define kidcArmorStrengthMeter 1104 +#define kidcWeaponStrength 1005 +#define kidcWeaponStrengthMeter 1105 +#define kidcWeaponRange 1006 +#define kidcWeaponRangeMeter 1106 +#define kidcList 1007 +#define kidcOrder 1008 +#define kidcCancelOrder 1009 +#define kidcDescription 1010 +#define kidcCostMeter 1101 +#define kidcLimitReached 1200 + +// BuildStructure form constants (reuses many BuildVehicle form constants) + +#define kidcPowerSupply 1011 +#define kidcPowerSupplyMeter 1111 +#define kidcPowerDemand 1012 +#define kidcPowerDemandMeter 1112 + +// EcomForm constants + +//#define kidcMessage 1001 +#define kidcFrom 1011 +#define kidcTo 1012 +#define kidcFromBitmap 1014 +#define kidcToBitmap 1015 +#define kidcEcomText 1016 + +// CutSceneForm constants + +//#define kidcMessage 1001 +#define kidcBitmap 1002 + +// Upgrade form constants + +#define kidcPrerequisites 1201 +#define kidcPrerequisitesLabel 1202 +#define kidcCostLabel 1203 + +// Objectives form constants + +#define kidcMissionTitle 1001 +#define kidcMissionResult 1002 +#define kidcStatistics 1003 +//#define kidcRestartMission 1004 +//#define kidcAbortMission 1005 +#define kidcObjectiveText1 1101 +#define kidcObjectiveText2 1102 +#define kidcObjectiveText3 1103 +#define kidcObjectiveText4 1104 +#define kidcObjectiveStatus1 1201 +#define kidcObjectiveStatus2 1202 +#define kidcObjectiveStatus3 1203 +#define kidcObjectiveStatus4 1204 +#define kidcPage1 1300 +#define kidcObjectiveInfo 1301 +#define kidcPage2 1400 +#define kidcMobileUnitsKilled 1401 +#define kidcStructuresKilled 1402 +#define kidcMobileUnitsLost 1403 +#define kidcStructuresLost 1404 +#define kidcCreditsAction 1405 +#define kidcGameTime 1406 +#define kidcInfo 1407 +#define kidcRankTitle 1408 +#define kidcPage3 1500 + +// GameOptions constants + +#define kidcInGameOptions 1001 +#define kidcSoundOptions 1002 +#define kidcPerformanceOptions 1003 +#define kidcColorOptions 1004 +#define kidcDisplayOptions 1005 +#define kidcDeleteMissionPack 1007 + +// InGameOptions constants + +#define kidcGameSpeed 1201 +#define kidcGameSpeedLabel 1202 +#define kidcLassoSelection 1003 +#define kidcEasy 1004 +#define kidcNormal 1005 +#define kidcHard 1006 +#define kidc1Select2Scroll 1203 +#define kidcScrollSpeed 1204 +#define kidcScrollSpeedLabel 1205 + +// SoundOptions constants + +#define kidcVol 1001 +#define kidcVolLabel 1002 +#define kidcMute 1003 +#define kidcVolumeString 1004 + +// ColorOptions constants + +#define kidcHueLabelString 1001 +#define kidcHue 1002 +#define kidcHueLabel 1003 +#define kidcSatLabelString 1004 +#define kidcSat 1005 +#define kidcSatLabel 1006 +#define kidcLumLabelString 1007 +#define kidcLum 1008 +#define kidcLumLabel 1009 + +// DisplayOptions constants + +#define kidcModesList 1001 + +// PerformanceOptions constants + +#define kidcRocketShots 1001 +#define kidcRocketTrails 1002 +#define kidcRocketImpacts 1003 +#define kidcShots 1004 +#define kidcShotImpacts 1005 +#define kidcSelectionBrackets 1006 +#define kidcSmoke 1007 +#define kidcEnemyDamageIndicator 1008 +#define kidcScorchMarks 1009 +#define kidcSymbolFlashing 1010 + +// kidfChooseServer +#define kidcServerList 1300 +#define kidcServerName 1301 +#define kidcServerLocation 1302 +#define kidcServerStatus 1303 +#define kidcRefresh 1304 + +// DRM Code + +#define kidcCode 1001 +#define kidcEnterKey 1002 +//#define kidcPlayDemo 1003 + +// DRM Key + +#define kidc0 1010 +#define kidc1 1011 +#define kidc2 1012 +#define kidc3 1013 +#define kidc4 1014 +#define kidc5 1015 +#define kidc6 1016 +#define kidc7 1017 +#define kidc8 1018 +#define kidc9 1019 +#define kidcA 1020 +#define kidcB 1021 +#define kidcC 1022 +#define kidcD 1023 +#define kidcE 1024 +#define kidcF 1025 +#define kidcBackspace 1026 +#define kidcKey 1027 + +// Input Panel + +#define kidcInputLabel 1010 +#define kidcInputEdit 1011 + +// Colors + +#define kiclrBlack 0 +#define kiclrWhite 1 +#define kiclrRed 2 +#define kiclrGreen 3 +#define kiclrYellow 4 +#define kiclrSide1 5 +#define kiclrSide2 6 +#define kiclrSide3 7 +#define kiclrSide4 8 +#define kiclrButtonFill 9 +#define kiclrButtonBorder 10 +#define kiclrMenuBack 11 +#define kiclrFormBackground 12 +#define kiclrMiniMapBorder 13 +#define kiclrGalaxite 14 +#define kiclrButtonFillHighlight 15 +#define kiclrMediumGray 16 +#define kiclrBlueSideFirst 17 +#define kiclr0BlueSide 17 +#define kiclr1BlueSide 18 +#define kiclr2BlueSide 19 +#define kiclr3BlueSide 20 +#define kiclr4BlueSide 21 +#define kiclrBlueSideLast 21 +#define kiclrRedSideFirst 22 +#define kiclr0RedSide 22 +#define kiclr1RedSide 23 +#define kiclr2RedSide 24 +#define kiclr3RedSide 25 +#define kiclr4RedSide 26 +#define kiclrRedSideLast 26 +#define kiclrYellowSideFirst 27 +#define kiclr0YellowSide 27 +#define kiclr1YellowSide 28 +#define kiclr2YellowSide 29 +#define kiclr3YellowSide 30 +#define kiclr4YellowSide 31 +#define kiclrYellowSideLast 31 +#define kiclrCyanSideFirst 32 +#define kiclr0CyanSide 32 +#define kiclr1CyanSide 33 +#define kiclr2CyanSide 34 +#define kiclr3CyanSide 35 +#define kiclr4CyanSide 36 +#define kiclrCyanSideLast 36 +#define kiclrListBackground 37 +#define kiclrListBorder 38 +#define kiclrJana 39 +#define kiclrAndy 40 +#define kiclrOlstrom 41 +#define kiclrFox 42 +#define kiclrSideNeutral 43 +#define kiclrNeutralSideFirst 43 +#define kiclr0NeutralSide 43 +#define kiclr1NeutralSide 44 +#define kiclr2NeutralSide 45 +#define kiclr3NeutralSide 46 +#define kiclr4NeutralSide 47 +#define kiclrNeutralSideLast 47 +#define kiclrFullnessIndicator 48 + +// ARM code resource ids + +#define kidrArmCode 1 + +// Datatypes used for conditions & actions + +#define knCaTypeNumber 0 +#define knCaTypeQualifiedNumber 1 +#define knCaTypeSide 2 +#define knCaTypeCounter 3 +#define knCaTypeUnit 4 +#define knCaTypeModifier 5 +#define knCaTypeResource 6 +#define knCaTypeWinLose 7 +#define knCaTypeOnOff 8 +#define knCaTypeCharacter 9 +#define knCaTypeText 10 +#define knCaTypeRichText 11 +#define knCaTypeUnitTypes 12 +#define knCaTypeSwitch 13 +#define knCaTypeArea 14 + +// Related enumerations + +#define knQualifierAtLeast 0 +#define knQualifierAtMost 1 +#define knQualifierExactly 2 + +#define knCaSideNeutral 0 +#define knCaSideSide1 1 +#define knCaSideSide2 2 +#define knCaSideSide3 3 +#define knCaSideSide4 4 +#define knCaSideEnemies 5 +#define knCaSideAllies 6 +#define knCaSideAllSides 7 +#define knCaSideCurrentSide 8 + +#define knCounterTypeNone 0 +#define knCounterTypeTotal 1 +#define knCounterTypeUnits 2 +#define knCounterTypeBuildings 3 +#define knCounterTypeUnitsAndBuildings 4 +#define knCounterTypeKills 5 +#define knCounterTypeCustom 6 + +#define knModifierTypeSet 0 +#define knModifierTypeAdd 1 +#define knModifierTypeSubtract 2 + +#define knWinLoseTypeNone 0 +#define knWinLoseTypeWin 1 +#define knWinLoseTypeLose 2 + +#define knOnOffTypeNone 0 +#define knOnOffTypeOn 1 +#define knOnOffTypeOff 2 + +#define knSmallLargeTypeNone 0 +#define knSmallLargeTypeSmallBottom 1 +#define knSmallLargeTypeLarge 2 +#define knSmallLargeTypeSmallTop 3 + +#define knMoreCloseTypeNone 0 +#define knMoreCloseTypeMore 1 +#define knMoreCloseTypeClose 2 + +#define knModifyCountdownTypeNone 0 +#define knModifyCountdownTypeStop 1 +#define knModifyCountdownTypeResume 2 +#define knModifyCountdownTypeHide 3 +#define knModifyCountdownTypeShow 4 + +#define knCharacterTypeNone 0 +#define knCharacterTypeAndy 1 +#define knCharacterTypeJana 2 +#define knCharacterTypeOlstrom 3 +#define knCharacterTypeFox 4 +#define knCharacterTypeACME_Security 5 +#define knCharacterTypeOMNI_Security 6 +#define knCharacterTypeAnonymous 7 +#define knCharacterTypeBlank 8 + +#define knAggressivenessCoward 0 +#define knAggressivenessPacifist 1 +#define knAggressivenessSelfDefense 2 +#define knAggressivenessDefender 3 +#define knAggressivenessPitbull 4 + +#define knModifyNumberTypeNone 0 +#define knModifyNumberTypeSet 1 +#define knModifyNumberTypeAdd 2 +#define knModifyNumberTypeSubtract 3 + +// Conditions + +#define knMissionLoadedCondition 0 +#define knCreditsCondition 1 +#define knAreaContainsUnitsCondition 2 +#define knGalaxiteCapacityReachedCondition 3 +#define knMobileHQDeployedCondition 4 +#define knPlaceStructureModeCondition 5 +#define knElapsedTimeCondition 6 +#define knOwnsUnitsCondition 7 +#define knMinerCantFindGalaxiteCondition 8 +#define knUnitSeesUnitCondition 9 +#define knUnitDestroyedCondition 10 +#define knSwitchCondition 11 +#define knPeriodicTimerCondition 12 +#define knDiscoversSideCondition 13 +#define knCountdownTimerCondition 14 +#define knCounterCondition 15 +#define knTestPvarCondition 16 +#define knHasUpgradesCondition 17 +#define knConditionMax 18 + +// TriggerActions + +#define knSetResourcesTriggerAction 0 +#define knSetAllowedUnitsTriggerAction 1 +#define knEcomTriggerAction 2 +#define knSetObjectiveTriggerAction 3 +#define knSetNextMissionTriggerAction 4 +#define knEndMissionTriggerAction 5 +#define knWaitTriggerAction 6 +#define knSetSwitchTriggerAction 7 +#define knSetPlayerControlsTriggerAction 8 +#define knPreserveTriggerTriggerAction 9 +#define knCenterViewTriggerAction 10 +#define knPanViewTriggerAction 11 +#define knDefogAreaTriggerAction 12 +#define knMoveUnitTriggerAction 13 +#define knTargetUnitTriggerAction 14 +#define knCreateUnitGroupTriggerAction 15 +// THis is deliberately the same value as knCreateUnitGroupTriggerAction +#define knCreateUnitAtAreaTriggerAction 15 +#define knHuntTriggerAction 16 +#define knCreateRandomUnitGroupTriggerAction 17 +#define knAlliesTriggerAction 18 +#define knStartCountdownTimerTriggerAction 19 +#define knModifyCountdownTimerTriggerAction 20 +#define knRepairTriggerAction 21 +#define knEnableReplicatorTriggerAction 22 +#define knModifyCreditsTriggerAction 23 +#define knModifyCounterTriggerAction 24 +#define knMoveUnitsInAreaTriggerAction 25 +#define knSetFormalObjectiveTextTriggerAction 26 +#define knSetFormalObjectiveStatusTriggerAction 27 +#define knShowObjectivesTriggerAction 28 +#define knSetFormalObjectiveInfoTriggerAction 29 +#define knCutSceneTriggerAction 30 +#define knJumpToMissionTriggerAction 31 +#define knModifyPvarTriggerAction 32 +#define knSetPvarTextTriggerAction 33 +#define knShowAlertTriggerAction 34 +#define knSetAllowedUpgradesTriggerAction 35 +#define knSetUpgradesTriggerAction 36 + +// UnitGroupActions + +#define knWaitUnitGroupAction 0 +#define knSetSwitchUnitGroupAction 1 +#define knMoveUnitGroupAction 2 +#define knAttackUnitGroupAction 3 +#define knGuardUnitGroupAction 4 +#define knMineUnitGroupAction 5 +#define knGuardVicinityUnitGroupAction 6 + +// UnitActions + +#define knGuardUnitAction 0 +#define knGuardVicinityUnitAction 1 +#define knGuardAreaUnitAction 2 +#define knMoveUnitAction 3 +#define knHuntEnemiesUnitAction 4 +#define knMineUnitAction 5 + +// sound effects for triggers + +#define knsfxHappyEnding 0 +#define knsfxExplosion 1 diff --git a/game/roomform.cpp b/game/roomform.cpp new file mode 100644 index 0000000..8967d29 --- /dev/null +++ b/game/roomform.cpp @@ -0,0 +1,671 @@ +#include "game/ht.h" +#include "game/gameform.h" +#include "game/creategameform.h" +#include "mpshared/messages.h" + +namespace wi { + +RoomForm::RoomForm(LoginHandler& handler, const RoomInfo& roominfo, + Chatter& chatter) : handler_(handler), roominfo_(roominfo), + chatter_(chatter), refresh_(true), creator_(false), status_(true) { + chatter_.SetChatTitle("Room Chat"); + chatter_.SignalOnBlink.connect(this, &RoomForm::OnChatBlink); + chatter_.SignalOnPlayers.connect(this, &RoomForm::OnPlayers); +} + +RoomForm::~RoomForm() { + chatter_.SignalOnBlink.disconnect(this); + chatter_.SignalOnPlayers.disconnect(this); + chatter_.HideChat(); +} + +void RoomForm::ShowJoinMessage(dword result) { + char *message = NULL; + switch (result) { + case knRoomJoinResultFail: + default: + message = "Could not join room."; + break; + + case knRoomJoinResultFull: + message = "The room is full. Try again later."; + break; + + case knRoomJoinResultNotFound: + message = "Can't find this room. Try another room."; + break; + + case knRoomJoinResultWrongPassword: + message = "Incorrect password. Try again."; + break; + + case knRoomJoinResultSuccess: + break; + } + if (message != NULL) { + HtMessageBox(kfMbWhiteBorder, "Join Room", message); + } +} + +dword RoomForm::DoForm(LoginHandler& handler, const RoomInfo& roominfo, + Chatter& chatter, GameInfo *joininfo) { + + if (gptra == NULL) { + return knRoomResultFail; + } + RoomForm *pfrm = (RoomForm *)gpmfrmm->LoadForm(gpiniForms, kidfRoom, + new RoomForm(handler, roominfo, chatter)); + if (pfrm == NULL) { + return knRoomResultFail; + } + + int result = 0; + pfrm->DoModal(&result); + *joininfo = pfrm->joininfo(); + bool creator = pfrm->creator(); + delete pfrm; + + // Map result codes + if (result == kidcCancel) { + return knRoomResultDone; + } + if (result == kidcJoinGame) { + if (creator) { + return knRoomResultCreated; + } else { + return knRoomResultJoin; + } + } + return knRoomResultFail; +} + +bool RoomForm::DoModal(int *presult) { + // Initialize controls + LabelControl *plbl = (LabelControl *)GetControlPtr(kidcRoomName); + plbl->SetText(roominfo_.name.c_str()); + + // Turn on ellipsis mode in list control + ListControl *plstc = (ListControl *)GetControlPtr(kidcGameList); + plstc->SetTabFlags(kfLstTabEllipsis); + + // Hide Join Game until a game shows up + GetControlPtr(kidcJoinGame)->Show(false); + + // Hide the list, and show the message, until a game shows up + plstc->Show(false); + GetControlPtr(kidcNoGames)->Show(true); + + // Add this player to the list + char name[kcbPlayerName]; + handler_.GetPlayerName(name, sizeof(name)); + OnAddPlayer(name); + + Show(true); + gptra->SetCallback(this); + dword result = gptra->JoinRoom(roominfo_.roomid, + roominfo_.password.c_str(), this); + if (result != knRoomJoinResultSuccess) { + gptra->SetCallback(NULL); + ShowJoinMessage(result); + *presult = 0; + return false; + } + *presult = 0; + bool success = ShellForm::DoModal(presult); + if (*presult == kidcJoinGame) { + gptra->LeaveRoom(knLeaveRoomHintJoinGame); + } else { + gptra->LeaveRoom(knLeaveRoomHintNone); + } + gptra->SetCallback(NULL); + + return success; +} + +void RoomForm::OnChatBlink(bool on) { + GetControlPtr(kidcChat)->Show(on); +} + +void RoomForm::OnPlayers() { + std::string s = GetPlayersString(); + chatter_.AddChat("", s.c_str(), true); +} + +void RoomForm::OnControlSelected(word idc) { + switch (idc) { + case kidcNewGame: + OnCreateGame(); + break; + + case kidcJoinGame: + OnJoinGame(); + break; + + case kidcCancel: + EndForm(idc); + break; + + case kidcChat: + chatter_.ShowChat(); + break; + } +} + +void RoomForm::OnControlNotify(word idc, int nNotify) { + if (idc == kidcGameList && nNotify == knNotifySelectionChange) { + HideShowJoinGame(); + } + ShellForm::OnControlNotify(idc, nNotify); +} + +void RoomForm::OnJoinGame() { + ListControl *plstc = (ListControl *)GetControlPtr(kidcGameList); + dword gameid = (dword)plstc->GetSelectedItemData(); + GameMap::iterator it = map_.find(gameid); + if (it == map_.end()) { + return; + } + GameInfo& info = it->second; + if (!AskInstallMissionPack(&info.params.packid, "Join Game", info.title)) { + return; + } + InitiateJoinGame(info); +} + +void RoomForm::OnCreateGame() { + GameParams params; + if (!CreateGameForm::DoForm(handler_, NULL, chatter_, ¶ms)) { + return; + } + + while (true) { + dword gameid; + PackId packidUpgrade; + dword result = gptra->CreateGame(¶ms, &packidUpgrade, &gameid); + switch (result) { + case knRoomCreateGameResultSuccess: + { + // Update teh list and select the game, in case the join + // fails + creator_ = true; + ListControl *plstc = (ListControl *)GetControlPtr(kidcGameList); + refresh_ = false; + plstc->Select(FindIndex(gameid), true, true); + refresh_ = true; + + // OnAddGame has been called already. Join the game + GameMap::iterator it = map_.find(gameid); + if (it != map_.end()) { + InitiateJoinGame(it->second); + } + } + break; + + case knRoomCreateGameResultFail: + HtMessageBox(kfMbWhiteBorder, "Create Game", + "Unable to create the game."); + break; + + case knRoomCreateGameResultUnknownMissionPack: + HtMessageBox(kfMbWhiteBorder, "Create Game", + "Unknown mission pack!"); + break; + + case knRoomCreateGameResultUpgradeMissionPack: + if (AskInstallMissionPack(&packidUpgrade, "Advertise Game", + NULL)) { + params.packid = packidUpgrade; + continue; + } + break; + + case knRoomCreateGameResultRoomFull: + HtMessageBox(kfMbWhiteBorder, "Create Game", + "Room full. Cannot create another game in this room."); + break; + } + break; + } +} + +int RoomForm::FindIndex(dword gameid) { + int index = 0; + GameMap::iterator it = map_.begin(); + for (; it != map_.end(); it++) { + if (it->first == gameid) { + return index; + } + index++; + } + return -1; +} + +void RoomForm::InitiateJoinGame(const GameInfo& info) { + if (gptra == NULL) { + return; + } + + // Before joining, check to see if this game is joinable. + // Joining also checks, but this is a way to provide the user + // with feedback before exiting this form. + + dword result = gptra->CanJoinGame(info.gameid); + GameForm::ShowJoinMessage(result); + if (result == knGameJoinResultSuccess) { + joininfo_ = info; + EndForm(kidcJoinGame); + } +} + +void RoomForm::OnAddGame(const char *player, dword gameid, + const GameParams& params, dword minplayers, dword maxplayers, + const char *title, dword ctotal) { + GameMap::iterator it = map_.find(gameid); + if (it != map_.end()) { + return; + } + GameInfo *info = new GameInfo; + if (info == NULL) { + return; + } + memset(info, 0, sizeof(*info)); + info->roomid = roominfo_.roomid; + info->gameid = gameid; + info->params = params; + info->minplayers = minplayers; + info->maxplayers = maxplayers; + strncpyz(info->title, title, sizeof(info->title)); + strncpyz(info->creator, player, sizeof(info->creator)); + map_.insert(GameMap::value_type(gameid, *info)); + Refresh(); + + if (!status_) { + chatter_.AddChat(player, base::Format::ToString( + "%s created and joined game %s.", player, + info->GetDescription()), true); + } +} + +void RoomForm::OnRemoveGame(dword gameid, dword ctotal) { + GameMap::iterator it = map_.find(gameid); + if (it == map_.end()) { + return; + } + map_.erase(gameid); + Refresh(); +} + +void RoomForm::OnGameInProgress(dword gameid) { + GameMap::iterator it = map_.find(gameid); + if (it == map_.end()) { + return; + } + it->second.playing = true; + Refresh(); + + if (!status_) { + chatter_.AddChat("", base::Format::ToString( + "%s's game %s has started.", + it->second.creator, it->second.GetDescription()), true); + } +} + +void RoomForm::OnGamePlayerNames(dword gameid, dword cnames, + const PlayerName *anames) { + GameMap::iterator it = map_.find(gameid); + if (it == map_.end()) { + return; + } + if (cnames > ARRAYSIZE(it->second.anames)) { + return; + } + GameInfo& info = it->second; + + // Find the players being added to the game + int cadded = 0; + PlayerName added[kcPlayersMax]; + for (int i = 0; i < cnames; i++) { + bool found = false; + for (int j = 0; j < info.cnames; j++) { + if (strcmp(anames[i].szPlayerName, + info.anames[j].szPlayerName) == 0) { + found = true; + break; + } + } + if (!found) { + strncpyz(added[cadded].szPlayerName, anames[i].szPlayerName, + sizeof(added[cadded].szPlayerName)); + cadded++; + } + } + + // Find the players being removed from the game + int cremoved = 0; + PlayerName removed[kcPlayersMax]; + for (int j = 0; j < info.cnames; j++) { + bool found = false; + for (int i = 0; i < cnames; i++) { + if (strcmp(anames[i].szPlayerName, + info.anames[j].szPlayerName) == 0) { + found = true; + break; + } + } + if (!found) { + strncpyz(removed[cremoved].szPlayerName, + info.anames[j].szPlayerName, + sizeof(removed[cremoved].szPlayerName)); + cremoved++; + } + } + + // Update the players in the game + memcpy(info.anames, anames, cnames * sizeof(PlayerName)); + info.cnames = cnames; + Refresh(); + + // Callbacks before Join has returned are current state, not changes + // in state. + if (status_) { + return; + } + + // Add to the chat window + for (int i = 0; i < cadded; i++) { + // Don't post if this player created the game + if (strcmp(added[i].szPlayerName, info.creator) == 0) { + continue; + } + + // Format: X joined Y's game Desert River Riches + chatter_.AddChat(added[i].szPlayerName, + base::Format::ToString("%s joined %s's game %s", + added[i].szPlayerName, info.creator, info.title), true); + } + + for (int i = 0; i < cremoved; i++) { + if (strcmp(removed[i].szPlayerName, info.creator) == 0) { + if (!info.playing) { + // Format: X cancelled Desert River Riches. + chatter_.AddChat(removed[i].szPlayerName, + base::Format::ToString("%s cancelled %s.", + removed[i].szPlayerName, info.title), true); + } else { + // Format: X left Desert River Riches. + chatter_.AddChat(removed[i].szPlayerName, + base::Format::ToString("%s left %s.", + removed[i].szPlayerName, info.title), true); + } + } else { + if (!info.playing) { + // Format: X left Y's game Desert River Riches before it + // started. + chatter_.AddChat(removed[i].szPlayerName, + base::Format::ToString( + "%s left %s's game %s before it started.", + removed[i].szPlayerName, info.creator, info.title), + true); + } else { + // Format: X left Y's game Desert River Riches. + chatter_.AddChat(removed[i].szPlayerName, + base::Format::ToString("%s left %s's game %s.", + removed[i].szPlayerName, info.creator, info.title), + true); + } + } + } +} + +void RoomForm::Refresh(int ct) { + if (!refresh_) { + return; + } + if (ct == -1) { + timer_.Stop(); + OnTimeout(0); + return; + } + if (ct == 0) { + timer_.Stop(); + } + if (timer_.IsStarted()) { + return; + } + timer_.Start(this, ct * 10); +} + +void RoomForm::OnTimeout(int id) { + refresh_ = false; + ListControl *plstc = (ListControl *)GetControlPtr(kidcGameList); + dword gameid = (dword)plstc->GetSelectedItemData(); + plstc->Clear(); + GameMap::iterator it = map_.begin(); + for (; it != map_.end(); it++) { + GameInfo& info = it->second; + char s[80]; + GetString(info, s, sizeof(s)); + plstc->Add(s, (void *)info.gameid); + } + int selected = FindIndex(gameid); + if (selected < 0 && map_.size() != 0) { + plstc->Select(0, true, true); + } else { + plstc->Select(selected, true); + } + refresh_ = true; + HideShowJoinGame(); + + // Update room label with player count. + int count = players_.size(); + for (GameMap::iterator it = map_.begin(); it != map_.end(); it++) { + count += it->second.cnames; + } + LabelControl *plbl = (LabelControl *)GetControlPtr(kidcRoomName); + plbl->SetText(base::Format::ToString("%s (%d %s, %d %s)", + roominfo_.name.c_str(), count, + count == 1 ? "player" : "players", map_.size(), + map_.size() == 1 ? "game" : "games")); +} + +void RoomForm::HideShowJoinGame() { + ListControl *plstc = (ListControl *)GetControlPtr(kidcGameList); + dword gameid = (dword)plstc->GetSelectedItemData(); + GameMap::iterator it = map_.find(gameid); + bool show = true; + if (it == map_.end()) { + show = false; + } else { + GameInfo& info = it->second; + if (info.playing) { + show = false; + } + if (info.cnames == info.maxplayers) { + show = false; + } + } + Control *pctl = GetControlPtr(kidcJoinGame); + pctl->Show(show); + + // Show the "no games" message if there are no games + if (map_.size() == 0) { + GetControlPtr(kidcGameList)->Show(false); + GetControlPtr(kidcNoGames)->Show(true); + } else { + GetControlPtr(kidcGameList)->Show(true); + GetControlPtr(kidcNoGames)->Show(false); + } +} + +void RoomForm::OnAddPlayer(const char *player) { + players_.push_back(player); + Refresh(); + + if (!status_) { + chatter_.AddChat(player, + base::Format::ToString("%s entered the room.", player), true); + } +} + +void RoomForm::OnRemovePlayer(dword hint, const char *player) { + // Remove the player from the list + { + std::vector::iterator it; + it = std::find(players_.begin(), players_.end(), player); + if (it != players_.end()) { + players_.erase(it); + } + Refresh(); + } + + // If the player is creating or joining a game, don't add a system + // message. Otherwise, do add one. + bool add = true; + GameMap::iterator it = map_.begin(); + for (; it != map_.end(); it++) { + if (strcmp(player, it->second.creator) == 0) { + add = false; + break; + } + } + + // If joining a game, a system message will be added later with that + // notification. + if (hint == knLeaveRoomHintJoinGame) { + add = false; + } + + if (add && !status_) { + chatter_.AddChat(player, base::Format::ToString("%s left the room.", + player), true); + } +} + +void RoomForm::OnReceiveChat(const char *player, const char *chat) { + chatter_.AddChat(player, chat, false); +} + +void RoomForm::OnStatusComplete() { + // Callbacks after this mean change in status, not current status. + status_ = false; + + // Add a system message to chat. The current player is in players_[0] + std::string s = base::Format::ToString("%s has entered the %s game room. ", + players_[0].c_str(), roominfo_.name.c_str()); + + s += GetPlayersString(); + chatter_.AddChat("", s.c_str(), true); +} + +std::string RoomForm::GetPlayersString() { + std::string s; + if (players_.size() == 1) { + s += "No other players are in the room."; + return s; + } + + if (players_.size() == 2) { + s += base::Format::ToString("%s is also in the room.", + players_[1].c_str()); + return s; + } + + for (int i = 1; i < players_.size(); i++) { + if (i != players_.size() - 1) { + s += base::Format::ToString("%s, ", players_[i].c_str()); + } else { + s += base::Format::ToString("and %s ", players_[i].c_str()); + } + } + s += "are also in the room."; + return s; +} + +void RoomForm::GetString(GameInfo& info, char *s, int cb) { + // Format: + // Desert River Riches, 1.0x, 2-4 players: jim, bob, sam + // Desert River Riches, 1.0x, 2 players: jim, bob + // (play) Desert River Riches, 1.5x, 2-4 players: jim, bob... + + const char *d = info.GetDescription(); + if (info.playing) { + snprintf(s, cb, "(play) %s: ", d); + } else { + snprintf(s, cb, "%s: ", d); + } + + // Add the players one by one. + for (int i = 0; i < info.cnames; i++) { + int cch = strlen(s); + if (i != 0) { + if (cb - cch < 3) { + break; + } + strcat(s, ", "); + cch += 2; + } + strncpyz(&s[cch], info.anames[i].szPlayerName, cb - cch); + } +} + +void RoomForm::OnStatusUpdate(char *pszStatus) { +} + +void RoomForm::OnConnectionClose() { + Event evt; + memset(&evt, 0, sizeof(evt)); + evt.idf = m_idf; + evt.eType = connectionCloseEvent; + gevm.PostEvent(&evt); +} + +void RoomForm::OnShowMessage(const char *message) { + message_ = message; + Event evt; + memset(&evt, 0, sizeof(evt)); + evt.idf = m_idf; + evt.eType = showMessageEvent; + gevm.PostEvent(&evt); +} + +bool RoomForm::OnFilterEvent(Event *pevt) { + if (pevt->eType == connectionCloseEvent) { + chatter_.HideChat(); + HtMessageBox(kfMbWhiteBorder, "Comm Problem", "The server has closed your connection."); + EndForm(kidcCancel); + return true; + } + + if (pevt->eType == showMessageEvent) { + chatter_.HideChat(); + HtMessageBox(kfMbWhiteBorder, "Server Message", message_.c_str()); + message_ = ""; + return true; + } + return false; +} + + +const char *GameInfo::GetDescription() const { + // Format if not playing: + // Desert River Riches, 1.0x, 2-4 players: + // Format if playing: + // Desert River Riches + + if (playing) { + return title; + } + + char szSpeed[32]; + GetSpeedMultiplierString(szSpeed, params.tGameSpeed); + + if (minplayers == maxplayers) { + return base::Format::ToString("%s, %s, %d players", title, + szSpeed, minplayers); + } else { + return base::Format::ToString("%s, %s, %d-%d players", + title, szSpeed, minplayers, maxplayers); + } +} + +} // namespace wi diff --git a/game/roomform.h b/game/roomform.h new file mode 100644 index 0000000..b2140f6 --- /dev/null +++ b/game/roomform.h @@ -0,0 +1,124 @@ +#ifndef __ROOMFORM_H__ +#define __ROOMFORM_H__ + +#include "game/ht.h" +#include "game/loginhandler.h" +#include "game/lobbyform.h" +#include "game/chatter.h" +#include "base/sigslot.h" +#include +#include +#include + +namespace wi { + +const dword knLeaveRoomHintNone = 0; +const dword knLeaveRoomHintJoinGame = 1; + +STARTLABEL(LeaveRoomHints) + LABEL(knLeaveRoomHintNone) + LABEL(knLeaveRoomHintJoinGame) +ENDLABEL(LeaveRoomHints) + +const dword knRoomResultDone = 0; +const dword knRoomResultFail = 1; +const dword knRoomResultJoin = 2; +const dword knRoomResultCreated = 3; + +STARTLABEL(RoomResults) + LABEL(knRoomResultDone) + LABEL(knRoomResultFail) + LABEL(knRoomResultJoin) + LABEL(knRoomResultCreated) +ENDLABEL(RoomResults) + +struct GameSorter { + bool operator()(dword id_a, dword id_b) const { + return id_a > id_b; + } +}; + +struct GameInfo { + dword roomid; + dword gameid; + GameParams params; + int minplayers; + int maxplayers; + char title[kcbLevelTitle]; + char creator[kcbPlayerName]; + bool playing; + int cnames; + PlayerName anames[kcPlayersMax]; + + const char *GetDescription() const; +}; +typedef std::map GameMap; + +class RoomForm : public ShellForm, ITransportCallback, IRoomCallback, + ITimeout, public base::has_slots<> { +public: + RoomForm(LoginHandler& handler, const RoomInfo& roominfo, + Chatter& chatter); + virtual ~RoomForm() secMultiplayer; + + static dword DoForm(LoginHandler& handler, const RoomInfo& roominfo, + Chatter& chatter, GameInfo *gameinfo); + static void ShowJoinMessage(dword result); + const GameInfo& joininfo() { return joininfo_; } + bool creator() { return creator_; } + + // Form methods + bool DoModal(int *pnResult = NULL); + void OnControlSelected(word idc); + void OnControlNotify(word idc, int nNotify); + bool OnFilterEvent(Event *pevt); + + // IRoomCallback methods + void OnAddGame(const char *player, dword gameid, const GameParams& params, + dword minplayers, dword maxplayers, const char *title, + dword ctotal); + void OnRemoveGame(dword gameid, dword ctotal); + void OnGameInProgress(dword gameid); + void OnGamePlayerNames(dword gameid, dword cnames, + const PlayerName *anames); + void OnAddPlayer(const char *player); + void OnRemovePlayer(dword hint, const char *player); + void OnReceiveChat(const char *player, const char *chat); + void OnStatusComplete(); + + // ITransportCallback methods + void OnStatusUpdate(char *pszStatus); + void OnShowMessage(const char *message); + void OnConnectionClose(); + + // ITimeout + void OnTimeout(int id); + +private: + std::string GetPlayersString(); + void OnChatBlink(bool on); + void OnPlayers(); + void HideShowJoinGame(); + void OnJoinGame(); + void OnCreateGame(); + void InitiateJoinGame(const GameInfo& info); + void Refresh(int ct = 20); + void GetString(GameInfo& info, char *s, int cb); + int FindIndex(dword gameid); + + Chatter& chatter_; + LoginHandler& handler_; + TimeoutTimer timer_; + GameMap map_; + RoomInfo roominfo_; + GameInfo joininfo_; + bool refresh_; + bool creator_; + bool status_; + std::vector players_; + std::string message_; +}; + +} // namespace wi + +#endif // __ROOMFORM_H__ diff --git a/game/sdl/display.cpp b/game/sdl/display.cpp new file mode 100644 index 0000000..76fb3eb --- /dev/null +++ b/game/sdl/display.cpp @@ -0,0 +1,274 @@ +#include "../ht.h" +#include "game/sdl/sdlspritemgr.h" +#include "hosthelpers.h" + +namespace wi { + +Display *HostCreateDisplay() { + // Create a display + + Display *pdisp = new Display(); + if (pdisp == NULL) + return NULL; + if (!pdisp->Init()) { + delete pdisp; + return NULL; + } + return pdisp; +} + +Display::Display() +{ + m_cx = 0; + m_cy = 0; + memset(m_amodeInfo, 0, sizeof(m_amodeInfo)); + m_imode = -1; + m_cmodes = 0; + m_pbmBack = NULL; + m_pbmFront = NULL; + m_pbmClip = NULL; +} + +Display::~Display() +{ + delete m_pbmBack; + m_pbmBack = NULL; + delete m_pbmFront; + m_pbmFront = NULL; + delete m_pbmClip; + m_pbmClip = NULL; +} + +// TODO(darrinm): horrible hack to see if it works (screen->map->dst = NULL) +// Alternatively: +// { SDL_SetSurfaceRLE(surface, 1); SDL_SetSurfaceRLE(surface, 0); } +#define SDL_InvalidateMap(surface) *(void **)surface->map = NULL; + + +bool Display::Init() +{ + Uint32 videoflags; + int cxScreen, cyScreen; + + /* Initialize SDL */ + if (SDL_Init(SDL_INIT_VIDEO) < 0) { + LOG() << "Couldn't initialize SDL: " << SDL_GetError(); + return false; + } + + // Absolutely do not mess with SDL_FULLSCREEN if there is any chance the app + // will crash or stop at a breakpoint. If it does you will be lost in full + // screen mode! (ssh from another machine and kill the Xcode process) + videoflags = SDL_SWSURFACE; + +#ifdef __IPHONEOS__ + cxScreen = 320; + cyScreen = 480; +#else + cxScreen = 800; + cyScreen = 600; +#endif + + /* Set 640x480 video mode */ + if ((screen = + SDL_SetVideoMode(cxScreen, cyScreen, 8, videoflags)) == NULL) { + LOG() << "Couldn't set " << cxScreen << "x" << cyScreen << " 8 video mode: " << SDL_GetError(); + return false; + } + + SurfaceProperties props; + props.cxWidth = cxScreen; + props.cyHeight = cyScreen; + props.cbxPitch = 1; + props.cbyPitch = props.cxWidth; + props.ffFormat = wi::kfDirect8; + + ModeInfo *pmode = m_amodeInfo; + +#if 1 + pmode->nDepth = 8; + pmode->cx = props.cxWidth; + pmode->cy = props.cyHeight; + pmode->cyGraffiti = 0; + pmode->fNative = true; + pmode->nDegreeOrientation = 0; + pmode++; +#endif + +#if 0 + pmode->nDepth = 8; + pmode->cx = props.cyHeight; + pmode->cy = props.cxWidth; + pmode->cyGraffiti = 0; + pmode->fNative = true; + pmode->nDegreeOrientation = 90; // leftie controls + pmode++; + + pmode->nDepth = 8; + pmode->cx = props.cxWidth; + pmode->cy = props.cyHeight; + pmode->cyGraffiti = 0; + pmode->fNative = true; + pmode->nDegreeOrientation = 180; // bizzaro controls + pmode++; +#endif + +#if 0 + pmode->nDepth = 8; + pmode->cx = props.cyHeight; + pmode->cy = props.cxWidth; + pmode->cyGraffiti = 0; + pmode->fNative = true; + pmode->nDegreeOrientation = 270; // rightie controls + pmode++; +#endif + + m_cmodes = pmode - m_amodeInfo; + + HostOutputDebugString("Display::Init %d", pmode - m_amodeInfo); + HostOutputDebugString("Display::Init %d modes", m_cmodes); + return true; +} + +void Display::SetPalette(Palette *ppal) +{ + SDL_Color aclr[256]; + int cEntries = BigWord(ppal->cEntries); + byte *pb = (byte *)ppal->argb; + SDL_Color *pclr = aclr; + + for (int i = 0; i < cEntries; i++) { + pclr->r = *pb++; + pclr->g = *pb++; + pclr->b = *pb++; + pclr++; + } + + SDL_SetColors(screen, aclr, 0, cEntries); + SDL_InvalidateMap(screen); +// SDL_UpdateRect(screen, 0, 0, 0, 0); +} + +int Display::GetModeCount() +{ + return m_cmodes; +} + +void Display::GetModeInfo(int imode, ModeInfo *pmode) +{ + memset(pmode, 0, sizeof(*pmode)); + if (imode >= 0 && imode < m_cmodes) + *pmode = m_amodeInfo[imode]; +} + +int Display::GetMode(ModeInfo *pmode) +{ + if (pmode != NULL) { + if (m_imode == -1) { + memset(pmode, 0, sizeof(*pmode)); + } else { + *pmode = m_amodeInfo[m_imode]; + } + } + return m_imode; +} + +bool Display::SetMode(int imode) +{ + // Allocate dib + + ModeInfo *pmode = &m_amodeInfo[imode]; + + DibBitmap *pbmBack = CreateDibBitmap(NULL, pmode->cx, pmode->cy); + if (pbmBack == NULL) + return false; + + // TODO(darrinm): uh, locking, unlocking pixels? + DibBitmap *pbmFront = CreateDibBitmap((byte *)screen->pixels, pmode->cx, pmode->cy); + if (pbmFront == NULL) { + delete pbmBack; + return NULL; + } + delete m_pbmBack; + delete m_pbmFront; + m_pbmBack = pbmBack; + m_pbmFront = pbmFront; + m_imode = imode; + + return true; +} + +void Display::DrawText(const char *psz, int x, int y, word wf) +{ +} + +void Display::DrawFrameInclusive(Rect *prc) +{ +} + +DibBitmap *Display::GetBackDib() +{ + return m_pbmBack; +} + +DibBitmap *Display::GetFrontDib() +{ + return m_pbmFront; +} + +DibBitmap *Display::GetClippingDib() +{ + DibBitmap *pbm = CreateDibBitmap(NULL, kcCopyBy4Procs * 4, kcCopyBy4Procs * 4); + if (pbm == NULL) + return NULL; + m_pbmClip = pbm; + return pbm; +} + +void Display::GetHslAdjustments(short *pnHueOffset, short *pnSatMultiplier, short *pnLumOffset) +{ + *pnHueOffset = 0; + *pnSatMultiplier = 0; + *pnLumOffset = 0; +} + +void Display::FrameStart() +{ + SDL_LockSurface(screen); + + // screen->pixels can change every time the surface is locked. + // TODO(darrinm): problem for, e.g. scrolling optimizations? + m_pbmFront->Init((byte *)screen->pixels, screen->w, screen->h); +} + +void Display::FrameComplete(int cfrmm, UpdateMap **apupd, Rect *arc, + bool fScrolled) +{ + SDL_UnlockSurface(screen); + SDL_UpdateRect(screen, 0, 0, 0, 0); +} + +void Display::ResetScrollOffset() +{ +#if 0 // TODO(darrinm): SDL-ify + IPhone::ResetScrollOffset(); +#endif +} + +static SdlSpriteManager *s_psprm; + +SpriteManager *Display::GetSpriteManager() +{ + if (s_psprm == NULL) + s_psprm = new SdlSpriteManager(); + return s_psprm; +} + +void Display::SetFormMgrs(FormMgr *pfrmmSimUI, FormMgr *pfrmmInput) +{ +#if 0 // TODO(darrinm): SDL-ify + IPhone::SetFormMgrs(pfrmmSimUI, pfrmmInput); +#endif +} + +} // namespace wi diff --git a/game/sdl/host.cpp b/game/sdl/host.cpp new file mode 100644 index 0000000..4acda50 --- /dev/null +++ b/game/sdl/host.cpp @@ -0,0 +1,514 @@ +#include "game/ht.h" +#include "game/sdl/hosthelpers.h" +#include "base/tick.h" +#include "base/log.h" +#include "base/thread.h" +#include "base/md5.h" +#include "game/httppackmanager.h" +#include "game/httppackinfomanager.h" +#include "game/httpservice.h" +#include "game/completemanager.h" +#include +#include +#include + +namespace wi { + +SdlPackFileReader gpakr; +HttpPackManager *gppackm; +HttpPackInfoManager *gppim; +CompleteManager *gpcptm; + +char *gpszUdid; + +bool HostInit() +{ + HostHelpers::Init(); + + // + gppackm = new HttpPackManager(gphttp, HostHelpers::GetMissionPacksDir(), HostHelpers::GetTempDir()); + gppackm->InitFromInstalled(); + gppim = new HttpPackInfoManager(gphttp, HostHelpers::GetMissionPackInfosDir(), HostHelpers::GetTempDir()); + gpcptm = new CompleteManager(HostHelpers::GetCompletesDir()); + gpcptm->Init(); + + // TODO(darrinm): get user id (gpszUdid) + + return true; +} + +void HostExit() +{ + delete gppim; + delete gppackm; + + HostHelpers::Cleanup(); +} + +const char *HostGetDeviceId() { + // Hash it so query params aren't obnoxious + MD5_CTX md5; + MD5Init(&md5); + const char *udid = HostHelpers::GetUdid(); + MD5Update(&md5, (const byte *)udid, strlen(udid)); + byte hash[16]; + MD5Final(hash, &md5); + return base::Format::ToHex(hash, 16); +} + +void HostInitiateWebView(const char *title, const char *url) { + return HostHelpers::InitiateWebView(title, url); +} + +IChatController *HostGetChatController() { + return HostHelpers::GetChatController(); +} + +void HostInitiateAsk(const char *title, int max, const char *def, + int keyboard, bool secure) { + return HostHelpers::InitiateAsk(title, max, def, keyboard, secure); +} + +void HostGetAskString(char *psz, int cb) { + return HostHelpers::GetAskString(psz, cb); +} + +void HostOpenUrl(const char *pszUrl) { + HostHelpers::OpenUrl(pszUrl); +} + +void HostSuspendModalLoop(DibBitmap *pbm) +{ + // Wait for WI to become active again + + LOG() << "Entering SuspendModalLoop"; + + base::Thread& thread = base::Thread::current(); + while (true) { + base::Message msg; + thread.Get(&msg); + if (msg.id == kidmAppSetFocus) { + break; + } + if (msg.id == kidmAppTerminate) { + thread.Post(&msg); + break; + } + thread.Dispatch(&msg); + } + + LOG() << "Leaving SuspendModalLoop"; +} + +bool ProcessSdlEvent(Event *pevt) +{ + memset(pevt, 0, sizeof(*pevt)); + SDL_Event event; + switch (SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_FIRSTEVENT, + SDL_LASTEVENT)) { + case -1: + // App is exiting, or there is an error + pevt->eType = appStopEvent; + break; + + case 0: + return false; + } + + switch (event.type) { + case SDL_MOUSEBUTTONDOWN: + pevt->eType = penDownEvent; + pevt->x = event.button.x; + pevt->y = event.button.y; + break; + + case SDL_MOUSEBUTTONUP: + pevt->eType = penUpEvent; + pevt->x = event.button.x; + pevt->y = event.button.y; + break; + + case SDL_MOUSEMOTION: + pevt->eType = penMoveEvent; + pevt->x = event.motion.x; + pevt->y = event.motion.y; + break; + + case SDL_KEYDOWN: + pevt->eType = keyDownEvent; + switch (event.key.keysym.sym) { + case SDLK_UP: + pevt->chr = chrPageUp; + break; + + case SDLK_DOWN: + pevt->chr = chrPageDown; + break; + + case SDLK_LEFT: + pevt->chr = chrLeft; + break; + + case SDLK_RIGHT: + pevt->chr = chrRight; + break; + + case SDLK_BACKSPACE: + pevt->chr = chrBackspace; + break; + + case SDLK_DELETE: + pevt->chr = chrDelete; + break; + +#if 0 + case SDLK_F7: + if (gpavir == NULL) { + gpavir = new AviRecorder(); + Size siz; + gpdisp->GetFrontDib()->GetSize(&siz); + if (!gpavir->Start(siz.cx, siz.cy)) { + delete gpavir; + gpavir = NULL; + } + } + break; + + case SDLK_F8: + if (gpavir != NULL) { + gpavir->Stop(); + delete gpavir; + gpavir = NULL; + } + break; +#endif + + case SDLK_KP_MINUS: // numpad + { + int i = 0; + for (; i < ARRAYSIZE(gatGameSpeeds); i++) + if (gatGameSpeeds[i] == gtGameSpeed) + break; + i--; + if (i < 0) + i = 0; + ggame.SetGameSpeed(gatGameSpeeds[i]); + } + break; + + case SDLK_KP_PLUS: // numpad + { + int i = 0; + for (; i < ARRAYSIZE(gatGameSpeeds); i++) + if (gatGameSpeeds[i] == gtGameSpeed) + break; + i++; + if (i >= ARRAYSIZE(gatGameSpeeds)) + i = ARRAYSIZE(gatGameSpeeds) - 1; + ggame.SetGameSpeed(gatGameSpeeds[i]); + } + break; + + default: +#ifdef DEBUG_HELPERS + extern void DebugHelperKeyHandler(word vk); + DebugHelperKeyHandler(pmsg->wParam); +#endif + pevt->chr = event.key.keysym.sym; + break; + } + break; + + case SDL_QUIT: + pevt->eType = appStopEvent; + break; + + default: + return false; + } + +#if 0 // TODO(scottlu) + - Add SDL processing for touch events. + - Generate penMoveEvent2, penDownEvent2, penUpEvent2 + - add pevt->ff |= kfEvtFinger + - add SDL support for device turned off, or locked and return + gameSuspendEvent. If no SDL support for this, post kidmAppKillFocus + in native layer and translate into gameSuspendEvent in ProcessEvents. + old code: + + case kidmAppKillFocus: + pevt->eType = gameSuspendEvent; + pevt->ff = 0; + break; +#endif + +#if 0 // TODO(darrinm) + if (gpdisp != NULL) { + int x = pevt->x; + int y = pevt->y; + + SurfaceProperties props; + HostHelpers::GetSurfaceProperties(&props); + int cx = props.cxWidth; + int cy = props.cyHeight; + + ModeInfo mode; + gpdisp->GetMode(&mode); + switch (mode.nDegreeOrientation) { + case 0: + // Screen rotated 90 degrees but coordinates unrotated + pevt->x = y; + pevt->y = (cy - 1) - x; + break; + + case 90: + pevt->x = (cy - 1) - y; + pevt->y = x; + break; + + case 180: + pevt->x = (cx - 1) - x; + pevt->y = (cy - 1) - y; + break; + + case 270: + pevt->x = y; + pevt->y = (cx - 1) - x; + break; + } + } +#endif + + return true; +} + +bool ProcessMessage(base::Message *pmsg, Event *pevt) +{ + memset(pevt, sizeof(*pevt), 0); + switch (pmsg->id) { + case kidmNullEvent: + pevt->eType = nullEvent; + break; + + case kidmTransportEvent: + pevt->eType = transportEvent; + break; + + case kidmAskStringEvent: + pevt->eType = askStringEvent; + break; + + default: + return false; + } + + return true; +} + +bool HostGetEvent(Event *pevt, long ctWait) +{ + base::Thread& thread = base::Thread::current(); + while (true) { + base::Message msg; + if (!thread.Get(&msg, ctWait)) { + return false; + } + + if (msg.handler != NULL) { + thread.Dispatch(&msg); + continue; + } + + if (msg.id == kidmSdlEvent) { + if (ProcessSdlEvent(pevt)) { + return true; + } + continue; + } + + if (ProcessMessage(&msg, pevt)) { + return true; + } + } +} + +void HostOutputDebugString(char *pszFormat, ...) +{ +#ifdef DEBUG + va_list va; + va_start(va, pszFormat); + HostHelpers::Log(pszFormat, va); + va_end(va); +#endif +} + +long HostGetTickCount() +{ + return (long)base::GetTickCount(); +} + +long HostGetMillisecondCount() +{ + return (long)base::GetMillisecondCount(); +} + +long HostRunSpeedTests(DibBitmap *pbmSrc) +{ + return 0; +} + +dword HostGetCurrentKeyState(dword keyBit) +{ + return 0; +} + +bool HostIsPenDown() +{ + return false; +} + +void HostMessageBox(TCHAR *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + HostHelpers::MessageBox(pszFormat, va); + va_end(va); +} + +void HostGetUserName(char *pszBuff, int cb) +{ + strncpyz(pszBuff, "anonymous", cb); +} + +bool HostGetOwnerName(char *pszBuff, int cb, bool fShowError) +{ + strncpyz(pszBuff, "Player", cb); + return true; +} + +// UNDONE: prefix directory? + +FileHandle HostOpenFile(const char *pszFilename, word wf) +{ + const char *pszMode; + if (wf == kfOfRead) + pszMode = "rb"; + else if (wf == kfOfWrite) + pszMode = "wb"; + else if (wf == (kfOfRead | kfOfWrite)) + pszMode = "rb+"; + + return (FileHandle)fopen((char *)pszFilename, pszMode); +} + +void HostCloseFile(FileHandle hf) +{ + fclose((FILE *)hf); +} + +dword HostWriteFile(FileHandle hf, void *pv, dword cb) +{ + return fwrite(pv, 1, cb, (FILE *)hf); +} + +dword HostReadFile(FileHandle hf, void *pv, dword cb) +{ + return fread(pv, 1, cb, (FILE *)hf); +} + +void HostSleep(dword ct) +{ + int cms = ct * 10; + struct timeval tvWait; + tvWait.tv_sec = cms / 1000; + tvWait.tv_usec = (cms % 1000) * 1000; + select(0, NULL, NULL, NULL, &tvWait); +} + +void HostGetSilkRect(int irc, Rect *prc) +{ + return; +} + +// Figure out what kind of sound device exists, and return a SoundDevice for it + +SoundDevice *HostOpenSoundDevice() +{ + return CreateSdlSoundDevice(); +} + +SoundDevice::~SoundDevice() +{ +} + +// Used for sound buffer maintenance requirements + +bool HostSoundServiceProc() +{ + return true; +} + +void HostGetCurrentDate(Date *pdate) +{ + time_t result = time(NULL); + struct tm *ptm = localtime(&result); + pdate->nYear = ptm->tm_year + 1900; + pdate->nMonth = ptm->tm_mon + 1; + pdate->nDay = ptm->tm_mday; +} + +bool HostSavePreferences(void *pv, int cb) +{ + LOG() << HostHelpers::GetPrefsFilename(); + + FILE *pf = fopen(HostHelpers::GetPrefsFilename(), "wb"); + if (pf == NULL) { + LOG() << "error opening preferences! " << errno; + return false; + } + if (fwrite(pv, cb, 1, pf) != 1) { + LOG() << "error writing preferences! " << errno; + fclose(pf); + return false; + } + fclose(pf); + return true; +} + +int HostLoadPreferences(void *pv, int cb) +{ + FILE *pf = fopen(HostHelpers::GetPrefsFilename(), "rb"); + if (pf == NULL) { + return -1; + } + + // Read prefs + + int cbRead = (int)fread(pv, 1, cb, pf); + fclose(pf); + return cbRead; +} + +const char *HostGetMainDataDir() +{ + return HostHelpers::GetMainDataDir(); +} + +const char *HostGetSaveGamesDir() +{ + return HostHelpers::GetSaveGamesDir(); +} + +void HostNotEnoughMemory(bool fStorage, dword cbFree, dword cbNeed) +{ + HostMessageBox(TEXT((char *)"Need %ld bytes of memory but only %ld bytes are free!"), cbNeed, cbFree); +} + +bool HostEnumAddonFiles(Enum *penm, char *pszAddonDir, int cbDir, + char *pszAddon, int cb) +{ + // PackManager is the way to do this now + return false; +} + +} // namespace wi diff --git a/game/sdl/hosthelpers.h b/game/sdl/hosthelpers.h new file mode 100644 index 0000000..8245728 --- /dev/null +++ b/game/sdl/hosthelpers.h @@ -0,0 +1,65 @@ +#ifndef __HOSTHELPERS_H__ +#define __HOSTHELPERS_H__ + +#include +#include "game/ht.h" +#include "game/chatcontroller.h" + +namespace wi { + +struct SurfaceProperties { + int cxWidth; + int cyHeight; + int cbxPitch; + int cbyPitch; + unsigned short ffFormat; +}; + +const unsigned short kfDirect565 = 0x0001; +const unsigned short kfDirect888 = 0x0002; +const unsigned short kfDirect8 = 0x0004; + +class FormMgr; + +class HostHelpers +{ +public: + static void OpenUrl(const char *pszUrl); + static void Log(const char *pszFormat, ...); + static void Log(const char *pszFormat, va_list va); + static void MessageBox(const char *pszFormat, va_list va); + static void Break(); + static void GetSurfaceProperties(SurfaceProperties *pprops); + static void FrameStart(); + static void FrameComplete(int cfrmm, UpdateMap **apupd, Rect *arc, + bool fScrolled); + static void ResetScrollOffset(); + static void SetFormMgrs(FormMgr *pfrmmSimUI, FormMgr *pfrmmInput); + static DibBitmap *CreateFrontDib(int cx, int cy, int nDegreeOrientation); + static void SetPalette(Palette *ppal); + static const char *GetUdid(); + static void InitiateAsk(const char *title, int max, const char *def, + int keyboard, bool secure); + static void GetAskString(char *psz, int cb); + static IChatController *GetChatController(); + static void InitiateWebView(const char *title, const char *url); + static bool IsExiting(); + static void GameThreadStart(void *pv); + + // TODO(darrinm): unused? + static int main(int argc, char **argv); + + static bool Init(); + static void Cleanup(); + static const char *GetMainDataDir(); + static const char *GetMissionPacksDir(); + static const char *GetMissionPackInfosDir(); + static const char *GetTempDir(); + static const char *GetCompletesDir(); + static const char *GetSaveGamesDir(); + static const char *GetPrefsFilename(); +}; + +} // namespace wi + +#endif // __HOSTHELPERS_H__ diff --git a/game/sdl/htplatform.h b/game/sdl/htplatform.h new file mode 100644 index 0000000..9143103 --- /dev/null +++ b/game/sdl/htplatform.h @@ -0,0 +1,154 @@ +// htplatform.h + +#include +#include +#include +#include +#include +#include +#include +#include +#include "game/sdl/sdlpackfile.h" +#include "game/sdl/sysmessages.h" + +// To determine if running on simulator, the sdk sets TARGET_IPHONE_SIMULATOR +// to 0 or 1. + +#include +#if TARGET_IPHONE_SIMULATOR == 1 +#define SIMULATOR +#endif + +namespace wi { + +#define MakeDword(a, b, c, d) ((a) | ((b) << 8) | ((c) << 16) | ((d) << 24)) +#define BigWord(x) ((((x)&0xFF)<<8) | (((x)&0xFF00)>>8)) +#define BigDword(x) ((((x)&0xFF)<<24) | (((x)&0xFF00)<<8) | (((x)&0xFF0000)>>8) | (((x)&0xFF000000)>>24)) + +typedef char TCHAR; +#define TEXT(str) str + +#define _MAX_PATH PATH_MAX +#define MAX_PATH PATH_MAX + +#define chrPageUp 0xb +#define chrPageDown 0xc +#define chrUp chrPageUp +#define chrDown chrPageDown +#define vchrMenu 0x105 +#define vchrFind 0x10a +#define vchrCalc 0x10b +#define penDownEvent 1 +#define penUpEvent 2 +#define penMoveEvent 3 +#define keyDownEvent 4 +#define penDownEvent2 5 +#define penUpEvent2 6 +#define penMoveEvent2 7 +#define appStopEvent 22 +#define chrBackspace 0x0008 +#define chrDelete 0x007f +#define chrLeft 0x1000 +#define chrRight 0x1001 +#define chrUp chrPageUp +#define chrDown chrPageDown + +#define keyBitPower 0x0001 // Power key +#define keyBitPageUp 0x0002 // Page-up +#define keyBitPageDown 0x0004 // Page-down +#define keyBitHard1 0x0008 // App #1 +#define keyBitHard2 0x0010 // App #2 +#define keyBitHard3 0x0020 // App #3 +#define keyBitHard4 0x0040 // App #4 +#define keyBitCradle 0x0080 // Button on cradle +#define keyBitAntenna 0x0100 // Antenna "key" +#define keyBitContrast 0x0200 // Contrast key + +// From PalmSG + +#define keyBitDpadLeft 0x01000000 // d-pad left +#define keyBitDpadRight 0x02000000 // d-pad right +#define keyBitDpadButton 0x04000000 // d-pad center button + +// From PalmSource + +#define keyBitRockerUp 0x00010000 // 5-way rocker +#define keyBitRockerDown 0x00020000 +#define keyBitRockerLeft 0x00040000 +#define keyBitRockerRight 0x00080000 +#define keyBitRockerCenter 0x00100000 + +#define keyBitsAll 0xFFFFFFFF // all keys + +struct ModeInfo +{ + int cx; + int cy; + int cyGraffiti; + int nDepth; + bool fNative; + int nDegreeOrientation; +}; +#define kcmodesMax 16 + +struct Palette; +class Rect; +class DibBitmap; +class UpdateMap; +class SpriteManager; +class FormMgr; + +class Display // disp +{ +public: + Display(); + ~Display(); + + bool Init(); + void SetPalette(Palette *ppal); + int GetModeCount(); + void GetModeInfo(int imode, ModeInfo *pmode); + int GetMode(ModeInfo *pmode); + bool SetMode(int imode); + void DrawText(const char *psz, int x, int y, word wf); + void DrawFrameInclusive(Rect *prc); + DibBitmap *GetBackDib(); + DibBitmap *GetFrontDib(); + DibBitmap *GetClippingDib(); + void GetHslAdjustments(short *pnHueOffset, short *pnSatMultiplier, short *pnLumOffset); + void FrameStart(); + void FrameComplete(int cfrmm, UpdateMap **apupd, Rect *arc, bool fScrolled); + void ResetScrollOffset(); + SpriteManager *GetSpriteManager(); + void SetFormMgrs(FormMgr *pfrmmSim, FormMgr *pfrmmInput); + +private: + int m_imode; + ModeInfo m_amodeInfo[kcmodesMax]; + int m_cmodes; + + int m_cx; + int m_cy; + DibBitmap *m_pbmBack; + DibBitmap *m_pbmFront; + DibBitmap *m_pbmClip; + + SDL_Surface *screen; +}; + +#define kfDtClearLine 1 + +extern Display *gpdisp; + +class PlatformSprite { +public: + virtual void Draw(void *pv, Size *psiz) = 0; +}; + +class SoundDevice; +SoundDevice *CreateSdlSoundDevice(); +void PrependDataDirectory(char *pszIn, char *pszOut); + +extern SdlPackFileReader gpakr; + +} // namespace wi diff --git a/game/sdl/mac/Info.plist b/game/sdl/mac/Info.plist new file mode 100644 index 0000000..c8efac9 --- /dev/null +++ b/game/sdl/mac/Info.plist @@ -0,0 +1,44 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDocumentTypes + + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + wi.icns + CFBundleIdentifier + com.spiffcode.${PRODUCT_NAME:rfc1034identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleURLTypes + + CFBundleVersion + 1 + LSApplicationCategoryType + public.app-category.strategy-games + LSMinimumSystemVersion + ${MACOSX_DEPLOYMENT_TARGET} + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + NSServices + + UTExportedTypeDeclarations + + UTImportedTypeDeclarations + + + diff --git a/game/sdl/mac/Prefix.pch b/game/sdl/mac/Prefix.pch new file mode 100644 index 0000000..8029900 --- /dev/null +++ b/game/sdl/mac/Prefix.pch @@ -0,0 +1,7 @@ +// +// Prefix header for all source files of the 'Warfare Incorporated' target in the 'Warfare Incorporated' project +// + +#ifdef __OBJC__ + #import +#endif diff --git a/game/sdl/mac/SDL.framework/Headers b/game/sdl/mac/SDL.framework/Headers new file mode 120000 index 0000000..a177d2a --- /dev/null +++ b/game/sdl/mac/SDL.framework/Headers @@ -0,0 +1 @@ +Versions/Current/Headers \ No newline at end of file diff --git a/game/sdl/mac/SDL.framework/Resources b/game/sdl/mac/SDL.framework/Resources new file mode 120000 index 0000000..953ee36 --- /dev/null +++ b/game/sdl/mac/SDL.framework/Resources @@ -0,0 +1 @@ +Versions/Current/Resources \ No newline at end of file diff --git a/game/sdl/mac/SDL.framework/SDL b/game/sdl/mac/SDL.framework/SDL new file mode 120000 index 0000000..35e94c3 --- /dev/null +++ b/game/sdl/mac/SDL.framework/SDL @@ -0,0 +1 @@ +Versions/Current/SDL \ No newline at end of file diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL.h new file mode 100644 index 0000000..d6f130f --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL.h @@ -0,0 +1,162 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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. +*/ + +/** + * \file SDL.h + * + * Main include header for the SDL library + */ + +/** + * \mainpage Simple DirectMedia Layer (SDL) + * + * http://www.libsdl.org/ + * + * \section intro_sec Introduction + * + * This is the Simple DirectMedia Layer, a general API that provides low + * level access to audio, keyboard, mouse, joystick, 3D hardware via OpenGL, + * and 2D framebuffer across multiple platforms. + * + * SDL is written in C, but works with C++ natively, and has bindings to + * several other languages, including Ada, C#, Eiffel, Erlang, Euphoria, + * Guile, Haskell, Java, Lisp, Lua, ML, Objective C, Pascal, Perl, PHP, + * Pike, Pliant, Python, Ruby, and Smalltalk. + * + * This library is distributed under GNU LGPL version 2, which can be + * found in the file "COPYING". This license allows you to use SDL + * freely in commercial programs as long as you link with the dynamic + * library. + * + * The best way to learn how to use SDL is to check out the header files in + * the "include" subdirectory and the programs in the "test" subdirectory. + * The header files and test programs are well commented and always up to date. + * More documentation is available in HTML format in "docs/index.html", and + * a documentation wiki is available online at: + * http://www.libsdl.org/cgi/docwiki.cgi + * + * The test programs in the "test" subdirectory are in the public domain. + * + * Frequently asked questions are answered online: + * http://www.libsdl.org/faq.php + * + * If you need help with the library, or just want to discuss SDL related + * issues, you can join the developers mailing list: + * http://www.libsdl.org/mailing-list.php + * + * Enjoy! + * Sam Lantinga (slouken@libsdl.org) + */ + +#ifndef _SDL_H +#define _SDL_H + +#include "SDL_main.h" +#include "SDL_stdinc.h" +#include "SDL_assert.h" +#include "SDL_atomic.h" +#include "SDL_audio.h" +#include "SDL_clipboard.h" +#include "SDL_cpuinfo.h" +#include "SDL_endian.h" +#include "SDL_error.h" +#include "SDL_events.h" +#include "SDL_hints.h" +#include "SDL_loadso.h" +#include "SDL_log.h" +#include "SDL_mutex.h" +#include "SDL_power.h" +#include "SDL_render.h" +#include "SDL_rwops.h" +#include "SDL_thread.h" +#include "SDL_timer.h" +#include "SDL_version.h" +#include "SDL_video.h" +#include "SDL_compat.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +/* As of version 0.5, SDL is loaded dynamically into the application */ + +/** + * \name SDL_INIT_* + * + * These are the flags which may be passed to SDL_Init(). You should + * specify the subsystems which you will be using in your application. + */ +/*@{*/ +#define SDL_INIT_TIMER 0x00000001 +#define SDL_INIT_AUDIO 0x00000010 +#define SDL_INIT_VIDEO 0x00000020 +#define SDL_INIT_JOYSTICK 0x00000200 +#define SDL_INIT_HAPTIC 0x00001000 +#define SDL_INIT_NOPARACHUTE 0x00100000 /**< Don't catch fatal signals */ +#define SDL_INIT_EVERYTHING 0x0000FFFF +/*@}*/ + +/** + * This function initializes the subsystems specified by \c flags + * Unless the ::SDL_INIT_NOPARACHUTE flag is set, it will install cleanup + * signal handlers for some commonly ignored fatal signals (like SIGSEGV). + */ +extern DECLSPEC int SDLCALL SDL_Init(Uint32 flags); + +/** + * This function initializes specific SDL subsystems + */ +extern DECLSPEC int SDLCALL SDL_InitSubSystem(Uint32 flags); + +/** + * This function cleans up specific SDL subsystems + */ +extern DECLSPEC void SDLCALL SDL_QuitSubSystem(Uint32 flags); + +/** + * This function returns a mask of the specified subsystems which have + * previously been initialized. + * + * If \c flags is 0, it returns a mask of all initialized subsystems. + */ +extern DECLSPEC Uint32 SDLCALL SDL_WasInit(Uint32 flags); + +/** + * This function cleans up all initialized subsystems. You should + * call it upon all exit conditions. + */ +extern DECLSPEC void SDLCALL SDL_Quit(void); + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif +#include "close_code.h" + +#endif /* _SDL_H */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_assert.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_assert.h new file mode 100644 index 0000000..13e3163 --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_assert.h @@ -0,0 +1,242 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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 _SDL_assert_h +#define _SDL_assert_h + +#include "SDL_config.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +#ifndef SDL_ASSERT_LEVEL +#ifdef SDL_DEFAULT_ASSERT_LEVEL +#define SDL_ASSERT_LEVEL SDL_DEFAULT_ASSERT_LEVEL +#elif defined(_DEBUG) || defined(DEBUG) || \ + (defined(__GNUC__) && !defined(__OPTIMIZE__)) +#define SDL_ASSERT_LEVEL 2 +#else +#define SDL_ASSERT_LEVEL 1 +#endif +#endif /* SDL_ASSERT_LEVEL */ + +/* +These are macros and not first class functions so that the debugger breaks +on the assertion line and not in some random guts of SDL, and so each +assert can have unique static variables associated with it. +*/ + +#if defined(_MSC_VER) && !defined(_WIN32_WCE) +/* Don't include intrin.h here because it contains C++ code */ +extern void __cdecl __debugbreak(void); + #define SDL_TriggerBreakpoint() __debugbreak() +#elif (defined(__GNUC__) && ((__i386__) || (__x86_64__))) + #define SDL_TriggerBreakpoint() __asm__ __volatile__ ( "int $3\n\t" ) +#elif defined(HAVE_SIGNAL_H) + #include + #define SDL_TriggerBreakpoint() raise(SIGTRAP) +#else + /* How do we trigger breakpoints on this platform? */ + #define SDL_TriggerBreakpoint() +#endif + +#if (__STDC_VERSION__ >= 199901L) /* C99 supports __func__ as a standard. */ +# define SDL_FUNCTION __func__ +#elif ((__GNUC__ >= 2) || defined(_MSC_VER)) +# define SDL_FUNCTION __FUNCTION__ +#else +# define SDL_FUNCTION "???" +#endif +#define SDL_FILE __FILE__ +#define SDL_LINE __LINE__ + +/* +sizeof (x) makes the compiler still parse the expression even without +assertions enabled, so the code is always checked at compile time, but +doesn't actually generate code for it, so there are no side effects or +expensive checks at run time, just the constant size of what x WOULD be, +which presumably gets optimized out as unused. +This also solves the problem of... + + int somevalue = blah(); + SDL_assert(somevalue == 1); + +...which would cause compiles to complain that somevalue is unused if we +disable assertions. +*/ + +#define SDL_disabled_assert(condition) \ + do { (void) sizeof ((condition)); } while (0) + +#if (SDL_ASSERT_LEVEL > 0) + +typedef enum +{ + SDL_ASSERTION_RETRY, /**< Retry the assert immediately. */ + SDL_ASSERTION_BREAK, /**< Make the debugger trigger a breakpoint. */ + SDL_ASSERTION_ABORT, /**< Terminate the program. */ + SDL_ASSERTION_IGNORE, /**< Ignore the assert. */ + SDL_ASSERTION_ALWAYS_IGNORE, /**< Ignore the assert from now on. */ +} SDL_assert_state; + +typedef struct SDL_assert_data +{ + int always_ignore; + unsigned int trigger_count; + const char *condition; + const char *filename; + int linenum; + const char *function; + const struct SDL_assert_data *next; +} SDL_assert_data; + +/* Never call this directly. Use the SDL_assert* macros. */ +extern DECLSPEC SDL_assert_state SDLCALL SDL_ReportAssertion(SDL_assert_data *, + const char *, + const char *, int); + +/* the do {} while(0) avoids dangling else problems: + if (x) SDL_assert(y); else blah(); + ... without the do/while, the "else" could attach to this macro's "if". + We try to handle just the minimum we need here in a macro...the loop, + the static vars, and break points. The heavy lifting is handled in + SDL_ReportAssertion(), in SDL_assert.c. +*/ +#define SDL_enabled_assert(condition) \ + do { \ + while ( !(condition) ) { \ + static struct SDL_assert_data assert_data = { \ + 0, 0, #condition, 0, 0, 0, 0 \ + }; \ + const SDL_assert_state state = SDL_ReportAssertion(&assert_data, \ + SDL_FUNCTION, \ + SDL_FILE, \ + SDL_LINE); \ + if (state == SDL_ASSERTION_RETRY) { \ + continue; /* go again. */ \ + } else if (state == SDL_ASSERTION_BREAK) { \ + SDL_TriggerBreakpoint(); \ + } \ + break; /* not retrying. */ \ + } \ + } while (0) + +#endif /* enabled assertions support code */ + +/* Enable various levels of assertions. */ +#if SDL_ASSERT_LEVEL == 0 /* assertions disabled */ +# define SDL_assert(condition) SDL_disabled_assert(condition) +# define SDL_assert_release(condition) SDL_disabled_assert(condition) +# define SDL_assert_paranoid(condition) SDL_disabled_assert(condition) +#elif SDL_ASSERT_LEVEL == 1 /* release settings. */ +# define SDL_assert(condition) SDL_disabled_assert(condition) +# define SDL_assert_release(condition) SDL_enabled_assert(condition) +# define SDL_assert_paranoid(condition) SDL_disabled_assert(condition) +#elif SDL_ASSERT_LEVEL == 2 /* normal settings. */ +# define SDL_assert(condition) SDL_enabled_assert(condition) +# define SDL_assert_release(condition) SDL_enabled_assert(condition) +# define SDL_assert_paranoid(condition) SDL_disabled_assert(condition) +#elif SDL_ASSERT_LEVEL == 3 /* paranoid settings. */ +# define SDL_assert(condition) SDL_enabled_assert(condition) +# define SDL_assert_release(condition) SDL_enabled_assert(condition) +# define SDL_assert_paranoid(condition) SDL_enabled_assert(condition) +#else +# error Unknown assertion level. +#endif + + +typedef SDL_assert_state (SDLCALL *SDL_AssertionHandler)( + const SDL_assert_data* data, void* userdata); + +/** + * \brief Set an application-defined assertion handler. + * + * This allows an app to show its own assertion UI and/or force the + * response to an assertion failure. If the app doesn't provide this, SDL + * will try to do the right thing, popping up a system-specific GUI dialog, + * and probably minimizing any fullscreen windows. + * + * This callback may fire from any thread, but it runs wrapped in a mutex, so + * it will only fire from one thread at a time. + * + * Setting the callback to NULL restores SDL's original internal handler. + * + * This callback is NOT reset to SDL's internal handler upon SDL_Quit()! + * + * \return SDL_assert_state value of how to handle the assertion failure. + * + * \param handler Callback function, called when an assertion fails. + * \param userdata A pointer passed to the callback as-is. + */ +extern DECLSPEC void SDLCALL SDL_SetAssertionHandler( + SDL_AssertionHandler handler, + void *userdata); + +/** + * \brief Get a list of all assertion failures. + * + * Get all assertions triggered since last call to SDL_ResetAssertionReport(), + * or the start of the program. + * + * The proper way to examine this data looks something like this: + * + * + * const SDL_assert_data *item = SDL_GetAssertionReport(); + * while (item->condition) { + * printf("'%s', %s (%s:%d), triggered %u times, always ignore: %s.\n", + * item->condition, item->function, item->filename, + * item->linenum, item->trigger_count, + * item->always_ignore ? "yes" : "no"); + * item = item->next; + * } + * + * + * \return List of all assertions. This never returns NULL, + * even if there are no items. + * \sa SDL_ResetAssertionReport + */ +extern DECLSPEC const SDL_assert_data * SDLCALL SDL_GetAssertionReport(void); + +/** + * \brief Reset the list of all assertion failures. + * + * Reset list of all assertions triggered. + * + * \sa SDL_GetAssertionReport + */ +extern DECLSPEC void SDLCALL SDL_ResetAssertionReport(void); + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif +#include "close_code.h" + +#endif /* _SDL_assert_h */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_atomic.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_atomic.h new file mode 100644 index 0000000..5c6eadb --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_atomic.h @@ -0,0 +1,318 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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. +*/ + +/** + * \file SDL_atomic.h + * + * Atomic operations. + * + * IMPORTANT: + * If you are not an expert in concurrent lockless programming, you should + * only be using the atomic lock and reference counting functions in this + * file. In all other cases you should be protecting your data structures + * with full mutexes. + * + * The list of "safe" functions to use are: + * SDL_AtomicLock() + * SDL_AtomicUnlock() + * SDL_AtomicIncRef() + * SDL_AtomicDecRef() + * + * Seriously, here be dragons! + * ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + * + * You can find out a little more about lockless programming and the + * subtle issues that can arise here: + * http://msdn.microsoft.com/en-us/library/ee418650%28v=vs.85%29.aspx + * + * There's also lots of good information here: + * http://www.1024cores.net/home/lock-free-algorithms + * + * These operations may or may not actually be implemented using + * processor specific atomic operations. When possible they are + * implemented as true processor specific atomic operations. When that + * is not possible the are implemented using locks that *do* use the + * available atomic operations. + * + * All of the atomic operations that modify memory are full memory barriers. + */ + +#ifndef _SDL_atomic_h_ +#define _SDL_atomic_h_ + +#include "SDL_stdinc.h" +#include "SDL_platform.h" + +#include "begin_code.h" + +/* Need to do this here because intrin.h has C++ code in it */ +/* Visual Studio 2005 has a bug where intrin.h conflicts with winnt.h */ +#if defined(_MSC_VER) && (_MSC_VER >= 1500) && !defined(_WIN32_WCE) +#include +#define HAVE_MSC_ATOMICS 1 +#endif + +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +/** + * \name SDL AtomicLock + * + * The atomic locks are efficient spinlocks using CPU instructions, + * but are vulnerable to starvation and can spin forever if a thread + * holding a lock has been terminated. For this reason you should + * minimize the code executed inside an atomic lock and never do + * expensive things like API or system calls while holding them. + * + * The atomic locks are not safe to lock recursively. + * + * Porting Note: + * The spin lock functions and type are required and can not be + * emulated because they are used in the atomic emulation code. + */ +/*@{*/ + +typedef int SDL_SpinLock; + +/** + * \brief Try to lock a spin lock by setting it to a non-zero value. + * + * \param lock Points to the lock. + * + * \return SDL_TRUE if the lock succeeded, SDL_FALSE if the lock is already held. + */ +extern DECLSPEC SDL_bool SDLCALL SDL_AtomicTryLock(SDL_SpinLock *lock); + +/** + * \brief Lock a spin lock by setting it to a non-zero value. + * + * \param lock Points to the lock. + */ +extern DECLSPEC void SDLCALL SDL_AtomicLock(SDL_SpinLock *lock); + +/** + * \brief Unlock a spin lock by setting it to 0. Always returns immediately + * + * \param lock Points to the lock. + */ +extern DECLSPEC void SDLCALL SDL_AtomicUnlock(SDL_SpinLock *lock); + +/*@}*//*SDL AtomicLock*/ + + +/** + * The compiler barrier prevents the compiler from reordering + * reads and writes to globally visible variables across the call. + */ +#ifdef _MSC_VER +void _ReadWriteBarrier(void); +#pragma intrinsic(_ReadWriteBarrier) +#define SDL_CompilerBarrier() _ReadWriteBarrier() +#elif defined(__GNUC__) +#define SDL_CompilerBarrier() __asm__ __volatile__ ("" : : : "memory") +#else +#define SDL_CompilerBarrier() \ +({ SDL_SpinLock _tmp = 0; SDL_AtomicLock(&_tmp); SDL_AtomicUnlock(&_tmp); }) +#endif + +/* Platform specific optimized versions of the atomic functions, + * you can disable these by defining SDL_DISABLE_ATOMIC_INLINE + */ +#if SDL_ATOMIC_DISABLED +#define SDL_DISABLE_ATOMIC_INLINE +#endif +#ifndef SDL_DISABLE_ATOMIC_INLINE + +#ifdef HAVE_MSC_ATOMICS + +#define SDL_AtomicSet(a, v) _InterlockedExchange((long*)&(a)->value, (v)) +#define SDL_AtomicAdd(a, v) _InterlockedExchangeAdd((long*)&(a)->value, (v)) +#define SDL_AtomicCAS(a, oldval, newval) (_InterlockedCompareExchange((long*)&(a)->value, (newval), (oldval)) == (oldval)) +#define SDL_AtomicSetPtr(a, v) _InterlockedExchangePointer((a), (v)) +#if _M_IX86 +#define SDL_AtomicCASPtr(a, oldval, newval) (_InterlockedCompareExchange((long*)(a), (long)(newval), (long)(oldval)) == (long)(oldval)) +#else +#define SDL_AtomicCASPtr(a, oldval, newval) (_InterlockedCompareExchangePointer((a), (newval), (oldval)) == (oldval)) +#endif + +#elif defined(__MACOSX__) +#include + +#define SDL_AtomicCAS(a, oldval, newval) OSAtomicCompareAndSwap32Barrier((oldval), (newval), &(a)->value) +#if SIZEOF_VOIDP == 4 +#define SDL_AtomicCASPtr(a, oldval, newval) OSAtomicCompareAndSwap32Barrier((int32_t)(oldval), (int32_t)(newval), (int32_t*)(a)) +#elif SIZEOF_VOIDP == 8 +#define SDL_AtomicCASPtr(a, oldval, newval) OSAtomicCompareAndSwap64Barrier((int64_t)(oldval), (int64_t)(newval), (int64_t*)(a)) +#endif + +#elif defined(HAVE_GCC_ATOMICS) + +#define SDL_AtomicSet(a, v) __sync_lock_test_and_set(&(a)->value, v) +#define SDL_AtomicAdd(a, v) __sync_fetch_and_add(&(a)->value, v) +#define SDL_AtomicSetPtr(a, v) __sync_lock_test_and_set(a, v) +#define SDL_AtomicCAS(a, oldval, newval) __sync_bool_compare_and_swap(&(a)->value, oldval, newval) +#define SDL_AtomicCASPtr(a, oldval, newval) __sync_bool_compare_and_swap(a, oldval, newval) + +#endif + +#endif /* !SDL_DISABLE_ATOMIC_INLINE */ + + +/** + * \brief A type representing an atomic integer value. It is a struct + * so people don't accidentally use numeric operations on it. + */ +#ifndef SDL_atomic_t_defined +typedef struct { int value; } SDL_atomic_t; +#endif + +/** + * \brief Set an atomic variable to a new value if it is currently an old value. + * + * \return SDL_TRUE if the atomic variable was set, SDL_FALSE otherwise. + * + * \note If you don't know what this function is for, you shouldn't use it! +*/ +#ifndef SDL_AtomicCAS +#define SDL_AtomicCAS SDL_AtomicCAS_ +#endif +extern DECLSPEC SDL_bool SDLCALL SDL_AtomicCAS_(SDL_atomic_t *a, int oldval, int newval); + +/** + * \brief Set an atomic variable to a value. + * + * \return The previous value of the atomic variable. + */ +#ifndef SDL_AtomicSet +static __inline__ int SDL_AtomicSet(SDL_atomic_t *a, int v) +{ + int value; + do { + value = a->value; + } while (!SDL_AtomicCAS(a, value, v)); + return value; +} +#endif + +/** + * \brief Get the value of an atomic variable + */ +#ifndef SDL_AtomicGet +static __inline__ int SDL_AtomicGet(SDL_atomic_t *a) +{ + int value = a->value; + SDL_CompilerBarrier(); + return value; +} +#endif + +/** + * \brief Add to an atomic variable. + * + * \return The previous value of the atomic variable. + * + * \note This same style can be used for any number operation + */ +#ifndef SDL_AtomicAdd +static __inline__ int SDL_AtomicAdd(SDL_atomic_t *a, int v) +{ + int value; + do { + value = a->value; + } while (!SDL_AtomicCAS(a, value, (value + v))); + return value; +} +#endif + +/** + * \brief Increment an atomic variable used as a reference count. + */ +#ifndef SDL_AtomicIncRef +#define SDL_AtomicIncRef(a) SDL_AtomicAdd(a, 1) +#endif + +/** + * \brief Decrement an atomic variable used as a reference count. + * + * \return SDL_TRUE if the variable reached zero after decrementing, + * SDL_FALSE otherwise + */ +#ifndef SDL_AtomicDecRef +#define SDL_AtomicDecRef(a) (SDL_AtomicAdd(a, -1) == 1) +#endif + +/** + * \brief Set a pointer to a new value if it is currently an old value. + * + * \return SDL_TRUE if the pointer was set, SDL_FALSE otherwise. + * + * \note If you don't know what this function is for, you shouldn't use it! +*/ +#ifndef SDL_AtomicCASPtr +#define SDL_AtomicCASPtr SDL_AtomicCASPtr_ +#endif +extern DECLSPEC SDL_bool SDLCALL SDL_AtomicCASPtr_(void* *a, void *oldval, void *newval); + +/** + * \brief Set a pointer to a value atomically. + * + * \return The previous value of the pointer. + */ +#ifndef SDL_AtomicSetPtr +static __inline__ void* SDL_AtomicSetPtr(void* *a, void* v) +{ + void* value; + do { + value = *a; + } while (!SDL_AtomicCASPtr(a, value, v)); + return value; +} +#endif + +/** + * \brief Get the value of a pointer atomically. + */ +#ifndef SDL_AtomicGetPtr +static __inline__ void* SDL_AtomicGetPtr(void* *a) +{ + void* value = *a; + SDL_CompilerBarrier(); + return value; +} +#endif + + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif + +#include "close_code.h" + +#endif /* _SDL_atomic_h_ */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_audio.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_audio.h new file mode 100644 index 0000000..6110841 --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_audio.h @@ -0,0 +1,509 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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. +*/ + +/** + * \file SDL_audio.h + * + * Access to the raw audio mixing buffer for the SDL library. + */ + +#ifndef _SDL_audio_h +#define _SDL_audio_h + +#include "SDL_stdinc.h" +#include "SDL_error.h" +#include "SDL_endian.h" +#include "SDL_mutex.h" +#include "SDL_thread.h" +#include "SDL_rwops.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +/** + * \brief Audio format flags. + * + * These are what the 16 bits in SDL_AudioFormat currently mean... + * (Unspecified bits are always zero). + * + * \verbatim + ++-----------------------sample is signed if set + || + || ++-----------sample is bigendian if set + || || + || || ++---sample is float if set + || || || + || || || +---sample bit size---+ + || || || | | + 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00 + \endverbatim + * + * There are macros in SDL 1.3 and later to query these bits. + */ +typedef Uint16 SDL_AudioFormat; + +/** + * \name Audio flags + */ +/*@{*/ + +#define SDL_AUDIO_MASK_BITSIZE (0xFF) +#define SDL_AUDIO_MASK_DATATYPE (1<<8) +#define SDL_AUDIO_MASK_ENDIAN (1<<12) +#define SDL_AUDIO_MASK_SIGNED (1<<15) +#define SDL_AUDIO_BITSIZE(x) (x & SDL_AUDIO_MASK_BITSIZE) +#define SDL_AUDIO_ISFLOAT(x) (x & SDL_AUDIO_MASK_DATATYPE) +#define SDL_AUDIO_ISBIGENDIAN(x) (x & SDL_AUDIO_MASK_ENDIAN) +#define SDL_AUDIO_ISSIGNED(x) (x & SDL_AUDIO_MASK_SIGNED) +#define SDL_AUDIO_ISINT(x) (!SDL_AUDIO_ISFLOAT(x)) +#define SDL_AUDIO_ISLITTLEENDIAN(x) (!SDL_AUDIO_ISBIGENDIAN(x)) +#define SDL_AUDIO_ISUNSIGNED(x) (!SDL_AUDIO_ISSIGNED(x)) + +/** + * \name Audio format flags + * + * Defaults to LSB byte order. + */ +/*@{*/ +#define AUDIO_U8 0x0008 /**< Unsigned 8-bit samples */ +#define AUDIO_S8 0x8008 /**< Signed 8-bit samples */ +#define AUDIO_U16LSB 0x0010 /**< Unsigned 16-bit samples */ +#define AUDIO_S16LSB 0x8010 /**< Signed 16-bit samples */ +#define AUDIO_U16MSB 0x1010 /**< As above, but big-endian byte order */ +#define AUDIO_S16MSB 0x9010 /**< As above, but big-endian byte order */ +#define AUDIO_U16 AUDIO_U16LSB +#define AUDIO_S16 AUDIO_S16LSB +/*@}*/ + +/** + * \name int32 support + * + * New to SDL 1.3. + */ +/*@{*/ +#define AUDIO_S32LSB 0x8020 /**< 32-bit integer samples */ +#define AUDIO_S32MSB 0x9020 /**< As above, but big-endian byte order */ +#define AUDIO_S32 AUDIO_S32LSB +/*@}*/ + +/** + * \name float32 support + * + * New to SDL 1.3. + */ +/*@{*/ +#define AUDIO_F32LSB 0x8120 /**< 32-bit floating point samples */ +#define AUDIO_F32MSB 0x9120 /**< As above, but big-endian byte order */ +#define AUDIO_F32 AUDIO_F32LSB +/*@}*/ + +/** + * \name Native audio byte ordering + */ +/*@{*/ +#if SDL_BYTEORDER == SDL_LIL_ENDIAN +#define AUDIO_U16SYS AUDIO_U16LSB +#define AUDIO_S16SYS AUDIO_S16LSB +#define AUDIO_S32SYS AUDIO_S32LSB +#define AUDIO_F32SYS AUDIO_F32LSB +#else +#define AUDIO_U16SYS AUDIO_U16MSB +#define AUDIO_S16SYS AUDIO_S16MSB +#define AUDIO_S32SYS AUDIO_S32MSB +#define AUDIO_F32SYS AUDIO_F32MSB +#endif +/*@}*/ + +/** + * \name Allow change flags + * + * Which audio format changes are allowed when opening a device. + */ +/*@{*/ +#define SDL_AUDIO_ALLOW_FREQUENCY_CHANGE 0x00000001 +#define SDL_AUDIO_ALLOW_FORMAT_CHANGE 0x00000002 +#define SDL_AUDIO_ALLOW_CHANNELS_CHANGE 0x00000004 +#define SDL_AUDIO_ALLOW_ANY_CHANGE (SDL_AUDIO_ALLOW_FREQUENCY_CHANGE|SDL_AUDIO_ALLOW_FORMAT_CHANGE|SDL_AUDIO_ALLOW_CHANNELS_CHANGE) +/*@}*/ + +/*@}*//*Audio flags*/ + +/** + * This function is called when the audio device needs more data. + * + * \param userdata An application-specific parameter saved in + * the SDL_AudioSpec structure + * \param stream A pointer to the audio data buffer. + * \param len The length of that buffer in bytes. + * + * Once the callback returns, the buffer will no longer be valid. + * Stereo samples are stored in a LRLRLR ordering. + */ +typedef void (SDLCALL * SDL_AudioCallback) (void *userdata, Uint8 * stream, + int len); + +/** + * The calculated values in this structure are calculated by SDL_OpenAudio(). + */ +typedef struct SDL_AudioSpec +{ + int freq; /**< DSP frequency -- samples per second */ + SDL_AudioFormat format; /**< Audio data format */ + Uint8 channels; /**< Number of channels: 1 mono, 2 stereo */ + Uint8 silence; /**< Audio buffer silence value (calculated) */ + Uint16 samples; /**< Audio buffer size in samples (power of 2) */ + Uint16 padding; /**< Necessary for some compile environments */ + Uint32 size; /**< Audio buffer size in bytes (calculated) */ + SDL_AudioCallback callback; + void *userdata; +} SDL_AudioSpec; + + +struct SDL_AudioCVT; +typedef void (SDLCALL * SDL_AudioFilter) (struct SDL_AudioCVT * cvt, + SDL_AudioFormat format); + +/** + * A structure to hold a set of audio conversion filters and buffers. + */ +typedef struct SDL_AudioCVT +{ + int needed; /**< Set to 1 if conversion possible */ + SDL_AudioFormat src_format; /**< Source audio format */ + SDL_AudioFormat dst_format; /**< Target audio format */ + double rate_incr; /**< Rate conversion increment */ + Uint8 *buf; /**< Buffer to hold entire audio data */ + int len; /**< Length of original audio buffer */ + int len_cvt; /**< Length of converted audio buffer */ + int len_mult; /**< buffer must be len*len_mult big */ + double len_ratio; /**< Given len, final size is len*len_ratio */ + SDL_AudioFilter filters[10]; /**< Filter list */ + int filter_index; /**< Current audio conversion function */ +} SDL_AudioCVT; + + +/* Function prototypes */ + +/** + * \name Driver discovery functions + * + * These functions return the list of built in audio drivers, in the + * order that they are normally initialized by default. + */ +/*@{*/ +extern DECLSPEC int SDLCALL SDL_GetNumAudioDrivers(void); +extern DECLSPEC const char *SDLCALL SDL_GetAudioDriver(int index); +/*@}*/ + +/** + * \name Initialization and cleanup + * + * \internal These functions are used internally, and should not be used unless + * you have a specific need to specify the audio driver you want to + * use. You should normally use SDL_Init() or SDL_InitSubSystem(). + */ +/*@{*/ +extern DECLSPEC int SDLCALL SDL_AudioInit(const char *driver_name); +extern DECLSPEC void SDLCALL SDL_AudioQuit(void); +/*@}*/ + +/** + * This function returns the name of the current audio driver, or NULL + * if no driver has been initialized. + */ +extern DECLSPEC const char *SDLCALL SDL_GetCurrentAudioDriver(void); + +/** + * This function opens the audio device with the desired parameters, and + * returns 0 if successful, placing the actual hardware parameters in the + * structure pointed to by \c obtained. If \c obtained is NULL, the audio + * data passed to the callback function will be guaranteed to be in the + * requested format, and will be automatically converted to the hardware + * audio format if necessary. This function returns -1 if it failed + * to open the audio device, or couldn't set up the audio thread. + * + * When filling in the desired audio spec structure, + * - \c desired->freq should be the desired audio frequency in samples-per- + * second. + * - \c desired->format should be the desired audio format. + * - \c desired->samples is the desired size of the audio buffer, in + * samples. This number should be a power of two, and may be adjusted by + * the audio driver to a value more suitable for the hardware. Good values + * seem to range between 512 and 8096 inclusive, depending on the + * application and CPU speed. Smaller values yield faster response time, + * but can lead to underflow if the application is doing heavy processing + * and cannot fill the audio buffer in time. A stereo sample consists of + * both right and left channels in LR ordering. + * Note that the number of samples is directly related to time by the + * following formula: \code ms = (samples*1000)/freq \endcode + * - \c desired->size is the size in bytes of the audio buffer, and is + * calculated by SDL_OpenAudio(). + * - \c desired->silence is the value used to set the buffer to silence, + * and is calculated by SDL_OpenAudio(). + * - \c desired->callback should be set to a function that will be called + * when the audio device is ready for more data. It is passed a pointer + * to the audio buffer, and the length in bytes of the audio buffer. + * This function usually runs in a separate thread, and so you should + * protect data structures that it accesses by calling SDL_LockAudio() + * and SDL_UnlockAudio() in your code. + * - \c desired->userdata is passed as the first parameter to your callback + * function. + * + * The audio device starts out playing silence when it's opened, and should + * be enabled for playing by calling \c SDL_PauseAudio(0) when you are ready + * for your audio callback function to be called. Since the audio driver + * may modify the requested size of the audio buffer, you should allocate + * any local mixing buffers after you open the audio device. + */ +extern DECLSPEC int SDLCALL SDL_OpenAudio(SDL_AudioSpec * desired, + SDL_AudioSpec * obtained); + +/** + * SDL Audio Device IDs. + * + * A successful call to SDL_OpenAudio() is always device id 1, and legacy + * SDL audio APIs assume you want this device ID. SDL_OpenAudioDevice() calls + * always returns devices >= 2 on success. The legacy calls are good both + * for backwards compatibility and when you don't care about multiple, + * specific, or capture devices. + */ +typedef Uint32 SDL_AudioDeviceID; + +/** + * Get the number of available devices exposed by the current driver. + * Only valid after a successfully initializing the audio subsystem. + * Returns -1 if an explicit list of devices can't be determined; this is + * not an error. For example, if SDL is set up to talk to a remote audio + * server, it can't list every one available on the Internet, but it will + * still allow a specific host to be specified to SDL_OpenAudioDevice(). + * + * In many common cases, when this function returns a value <= 0, it can still + * successfully open the default device (NULL for first argument of + * SDL_OpenAudioDevice()). + */ +extern DECLSPEC int SDLCALL SDL_GetNumAudioDevices(int iscapture); + +/** + * Get the human-readable name of a specific audio device. + * Must be a value between 0 and (number of audio devices-1). + * Only valid after a successfully initializing the audio subsystem. + * The values returned by this function reflect the latest call to + * SDL_GetNumAudioDevices(); recall that function to redetect available + * hardware. + * + * The string returned by this function is UTF-8 encoded, read-only, and + * managed internally. You are not to free it. If you need to keep the + * string for any length of time, you should make your own copy of it, as it + * will be invalid next time any of several other SDL functions is called. + */ +extern DECLSPEC const char *SDLCALL SDL_GetAudioDeviceName(int index, + int iscapture); + + +/** + * Open a specific audio device. Passing in a device name of NULL requests + * the most reasonable default (and is equivalent to calling SDL_OpenAudio()). + * + * The device name is a UTF-8 string reported by SDL_GetAudioDeviceName(), but + * some drivers allow arbitrary and driver-specific strings, such as a + * hostname/IP address for a remote audio server, or a filename in the + * diskaudio driver. + * + * \return 0 on error, a valid device ID that is >= 2 on success. + * + * SDL_OpenAudio(), unlike this function, always acts on device ID 1. + */ +extern DECLSPEC SDL_AudioDeviceID SDLCALL SDL_OpenAudioDevice(const char + *device, + int iscapture, + const + SDL_AudioSpec * + desired, + SDL_AudioSpec * + obtained, + int + allowed_changes); + + + +/** + * \name Audio state + * + * Get the current audio state. + */ +/*@{*/ +typedef enum +{ + SDL_AUDIO_STOPPED = 0, + SDL_AUDIO_PLAYING, + SDL_AUDIO_PAUSED +} SDL_AudioStatus; +extern DECLSPEC SDL_AudioStatus SDLCALL SDL_GetAudioStatus(void); + +extern DECLSPEC SDL_AudioStatus SDLCALL +SDL_GetAudioDeviceStatus(SDL_AudioDeviceID dev); +/*@}*//*Audio State*/ + +/** + * \name Pause audio functions + * + * These functions pause and unpause the audio callback processing. + * They should be called with a parameter of 0 after opening the audio + * device to start playing sound. This is so you can safely initialize + * data for your callback function after opening the audio device. + * Silence will be written to the audio device during the pause. + */ +/*@{*/ +extern DECLSPEC void SDLCALL SDL_PauseAudio(int pause_on); +extern DECLSPEC void SDLCALL SDL_PauseAudioDevice(SDL_AudioDeviceID dev, + int pause_on); +/*@}*//*Pause audio functions*/ + +/** + * This function loads a WAVE from the data source, automatically freeing + * that source if \c freesrc is non-zero. For example, to load a WAVE file, + * you could do: + * \code + * SDL_LoadWAV_RW(SDL_RWFromFile("sample.wav", "rb"), 1, ...); + * \endcode + * + * If this function succeeds, it returns the given SDL_AudioSpec, + * filled with the audio data format of the wave data, and sets + * \c *audio_buf to a malloc()'d buffer containing the audio data, + * and sets \c *audio_len to the length of that audio buffer, in bytes. + * You need to free the audio buffer with SDL_FreeWAV() when you are + * done with it. + * + * This function returns NULL and sets the SDL error message if the + * wave file cannot be opened, uses an unknown data format, or is + * corrupt. Currently raw and MS-ADPCM WAVE files are supported. + */ +extern DECLSPEC SDL_AudioSpec *SDLCALL SDL_LoadWAV_RW(SDL_RWops * src, + int freesrc, + SDL_AudioSpec * spec, + Uint8 ** audio_buf, + Uint32 * audio_len); + +/** + * Loads a WAV from a file. + * Compatibility convenience function. + */ +#define SDL_LoadWAV(file, spec, audio_buf, audio_len) \ + SDL_LoadWAV_RW(SDL_RWFromFile(file, "rb"),1, spec,audio_buf,audio_len) + +/** + * This function frees data previously allocated with SDL_LoadWAV_RW() + */ +extern DECLSPEC void SDLCALL SDL_FreeWAV(Uint8 * audio_buf); + +/** + * This function takes a source format and rate and a destination format + * and rate, and initializes the \c cvt structure with information needed + * by SDL_ConvertAudio() to convert a buffer of audio data from one format + * to the other. + * + * \return -1 if the format conversion is not supported, 0 if there's + * no conversion needed, or 1 if the audio filter is set up. + */ +extern DECLSPEC int SDLCALL SDL_BuildAudioCVT(SDL_AudioCVT * cvt, + SDL_AudioFormat src_format, + Uint8 src_channels, + int src_rate, + SDL_AudioFormat dst_format, + Uint8 dst_channels, + int dst_rate); + +/** + * Once you have initialized the \c cvt structure using SDL_BuildAudioCVT(), + * created an audio buffer \c cvt->buf, and filled it with \c cvt->len bytes of + * audio data in the source format, this function will convert it in-place + * to the desired format. + * + * The data conversion may expand the size of the audio data, so the buffer + * \c cvt->buf should be allocated after the \c cvt structure is initialized by + * SDL_BuildAudioCVT(), and should be \c cvt->len*cvt->len_mult bytes long. + */ +extern DECLSPEC int SDLCALL SDL_ConvertAudio(SDL_AudioCVT * cvt); + +#define SDL_MIX_MAXVOLUME 128 +/** + * This takes two audio buffers of the playing audio format and mixes + * them, performing addition, volume adjustment, and overflow clipping. + * The volume ranges from 0 - 128, and should be set to ::SDL_MIX_MAXVOLUME + * for full audio volume. Note this does not change hardware volume. + * This is provided for convenience -- you can mix your own audio data. + */ +extern DECLSPEC void SDLCALL SDL_MixAudio(Uint8 * dst, const Uint8 * src, + Uint32 len, int volume); + +/** + * This works like SDL_MixAudio(), but you specify the audio format instead of + * using the format of audio device 1. Thus it can be used when no audio + * device is open at all. + */ +extern DECLSPEC void SDLCALL SDL_MixAudioFormat(Uint8 * dst, + const Uint8 * src, + SDL_AudioFormat format, + Uint32 len, int volume); + +/** + * \name Audio lock functions + * + * The lock manipulated by these functions protects the callback function. + * During a SDL_LockAudio()/SDL_UnlockAudio() pair, you can be guaranteed that + * the callback function is not running. Do not call these from the callback + * function or you will cause deadlock. + */ +/*@{*/ +extern DECLSPEC void SDLCALL SDL_LockAudio(void); +extern DECLSPEC void SDLCALL SDL_LockAudioDevice(SDL_AudioDeviceID dev); +extern DECLSPEC void SDLCALL SDL_UnlockAudio(void); +extern DECLSPEC void SDLCALL SDL_UnlockAudioDevice(SDL_AudioDeviceID dev); +/*@}*//*Audio lock functions*/ + +/** + * This function shuts down audio processing and closes the audio device. + */ +extern DECLSPEC void SDLCALL SDL_CloseAudio(void); +extern DECLSPEC void SDLCALL SDL_CloseAudioDevice(SDL_AudioDeviceID dev); + +/** + * \return 1 if audio device is still functioning, zero if not, -1 on error. + */ +extern DECLSPEC int SDLCALL SDL_AudioDeviceConnected(SDL_AudioDeviceID dev); + + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif +#include "close_code.h" + +#endif /* _SDL_audio_h */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_blendmode.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_blendmode.h new file mode 100644 index 0000000..11d91bb --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_blendmode.h @@ -0,0 +1,60 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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. +*/ + +/** + * \file SDL_blendmode.h + * + * Header file declaring the SDL_BlendMode enumeration + */ + +#ifndef _SDL_blendmode_h +#define _SDL_blendmode_h + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +/** + * \brief The blend mode used in SDL_RenderCopy() and drawing operations. + */ +typedef enum +{ + SDL_BLENDMODE_NONE = 0x00000000, /**< No blending */ + SDL_BLENDMODE_BLEND = 0x00000001, /**< dst = (src * A) + (dst * (1-A)) */ + SDL_BLENDMODE_ADD = 0x00000002, /**< dst = (src * A) + dst */ + SDL_BLENDMODE_MOD = 0x00000004 /**< dst = src * dst */ +} SDL_BlendMode; + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif +#include "close_code.h" + +#endif /* _SDL_video_h */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_clipboard.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_clipboard.h new file mode 100644 index 0000000..fa2ee08 --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_clipboard.h @@ -0,0 +1,75 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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. +*/ + +/** + * \file SDL_clipboard.h + * + * Include file for SDL clipboard handling + */ + +#ifndef _SDL_clipboard_h +#define _SDL_clipboard_h + +#include "SDL_stdinc.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +/* Function prototypes */ + +/** + * \brief Put UTF-8 text into the clipboard + * + * \sa SDL_GetClipboardText() + */ +extern DECLSPEC int SDLCALL SDL_SetClipboardText(const char *text); + +/** + * \brief Get UTF-8 text from the clipboard, which must be freed with SDL_free() + * + * \sa SDL_SetClipboardText() + */ +extern DECLSPEC char * SDLCALL SDL_GetClipboardText(void); + +/** + * \brief Returns whether the clipboard has text + * + * \sa SDL_GetClipboardText() + */ +extern DECLSPEC SDL_bool SDLCALL SDL_HasClipboardText(void); + + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif +#include "close_code.h" + +#endif /* _SDL_clipboard_h */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_compat.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_compat.h new file mode 100644 index 0000000..c71022d --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_compat.h @@ -0,0 +1,365 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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. +*/ + + /** + * \defgroup Compatibility SDL 1.2 Compatibility API + */ +/*@{*/ + +/** + * \file SDL_compat.h + * + * This file contains functions for backwards compatibility with SDL 1.2. + */ + +/** + * \def SDL_NO_COMPAT + * + * #define SDL_NO_COMPAT to prevent SDL_compat.h from being included. + * SDL_NO_COMPAT is intended to make it easier to covert SDL 1.2 code to + * SDL 1.3/2.0. + */ + + /*@}*/ + +#ifdef SDL_NO_COMPAT +#define _SDL_compat_h +#endif + +#ifndef _SDL_compat_h +#define _SDL_compat_h + +#include "SDL_video.h" +#include "SDL_version.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +/** + * \addtogroup Compatibility + */ +/*@{*/ + +/* Platform */ +#ifdef __WIN32__ +#undef __WIN32__ +#define __WIN32__ 1 +#endif + +/** + * \name Surface flags + */ +/*@{*/ +#define SDL_SWSURFACE 0x00000000 /**< \note Not used */ +#define SDL_SRCALPHA 0x00010000 +#define SDL_SRCCOLORKEY 0x00020000 +#define SDL_ANYFORMAT 0x00100000 +#define SDL_HWPALETTE 0x00200000 +#define SDL_DOUBLEBUF 0x00400000 +#define SDL_FULLSCREEN 0x00800000 +#define SDL_RESIZABLE 0x01000000 +#define SDL_NOFRAME 0x02000000 +#define SDL_OPENGL 0x04000000 +#define SDL_HWSURFACE 0x08000001 /**< \note Not used */ +#define SDL_ASYNCBLIT 0x08000000 /**< \note Not used */ +#define SDL_RLEACCELOK 0x08000000 /**< \note Not used */ +#define SDL_HWACCEL 0x08000000 /**< \note Not used */ +/*@}*//*Surface flags*/ + +#define SDL_APPMOUSEFOCUS 0x01 +#define SDL_APPINPUTFOCUS 0x02 +#define SDL_APPACTIVE 0x04 + +#define SDL_LOGPAL 0x01 +#define SDL_PHYSPAL 0x02 + +#define SDL_ACTIVEEVENT SDL_EVENT_COMPAT1 +#define SDL_VIDEORESIZE SDL_EVENT_COMPAT2 +#define SDL_VIDEOEXPOSE SDL_EVENT_COMPAT3 +#define SDL_ACTIVEEVENTMASK SDL_ACTIVEEVENT, SDL_ACTIVEEVENT +#define SDL_VIDEORESIZEMASK SDL_VIDEORESIZE, SDL_VIDEORESIZE +#define SDL_VIDEOEXPOSEMASK SDL_VIDEOEXPOSE, SDL_VIDEOEXPOSE +#define SDL_WINDOWEVENTMASK SDL_WINDOWEVENT, SDL_WINDOWEVENT +#define SDL_KEYDOWNMASK SDL_KEYDOWN, SDL_KEYDOWN +#define SDL_KEYUPMASK SDL_KEYUP, SDL_KEYUP +#define SDL_KEYEVENTMASK SDL_KEYDOWN, SDL_KEYUP +#define SDL_TEXTEDITINGMASK SDL_TEXTEDITING, SDL_TEXTEDITING +#define SDL_TEXTINPUTMASK SDL_TEXTINPUT, SDL_TEXTINPUT +#define SDL_MOUSEMOTIONMASK SDL_MOUSEMOTION, SDL_MOUSEMOTION +#define SDL_MOUSEBUTTONDOWNMASK SDL_MOUSEBUTTONDOWN, SDL_MOUSEBUTTONDOWN +#define SDL_MOUSEBUTTONUPMASK SDL_MOUSEBUTTONUP, SDL_MOUSEBUTTONUP +#define SDL_MOUSEWHEELMASK SDL_MOUSEWHEEL, SDL_MOUSEWHEEL +#define SDL_MOUSEEVENTMASK SDL_MOUSEMOTION, SDL_MOUSEBUTTONUP +#define SDL_JOYAXISMOTIONMASK SDL_JOYAXISMOTION, SDL_JOYAXISMOTION +#define SDL_JOYBALLMOTIONMASK SDL_JOYBALLMOTION, SDL_JOYBALLMOTION +#define SDL_JOYHATMOTIONMASK SDL_JOYHATMOTION, SDL_JOYHATMOTION +#define SDL_JOYBUTTONDOWNMASK SDL_JOYBUTTONDOWN, SDL_JOYBUTTONDOWN +#define SDL_JOYBUTTONUPMASK SDL_JOYBUTTONUP, SDL_JOYBUTTONUP +#define SDL_JOYEVENTMASK SDL_JOYAXISMOTION, SDL_JOYBUTTONUP +#define SDL_QUITMASK SDL_QUIT, SDL_QUIT +#define SDL_SYSWMEVENTMASK SDL_SYSWMEVENT, SDL_SYSWMEVENT +#define SDL_PROXIMITYINMASK SDL_PROXIMITYIN, SDL_PROXIMITYIN +#define SDL_PROXIMITYOUTMASK SDL_PROXIMITYOUT, SDL_PROXIMITYOUT +#define SDL_ALLEVENTS SDL_FIRSTEVENT, SDL_LASTEVENT + +#define SDL_BUTTON_WHEELUP 4 +#define SDL_BUTTON_WHEELDOWN 5 + +#define SDL_DEFAULT_REPEAT_DELAY 500 +#define SDL_DEFAULT_REPEAT_INTERVAL 30 + +typedef struct SDL_VideoInfo +{ + Uint32 hw_available:1; + Uint32 wm_available:1; + Uint32 UnusedBits1:6; + Uint32 UnusedBits2:1; + Uint32 blit_hw:1; + Uint32 blit_hw_CC:1; + Uint32 blit_hw_A:1; + Uint32 blit_sw:1; + Uint32 blit_sw_CC:1; + Uint32 blit_sw_A:1; + Uint32 blit_fill:1; + Uint32 UnusedBits3:16; + Uint32 video_mem; + + SDL_PixelFormat *vfmt; + + int current_w; + int current_h; +} SDL_VideoInfo; + +/** + * \name Overlay formats + * + * The most common video overlay formats. + * + * For an explanation of these pixel formats, see: + * http://www.webartz.com/fourcc/indexyuv.htm + * + * For information on the relationship between color spaces, see: + * http://www.neuro.sfc.keio.ac.jp/~aly/polygon/info/color-space-faq.html + */ +/*@{*/ +#define SDL_YV12_OVERLAY 0x32315659 /**< Planar mode: Y + V + U (3 planes) */ +#define SDL_IYUV_OVERLAY 0x56555949 /**< Planar mode: Y + U + V (3 planes) */ +#define SDL_YUY2_OVERLAY 0x32595559 /**< Packed mode: Y0+U0+Y1+V0 (1 plane) */ +#define SDL_UYVY_OVERLAY 0x59565955 /**< Packed mode: U0+Y0+V0+Y1 (1 plane) */ +#define SDL_YVYU_OVERLAY 0x55595659 /**< Packed mode: Y0+V0+Y1+U0 (1 plane) */ +/*@}*//*Overlay formats*/ + +/** + * The YUV hardware video overlay. + */ +typedef struct SDL_Overlay +{ + Uint32 format; /**< Read-only */ + int w, h; /**< Read-only */ + int planes; /**< Read-only */ + Uint16 *pitches; /**< Read-only */ + Uint8 **pixels; /**< Read-write */ + + /** + * \name Hardware-specific surface info + */ + /*@{*/ + struct private_yuvhwfuncs *hwfuncs; + struct private_yuvhwdata *hwdata; + /*@}*//*Hardware-specific surface info*/ + + /** + * \name Special flags + */ + /*@{*/ + Uint32 hw_overlay:1; /**< Flag: This overlay hardware accelerated? */ + Uint32 UnusedBits:31; + /*@}*//*Special flags*/ +} SDL_Overlay; + +typedef enum +{ + SDL_GRAB_QUERY = -1, + SDL_GRAB_OFF = 0, + SDL_GRAB_ON = 1 +} SDL_GrabMode; + +struct SDL_SysWMinfo; + +/** + * \name Obsolete or renamed key codes + */ +/*@{*/ + +#define SDL_keysym SDL_Keysym +#define SDL_KeySym SDL_Keysym +#define SDL_scancode SDL_Scancode +#define SDL_ScanCode SDL_Scancode +#define SDLKey SDL_Keycode +#define SDLMod SDL_Keymod + +/** + * \name Renamed keys + * + * These key constants were renamed for clarity or consistency. + */ +/*@{*/ +#define SDLK_KP0 SDLK_KP_0 +#define SDLK_KP1 SDLK_KP_1 +#define SDLK_KP2 SDLK_KP_2 +#define SDLK_KP3 SDLK_KP_3 +#define SDLK_KP4 SDLK_KP_4 +#define SDLK_KP5 SDLK_KP_5 +#define SDLK_KP6 SDLK_KP_6 +#define SDLK_KP7 SDLK_KP_7 +#define SDLK_KP8 SDLK_KP_8 +#define SDLK_KP9 SDLK_KP_9 +#define SDLK_NUMLOCK SDLK_NUMLOCKCLEAR +#define SDLK_SCROLLOCK SDLK_SCROLLLOCK +#define SDLK_PRINT SDLK_PRINTSCREEN +#define SDLK_LMETA SDLK_LGUI +#define SDLK_RMETA SDLK_RGUI +/*@}*//*Renamed keys*/ + +/** + * \name META modifier + * + * The META modifier is equivalent to the GUI modifier from the USB standard. + */ +/*@{*/ +#define KMOD_LMETA KMOD_LGUI +#define KMOD_RMETA KMOD_RGUI +#define KMOD_META KMOD_GUI +/*@}*//*META modifier*/ + +/** + * \name Not in USB + * + * These keys don't appear in the USB specification (or at least not under + * those names). I'm unsure if the following assignments make sense or if these + * codes should be defined as actual additional SDLK_ constants. + */ +/*@{*/ +#define SDLK_LSUPER SDLK_LMETA +#define SDLK_RSUPER SDLK_RMETA +#define SDLK_COMPOSE SDLK_APPLICATION +#define SDLK_BREAK SDLK_STOP +#define SDLK_EURO SDLK_2 +/*@}*//*Not in USB*/ + +/*@}*//*Obsolete or renamed key codes*/ + +#define SDL_SetModuleHandle(x) +#define SDL_AllocSurface SDL_CreateRGBSurface + +extern DECLSPEC const SDL_version *SDLCALL SDL_Linked_Version(void); +extern DECLSPEC const char *SDLCALL SDL_AudioDriverName(char *namebuf, int maxlen); +extern DECLSPEC const char *SDLCALL SDL_VideoDriverName(char *namebuf, int maxlen); +extern DECLSPEC const SDL_VideoInfo *SDLCALL SDL_GetVideoInfo(void); +extern DECLSPEC int SDLCALL SDL_VideoModeOK(int width, + int height, + int bpp, Uint32 flags); +extern DECLSPEC SDL_Rect **SDLCALL SDL_ListModes(const SDL_PixelFormat * + format, Uint32 flags); +extern DECLSPEC SDL_Surface *SDLCALL SDL_SetVideoMode(int width, int height, + int bpp, Uint32 flags); +extern DECLSPEC SDL_Surface *SDLCALL SDL_GetVideoSurface(void); +extern DECLSPEC void SDLCALL SDL_UpdateRects(SDL_Surface * screen, + int numrects, SDL_Rect * rects); +extern DECLSPEC void SDLCALL SDL_UpdateRect(SDL_Surface * screen, + Sint32 x, + Sint32 y, Uint32 w, Uint32 h); +extern DECLSPEC int SDLCALL SDL_Flip(SDL_Surface * screen); +extern DECLSPEC int SDLCALL SDL_SetAlpha(SDL_Surface * surface, + Uint32 flag, Uint8 alpha); +extern DECLSPEC SDL_Surface *SDLCALL SDL_DisplayFormat(SDL_Surface * surface); +extern DECLSPEC SDL_Surface *SDLCALL SDL_DisplayFormatAlpha(SDL_Surface * + surface); +extern DECLSPEC void SDLCALL SDL_WM_SetCaption(const char *title, + const char *icon); +extern DECLSPEC void SDLCALL SDL_WM_GetCaption(const char **title, + const char **icon); +extern DECLSPEC void SDLCALL SDL_WM_SetIcon(SDL_Surface * icon, Uint8 * mask); +extern DECLSPEC int SDLCALL SDL_WM_IconifyWindow(void); +extern DECLSPEC int SDLCALL SDL_WM_ToggleFullScreen(SDL_Surface * surface); +extern DECLSPEC SDL_GrabMode SDLCALL SDL_WM_GrabInput(SDL_GrabMode mode); +extern DECLSPEC int SDLCALL SDL_SetPalette(SDL_Surface * surface, + int flags, + const SDL_Color * colors, + int firstcolor, int ncolors); +extern DECLSPEC int SDLCALL SDL_SetColors(SDL_Surface * surface, + const SDL_Color * colors, + int firstcolor, int ncolors); +extern DECLSPEC int SDLCALL SDL_GetWMInfo(struct SDL_SysWMinfo *info); +extern DECLSPEC Uint8 SDLCALL SDL_GetAppState(void); +extern DECLSPEC void SDLCALL SDL_WarpMouse(Uint16 x, Uint16 y); +extern DECLSPEC SDL_Overlay *SDLCALL SDL_CreateYUVOverlay(int width, + int height, + Uint32 format, + SDL_Surface * + display); +extern DECLSPEC int SDLCALL SDL_LockYUVOverlay(SDL_Overlay * overlay); +extern DECLSPEC void SDLCALL SDL_UnlockYUVOverlay(SDL_Overlay * overlay); +extern DECLSPEC int SDLCALL SDL_DisplayYUVOverlay(SDL_Overlay * overlay, + SDL_Rect * dstrect); +extern DECLSPEC void SDLCALL SDL_FreeYUVOverlay(SDL_Overlay * overlay); +extern DECLSPEC void SDLCALL SDL_GL_SwapBuffers(void); +extern DECLSPEC int SDLCALL SDL_SetGamma(float red, float green, float blue); +extern DECLSPEC int SDLCALL SDL_SetGammaRamp(const Uint16 * red, + const Uint16 * green, + const Uint16 * blue); +extern DECLSPEC int SDLCALL SDL_GetGammaRamp(Uint16 * red, Uint16 * green, + Uint16 * blue); +extern DECLSPEC int SDLCALL SDL_EnableKeyRepeat(int delay, int interval); +extern DECLSPEC void SDLCALL SDL_GetKeyRepeat(int *delay, int *interval); +extern DECLSPEC int SDLCALL SDL_EnableUNICODE(int enable); + +typedef SDL_Window* SDL_WindowID; + +#define SDL_KillThread(X) + +/* The timeslice and timer resolution are no longer relevant */ +#define SDL_TIMESLICE 10 +#define TIMER_RESOLUTION 10 + +typedef Uint32 (SDLCALL * SDL_OldTimerCallback) (Uint32 interval); +extern DECLSPEC int SDLCALL SDL_SetTimer(Uint32 interval, SDL_OldTimerCallback callback); + +extern DECLSPEC int SDLCALL SDL_putenv(const char *variable); + +/*@}*//*Compatibility*/ + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif +#include "close_code.h" + +#endif /* _SDL_compat_h */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_config.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_config.h new file mode 100644 index 0000000..18cecbb --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_config.h @@ -0,0 +1,295 @@ +/* include/SDL_config.h. Generated from SDL_config.h.in by configure. */ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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 _SDL_config_h +#define _SDL_config_h + +/** + * \file SDL_config.h.in + * + * This is a set of defines to configure the SDL features + */ + +/* General platform specific identifiers */ +#include "SDL_platform.h" + +/* Make sure that this isn't included by Visual C++ */ +#ifdef _MSC_VER +#error You should run hg revert SDL_config.h +#endif + +/* C language features */ +/* #undef const */ +/* #undef inline */ +/* #undef volatile */ + +/* C datatypes */ +#define SIZEOF_VOIDP 8 +#define HAVE_GCC_ATOMICS 1 +/* #undef HAVE_GCC_SYNC_LOCK_TEST_AND_SET */ + +/* Comment this if you want to build without any C library requirements */ +#define HAVE_LIBC 1 +#if HAVE_LIBC + +/* Useful headers */ +#define HAVE_ALLOCA_H 1 +#define HAVE_SYS_TYPES_H 1 +#define HAVE_STDIO_H 1 +#define STDC_HEADERS 1 +#define HAVE_STDLIB_H 1 +#define HAVE_STDARG_H 1 +/* #undef HAVE_MALLOC_H */ +#define HAVE_MEMORY_H 1 +#define HAVE_STRING_H 1 +#define HAVE_STRINGS_H 1 +#define HAVE_INTTYPES_H 1 +#define HAVE_STDINT_H 1 +#define HAVE_CTYPE_H 1 +#define HAVE_MATH_H 1 +#define HAVE_ICONV_H 1 +#define HAVE_SIGNAL_H 1 +/* #undef HAVE_ALTIVEC_H */ + +/* C library functions */ +#define HAVE_MALLOC 1 +#define HAVE_CALLOC 1 +#define HAVE_REALLOC 1 +#define HAVE_FREE 1 +#define HAVE_ALLOCA 1 +#ifndef __WIN32__ /* Don't use C runtime versions of these on Windows */ +#define HAVE_GETENV 1 +#define HAVE_SETENV 1 +#define HAVE_PUTENV 1 +#define HAVE_UNSETENV 1 +#endif +#define HAVE_QSORT 1 +#define HAVE_ABS 1 +#define HAVE_BCOPY 1 +#define HAVE_MEMSET 1 +#define HAVE_MEMCPY 1 +#define HAVE_MEMMOVE 1 +#define HAVE_MEMCMP 1 +#define HAVE_STRLEN 1 +#define HAVE_STRLCPY 1 +#define HAVE_STRLCAT 1 +#define HAVE_STRDUP 1 +/* #undef HAVE__STRREV */ +/* #undef HAVE__STRUPR */ +/* #undef HAVE__STRLWR */ +/* #undef HAVE_INDEX */ +/* #undef HAVE_RINDEX */ +#define HAVE_STRCHR 1 +#define HAVE_STRRCHR 1 +#define HAVE_STRSTR 1 +/* #undef HAVE_ITOA */ +/* #undef HAVE__LTOA */ +/* #undef HAVE__UITOA */ +/* #undef HAVE__ULTOA */ +#define HAVE_STRTOL 1 +#define HAVE_STRTOUL 1 +/* #undef HAVE__I64TOA */ +/* #undef HAVE__UI64TOA */ +#define HAVE_STRTOLL 1 +#define HAVE_STRTOULL 1 +#define HAVE_STRTOD 1 +#define HAVE_ATOI 1 +#define HAVE_ATOF 1 +#define HAVE_STRCMP 1 +#define HAVE_STRNCMP 1 +/* #undef HAVE__STRICMP */ +#define HAVE_STRCASECMP 1 +/* #undef HAVE__STRNICMP */ +#define HAVE_STRNCASECMP 1 +#define HAVE_SSCANF 1 +#define HAVE_SNPRINTF 1 +#define HAVE_VSNPRINTF 1 +#define HAVE_M_PI +#define HAVE_ATAN 1 +#define HAVE_ATAN2 1 +#define HAVE_CEIL 1 +#define HAVE_COPYSIGN 1 +#define HAVE_COS 1 +#define HAVE_COSF 1 +#define HAVE_FABS 1 +#define HAVE_FLOOR 1 +#define HAVE_LOG 1 +#define HAVE_POW 1 +#define HAVE_SCALBN 1 +#define HAVE_SIN 1 +#define HAVE_SINF 1 +#define HAVE_SQRT 1 +#define HAVE_SIGACTION 1 +#define HAVE_SETJMP 1 +#define HAVE_NANOSLEEP 1 +#define HAVE_SYSCONF 1 +#define HAVE_SYSCTLBYNAME 1 +/* #undef HAVE_CLOCK_GETTIME */ +/* #undef HAVE_GETPAGESIZE */ +#define HAVE_MPROTECT 1 +#define HAVE_ICONV 1 + +#else +/* We may need some replacement for stdarg.h here */ +#include +#endif /* HAVE_LIBC */ + +/* SDL internal assertion support */ +/* #undef SDL_DEFAULT_ASSERT_LEVEL */ + +/* Allow disabling of core subsystems */ +/* #undef SDL_ATOMIC_DISABLED */ +/* #undef SDL_AUDIO_DISABLED */ +/* #undef SDL_CPUINFO_DISABLED */ +/* #undef SDL_EVENTS_DISABLED */ +/* #undef SDL_FILE_DISABLED */ +/* #undef SDL_JOYSTICK_DISABLED */ +/* #undef SDL_HAPTIC_DISABLED */ +/* #undef SDL_LOADSO_DISABLED */ +/* #undef SDL_RENDER_DISABLED */ +/* #undef SDL_THREADS_DISABLED */ +/* #undef SDL_TIMERS_DISABLED */ +/* #undef SDL_VIDEO_DISABLED */ +/* #undef SDL_POWER_DISABLED */ + +/* Enable various audio drivers */ +/* #undef SDL_AUDIO_DRIVER_ALSA */ +/* #undef SDL_AUDIO_DRIVER_ALSA_DYNAMIC */ +/* #undef SDL_AUDIO_DRIVER_ARTS */ +/* #undef SDL_AUDIO_DRIVER_ARTS_DYNAMIC */ +/* #undef SDL_AUDIO_DRIVER_PULSEAUDIO */ +/* #undef SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC */ +/* #undef SDL_AUDIO_DRIVER_BEOSAUDIO */ +/* #undef SDL_AUDIO_DRIVER_BSD */ +#define SDL_AUDIO_DRIVER_COREAUDIO 1 +#define SDL_AUDIO_DRIVER_DISK 1 +#define SDL_AUDIO_DRIVER_DUMMY 1 +/* #undef SDL_AUDIO_DRIVER_DSOUND */ +/* #undef SDL_AUDIO_DRIVER_ESD */ +/* #undef SDL_AUDIO_DRIVER_ESD_DYNAMIC */ +/* #undef SDL_AUDIO_DRIVER_NAS */ +/* #undef SDL_AUDIO_DRIVER_NAS_DYNAMIC */ +/* #undef SDL_AUDIO_DRIVER_NDS */ +/* #undef SDL_AUDIO_DRIVER_OSS */ +/* #undef SDL_AUDIO_DRIVER_OSS_SOUNDCARD_H */ +/* #undef SDL_AUDIO_DRIVER_PAUDIO */ +/* #undef SDL_AUDIO_DRIVER_QSA */ +/* #undef SDL_AUDIO_DRIVER_SUNAUDIO */ +/* #undef SDL_AUDIO_DRIVER_WINWAVEOUT */ +/* #undef SDL_AUDIO_DRIVER_FUSIONSOUND */ +/* #undef SDL_AUDIO_DRIVER_FUSIONSOUND_DYNAMIC */ + +/* Enable various input drivers */ +/* #undef SDL_INPUT_LINUXEV */ +/* #undef SDL_INPUT_TSLIB */ +/* #undef SDL_JOYSTICK_BEOS */ +/* #undef SDL_JOYSTICK_DINPUT */ +/* #undef SDL_JOYSTICK_DUMMY */ +#define SDL_JOYSTICK_IOKIT 1 +/* #undef SDL_JOYSTICK_LINUX */ +/* #undef SDL_JOYSTICK_NDS */ +/* #undef SDL_JOYSTICK_WINMM */ +/* #undef SDL_JOYSTICK_USBHID */ +/* #undef SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H */ +/* #undef SDL_HAPTIC_DUMMY */ +/* #undef SDL_HAPTIC_LINUX */ +#define SDL_HAPTIC_IOKIT 1 +/* #undef SDL_HAPTIC_DINPUT */ + +/* Enable various shared object loading systems */ +/* #undef SDL_LOADSO_BEOS */ +/* #undef SDL_LOADSO_DLCOMPAT */ +#define SDL_LOADSO_DLOPEN 1 +/* #undef SDL_LOADSO_DUMMY */ +/* #undef SDL_LOADSO_LDG */ +/* #undef SDL_LOADSO_WINDOWS */ + +/* Enable various threading systems */ +/* #undef SDL_THREAD_BEOS */ +/* #undef SDL_THREAD_NDS */ +#define SDL_THREAD_PTHREAD 1 +#define SDL_THREAD_PTHREAD_RECURSIVE_MUTEX 1 +/* #undef SDL_THREAD_PTHREAD_RECURSIVE_MUTEX_NP */ +/* #undef SDL_THREAD_SPROC */ +/* #undef SDL_THREAD_WINDOWS */ + +/* Enable various timer systems */ +/* #undef SDL_TIMER_BEOS */ +/* #undef SDL_TIMER_DUMMY */ +/* #undef SDL_TIMER_NDS */ +#define SDL_TIMER_UNIX 1 +/* #undef SDL_TIMER_WINDOWS */ +/* #undef SDL_TIMER_WINCE */ + +/* Enable various video drivers */ +/* #undef SDL_VIDEO_DRIVER_BWINDOW */ +#define SDL_VIDEO_DRIVER_COCOA 1 +/* #undef SDL_VIDEO_DRIVER_DIRECTFB */ +/* #undef SDL_VIDEO_DRIVER_DIRECTFB_DYNAMIC */ +#define SDL_VIDEO_DRIVER_DUMMY 1 +/* #undef SDL_VIDEO_DRIVER_NDS */ +/* #undef SDL_VIDEO_DRIVER_WINDOWS */ +#define SDL_VIDEO_DRIVER_X11 1 +#define SDL_VIDEO_DRIVER_X11_DYNAMIC "/usr/X11R6/lib/libX11.6.dylib" +#define SDL_VIDEO_DRIVER_X11_DYNAMIC_XEXT "/usr/X11R6/lib/libXext.6.dylib" +#define SDL_VIDEO_DRIVER_X11_DYNAMIC_XCURSOR "/usr/X11R6/lib/libXcursor.1.dylib" +#define SDL_VIDEO_DRIVER_X11_DYNAMIC_XINERAMA "/usr/X11R6/lib/libXinerama.1.dylib" +#define SDL_VIDEO_DRIVER_X11_DYNAMIC_XINPUT "/usr/X11R6/lib/libXi.6.dylib" +#define SDL_VIDEO_DRIVER_X11_DYNAMIC_XRANDR "/usr/X11R6/lib/libXrandr.2.dylib" +#define SDL_VIDEO_DRIVER_X11_DYNAMIC_XSS "/usr/X11R6/lib/libXss.1.dylib" +#define SDL_VIDEO_DRIVER_X11_DYNAMIC_XVIDMODE "/usr/X11R6/lib/libXxf86vm.1.dylib" +#define SDL_VIDEO_DRIVER_X11_XCURSOR 1 +#define SDL_VIDEO_DRIVER_X11_XINERAMA 1 +#define SDL_VIDEO_DRIVER_X11_XINPUT 1 +#define SDL_VIDEO_DRIVER_X11_XRANDR 1 +#define SDL_VIDEO_DRIVER_X11_XSCRNSAVER 1 +#define SDL_VIDEO_DRIVER_X11_XSHAPE 1 +#define SDL_VIDEO_DRIVER_X11_XVIDMODE 1 + +/* #undef SDL_VIDEO_RENDER_D3D */ +#define SDL_VIDEO_RENDER_OGL 1 +/* #undef SDL_VIDEO_RENDER_OGL_ES */ +/* #undef SDL_VIDEO_RENDER_DIRECTFB */ + +/* Enable OpenGL support */ +#define SDL_VIDEO_OPENGL 1 +/* #undef SDL_VIDEO_OPENGL_ES */ +/* #undef SDL_VIDEO_OPENGL_BGL */ +#define SDL_VIDEO_OPENGL_CGL 1 +#define SDL_VIDEO_OPENGL_GLX 1 +/* #undef SDL_VIDEO_OPENGL_WGL */ +/* #undef SDL_VIDEO_OPENGL_OSMESA */ +/* #undef SDL_VIDEO_OPENGL_OSMESA_DYNAMIC */ + +/* Enable system power support */ +/* #undef SDL_POWER_LINUX */ +/* #undef SDL_POWER_WINDOWS */ +#define SDL_POWER_MACOSX 1 +/* #undef SDL_POWER_BEOS */ +/* #undef SDL_POWER_NINTENDODS */ +/* #undef SDL_POWER_HARDWIRED */ + +/* Enable assembly routines */ +#define SDL_ASSEMBLY_ROUTINES 1 +/* #undef SDL_ALTIVEC_BLITTERS */ + +#endif /* _SDL_config_h */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_config_macosx.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_config_macosx.h new file mode 100644 index 0000000..57e2f74 --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_config_macosx.h @@ -0,0 +1,172 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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 _SDL_config_macosx_h +#define _SDL_config_macosx_h + +#include "SDL_platform.h" + +/* This gets us MAC_OS_X_VERSION_MIN_REQUIRED... */ +#include + +/* This is a set of defines to configure the SDL features */ + +#ifdef __LP64__ + #define SIZEOF_VOIDP 8 +#else + #define SIZEOF_VOIDP 4 +#endif + +/* Useful headers */ +/* If we specified an SDK or have a post-PowerPC chip, then alloca.h exists. */ +#if ( (MAC_OS_X_VERSION_MIN_REQUIRED >= 1030) || (!defined(__POWERPC__)) ) +#define HAVE_ALLOCA_H 1 +#endif +#define HAVE_SYS_TYPES_H 1 +#define HAVE_STDIO_H 1 +#define STDC_HEADERS 1 +#define HAVE_STRING_H 1 +#define HAVE_INTTYPES_H 1 +#define HAVE_STDINT_H 1 +#define HAVE_CTYPE_H 1 +#define HAVE_MATH_H 1 +#define HAVE_SIGNAL_H 1 + +/* C library functions */ +#define HAVE_MALLOC 1 +#define HAVE_CALLOC 1 +#define HAVE_REALLOC 1 +#define HAVE_FREE 1 +#define HAVE_ALLOCA 1 +#define HAVE_GETENV 1 +#define HAVE_SETENV 1 +#define HAVE_PUTENV 1 +#define HAVE_UNSETENV 1 +#define HAVE_QSORT 1 +#define HAVE_ABS 1 +#define HAVE_BCOPY 1 +#define HAVE_MEMSET 1 +#define HAVE_MEMCPY 1 +#define HAVE_MEMMOVE 1 +#define HAVE_MEMCMP 1 +#define HAVE_STRLEN 1 +#define HAVE_STRLCPY 1 +#define HAVE_STRLCAT 1 +#define HAVE_STRDUP 1 +#define HAVE_STRCHR 1 +#define HAVE_STRRCHR 1 +#define HAVE_STRSTR 1 +#define HAVE_STRTOL 1 +#define HAVE_STRTOUL 1 +#define HAVE_STRTOLL 1 +#define HAVE_STRTOULL 1 +#define HAVE_STRTOD 1 +#define HAVE_ATOI 1 +#define HAVE_ATOF 1 +#define HAVE_STRCMP 1 +#define HAVE_STRNCMP 1 +#define HAVE_STRCASECMP 1 +#define HAVE_STRNCASECMP 1 +#define HAVE_SSCANF 1 +#define HAVE_SNPRINTF 1 +#define HAVE_VSNPRINTF 1 +#define HAVE_CEIL 1 +#define HAVE_COPYSIGN 1 +#define HAVE_COS 1 +#define HAVE_COSF 1 +#define HAVE_FABS 1 +#define HAVE_FLOOR 1 +#define HAVE_LOG 1 +#define HAVE_POW 1 +#define HAVE_SCALBN 1 +#define HAVE_SIN 1 +#define HAVE_SINF 1 +#define HAVE_SQRT 1 +#define HAVE_SIGACTION 1 +#define HAVE_SETJMP 1 +#define HAVE_NANOSLEEP 1 +#define HAVE_SYSCONF 1 +#define HAVE_SYSCTLBYNAME 1 +#define HAVE_ATAN 1 +#define HAVE_ATAN2 1 + +/* Enable various audio drivers */ +#define SDL_AUDIO_DRIVER_COREAUDIO 1 +#define SDL_AUDIO_DRIVER_DISK 1 +#define SDL_AUDIO_DRIVER_DUMMY 1 + +/* Enable various input drivers */ +#define SDL_JOYSTICK_IOKIT 1 +#define SDL_HAPTIC_IOKIT 1 + +/* Enable various shared object loading systems */ +#define SDL_LOADSO_DLOPEN 1 + +/* Enable various threading systems */ +#define SDL_THREAD_PTHREAD 1 +#define SDL_THREAD_PTHREAD_RECURSIVE_MUTEX 1 + +/* Enable various timer systems */ +#define SDL_TIMER_UNIX 1 + +/* Enable various video drivers */ +#define SDL_VIDEO_DRIVER_COCOA 1 +#define SDL_VIDEO_DRIVER_DUMMY 1 +#define SDL_VIDEO_DRIVER_X11 1 +#define SDL_VIDEO_DRIVER_X11_DYNAMIC "/usr/X11R6/lib/libX11.6.dylib" +#define SDL_VIDEO_DRIVER_X11_DYNAMIC_XEXT "/usr/X11R6/lib/libXext.6.dylib" +#define SDL_VIDEO_DRIVER_X11_DYNAMIC_XINERAMA "/usr/X11R6/lib/libXinerama.1.dylib" +#define SDL_VIDEO_DRIVER_X11_DYNAMIC_XINPUT "/usr/X11R6/lib/libXi.6.dylib" +#define SDL_VIDEO_DRIVER_X11_DYNAMIC_XRANDR "/usr/X11R6/lib/libXrandr.2.dylib" +#define SDL_VIDEO_DRIVER_X11_DYNAMIC_XSS "/usr/X11R6/lib/libXss.1.dylib" +#define SDL_VIDEO_DRIVER_X11_DYNAMIC_XVIDMODE "/usr/X11R6/lib/libXxf86vm.1.dylib" +#define SDL_VIDEO_DRIVER_X11_XINERAMA 1 +#define SDL_VIDEO_DRIVER_X11_XINPUT 1 +#define SDL_VIDEO_DRIVER_X11_XRANDR 1 +#define SDL_VIDEO_DRIVER_X11_XSCRNSAVER 1 +#define SDL_VIDEO_DRIVER_X11_XSHAPE 1 +#define SDL_VIDEO_DRIVER_X11_XVIDMODE 1 + +#ifndef SDL_VIDEO_RENDER_OGL +#define SDL_VIDEO_RENDER_OGL 1 +#endif + +/* Enable OpenGL support */ +#ifndef SDL_VIDEO_OPENGL +#define SDL_VIDEO_OPENGL 1 +#endif +#ifndef SDL_VIDEO_OPENGL_CGL +#define SDL_VIDEO_OPENGL_CGL 1 +#endif +#ifndef SDL_VIDEO_OPENGL_GLX +#define SDL_VIDEO_OPENGL_GLX 1 +#endif + +/* Enable system power support */ +#define SDL_POWER_MACOSX 1 + +/* Enable assembly routines */ +#define SDL_ASSEMBLY_ROUTINES 1 +#ifdef __ppc__ +#define SDL_ALTIVEC_BLITTERS 1 +#endif + +#endif /* _SDL_config_macosx_h */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_copying.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_copying.h new file mode 100644 index 0000000..91049cd --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_copying.h @@ -0,0 +1,20 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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. +*/ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_cpuinfo.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_cpuinfo.h new file mode 100644 index 0000000..7fb39ed --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_cpuinfo.h @@ -0,0 +1,150 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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. +*/ + +/** + * \file SDL_cpuinfo.h + * + * CPU feature detection for SDL. + */ + +#ifndef _SDL_cpuinfo_h +#define _SDL_cpuinfo_h + +#include "SDL_stdinc.h" + +/* Need to do this here because intrin.h has C++ code in it */ +/* Visual Studio 2005 has a bug where intrin.h conflicts with winnt.h */ +#if defined(_MSC_VER) && (_MSC_VER >= 1500) && !defined(_WIN32_WCE) +#include +#ifndef _WIN64 +#define __MMX__ +#define __3dNOW__ +#endif +#define __SSE__ +#define __SSE2__ +#elif defined(__MINGW64_VERSION_MAJOR) +#include +#else +#ifdef __ALTIVEC__ +#if HAVE_ALTIVEC_H && !defined(__APPLE_ALTIVEC__) +#include +#undef pixel +#endif +#endif +#ifdef __MMX__ +#include +#endif +#ifdef __3dNOW__ +#include +#endif +#ifdef __SSE__ +#include +#endif +#ifdef __SSE2__ +#include +#endif +#endif + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +/* This is a guess for the cacheline size used for padding. + * Most x86 processors have a 64 byte cache line. + * The 64-bit PowerPC processors have a 128 byte cache line. + * We'll use the larger value to be generally safe. + */ +#define SDL_CACHELINE_SIZE 128 + +/** + * This function returns the number of CPU cores available. + */ +extern DECLSPEC int SDLCALL SDL_GetCPUCount(void); + +/** + * This function returns the L1 cache line size of the CPU + * + * This is useful for determining multi-threaded structure padding + * or SIMD prefetch sizes. + */ +extern DECLSPEC int SDLCALL SDL_GetCPUCacheLineSize(void); + +/** + * This function returns true if the CPU has the RDTSC instruction. + */ +extern DECLSPEC SDL_bool SDLCALL SDL_HasRDTSC(void); + +/** + * This function returns true if the CPU has AltiVec features. + */ +extern DECLSPEC SDL_bool SDLCALL SDL_HasAltiVec(void); + +/** + * This function returns true if the CPU has MMX features. + */ +extern DECLSPEC SDL_bool SDLCALL SDL_HasMMX(void); + +/** + * This function returns true if the CPU has 3DNow! features. + */ +extern DECLSPEC SDL_bool SDLCALL SDL_Has3DNow(void); + +/** + * This function returns true if the CPU has SSE features. + */ +extern DECLSPEC SDL_bool SDLCALL SDL_HasSSE(void); + +/** + * This function returns true if the CPU has SSE2 features. + */ +extern DECLSPEC SDL_bool SDLCALL SDL_HasSSE2(void); + +/** + * This function returns true if the CPU has SSE3 features. + */ +extern DECLSPEC SDL_bool SDLCALL SDL_HasSSE3(void); + +/** + * This function returns true if the CPU has SSE4.1 features. + */ +extern DECLSPEC SDL_bool SDLCALL SDL_HasSSE41(void); + +/** + * This function returns true if the CPU has SSE4.2 features. + */ +extern DECLSPEC SDL_bool SDLCALL SDL_HasSSE42(void); + + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif +#include "close_code.h" + +#endif /* _SDL_cpuinfo_h */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_endian.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_endian.h new file mode 100644 index 0000000..5b23e78 --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_endian.h @@ -0,0 +1,248 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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. +*/ + +/** + * \file SDL_endian.h + * + * Functions for reading and writing endian-specific values + */ + +#ifndef _SDL_endian_h +#define _SDL_endian_h + +#include "SDL_stdinc.h" + +/** + * \name The two types of endianness + */ +/*@{*/ +#define SDL_LIL_ENDIAN 1234 +#define SDL_BIG_ENDIAN 4321 +/*@}*/ + +#ifndef SDL_BYTEORDER /* Not defined in SDL_config.h? */ +#ifdef __linux__ +#include +#define SDL_BYTEORDER __BYTE_ORDER +#else /* __linux __ */ +#if defined(__hppa__) || \ + defined(__m68k__) || defined(mc68000) || defined(_M_M68K) || \ + (defined(__MIPS__) && defined(__MISPEB__)) || \ + defined(__ppc__) || defined(__POWERPC__) || defined(_M_PPC) || \ + defined(__sparc__) +#define SDL_BYTEORDER SDL_BIG_ENDIAN +#else +#define SDL_BYTEORDER SDL_LIL_ENDIAN +#endif +#endif /* __linux __ */ +#endif /* !SDL_BYTEORDER */ + + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +/** + * \file SDL_endian.h + * + * Uses inline functions for compilers that support them, and static + * functions for those that do not. Because these functions become + * static for compilers that do not support inline functions, this + * header should only be included in files that actually use them. + */ +#if defined(__GNUC__) && defined(__i386__) && \ + !(__GNUC__ == 2 && __GNUC_MINOR__ == 95 /* broken gcc version */) +static __inline__ Uint16 +SDL_Swap16(Uint16 x) +{ + __asm__("xchgb %b0,%h0": "=q"(x):"0"(x)); + return x; +} +#elif defined(__GNUC__) && defined(__x86_64__) +static __inline__ Uint16 +SDL_Swap16(Uint16 x) +{ + __asm__("xchgb %b0,%h0": "=Q"(x):"0"(x)); + return x; +} +#elif defined(__GNUC__) && (defined(__powerpc__) || defined(__ppc__)) +static __inline__ Uint16 +SDL_Swap16(Uint16 x) +{ + Uint16 result; + + __asm__("rlwimi %0,%2,8,16,23": "=&r"(result):"0"(x >> 8), "r"(x)); + return result; +} +#elif defined(__GNUC__) && (defined(__M68000__) || defined(__M68020__)) && !defined(__mcoldfire__) +static __inline__ Uint16 +SDL_Swap16(Uint16 x) +{ + __asm__("rorw #8,%0": "=d"(x): "0"(x):"cc"); + return x; +} +#else +static __inline__ Uint16 +SDL_Swap16(Uint16 x) +{ + return SDL_static_cast(Uint16, ((x << 8) | (x >> 8))); +} +#endif + +#if defined(__GNUC__) && defined(__i386__) +static __inline__ Uint32 +SDL_Swap32(Uint32 x) +{ + __asm__("bswap %0": "=r"(x):"0"(x)); + return x; +} +#elif defined(__GNUC__) && defined(__x86_64__) +static __inline__ Uint32 +SDL_Swap32(Uint32 x) +{ + __asm__("bswapl %0": "=r"(x):"0"(x)); + return x; +} +#elif defined(__GNUC__) && (defined(__powerpc__) || defined(__ppc__)) +static __inline__ Uint32 +SDL_Swap32(Uint32 x) +{ + Uint32 result; + + __asm__("rlwimi %0,%2,24,16,23": "=&r"(result):"0"(x >> 24), "r"(x)); + __asm__("rlwimi %0,%2,8,8,15": "=&r"(result):"0"(result), "r"(x)); + __asm__("rlwimi %0,%2,24,0,7": "=&r"(result):"0"(result), "r"(x)); + return result; +} +#elif defined(__GNUC__) && (defined(__M68000__) || defined(__M68020__)) && !defined(__mcoldfire__) +static __inline__ Uint32 +SDL_Swap32(Uint32 x) +{ + __asm__("rorw #8,%0\n\tswap %0\n\trorw #8,%0": "=d"(x): "0"(x):"cc"); + return x; +} +#else +static __inline__ Uint32 +SDL_Swap32(Uint32 x) +{ + return SDL_static_cast(Uint32, ((x << 24) | ((x << 8) & 0x00FF0000) | + ((x >> 8) & 0x0000FF00) | (x >> 24))); +} +#endif + +#if defined(__GNUC__) && defined(__i386__) +static __inline__ Uint64 +SDL_Swap64(Uint64 x) +{ + union + { + struct + { + Uint32 a, b; + } s; + Uint64 u; + } v; + v.u = x; + __asm__("bswapl %0 ; bswapl %1 ; xchgl %0,%1": "=r"(v.s.a), "=r"(v.s.b):"0"(v.s.a), + "1"(v.s. + b)); + return v.u; +} +#elif defined(__GNUC__) && defined(__x86_64__) +static __inline__ Uint64 +SDL_Swap64(Uint64 x) +{ + __asm__("bswapq %0": "=r"(x):"0"(x)); + return x; +} +#else +static __inline__ Uint64 +SDL_Swap64(Uint64 x) +{ + Uint32 hi, lo; + + /* Separate into high and low 32-bit values and swap them */ + lo = SDL_static_cast(Uint32, x & 0xFFFFFFFF); + x >>= 32; + hi = SDL_static_cast(Uint32, x & 0xFFFFFFFF); + x = SDL_Swap32(lo); + x <<= 32; + x |= SDL_Swap32(hi); + return (x); +} +#endif + + +static __inline__ float +SDL_SwapFloat(float x) +{ + union + { + float f; + Uint32 ui32; + } swapper; + swapper.f = x; + swapper.ui32 = SDL_Swap32(swapper.ui32); + return swapper.f; +} + + +/** + * \name Swap to native + * Byteswap item from the specified endianness to the native endianness. + */ +/*@{*/ +#if SDL_BYTEORDER == SDL_LIL_ENDIAN +#define SDL_SwapLE16(X) (X) +#define SDL_SwapLE32(X) (X) +#define SDL_SwapLE64(X) (X) +#define SDL_SwapFloatLE(X) (X) +#define SDL_SwapBE16(X) SDL_Swap16(X) +#define SDL_SwapBE32(X) SDL_Swap32(X) +#define SDL_SwapBE64(X) SDL_Swap64(X) +#define SDL_SwapFloatBE(X) SDL_SwapFloat(X) +#else +#define SDL_SwapLE16(X) SDL_Swap16(X) +#define SDL_SwapLE32(X) SDL_Swap32(X) +#define SDL_SwapLE64(X) SDL_Swap64(X) +#define SDL_SwapFloatLE(X) SDL_SwapFloat(X) +#define SDL_SwapBE16(X) (X) +#define SDL_SwapBE32(X) (X) +#define SDL_SwapBE64(X) (X) +#define SDL_SwapFloatBE(X) (X) +#endif +/*@}*//*Swap to native*/ + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif +#include "close_code.h" + +#endif /* _SDL_endian_h */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_error.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_error.h new file mode 100644 index 0000000..d28b416 --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_error.h @@ -0,0 +1,77 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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. +*/ + +/** + * \file SDL_error.h + * + * Simple error message routines for SDL. + */ + +#ifndef _SDL_error_h +#define _SDL_error_h + +#include "SDL_stdinc.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +/* Public functions */ +extern DECLSPEC void SDLCALL SDL_SetError(const char *fmt, ...); +extern DECLSPEC const char *SDLCALL SDL_GetError(void); +extern DECLSPEC void SDLCALL SDL_ClearError(void); + +/** + * \name Internal error functions + * + * \internal + * Private error reporting function - used internally. + */ +/*@{*/ +#define SDL_OutOfMemory() SDL_Error(SDL_ENOMEM) +#define SDL_Unsupported() SDL_Error(SDL_UNSUPPORTED) +typedef enum +{ + SDL_ENOMEM, + SDL_EFREAD, + SDL_EFWRITE, + SDL_EFSEEK, + SDL_UNSUPPORTED, + SDL_LASTERROR +} SDL_errorcode; +extern DECLSPEC void SDLCALL SDL_Error(SDL_errorcode code); +/*@}*//*Internal error functions*/ + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif +#include "close_code.h" + +#endif /* _SDL_error_h */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_events.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_events.h new file mode 100644 index 0000000..804ac57 --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_events.h @@ -0,0 +1,639 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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. +*/ + +/** + * \file SDL_events.h + * + * Include file for SDL event handling. + */ + +#ifndef _SDL_events_h +#define _SDL_events_h + +#include "SDL_stdinc.h" +#include "SDL_error.h" +#include "SDL_video.h" +#include "SDL_keyboard.h" +#include "SDL_mouse.h" +#include "SDL_joystick.h" +#include "SDL_quit.h" +#include "SDL_gesture.h" +#include "SDL_touch.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +/* General keyboard/mouse state definitions */ +#define SDL_RELEASED 0 +#define SDL_PRESSED 1 + +/** + * \brief The types of events that can be delivered. + */ +typedef enum +{ + SDL_FIRSTEVENT = 0, /**< Unused (do not remove) */ + + /* Application events */ + SDL_QUIT = 0x100, /**< User-requested quit */ + + /* Window events */ + SDL_WINDOWEVENT = 0x200, /**< Window state change */ + SDL_SYSWMEVENT, /**< System specific event */ + + /* Keyboard events */ + SDL_KEYDOWN = 0x300, /**< Key pressed */ + SDL_KEYUP, /**< Key released */ + SDL_TEXTEDITING, /**< Keyboard text editing (composition) */ + SDL_TEXTINPUT, /**< Keyboard text input */ + + /* Mouse events */ + SDL_MOUSEMOTION = 0x400, /**< Mouse moved */ + SDL_MOUSEBUTTONDOWN, /**< Mouse button pressed */ + SDL_MOUSEBUTTONUP, /**< Mouse button released */ + SDL_MOUSEWHEEL, /**< Mouse wheel motion */ + + /* Tablet or multiple mice input device events */ + SDL_INPUTMOTION = 0x500, /**< Input moved */ + SDL_INPUTBUTTONDOWN, /**< Input button pressed */ + SDL_INPUTBUTTONUP, /**< Input button released */ + SDL_INPUTWHEEL, /**< Input wheel motion */ + SDL_INPUTPROXIMITYIN, /**< Input pen entered proximity */ + SDL_INPUTPROXIMITYOUT, /**< Input pen left proximity */ + + /* Joystick events */ + SDL_JOYAXISMOTION = 0x600, /**< Joystick axis motion */ + SDL_JOYBALLMOTION, /**< Joystick trackball motion */ + SDL_JOYHATMOTION, /**< Joystick hat position change */ + SDL_JOYBUTTONDOWN, /**< Joystick button pressed */ + SDL_JOYBUTTONUP, /**< Joystick button released */ + + /* Touch events */ + SDL_FINGERDOWN = 0x700, + SDL_FINGERUP, + SDL_FINGERMOTION, + SDL_TOUCHBUTTONDOWN, + SDL_TOUCHBUTTONUP, + + /* Gesture events */ + SDL_DOLLARGESTURE = 0x800, + SDL_DOLLARRECORD, + SDL_MULTIGESTURE, + + /* Clipboard events */ + + SDL_CLIPBOARDUPDATE = 0x900, /**< The clipboard changed */ + + /* Obsolete events */ + SDL_EVENT_COMPAT1 = 0x7000, /**< SDL 1.2 events for compatibility */ + SDL_EVENT_COMPAT2, + SDL_EVENT_COMPAT3, + + + /** Events ::SDL_USEREVENT through ::SDL_LASTEVENT are for your use, + * and should be allocated with SDL_RegisterEvents() + */ + SDL_USEREVENT = 0x8000, + + /** + * This last event is only for bounding internal arrays + */ + SDL_LASTEVENT = 0xFFFF +} SDL_EventType; + +/** + * \brief Window state change event data (event.window.*) + */ +typedef struct SDL_WindowEvent +{ + Uint32 type; /**< ::SDL_WINDOWEVENT */ + Uint32 windowID; /**< The associated window */ + Uint8 event; /**< ::SDL_WindowEventID */ + Uint8 padding1; + Uint8 padding2; + Uint8 padding3; + int data1; /**< event dependent data */ + int data2; /**< event dependent data */ +} SDL_WindowEvent; + +/** + * \brief Keyboard button event structure (event.key.*) + */ +typedef struct SDL_KeyboardEvent +{ + Uint32 type; /**< ::SDL_KEYDOWN or ::SDL_KEYUP */ + Uint32 windowID; /**< The window with keyboard focus, if any */ + Uint8 state; /**< ::SDL_PRESSED or ::SDL_RELEASED */ + Uint8 repeat; /**< Non-zero if this is a key repeat */ + Uint8 padding2; + Uint8 padding3; + SDL_Keysym keysym; /**< The key that was pressed or released */ +} SDL_KeyboardEvent; + +#define SDL_TEXTEDITINGEVENT_TEXT_SIZE (32) +/** + * \brief Keyboard text editing event structure (event.edit.*) + */ +typedef struct SDL_TextEditingEvent +{ + Uint32 type; /**< ::SDL_TEXTEDITING */ + Uint32 windowID; /**< The window with keyboard focus, if any */ + char text[SDL_TEXTEDITINGEVENT_TEXT_SIZE]; /**< The editing text */ + int start; /**< The start cursor of selected editing text */ + int length; /**< The length of selected editing text */ +} SDL_TextEditingEvent; + + +#define SDL_TEXTINPUTEVENT_TEXT_SIZE (32) +/** + * \brief Keyboard text input event structure (event.text.*) + */ +typedef struct SDL_TextInputEvent +{ + Uint32 type; /**< ::SDL_TEXTINPUT */ + Uint32 windowID; /**< The window with keyboard focus, if any */ + char text[SDL_TEXTINPUTEVENT_TEXT_SIZE]; /**< The input text */ +} SDL_TextInputEvent; + +/** + * \brief Mouse motion event structure (event.motion.*) + */ +typedef struct SDL_MouseMotionEvent +{ + Uint32 type; /**< ::SDL_MOUSEMOTION */ + Uint32 windowID; /**< The window with mouse focus, if any */ + Uint8 state; /**< The current button state */ + Uint8 padding1; + Uint8 padding2; + Uint8 padding3; + int x; /**< X coordinate, relative to window */ + int y; /**< Y coordinate, relative to window */ + int xrel; /**< The relative motion in the X direction */ + int yrel; /**< The relative motion in the Y direction */ +} SDL_MouseMotionEvent; + +/** + * \brief Mouse button event structure (event.button.*) + */ +typedef struct SDL_MouseButtonEvent +{ + Uint32 type; /**< ::SDL_MOUSEBUTTONDOWN or ::SDL_MOUSEBUTTONUP */ + Uint32 windowID; /**< The window with mouse focus, if any */ + Uint8 button; /**< The mouse button index */ + Uint8 state; /**< ::SDL_PRESSED or ::SDL_RELEASED */ + Uint8 padding1; + Uint8 padding2; + int x; /**< X coordinate, relative to window */ + int y; /**< Y coordinate, relative to window */ +} SDL_MouseButtonEvent; + +/** + * \brief Mouse wheel event structure (event.wheel.*) + */ +typedef struct SDL_MouseWheelEvent +{ + Uint32 type; /**< ::SDL_MOUSEWHEEL */ + Uint32 windowID; /**< The window with mouse focus, if any */ + int x; /**< The amount scrolled horizontally */ + int y; /**< The amount scrolled vertically */ +} SDL_MouseWheelEvent; + +/** + * \brief Joystick axis motion event structure (event.jaxis.*) + */ +typedef struct SDL_JoyAxisEvent +{ + Uint32 type; /**< ::SDL_JOYAXISMOTION */ + Uint8 which; /**< The joystick device index */ + Uint8 axis; /**< The joystick axis index */ + Uint8 padding1; + Uint8 padding2; + int value; /**< The axis value (range: -32768 to 32767) */ +} SDL_JoyAxisEvent; + +/** + * \brief Joystick trackball motion event structure (event.jball.*) + */ +typedef struct SDL_JoyBallEvent +{ + Uint32 type; /**< ::SDL_JOYBALLMOTION */ + Uint8 which; /**< The joystick device index */ + Uint8 ball; /**< The joystick trackball index */ + Uint8 padding1; + Uint8 padding2; + int xrel; /**< The relative motion in the X direction */ + int yrel; /**< The relative motion in the Y direction */ +} SDL_JoyBallEvent; + +/** + * \brief Joystick hat position change event structure (event.jhat.*) + */ +typedef struct SDL_JoyHatEvent +{ + Uint32 type; /**< ::SDL_JOYHATMOTION */ + Uint8 which; /**< The joystick device index */ + Uint8 hat; /**< The joystick hat index */ + Uint8 value; /**< The hat position value. + * \sa ::SDL_HAT_LEFTUP ::SDL_HAT_UP ::SDL_HAT_RIGHTUP + * \sa ::SDL_HAT_LEFT ::SDL_HAT_CENTERED ::SDL_HAT_RIGHT + * \sa ::SDL_HAT_LEFTDOWN ::SDL_HAT_DOWN ::SDL_HAT_RIGHTDOWN + * + * Note that zero means the POV is centered. + */ + Uint8 padding1; +} SDL_JoyHatEvent; + +/** + * \brief Joystick button event structure (event.jbutton.*) + */ +typedef struct SDL_JoyButtonEvent +{ + Uint32 type; /**< ::SDL_JOYBUTTONDOWN or ::SDL_JOYBUTTONUP */ + Uint8 which; /**< The joystick device index */ + Uint8 button; /**< The joystick button index */ + Uint8 state; /**< ::SDL_PRESSED or ::SDL_RELEASED */ + Uint8 padding1; +} SDL_JoyButtonEvent; + + +/** + * \brief Touch finger motion/finger event structure (event.tmotion.*) + */ +typedef struct SDL_TouchFingerEvent +{ + Uint32 type; /**< ::SDL_FINGERMOTION OR + SDL_FINGERDOWN OR SDL_FINGERUP*/ + Uint32 windowID; /**< The window with mouse focus, if any */ + SDL_TouchID touchId; /**< The touch device id */ + SDL_FingerID fingerId; + Uint8 state; /**< The current button state */ + Uint8 padding1; + Uint8 padding2; + Uint8 padding3; + Uint16 x; + Uint16 y; + Sint16 dx; + Sint16 dy; + Uint16 pressure; +} SDL_TouchFingerEvent; + + +/** + * \brief Touch finger motion/finger event structure (event.tmotion.*) + */ +typedef struct SDL_TouchButtonEvent +{ + Uint32 type; /**< ::SDL_TOUCHBUTTONUP OR SDL_TOUCHBUTTONDOWN */ + Uint32 windowID; /**< The window with mouse focus, if any */ + SDL_TouchID touchId; /**< The touch device index */ + Uint8 state; /**< The current button state */ + Uint8 button; /**< The button changing state */ + Uint8 padding1; + Uint8 padding2; +} SDL_TouchButtonEvent; + + +/** + * \brief Multiple Finger Gesture Event (event.mgesture.*) + */ +typedef struct SDL_MultiGestureEvent +{ + Uint32 type; /**< ::SDL_MULTIGESTURE */ + Uint32 windowID; /**< The window with mouse focus, if any */ + SDL_TouchID touchId; /**< The touch device index */ + float dTheta; + float dDist; + float x; //currently 0...1. Change to screen coords? + float y; + Uint16 numFingers; + Uint16 padding; +} SDL_MultiGestureEvent; + +/* (event.dgesture.*) */ +typedef struct SDL_DollarGestureEvent +{ + Uint32 type; /**< ::SDL_DOLLARGESTURE */ + Uint32 windowID; /**< The window with mouse focus, if any */ + SDL_TouchID touchId; /**< The touch device index */ + SDL_GestureID gestureId; + Uint32 numFingers; + float error; + /* + //TODO: Enable to give location? + float x; //currently 0...1. Change to screen coords? + float y; + */ +} SDL_DollarGestureEvent; + + +/** + * \brief The "quit requested" event + */ +typedef struct SDL_QuitEvent +{ + Uint32 type; /**< ::SDL_QUIT */ +} SDL_QuitEvent; + + +/** + * \brief A user-defined event type (event.user.*) + */ +typedef struct SDL_UserEvent +{ + Uint32 type; /**< ::SDL_USEREVENT through ::SDL_NUMEVENTS-1 */ + Uint32 windowID; /**< The associated window if any */ + int code; /**< User defined event code */ + void *data1; /**< User defined data pointer */ + void *data2; /**< User defined data pointer */ +} SDL_UserEvent; + + +struct SDL_SysWMmsg; +typedef struct SDL_SysWMmsg SDL_SysWMmsg; + +/** + * \brief A video driver dependent system event (event.syswm.*) + * + * \note If you want to use this event, you should include SDL_syswm.h. + */ +typedef struct SDL_SysWMEvent +{ + Uint32 type; /**< ::SDL_SYSWMEVENT */ + SDL_SysWMmsg *msg; /**< driver dependent data, defined in SDL_syswm.h */ +} SDL_SysWMEvent; + +#ifndef SDL_NO_COMPAT +/** + * \addtogroup Compatibility + */ +/*@{*/ + +/** + * \name Typedefs for backwards compatibility + */ +/*@{*/ +typedef struct SDL_ActiveEvent +{ + Uint32 type; + Uint8 gain; + Uint8 state; +} SDL_ActiveEvent; + +typedef struct SDL_ResizeEvent +{ + Uint32 type; + int w; + int h; +} SDL_ResizeEvent; +/*@}*/ + +/*@}*//*Compatibility*/ +#endif + +/** + * \brief General event structure + */ +typedef union SDL_Event +{ + Uint32 type; /**< Event type, shared with all events */ + SDL_WindowEvent window; /**< Window event data */ + SDL_KeyboardEvent key; /**< Keyboard event data */ + SDL_TextEditingEvent edit; /**< Text editing event data */ + SDL_TextInputEvent text; /**< Text input event data */ + SDL_MouseMotionEvent motion; /**< Mouse motion event data */ + SDL_MouseButtonEvent button; /**< Mouse button event data */ + SDL_MouseWheelEvent wheel; /**< Mouse wheel event data */ + SDL_JoyAxisEvent jaxis; /**< Joystick axis event data */ + SDL_JoyBallEvent jball; /**< Joystick ball event data */ + SDL_JoyHatEvent jhat; /**< Joystick hat event data */ + SDL_JoyButtonEvent jbutton; /**< Joystick button event data */ + SDL_QuitEvent quit; /**< Quit request event data */ + SDL_UserEvent user; /**< Custom event data */ + SDL_SysWMEvent syswm; /**< System dependent window event data */ + SDL_TouchFingerEvent tfinger; /**< Touch finger event data */ + SDL_TouchButtonEvent tbutton; /**< Touch button event data */ + SDL_MultiGestureEvent mgesture; /**< Multi Finger Gesture data */ + SDL_DollarGestureEvent dgesture; /**< Multi Finger Gesture data */ + + /** Temporarily here for backwards compatibility */ + /*@{*/ +#ifndef SDL_NO_COMPAT + SDL_ActiveEvent active; + SDL_ResizeEvent resize; +#endif + /*@}*/ +} SDL_Event; + + +/* Function prototypes */ + +/** + * Pumps the event loop, gathering events from the input devices. + * + * This function updates the event queue and internal input device state. + * + * This should only be run in the thread that sets the video mode. + */ +extern DECLSPEC void SDLCALL SDL_PumpEvents(void); + +/*@{*/ +typedef enum +{ + SDL_ADDEVENT, + SDL_PEEKEVENT, + SDL_GETEVENT +} SDL_eventaction; + +/** + * Checks the event queue for messages and optionally returns them. + * + * If \c action is ::SDL_ADDEVENT, up to \c numevents events will be added to + * the back of the event queue. + * + * If \c action is ::SDL_PEEKEVENT, up to \c numevents events at the front + * of the event queue, within the specified minimum and maximum type, + * will be returned and will not be removed from the queue. + * + * If \c action is ::SDL_GETEVENT, up to \c numevents events at the front + * of the event queue, within the specified minimum and maximum type, + * will be returned and will be removed from the queue. + * + * \return The number of events actually stored, or -1 if there was an error. + * + * This function is thread-safe. + */ +extern DECLSPEC int SDLCALL SDL_PeepEvents(SDL_Event * events, int numevents, + SDL_eventaction action, + Uint32 minType, Uint32 maxType); +/*@}*/ + +/** + * Checks to see if certain event types are in the event queue. + */ +extern DECLSPEC SDL_bool SDLCALL SDL_HasEvent(Uint32 type); +extern DECLSPEC SDL_bool SDLCALL SDL_HasEvents(Uint32 minType, Uint32 maxType); + +/** + * This function clears events from the event queue + */ +extern DECLSPEC void SDLCALL SDL_FlushEvent(Uint32 type); +extern DECLSPEC void SDLCALL SDL_FlushEvents(Uint32 minType, Uint32 maxType); + +/** + * \brief Polls for currently pending events. + * + * \return 1 if there are any pending events, or 0 if there are none available. + * + * \param event If not NULL, the next event is removed from the queue and + * stored in that area. + */ +extern DECLSPEC int SDLCALL SDL_PollEvent(SDL_Event * event); + +/** + * \brief Waits indefinitely for the next available event. + * + * \return 1, or 0 if there was an error while waiting for events. + * + * \param event If not NULL, the next event is removed from the queue and + * stored in that area. + */ +extern DECLSPEC int SDLCALL SDL_WaitEvent(SDL_Event * event); + +/** + * \brief Waits until the specified timeout (in milliseconds) for the next + * available event. + * + * \return 1, or 0 if there was an error while waiting for events. + * + * \param event If not NULL, the next event is removed from the queue and + * stored in that area. + */ +extern DECLSPEC int SDLCALL SDL_WaitEventTimeout(SDL_Event * event, + int timeout); + +/** + * \brief Add an event to the event queue. + * + * \return 1 on success, 0 if the event was filtered, or -1 if the event queue + * was full or there was some other error. + */ +extern DECLSPEC int SDLCALL SDL_PushEvent(SDL_Event * event); + +typedef int (SDLCALL * SDL_EventFilter) (void *userdata, SDL_Event * event); + +/** + * Sets up a filter to process all events before they change internal state and + * are posted to the internal event queue. + * + * The filter is protypted as: + * \code + * int SDL_EventFilter(void *userdata, SDL_Event * event); + * \endcode + * + * If the filter returns 1, then the event will be added to the internal queue. + * If it returns 0, then the event will be dropped from the queue, but the + * internal state will still be updated. This allows selective filtering of + * dynamically arriving events. + * + * \warning Be very careful of what you do in the event filter function, as + * it may run in a different thread! + * + * There is one caveat when dealing with the ::SDL_QUITEVENT event type. The + * event filter is only called when the window manager desires to close the + * application window. If the event filter returns 1, then the window will + * be closed, otherwise the window will remain open if possible. + * + * If the quit event is generated by an interrupt signal, it will bypass the + * internal queue and be delivered to the application at the next event poll. + */ +extern DECLSPEC void SDLCALL SDL_SetEventFilter(SDL_EventFilter filter, + void *userdata); + +/** + * Return the current event filter - can be used to "chain" filters. + * If there is no event filter set, this function returns SDL_FALSE. + */ +extern DECLSPEC SDL_bool SDLCALL SDL_GetEventFilter(SDL_EventFilter * filter, + void **userdata); + +/** + * Add a function which is called when an event is added to the queue. + */ +extern DECLSPEC void SDLCALL SDL_AddEventWatch(SDL_EventFilter filter, + void *userdata); + +/** + * Remove an event watch function added with SDL_AddEventWatch() + */ +extern DECLSPEC void SDLCALL SDL_DelEventWatch(SDL_EventFilter filter, + void *userdata); + +/** + * Run the filter function on the current event queue, removing any + * events for which the filter returns 0. + */ +extern DECLSPEC void SDLCALL SDL_FilterEvents(SDL_EventFilter filter, + void *userdata); + +/*@{*/ +#define SDL_QUERY -1 +#define SDL_IGNORE 0 +#define SDL_DISABLE 0 +#define SDL_ENABLE 1 + +/** + * This function allows you to set the state of processing certain events. + * - If \c state is set to ::SDL_IGNORE, that event will be automatically + * dropped from the event queue and will not event be filtered. + * - If \c state is set to ::SDL_ENABLE, that event will be processed + * normally. + * - If \c state is set to ::SDL_QUERY, SDL_EventState() will return the + * current processing state of the specified event. + */ +extern DECLSPEC Uint8 SDLCALL SDL_EventState(Uint32 type, int state); +/*@}*/ +#define SDL_GetEventState(type) SDL_EventState(type, SDL_QUERY) + +/** + * This function allocates a set of user-defined events, and returns + * the beginning event number for that set of events. + * + * If there aren't enough user-defined events left, this function + * returns (Uint32)-1 + */ +extern DECLSPEC Uint32 SDLCALL SDL_RegisterEvents(int numevents); + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif +#include "close_code.h" + +#endif /* _SDL_events_h */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_gesture.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_gesture.h new file mode 100644 index 0000000..940d346 --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_gesture.h @@ -0,0 +1,91 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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. +*/ + +/** + * \file SDL_gesture.h + * + * Include file for SDL gesture event handling. + */ + +#ifndef _SDL_gesture_h +#define _SDL_gesture_h + +#include "SDL_stdinc.h" +#include "SDL_error.h" +#include "SDL_video.h" + +#include "SDL_touch.h" + + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +typedef Sint64 SDL_GestureID; + +/* Function prototypes */ + +/** + * \brief Begin Recording a gesture on the specified touch, or all touches (-1) + * + * + */ +extern DECLSPEC int SDLCALL SDL_RecordGesture(SDL_TouchID touchId); + + +/** + * \brief Save all currently loaded Dollar Gesture templates + * + * + */ +extern DECLSPEC int SDLCALL SDL_SaveAllDollarTemplates(SDL_RWops *src); + +/** + * \brief Save a currently loaded Dollar Gesture template + * + * + */ +extern DECLSPEC int SDLCALL SDL_SaveDollarTemplate(SDL_GestureID gestureId,SDL_RWops *src); + + +/** + * \brief Load Dollar Gesture templates from a file + * + * + */ +extern DECLSPEC int SDLCALL SDL_LoadDollarTemplates(SDL_TouchID touchId, SDL_RWops *src); + + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif +#include "close_code.h" + +#endif /* _SDL_gesture_h */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_haptic.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_haptic.h new file mode 100644 index 0000000..6b354a2 --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_haptic.h @@ -0,0 +1,1200 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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. +*/ + +/** + * \file SDL_haptic.h + * + * \brief The SDL Haptic subsystem allows you to control haptic (force feedback) + * devices. + * + * The basic usage is as follows: + * - Initialize the Subsystem (::SDL_INIT_HAPTIC). + * - Open a Haptic Device. + * - SDL_HapticOpen() to open from index. + * - SDL_HapticOpenFromJoystick() to open from an existing joystick. + * - Create an effect (::SDL_HapticEffect). + * - Upload the effect with SDL_HapticNewEffect(). + * - Run the effect with SDL_HapticRunEffect(). + * - (optional) Free the effect with SDL_HapticDestroyEffect(). + * - Close the haptic device with SDL_HapticClose(). + * + * \par Simple rumble example: + * \code + * SDL_Haptic *haptic; + * + * // Open the device + * haptic = SDL_HapticOpen( 0 ); + * if (haptic == NULL) + * return -1; + * + * // Initialize simple rumble + * if (SDL_HapticRumbleInit( haptic ) != 0) + * return -1; + * + * // Play effect at 50% strength for 2 seconds + * if (SDL_HapticRumblePlay( haptic, 0.5, 2000 ) != 0) + * return -1; + * SDL_Delay( 2000 ); + * + * // Clean up + * SDL_HapticClose( haptic ); + * \endcode + * + * \par Complete example: + * \code + * int test_haptic( SDL_Joystick * joystick ) { + * SDL_Haptic *haptic; + * SDL_HapticEffect effect; + * int effect_id; + * + * // Open the device + * haptic = SDL_HapticOpenFromJoystick( joystick ); + * if (haptic == NULL) return -1; // Most likely joystick isn't haptic + * + * // See if it can do sine waves + * if ((SDL_HapticQuery(haptic) & SDL_HAPTIC_SINE)==0) { + * SDL_HapticClose(haptic); // No sine effect + * return -1; + * } + * + * // Create the effect + * memset( &effect, 0, sizeof(SDL_HapticEffect) ); // 0 is safe default + * effect.type = SDL_HAPTIC_SINE; + * effect.periodic.direction.type = SDL_HAPTIC_POLAR; // Polar coordinates + * effect.periodic.direction.dir[0] = 18000; // Force comes from south + * effect.periodic.period = 1000; // 1000 ms + * effect.periodic.magnitude = 20000; // 20000/32767 strength + * effect.periodic.length = 5000; // 5 seconds long + * effect.periodic.attack_length = 1000; // Takes 1 second to get max strength + * effect.periodic.fade_length = 1000; // Takes 1 second to fade away + * + * // Upload the effect + * effect_id = SDL_HapticNewEffect( haptic, &effect ); + * + * // Test the effect + * SDL_HapticRunEffect( haptic, effect_id, 1 ); + * SDL_Delay( 5000); // Wait for the effect to finish + * + * // We destroy the effect, although closing the device also does this + * SDL_HapticDestroyEffect( haptic, effect_id ); + * + * // Close the device + * SDL_HapticClose(haptic); + * + * return 0; // Success + * } + * \endcode + * + * You can also find out more information on my blog: + * http://bobbens.dyndns.org/journal/2010/sdl_haptic/ + * + * \author Edgar Simo Serra + */ + +#ifndef _SDL_haptic_h +#define _SDL_haptic_h + +#include "SDL_stdinc.h" +#include "SDL_error.h" +#include "SDL_joystick.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { + /* *INDENT-ON* */ +#endif /* __cplusplus */ + +/** + * \typedef SDL_Haptic + * + * \brief The haptic structure used to identify an SDL haptic. + * + * \sa SDL_HapticOpen + * \sa SDL_HapticOpenFromJoystick + * \sa SDL_HapticClose + */ +struct _SDL_Haptic; +typedef struct _SDL_Haptic SDL_Haptic; + + +/** + * \name Haptic features + * + * Different haptic features a device can have. + */ +/*@{*/ + +/** + * \name Haptic effects + */ +/*@{*/ + +/** + * \brief Constant effect supported. + * + * Constant haptic effect. + * + * \sa SDL_HapticCondition + */ +#define SDL_HAPTIC_CONSTANT (1<<0) + +/** + * \brief Sine wave effect supported. + * + * Periodic haptic effect that simulates sine waves. + * + * \sa SDL_HapticPeriodic + */ +#define SDL_HAPTIC_SINE (1<<1) + +/** + * \brief Square wave effect supported. + * + * Periodic haptic effect that simulates square waves. + * + * \sa SDL_HapticPeriodic + */ +#define SDL_HAPTIC_SQUARE (1<<2) + +/** + * \brief Triangle wave effect supported. + * + * Periodic haptic effect that simulates triangular waves. + * + * \sa SDL_HapticPeriodic + */ +#define SDL_HAPTIC_TRIANGLE (1<<3) + +/** + * \brief Sawtoothup wave effect supported. + * + * Periodic haptic effect that simulates saw tooth up waves. + * + * \sa SDL_HapticPeriodic + */ +#define SDL_HAPTIC_SAWTOOTHUP (1<<4) + +/** + * \brief Sawtoothdown wave effect supported. + * + * Periodic haptic effect that simulates saw tooth down waves. + * + * \sa SDL_HapticPeriodic + */ +#define SDL_HAPTIC_SAWTOOTHDOWN (1<<5) + +/** + * \brief Ramp effect supported. + * + * Ramp haptic effect. + * + * \sa SDL_HapticRamp + */ +#define SDL_HAPTIC_RAMP (1<<6) + +/** + * \brief Spring effect supported - uses axes position. + * + * Condition haptic effect that simulates a spring. Effect is based on the + * axes position. + * + * \sa SDL_HapticCondition + */ +#define SDL_HAPTIC_SPRING (1<<7) + +/** + * \brief Damper effect supported - uses axes velocity. + * + * Condition haptic effect that simulates dampening. Effect is based on the + * axes velocity. + * + * \sa SDL_HapticCondition + */ +#define SDL_HAPTIC_DAMPER (1<<8) + +/** + * \brief Inertia effect supported - uses axes acceleration. + * + * Condition haptic effect that simulates inertia. Effect is based on the axes + * acceleration. + * + * \sa SDL_HapticCondition + */ +#define SDL_HAPTIC_INERTIA (1<<9) + +/** + * \brief Friction effect supported - uses axes movement. + * + * Condition haptic effect that simulates friction. Effect is based on the + * axes movement. + * + * \sa SDL_HapticCondition + */ +#define SDL_HAPTIC_FRICTION (1<<10) + +/** + * \brief Custom effect is supported. + * + * User defined custom haptic effect. + */ +#define SDL_HAPTIC_CUSTOM (1<<11) + +/*@}*//*Haptic effects*/ + +/* These last few are features the device has, not effects */ + +/** + * \brief Device can set global gain. + * + * Device supports setting the global gain. + * + * \sa SDL_HapticSetGain + */ +#define SDL_HAPTIC_GAIN (1<<12) + +/** + * \brief Device can set autocenter. + * + * Device supports setting autocenter. + * + * \sa SDL_HapticSetAutocenter + */ +#define SDL_HAPTIC_AUTOCENTER (1<<13) + +/** + * \brief Device can be queried for effect status. + * + * Device can be queried for effect status. + * + * \sa SDL_HapticGetEffectStatus + */ +#define SDL_HAPTIC_STATUS (1<<14) + +/** + * \brief Device can be paused. + * + * \sa SDL_HapticPause + * \sa SDL_HapticUnpause + */ +#define SDL_HAPTIC_PAUSE (1<<15) + + +/** + * \name Direction encodings + */ +/*@{*/ + +/** + * \brief Uses polar coordinates for the direction. + * + * \sa SDL_HapticDirection + */ +#define SDL_HAPTIC_POLAR 0 + +/** + * \brief Uses cartesian coordinates for the direction. + * + * \sa SDL_HapticDirection + */ +#define SDL_HAPTIC_CARTESIAN 1 + +/** + * \brief Uses spherical coordinates for the direction. + * + * \sa SDL_HapticDirection + */ +#define SDL_HAPTIC_SPHERICAL 2 + +/*@}*//*Direction encodings*/ + +/*@}*//*Haptic features*/ + +/* + * Misc defines. + */ + +/** + * \brief Used to play a device an infinite number of times. + * + * \sa SDL_HapticRunEffect + */ +#define SDL_HAPTIC_INFINITY 4294967295U + + +/** + * \brief Structure that represents a haptic direction. + * + * Directions can be specified by: + * - ::SDL_HAPTIC_POLAR : Specified by polar coordinates. + * - ::SDL_HAPTIC_CARTESIAN : Specified by cartesian coordinates. + * - ::SDL_HAPTIC_SPHERICAL : Specified by spherical coordinates. + * + * Cardinal directions of the haptic device are relative to the positioning + * of the device. North is considered to be away from the user. + * + * The following diagram represents the cardinal directions: + * \verbatim + .--. + |__| .-------. + |=.| |.-----.| + |--| || || + | | |'-----'| + |__|~')_____(' + [ COMPUTER ] + + + North (0,-1) + ^ + | + | + (1,0) West <----[ HAPTIC ]----> East (-1,0) + | + | + v + South (0,1) + + + [ USER ] + \|||/ + (o o) + ---ooO-(_)-Ooo--- + \endverbatim + * + * If type is ::SDL_HAPTIC_POLAR, direction is encoded by hundredths of a + * degree starting north and turning clockwise. ::SDL_HAPTIC_POLAR only uses + * the first \c dir parameter. The cardinal directions would be: + * - North: 0 (0 degrees) + * - East: 9000 (90 degrees) + * - South: 18000 (180 degrees) + * - West: 27000 (270 degrees) + * + * If type is ::SDL_HAPTIC_CARTESIAN, direction is encoded by three positions + * (X axis, Y axis and Z axis (with 3 axes)). ::SDL_HAPTIC_CARTESIAN uses + * the first three \c dir parameters. The cardinal directions would be: + * - North: 0,-1, 0 + * - East: -1, 0, 0 + * - South: 0, 1, 0 + * - West: 1, 0, 0 + * + * The Z axis represents the height of the effect if supported, otherwise + * it's unused. In cartesian encoding (1, 2) would be the same as (2, 4), you + * can use any multiple you want, only the direction matters. + * + * If type is ::SDL_HAPTIC_SPHERICAL, direction is encoded by two rotations. + * The first two \c dir parameters are used. The \c dir parameters are as + * follows (all values are in hundredths of degrees): + * - Degrees from (1, 0) rotated towards (0, 1). + * - Degrees towards (0, 0, 1) (device needs at least 3 axes). + * + * + * Example of force coming from the south with all encodings (force coming + * from the south means the user will have to pull the stick to counteract): + * \code + * SDL_HapticDirection direction; + * + * // Cartesian directions + * direction.type = SDL_HAPTIC_CARTESIAN; // Using cartesian direction encoding. + * direction.dir[0] = 0; // X position + * direction.dir[1] = 1; // Y position + * // Assuming the device has 2 axes, we don't need to specify third parameter. + * + * // Polar directions + * direction.type = SDL_HAPTIC_POLAR; // We'll be using polar direction encoding. + * direction.dir[0] = 18000; // Polar only uses first parameter + * + * // Spherical coordinates + * direction.type = SDL_HAPTIC_SPHERICAL; // Spherical encoding + * direction.dir[0] = 9000; // Since we only have two axes we don't need more parameters. + * \endcode + * + * \sa SDL_HAPTIC_POLAR + * \sa SDL_HAPTIC_CARTESIAN + * \sa SDL_HAPTIC_SPHERICAL + * \sa SDL_HapticEffect + * \sa SDL_HapticNumAxes + */ +typedef struct SDL_HapticDirection +{ + Uint8 type; /**< The type of encoding. */ + Sint32 dir[3]; /**< The encoded direction. */ +} SDL_HapticDirection; + + +/** + * \brief A structure containing a template for a Constant effect. + * + * The struct is exclusive to the ::SDL_HAPTIC_CONSTANT effect. + * + * A constant effect applies a constant force in the specified direction + * to the joystick. + * + * \sa SDL_HAPTIC_CONSTANT + * \sa SDL_HapticEffect + */ +typedef struct SDL_HapticConstant +{ + /* Header */ + Uint16 type; /**< ::SDL_HAPTIC_CONSTANT */ + SDL_HapticDirection direction; /**< Direction of the effect. */ + + /* Replay */ + Uint32 length; /**< Duration of the effect. */ + Uint16 delay; /**< Delay before starting the effect. */ + + /* Trigger */ + Uint16 button; /**< Button that triggers the effect. */ + Uint16 interval; /**< How soon it can be triggered again after button. */ + + /* Constant */ + Sint16 level; /**< Strength of the constant effect. */ + + /* Envelope */ + Uint16 attack_length; /**< Duration of the attack. */ + Uint16 attack_level; /**< Level at the start of the attack. */ + Uint16 fade_length; /**< Duration of the fade. */ + Uint16 fade_level; /**< Level at the end of the fade. */ +} SDL_HapticConstant; + +/** + * \brief A structure containing a template for a Periodic effect. + * + * The struct handles the following effects: + * - ::SDL_HAPTIC_SINE + * - ::SDL_HAPTIC_SQUARE + * - ::SDL_HAPTIC_TRIANGLE + * - ::SDL_HAPTIC_SAWTOOTHUP + * - ::SDL_HAPTIC_SAWTOOTHDOWN + * + * A periodic effect consists in a wave-shaped effect that repeats itself + * over time. The type determines the shape of the wave and the parameters + * determine the dimensions of the wave. + * + * Phase is given by hundredth of a cyle meaning that giving the phase a value + * of 9000 will displace it 25% of it's period. Here are sample values: + * - 0: No phase displacement. + * - 9000: Displaced 25% of it's period. + * - 18000: Displaced 50% of it's period. + * - 27000: Displaced 75% of it's period. + * - 36000: Displaced 100% of it's period, same as 0, but 0 is preffered. + * + * Examples: + * \verbatim + SDL_HAPTIC_SINE + __ __ __ __ + / \ / \ / \ / + / \__/ \__/ \__/ + + SDL_HAPTIC_SQUARE + __ __ __ __ __ + | | | | | | | | | | + | |__| |__| |__| |__| | + + SDL_HAPTIC_TRIANGLE + /\ /\ /\ /\ /\ + / \ / \ / \ / \ / + / \/ \/ \/ \/ + + SDL_HAPTIC_SAWTOOTHUP + /| /| /| /| /| /| /| + / | / | / | / | / | / | / | + / |/ |/ |/ |/ |/ |/ | + + SDL_HAPTIC_SAWTOOTHDOWN + \ |\ |\ |\ |\ |\ |\ | + \ | \ | \ | \ | \ | \ | \ | + \| \| \| \| \| \| \| + \endverbatim + * + * \sa SDL_HAPTIC_SINE + * \sa SDL_HAPTIC_SQUARE + * \sa SDL_HAPTIC_TRIANGLE + * \sa SDL_HAPTIC_SAWTOOTHUP + * \sa SDL_HAPTIC_SAWTOOTHDOWN + * \sa SDL_HapticEffect + */ +typedef struct SDL_HapticPeriodic +{ + /* Header */ + Uint16 type; /**< ::SDL_HAPTIC_SINE, ::SDL_HAPTIC_SQUARE, + ::SDL_HAPTIC_TRIANGLE, ::SDL_HAPTIC_SAWTOOTHUP or + ::SDL_HAPTIC_SAWTOOTHDOWN */ + SDL_HapticDirection direction; /**< Direction of the effect. */ + + /* Replay */ + Uint32 length; /**< Duration of the effect. */ + Uint16 delay; /**< Delay before starting the effect. */ + + /* Trigger */ + Uint16 button; /**< Button that triggers the effect. */ + Uint16 interval; /**< How soon it can be triggered again after button. */ + + /* Periodic */ + Uint16 period; /**< Period of the wave. */ + Sint16 magnitude; /**< Peak value. */ + Sint16 offset; /**< Mean value of the wave. */ + Uint16 phase; /**< Horizontal shift given by hundredth of a cycle. */ + + /* Envelope */ + Uint16 attack_length; /**< Duration of the attack. */ + Uint16 attack_level; /**< Level at the start of the attack. */ + Uint16 fade_length; /**< Duration of the fade. */ + Uint16 fade_level; /**< Level at the end of the fade. */ +} SDL_HapticPeriodic; + +/** + * \brief A structure containing a template for a Condition effect. + * + * The struct handles the following effects: + * - ::SDL_HAPTIC_SPRING: Effect based on axes position. + * - ::SDL_HAPTIC_DAMPER: Effect based on axes velocity. + * - ::SDL_HAPTIC_INERTIA: Effect based on axes acceleration. + * - ::SDL_HAPTIC_FRICTION: Effect based on axes movement. + * + * Direction is handled by condition internals instead of a direction member. + * The condition effect specific members have three parameters. The first + * refers to the X axis, the second refers to the Y axis and the third + * refers to the Z axis. The right terms refer to the positive side of the + * axis and the left terms refer to the negative side of the axis. Please + * refer to the ::SDL_HapticDirection diagram for which side is positive and + * which is negative. + * + * \sa SDL_HapticDirection + * \sa SDL_HAPTIC_SPRING + * \sa SDL_HAPTIC_DAMPER + * \sa SDL_HAPTIC_INERTIA + * \sa SDL_HAPTIC_FRICTION + * \sa SDL_HapticEffect + */ +typedef struct SDL_HapticCondition +{ + /* Header */ + Uint16 type; /**< ::SDL_HAPTIC_SPRING, ::SDL_HAPTIC_DAMPER, + ::SDL_HAPTIC_INERTIA or ::SDL_HAPTIC_FRICTION */ + SDL_HapticDirection direction; /**< Direction of the effect - Not used ATM. */ + + /* Replay */ + Uint32 length; /**< Duration of the effect. */ + Uint16 delay; /**< Delay before starting the effect. */ + + /* Trigger */ + Uint16 button; /**< Button that triggers the effect. */ + Uint16 interval; /**< How soon it can be triggered again after button. */ + + /* Condition */ + Uint16 right_sat[3]; /**< Level when joystick is to the positive side. */ + Uint16 left_sat[3]; /**< Level when joystick is to the negative side. */ + Sint16 right_coeff[3]; /**< How fast to increase the force towards the positive side. */ + Sint16 left_coeff[3]; /**< How fast to increase the force towards the negative side. */ + Uint16 deadband[3]; /**< Size of the dead zone. */ + Sint16 center[3]; /**< Position of the dead zone. */ +} SDL_HapticCondition; + +/** + * \brief A structure containing a template for a Ramp effect. + * + * This struct is exclusively for the ::SDL_HAPTIC_RAMP effect. + * + * The ramp effect starts at start strength and ends at end strength. + * It augments in linear fashion. If you use attack and fade with a ramp + * they effects get added to the ramp effect making the effect become + * quadratic instead of linear. + * + * \sa SDL_HAPTIC_RAMP + * \sa SDL_HapticEffect + */ +typedef struct SDL_HapticRamp +{ + /* Header */ + Uint16 type; /**< ::SDL_HAPTIC_RAMP */ + SDL_HapticDirection direction; /**< Direction of the effect. */ + + /* Replay */ + Uint32 length; /**< Duration of the effect. */ + Uint16 delay; /**< Delay before starting the effect. */ + + /* Trigger */ + Uint16 button; /**< Button that triggers the effect. */ + Uint16 interval; /**< How soon it can be triggered again after button. */ + + /* Ramp */ + Sint16 start; /**< Beginning strength level. */ + Sint16 end; /**< Ending strength level. */ + + /* Envelope */ + Uint16 attack_length; /**< Duration of the attack. */ + Uint16 attack_level; /**< Level at the start of the attack. */ + Uint16 fade_length; /**< Duration of the fade. */ + Uint16 fade_level; /**< Level at the end of the fade. */ +} SDL_HapticRamp; + +/** + * \brief A structure containing a template for the ::SDL_HAPTIC_CUSTOM effect. + * + * A custom force feedback effect is much like a periodic effect, where the + * application can define it's exact shape. You will have to allocate the + * data yourself. Data should consist of channels * samples Uint16 samples. + * + * If channels is one, the effect is rotated using the defined direction. + * Otherwise it uses the samples in data for the different axes. + * + * \sa SDL_HAPTIC_CUSTOM + * \sa SDL_HapticEffect + */ +typedef struct SDL_HapticCustom +{ + /* Header */ + Uint16 type; /**< ::SDL_HAPTIC_CUSTOM */ + SDL_HapticDirection direction; /**< Direction of the effect. */ + + /* Replay */ + Uint32 length; /**< Duration of the effect. */ + Uint16 delay; /**< Delay before starting the effect. */ + + /* Trigger */ + Uint16 button; /**< Button that triggers the effect. */ + Uint16 interval; /**< How soon it can be triggered again after button. */ + + /* Custom */ + Uint8 channels; /**< Axes to use, minimum of one. */ + Uint16 period; /**< Sample periods. */ + Uint16 samples; /**< Amount of samples. */ + Uint16 *data; /**< Should contain channels*samples items. */ + + /* Envelope */ + Uint16 attack_length; /**< Duration of the attack. */ + Uint16 attack_level; /**< Level at the start of the attack. */ + Uint16 fade_length; /**< Duration of the fade. */ + Uint16 fade_level; /**< Level at the end of the fade. */ +} SDL_HapticCustom; + +/** + * \brief The generic template for any haptic effect. + * + * All values max at 32767 (0x7FFF). Signed values also can be negative. + * Time values unless specified otherwise are in milliseconds. + * + * You can also pass ::SDL_HAPTIC_INFINITY to length instead of a 0-32767 + * value. Neither delay, interval, attack_length nor fade_length support + * ::SDL_HAPTIC_INFINITY. Fade will also not be used since effect never ends. + * + * Additionally, the ::SDL_HAPTIC_RAMP effect does not support a duration of + * ::SDL_HAPTIC_INFINITY. + * + * Button triggers may not be supported on all devices, it is advised to not + * use them if possible. Buttons start at index 1 instead of index 0 like + * they joystick. + * + * If both attack_length and fade_level are 0, the envelope is not used, + * otherwise both values are used. + * + * Common parts: + * \code + * // Replay - All effects have this + * Uint32 length; // Duration of effect (ms). + * Uint16 delay; // Delay before starting effect. + * + * // Trigger - All effects have this + * Uint16 button; // Button that triggers effect. + * Uint16 interval; // How soon before effect can be triggered again. + * + * // Envelope - All effects except condition effects have this + * Uint16 attack_length; // Duration of the attack (ms). + * Uint16 attack_level; // Level at the start of the attack. + * Uint16 fade_length; // Duration of the fade out (ms). + * Uint16 fade_level; // Level at the end of the fade. + * \endcode + * + * + * Here we have an example of a constant effect evolution in time: + * \verbatim + Strength + ^ + | + | effect level --> _________________ + | / \ + | / \ + | / \ + | / \ + | attack_level --> | \ + | | | <--- fade_level + | + +--------------------------------------------------> Time + [--] [---] + attack_length fade_length + + [------------------][-----------------------] + delay length + \endverbatim + * + * Note either the attack_level or the fade_level may be above the actual + * effect level. + * + * \sa SDL_HapticConstant + * \sa SDL_HapticPeriodic + * \sa SDL_HapticCondition + * \sa SDL_HapticRamp + * \sa SDL_HapticCustom + */ +typedef union SDL_HapticEffect +{ + /* Common for all force feedback effects */ + Uint16 type; /**< Effect type. */ + SDL_HapticConstant constant; /**< Constant effect. */ + SDL_HapticPeriodic periodic; /**< Periodic effect. */ + SDL_HapticCondition condition; /**< Condition effect. */ + SDL_HapticRamp ramp; /**< Ramp effect. */ + SDL_HapticCustom custom; /**< Custom effect. */ +} SDL_HapticEffect; + + +/* Function prototypes */ +/** + * \brief Count the number of joysticks attached to the system. + * + * \return Number of haptic devices detected on the system. + */ +extern DECLSPEC int SDLCALL SDL_NumHaptics(void); + +/** + * \brief Get the implementation dependent name of a Haptic device. + * + * This can be called before any joysticks are opened. + * If no name can be found, this function returns NULL. + * + * \param device_index Index of the device to get it's name. + * \return Name of the device or NULL on error. + * + * \sa SDL_NumHaptics + */ +extern DECLSPEC const char *SDLCALL SDL_HapticName(int device_index); + +/** + * \brief Opens a Haptic device for usage. + * + * The index passed as an argument refers to the N'th Haptic device on this + * system. + * + * When opening a haptic device, it's gain will be set to maximum and + * autocenter will be disabled. To modify these values use + * SDL_HapticSetGain() and SDL_HapticSetAutocenter(). + * + * \param device_index Index of the device to open. + * \return Device identifier or NULL on error. + * + * \sa SDL_HapticIndex + * \sa SDL_HapticOpenFromMouse + * \sa SDL_HapticOpenFromJoystick + * \sa SDL_HapticClose + * \sa SDL_HapticSetGain + * \sa SDL_HapticSetAutocenter + * \sa SDL_HapticPause + * \sa SDL_HapticStopAll + */ +extern DECLSPEC SDL_Haptic *SDLCALL SDL_HapticOpen(int device_index); + +/** + * \brief Checks if the haptic device at index has been opened. + * + * \param device_index Index to check to see if it has been opened. + * \return 1 if it has been opened or 0 if it hasn't. + * + * \sa SDL_HapticOpen + * \sa SDL_HapticIndex + */ +extern DECLSPEC int SDLCALL SDL_HapticOpened(int device_index); + +/** + * \brief Gets the index of a haptic device. + * + * \param haptic Haptic device to get the index of. + * \return The index of the haptic device or -1 on error. + * + * \sa SDL_HapticOpen + * \sa SDL_HapticOpened + */ +extern DECLSPEC int SDLCALL SDL_HapticIndex(SDL_Haptic * haptic); + +/** + * \brief Gets whether or not the current mouse has haptic capabilities. + * + * \return SDL_TRUE if the mouse is haptic, SDL_FALSE if it isn't. + * + * \sa SDL_HapticOpenFromMouse + */ +extern DECLSPEC int SDLCALL SDL_MouseIsHaptic(void); + +/** + * \brief Tries to open a haptic device from the current mouse. + * + * \return The haptic device identifier or NULL on error. + * + * \sa SDL_MouseIsHaptic + * \sa SDL_HapticOpen + */ +extern DECLSPEC SDL_Haptic *SDLCALL SDL_HapticOpenFromMouse(void); + +/** + * \brief Checks to see if a joystick has haptic features. + * + * \param joystick Joystick to test for haptic capabilities. + * \return 1 if the joystick is haptic, 0 if it isn't + * or -1 if an error ocurred. + * + * \sa SDL_HapticOpenFromJoystick + */ +extern DECLSPEC int SDLCALL SDL_JoystickIsHaptic(SDL_Joystick * joystick); + +/** + * \brief Opens a Haptic device for usage from a Joystick device. + * + * You must still close the haptic device seperately. It will not be closed + * with the joystick. + * + * When opening from a joystick you should first close the haptic device before + * closing the joystick device. If not, on some implementations the haptic + * device will also get unallocated and you'll be unable to use force feedback + * on that device. + * + * \param joystick Joystick to create a haptic device from. + * \return A valid haptic device identifier on success or NULL on error. + * + * \sa SDL_HapticOpen + * \sa SDL_HapticClose + */ +extern DECLSPEC SDL_Haptic *SDLCALL SDL_HapticOpenFromJoystick(SDL_Joystick * + joystick); + +/** + * \brief Closes a Haptic device previously opened with SDL_HapticOpen(). + * + * \param haptic Haptic device to close. + */ +extern DECLSPEC void SDLCALL SDL_HapticClose(SDL_Haptic * haptic); + +/** + * \brief Returns the number of effects a haptic device can store. + * + * On some platforms this isn't fully supported, and therefore is an + * aproximation. Always check to see if your created effect was actually + * created and do not rely solely on SDL_HapticNumEffects(). + * + * \param haptic The haptic device to query effect max. + * \return The number of effects the haptic device can store or + * -1 on error. + * + * \sa SDL_HapticNumEffectsPlaying + * \sa SDL_HapticQuery + */ +extern DECLSPEC int SDLCALL SDL_HapticNumEffects(SDL_Haptic * haptic); + +/** + * \brief Returns the number of effects a haptic device can play at the same + * time. + * + * This is not supported on all platforms, but will always return a value. + * Added here for the sake of completness. + * + * \param haptic The haptic device to query maximum playing effects. + * \return The number of effects the haptic device can play at the same time + * or -1 on error. + * + * \sa SDL_HapticNumEffects + * \sa SDL_HapticQuery + */ +extern DECLSPEC int SDLCALL SDL_HapticNumEffectsPlaying(SDL_Haptic * haptic); + +/** + * \brief Gets the haptic devices supported features in bitwise matter. + * + * Example: + * \code + * if (SDL_HapticQueryEffects(haptic) & SDL_HAPTIC_CONSTANT) { + * printf("We have constant haptic effect!"); + * } + * \endcode + * + * \param haptic The haptic device to query. + * \return Haptic features in bitwise manner (OR'd). + * + * \sa SDL_HapticNumEffects + * \sa SDL_HapticEffectSupported + */ +extern DECLSPEC unsigned int SDLCALL SDL_HapticQuery(SDL_Haptic * haptic); + + +/** + * \brief Gets the number of haptic axes the device has. + * + * \sa SDL_HapticDirection + */ +extern DECLSPEC int SDLCALL SDL_HapticNumAxes(SDL_Haptic * haptic); + +/** + * \brief Checks to see if effect is supported by haptic. + * + * \param haptic Haptic device to check on. + * \param effect Effect to check to see if it is supported. + * \return SDL_TRUE if effect is supported, SDL_FALSE if it isn't or -1 on error. + * + * \sa SDL_HapticQuery + * \sa SDL_HapticNewEffect + */ +extern DECLSPEC int SDLCALL SDL_HapticEffectSupported(SDL_Haptic * haptic, + SDL_HapticEffect * + effect); + +/** + * \brief Creates a new haptic effect on the device. + * + * \param haptic Haptic device to create the effect on. + * \param effect Properties of the effect to create. + * \return The id of the effect on success or -1 on error. + * + * \sa SDL_HapticUpdateEffect + * \sa SDL_HapticRunEffect + * \sa SDL_HapticDestroyEffect + */ +extern DECLSPEC int SDLCALL SDL_HapticNewEffect(SDL_Haptic * haptic, + SDL_HapticEffect * effect); + +/** + * \brief Updates the properties of an effect. + * + * Can be used dynamically, although behaviour when dynamically changing + * direction may be strange. Specifically the effect may reupload itself + * and start playing from the start. You cannot change the type either when + * running SDL_HapticUpdateEffect(). + * + * \param haptic Haptic device that has the effect. + * \param effect Effect to update. + * \param data New effect properties to use. + * \return The id of the effect on success or -1 on error. + * + * \sa SDL_HapticNewEffect + * \sa SDL_HapticRunEffect + * \sa SDL_HapticDestroyEffect + */ +extern DECLSPEC int SDLCALL SDL_HapticUpdateEffect(SDL_Haptic * haptic, + int effect, + SDL_HapticEffect * data); + +/** + * \brief Runs the haptic effect on it's assosciated haptic device. + * + * If iterations are ::SDL_HAPTIC_INFINITY, it'll run the effect over and over + * repeating the envelope (attack and fade) every time. If you only want the + * effect to last forever, set ::SDL_HAPTIC_INFINITY in the effect's length + * parameter. + * + * \param haptic Haptic device to run the effect on. + * \param effect Identifier of the haptic effect to run. + * \param iterations Number of iterations to run the effect. Use + * ::SDL_HAPTIC_INFINITY for infinity. + * \return 0 on success or -1 on error. + * + * \sa SDL_HapticStopEffect + * \sa SDL_HapticDestroyEffect + * \sa SDL_HapticGetEffectStatus + */ +extern DECLSPEC int SDLCALL SDL_HapticRunEffect(SDL_Haptic * haptic, + int effect, + Uint32 iterations); + +/** + * \brief Stops the haptic effect on it's assosciated haptic device. + * + * \param haptic Haptic device to stop the effect on. + * \param effect Identifier of the effect to stop. + * \return 0 on success or -1 on error. + * + * \sa SDL_HapticRunEffect + * \sa SDL_HapticDestroyEffect + */ +extern DECLSPEC int SDLCALL SDL_HapticStopEffect(SDL_Haptic * haptic, + int effect); + +/** + * \brief Destroys a haptic effect on the device. + * + * This will stop the effect if it's running. Effects are automatically + * destroyed when the device is closed. + * + * \param haptic Device to destroy the effect on. + * \param effect Identifier of the effect to destroy. + * + * \sa SDL_HapticNewEffect + */ +extern DECLSPEC void SDLCALL SDL_HapticDestroyEffect(SDL_Haptic * haptic, + int effect); + +/** + * \brief Gets the status of the current effect on the haptic device. + * + * Device must support the ::SDL_HAPTIC_STATUS feature. + * + * \param haptic Haptic device to query the effect status on. + * \param effect Identifier of the effect to query it's status. + * \return 0 if it isn't playing, ::SDL_HAPTIC_PLAYING if it is playing + * or -1 on error. + * + * \sa SDL_HapticRunEffect + * \sa SDL_HapticStopEffect + */ +extern DECLSPEC int SDLCALL SDL_HapticGetEffectStatus(SDL_Haptic * haptic, + int effect); + +/** + * \brief Sets the global gain of the device. + * + * Device must support the ::SDL_HAPTIC_GAIN feature. + * + * The user may specify the maxmimum gain by setting the environment variable + * ::SDL_HAPTIC_GAIN_MAX which should be between 0 and 100. All calls to + * SDL_HapticSetGain() will scale linearly using ::SDL_HAPTIC_GAIN_MAX as the + * maximum. + * + * \param haptic Haptic device to set the gain on. + * \param gain Value to set the gain to, should be between 0 and 100. + * \return 0 on success or -1 on error. + * + * \sa SDL_HapticQuery + */ +extern DECLSPEC int SDLCALL SDL_HapticSetGain(SDL_Haptic * haptic, int gain); + +/** + * \brief Sets the global autocenter of the device. + * + * Autocenter should be between 0 and 100. Setting it to 0 will disable + * autocentering. + * + * Device must support the ::SDL_HAPTIC_AUTOCENTER feature. + * + * \param haptic Haptic device to set autocentering on. + * \param autocenter Value to set autocenter to, 0 disables autocentering. + * \return 0 on success or -1 on error. + * + * \sa SDL_HapticQuery + */ +extern DECLSPEC int SDLCALL SDL_HapticSetAutocenter(SDL_Haptic * haptic, + int autocenter); + +/** + * \brief Pauses a haptic device. + * + * Device must support the ::SDL_HAPTIC_PAUSE feature. Call + * SDL_HapticUnpause() to resume playback. + * + * Do not modify the effects nor add new ones while the device is paused. + * That can cause all sorts of weird errors. + * + * \param haptic Haptic device to pause. + * \return 0 on success or -1 on error. + * + * \sa SDL_HapticUnpause + */ +extern DECLSPEC int SDLCALL SDL_HapticPause(SDL_Haptic * haptic); + +/** + * \brief Unpauses a haptic device. + * + * Call to unpause after SDL_HapticPause(). + * + * \param haptic Haptic device to pause. + * \return 0 on success or -1 on error. + * + * \sa SDL_HapticPause + */ +extern DECLSPEC int SDLCALL SDL_HapticUnpause(SDL_Haptic * haptic); + +/** + * \brief Stops all the currently playing effects on a haptic device. + * + * \param haptic Haptic device to stop. + * \return 0 on success or -1 on error. + */ +extern DECLSPEC int SDLCALL SDL_HapticStopAll(SDL_Haptic * haptic); + +/** + * \brief Checks to see if rumble is supported on a haptic device.. + * + * \param haptic Haptic device to check to see if it supports rumble. + * \return SDL_TRUE if effect is supported, SDL_FALSE if it isn't or -1 on error. + * + * \sa SDL_HapticRumbleInit + * \sa SDL_HapticRumblePlay + * \sa SDL_HapticRumbleStop + */ +extern DECLSPEC int SDLCALL SDL_HapticRumbleSupported(SDL_Haptic * haptic); + +/** + * \brief Initializes the haptic device for simple rumble playback. + * + * \param haptic Haptic device to initialize for simple rumble playback. + * \return 0 on success or -1 on error. + * + * \sa SDL_HapticOpen + * \sa SDL_HapticRumbleSupported + * \sa SDL_HapticRumblePlay + * \sa SDL_HapticRumbleStop + */ +extern DECLSPEC int SDLCALL SDL_HapticRumbleInit(SDL_Haptic * haptic); + +/** + * \brief Runs simple rumble on a haptic device + * + * \param haptic Haptic device to play rumble effect on. + * \param strength Strength of the rumble to play as a 0-1 float value. + * \param length Length of the rumble to play in miliseconds. + * \return 0 on success or -1 on error. + * + * \sa SDL_HapticRumbleSupported + * \sa SDL_HapticRumbleInit + * \sa SDL_HapticRumbleStop + */ +extern DECLSPEC int SDLCALL SDL_HapticRumblePlay(SDL_Haptic * haptic, float strength, Uint32 length ); + +/** + * \brief Stops the simple rumble on a haptic device. + * + * \param haptic Haptic to stop the rumble on. + * \return 0 on success or -1 on error. + * + * \sa SDL_HapticRumbleSupported + * \sa SDL_HapticRumbleInit + * \sa SDL_HapticRumblePlay + */ +extern DECLSPEC int SDLCALL SDL_HapticRumbleStop(SDL_Haptic * haptic); + + + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif +#include "close_code.h" + +#endif /* _SDL_haptic_h */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_hints.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_hints.h new file mode 100644 index 0000000..ed76ce5 --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_hints.h @@ -0,0 +1,181 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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. +*/ + +/** + * \file SDL_hints.h + * + * Official documentation for SDL configuration variables + * + * This file contains functions to set and get configuration hints, + * as well as listing each of them alphabetically. + * + * The convention for naming hints is SDL_HINT_X, where "SDL_X" is + * the environment variable that can be used to override the default. + * + * In general these hints are just that - they may or may not be + * supported or applicable on any given platform, but they provide + * a way for an application or user to give the library a hint as + * to how they would like the library to work. + */ + +#ifndef _SDL_hints_h +#define _SDL_hints_h + +#include "SDL_stdinc.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +/** + * \brief A variable controlling how 3D acceleration is used to accelerate the SDL 1.2 screen surface. + * + * SDL can try to accelerate the SDL 1.2 screen surface by using streaming + * textures with a 3D rendering engine. This variable controls whether and + * how this is done. + * + * This variable can be set to the following values: + * "0" - Disable 3D acceleration + * "1" - Enable 3D acceleration, using the default renderer. + * "X" - Enable 3D acceleration, using X where X is one of the valid rendering drivers. (e.g. "direct3d", "opengl", etc.) + * + * By default SDL tries to make a best guess for each platform whether + * to use acceleration or not. + */ +#define SDL_HINT_FRAMEBUFFER_ACCELERATION "SDL_FRAMEBUFFER_ACCELERATION" + +/** + * \brief A variable specifying which render driver to use. + * + * If the application doesn't pick a specific renderer to use, this variable + * specifies the name of the preferred renderer. If the preferred renderer + * can't be initialized, the normal default renderer is used. + * + * This variable is case insensitive and can be set to the following values: + * "direct3d" + * "opengl" + * "opengles2" + * "opengles" + * "software" + * + * The default varies by platform, but it's the first one in the list that + * is available on the current platform. + */ +#define SDL_HINT_RENDER_DRIVER "SDL_RENDER_DRIVER" + +/** + * \brief A variable controlling whether the OpenGL render driver uses shaders if they are available. + * + * This variable can be set to the following values: + * "0" - Disable shaders + * "1" - Enable shaders + * + * By default shaders are used if OpenGL supports them. + */ +#define SDL_HINT_RENDER_OPENGL_SHADERS "SDL_RENDER_OPENGL_SHADERS" + +/** + * \brief A variable controlling the scaling quality + * + * This variable can be set to the following values: + * "0" or "nearest" - Nearest pixel sampling + * "1" or "linear" - Linear filtering (supported by OpenGL and Direct3D) + * "2" or "best" - Anisotropic filtering (supported by Direct3D) + * + * By default nearest pixel sampling is used + */ +#define SDL_HINT_RENDER_SCALE_QUALITY "SDL_RENDER_SCALE_QUALITY" + +/** + * \brief A variable controlling whether updates to the SDL 1.2 screen surface should be synchronized with the vertical refresh, to avoid tearing. + * + * This variable can be set to the following values: + * "0" - Disable vsync + * "1" - Enable vsync + * + * By default SDL does not sync screen surface updates with vertical refresh. + */ +#define SDL_HINT_RENDER_VSYNC "SDL_RENDER_VSYNC" + + +/** + * \brief An enumeration of hint priorities + */ +typedef enum +{ + SDL_HINT_DEFAULT, + SDL_HINT_NORMAL, + SDL_HINT_OVERRIDE +} SDL_HintPriority; + + +/** + * \brief Set a hint with a specific priority + * + * The priority controls the behavior when setting a hint that already + * has a value. Hints will replace existing hints of their priority and + * lower. Environment variables are considered to have override priority. + * + * \return SDL_TRUE if the hint was set, SDL_FALSE otherwise + */ +extern DECLSPEC SDL_bool SDLCALL SDL_SetHintWithPriority(const char *name, + const char *value, + SDL_HintPriority priority); + +/** + * \brief Set a hint with normal priority + * + * \return SDL_TRUE if the hint was set, SDL_FALSE otherwise + */ +extern DECLSPEC SDL_bool SDLCALL SDL_SetHint(const char *name, + const char *value); + + +/** + * \brief Get a hint + * + * \return The string value of a hint variable. + */ +extern DECLSPEC const char * SDLCALL SDL_GetHint(const char *name); + +/** + * \brief Clear all hints + * + * This function is called during SDL_Quit() to free stored hints. + */ +extern DECLSPEC void SDLCALL SDL_ClearHints(void); + + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif +#include "close_code.h" + +#endif /* _SDL_hints_h */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_input.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_input.h new file mode 100644 index 0000000..354d4d3 --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_input.h @@ -0,0 +1,87 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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. +*/ + +/** + * \file SDL_input.h + * + * Include file for lowlevel SDL input device handling. + * + * This talks about individual devices, and not the system cursor. If you + * just want to know when the user moves the pointer somewhere in your + * window, this is NOT the API you want. This one handles things like + * multi-touch, drawing tablets, and multiple, separate mice. + * + * The other API is in SDL_mouse.h + */ + +#ifndef _SDL_input_h +#define _SDL_input_h + +#include "SDL_stdinc.h" +#include "SDL_error.h" +#include "SDL_video.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + + +/* Function prototypes */ + +/* !!! FIXME: real documentation + * - Redetect devices + * - This invalidates all existing device information from previous queries! + * - There is an implicit (re)detect upon SDL_Init(). + */ +extern DECLSPEC int SDLCALL SDL_RedetectInputDevices(void); + +/** + * \brief Get the number of mouse input devices available. + */ +extern DECLSPEC int SDLCALL SDL_GetNumInputDevices(void); + +/** + * \brief Gets the name of a device with the given index. + * + * \param index is the index of the device, whose name is to be returned. + * + * \return the name of the device with the specified index + */ +extern DECLSPEC const char *SDLCALL SDL_GetInputDeviceName(int index); + + +extern DECLSPEC int SDLCALL SDL_IsDeviceDisconnected(int index); + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif +#include "close_code.h" + +#endif /* _SDL_mouse_h */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_joystick.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_joystick.h new file mode 100644 index 0000000..e83726a --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_joystick.h @@ -0,0 +1,208 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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. +*/ + +/** + * \file SDL_joystick.h + * + * Include file for SDL joystick event handling + */ + +#ifndef _SDL_joystick_h +#define _SDL_joystick_h + +#include "SDL_stdinc.h" +#include "SDL_error.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +/** + * \file SDL_joystick.h + * + * In order to use these functions, SDL_Init() must have been called + * with the ::SDL_INIT_JOYSTICK flag. This causes SDL to scan the system + * for joysticks, and load appropriate drivers. + */ + +/* The joystick structure used to identify an SDL joystick */ +struct _SDL_Joystick; +typedef struct _SDL_Joystick SDL_Joystick; + + +/* Function prototypes */ +/** + * Count the number of joysticks attached to the system + */ +extern DECLSPEC int SDLCALL SDL_NumJoysticks(void); + +/** + * Get the implementation dependent name of a joystick. + * This can be called before any joysticks are opened. + * If no name can be found, this function returns NULL. + */ +extern DECLSPEC const char *SDLCALL SDL_JoystickName(int device_index); + +/** + * Open a joystick for use. + * The index passed as an argument refers tothe N'th joystick on the system. + * This index is the value which will identify this joystick in future joystick + * events. + * + * \return A joystick identifier, or NULL if an error occurred. + */ +extern DECLSPEC SDL_Joystick *SDLCALL SDL_JoystickOpen(int device_index); + +/** + * Returns 1 if the joystick has been opened, or 0 if it has not. + */ +extern DECLSPEC int SDLCALL SDL_JoystickOpened(int device_index); + +/** + * Get the device index of an opened joystick. + */ +extern DECLSPEC int SDLCALL SDL_JoystickIndex(SDL_Joystick * joystick); + +/** + * Get the number of general axis controls on a joystick. + */ +extern DECLSPEC int SDLCALL SDL_JoystickNumAxes(SDL_Joystick * joystick); + +/** + * Get the number of trackballs on a joystick. + * + * Joystick trackballs have only relative motion events associated + * with them and their state cannot be polled. + */ +extern DECLSPEC int SDLCALL SDL_JoystickNumBalls(SDL_Joystick * joystick); + +/** + * Get the number of POV hats on a joystick. + */ +extern DECLSPEC int SDLCALL SDL_JoystickNumHats(SDL_Joystick * joystick); + +/** + * Get the number of buttons on a joystick. + */ +extern DECLSPEC int SDLCALL SDL_JoystickNumButtons(SDL_Joystick * joystick); + +/** + * Update the current state of the open joysticks. + * + * This is called automatically by the event loop if any joystick + * events are enabled. + */ +extern DECLSPEC void SDLCALL SDL_JoystickUpdate(void); + +/** + * Enable/disable joystick event polling. + * + * If joystick events are disabled, you must call SDL_JoystickUpdate() + * yourself and check the state of the joystick when you want joystick + * information. + * + * The state can be one of ::SDL_QUERY, ::SDL_ENABLE or ::SDL_IGNORE. + */ +extern DECLSPEC int SDLCALL SDL_JoystickEventState(int state); + +/** + * Get the current state of an axis control on a joystick. + * + * The state is a value ranging from -32768 to 32767. + * + * The axis indices start at index 0. + */ +extern DECLSPEC Sint16 SDLCALL SDL_JoystickGetAxis(SDL_Joystick * joystick, + int axis); + +/** + * \name Hat positions + */ +/*@{*/ +#define SDL_HAT_CENTERED 0x00 +#define SDL_HAT_UP 0x01 +#define SDL_HAT_RIGHT 0x02 +#define SDL_HAT_DOWN 0x04 +#define SDL_HAT_LEFT 0x08 +#define SDL_HAT_RIGHTUP (SDL_HAT_RIGHT|SDL_HAT_UP) +#define SDL_HAT_RIGHTDOWN (SDL_HAT_RIGHT|SDL_HAT_DOWN) +#define SDL_HAT_LEFTUP (SDL_HAT_LEFT|SDL_HAT_UP) +#define SDL_HAT_LEFTDOWN (SDL_HAT_LEFT|SDL_HAT_DOWN) +/*@}*/ + +/** + * Get the current state of a POV hat on a joystick. + * + * The hat indices start at index 0. + * + * \return The return value is one of the following positions: + * - ::SDL_HAT_CENTERED + * - ::SDL_HAT_UP + * - ::SDL_HAT_RIGHT + * - ::SDL_HAT_DOWN + * - ::SDL_HAT_LEFT + * - ::SDL_HAT_RIGHTUP + * - ::SDL_HAT_RIGHTDOWN + * - ::SDL_HAT_LEFTUP + * - ::SDL_HAT_LEFTDOWN + */ +extern DECLSPEC Uint8 SDLCALL SDL_JoystickGetHat(SDL_Joystick * joystick, + int hat); + +/** + * Get the ball axis change since the last poll. + * + * \return 0, or -1 if you passed it invalid parameters. + * + * The ball indices start at index 0. + */ +extern DECLSPEC int SDLCALL SDL_JoystickGetBall(SDL_Joystick * joystick, + int ball, int *dx, int *dy); + +/** + * Get the current state of a button on a joystick. + * + * The button indices start at index 0. + */ +extern DECLSPEC Uint8 SDLCALL SDL_JoystickGetButton(SDL_Joystick * joystick, + int button); + +/** + * Close a joystick previously opened with SDL_JoystickOpen(). + */ +extern DECLSPEC void SDLCALL SDL_JoystickClose(SDL_Joystick * joystick); + + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif +#include "close_code.h" + +#endif /* _SDL_joystick_h */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_keyboard.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_keyboard.h new file mode 100644 index 0000000..c390cb6 --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_keyboard.h @@ -0,0 +1,169 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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. +*/ + +/** + * \file SDL_keyboard.h + * + * Include file for SDL keyboard event handling + */ + +#ifndef _SDL_keyboard_h +#define _SDL_keyboard_h + +#include "SDL_stdinc.h" +#include "SDL_error.h" +#include "SDL_keycode.h" +#include "SDL_video.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +/** + * \brief The SDL keysym structure, used in key events. + */ +typedef struct SDL_Keysym +{ + SDL_Scancode scancode; /**< SDL physical key code - see ::SDL_Scancode for details */ + SDL_Keycode sym; /**< SDL virtual key code - see ::SDL_Keycode for details */ + Uint16 mod; /**< current key modifiers */ + Uint32 unicode; /**< \deprecated use SDL_TextInputEvent instead */ +} SDL_Keysym; + +/* Function prototypes */ + +/** + * \brief Get the window which currently has keyboard focus. + */ +extern DECLSPEC SDL_Window * SDLCALL SDL_GetKeyboardFocus(void); + +/** + * \brief Get a snapshot of the current state of the keyboard. + * + * \param numkeys if non-NULL, receives the length of the returned array. + * + * \return An array of key states. Indexes into this array are obtained by using ::SDL_Scancode values. + * + * \b Example: + * \code + * Uint8 *state = SDL_GetKeyboardState(NULL); + * if ( state[SDL_SCANCODE_RETURN] ) { + * printf(" is pressed.\n"); + * } + * \endcode + */ +extern DECLSPEC Uint8 *SDLCALL SDL_GetKeyboardState(int *numkeys); + +/** + * \brief Get the current key modifier state for the keyboard. + */ +extern DECLSPEC SDL_Keymod SDLCALL SDL_GetModState(void); + +/** + * \brief Set the current key modifier state for the keyboard. + * + * \note This does not change the keyboard state, only the key modifier flags. + */ +extern DECLSPEC void SDLCALL SDL_SetModState(SDL_Keymod modstate); + +/** + * \brief Get the key code corresponding to the given scancode according + * to the current keyboard layout. + * + * See ::SDL_Keycode for details. + * + * \sa SDL_GetKeyName() + */ +extern DECLSPEC SDL_Keycode SDLCALL SDL_GetKeyFromScancode(SDL_Scancode scancode); + +/** + * \brief Get the scancode corresponding to the given key code according to the + * current keyboard layout. + * + * See ::SDL_Scancode for details. + * + * \sa SDL_GetScancodeName() + */ +extern DECLSPEC SDL_Scancode SDLCALL SDL_GetScancodeFromKey(SDL_Keycode key); + +/** + * \brief Get a human-readable name for a scancode. + * + * \return A pointer to a UTF-8 string that stays valid at least until the next + * call to this function. If you need it around any longer, you must + * copy it. If the scancode doesn't have a name, this function returns + * an empty string (""). + * + * \sa SDL_Scancode + */ +extern DECLSPEC const char *SDLCALL SDL_GetScancodeName(SDL_Scancode + scancode); + +/** + * \brief Get a human-readable name for a key. + * + * \return A pointer to a UTF-8 string that stays valid at least until the next + * call to this function. If you need it around any longer, you must + * copy it. If the key doesn't have a name, this function returns an + * empty string (""). + * + * \sa SDL_Key + */ +extern DECLSPEC const char *SDLCALL SDL_GetKeyName(SDL_Keycode key); + +/** + * \brief Start accepting Unicode text input events. + * + * \sa SDL_StopTextInput() + * \sa SDL_SetTextInputRect() + */ +extern DECLSPEC void SDLCALL SDL_StartTextInput(void); + +/** + * \brief Stop receiving any text input events. + * + * \sa SDL_StartTextInput() + */ +extern DECLSPEC void SDLCALL SDL_StopTextInput(void); + +/** + * \brief Set the rectangle used to type Unicode text inputs. + * + * \sa SDL_StartTextInput() + */ +extern DECLSPEC void SDLCALL SDL_SetTextInputRect(SDL_Rect *rect); + + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif +#include "close_code.h" + +#endif /* _SDL_keyboard_h */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_keycode.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_keycode.h new file mode 100644 index 0000000..5be1f71 --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_keycode.h @@ -0,0 +1,341 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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. +*/ + +/** + * \file SDL_keycode.h + * + * Defines constants which identify keyboard keys and modifiers. + */ + +#ifndef _SDL_keycode_h +#define _SDL_keycode_h + +#include "SDL_stdinc.h" +#include "SDL_scancode.h" + +/** + * \brief The SDL virtual key representation. + * + * Values of this type are used to represent keyboard keys using the current + * layout of the keyboard. These values include Unicode values representing + * the unmodified character that would be generated by pressing the key, or + * an SDLK_* constant for those keys that do not generate characters. + */ +typedef Sint32 SDL_Keycode; + +#define SDLK_SCANCODE_MASK (1<<30) +#define SDL_SCANCODE_TO_KEYCODE(X) (X | SDLK_SCANCODE_MASK) + +enum +{ + SDLK_UNKNOWN = 0, + + SDLK_RETURN = '\r', + SDLK_ESCAPE = '\033', + SDLK_BACKSPACE = '\b', + SDLK_TAB = '\t', + SDLK_SPACE = ' ', + SDLK_EXCLAIM = '!', + SDLK_QUOTEDBL = '"', + SDLK_HASH = '#', + SDLK_PERCENT = '%', + SDLK_DOLLAR = '$', + SDLK_AMPERSAND = '&', + SDLK_QUOTE = '\'', + SDLK_LEFTPAREN = '(', + SDLK_RIGHTPAREN = ')', + SDLK_ASTERISK = '*', + SDLK_PLUS = '+', + SDLK_COMMA = ',', + SDLK_MINUS = '-', + SDLK_PERIOD = '.', + SDLK_SLASH = '/', + SDLK_0 = '0', + SDLK_1 = '1', + SDLK_2 = '2', + SDLK_3 = '3', + SDLK_4 = '4', + SDLK_5 = '5', + SDLK_6 = '6', + SDLK_7 = '7', + SDLK_8 = '8', + SDLK_9 = '9', + SDLK_COLON = ':', + SDLK_SEMICOLON = ';', + SDLK_LESS = '<', + SDLK_EQUALS = '=', + SDLK_GREATER = '>', + SDLK_QUESTION = '?', + SDLK_AT = '@', + /* + Skip uppercase letters + */ + SDLK_LEFTBRACKET = '[', + SDLK_BACKSLASH = '\\', + SDLK_RIGHTBRACKET = ']', + SDLK_CARET = '^', + SDLK_UNDERSCORE = '_', + SDLK_BACKQUOTE = '`', + SDLK_a = 'a', + SDLK_b = 'b', + SDLK_c = 'c', + SDLK_d = 'd', + SDLK_e = 'e', + SDLK_f = 'f', + SDLK_g = 'g', + SDLK_h = 'h', + SDLK_i = 'i', + SDLK_j = 'j', + SDLK_k = 'k', + SDLK_l = 'l', + SDLK_m = 'm', + SDLK_n = 'n', + SDLK_o = 'o', + SDLK_p = 'p', + SDLK_q = 'q', + SDLK_r = 'r', + SDLK_s = 's', + SDLK_t = 't', + SDLK_u = 'u', + SDLK_v = 'v', + SDLK_w = 'w', + SDLK_x = 'x', + SDLK_y = 'y', + SDLK_z = 'z', + + SDLK_CAPSLOCK = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_CAPSLOCK), + + SDLK_F1 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F1), + SDLK_F2 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F2), + SDLK_F3 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F3), + SDLK_F4 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F4), + SDLK_F5 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F5), + SDLK_F6 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F6), + SDLK_F7 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F7), + SDLK_F8 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F8), + SDLK_F9 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F9), + SDLK_F10 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F10), + SDLK_F11 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F11), + SDLK_F12 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F12), + + SDLK_PRINTSCREEN = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_PRINTSCREEN), + SDLK_SCROLLLOCK = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_SCROLLLOCK), + SDLK_PAUSE = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_PAUSE), + SDLK_INSERT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_INSERT), + SDLK_HOME = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_HOME), + SDLK_PAGEUP = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_PAGEUP), + SDLK_DELETE = '\177', + SDLK_END = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_END), + SDLK_PAGEDOWN = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_PAGEDOWN), + SDLK_RIGHT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_RIGHT), + SDLK_LEFT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_LEFT), + SDLK_DOWN = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_DOWN), + SDLK_UP = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_UP), + + SDLK_NUMLOCKCLEAR = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_NUMLOCKCLEAR), + SDLK_KP_DIVIDE = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_DIVIDE), + SDLK_KP_MULTIPLY = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_MULTIPLY), + SDLK_KP_MINUS = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_MINUS), + SDLK_KP_PLUS = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_PLUS), + SDLK_KP_ENTER = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_ENTER), + SDLK_KP_1 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_1), + SDLK_KP_2 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_2), + SDLK_KP_3 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_3), + SDLK_KP_4 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_4), + SDLK_KP_5 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_5), + SDLK_KP_6 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_6), + SDLK_KP_7 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_7), + SDLK_KP_8 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_8), + SDLK_KP_9 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_9), + SDLK_KP_0 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_0), + SDLK_KP_PERIOD = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_PERIOD), + + SDLK_APPLICATION = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_APPLICATION), + SDLK_POWER = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_POWER), + SDLK_KP_EQUALS = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_EQUALS), + SDLK_F13 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F13), + SDLK_F14 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F14), + SDLK_F15 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F15), + SDLK_F16 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F16), + SDLK_F17 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F17), + SDLK_F18 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F18), + SDLK_F19 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F19), + SDLK_F20 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F20), + SDLK_F21 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F21), + SDLK_F22 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F22), + SDLK_F23 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F23), + SDLK_F24 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F24), + SDLK_EXECUTE = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_EXECUTE), + SDLK_HELP = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_HELP), + SDLK_MENU = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_MENU), + SDLK_SELECT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_SELECT), + SDLK_STOP = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_STOP), + SDLK_AGAIN = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AGAIN), + SDLK_UNDO = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_UNDO), + SDLK_CUT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_CUT), + SDLK_COPY = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_COPY), + SDLK_PASTE = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_PASTE), + SDLK_FIND = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_FIND), + SDLK_MUTE = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_MUTE), + SDLK_VOLUMEUP = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_VOLUMEUP), + SDLK_VOLUMEDOWN = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_VOLUMEDOWN), + SDLK_KP_COMMA = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_COMMA), + SDLK_KP_EQUALSAS400 = + SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_EQUALSAS400), + + SDLK_ALTERASE = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_ALTERASE), + SDLK_SYSREQ = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_SYSREQ), + SDLK_CANCEL = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_CANCEL), + SDLK_CLEAR = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_CLEAR), + SDLK_PRIOR = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_PRIOR), + SDLK_RETURN2 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_RETURN2), + SDLK_SEPARATOR = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_SEPARATOR), + SDLK_OUT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_OUT), + SDLK_OPER = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_OPER), + SDLK_CLEARAGAIN = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_CLEARAGAIN), + SDLK_CRSEL = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_CRSEL), + SDLK_EXSEL = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_EXSEL), + + SDLK_KP_00 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_00), + SDLK_KP_000 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_000), + SDLK_THOUSANDSSEPARATOR = + SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_THOUSANDSSEPARATOR), + SDLK_DECIMALSEPARATOR = + SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_DECIMALSEPARATOR), + SDLK_CURRENCYUNIT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_CURRENCYUNIT), + SDLK_CURRENCYSUBUNIT = + SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_CURRENCYSUBUNIT), + SDLK_KP_LEFTPAREN = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_LEFTPAREN), + SDLK_KP_RIGHTPAREN = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_RIGHTPAREN), + SDLK_KP_LEFTBRACE = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_LEFTBRACE), + SDLK_KP_RIGHTBRACE = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_RIGHTBRACE), + SDLK_KP_TAB = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_TAB), + SDLK_KP_BACKSPACE = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_BACKSPACE), + SDLK_KP_A = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_A), + SDLK_KP_B = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_B), + SDLK_KP_C = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_C), + SDLK_KP_D = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_D), + SDLK_KP_E = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_E), + SDLK_KP_F = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_F), + SDLK_KP_XOR = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_XOR), + SDLK_KP_POWER = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_POWER), + SDLK_KP_PERCENT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_PERCENT), + SDLK_KP_LESS = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_LESS), + SDLK_KP_GREATER = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_GREATER), + SDLK_KP_AMPERSAND = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_AMPERSAND), + SDLK_KP_DBLAMPERSAND = + SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_DBLAMPERSAND), + SDLK_KP_VERTICALBAR = + SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_VERTICALBAR), + SDLK_KP_DBLVERTICALBAR = + SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_DBLVERTICALBAR), + SDLK_KP_COLON = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_COLON), + SDLK_KP_HASH = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_HASH), + SDLK_KP_SPACE = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_SPACE), + SDLK_KP_AT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_AT), + SDLK_KP_EXCLAM = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_EXCLAM), + SDLK_KP_MEMSTORE = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_MEMSTORE), + SDLK_KP_MEMRECALL = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_MEMRECALL), + SDLK_KP_MEMCLEAR = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_MEMCLEAR), + SDLK_KP_MEMADD = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_MEMADD), + SDLK_KP_MEMSUBTRACT = + SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_MEMSUBTRACT), + SDLK_KP_MEMMULTIPLY = + SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_MEMMULTIPLY), + SDLK_KP_MEMDIVIDE = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_MEMDIVIDE), + SDLK_KP_PLUSMINUS = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_PLUSMINUS), + SDLK_KP_CLEAR = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_CLEAR), + SDLK_KP_CLEARENTRY = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_CLEARENTRY), + SDLK_KP_BINARY = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_BINARY), + SDLK_KP_OCTAL = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_OCTAL), + SDLK_KP_DECIMAL = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_DECIMAL), + SDLK_KP_HEXADECIMAL = + SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_HEXADECIMAL), + + SDLK_LCTRL = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_LCTRL), + SDLK_LSHIFT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_LSHIFT), + SDLK_LALT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_LALT), + SDLK_LGUI = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_LGUI), + SDLK_RCTRL = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_RCTRL), + SDLK_RSHIFT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_RSHIFT), + SDLK_RALT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_RALT), + SDLK_RGUI = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_RGUI), + + SDLK_MODE = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_MODE), + + SDLK_AUDIONEXT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AUDIONEXT), + SDLK_AUDIOPREV = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AUDIOPREV), + SDLK_AUDIOSTOP = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AUDIOSTOP), + SDLK_AUDIOPLAY = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AUDIOPLAY), + SDLK_AUDIOMUTE = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AUDIOMUTE), + SDLK_MEDIASELECT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_MEDIASELECT), + SDLK_WWW = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_WWW), + SDLK_MAIL = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_MAIL), + SDLK_CALCULATOR = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_CALCULATOR), + SDLK_COMPUTER = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_COMPUTER), + SDLK_AC_SEARCH = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AC_SEARCH), + SDLK_AC_HOME = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AC_HOME), + SDLK_AC_BACK = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AC_BACK), + SDLK_AC_FORWARD = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AC_FORWARD), + SDLK_AC_STOP = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AC_STOP), + SDLK_AC_REFRESH = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AC_REFRESH), + SDLK_AC_BOOKMARKS = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AC_BOOKMARKS), + + SDLK_BRIGHTNESSDOWN = + SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_BRIGHTNESSDOWN), + SDLK_BRIGHTNESSUP = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_BRIGHTNESSUP), + SDLK_DISPLAYSWITCH = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_DISPLAYSWITCH), + SDLK_KBDILLUMTOGGLE = + SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KBDILLUMTOGGLE), + SDLK_KBDILLUMDOWN = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KBDILLUMDOWN), + SDLK_KBDILLUMUP = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KBDILLUMUP), + SDLK_EJECT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_EJECT), + SDLK_SLEEP = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_SLEEP) +}; + +/** + * \brief Enumeration of valid key mods (possibly OR'd together). + */ +typedef enum +{ + KMOD_NONE = 0x0000, + KMOD_LSHIFT = 0x0001, + KMOD_RSHIFT = 0x0002, + KMOD_LCTRL = 0x0040, + KMOD_RCTRL = 0x0080, + KMOD_LALT = 0x0100, + KMOD_RALT = 0x0200, + KMOD_LGUI = 0x0400, + KMOD_RGUI = 0x0800, + KMOD_NUM = 0x1000, + KMOD_CAPS = 0x2000, + KMOD_MODE = 0x4000, + KMOD_RESERVED = 0x8000 +} SDL_Keymod; + +#define KMOD_CTRL (KMOD_LCTRL|KMOD_RCTRL) +#define KMOD_SHIFT (KMOD_LSHIFT|KMOD_RSHIFT) +#define KMOD_ALT (KMOD_LALT|KMOD_RALT) +#define KMOD_GUI (KMOD_LGUI|KMOD_RGUI) + +#endif /* _SDL_keycode_h */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_loadso.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_loadso.h new file mode 100644 index 0000000..821ca2d --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_loadso.h @@ -0,0 +1,85 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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. +*/ + +/** + * \file SDL_loadso.h + * + * System dependent library loading routines + * + * Some things to keep in mind: + * \li These functions only work on C function names. Other languages may + * have name mangling and intrinsic language support that varies from + * compiler to compiler. + * \li Make sure you declare your function pointers with the same calling + * convention as the actual library function. Your code will crash + * mysteriously if you do not do this. + * \li Avoid namespace collisions. If you load a symbol from the library, + * it is not defined whether or not it goes into the global symbol + * namespace for the application. If it does and it conflicts with + * symbols in your code or other shared libraries, you will not get + * the results you expect. :) + */ + +#ifndef _SDL_loadso_h +#define _SDL_loadso_h + +#include "SDL_stdinc.h" +#include "SDL_error.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +/** + * This function dynamically loads a shared object and returns a pointer + * to the object handle (or NULL if there was an error). + * The 'sofile' parameter is a system dependent name of the object file. + */ +extern DECLSPEC void *SDLCALL SDL_LoadObject(const char *sofile); + +/** + * Given an object handle, this function looks up the address of the + * named function in the shared object and returns it. This address + * is no longer valid after calling SDL_UnloadObject(). + */ +extern DECLSPEC void *SDLCALL SDL_LoadFunction(void *handle, + const char *name); + +/** + * Unload a shared object from memory. + */ +extern DECLSPEC void SDLCALL SDL_UnloadObject(void *handle); + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif +#include "close_code.h" + +#endif /* _SDL_loadso_h */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_log.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_log.h new file mode 100644 index 0000000..b48e246 --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_log.h @@ -0,0 +1,211 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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. +*/ + +/** + * \file SDL_log.h + * + * Simple log messages with categories and priorities. + * + * By default logs are quiet, but if you're debugging SDL you might want: + * + * SDL_LogSetAllPriority(SDL_LOG_PRIORITY_WARN); + * + * Here's where the messages go on different platforms: + * Windows: debug output stream + * Android: log output + * Others: standard error output (stderr) + */ + +#ifndef _SDL_log_h +#define _SDL_log_h + +#include "SDL_stdinc.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + + +/** + * \brief The maximum size of a log message + * + * Messages longer than the maximum size will be truncated + */ +#define SDL_MAX_LOG_MESSAGE 4096 + +/** + * \brief The predefined log categories + * + * By default the application category is enabled at the INFO level, + * and all other categories are enabled at the CRITICAL level. + */ +enum +{ + SDL_LOG_CATEGORY_APPLICATION, + SDL_LOG_CATEGORY_ERROR, + SDL_LOG_CATEGORY_SYSTEM, + SDL_LOG_CATEGORY_AUDIO, + SDL_LOG_CATEGORY_VIDEO, + SDL_LOG_CATEGORY_RENDER, + SDL_LOG_CATEGORY_INPUT, + + /* Reserved for future SDL library use */ + SDL_LOG_CATEGORY_RESERVED1, + SDL_LOG_CATEGORY_RESERVED2, + SDL_LOG_CATEGORY_RESERVED3, + SDL_LOG_CATEGORY_RESERVED4, + SDL_LOG_CATEGORY_RESERVED5, + SDL_LOG_CATEGORY_RESERVED6, + SDL_LOG_CATEGORY_RESERVED7, + SDL_LOG_CATEGORY_RESERVED8, + SDL_LOG_CATEGORY_RESERVED9, + SDL_LOG_CATEGORY_RESERVED10, + + /* Beyond this point is reserved for application use, e.g. + enum { + MYAPP_CATEGORY_AWESOME1 = SDL_LOG_CATEGORY_CUSTOM, + MYAPP_CATEGORY_AWESOME2, + MYAPP_CATEGORY_AWESOME3, + ... + }; + */ + SDL_LOG_CATEGORY_CUSTOM +}; + +/** + * \brief The predefined log priorities + */ +typedef enum +{ + SDL_LOG_PRIORITY_VERBOSE = 1, + SDL_LOG_PRIORITY_DEBUG, + SDL_LOG_PRIORITY_INFO, + SDL_LOG_PRIORITY_WARN, + SDL_LOG_PRIORITY_ERROR, + SDL_LOG_PRIORITY_CRITICAL, + SDL_NUM_LOG_PRIORITIES +} SDL_LogPriority; + + +/** + * \brief Set the priority of all log categories + */ +extern DECLSPEC void SDLCALL SDL_LogSetAllPriority(SDL_LogPriority priority); + +/** + * \brief Set the priority of a particular log category + */ +extern DECLSPEC void SDLCALL SDL_LogSetPriority(int category, + SDL_LogPriority priority); + +/** + * \brief Set the priority of a particular log category + */ +extern DECLSPEC SDL_LogPriority SDLCALL SDL_LogGetPriority(int category); + +/** + * \brief Reset all priorities to default. + * + * \note This is called in SDL_Quit(). + */ +extern DECLSPEC void SDLCALL SDL_LogResetPriorities(void); + +/** + * \brief Log a message with SDL_LOG_CATEGORY_APPLICATION and SDL_LOG_PRIORITY_INFO + */ +extern DECLSPEC void SDLCALL SDL_Log(const char *fmt, ...); + +/** + * \brief Log a message with SDL_LOG_PRIORITY_VERBOSE + */ +extern DECLSPEC void SDLCALL SDL_LogVerbose(int category, const char *fmt, ...); + +/** + * \brief Log a message with SDL_LOG_PRIORITY_DEBUG + */ +extern DECLSPEC void SDLCALL SDL_LogDebug(int category, const char *fmt, ...); + +/** + * \brief Log a message with SDL_LOG_PRIORITY_INFO + */ +extern DECLSPEC void SDLCALL SDL_LogInfo(int category, const char *fmt, ...); + +/** + * \brief Log a message with SDL_LOG_PRIORITY_WARN + */ +extern DECLSPEC void SDLCALL SDL_LogWarn(int category, const char *fmt, ...); + +/** + * \brief Log a message with SDL_LOG_PRIORITY_ERROR + */ +extern DECLSPEC void SDLCALL SDL_LogError(int category, const char *fmt, ...); + +/** + * \brief Log a message with SDL_LOG_PRIORITY_CRITICAL + */ +extern DECLSPEC void SDLCALL SDL_LogCritical(int category, const char *fmt, ...); + +/** + * \brief Log a message with the specified category and priority. + */ +extern DECLSPEC void SDLCALL SDL_LogMessage(int category, + SDL_LogPriority priority, + const char *fmt, ...); + +/** + * \brief Log a message with the specified category and priority. + */ +extern DECLSPEC void SDLCALL SDL_LogMessageV(int category, + SDL_LogPriority priority, + const char *fmt, va_list ap); + +/** + * \brief The prototype for the log output function + */ +typedef void (*SDL_LogOutputFunction)(void *userdata, int category, SDL_LogPriority priority, const char *message); + +/** + * \brief Get the current log output function. + */ +extern DECLSPEC void SDLCALL SDL_LogGetOutputFunction(SDL_LogOutputFunction *callback, void **userdata); + +/** + * \brief This function allows you to replace the default log output + * function with one of your own. + */ +extern DECLSPEC void SDLCALL SDL_LogSetOutputFunction(SDL_LogOutputFunction callback, void *userdata); + + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif +#include "close_code.h" + +#endif /* _SDL_log_h */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_main.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_main.h new file mode 100644 index 0000000..905fd53 --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_main.h @@ -0,0 +1,98 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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 _SDL_main_h +#define _SDL_main_h + +#include "SDL_stdinc.h" + +/** + * \file SDL_main.h + * + * Redefine main() on some platforms so that it is called by SDL. + */ + +#if defined(__WIN32__) || defined(__IPHONEOS__) || defined(__ANDROID__) +#ifndef SDL_MAIN_HANDLED +#define SDL_MAIN_NEEDED +#endif +#endif + +#ifdef __cplusplus +#define C_LINKAGE "C" +#else +#define C_LINKAGE +#endif /* __cplusplus */ + +/** + * \file SDL_main.h + * + * The application's main() function must be called with C linkage, + * and should be declared like this: + * \code + * #ifdef __cplusplus + * extern "C" + * #endif + * int main(int argc, char *argv[]) + * { + * } + * \endcode + */ + +#ifdef SDL_MAIN_NEEDED +#define main SDL_main +#endif + +/** + * The prototype for the application's main() function + */ +extern C_LINKAGE int SDL_main(int argc, char *argv[]); + + +#include "begin_code.h" +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +#ifdef __WIN32__ + +/** + * This can be called to set the application class at startup + */ +extern DECLSPEC int SDLCALL SDL_RegisterApp(char *name, Uint32 style, + void *hInst); +extern DECLSPEC void SDLCALL SDL_UnregisterApp(void); + +#endif /* __WIN32__ */ + + +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif +#include "close_code.h" + +#endif /* _SDL_main_h */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_mouse.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_mouse.h new file mode 100644 index 0000000..de7d141 --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_mouse.h @@ -0,0 +1,213 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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. +*/ + +/** + * \file SDL_mouse.h + * + * Include file for SDL mouse event handling. + * + * Please note that this ONLY discusses "mice" with the notion of the + * desktop GUI. You (usually) have one system cursor, and the OS hides + * the hardware details from you. If you plug in 10 mice, all ten move that + * one cursor. For many applications and games, this is perfect, and this + * API has served hundreds of SDL programs well since its birth. + * + * It's not the whole picture, though. If you want more lowlevel control, + * SDL offers a different API, that gives you visibility into each input + * device, multi-touch interfaces, etc. + * + * Those two APIs are incompatible, and you usually should not use both + * at the same time. But for legacy purposes, this API refers to a "mouse" + * when it actually means the system pointer and not a physical mouse. + * + * The other API is in SDL_input.h + */ + +#ifndef _SDL_mouse_h +#define _SDL_mouse_h + +#include "SDL_stdinc.h" +#include "SDL_error.h" +#include "SDL_video.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +typedef struct SDL_Cursor SDL_Cursor; /* Implementation dependent */ + + +/* Function prototypes */ + +/** + * \brief Get the window which currently has mouse focus. + */ +extern DECLSPEC SDL_Window * SDLCALL SDL_GetMouseFocus(void); + +/** + * \brief Retrieve the current state of the mouse. + * + * The current button state is returned as a button bitmask, which can + * be tested using the SDL_BUTTON(X) macros, and x and y are set to the + * mouse cursor position relative to the focus window for the currently + * selected mouse. You can pass NULL for either x or y. + */ +extern DECLSPEC Uint8 SDLCALL SDL_GetMouseState(int *x, int *y); + +/** + * \brief Retrieve the relative state of the mouse. + * + * The current button state is returned as a button bitmask, which can + * be tested using the SDL_BUTTON(X) macros, and x and y are set to the + * mouse deltas since the last call to SDL_GetRelativeMouseState(). + */ +extern DECLSPEC Uint8 SDLCALL SDL_GetRelativeMouseState(int *x, int *y); + +/** + * \brief Moves the mouse to the given position within the window. + * + * \param window The window to move the mouse into, or NULL for the current mouse focus + * \param x The x coordinate within the window + * \param y The y coordinate within the window + * + * \note This function generates a mouse motion event + */ +extern DECLSPEC void SDLCALL SDL_WarpMouseInWindow(SDL_Window * window, + int x, int y); + +/** + * \brief Set relative mouse mode. + * + * \param enabled Whether or not to enable relative mode + * + * \return 0 on success, or -1 if relative mode is not supported. + * + * While the mouse is in relative mode, the cursor is hidden, and the + * driver will try to report continuous motion in the current window. + * Only relative motion events will be delivered, the mouse position + * will not change. + * + * \note This function will flush any pending mouse motion. + * + * \sa SDL_GetRelativeMouseMode() + */ +extern DECLSPEC int SDLCALL SDL_SetRelativeMouseMode(SDL_bool enabled); + +/** + * \brief Query whether relative mouse mode is enabled. + * + * \sa SDL_SetRelativeMouseMode() + */ +extern DECLSPEC SDL_bool SDLCALL SDL_GetRelativeMouseMode(void); + +/** + * \brief Create a cursor, using the specified bitmap data and + * mask (in MSB format). + * + * The cursor width must be a multiple of 8 bits. + * + * The cursor is created in black and white according to the following: + * + * + * + * + * + * + *
data mask resulting pixel on screen
0 1 White
1 1 Black
0 0 Transparent
1 0 Inverted color if possible, black + * if not.
+ * + * \sa SDL_FreeCursor() + */ +extern DECLSPEC SDL_Cursor *SDLCALL SDL_CreateCursor(const Uint8 * data, + const Uint8 * mask, + int w, int h, int hot_x, + int hot_y); + +/** + * \brief Create a color cursor. + * + * \sa SDL_FreeCursor() + */ +extern DECLSPEC SDL_Cursor *SDLCALL SDL_CreateColorCursor(SDL_Surface *surface, + int hot_x, + int hot_y); + +/** + * \brief Set the active cursor. + */ +extern DECLSPEC void SDLCALL SDL_SetCursor(SDL_Cursor * cursor); + +/** + * \brief Return the active cursor. + */ +extern DECLSPEC SDL_Cursor *SDLCALL SDL_GetCursor(void); + +/** + * \brief Frees a cursor created with SDL_CreateCursor(). + * + * \sa SDL_CreateCursor() + */ +extern DECLSPEC void SDLCALL SDL_FreeCursor(SDL_Cursor * cursor); + +/** + * \brief Toggle whether or not the cursor is shown. + * + * \param toggle 1 to show the cursor, 0 to hide it, -1 to query the current + * state. + * + * \return 1 if the cursor is shown, or 0 if the cursor is hidden. + */ +extern DECLSPEC int SDLCALL SDL_ShowCursor(int toggle); + +/** + * Used as a mask when testing buttons in buttonstate. + * - Button 1: Left mouse button + * - Button 2: Middle mouse button + * - Button 3: Right mouse button + */ +#define SDL_BUTTON(X) (1 << ((X)-1)) +#define SDL_BUTTON_LEFT 1 +#define SDL_BUTTON_MIDDLE 2 +#define SDL_BUTTON_RIGHT 3 +#define SDL_BUTTON_X1 4 +#define SDL_BUTTON_X2 5 +#define SDL_BUTTON_LMASK SDL_BUTTON(SDL_BUTTON_LEFT) +#define SDL_BUTTON_MMASK SDL_BUTTON(SDL_BUTTON_MIDDLE) +#define SDL_BUTTON_RMASK SDL_BUTTON(SDL_BUTTON_RIGHT) +#define SDL_BUTTON_X1MASK SDL_BUTTON(SDL_BUTTON_X1) +#define SDL_BUTTON_X2MASK SDL_BUTTON(SDL_BUTTON_X2) + + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif +#include "close_code.h" + +#endif /* _SDL_mouse_h */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_mutex.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_mutex.h new file mode 100644 index 0000000..0e87267 --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_mutex.h @@ -0,0 +1,248 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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 _SDL_mutex_h +#define _SDL_mutex_h + +/** + * \file SDL_mutex.h + * + * Functions to provide thread synchronization primitives. + */ + +#include "SDL_stdinc.h" +#include "SDL_error.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +/** + * Synchronization functions which can time out return this value + * if they time out. + */ +#define SDL_MUTEX_TIMEDOUT 1 + +/** + * This is the timeout value which corresponds to never time out. + */ +#define SDL_MUTEX_MAXWAIT (~(Uint32)0) + + +/** + * \name Mutex functions + */ +/*@{*/ + +/* The SDL mutex structure, defined in SDL_mutex.c */ +struct SDL_mutex; +typedef struct SDL_mutex SDL_mutex; + +/** + * Create a mutex, initialized unlocked. + */ +extern DECLSPEC SDL_mutex *SDLCALL SDL_CreateMutex(void); + +/** + * Lock the mutex. + * + * \return 0, or -1 on error. + */ +#define SDL_LockMutex(m) SDL_mutexP(m) +extern DECLSPEC int SDLCALL SDL_mutexP(SDL_mutex * mutex); + +/** + * Unlock the mutex. + * + * \return 0, or -1 on error. + * + * \warning It is an error to unlock a mutex that has not been locked by + * the current thread, and doing so results in undefined behavior. + */ +#define SDL_UnlockMutex(m) SDL_mutexV(m) +extern DECLSPEC int SDLCALL SDL_mutexV(SDL_mutex * mutex); + +/** + * Destroy a mutex. + */ +extern DECLSPEC void SDLCALL SDL_DestroyMutex(SDL_mutex * mutex); + +/*@}*//*Mutex functions*/ + + +/** + * \name Semaphore functions + */ +/*@{*/ + +/* The SDL semaphore structure, defined in SDL_sem.c */ +struct SDL_semaphore; +typedef struct SDL_semaphore SDL_sem; + +/** + * Create a semaphore, initialized with value, returns NULL on failure. + */ +extern DECLSPEC SDL_sem *SDLCALL SDL_CreateSemaphore(Uint32 initial_value); + +/** + * Destroy a semaphore. + */ +extern DECLSPEC void SDLCALL SDL_DestroySemaphore(SDL_sem * sem); + +/** + * This function suspends the calling thread until the semaphore pointed + * to by \c sem has a positive count. It then atomically decreases the + * semaphore count. + */ +extern DECLSPEC int SDLCALL SDL_SemWait(SDL_sem * sem); + +/** + * Non-blocking variant of SDL_SemWait(). + * + * \return 0 if the wait succeeds, ::SDL_MUTEX_TIMEDOUT if the wait would + * block, and -1 on error. + */ +extern DECLSPEC int SDLCALL SDL_SemTryWait(SDL_sem * sem); + +/** + * Variant of SDL_SemWait() with a timeout in milliseconds. + * + * \return 0 if the wait succeeds, ::SDL_MUTEX_TIMEDOUT if the wait does not + * succeed in the allotted time, and -1 on error. + * + * \warning On some platforms this function is implemented by looping with a + * delay of 1 ms, and so should be avoided if possible. + */ +extern DECLSPEC int SDLCALL SDL_SemWaitTimeout(SDL_sem * sem, Uint32 ms); + +/** + * Atomically increases the semaphore's count (not blocking). + * + * \return 0, or -1 on error. + */ +extern DECLSPEC int SDLCALL SDL_SemPost(SDL_sem * sem); + +/** + * Returns the current count of the semaphore. + */ +extern DECLSPEC Uint32 SDLCALL SDL_SemValue(SDL_sem * sem); + +/*@}*//*Semaphore functions*/ + + +/** + * \name Condition variable functions + */ +/*@{*/ + +/* The SDL condition variable structure, defined in SDL_cond.c */ +struct SDL_cond; +typedef struct SDL_cond SDL_cond; + +/** + * Create a condition variable. + * + * Typical use of condition variables: + * + * Thread A: + * SDL_LockMutex(lock); + * while ( ! condition ) { + * SDL_CondWait(cond, lock); + * } + * SDL_UnlockMutex(lock); + * + * Thread B: + * SDL_LockMutex(lock); + * ... + * condition = true; + * ... + * SDL_CondSignal(cond); + * SDL_UnlockMutex(lock); + * + * There is some discussion whether to signal the condition variable + * with the mutex locked or not. There is some potential performance + * benefit to unlocking first on some platforms, but there are some + * potential race conditions depending on how your code is structured. + * + * In general it's safer to signal the condition variable while the + * mutex is locked. + */ +extern DECLSPEC SDL_cond *SDLCALL SDL_CreateCond(void); + +/** + * Destroy a condition variable. + */ +extern DECLSPEC void SDLCALL SDL_DestroyCond(SDL_cond * cond); + +/** + * Restart one of the threads that are waiting on the condition variable. + * + * \return 0 or -1 on error. + */ +extern DECLSPEC int SDLCALL SDL_CondSignal(SDL_cond * cond); + +/** + * Restart all threads that are waiting on the condition variable. + * + * \return 0 or -1 on error. + */ +extern DECLSPEC int SDLCALL SDL_CondBroadcast(SDL_cond * cond); + +/** + * Wait on the condition variable, unlocking the provided mutex. + * + * \warning The mutex must be locked before entering this function! + * + * The mutex is re-locked once the condition variable is signaled. + * + * \return 0 when it is signaled, or -1 on error. + */ +extern DECLSPEC int SDLCALL SDL_CondWait(SDL_cond * cond, SDL_mutex * mutex); + +/** + * Waits for at most \c ms milliseconds, and returns 0 if the condition + * variable is signaled, ::SDL_MUTEX_TIMEDOUT if the condition is not + * signaled in the allotted time, and -1 on error. + * + * \warning On some platforms this function is implemented by looping with a + * delay of 1 ms, and so should be avoided if possible. + */ +extern DECLSPEC int SDLCALL SDL_CondWaitTimeout(SDL_cond * cond, + SDL_mutex * mutex, Uint32 ms); + +/*@}*//*Condition variable functions*/ + + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif +#include "close_code.h" + +#endif /* _SDL_mutex_h */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_name.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_name.h new file mode 100644 index 0000000..511619a --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_name.h @@ -0,0 +1,11 @@ + +#ifndef _SDLname_h_ +#define _SDLname_h_ + +#if defined(__STDC__) || defined(__cplusplus) +#define NeedFunctionPrototypes 1 +#endif + +#define SDL_NAME(X) SDL_##X + +#endif /* _SDLname_h_ */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_opengl.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_opengl.h new file mode 100644 index 0000000..c0db826 --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_opengl.h @@ -0,0 +1,11109 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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. +*/ + +/** + * \file SDL_opengl.h + * + * This is a simple file to encapsulate the OpenGL API headers. + */ + +#ifndef _SDL_opengl_h +#define _SDL_opengl_h + +#include "SDL_config.h" + +#ifdef __WIN32__ +#define WIN32_LEAN_AND_MEAN +#ifndef NOMINMAX +#define NOMINMAX /* Don't defined min() and max() */ +#endif +#include +#endif +#ifdef __glext_h_ +/* Someone has already included glext.h */ +#define NO_SDL_GLEXT +#endif +#ifndef NO_SDL_GLEXT +#define __glext_h_ /* Don't let gl.h include glext.h */ +#endif +#if defined(__MACOSX__) +#include /* Header File For The OpenGL Library */ +#define __X_GL_H +#else +#include /* Header File For The OpenGL Library */ +#endif +#ifndef NO_SDL_GLEXT +#undef __glext_h_ +#endif + +/** + * \file SDL_opengl.h + * + * This file is included because glext.h is not available on some systems. + * If you don't want this version included, simply define ::NO_SDL_GLEXT. + * + * The latest version is available from: + * http://www.opengl.org/registry/ + */ + +/** + * \def NO_SDL_GLEXT + * + * Define this if you have your own version of glext.h and want to disable the + * version included in SDL_opengl.h. + */ + +#if !defined(NO_SDL_GLEXT) && !defined(GL_GLEXT_LEGACY) +/* *INDENT-OFF* */ +#ifndef __glext_h_ +#define __glext_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* +** Copyright (c) 2007-2010 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +/* Header file version number, required by OpenGL ABI for Linux */ +/* glext.h last updated $Date: 2010-08-03 01:30:25 -0700 (Tue, 03 Aug 2010) $ */ +/* Current version at http://www.opengl.org/registry/ */ +#define GL_GLEXT_VERSION 64 +/* Function declaration macros - to move into glplatform.h */ + +#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) +#define WIN32_LEAN_AND_MEAN 1 +#include +#endif + +#ifndef APIENTRY +#define APIENTRY +#endif +#ifndef APIENTRYP +#define APIENTRYP APIENTRY * +#endif +#ifndef GLAPI +#define GLAPI extern +#endif + +/*************************************************************/ + +#ifndef GL_VERSION_1_2 +#define GL_UNSIGNED_BYTE_3_3_2 0x8032 +#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 +#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 +#define GL_UNSIGNED_INT_8_8_8_8 0x8035 +#define GL_UNSIGNED_INT_10_10_10_2 0x8036 +#define GL_TEXTURE_BINDING_3D 0x806A +#define GL_PACK_SKIP_IMAGES 0x806B +#define GL_PACK_IMAGE_HEIGHT 0x806C +#define GL_UNPACK_SKIP_IMAGES 0x806D +#define GL_UNPACK_IMAGE_HEIGHT 0x806E +#define GL_TEXTURE_3D 0x806F +#define GL_PROXY_TEXTURE_3D 0x8070 +#define GL_TEXTURE_DEPTH 0x8071 +#define GL_TEXTURE_WRAP_R 0x8072 +#define GL_MAX_3D_TEXTURE_SIZE 0x8073 +#define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362 +#define GL_UNSIGNED_SHORT_5_6_5 0x8363 +#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364 +#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365 +#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366 +#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 +#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 +#define GL_BGR 0x80E0 +#define GL_BGRA 0x80E1 +#define GL_MAX_ELEMENTS_VERTICES 0x80E8 +#define GL_MAX_ELEMENTS_INDICES 0x80E9 +#define GL_CLAMP_TO_EDGE 0x812F +#define GL_TEXTURE_MIN_LOD 0x813A +#define GL_TEXTURE_MAX_LOD 0x813B +#define GL_TEXTURE_BASE_LEVEL 0x813C +#define GL_TEXTURE_MAX_LEVEL 0x813D +#define GL_SMOOTH_POINT_SIZE_RANGE 0x0B12 +#define GL_SMOOTH_POINT_SIZE_GRANULARITY 0x0B13 +#define GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22 +#define GL_SMOOTH_LINE_WIDTH_GRANULARITY 0x0B23 +#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E +#endif + +#ifndef GL_VERSION_1_2_DEPRECATED +#define GL_RESCALE_NORMAL 0x803A +#define GL_LIGHT_MODEL_COLOR_CONTROL 0x81F8 +#define GL_SINGLE_COLOR 0x81F9 +#define GL_SEPARATE_SPECULAR_COLOR 0x81FA +#define GL_ALIASED_POINT_SIZE_RANGE 0x846D +#endif + +#ifndef GL_ARB_imaging +#define GL_CONSTANT_COLOR 0x8001 +#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 +#define GL_CONSTANT_ALPHA 0x8003 +#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 +#define GL_BLEND_COLOR 0x8005 +#define GL_FUNC_ADD 0x8006 +#define GL_MIN 0x8007 +#define GL_MAX 0x8008 +#define GL_BLEND_EQUATION 0x8009 +#define GL_FUNC_SUBTRACT 0x800A +#define GL_FUNC_REVERSE_SUBTRACT 0x800B +#endif + +#ifndef GL_ARB_imaging_DEPRECATED +#define GL_CONVOLUTION_1D 0x8010 +#define GL_CONVOLUTION_2D 0x8011 +#define GL_SEPARABLE_2D 0x8012 +#define GL_CONVOLUTION_BORDER_MODE 0x8013 +#define GL_CONVOLUTION_FILTER_SCALE 0x8014 +#define GL_CONVOLUTION_FILTER_BIAS 0x8015 +#define GL_REDUCE 0x8016 +#define GL_CONVOLUTION_FORMAT 0x8017 +#define GL_CONVOLUTION_WIDTH 0x8018 +#define GL_CONVOLUTION_HEIGHT 0x8019 +#define GL_MAX_CONVOLUTION_WIDTH 0x801A +#define GL_MAX_CONVOLUTION_HEIGHT 0x801B +#define GL_POST_CONVOLUTION_RED_SCALE 0x801C +#define GL_POST_CONVOLUTION_GREEN_SCALE 0x801D +#define GL_POST_CONVOLUTION_BLUE_SCALE 0x801E +#define GL_POST_CONVOLUTION_ALPHA_SCALE 0x801F +#define GL_POST_CONVOLUTION_RED_BIAS 0x8020 +#define GL_POST_CONVOLUTION_GREEN_BIAS 0x8021 +#define GL_POST_CONVOLUTION_BLUE_BIAS 0x8022 +#define GL_POST_CONVOLUTION_ALPHA_BIAS 0x8023 +#define GL_HISTOGRAM 0x8024 +#define GL_PROXY_HISTOGRAM 0x8025 +#define GL_HISTOGRAM_WIDTH 0x8026 +#define GL_HISTOGRAM_FORMAT 0x8027 +#define GL_HISTOGRAM_RED_SIZE 0x8028 +#define GL_HISTOGRAM_GREEN_SIZE 0x8029 +#define GL_HISTOGRAM_BLUE_SIZE 0x802A +#define GL_HISTOGRAM_ALPHA_SIZE 0x802B +#define GL_HISTOGRAM_LUMINANCE_SIZE 0x802C +#define GL_HISTOGRAM_SINK 0x802D +#define GL_MINMAX 0x802E +#define GL_MINMAX_FORMAT 0x802F +#define GL_MINMAX_SINK 0x8030 +#define GL_TABLE_TOO_LARGE 0x8031 +#define GL_COLOR_MATRIX 0x80B1 +#define GL_COLOR_MATRIX_STACK_DEPTH 0x80B2 +#define GL_MAX_COLOR_MATRIX_STACK_DEPTH 0x80B3 +#define GL_POST_COLOR_MATRIX_RED_SCALE 0x80B4 +#define GL_POST_COLOR_MATRIX_GREEN_SCALE 0x80B5 +#define GL_POST_COLOR_MATRIX_BLUE_SCALE 0x80B6 +#define GL_POST_COLOR_MATRIX_ALPHA_SCALE 0x80B7 +#define GL_POST_COLOR_MATRIX_RED_BIAS 0x80B8 +#define GL_POST_COLOR_MATRIX_GREEN_BIAS 0x80B9 +#define GL_POST_COLOR_MATRIX_BLUE_BIAS 0x80BA +#define GL_POST_COLOR_MATRIX_ALPHA_BIAS 0x80BB +#define GL_COLOR_TABLE 0x80D0 +#define GL_POST_CONVOLUTION_COLOR_TABLE 0x80D1 +#define GL_POST_COLOR_MATRIX_COLOR_TABLE 0x80D2 +#define GL_PROXY_COLOR_TABLE 0x80D3 +#define GL_PROXY_POST_CONVOLUTION_COLOR_TABLE 0x80D4 +#define GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE 0x80D5 +#define GL_COLOR_TABLE_SCALE 0x80D6 +#define GL_COLOR_TABLE_BIAS 0x80D7 +#define GL_COLOR_TABLE_FORMAT 0x80D8 +#define GL_COLOR_TABLE_WIDTH 0x80D9 +#define GL_COLOR_TABLE_RED_SIZE 0x80DA +#define GL_COLOR_TABLE_GREEN_SIZE 0x80DB +#define GL_COLOR_TABLE_BLUE_SIZE 0x80DC +#define GL_COLOR_TABLE_ALPHA_SIZE 0x80DD +#define GL_COLOR_TABLE_LUMINANCE_SIZE 0x80DE +#define GL_COLOR_TABLE_INTENSITY_SIZE 0x80DF +#define GL_CONSTANT_BORDER 0x8151 +#define GL_REPLICATE_BORDER 0x8153 +#define GL_CONVOLUTION_BORDER_COLOR 0x8154 +#endif + +#ifndef GL_VERSION_1_3 +#define GL_TEXTURE0 0x84C0 +#define GL_TEXTURE1 0x84C1 +#define GL_TEXTURE2 0x84C2 +#define GL_TEXTURE3 0x84C3 +#define GL_TEXTURE4 0x84C4 +#define GL_TEXTURE5 0x84C5 +#define GL_TEXTURE6 0x84C6 +#define GL_TEXTURE7 0x84C7 +#define GL_TEXTURE8 0x84C8 +#define GL_TEXTURE9 0x84C9 +#define GL_TEXTURE10 0x84CA +#define GL_TEXTURE11 0x84CB +#define GL_TEXTURE12 0x84CC +#define GL_TEXTURE13 0x84CD +#define GL_TEXTURE14 0x84CE +#define GL_TEXTURE15 0x84CF +#define GL_TEXTURE16 0x84D0 +#define GL_TEXTURE17 0x84D1 +#define GL_TEXTURE18 0x84D2 +#define GL_TEXTURE19 0x84D3 +#define GL_TEXTURE20 0x84D4 +#define GL_TEXTURE21 0x84D5 +#define GL_TEXTURE22 0x84D6 +#define GL_TEXTURE23 0x84D7 +#define GL_TEXTURE24 0x84D8 +#define GL_TEXTURE25 0x84D9 +#define GL_TEXTURE26 0x84DA +#define GL_TEXTURE27 0x84DB +#define GL_TEXTURE28 0x84DC +#define GL_TEXTURE29 0x84DD +#define GL_TEXTURE30 0x84DE +#define GL_TEXTURE31 0x84DF +#define GL_ACTIVE_TEXTURE 0x84E0 +#define GL_MULTISAMPLE 0x809D +#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE 0x809F +#define GL_SAMPLE_COVERAGE 0x80A0 +#define GL_SAMPLE_BUFFERS 0x80A8 +#define GL_SAMPLES 0x80A9 +#define GL_SAMPLE_COVERAGE_VALUE 0x80AA +#define GL_SAMPLE_COVERAGE_INVERT 0x80AB +#define GL_TEXTURE_CUBE_MAP 0x8513 +#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A +#define GL_PROXY_TEXTURE_CUBE_MAP 0x851B +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C +#define GL_COMPRESSED_RGB 0x84ED +#define GL_COMPRESSED_RGBA 0x84EE +#define GL_TEXTURE_COMPRESSION_HINT 0x84EF +#define GL_TEXTURE_COMPRESSED_IMAGE_SIZE 0x86A0 +#define GL_TEXTURE_COMPRESSED 0x86A1 +#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2 +#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3 +#define GL_CLAMP_TO_BORDER 0x812D +#endif + +#ifndef GL_VERSION_1_3_DEPRECATED +#define GL_CLIENT_ACTIVE_TEXTURE 0x84E1 +#define GL_MAX_TEXTURE_UNITS 0x84E2 +#define GL_TRANSPOSE_MODELVIEW_MATRIX 0x84E3 +#define GL_TRANSPOSE_PROJECTION_MATRIX 0x84E4 +#define GL_TRANSPOSE_TEXTURE_MATRIX 0x84E5 +#define GL_TRANSPOSE_COLOR_MATRIX 0x84E6 +#define GL_MULTISAMPLE_BIT 0x20000000 +#define GL_NORMAL_MAP 0x8511 +#define GL_REFLECTION_MAP 0x8512 +#define GL_COMPRESSED_ALPHA 0x84E9 +#define GL_COMPRESSED_LUMINANCE 0x84EA +#define GL_COMPRESSED_LUMINANCE_ALPHA 0x84EB +#define GL_COMPRESSED_INTENSITY 0x84EC +#define GL_COMBINE 0x8570 +#define GL_COMBINE_RGB 0x8571 +#define GL_COMBINE_ALPHA 0x8572 +#define GL_SOURCE0_RGB 0x8580 +#define GL_SOURCE1_RGB 0x8581 +#define GL_SOURCE2_RGB 0x8582 +#define GL_SOURCE0_ALPHA 0x8588 +#define GL_SOURCE1_ALPHA 0x8589 +#define GL_SOURCE2_ALPHA 0x858A +#define GL_OPERAND0_RGB 0x8590 +#define GL_OPERAND1_RGB 0x8591 +#define GL_OPERAND2_RGB 0x8592 +#define GL_OPERAND0_ALPHA 0x8598 +#define GL_OPERAND1_ALPHA 0x8599 +#define GL_OPERAND2_ALPHA 0x859A +#define GL_RGB_SCALE 0x8573 +#define GL_ADD_SIGNED 0x8574 +#define GL_INTERPOLATE 0x8575 +#define GL_SUBTRACT 0x84E7 +#define GL_CONSTANT 0x8576 +#define GL_PRIMARY_COLOR 0x8577 +#define GL_PREVIOUS 0x8578 +#define GL_DOT3_RGB 0x86AE +#define GL_DOT3_RGBA 0x86AF +#endif + +#ifndef GL_VERSION_1_4 +#define GL_BLEND_DST_RGB 0x80C8 +#define GL_BLEND_SRC_RGB 0x80C9 +#define GL_BLEND_DST_ALPHA 0x80CA +#define GL_BLEND_SRC_ALPHA 0x80CB +#define GL_POINT_FADE_THRESHOLD_SIZE 0x8128 +#define GL_DEPTH_COMPONENT16 0x81A5 +#define GL_DEPTH_COMPONENT24 0x81A6 +#define GL_DEPTH_COMPONENT32 0x81A7 +#define GL_MIRRORED_REPEAT 0x8370 +#define GL_MAX_TEXTURE_LOD_BIAS 0x84FD +#define GL_TEXTURE_LOD_BIAS 0x8501 +#define GL_INCR_WRAP 0x8507 +#define GL_DECR_WRAP 0x8508 +#define GL_TEXTURE_DEPTH_SIZE 0x884A +#define GL_TEXTURE_COMPARE_MODE 0x884C +#define GL_TEXTURE_COMPARE_FUNC 0x884D +#endif + +#ifndef GL_VERSION_1_4_DEPRECATED +#define GL_POINT_SIZE_MIN 0x8126 +#define GL_POINT_SIZE_MAX 0x8127 +#define GL_POINT_DISTANCE_ATTENUATION 0x8129 +#define GL_GENERATE_MIPMAP 0x8191 +#define GL_GENERATE_MIPMAP_HINT 0x8192 +#define GL_FOG_COORDINATE_SOURCE 0x8450 +#define GL_FOG_COORDINATE 0x8451 +#define GL_FRAGMENT_DEPTH 0x8452 +#define GL_CURRENT_FOG_COORDINATE 0x8453 +#define GL_FOG_COORDINATE_ARRAY_TYPE 0x8454 +#define GL_FOG_COORDINATE_ARRAY_STRIDE 0x8455 +#define GL_FOG_COORDINATE_ARRAY_POINTER 0x8456 +#define GL_FOG_COORDINATE_ARRAY 0x8457 +#define GL_COLOR_SUM 0x8458 +#define GL_CURRENT_SECONDARY_COLOR 0x8459 +#define GL_SECONDARY_COLOR_ARRAY_SIZE 0x845A +#define GL_SECONDARY_COLOR_ARRAY_TYPE 0x845B +#define GL_SECONDARY_COLOR_ARRAY_STRIDE 0x845C +#define GL_SECONDARY_COLOR_ARRAY_POINTER 0x845D +#define GL_SECONDARY_COLOR_ARRAY 0x845E +#define GL_TEXTURE_FILTER_CONTROL 0x8500 +#define GL_DEPTH_TEXTURE_MODE 0x884B +#define GL_COMPARE_R_TO_TEXTURE 0x884E +#endif + +#ifndef GL_VERSION_1_5 +#define GL_BUFFER_SIZE 0x8764 +#define GL_BUFFER_USAGE 0x8765 +#define GL_QUERY_COUNTER_BITS 0x8864 +#define GL_CURRENT_QUERY 0x8865 +#define GL_QUERY_RESULT 0x8866 +#define GL_QUERY_RESULT_AVAILABLE 0x8867 +#define GL_ARRAY_BUFFER 0x8892 +#define GL_ELEMENT_ARRAY_BUFFER 0x8893 +#define GL_ARRAY_BUFFER_BINDING 0x8894 +#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 +#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F +#define GL_READ_ONLY 0x88B8 +#define GL_WRITE_ONLY 0x88B9 +#define GL_READ_WRITE 0x88BA +#define GL_BUFFER_ACCESS 0x88BB +#define GL_BUFFER_MAPPED 0x88BC +#define GL_BUFFER_MAP_POINTER 0x88BD +#define GL_STREAM_DRAW 0x88E0 +#define GL_STREAM_READ 0x88E1 +#define GL_STREAM_COPY 0x88E2 +#define GL_STATIC_DRAW 0x88E4 +#define GL_STATIC_READ 0x88E5 +#define GL_STATIC_COPY 0x88E6 +#define GL_DYNAMIC_DRAW 0x88E8 +#define GL_DYNAMIC_READ 0x88E9 +#define GL_DYNAMIC_COPY 0x88EA +#define GL_SAMPLES_PASSED 0x8914 +#endif + +#ifndef GL_VERSION_1_5_DEPRECATED +#define GL_VERTEX_ARRAY_BUFFER_BINDING 0x8896 +#define GL_NORMAL_ARRAY_BUFFER_BINDING 0x8897 +#define GL_COLOR_ARRAY_BUFFER_BINDING 0x8898 +#define GL_INDEX_ARRAY_BUFFER_BINDING 0x8899 +#define GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING 0x889A +#define GL_EDGE_FLAG_ARRAY_BUFFER_BINDING 0x889B +#define GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING 0x889C +#define GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING 0x889D +#define GL_WEIGHT_ARRAY_BUFFER_BINDING 0x889E +#define GL_FOG_COORD_SRC 0x8450 +#define GL_FOG_COORD 0x8451 +#define GL_CURRENT_FOG_COORD 0x8453 +#define GL_FOG_COORD_ARRAY_TYPE 0x8454 +#define GL_FOG_COORD_ARRAY_STRIDE 0x8455 +#define GL_FOG_COORD_ARRAY_POINTER 0x8456 +#define GL_FOG_COORD_ARRAY 0x8457 +#define GL_FOG_COORD_ARRAY_BUFFER_BINDING 0x889D +#define GL_SRC0_RGB 0x8580 +#define GL_SRC1_RGB 0x8581 +#define GL_SRC2_RGB 0x8582 +#define GL_SRC0_ALPHA 0x8588 +#define GL_SRC1_ALPHA 0x8589 +#define GL_SRC2_ALPHA 0x858A +#endif + +#ifndef GL_VERSION_2_0 +#define GL_BLEND_EQUATION_RGB 0x8009 +#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622 +#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623 +#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624 +#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625 +#define GL_CURRENT_VERTEX_ATTRIB 0x8626 +#define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642 +#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645 +#define GL_STENCIL_BACK_FUNC 0x8800 +#define GL_STENCIL_BACK_FAIL 0x8801 +#define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802 +#define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803 +#define GL_MAX_DRAW_BUFFERS 0x8824 +#define GL_DRAW_BUFFER0 0x8825 +#define GL_DRAW_BUFFER1 0x8826 +#define GL_DRAW_BUFFER2 0x8827 +#define GL_DRAW_BUFFER3 0x8828 +#define GL_DRAW_BUFFER4 0x8829 +#define GL_DRAW_BUFFER5 0x882A +#define GL_DRAW_BUFFER6 0x882B +#define GL_DRAW_BUFFER7 0x882C +#define GL_DRAW_BUFFER8 0x882D +#define GL_DRAW_BUFFER9 0x882E +#define GL_DRAW_BUFFER10 0x882F +#define GL_DRAW_BUFFER11 0x8830 +#define GL_DRAW_BUFFER12 0x8831 +#define GL_DRAW_BUFFER13 0x8832 +#define GL_DRAW_BUFFER14 0x8833 +#define GL_DRAW_BUFFER15 0x8834 +#define GL_BLEND_EQUATION_ALPHA 0x883D +#define GL_MAX_VERTEX_ATTRIBS 0x8869 +#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A +#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872 +#define GL_FRAGMENT_SHADER 0x8B30 +#define GL_VERTEX_SHADER 0x8B31 +#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS 0x8B49 +#define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A +#define GL_MAX_VARYING_FLOATS 0x8B4B +#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C +#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D +#define GL_SHADER_TYPE 0x8B4F +#define GL_FLOAT_VEC2 0x8B50 +#define GL_FLOAT_VEC3 0x8B51 +#define GL_FLOAT_VEC4 0x8B52 +#define GL_INT_VEC2 0x8B53 +#define GL_INT_VEC3 0x8B54 +#define GL_INT_VEC4 0x8B55 +#define GL_BOOL 0x8B56 +#define GL_BOOL_VEC2 0x8B57 +#define GL_BOOL_VEC3 0x8B58 +#define GL_BOOL_VEC4 0x8B59 +#define GL_FLOAT_MAT2 0x8B5A +#define GL_FLOAT_MAT3 0x8B5B +#define GL_FLOAT_MAT4 0x8B5C +#define GL_SAMPLER_1D 0x8B5D +#define GL_SAMPLER_2D 0x8B5E +#define GL_SAMPLER_3D 0x8B5F +#define GL_SAMPLER_CUBE 0x8B60 +#define GL_SAMPLER_1D_SHADOW 0x8B61 +#define GL_SAMPLER_2D_SHADOW 0x8B62 +#define GL_DELETE_STATUS 0x8B80 +#define GL_COMPILE_STATUS 0x8B81 +#define GL_LINK_STATUS 0x8B82 +#define GL_VALIDATE_STATUS 0x8B83 +#define GL_INFO_LOG_LENGTH 0x8B84 +#define GL_ATTACHED_SHADERS 0x8B85 +#define GL_ACTIVE_UNIFORMS 0x8B86 +#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87 +#define GL_SHADER_SOURCE_LENGTH 0x8B88 +#define GL_ACTIVE_ATTRIBUTES 0x8B89 +#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A +#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT 0x8B8B +#define GL_SHADING_LANGUAGE_VERSION 0x8B8C +#define GL_CURRENT_PROGRAM 0x8B8D +#define GL_POINT_SPRITE_COORD_ORIGIN 0x8CA0 +#define GL_LOWER_LEFT 0x8CA1 +#define GL_UPPER_LEFT 0x8CA2 +#define GL_STENCIL_BACK_REF 0x8CA3 +#define GL_STENCIL_BACK_VALUE_MASK 0x8CA4 +#define GL_STENCIL_BACK_WRITEMASK 0x8CA5 +#endif + +#ifndef GL_VERSION_2_0_DEPRECATED +#define GL_VERTEX_PROGRAM_TWO_SIDE 0x8643 +#define GL_POINT_SPRITE 0x8861 +#define GL_COORD_REPLACE 0x8862 +#define GL_MAX_TEXTURE_COORDS 0x8871 +#endif + +#ifndef GL_VERSION_2_1 +#define GL_PIXEL_PACK_BUFFER 0x88EB +#define GL_PIXEL_UNPACK_BUFFER 0x88EC +#define GL_PIXEL_PACK_BUFFER_BINDING 0x88ED +#define GL_PIXEL_UNPACK_BUFFER_BINDING 0x88EF +#define GL_FLOAT_MAT2x3 0x8B65 +#define GL_FLOAT_MAT2x4 0x8B66 +#define GL_FLOAT_MAT3x2 0x8B67 +#define GL_FLOAT_MAT3x4 0x8B68 +#define GL_FLOAT_MAT4x2 0x8B69 +#define GL_FLOAT_MAT4x3 0x8B6A +#define GL_SRGB 0x8C40 +#define GL_SRGB8 0x8C41 +#define GL_SRGB_ALPHA 0x8C42 +#define GL_SRGB8_ALPHA8 0x8C43 +#define GL_COMPRESSED_SRGB 0x8C48 +#define GL_COMPRESSED_SRGB_ALPHA 0x8C49 +#endif + +#ifndef GL_VERSION_2_1_DEPRECATED +#define GL_CURRENT_RASTER_SECONDARY_COLOR 0x845F +#define GL_SLUMINANCE_ALPHA 0x8C44 +#define GL_SLUMINANCE8_ALPHA8 0x8C45 +#define GL_SLUMINANCE 0x8C46 +#define GL_SLUMINANCE8 0x8C47 +#define GL_COMPRESSED_SLUMINANCE 0x8C4A +#define GL_COMPRESSED_SLUMINANCE_ALPHA 0x8C4B +#endif + +#ifndef GL_VERSION_3_0 +#define GL_COMPARE_REF_TO_TEXTURE 0x884E +#define GL_CLIP_DISTANCE0 0x3000 +#define GL_CLIP_DISTANCE1 0x3001 +#define GL_CLIP_DISTANCE2 0x3002 +#define GL_CLIP_DISTANCE3 0x3003 +#define GL_CLIP_DISTANCE4 0x3004 +#define GL_CLIP_DISTANCE5 0x3005 +#define GL_CLIP_DISTANCE6 0x3006 +#define GL_CLIP_DISTANCE7 0x3007 +#define GL_MAX_CLIP_DISTANCES 0x0D32 +#define GL_MAJOR_VERSION 0x821B +#define GL_MINOR_VERSION 0x821C +#define GL_NUM_EXTENSIONS 0x821D +#define GL_CONTEXT_FLAGS 0x821E +#define GL_DEPTH_BUFFER 0x8223 +#define GL_STENCIL_BUFFER 0x8224 +#define GL_COMPRESSED_RED 0x8225 +#define GL_COMPRESSED_RG 0x8226 +#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x0001 +#define GL_RGBA32F 0x8814 +#define GL_RGB32F 0x8815 +#define GL_RGBA16F 0x881A +#define GL_RGB16F 0x881B +#define GL_VERTEX_ATTRIB_ARRAY_INTEGER 0x88FD +#define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF +#define GL_MIN_PROGRAM_TEXEL_OFFSET 0x8904 +#define GL_MAX_PROGRAM_TEXEL_OFFSET 0x8905 +#define GL_CLAMP_READ_COLOR 0x891C +#define GL_FIXED_ONLY 0x891D +#define GL_MAX_VARYING_COMPONENTS 0x8B4B +#define GL_TEXTURE_1D_ARRAY 0x8C18 +#define GL_PROXY_TEXTURE_1D_ARRAY 0x8C19 +#define GL_TEXTURE_2D_ARRAY 0x8C1A +#define GL_PROXY_TEXTURE_2D_ARRAY 0x8C1B +#define GL_TEXTURE_BINDING_1D_ARRAY 0x8C1C +#define GL_TEXTURE_BINDING_2D_ARRAY 0x8C1D +#define GL_R11F_G11F_B10F 0x8C3A +#define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B +#define GL_RGB9_E5 0x8C3D +#define GL_UNSIGNED_INT_5_9_9_9_REV 0x8C3E +#define GL_TEXTURE_SHARED_SIZE 0x8C3F +#define GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH 0x8C76 +#define GL_TRANSFORM_FEEDBACK_BUFFER_MODE 0x8C7F +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS 0x8C80 +#define GL_TRANSFORM_FEEDBACK_VARYINGS 0x8C83 +#define GL_TRANSFORM_FEEDBACK_BUFFER_START 0x8C84 +#define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE 0x8C85 +#define GL_PRIMITIVES_GENERATED 0x8C87 +#define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN 0x8C88 +#define GL_RASTERIZER_DISCARD 0x8C89 +#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS 0x8C8A +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS 0x8C8B +#define GL_INTERLEAVED_ATTRIBS 0x8C8C +#define GL_SEPARATE_ATTRIBS 0x8C8D +#define GL_TRANSFORM_FEEDBACK_BUFFER 0x8C8E +#define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING 0x8C8F +#define GL_RGBA32UI 0x8D70 +#define GL_RGB32UI 0x8D71 +#define GL_RGBA16UI 0x8D76 +#define GL_RGB16UI 0x8D77 +#define GL_RGBA8UI 0x8D7C +#define GL_RGB8UI 0x8D7D +#define GL_RGBA32I 0x8D82 +#define GL_RGB32I 0x8D83 +#define GL_RGBA16I 0x8D88 +#define GL_RGB16I 0x8D89 +#define GL_RGBA8I 0x8D8E +#define GL_RGB8I 0x8D8F +#define GL_RED_INTEGER 0x8D94 +#define GL_GREEN_INTEGER 0x8D95 +#define GL_BLUE_INTEGER 0x8D96 +#define GL_RGB_INTEGER 0x8D98 +#define GL_RGBA_INTEGER 0x8D99 +#define GL_BGR_INTEGER 0x8D9A +#define GL_BGRA_INTEGER 0x8D9B +#define GL_SAMPLER_1D_ARRAY 0x8DC0 +#define GL_SAMPLER_2D_ARRAY 0x8DC1 +#define GL_SAMPLER_1D_ARRAY_SHADOW 0x8DC3 +#define GL_SAMPLER_2D_ARRAY_SHADOW 0x8DC4 +#define GL_SAMPLER_CUBE_SHADOW 0x8DC5 +#define GL_UNSIGNED_INT_VEC2 0x8DC6 +#define GL_UNSIGNED_INT_VEC3 0x8DC7 +#define GL_UNSIGNED_INT_VEC4 0x8DC8 +#define GL_INT_SAMPLER_1D 0x8DC9 +#define GL_INT_SAMPLER_2D 0x8DCA +#define GL_INT_SAMPLER_3D 0x8DCB +#define GL_INT_SAMPLER_CUBE 0x8DCC +#define GL_INT_SAMPLER_1D_ARRAY 0x8DCE +#define GL_INT_SAMPLER_2D_ARRAY 0x8DCF +#define GL_UNSIGNED_INT_SAMPLER_1D 0x8DD1 +#define GL_UNSIGNED_INT_SAMPLER_2D 0x8DD2 +#define GL_UNSIGNED_INT_SAMPLER_3D 0x8DD3 +#define GL_UNSIGNED_INT_SAMPLER_CUBE 0x8DD4 +#define GL_UNSIGNED_INT_SAMPLER_1D_ARRAY 0x8DD6 +#define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY 0x8DD7 +#define GL_QUERY_WAIT 0x8E13 +#define GL_QUERY_NO_WAIT 0x8E14 +#define GL_QUERY_BY_REGION_WAIT 0x8E15 +#define GL_QUERY_BY_REGION_NO_WAIT 0x8E16 +#define GL_BUFFER_ACCESS_FLAGS 0x911F +#define GL_BUFFER_MAP_LENGTH 0x9120 +#define GL_BUFFER_MAP_OFFSET 0x9121 +/* Reuse tokens from ARB_depth_buffer_float */ +/* reuse GL_DEPTH_COMPONENT32F */ +/* reuse GL_DEPTH32F_STENCIL8 */ +/* reuse GL_FLOAT_32_UNSIGNED_INT_24_8_REV */ +/* Reuse tokens from ARB_framebuffer_object */ +/* reuse GL_INVALID_FRAMEBUFFER_OPERATION */ +/* reuse GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING */ +/* reuse GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE */ +/* reuse GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE */ +/* reuse GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE */ +/* reuse GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE */ +/* reuse GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE */ +/* reuse GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE */ +/* reuse GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE */ +/* reuse GL_FRAMEBUFFER_DEFAULT */ +/* reuse GL_FRAMEBUFFER_UNDEFINED */ +/* reuse GL_DEPTH_STENCIL_ATTACHMENT */ +/* reuse GL_INDEX */ +/* reuse GL_MAX_RENDERBUFFER_SIZE */ +/* reuse GL_DEPTH_STENCIL */ +/* reuse GL_UNSIGNED_INT_24_8 */ +/* reuse GL_DEPTH24_STENCIL8 */ +/* reuse GL_TEXTURE_STENCIL_SIZE */ +/* reuse GL_TEXTURE_RED_TYPE */ +/* reuse GL_TEXTURE_GREEN_TYPE */ +/* reuse GL_TEXTURE_BLUE_TYPE */ +/* reuse GL_TEXTURE_ALPHA_TYPE */ +/* reuse GL_TEXTURE_DEPTH_TYPE */ +/* reuse GL_UNSIGNED_NORMALIZED */ +/* reuse GL_FRAMEBUFFER_BINDING */ +/* reuse GL_DRAW_FRAMEBUFFER_BINDING */ +/* reuse GL_RENDERBUFFER_BINDING */ +/* reuse GL_READ_FRAMEBUFFER */ +/* reuse GL_DRAW_FRAMEBUFFER */ +/* reuse GL_READ_FRAMEBUFFER_BINDING */ +/* reuse GL_RENDERBUFFER_SAMPLES */ +/* reuse GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE */ +/* reuse GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME */ +/* reuse GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL */ +/* reuse GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE */ +/* reuse GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER */ +/* reuse GL_FRAMEBUFFER_COMPLETE */ +/* reuse GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT */ +/* reuse GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT */ +/* reuse GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER */ +/* reuse GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER */ +/* reuse GL_FRAMEBUFFER_UNSUPPORTED */ +/* reuse GL_MAX_COLOR_ATTACHMENTS */ +/* reuse GL_COLOR_ATTACHMENT0 */ +/* reuse GL_COLOR_ATTACHMENT1 */ +/* reuse GL_COLOR_ATTACHMENT2 */ +/* reuse GL_COLOR_ATTACHMENT3 */ +/* reuse GL_COLOR_ATTACHMENT4 */ +/* reuse GL_COLOR_ATTACHMENT5 */ +/* reuse GL_COLOR_ATTACHMENT6 */ +/* reuse GL_COLOR_ATTACHMENT7 */ +/* reuse GL_COLOR_ATTACHMENT8 */ +/* reuse GL_COLOR_ATTACHMENT9 */ +/* reuse GL_COLOR_ATTACHMENT10 */ +/* reuse GL_COLOR_ATTACHMENT11 */ +/* reuse GL_COLOR_ATTACHMENT12 */ +/* reuse GL_COLOR_ATTACHMENT13 */ +/* reuse GL_COLOR_ATTACHMENT14 */ +/* reuse GL_COLOR_ATTACHMENT15 */ +/* reuse GL_DEPTH_ATTACHMENT */ +/* reuse GL_STENCIL_ATTACHMENT */ +/* reuse GL_FRAMEBUFFER */ +/* reuse GL_RENDERBUFFER */ +/* reuse GL_RENDERBUFFER_WIDTH */ +/* reuse GL_RENDERBUFFER_HEIGHT */ +/* reuse GL_RENDERBUFFER_INTERNAL_FORMAT */ +/* reuse GL_STENCIL_INDEX1 */ +/* reuse GL_STENCIL_INDEX4 */ +/* reuse GL_STENCIL_INDEX8 */ +/* reuse GL_STENCIL_INDEX16 */ +/* reuse GL_RENDERBUFFER_RED_SIZE */ +/* reuse GL_RENDERBUFFER_GREEN_SIZE */ +/* reuse GL_RENDERBUFFER_BLUE_SIZE */ +/* reuse GL_RENDERBUFFER_ALPHA_SIZE */ +/* reuse GL_RENDERBUFFER_DEPTH_SIZE */ +/* reuse GL_RENDERBUFFER_STENCIL_SIZE */ +/* reuse GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE */ +/* reuse GL_MAX_SAMPLES */ +/* Reuse tokens from ARB_framebuffer_sRGB */ +/* reuse GL_FRAMEBUFFER_SRGB */ +/* Reuse tokens from ARB_half_float_vertex */ +/* reuse GL_HALF_FLOAT */ +/* Reuse tokens from ARB_map_buffer_range */ +/* reuse GL_MAP_READ_BIT */ +/* reuse GL_MAP_WRITE_BIT */ +/* reuse GL_MAP_INVALIDATE_RANGE_BIT */ +/* reuse GL_MAP_INVALIDATE_BUFFER_BIT */ +/* reuse GL_MAP_FLUSH_EXPLICIT_BIT */ +/* reuse GL_MAP_UNSYNCHRONIZED_BIT */ +/* Reuse tokens from ARB_texture_compression_rgtc */ +/* reuse GL_COMPRESSED_RED_RGTC1 */ +/* reuse GL_COMPRESSED_SIGNED_RED_RGTC1 */ +/* reuse GL_COMPRESSED_RG_RGTC2 */ +/* reuse GL_COMPRESSED_SIGNED_RG_RGTC2 */ +/* Reuse tokens from ARB_texture_rg */ +/* reuse GL_RG */ +/* reuse GL_RG_INTEGER */ +/* reuse GL_R8 */ +/* reuse GL_R16 */ +/* reuse GL_RG8 */ +/* reuse GL_RG16 */ +/* reuse GL_R16F */ +/* reuse GL_R32F */ +/* reuse GL_RG16F */ +/* reuse GL_RG32F */ +/* reuse GL_R8I */ +/* reuse GL_R8UI */ +/* reuse GL_R16I */ +/* reuse GL_R16UI */ +/* reuse GL_R32I */ +/* reuse GL_R32UI */ +/* reuse GL_RG8I */ +/* reuse GL_RG8UI */ +/* reuse GL_RG16I */ +/* reuse GL_RG16UI */ +/* reuse GL_RG32I */ +/* reuse GL_RG32UI */ +/* Reuse tokens from ARB_vertex_array_object */ +/* reuse GL_VERTEX_ARRAY_BINDING */ +#endif + +#ifndef GL_VERSION_3_0_DEPRECATED +#define GL_CLAMP_VERTEX_COLOR 0x891A +#define GL_CLAMP_FRAGMENT_COLOR 0x891B +#define GL_ALPHA_INTEGER 0x8D97 +/* Reuse tokens from ARB_framebuffer_object */ +/* reuse GL_TEXTURE_LUMINANCE_TYPE */ +/* reuse GL_TEXTURE_INTENSITY_TYPE */ +#endif + +#ifndef GL_VERSION_3_1 +#define GL_SAMPLER_2D_RECT 0x8B63 +#define GL_SAMPLER_2D_RECT_SHADOW 0x8B64 +#define GL_SAMPLER_BUFFER 0x8DC2 +#define GL_INT_SAMPLER_2D_RECT 0x8DCD +#define GL_INT_SAMPLER_BUFFER 0x8DD0 +#define GL_UNSIGNED_INT_SAMPLER_2D_RECT 0x8DD5 +#define GL_UNSIGNED_INT_SAMPLER_BUFFER 0x8DD8 +#define GL_TEXTURE_BUFFER 0x8C2A +#define GL_MAX_TEXTURE_BUFFER_SIZE 0x8C2B +#define GL_TEXTURE_BINDING_BUFFER 0x8C2C +#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING 0x8C2D +#define GL_TEXTURE_BUFFER_FORMAT 0x8C2E +#define GL_TEXTURE_RECTANGLE 0x84F5 +#define GL_TEXTURE_BINDING_RECTANGLE 0x84F6 +#define GL_PROXY_TEXTURE_RECTANGLE 0x84F7 +#define GL_MAX_RECTANGLE_TEXTURE_SIZE 0x84F8 +#define GL_RED_SNORM 0x8F90 +#define GL_RG_SNORM 0x8F91 +#define GL_RGB_SNORM 0x8F92 +#define GL_RGBA_SNORM 0x8F93 +#define GL_R8_SNORM 0x8F94 +#define GL_RG8_SNORM 0x8F95 +#define GL_RGB8_SNORM 0x8F96 +#define GL_RGBA8_SNORM 0x8F97 +#define GL_R16_SNORM 0x8F98 +#define GL_RG16_SNORM 0x8F99 +#define GL_RGB16_SNORM 0x8F9A +#define GL_RGBA16_SNORM 0x8F9B +#define GL_SIGNED_NORMALIZED 0x8F9C +#define GL_PRIMITIVE_RESTART 0x8F9D +#define GL_PRIMITIVE_RESTART_INDEX 0x8F9E +/* Reuse tokens from ARB_copy_buffer */ +/* reuse GL_COPY_READ_BUFFER */ +/* reuse GL_COPY_WRITE_BUFFER */ +/* Reuse tokens from ARB_draw_instanced (none) */ +/* Reuse tokens from ARB_uniform_buffer_object */ +/* reuse GL_UNIFORM_BUFFER */ +/* reuse GL_UNIFORM_BUFFER_BINDING */ +/* reuse GL_UNIFORM_BUFFER_START */ +/* reuse GL_UNIFORM_BUFFER_SIZE */ +/* reuse GL_MAX_VERTEX_UNIFORM_BLOCKS */ +/* reuse GL_MAX_FRAGMENT_UNIFORM_BLOCKS */ +/* reuse GL_MAX_COMBINED_UNIFORM_BLOCKS */ +/* reuse GL_MAX_UNIFORM_BUFFER_BINDINGS */ +/* reuse GL_MAX_UNIFORM_BLOCK_SIZE */ +/* reuse GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS */ +/* reuse GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS */ +/* reuse GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT */ +/* reuse GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH */ +/* reuse GL_ACTIVE_UNIFORM_BLOCKS */ +/* reuse GL_UNIFORM_TYPE */ +/* reuse GL_UNIFORM_SIZE */ +/* reuse GL_UNIFORM_NAME_LENGTH */ +/* reuse GL_UNIFORM_BLOCK_INDEX */ +/* reuse GL_UNIFORM_OFFSET */ +/* reuse GL_UNIFORM_ARRAY_STRIDE */ +/* reuse GL_UNIFORM_MATRIX_STRIDE */ +/* reuse GL_UNIFORM_IS_ROW_MAJOR */ +/* reuse GL_UNIFORM_BLOCK_BINDING */ +/* reuse GL_UNIFORM_BLOCK_DATA_SIZE */ +/* reuse GL_UNIFORM_BLOCK_NAME_LENGTH */ +/* reuse GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS */ +/* reuse GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES */ +/* reuse GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER */ +/* reuse GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER */ +/* reuse GL_INVALID_INDEX */ +#endif + +#ifndef GL_VERSION_3_2 +#define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001 +#define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002 +#define GL_LINES_ADJACENCY 0x000A +#define GL_LINE_STRIP_ADJACENCY 0x000B +#define GL_TRIANGLES_ADJACENCY 0x000C +#define GL_TRIANGLE_STRIP_ADJACENCY 0x000D +#define GL_PROGRAM_POINT_SIZE 0x8642 +#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS 0x8C29 +#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED 0x8DA7 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS 0x8DA8 +#define GL_GEOMETRY_SHADER 0x8DD9 +#define GL_GEOMETRY_VERTICES_OUT 0x8916 +#define GL_GEOMETRY_INPUT_TYPE 0x8917 +#define GL_GEOMETRY_OUTPUT_TYPE 0x8918 +#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS 0x8DDF +#define GL_MAX_GEOMETRY_OUTPUT_VERTICES 0x8DE0 +#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS 0x8DE1 +#define GL_MAX_VERTEX_OUTPUT_COMPONENTS 0x9122 +#define GL_MAX_GEOMETRY_INPUT_COMPONENTS 0x9123 +#define GL_MAX_GEOMETRY_OUTPUT_COMPONENTS 0x9124 +#define GL_MAX_FRAGMENT_INPUT_COMPONENTS 0x9125 +#define GL_CONTEXT_PROFILE_MASK 0x9126 +/* reuse GL_MAX_VARYING_COMPONENTS */ +/* reuse GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER */ +/* Reuse tokens from ARB_depth_clamp */ +/* reuse GL_DEPTH_CLAMP */ +/* Reuse tokens from ARB_draw_elements_base_vertex (none) */ +/* Reuse tokens from ARB_fragment_coord_conventions (none) */ +/* Reuse tokens from ARB_provoking_vertex */ +/* reuse GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION */ +/* reuse GL_FIRST_VERTEX_CONVENTION */ +/* reuse GL_LAST_VERTEX_CONVENTION */ +/* reuse GL_PROVOKING_VERTEX */ +/* Reuse tokens from ARB_seamless_cube_map */ +/* reuse GL_TEXTURE_CUBE_MAP_SEAMLESS */ +/* Reuse tokens from ARB_sync */ +/* reuse GL_MAX_SERVER_WAIT_TIMEOUT */ +/* reuse GL_OBJECT_TYPE */ +/* reuse GL_SYNC_CONDITION */ +/* reuse GL_SYNC_STATUS */ +/* reuse GL_SYNC_FLAGS */ +/* reuse GL_SYNC_FENCE */ +/* reuse GL_SYNC_GPU_COMMANDS_COMPLETE */ +/* reuse GL_UNSIGNALED */ +/* reuse GL_SIGNALED */ +/* reuse GL_ALREADY_SIGNALED */ +/* reuse GL_TIMEOUT_EXPIRED */ +/* reuse GL_CONDITION_SATISFIED */ +/* reuse GL_WAIT_FAILED */ +/* reuse GL_TIMEOUT_IGNORED */ +/* reuse GL_SYNC_FLUSH_COMMANDS_BIT */ +/* reuse GL_TIMEOUT_IGNORED */ +/* Reuse tokens from ARB_texture_multisample */ +/* reuse GL_SAMPLE_POSITION */ +/* reuse GL_SAMPLE_MASK */ +/* reuse GL_SAMPLE_MASK_VALUE */ +/* reuse GL_MAX_SAMPLE_MASK_WORDS */ +/* reuse GL_TEXTURE_2D_MULTISAMPLE */ +/* reuse GL_PROXY_TEXTURE_2D_MULTISAMPLE */ +/* reuse GL_TEXTURE_2D_MULTISAMPLE_ARRAY */ +/* reuse GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY */ +/* reuse GL_TEXTURE_BINDING_2D_MULTISAMPLE */ +/* reuse GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY */ +/* reuse GL_TEXTURE_SAMPLES */ +/* reuse GL_TEXTURE_FIXED_SAMPLE_LOCATIONS */ +/* reuse GL_SAMPLER_2D_MULTISAMPLE */ +/* reuse GL_INT_SAMPLER_2D_MULTISAMPLE */ +/* reuse GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE */ +/* reuse GL_SAMPLER_2D_MULTISAMPLE_ARRAY */ +/* reuse GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY */ +/* reuse GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY */ +/* reuse GL_MAX_COLOR_TEXTURE_SAMPLES */ +/* reuse GL_MAX_DEPTH_TEXTURE_SAMPLES */ +/* reuse GL_MAX_INTEGER_SAMPLES */ +/* Don't need to reuse tokens from ARB_vertex_array_bgra since they're already in 1.2 core */ +#endif + +#ifndef GL_VERSION_3_3 +#define GL_VERTEX_ATTRIB_ARRAY_DIVISOR 0x88FE +/* Reuse tokens from ARB_blend_func_extended */ +/* reuse GL_SRC1_COLOR */ +/* reuse GL_ONE_MINUS_SRC1_COLOR */ +/* reuse GL_ONE_MINUS_SRC1_ALPHA */ +/* reuse GL_MAX_DUAL_SOURCE_DRAW_BUFFERS */ +/* Reuse tokens from ARB_explicit_attrib_location (none) */ +/* Reuse tokens from ARB_occlusion_query2 */ +/* reuse GL_ANY_SAMPLES_PASSED */ +/* Reuse tokens from ARB_sampler_objects */ +/* reuse GL_SAMPLER_BINDING */ +/* Reuse tokens from ARB_shader_bit_encoding (none) */ +/* Reuse tokens from ARB_texture_rgb10_a2ui */ +/* reuse GL_RGB10_A2UI */ +/* Reuse tokens from ARB_texture_swizzle */ +/* reuse GL_TEXTURE_SWIZZLE_R */ +/* reuse GL_TEXTURE_SWIZZLE_G */ +/* reuse GL_TEXTURE_SWIZZLE_B */ +/* reuse GL_TEXTURE_SWIZZLE_A */ +/* reuse GL_TEXTURE_SWIZZLE_RGBA */ +/* Reuse tokens from ARB_timer_query */ +/* reuse GL_TIME_ELAPSED */ +/* reuse GL_TIMESTAMP */ +/* Reuse tokens from ARB_vertex_type_2_10_10_10_rev */ +/* reuse GL_INT_2_10_10_10_REV */ +#endif + +#ifndef GL_VERSION_4_0 +#define GL_SAMPLE_SHADING 0x8C36 +#define GL_MIN_SAMPLE_SHADING_VALUE 0x8C37 +#define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET 0x8E5E +#define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET 0x8E5F +#define GL_TEXTURE_CUBE_MAP_ARRAY 0x9009 +#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY 0x900A +#define GL_PROXY_TEXTURE_CUBE_MAP_ARRAY 0x900B +#define GL_SAMPLER_CUBE_MAP_ARRAY 0x900C +#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW 0x900D +#define GL_INT_SAMPLER_CUBE_MAP_ARRAY 0x900E +#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY 0x900F +/* Reuse tokens from ARB_texture_query_lod (none) */ +/* Reuse tokens from ARB_draw_buffers_blend (none) */ +/* Reuse tokens from ARB_draw_indirect */ +/* reuse GL_DRAW_INDIRECT_BUFFER */ +/* reuse GL_DRAW_INDIRECT_BUFFER_BINDING */ +/* Reuse tokens from ARB_gpu_shader5 */ +/* reuse GL_GEOMETRY_SHADER_INVOCATIONS */ +/* reuse GL_MAX_GEOMETRY_SHADER_INVOCATIONS */ +/* reuse GL_MIN_FRAGMENT_INTERPOLATION_OFFSET */ +/* reuse GL_MAX_FRAGMENT_INTERPOLATION_OFFSET */ +/* reuse GL_FRAGMENT_INTERPOLATION_OFFSET_BITS */ +/* reuse GL_MAX_VERTEX_STREAMS */ +/* Reuse tokens from ARB_gpu_shader_fp64 */ +/* reuse GL_DOUBLE_VEC2 */ +/* reuse GL_DOUBLE_VEC3 */ +/* reuse GL_DOUBLE_VEC4 */ +/* reuse GL_DOUBLE_MAT2 */ +/* reuse GL_DOUBLE_MAT3 */ +/* reuse GL_DOUBLE_MAT4 */ +/* reuse GL_DOUBLE_MAT2x3 */ +/* reuse GL_DOUBLE_MAT2x4 */ +/* reuse GL_DOUBLE_MAT3x2 */ +/* reuse GL_DOUBLE_MAT3x4 */ +/* reuse GL_DOUBLE_MAT4x2 */ +/* reuse GL_DOUBLE_MAT4x3 */ +/* Reuse tokens from ARB_shader_subroutine */ +/* reuse GL_ACTIVE_SUBROUTINES */ +/* reuse GL_ACTIVE_SUBROUTINE_UNIFORMS */ +/* reuse GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS */ +/* reuse GL_ACTIVE_SUBROUTINE_MAX_LENGTH */ +/* reuse GL_ACTIVE_SUBROUTINE_UNIFORM_MAX_LENGTH */ +/* reuse GL_MAX_SUBROUTINES */ +/* reuse GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS */ +/* reuse GL_NUM_COMPATIBLE_SUBROUTINES */ +/* reuse GL_COMPATIBLE_SUBROUTINES */ +/* Reuse tokens from ARB_tessellation_shader */ +/* reuse GL_PATCHES */ +/* reuse GL_PATCH_VERTICES */ +/* reuse GL_PATCH_DEFAULT_INNER_LEVEL */ +/* reuse GL_PATCH_DEFAULT_OUTER_LEVEL */ +/* reuse GL_TESS_CONTROL_OUTPUT_VERTICES */ +/* reuse GL_TESS_GEN_MODE */ +/* reuse GL_TESS_GEN_SPACING */ +/* reuse GL_TESS_GEN_VERTEX_ORDER */ +/* reuse GL_TESS_GEN_POINT_MODE */ +/* reuse GL_ISOLINES */ +/* reuse GL_FRACTIONAL_ODD */ +/* reuse GL_FRACTIONAL_EVEN */ +/* reuse GL_MAX_PATCH_VERTICES */ +/* reuse GL_MAX_TESS_GEN_LEVEL */ +/* reuse GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS */ +/* reuse GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS */ +/* reuse GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS */ +/* reuse GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS */ +/* reuse GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS */ +/* reuse GL_MAX_TESS_PATCH_COMPONENTS */ +/* reuse GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS */ +/* reuse GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS */ +/* reuse GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS */ +/* reuse GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS */ +/* reuse GL_MAX_TESS_CONTROL_INPUT_COMPONENTS */ +/* reuse GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS */ +/* reuse GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS */ +/* reuse GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS */ +/* reuse GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_CONTROL_SHADER */ +/* reuse GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_EVALUATION_SHADER */ +/* reuse GL_TESS_EVALUATION_SHADER */ +/* reuse GL_TESS_CONTROL_SHADER */ +/* Reuse tokens from ARB_texture_buffer_object_rgb32 (none) */ +/* Reuse tokens from ARB_transform_feedback2 */ +/* reuse GL_TRANSFORM_FEEDBACK */ +/* reuse GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED */ +/* reuse GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE */ +/* reuse GL_TRANSFORM_FEEDBACK_BINDING */ +/* Reuse tokens from ARB_transform_feedback3 */ +/* reuse GL_MAX_TRANSFORM_FEEDBACK_BUFFERS */ +/* reuse GL_MAX_VERTEX_STREAMS */ +#endif + +#ifndef GL_VERSION_4_1 +/* Reuse tokens from ARB_ES2_compatibility */ +/* reuse GL_FIXED */ +/* reuse GL_IMPLEMENTATION_COLOR_READ_TYPE */ +/* reuse GL_IMPLEMENTATION_COLOR_READ_FORMAT */ +/* reuse GL_LOW_FLOAT */ +/* reuse GL_MEDIUM_FLOAT */ +/* reuse GL_HIGH_FLOAT */ +/* reuse GL_LOW_INT */ +/* reuse GL_MEDIUM_INT */ +/* reuse GL_HIGH_INT */ +/* reuse GL_SHADER_COMPILER */ +/* reuse GL_NUM_SHADER_BINARY_FORMATS */ +/* reuse GL_MAX_VERTEX_UNIFORM_VECTORS */ +/* reuse GL_MAX_VARYING_VECTORS */ +/* reuse GL_MAX_FRAGMENT_UNIFORM_VECTORS */ +/* Reuse tokens from ARB_get_program_binary */ +/* reuse GL_PROGRAM_BINARY_RETRIEVABLE_HINT */ +/* reuse GL_PROGRAM_BINARY_LENGTH */ +/* reuse GL_NUM_PROGRAM_BINARY_FORMATS */ +/* reuse GL_PROGRAM_BINARY_FORMATS */ +/* Reuse tokens from ARB_separate_shader_objects */ +/* reuse GL_VERTEX_SHADER_BIT */ +/* reuse GL_FRAGMENT_SHADER_BIT */ +/* reuse GL_GEOMETRY_SHADER_BIT */ +/* reuse GL_TESS_CONTROL_SHADER_BIT */ +/* reuse GL_TESS_EVALUATION_SHADER_BIT */ +/* reuse GL_ALL_SHADER_BITS */ +/* reuse GL_PROGRAM_SEPARABLE */ +/* reuse GL_ACTIVE_PROGRAM */ +/* reuse GL_PROGRAM_PIPELINE_BINDING */ +/* Reuse tokens from ARB_shader_precision (none) */ +/* Reuse tokens from ARB_vertex_attrib_64bit - all are in GL 3.0 and 4.0 already */ +/* Reuse tokens from ARB_viewport_array - some are in GL 1.1 and ARB_provoking_vertex already */ +/* reuse GL_MAX_VIEWPORTS */ +/* reuse GL_VIEWPORT_SUBPIXEL_BITS */ +/* reuse GL_VIEWPORT_BOUNDS_RANGE */ +/* reuse GL_LAYER_PROVOKING_VERTEX */ +/* reuse GL_VIEWPORT_INDEX_PROVOKING_VERTEX */ +/* reuse GL_UNDEFINED_VERTEX */ +#endif + +#ifndef GL_ARB_multitexture +#define GL_TEXTURE0_ARB 0x84C0 +#define GL_TEXTURE1_ARB 0x84C1 +#define GL_TEXTURE2_ARB 0x84C2 +#define GL_TEXTURE3_ARB 0x84C3 +#define GL_TEXTURE4_ARB 0x84C4 +#define GL_TEXTURE5_ARB 0x84C5 +#define GL_TEXTURE6_ARB 0x84C6 +#define GL_TEXTURE7_ARB 0x84C7 +#define GL_TEXTURE8_ARB 0x84C8 +#define GL_TEXTURE9_ARB 0x84C9 +#define GL_TEXTURE10_ARB 0x84CA +#define GL_TEXTURE11_ARB 0x84CB +#define GL_TEXTURE12_ARB 0x84CC +#define GL_TEXTURE13_ARB 0x84CD +#define GL_TEXTURE14_ARB 0x84CE +#define GL_TEXTURE15_ARB 0x84CF +#define GL_TEXTURE16_ARB 0x84D0 +#define GL_TEXTURE17_ARB 0x84D1 +#define GL_TEXTURE18_ARB 0x84D2 +#define GL_TEXTURE19_ARB 0x84D3 +#define GL_TEXTURE20_ARB 0x84D4 +#define GL_TEXTURE21_ARB 0x84D5 +#define GL_TEXTURE22_ARB 0x84D6 +#define GL_TEXTURE23_ARB 0x84D7 +#define GL_TEXTURE24_ARB 0x84D8 +#define GL_TEXTURE25_ARB 0x84D9 +#define GL_TEXTURE26_ARB 0x84DA +#define GL_TEXTURE27_ARB 0x84DB +#define GL_TEXTURE28_ARB 0x84DC +#define GL_TEXTURE29_ARB 0x84DD +#define GL_TEXTURE30_ARB 0x84DE +#define GL_TEXTURE31_ARB 0x84DF +#define GL_ACTIVE_TEXTURE_ARB 0x84E0 +#define GL_CLIENT_ACTIVE_TEXTURE_ARB 0x84E1 +#define GL_MAX_TEXTURE_UNITS_ARB 0x84E2 +#endif + +#ifndef GL_ARB_transpose_matrix +#define GL_TRANSPOSE_MODELVIEW_MATRIX_ARB 0x84E3 +#define GL_TRANSPOSE_PROJECTION_MATRIX_ARB 0x84E4 +#define GL_TRANSPOSE_TEXTURE_MATRIX_ARB 0x84E5 +#define GL_TRANSPOSE_COLOR_MATRIX_ARB 0x84E6 +#endif + +#ifndef GL_ARB_multisample +#define GL_MULTISAMPLE_ARB 0x809D +#define GL_SAMPLE_ALPHA_TO_COVERAGE_ARB 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE_ARB 0x809F +#define GL_SAMPLE_COVERAGE_ARB 0x80A0 +#define GL_SAMPLE_BUFFERS_ARB 0x80A8 +#define GL_SAMPLES_ARB 0x80A9 +#define GL_SAMPLE_COVERAGE_VALUE_ARB 0x80AA +#define GL_SAMPLE_COVERAGE_INVERT_ARB 0x80AB +#define GL_MULTISAMPLE_BIT_ARB 0x20000000 +#endif + +#ifndef GL_ARB_texture_env_add +#endif + +#ifndef GL_ARB_texture_cube_map +#define GL_NORMAL_MAP_ARB 0x8511 +#define GL_REFLECTION_MAP_ARB 0x8512 +#define GL_TEXTURE_CUBE_MAP_ARB 0x8513 +#define GL_TEXTURE_BINDING_CUBE_MAP_ARB 0x8514 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB 0x8515 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB 0x8516 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB 0x8517 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB 0x8518 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB 0x8519 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB 0x851A +#define GL_PROXY_TEXTURE_CUBE_MAP_ARB 0x851B +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE_ARB 0x851C +#endif + +#ifndef GL_ARB_texture_compression +#define GL_COMPRESSED_ALPHA_ARB 0x84E9 +#define GL_COMPRESSED_LUMINANCE_ARB 0x84EA +#define GL_COMPRESSED_LUMINANCE_ALPHA_ARB 0x84EB +#define GL_COMPRESSED_INTENSITY_ARB 0x84EC +#define GL_COMPRESSED_RGB_ARB 0x84ED +#define GL_COMPRESSED_RGBA_ARB 0x84EE +#define GL_TEXTURE_COMPRESSION_HINT_ARB 0x84EF +#define GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB 0x86A0 +#define GL_TEXTURE_COMPRESSED_ARB 0x86A1 +#define GL_NUM_COMPRESSED_TEXTURE_FORMATS_ARB 0x86A2 +#define GL_COMPRESSED_TEXTURE_FORMATS_ARB 0x86A3 +#endif + +#ifndef GL_ARB_texture_border_clamp +#define GL_CLAMP_TO_BORDER_ARB 0x812D +#endif + +#ifndef GL_ARB_point_parameters +#define GL_POINT_SIZE_MIN_ARB 0x8126 +#define GL_POINT_SIZE_MAX_ARB 0x8127 +#define GL_POINT_FADE_THRESHOLD_SIZE_ARB 0x8128 +#define GL_POINT_DISTANCE_ATTENUATION_ARB 0x8129 +#endif + +#ifndef GL_ARB_vertex_blend +#define GL_MAX_VERTEX_UNITS_ARB 0x86A4 +#define GL_ACTIVE_VERTEX_UNITS_ARB 0x86A5 +#define GL_WEIGHT_SUM_UNITY_ARB 0x86A6 +#define GL_VERTEX_BLEND_ARB 0x86A7 +#define GL_CURRENT_WEIGHT_ARB 0x86A8 +#define GL_WEIGHT_ARRAY_TYPE_ARB 0x86A9 +#define GL_WEIGHT_ARRAY_STRIDE_ARB 0x86AA +#define GL_WEIGHT_ARRAY_SIZE_ARB 0x86AB +#define GL_WEIGHT_ARRAY_POINTER_ARB 0x86AC +#define GL_WEIGHT_ARRAY_ARB 0x86AD +#define GL_MODELVIEW0_ARB 0x1700 +#define GL_MODELVIEW1_ARB 0x850A +#define GL_MODELVIEW2_ARB 0x8722 +#define GL_MODELVIEW3_ARB 0x8723 +#define GL_MODELVIEW4_ARB 0x8724 +#define GL_MODELVIEW5_ARB 0x8725 +#define GL_MODELVIEW6_ARB 0x8726 +#define GL_MODELVIEW7_ARB 0x8727 +#define GL_MODELVIEW8_ARB 0x8728 +#define GL_MODELVIEW9_ARB 0x8729 +#define GL_MODELVIEW10_ARB 0x872A +#define GL_MODELVIEW11_ARB 0x872B +#define GL_MODELVIEW12_ARB 0x872C +#define GL_MODELVIEW13_ARB 0x872D +#define GL_MODELVIEW14_ARB 0x872E +#define GL_MODELVIEW15_ARB 0x872F +#define GL_MODELVIEW16_ARB 0x8730 +#define GL_MODELVIEW17_ARB 0x8731 +#define GL_MODELVIEW18_ARB 0x8732 +#define GL_MODELVIEW19_ARB 0x8733 +#define GL_MODELVIEW20_ARB 0x8734 +#define GL_MODELVIEW21_ARB 0x8735 +#define GL_MODELVIEW22_ARB 0x8736 +#define GL_MODELVIEW23_ARB 0x8737 +#define GL_MODELVIEW24_ARB 0x8738 +#define GL_MODELVIEW25_ARB 0x8739 +#define GL_MODELVIEW26_ARB 0x873A +#define GL_MODELVIEW27_ARB 0x873B +#define GL_MODELVIEW28_ARB 0x873C +#define GL_MODELVIEW29_ARB 0x873D +#define GL_MODELVIEW30_ARB 0x873E +#define GL_MODELVIEW31_ARB 0x873F +#endif + +#ifndef GL_ARB_matrix_palette +#define GL_MATRIX_PALETTE_ARB 0x8840 +#define GL_MAX_MATRIX_PALETTE_STACK_DEPTH_ARB 0x8841 +#define GL_MAX_PALETTE_MATRICES_ARB 0x8842 +#define GL_CURRENT_PALETTE_MATRIX_ARB 0x8843 +#define GL_MATRIX_INDEX_ARRAY_ARB 0x8844 +#define GL_CURRENT_MATRIX_INDEX_ARB 0x8845 +#define GL_MATRIX_INDEX_ARRAY_SIZE_ARB 0x8846 +#define GL_MATRIX_INDEX_ARRAY_TYPE_ARB 0x8847 +#define GL_MATRIX_INDEX_ARRAY_STRIDE_ARB 0x8848 +#define GL_MATRIX_INDEX_ARRAY_POINTER_ARB 0x8849 +#endif + +#ifndef GL_ARB_texture_env_combine +#define GL_COMBINE_ARB 0x8570 +#define GL_COMBINE_RGB_ARB 0x8571 +#define GL_COMBINE_ALPHA_ARB 0x8572 +#define GL_SOURCE0_RGB_ARB 0x8580 +#define GL_SOURCE1_RGB_ARB 0x8581 +#define GL_SOURCE2_RGB_ARB 0x8582 +#define GL_SOURCE0_ALPHA_ARB 0x8588 +#define GL_SOURCE1_ALPHA_ARB 0x8589 +#define GL_SOURCE2_ALPHA_ARB 0x858A +#define GL_OPERAND0_RGB_ARB 0x8590 +#define GL_OPERAND1_RGB_ARB 0x8591 +#define GL_OPERAND2_RGB_ARB 0x8592 +#define GL_OPERAND0_ALPHA_ARB 0x8598 +#define GL_OPERAND1_ALPHA_ARB 0x8599 +#define GL_OPERAND2_ALPHA_ARB 0x859A +#define GL_RGB_SCALE_ARB 0x8573 +#define GL_ADD_SIGNED_ARB 0x8574 +#define GL_INTERPOLATE_ARB 0x8575 +#define GL_SUBTRACT_ARB 0x84E7 +#define GL_CONSTANT_ARB 0x8576 +#define GL_PRIMARY_COLOR_ARB 0x8577 +#define GL_PREVIOUS_ARB 0x8578 +#endif + +#ifndef GL_ARB_texture_env_crossbar +#endif + +#ifndef GL_ARB_texture_env_dot3 +#define GL_DOT3_RGB_ARB 0x86AE +#define GL_DOT3_RGBA_ARB 0x86AF +#endif + +#ifndef GL_ARB_texture_mirrored_repeat +#define GL_MIRRORED_REPEAT_ARB 0x8370 +#endif + +#ifndef GL_ARB_depth_texture +#define GL_DEPTH_COMPONENT16_ARB 0x81A5 +#define GL_DEPTH_COMPONENT24_ARB 0x81A6 +#define GL_DEPTH_COMPONENT32_ARB 0x81A7 +#define GL_TEXTURE_DEPTH_SIZE_ARB 0x884A +#define GL_DEPTH_TEXTURE_MODE_ARB 0x884B +#endif + +#ifndef GL_ARB_shadow +#define GL_TEXTURE_COMPARE_MODE_ARB 0x884C +#define GL_TEXTURE_COMPARE_FUNC_ARB 0x884D +#define GL_COMPARE_R_TO_TEXTURE_ARB 0x884E +#endif + +#ifndef GL_ARB_shadow_ambient +#define GL_TEXTURE_COMPARE_FAIL_VALUE_ARB 0x80BF +#endif + +#ifndef GL_ARB_window_pos +#endif + +#ifndef GL_ARB_vertex_program +#define GL_COLOR_SUM_ARB 0x8458 +#define GL_VERTEX_PROGRAM_ARB 0x8620 +#define GL_VERTEX_ATTRIB_ARRAY_ENABLED_ARB 0x8622 +#define GL_VERTEX_ATTRIB_ARRAY_SIZE_ARB 0x8623 +#define GL_VERTEX_ATTRIB_ARRAY_STRIDE_ARB 0x8624 +#define GL_VERTEX_ATTRIB_ARRAY_TYPE_ARB 0x8625 +#define GL_CURRENT_VERTEX_ATTRIB_ARB 0x8626 +#define GL_PROGRAM_LENGTH_ARB 0x8627 +#define GL_PROGRAM_STRING_ARB 0x8628 +#define GL_MAX_PROGRAM_MATRIX_STACK_DEPTH_ARB 0x862E +#define GL_MAX_PROGRAM_MATRICES_ARB 0x862F +#define GL_CURRENT_MATRIX_STACK_DEPTH_ARB 0x8640 +#define GL_CURRENT_MATRIX_ARB 0x8641 +#define GL_VERTEX_PROGRAM_POINT_SIZE_ARB 0x8642 +#define GL_VERTEX_PROGRAM_TWO_SIDE_ARB 0x8643 +#define GL_VERTEX_ATTRIB_ARRAY_POINTER_ARB 0x8645 +#define GL_PROGRAM_ERROR_POSITION_ARB 0x864B +#define GL_PROGRAM_BINDING_ARB 0x8677 +#define GL_MAX_VERTEX_ATTRIBS_ARB 0x8869 +#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED_ARB 0x886A +#define GL_PROGRAM_ERROR_STRING_ARB 0x8874 +#define GL_PROGRAM_FORMAT_ASCII_ARB 0x8875 +#define GL_PROGRAM_FORMAT_ARB 0x8876 +#define GL_PROGRAM_INSTRUCTIONS_ARB 0x88A0 +#define GL_MAX_PROGRAM_INSTRUCTIONS_ARB 0x88A1 +#define GL_PROGRAM_NATIVE_INSTRUCTIONS_ARB 0x88A2 +#define GL_MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB 0x88A3 +#define GL_PROGRAM_TEMPORARIES_ARB 0x88A4 +#define GL_MAX_PROGRAM_TEMPORARIES_ARB 0x88A5 +#define GL_PROGRAM_NATIVE_TEMPORARIES_ARB 0x88A6 +#define GL_MAX_PROGRAM_NATIVE_TEMPORARIES_ARB 0x88A7 +#define GL_PROGRAM_PARAMETERS_ARB 0x88A8 +#define GL_MAX_PROGRAM_PARAMETERS_ARB 0x88A9 +#define GL_PROGRAM_NATIVE_PARAMETERS_ARB 0x88AA +#define GL_MAX_PROGRAM_NATIVE_PARAMETERS_ARB 0x88AB +#define GL_PROGRAM_ATTRIBS_ARB 0x88AC +#define GL_MAX_PROGRAM_ATTRIBS_ARB 0x88AD +#define GL_PROGRAM_NATIVE_ATTRIBS_ARB 0x88AE +#define GL_MAX_PROGRAM_NATIVE_ATTRIBS_ARB 0x88AF +#define GL_PROGRAM_ADDRESS_REGISTERS_ARB 0x88B0 +#define GL_MAX_PROGRAM_ADDRESS_REGISTERS_ARB 0x88B1 +#define GL_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB 0x88B2 +#define GL_MAX_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB 0x88B3 +#define GL_MAX_PROGRAM_LOCAL_PARAMETERS_ARB 0x88B4 +#define GL_MAX_PROGRAM_ENV_PARAMETERS_ARB 0x88B5 +#define GL_PROGRAM_UNDER_NATIVE_LIMITS_ARB 0x88B6 +#define GL_TRANSPOSE_CURRENT_MATRIX_ARB 0x88B7 +#define GL_MATRIX0_ARB 0x88C0 +#define GL_MATRIX1_ARB 0x88C1 +#define GL_MATRIX2_ARB 0x88C2 +#define GL_MATRIX3_ARB 0x88C3 +#define GL_MATRIX4_ARB 0x88C4 +#define GL_MATRIX5_ARB 0x88C5 +#define GL_MATRIX6_ARB 0x88C6 +#define GL_MATRIX7_ARB 0x88C7 +#define GL_MATRIX8_ARB 0x88C8 +#define GL_MATRIX9_ARB 0x88C9 +#define GL_MATRIX10_ARB 0x88CA +#define GL_MATRIX11_ARB 0x88CB +#define GL_MATRIX12_ARB 0x88CC +#define GL_MATRIX13_ARB 0x88CD +#define GL_MATRIX14_ARB 0x88CE +#define GL_MATRIX15_ARB 0x88CF +#define GL_MATRIX16_ARB 0x88D0 +#define GL_MATRIX17_ARB 0x88D1 +#define GL_MATRIX18_ARB 0x88D2 +#define GL_MATRIX19_ARB 0x88D3 +#define GL_MATRIX20_ARB 0x88D4 +#define GL_MATRIX21_ARB 0x88D5 +#define GL_MATRIX22_ARB 0x88D6 +#define GL_MATRIX23_ARB 0x88D7 +#define GL_MATRIX24_ARB 0x88D8 +#define GL_MATRIX25_ARB 0x88D9 +#define GL_MATRIX26_ARB 0x88DA +#define GL_MATRIX27_ARB 0x88DB +#define GL_MATRIX28_ARB 0x88DC +#define GL_MATRIX29_ARB 0x88DD +#define GL_MATRIX30_ARB 0x88DE +#define GL_MATRIX31_ARB 0x88DF +#endif + +#ifndef GL_ARB_fragment_program +#define GL_FRAGMENT_PROGRAM_ARB 0x8804 +#define GL_PROGRAM_ALU_INSTRUCTIONS_ARB 0x8805 +#define GL_PROGRAM_TEX_INSTRUCTIONS_ARB 0x8806 +#define GL_PROGRAM_TEX_INDIRECTIONS_ARB 0x8807 +#define GL_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB 0x8808 +#define GL_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB 0x8809 +#define GL_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB 0x880A +#define GL_MAX_PROGRAM_ALU_INSTRUCTIONS_ARB 0x880B +#define GL_MAX_PROGRAM_TEX_INSTRUCTIONS_ARB 0x880C +#define GL_MAX_PROGRAM_TEX_INDIRECTIONS_ARB 0x880D +#define GL_MAX_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB 0x880E +#define GL_MAX_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB 0x880F +#define GL_MAX_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB 0x8810 +#define GL_MAX_TEXTURE_COORDS_ARB 0x8871 +#define GL_MAX_TEXTURE_IMAGE_UNITS_ARB 0x8872 +#endif + +#ifndef GL_ARB_vertex_buffer_object +#define GL_BUFFER_SIZE_ARB 0x8764 +#define GL_BUFFER_USAGE_ARB 0x8765 +#define GL_ARRAY_BUFFER_ARB 0x8892 +#define GL_ELEMENT_ARRAY_BUFFER_ARB 0x8893 +#define GL_ARRAY_BUFFER_BINDING_ARB 0x8894 +#define GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB 0x8895 +#define GL_VERTEX_ARRAY_BUFFER_BINDING_ARB 0x8896 +#define GL_NORMAL_ARRAY_BUFFER_BINDING_ARB 0x8897 +#define GL_COLOR_ARRAY_BUFFER_BINDING_ARB 0x8898 +#define GL_INDEX_ARRAY_BUFFER_BINDING_ARB 0x8899 +#define GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING_ARB 0x889A +#define GL_EDGE_FLAG_ARRAY_BUFFER_BINDING_ARB 0x889B +#define GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING_ARB 0x889C +#define GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING_ARB 0x889D +#define GL_WEIGHT_ARRAY_BUFFER_BINDING_ARB 0x889E +#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB 0x889F +#define GL_READ_ONLY_ARB 0x88B8 +#define GL_WRITE_ONLY_ARB 0x88B9 +#define GL_READ_WRITE_ARB 0x88BA +#define GL_BUFFER_ACCESS_ARB 0x88BB +#define GL_BUFFER_MAPPED_ARB 0x88BC +#define GL_BUFFER_MAP_POINTER_ARB 0x88BD +#define GL_STREAM_DRAW_ARB 0x88E0 +#define GL_STREAM_READ_ARB 0x88E1 +#define GL_STREAM_COPY_ARB 0x88E2 +#define GL_STATIC_DRAW_ARB 0x88E4 +#define GL_STATIC_READ_ARB 0x88E5 +#define GL_STATIC_COPY_ARB 0x88E6 +#define GL_DYNAMIC_DRAW_ARB 0x88E8 +#define GL_DYNAMIC_READ_ARB 0x88E9 +#define GL_DYNAMIC_COPY_ARB 0x88EA +#endif + +#ifndef GL_ARB_occlusion_query +#define GL_QUERY_COUNTER_BITS_ARB 0x8864 +#define GL_CURRENT_QUERY_ARB 0x8865 +#define GL_QUERY_RESULT_ARB 0x8866 +#define GL_QUERY_RESULT_AVAILABLE_ARB 0x8867 +#define GL_SAMPLES_PASSED_ARB 0x8914 +#endif + +#ifndef GL_ARB_shader_objects +#define GL_PROGRAM_OBJECT_ARB 0x8B40 +#define GL_SHADER_OBJECT_ARB 0x8B48 +#define GL_OBJECT_TYPE_ARB 0x8B4E +#define GL_OBJECT_SUBTYPE_ARB 0x8B4F +#define GL_FLOAT_VEC2_ARB 0x8B50 +#define GL_FLOAT_VEC3_ARB 0x8B51 +#define GL_FLOAT_VEC4_ARB 0x8B52 +#define GL_INT_VEC2_ARB 0x8B53 +#define GL_INT_VEC3_ARB 0x8B54 +#define GL_INT_VEC4_ARB 0x8B55 +#define GL_BOOL_ARB 0x8B56 +#define GL_BOOL_VEC2_ARB 0x8B57 +#define GL_BOOL_VEC3_ARB 0x8B58 +#define GL_BOOL_VEC4_ARB 0x8B59 +#define GL_FLOAT_MAT2_ARB 0x8B5A +#define GL_FLOAT_MAT3_ARB 0x8B5B +#define GL_FLOAT_MAT4_ARB 0x8B5C +#define GL_SAMPLER_1D_ARB 0x8B5D +#define GL_SAMPLER_2D_ARB 0x8B5E +#define GL_SAMPLER_3D_ARB 0x8B5F +#define GL_SAMPLER_CUBE_ARB 0x8B60 +#define GL_SAMPLER_1D_SHADOW_ARB 0x8B61 +#define GL_SAMPLER_2D_SHADOW_ARB 0x8B62 +#define GL_SAMPLER_2D_RECT_ARB 0x8B63 +#define GL_SAMPLER_2D_RECT_SHADOW_ARB 0x8B64 +#define GL_OBJECT_DELETE_STATUS_ARB 0x8B80 +#define GL_OBJECT_COMPILE_STATUS_ARB 0x8B81 +#define GL_OBJECT_LINK_STATUS_ARB 0x8B82 +#define GL_OBJECT_VALIDATE_STATUS_ARB 0x8B83 +#define GL_OBJECT_INFO_LOG_LENGTH_ARB 0x8B84 +#define GL_OBJECT_ATTACHED_OBJECTS_ARB 0x8B85 +#define GL_OBJECT_ACTIVE_UNIFORMS_ARB 0x8B86 +#define GL_OBJECT_ACTIVE_UNIFORM_MAX_LENGTH_ARB 0x8B87 +#define GL_OBJECT_SHADER_SOURCE_LENGTH_ARB 0x8B88 +#endif + +#ifndef GL_ARB_vertex_shader +#define GL_VERTEX_SHADER_ARB 0x8B31 +#define GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB 0x8B4A +#define GL_MAX_VARYING_FLOATS_ARB 0x8B4B +#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB 0x8B4C +#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS_ARB 0x8B4D +#define GL_OBJECT_ACTIVE_ATTRIBUTES_ARB 0x8B89 +#define GL_OBJECT_ACTIVE_ATTRIBUTE_MAX_LENGTH_ARB 0x8B8A +#endif + +#ifndef GL_ARB_fragment_shader +#define GL_FRAGMENT_SHADER_ARB 0x8B30 +#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS_ARB 0x8B49 +#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT_ARB 0x8B8B +#endif + +#ifndef GL_ARB_shading_language_100 +#define GL_SHADING_LANGUAGE_VERSION_ARB 0x8B8C +#endif + +#ifndef GL_ARB_texture_non_power_of_two +#endif + +#ifndef GL_ARB_point_sprite +#define GL_POINT_SPRITE_ARB 0x8861 +#define GL_COORD_REPLACE_ARB 0x8862 +#endif + +#ifndef GL_ARB_fragment_program_shadow +#endif + +#ifndef GL_ARB_draw_buffers +#define GL_MAX_DRAW_BUFFERS_ARB 0x8824 +#define GL_DRAW_BUFFER0_ARB 0x8825 +#define GL_DRAW_BUFFER1_ARB 0x8826 +#define GL_DRAW_BUFFER2_ARB 0x8827 +#define GL_DRAW_BUFFER3_ARB 0x8828 +#define GL_DRAW_BUFFER4_ARB 0x8829 +#define GL_DRAW_BUFFER5_ARB 0x882A +#define GL_DRAW_BUFFER6_ARB 0x882B +#define GL_DRAW_BUFFER7_ARB 0x882C +#define GL_DRAW_BUFFER8_ARB 0x882D +#define GL_DRAW_BUFFER9_ARB 0x882E +#define GL_DRAW_BUFFER10_ARB 0x882F +#define GL_DRAW_BUFFER11_ARB 0x8830 +#define GL_DRAW_BUFFER12_ARB 0x8831 +#define GL_DRAW_BUFFER13_ARB 0x8832 +#define GL_DRAW_BUFFER14_ARB 0x8833 +#define GL_DRAW_BUFFER15_ARB 0x8834 +#endif + +#ifndef GL_ARB_texture_rectangle +#define GL_TEXTURE_RECTANGLE_ARB 0x84F5 +#define GL_TEXTURE_BINDING_RECTANGLE_ARB 0x84F6 +#define GL_PROXY_TEXTURE_RECTANGLE_ARB 0x84F7 +#define GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB 0x84F8 +#endif + +#ifndef GL_ARB_color_buffer_float +#define GL_RGBA_FLOAT_MODE_ARB 0x8820 +#define GL_CLAMP_VERTEX_COLOR_ARB 0x891A +#define GL_CLAMP_FRAGMENT_COLOR_ARB 0x891B +#define GL_CLAMP_READ_COLOR_ARB 0x891C +#define GL_FIXED_ONLY_ARB 0x891D +#endif + +#ifndef GL_ARB_half_float_pixel +#define GL_HALF_FLOAT_ARB 0x140B +#endif + +#ifndef GL_ARB_texture_float +#define GL_TEXTURE_RED_TYPE_ARB 0x8C10 +#define GL_TEXTURE_GREEN_TYPE_ARB 0x8C11 +#define GL_TEXTURE_BLUE_TYPE_ARB 0x8C12 +#define GL_TEXTURE_ALPHA_TYPE_ARB 0x8C13 +#define GL_TEXTURE_LUMINANCE_TYPE_ARB 0x8C14 +#define GL_TEXTURE_INTENSITY_TYPE_ARB 0x8C15 +#define GL_TEXTURE_DEPTH_TYPE_ARB 0x8C16 +#define GL_UNSIGNED_NORMALIZED_ARB 0x8C17 +#define GL_RGBA32F_ARB 0x8814 +#define GL_RGB32F_ARB 0x8815 +#define GL_ALPHA32F_ARB 0x8816 +#define GL_INTENSITY32F_ARB 0x8817 +#define GL_LUMINANCE32F_ARB 0x8818 +#define GL_LUMINANCE_ALPHA32F_ARB 0x8819 +#define GL_RGBA16F_ARB 0x881A +#define GL_RGB16F_ARB 0x881B +#define GL_ALPHA16F_ARB 0x881C +#define GL_INTENSITY16F_ARB 0x881D +#define GL_LUMINANCE16F_ARB 0x881E +#define GL_LUMINANCE_ALPHA16F_ARB 0x881F +#endif + +#ifndef GL_ARB_pixel_buffer_object +#define GL_PIXEL_PACK_BUFFER_ARB 0x88EB +#define GL_PIXEL_UNPACK_BUFFER_ARB 0x88EC +#define GL_PIXEL_PACK_BUFFER_BINDING_ARB 0x88ED +#define GL_PIXEL_UNPACK_BUFFER_BINDING_ARB 0x88EF +#endif + +#ifndef GL_ARB_depth_buffer_float +#define GL_DEPTH_COMPONENT32F 0x8CAC +#define GL_DEPTH32F_STENCIL8 0x8CAD +#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV 0x8DAD +#endif + +#ifndef GL_ARB_draw_instanced +#endif + +#ifndef GL_ARB_framebuffer_object +#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506 +#define GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING 0x8210 +#define GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE 0x8211 +#define GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE 0x8212 +#define GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE 0x8213 +#define GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE 0x8214 +#define GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE 0x8215 +#define GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE 0x8216 +#define GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE 0x8217 +#define GL_FRAMEBUFFER_DEFAULT 0x8218 +#define GL_FRAMEBUFFER_UNDEFINED 0x8219 +#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A +#define GL_MAX_RENDERBUFFER_SIZE 0x84E8 +#define GL_DEPTH_STENCIL 0x84F9 +#define GL_UNSIGNED_INT_24_8 0x84FA +#define GL_DEPTH24_STENCIL8 0x88F0 +#define GL_TEXTURE_STENCIL_SIZE 0x88F1 +#define GL_TEXTURE_RED_TYPE 0x8C10 +#define GL_TEXTURE_GREEN_TYPE 0x8C11 +#define GL_TEXTURE_BLUE_TYPE 0x8C12 +#define GL_TEXTURE_ALPHA_TYPE 0x8C13 +#define GL_TEXTURE_DEPTH_TYPE 0x8C16 +#define GL_UNSIGNED_NORMALIZED 0x8C17 +#define GL_FRAMEBUFFER_BINDING 0x8CA6 +#define GL_DRAW_FRAMEBUFFER_BINDING GL_FRAMEBUFFER_BINDING +#define GL_RENDERBUFFER_BINDING 0x8CA7 +#define GL_READ_FRAMEBUFFER 0x8CA8 +#define GL_DRAW_FRAMEBUFFER 0x8CA9 +#define GL_READ_FRAMEBUFFER_BINDING 0x8CAA +#define GL_RENDERBUFFER_SAMPLES 0x8CAB +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER 0x8CD4 +#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 +#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER 0x8CDB +#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 0x8CDC +#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD +#define GL_MAX_COLOR_ATTACHMENTS 0x8CDF +#define GL_COLOR_ATTACHMENT0 0x8CE0 +#define GL_COLOR_ATTACHMENT1 0x8CE1 +#define GL_COLOR_ATTACHMENT2 0x8CE2 +#define GL_COLOR_ATTACHMENT3 0x8CE3 +#define GL_COLOR_ATTACHMENT4 0x8CE4 +#define GL_COLOR_ATTACHMENT5 0x8CE5 +#define GL_COLOR_ATTACHMENT6 0x8CE6 +#define GL_COLOR_ATTACHMENT7 0x8CE7 +#define GL_COLOR_ATTACHMENT8 0x8CE8 +#define GL_COLOR_ATTACHMENT9 0x8CE9 +#define GL_COLOR_ATTACHMENT10 0x8CEA +#define GL_COLOR_ATTACHMENT11 0x8CEB +#define GL_COLOR_ATTACHMENT12 0x8CEC +#define GL_COLOR_ATTACHMENT13 0x8CED +#define GL_COLOR_ATTACHMENT14 0x8CEE +#define GL_COLOR_ATTACHMENT15 0x8CEF +#define GL_DEPTH_ATTACHMENT 0x8D00 +#define GL_STENCIL_ATTACHMENT 0x8D20 +#define GL_FRAMEBUFFER 0x8D40 +#define GL_RENDERBUFFER 0x8D41 +#define GL_RENDERBUFFER_WIDTH 0x8D42 +#define GL_RENDERBUFFER_HEIGHT 0x8D43 +#define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44 +#define GL_STENCIL_INDEX1 0x8D46 +#define GL_STENCIL_INDEX4 0x8D47 +#define GL_STENCIL_INDEX8 0x8D48 +#define GL_STENCIL_INDEX16 0x8D49 +#define GL_RENDERBUFFER_RED_SIZE 0x8D50 +#define GL_RENDERBUFFER_GREEN_SIZE 0x8D51 +#define GL_RENDERBUFFER_BLUE_SIZE 0x8D52 +#define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53 +#define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54 +#define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55 +#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56 +#define GL_MAX_SAMPLES 0x8D57 +#endif + +#ifndef GL_ARB_framebuffer_object_DEPRECATED +#define GL_INDEX 0x8222 +#define GL_TEXTURE_LUMINANCE_TYPE 0x8C14 +#define GL_TEXTURE_INTENSITY_TYPE 0x8C15 +#endif + +#ifndef GL_ARB_framebuffer_sRGB +#define GL_FRAMEBUFFER_SRGB 0x8DB9 +#endif + +#ifndef GL_ARB_geometry_shader4 +#define GL_LINES_ADJACENCY_ARB 0x000A +#define GL_LINE_STRIP_ADJACENCY_ARB 0x000B +#define GL_TRIANGLES_ADJACENCY_ARB 0x000C +#define GL_TRIANGLE_STRIP_ADJACENCY_ARB 0x000D +#define GL_PROGRAM_POINT_SIZE_ARB 0x8642 +#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_ARB 0x8C29 +#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED_ARB 0x8DA7 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_ARB 0x8DA8 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_ARB 0x8DA9 +#define GL_GEOMETRY_SHADER_ARB 0x8DD9 +#define GL_GEOMETRY_VERTICES_OUT_ARB 0x8DDA +#define GL_GEOMETRY_INPUT_TYPE_ARB 0x8DDB +#define GL_GEOMETRY_OUTPUT_TYPE_ARB 0x8DDC +#define GL_MAX_GEOMETRY_VARYING_COMPONENTS_ARB 0x8DDD +#define GL_MAX_VERTEX_VARYING_COMPONENTS_ARB 0x8DDE +#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_ARB 0x8DDF +#define GL_MAX_GEOMETRY_OUTPUT_VERTICES_ARB 0x8DE0 +#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_ARB 0x8DE1 +/* reuse GL_MAX_VARYING_COMPONENTS */ +/* reuse GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER */ +#endif + +#ifndef GL_ARB_half_float_vertex +#define GL_HALF_FLOAT 0x140B +#endif + +#ifndef GL_ARB_instanced_arrays +#define GL_VERTEX_ATTRIB_ARRAY_DIVISOR_ARB 0x88FE +#endif + +#ifndef GL_ARB_map_buffer_range +#define GL_MAP_READ_BIT 0x0001 +#define GL_MAP_WRITE_BIT 0x0002 +#define GL_MAP_INVALIDATE_RANGE_BIT 0x0004 +#define GL_MAP_INVALIDATE_BUFFER_BIT 0x0008 +#define GL_MAP_FLUSH_EXPLICIT_BIT 0x0010 +#define GL_MAP_UNSYNCHRONIZED_BIT 0x0020 +#endif + +#ifndef GL_ARB_texture_buffer_object +#define GL_TEXTURE_BUFFER_ARB 0x8C2A +#define GL_MAX_TEXTURE_BUFFER_SIZE_ARB 0x8C2B +#define GL_TEXTURE_BINDING_BUFFER_ARB 0x8C2C +#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING_ARB 0x8C2D +#define GL_TEXTURE_BUFFER_FORMAT_ARB 0x8C2E +#endif + +#ifndef GL_ARB_texture_compression_rgtc +#define GL_COMPRESSED_RED_RGTC1 0x8DBB +#define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC +#define GL_COMPRESSED_RG_RGTC2 0x8DBD +#define GL_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE +#endif + +#ifndef GL_ARB_texture_rg +#define GL_RG 0x8227 +#define GL_RG_INTEGER 0x8228 +#define GL_R8 0x8229 +#define GL_R16 0x822A +#define GL_RG8 0x822B +#define GL_RG16 0x822C +#define GL_R16F 0x822D +#define GL_R32F 0x822E +#define GL_RG16F 0x822F +#define GL_RG32F 0x8230 +#define GL_R8I 0x8231 +#define GL_R8UI 0x8232 +#define GL_R16I 0x8233 +#define GL_R16UI 0x8234 +#define GL_R32I 0x8235 +#define GL_R32UI 0x8236 +#define GL_RG8I 0x8237 +#define GL_RG8UI 0x8238 +#define GL_RG16I 0x8239 +#define GL_RG16UI 0x823A +#define GL_RG32I 0x823B +#define GL_RG32UI 0x823C +#endif + +#ifndef GL_ARB_vertex_array_object +#define GL_VERTEX_ARRAY_BINDING 0x85B5 +#endif + +#ifndef GL_ARB_uniform_buffer_object +#define GL_UNIFORM_BUFFER 0x8A11 +#define GL_UNIFORM_BUFFER_BINDING 0x8A28 +#define GL_UNIFORM_BUFFER_START 0x8A29 +#define GL_UNIFORM_BUFFER_SIZE 0x8A2A +#define GL_MAX_VERTEX_UNIFORM_BLOCKS 0x8A2B +#define GL_MAX_GEOMETRY_UNIFORM_BLOCKS 0x8A2C +#define GL_MAX_FRAGMENT_UNIFORM_BLOCKS 0x8A2D +#define GL_MAX_COMBINED_UNIFORM_BLOCKS 0x8A2E +#define GL_MAX_UNIFORM_BUFFER_BINDINGS 0x8A2F +#define GL_MAX_UNIFORM_BLOCK_SIZE 0x8A30 +#define GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS 0x8A31 +#define GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS 0x8A32 +#define GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS 0x8A33 +#define GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT 0x8A34 +#define GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH 0x8A35 +#define GL_ACTIVE_UNIFORM_BLOCKS 0x8A36 +#define GL_UNIFORM_TYPE 0x8A37 +#define GL_UNIFORM_SIZE 0x8A38 +#define GL_UNIFORM_NAME_LENGTH 0x8A39 +#define GL_UNIFORM_BLOCK_INDEX 0x8A3A +#define GL_UNIFORM_OFFSET 0x8A3B +#define GL_UNIFORM_ARRAY_STRIDE 0x8A3C +#define GL_UNIFORM_MATRIX_STRIDE 0x8A3D +#define GL_UNIFORM_IS_ROW_MAJOR 0x8A3E +#define GL_UNIFORM_BLOCK_BINDING 0x8A3F +#define GL_UNIFORM_BLOCK_DATA_SIZE 0x8A40 +#define GL_UNIFORM_BLOCK_NAME_LENGTH 0x8A41 +#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS 0x8A42 +#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES 0x8A43 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER 0x8A44 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER 0x8A45 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER 0x8A46 +#define GL_INVALID_INDEX 0xFFFFFFFFu +#endif + +#ifndef GL_ARB_compatibility +/* ARB_compatibility just defines tokens from core 3.0 */ +#endif + +#ifndef GL_ARB_copy_buffer +#define GL_COPY_READ_BUFFER 0x8F36 +#define GL_COPY_WRITE_BUFFER 0x8F37 +#endif + +#ifndef GL_ARB_shader_texture_lod +#endif + +#ifndef GL_ARB_depth_clamp +#define GL_DEPTH_CLAMP 0x864F +#endif + +#ifndef GL_ARB_draw_elements_base_vertex +#endif + +#ifndef GL_ARB_fragment_coord_conventions +#endif + +#ifndef GL_ARB_provoking_vertex +#define GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION 0x8E4C +#define GL_FIRST_VERTEX_CONVENTION 0x8E4D +#define GL_LAST_VERTEX_CONVENTION 0x8E4E +#define GL_PROVOKING_VERTEX 0x8E4F +#endif + +#ifndef GL_ARB_seamless_cube_map +#define GL_TEXTURE_CUBE_MAP_SEAMLESS 0x884F +#endif + +#ifndef GL_ARB_sync +#define GL_MAX_SERVER_WAIT_TIMEOUT 0x9111 +#define GL_OBJECT_TYPE 0x9112 +#define GL_SYNC_CONDITION 0x9113 +#define GL_SYNC_STATUS 0x9114 +#define GL_SYNC_FLAGS 0x9115 +#define GL_SYNC_FENCE 0x9116 +#define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117 +#define GL_UNSIGNALED 0x9118 +#define GL_SIGNALED 0x9119 +#define GL_ALREADY_SIGNALED 0x911A +#define GL_TIMEOUT_EXPIRED 0x911B +#define GL_CONDITION_SATISFIED 0x911C +#define GL_WAIT_FAILED 0x911D +#define GL_SYNC_FLUSH_COMMANDS_BIT 0x00000001 +#define GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFFull +#endif + +#ifndef GL_ARB_texture_multisample +#define GL_SAMPLE_POSITION 0x8E50 +#define GL_SAMPLE_MASK 0x8E51 +#define GL_SAMPLE_MASK_VALUE 0x8E52 +#define GL_MAX_SAMPLE_MASK_WORDS 0x8E59 +#define GL_TEXTURE_2D_MULTISAMPLE 0x9100 +#define GL_PROXY_TEXTURE_2D_MULTISAMPLE 0x9101 +#define GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102 +#define GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9103 +#define GL_TEXTURE_BINDING_2D_MULTISAMPLE 0x9104 +#define GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY 0x9105 +#define GL_TEXTURE_SAMPLES 0x9106 +#define GL_TEXTURE_FIXED_SAMPLE_LOCATIONS 0x9107 +#define GL_SAMPLER_2D_MULTISAMPLE 0x9108 +#define GL_INT_SAMPLER_2D_MULTISAMPLE 0x9109 +#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE 0x910A +#define GL_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910B +#define GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910C +#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910D +#define GL_MAX_COLOR_TEXTURE_SAMPLES 0x910E +#define GL_MAX_DEPTH_TEXTURE_SAMPLES 0x910F +#define GL_MAX_INTEGER_SAMPLES 0x9110 +#endif + +#ifndef GL_ARB_vertex_array_bgra +/* reuse GL_BGRA */ +#endif + +#ifndef GL_ARB_draw_buffers_blend +#endif + +#ifndef GL_ARB_sample_shading +#define GL_SAMPLE_SHADING_ARB 0x8C36 +#define GL_MIN_SAMPLE_SHADING_VALUE_ARB 0x8C37 +#endif + +#ifndef GL_ARB_texture_cube_map_array +#define GL_TEXTURE_CUBE_MAP_ARRAY_ARB 0x9009 +#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY_ARB 0x900A +#define GL_PROXY_TEXTURE_CUBE_MAP_ARRAY_ARB 0x900B +#define GL_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900C +#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW_ARB 0x900D +#define GL_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900E +#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900F +#endif + +#ifndef GL_ARB_texture_gather +#define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET_ARB 0x8E5E +#define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET_ARB 0x8E5F +#endif + +#ifndef GL_ARB_texture_query_lod +#endif + +#ifndef GL_ARB_shading_language_include +#define GL_SHADER_INCLUDE_ARB 0x8DAE +#define GL_NAMED_STRING_LENGTH_ARB 0x8DE9 +#define GL_NAMED_STRING_TYPE_ARB 0x8DEA +#endif + +#ifndef GL_ARB_texture_compression_bptc +#define GL_COMPRESSED_RGBA_BPTC_UNORM_ARB 0x8E8C +#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB 0x8E8D +#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB 0x8E8E +#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB 0x8E8F +#endif + +#ifndef GL_ARB_blend_func_extended +#define GL_SRC1_COLOR 0x88F9 +/* reuse GL_SRC1_ALPHA */ +#define GL_ONE_MINUS_SRC1_COLOR 0x88FA +#define GL_ONE_MINUS_SRC1_ALPHA 0x88FB +#define GL_MAX_DUAL_SOURCE_DRAW_BUFFERS 0x88FC +#endif + +#ifndef GL_ARB_explicit_attrib_location +#endif + +#ifndef GL_ARB_occlusion_query2 +#define GL_ANY_SAMPLES_PASSED 0x8C2F +#endif + +#ifndef GL_ARB_sampler_objects +#define GL_SAMPLER_BINDING 0x8919 +#endif + +#ifndef GL_ARB_shader_bit_encoding +#endif + +#ifndef GL_ARB_texture_rgb10_a2ui +#define GL_RGB10_A2UI 0x906F +#endif + +#ifndef GL_ARB_texture_swizzle +#define GL_TEXTURE_SWIZZLE_R 0x8E42 +#define GL_TEXTURE_SWIZZLE_G 0x8E43 +#define GL_TEXTURE_SWIZZLE_B 0x8E44 +#define GL_TEXTURE_SWIZZLE_A 0x8E45 +#define GL_TEXTURE_SWIZZLE_RGBA 0x8E46 +#endif + +#ifndef GL_ARB_timer_query +#define GL_TIME_ELAPSED 0x88BF +#define GL_TIMESTAMP 0x8E28 +#endif + +#ifndef GL_ARB_vertex_type_2_10_10_10_rev +/* reuse GL_UNSIGNED_INT_2_10_10_10_REV */ +#define GL_INT_2_10_10_10_REV 0x8D9F +#endif + +#ifndef GL_ARB_draw_indirect +#define GL_DRAW_INDIRECT_BUFFER 0x8F3F +#define GL_DRAW_INDIRECT_BUFFER_BINDING 0x8F43 +#endif + +#ifndef GL_ARB_gpu_shader5 +#define GL_GEOMETRY_SHADER_INVOCATIONS 0x887F +#define GL_MAX_GEOMETRY_SHADER_INVOCATIONS 0x8E5A +#define GL_MIN_FRAGMENT_INTERPOLATION_OFFSET 0x8E5B +#define GL_MAX_FRAGMENT_INTERPOLATION_OFFSET 0x8E5C +#define GL_FRAGMENT_INTERPOLATION_OFFSET_BITS 0x8E5D +/* reuse GL_MAX_VERTEX_STREAMS */ +#endif + +#ifndef GL_ARB_gpu_shader_fp64 +/* reuse GL_DOUBLE */ +#define GL_DOUBLE_VEC2 0x8FFC +#define GL_DOUBLE_VEC3 0x8FFD +#define GL_DOUBLE_VEC4 0x8FFE +#define GL_DOUBLE_MAT2 0x8F46 +#define GL_DOUBLE_MAT3 0x8F47 +#define GL_DOUBLE_MAT4 0x8F48 +#define GL_DOUBLE_MAT2x3 0x8F49 +#define GL_DOUBLE_MAT2x4 0x8F4A +#define GL_DOUBLE_MAT3x2 0x8F4B +#define GL_DOUBLE_MAT3x4 0x8F4C +#define GL_DOUBLE_MAT4x2 0x8F4D +#define GL_DOUBLE_MAT4x3 0x8F4E +#endif + +#ifndef GL_ARB_shader_subroutine +#define GL_ACTIVE_SUBROUTINES 0x8DE5 +#define GL_ACTIVE_SUBROUTINE_UNIFORMS 0x8DE6 +#define GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS 0x8E47 +#define GL_ACTIVE_SUBROUTINE_MAX_LENGTH 0x8E48 +#define GL_ACTIVE_SUBROUTINE_UNIFORM_MAX_LENGTH 0x8E49 +#define GL_MAX_SUBROUTINES 0x8DE7 +#define GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS 0x8DE8 +#define GL_NUM_COMPATIBLE_SUBROUTINES 0x8E4A +#define GL_COMPATIBLE_SUBROUTINES 0x8E4B +/* reuse GL_UNIFORM_SIZE */ +/* reuse GL_UNIFORM_NAME_LENGTH */ +#endif + +#ifndef GL_ARB_tessellation_shader +#define GL_PATCHES 0x000E +#define GL_PATCH_VERTICES 0x8E72 +#define GL_PATCH_DEFAULT_INNER_LEVEL 0x8E73 +#define GL_PATCH_DEFAULT_OUTER_LEVEL 0x8E74 +#define GL_TESS_CONTROL_OUTPUT_VERTICES 0x8E75 +#define GL_TESS_GEN_MODE 0x8E76 +#define GL_TESS_GEN_SPACING 0x8E77 +#define GL_TESS_GEN_VERTEX_ORDER 0x8E78 +#define GL_TESS_GEN_POINT_MODE 0x8E79 +/* reuse GL_TRIANGLES */ +/* reuse GL_QUADS */ +#define GL_ISOLINES 0x8E7A +/* reuse GL_EQUAL */ +#define GL_FRACTIONAL_ODD 0x8E7B +#define GL_FRACTIONAL_EVEN 0x8E7C +/* reuse GL_CCW */ +/* reuse GL_CW */ +#define GL_MAX_PATCH_VERTICES 0x8E7D +#define GL_MAX_TESS_GEN_LEVEL 0x8E7E +#define GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E7F +#define GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E80 +#define GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS 0x8E81 +#define GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS 0x8E82 +#define GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS 0x8E83 +#define GL_MAX_TESS_PATCH_COMPONENTS 0x8E84 +#define GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS 0x8E85 +#define GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS 0x8E86 +#define GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS 0x8E89 +#define GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS 0x8E8A +#define GL_MAX_TESS_CONTROL_INPUT_COMPONENTS 0x886C +#define GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS 0x886D +#define GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E1E +#define GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E1F +#define GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_CONTROL_SHADER 0x84F0 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_EVALUATION_SHADER 0x84F1 +#define GL_TESS_EVALUATION_SHADER 0x8E87 +#define GL_TESS_CONTROL_SHADER 0x8E88 +#endif + +#ifndef GL_ARB_texture_buffer_object_rgb32 +/* reuse GL_RGB32F */ +/* reuse GL_RGB32UI */ +/* reuse GL_RGB32I */ +#endif + +#ifndef GL_ARB_transform_feedback2 +#define GL_TRANSFORM_FEEDBACK 0x8E22 +#define GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED 0x8E23 +#define GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE 0x8E24 +#define GL_TRANSFORM_FEEDBACK_BINDING 0x8E25 +#endif + +#ifndef GL_ARB_transform_feedback3 +#define GL_MAX_TRANSFORM_FEEDBACK_BUFFERS 0x8E70 +#define GL_MAX_VERTEX_STREAMS 0x8E71 +#endif + +#ifndef GL_ARB_ES2_compatibility +#define GL_FIXED 0x140C +#define GL_IMPLEMENTATION_COLOR_READ_TYPE 0x8B9A +#define GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B +#define GL_LOW_FLOAT 0x8DF0 +#define GL_MEDIUM_FLOAT 0x8DF1 +#define GL_HIGH_FLOAT 0x8DF2 +#define GL_LOW_INT 0x8DF3 +#define GL_MEDIUM_INT 0x8DF4 +#define GL_HIGH_INT 0x8DF5 +#define GL_SHADER_COMPILER 0x8DFA +#define GL_NUM_SHADER_BINARY_FORMATS 0x8DF9 +#define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB +#define GL_MAX_VARYING_VECTORS 0x8DFC +#define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD +#endif + +#ifndef GL_ARB_get_program_binary +#define GL_PROGRAM_BINARY_RETRIEVABLE_HINT 0x8257 +#define GL_PROGRAM_BINARY_LENGTH 0x8741 +#define GL_NUM_PROGRAM_BINARY_FORMATS 0x87FE +#define GL_PROGRAM_BINARY_FORMATS 0x87FF +#endif + +#ifndef GL_ARB_separate_shader_objects +#define GL_VERTEX_SHADER_BIT 0x00000001 +#define GL_FRAGMENT_SHADER_BIT 0x00000002 +#define GL_GEOMETRY_SHADER_BIT 0x00000004 +#define GL_TESS_CONTROL_SHADER_BIT 0x00000008 +#define GL_TESS_EVALUATION_SHADER_BIT 0x00000010 +#define GL_ALL_SHADER_BITS 0xFFFFFFFF +#define GL_PROGRAM_SEPARABLE 0x8258 +#define GL_ACTIVE_PROGRAM 0x8259 +#define GL_PROGRAM_PIPELINE_BINDING 0x825A +#endif + +#ifndef GL_ARB_shader_precision +#endif + +#ifndef GL_ARB_vertex_attrib_64bit +/* reuse GL_RGB32I */ +/* reuse GL_DOUBLE_VEC2 */ +/* reuse GL_DOUBLE_VEC3 */ +/* reuse GL_DOUBLE_VEC4 */ +/* reuse GL_DOUBLE_MAT2 */ +/* reuse GL_DOUBLE_MAT3 */ +/* reuse GL_DOUBLE_MAT4 */ +/* reuse GL_DOUBLE_MAT2x3 */ +/* reuse GL_DOUBLE_MAT2x4 */ +/* reuse GL_DOUBLE_MAT3x2 */ +/* reuse GL_DOUBLE_MAT3x4 */ +/* reuse GL_DOUBLE_MAT4x2 */ +/* reuse GL_DOUBLE_MAT4x3 */ +#endif + +#ifndef GL_ARB_viewport_array +/* reuse GL_SCISSOR_BOX */ +/* reuse GL_VIEWPORT */ +/* reuse GL_DEPTH_RANGE */ +/* reuse GL_SCISSOR_TEST */ +#define GL_MAX_VIEWPORTS 0x825B +#define GL_VIEWPORT_SUBPIXEL_BITS 0x825C +#define GL_VIEWPORT_BOUNDS_RANGE 0x825D +#define GL_LAYER_PROVOKING_VERTEX 0x825E +#define GL_VIEWPORT_INDEX_PROVOKING_VERTEX 0x825F +#define GL_UNDEFINED_VERTEX 0x8260 +/* reuse GL_FIRST_VERTEX_CONVENTION */ +/* reuse GL_LAST_VERTEX_CONVENTION */ +/* reuse GL_PROVOKING_VERTEX */ +#endif + +#ifndef GL_ARB_cl_event +#define GL_SYNC_CL_EVENT_ARB 0x8240 +#define GL_SYNC_CL_EVENT_COMPLETE_ARB 0x8241 +#endif + +#ifndef GL_ARB_debug_output +#define GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB 0x8242 +#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_ARB 0x8243 +#define GL_DEBUG_CALLBACK_FUNCTION_ARB 0x8244 +#define GL_DEBUG_CALLBACK_USER_PARAM_ARB 0x8245 +#define GL_DEBUG_SOURCE_API_ARB 0x8246 +#define GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB 0x8247 +#define GL_DEBUG_SOURCE_SHADER_COMPILER_ARB 0x8248 +#define GL_DEBUG_SOURCE_THIRD_PARTY_ARB 0x8249 +#define GL_DEBUG_SOURCE_APPLICATION_ARB 0x824A +#define GL_DEBUG_SOURCE_OTHER_ARB 0x824B +#define GL_DEBUG_TYPE_ERROR_ARB 0x824C +#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB 0x824D +#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB 0x824E +#define GL_DEBUG_TYPE_PORTABILITY_ARB 0x824F +#define GL_DEBUG_TYPE_PERFORMANCE_ARB 0x8250 +#define GL_DEBUG_TYPE_OTHER_ARB 0x8251 +#define GL_MAX_DEBUG_MESSAGE_LENGTH_ARB 0x9143 +#define GL_MAX_DEBUG_LOGGED_MESSAGES_ARB 0x9144 +#define GL_DEBUG_LOGGED_MESSAGES_ARB 0x9145 +#define GL_DEBUG_SEVERITY_HIGH_ARB 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM_ARB 0x9147 +#define GL_DEBUG_SEVERITY_LOW_ARB 0x9148 +#endif + +#ifndef GL_ARB_robustness +/* reuse GL_NO_ERROR */ +#define GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB 0x00000004 +#define GL_LOSE_CONTEXT_ON_RESET_ARB 0x8252 +#define GL_GUILTY_CONTEXT_RESET_ARB 0x8253 +#define GL_INNOCENT_CONTEXT_RESET_ARB 0x8254 +#define GL_UNKNOWN_CONTEXT_RESET_ARB 0x8255 +#define GL_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 +#define GL_NO_RESET_NOTIFICATION_ARB 0x8261 +#endif + +#ifndef GL_ARB_shader_stencil_export +#endif + +#ifndef GL_EXT_abgr +#define GL_ABGR_EXT 0x8000 +#endif + +#ifndef GL_EXT_blend_color +#define GL_CONSTANT_COLOR_EXT 0x8001 +#define GL_ONE_MINUS_CONSTANT_COLOR_EXT 0x8002 +#define GL_CONSTANT_ALPHA_EXT 0x8003 +#define GL_ONE_MINUS_CONSTANT_ALPHA_EXT 0x8004 +#define GL_BLEND_COLOR_EXT 0x8005 +#endif + +#ifndef GL_EXT_polygon_offset +#define GL_POLYGON_OFFSET_EXT 0x8037 +#define GL_POLYGON_OFFSET_FACTOR_EXT 0x8038 +#define GL_POLYGON_OFFSET_BIAS_EXT 0x8039 +#endif + +#ifndef GL_EXT_texture +#define GL_ALPHA4_EXT 0x803B +#define GL_ALPHA8_EXT 0x803C +#define GL_ALPHA12_EXT 0x803D +#define GL_ALPHA16_EXT 0x803E +#define GL_LUMINANCE4_EXT 0x803F +#define GL_LUMINANCE8_EXT 0x8040 +#define GL_LUMINANCE12_EXT 0x8041 +#define GL_LUMINANCE16_EXT 0x8042 +#define GL_LUMINANCE4_ALPHA4_EXT 0x8043 +#define GL_LUMINANCE6_ALPHA2_EXT 0x8044 +#define GL_LUMINANCE8_ALPHA8_EXT 0x8045 +#define GL_LUMINANCE12_ALPHA4_EXT 0x8046 +#define GL_LUMINANCE12_ALPHA12_EXT 0x8047 +#define GL_LUMINANCE16_ALPHA16_EXT 0x8048 +#define GL_INTENSITY_EXT 0x8049 +#define GL_INTENSITY4_EXT 0x804A +#define GL_INTENSITY8_EXT 0x804B +#define GL_INTENSITY12_EXT 0x804C +#define GL_INTENSITY16_EXT 0x804D +#define GL_RGB2_EXT 0x804E +#define GL_RGB4_EXT 0x804F +#define GL_RGB5_EXT 0x8050 +#define GL_RGB8_EXT 0x8051 +#define GL_RGB10_EXT 0x8052 +#define GL_RGB12_EXT 0x8053 +#define GL_RGB16_EXT 0x8054 +#define GL_RGBA2_EXT 0x8055 +#define GL_RGBA4_EXT 0x8056 +#define GL_RGB5_A1_EXT 0x8057 +#define GL_RGBA8_EXT 0x8058 +#define GL_RGB10_A2_EXT 0x8059 +#define GL_RGBA12_EXT 0x805A +#define GL_RGBA16_EXT 0x805B +#define GL_TEXTURE_RED_SIZE_EXT 0x805C +#define GL_TEXTURE_GREEN_SIZE_EXT 0x805D +#define GL_TEXTURE_BLUE_SIZE_EXT 0x805E +#define GL_TEXTURE_ALPHA_SIZE_EXT 0x805F +#define GL_TEXTURE_LUMINANCE_SIZE_EXT 0x8060 +#define GL_TEXTURE_INTENSITY_SIZE_EXT 0x8061 +#define GL_REPLACE_EXT 0x8062 +#define GL_PROXY_TEXTURE_1D_EXT 0x8063 +#define GL_PROXY_TEXTURE_2D_EXT 0x8064 +#define GL_TEXTURE_TOO_LARGE_EXT 0x8065 +#endif + +#ifndef GL_EXT_texture3D +#define GL_PACK_SKIP_IMAGES_EXT 0x806B +#define GL_PACK_IMAGE_HEIGHT_EXT 0x806C +#define GL_UNPACK_SKIP_IMAGES_EXT 0x806D +#define GL_UNPACK_IMAGE_HEIGHT_EXT 0x806E +#define GL_TEXTURE_3D_EXT 0x806F +#define GL_PROXY_TEXTURE_3D_EXT 0x8070 +#define GL_TEXTURE_DEPTH_EXT 0x8071 +#define GL_TEXTURE_WRAP_R_EXT 0x8072 +#define GL_MAX_3D_TEXTURE_SIZE_EXT 0x8073 +#endif + +#ifndef GL_SGIS_texture_filter4 +#define GL_FILTER4_SGIS 0x8146 +#define GL_TEXTURE_FILTER4_SIZE_SGIS 0x8147 +#endif + +#ifndef GL_EXT_subtexture +#endif + +#ifndef GL_EXT_copy_texture +#endif + +#ifndef GL_EXT_histogram +#define GL_HISTOGRAM_EXT 0x8024 +#define GL_PROXY_HISTOGRAM_EXT 0x8025 +#define GL_HISTOGRAM_WIDTH_EXT 0x8026 +#define GL_HISTOGRAM_FORMAT_EXT 0x8027 +#define GL_HISTOGRAM_RED_SIZE_EXT 0x8028 +#define GL_HISTOGRAM_GREEN_SIZE_EXT 0x8029 +#define GL_HISTOGRAM_BLUE_SIZE_EXT 0x802A +#define GL_HISTOGRAM_ALPHA_SIZE_EXT 0x802B +#define GL_HISTOGRAM_LUMINANCE_SIZE_EXT 0x802C +#define GL_HISTOGRAM_SINK_EXT 0x802D +#define GL_MINMAX_EXT 0x802E +#define GL_MINMAX_FORMAT_EXT 0x802F +#define GL_MINMAX_SINK_EXT 0x8030 +#define GL_TABLE_TOO_LARGE_EXT 0x8031 +#endif + +#ifndef GL_EXT_convolution +#define GL_CONVOLUTION_1D_EXT 0x8010 +#define GL_CONVOLUTION_2D_EXT 0x8011 +#define GL_SEPARABLE_2D_EXT 0x8012 +#define GL_CONVOLUTION_BORDER_MODE_EXT 0x8013 +#define GL_CONVOLUTION_FILTER_SCALE_EXT 0x8014 +#define GL_CONVOLUTION_FILTER_BIAS_EXT 0x8015 +#define GL_REDUCE_EXT 0x8016 +#define GL_CONVOLUTION_FORMAT_EXT 0x8017 +#define GL_CONVOLUTION_WIDTH_EXT 0x8018 +#define GL_CONVOLUTION_HEIGHT_EXT 0x8019 +#define GL_MAX_CONVOLUTION_WIDTH_EXT 0x801A +#define GL_MAX_CONVOLUTION_HEIGHT_EXT 0x801B +#define GL_POST_CONVOLUTION_RED_SCALE_EXT 0x801C +#define GL_POST_CONVOLUTION_GREEN_SCALE_EXT 0x801D +#define GL_POST_CONVOLUTION_BLUE_SCALE_EXT 0x801E +#define GL_POST_CONVOLUTION_ALPHA_SCALE_EXT 0x801F +#define GL_POST_CONVOLUTION_RED_BIAS_EXT 0x8020 +#define GL_POST_CONVOLUTION_GREEN_BIAS_EXT 0x8021 +#define GL_POST_CONVOLUTION_BLUE_BIAS_EXT 0x8022 +#define GL_POST_CONVOLUTION_ALPHA_BIAS_EXT 0x8023 +#endif + +#ifndef GL_SGI_color_matrix +#define GL_COLOR_MATRIX_SGI 0x80B1 +#define GL_COLOR_MATRIX_STACK_DEPTH_SGI 0x80B2 +#define GL_MAX_COLOR_MATRIX_STACK_DEPTH_SGI 0x80B3 +#define GL_POST_COLOR_MATRIX_RED_SCALE_SGI 0x80B4 +#define GL_POST_COLOR_MATRIX_GREEN_SCALE_SGI 0x80B5 +#define GL_POST_COLOR_MATRIX_BLUE_SCALE_SGI 0x80B6 +#define GL_POST_COLOR_MATRIX_ALPHA_SCALE_SGI 0x80B7 +#define GL_POST_COLOR_MATRIX_RED_BIAS_SGI 0x80B8 +#define GL_POST_COLOR_MATRIX_GREEN_BIAS_SGI 0x80B9 +#define GL_POST_COLOR_MATRIX_BLUE_BIAS_SGI 0x80BA +#define GL_POST_COLOR_MATRIX_ALPHA_BIAS_SGI 0x80BB +#endif + +#ifndef GL_SGI_color_table +#define GL_COLOR_TABLE_SGI 0x80D0 +#define GL_POST_CONVOLUTION_COLOR_TABLE_SGI 0x80D1 +#define GL_POST_COLOR_MATRIX_COLOR_TABLE_SGI 0x80D2 +#define GL_PROXY_COLOR_TABLE_SGI 0x80D3 +#define GL_PROXY_POST_CONVOLUTION_COLOR_TABLE_SGI 0x80D4 +#define GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE_SGI 0x80D5 +#define GL_COLOR_TABLE_SCALE_SGI 0x80D6 +#define GL_COLOR_TABLE_BIAS_SGI 0x80D7 +#define GL_COLOR_TABLE_FORMAT_SGI 0x80D8 +#define GL_COLOR_TABLE_WIDTH_SGI 0x80D9 +#define GL_COLOR_TABLE_RED_SIZE_SGI 0x80DA +#define GL_COLOR_TABLE_GREEN_SIZE_SGI 0x80DB +#define GL_COLOR_TABLE_BLUE_SIZE_SGI 0x80DC +#define GL_COLOR_TABLE_ALPHA_SIZE_SGI 0x80DD +#define GL_COLOR_TABLE_LUMINANCE_SIZE_SGI 0x80DE +#define GL_COLOR_TABLE_INTENSITY_SIZE_SGI 0x80DF +#endif + +#ifndef GL_SGIS_pixel_texture +#define GL_PIXEL_TEXTURE_SGIS 0x8353 +#define GL_PIXEL_FRAGMENT_RGB_SOURCE_SGIS 0x8354 +#define GL_PIXEL_FRAGMENT_ALPHA_SOURCE_SGIS 0x8355 +#define GL_PIXEL_GROUP_COLOR_SGIS 0x8356 +#endif + +#ifndef GL_SGIX_pixel_texture +#define GL_PIXEL_TEX_GEN_SGIX 0x8139 +#define GL_PIXEL_TEX_GEN_MODE_SGIX 0x832B +#endif + +#ifndef GL_SGIS_texture4D +#define GL_PACK_SKIP_VOLUMES_SGIS 0x8130 +#define GL_PACK_IMAGE_DEPTH_SGIS 0x8131 +#define GL_UNPACK_SKIP_VOLUMES_SGIS 0x8132 +#define GL_UNPACK_IMAGE_DEPTH_SGIS 0x8133 +#define GL_TEXTURE_4D_SGIS 0x8134 +#define GL_PROXY_TEXTURE_4D_SGIS 0x8135 +#define GL_TEXTURE_4DSIZE_SGIS 0x8136 +#define GL_TEXTURE_WRAP_Q_SGIS 0x8137 +#define GL_MAX_4D_TEXTURE_SIZE_SGIS 0x8138 +#define GL_TEXTURE_4D_BINDING_SGIS 0x814F +#endif + +#ifndef GL_SGI_texture_color_table +#define GL_TEXTURE_COLOR_TABLE_SGI 0x80BC +#define GL_PROXY_TEXTURE_COLOR_TABLE_SGI 0x80BD +#endif + +#ifndef GL_EXT_cmyka +#define GL_CMYK_EXT 0x800C +#define GL_CMYKA_EXT 0x800D +#define GL_PACK_CMYK_HINT_EXT 0x800E +#define GL_UNPACK_CMYK_HINT_EXT 0x800F +#endif + +#ifndef GL_EXT_texture_object +#define GL_TEXTURE_PRIORITY_EXT 0x8066 +#define GL_TEXTURE_RESIDENT_EXT 0x8067 +#define GL_TEXTURE_1D_BINDING_EXT 0x8068 +#define GL_TEXTURE_2D_BINDING_EXT 0x8069 +#define GL_TEXTURE_3D_BINDING_EXT 0x806A +#endif + +#ifndef GL_SGIS_detail_texture +#define GL_DETAIL_TEXTURE_2D_SGIS 0x8095 +#define GL_DETAIL_TEXTURE_2D_BINDING_SGIS 0x8096 +#define GL_LINEAR_DETAIL_SGIS 0x8097 +#define GL_LINEAR_DETAIL_ALPHA_SGIS 0x8098 +#define GL_LINEAR_DETAIL_COLOR_SGIS 0x8099 +#define GL_DETAIL_TEXTURE_LEVEL_SGIS 0x809A +#define GL_DETAIL_TEXTURE_MODE_SGIS 0x809B +#define GL_DETAIL_TEXTURE_FUNC_POINTS_SGIS 0x809C +#endif + +#ifndef GL_SGIS_sharpen_texture +#define GL_LINEAR_SHARPEN_SGIS 0x80AD +#define GL_LINEAR_SHARPEN_ALPHA_SGIS 0x80AE +#define GL_LINEAR_SHARPEN_COLOR_SGIS 0x80AF +#define GL_SHARPEN_TEXTURE_FUNC_POINTS_SGIS 0x80B0 +#endif + +#ifndef GL_EXT_packed_pixels +#define GL_UNSIGNED_BYTE_3_3_2_EXT 0x8032 +#define GL_UNSIGNED_SHORT_4_4_4_4_EXT 0x8033 +#define GL_UNSIGNED_SHORT_5_5_5_1_EXT 0x8034 +#define GL_UNSIGNED_INT_8_8_8_8_EXT 0x8035 +#define GL_UNSIGNED_INT_10_10_10_2_EXT 0x8036 +#endif + +#ifndef GL_SGIS_texture_lod +#define GL_TEXTURE_MIN_LOD_SGIS 0x813A +#define GL_TEXTURE_MAX_LOD_SGIS 0x813B +#define GL_TEXTURE_BASE_LEVEL_SGIS 0x813C +#define GL_TEXTURE_MAX_LEVEL_SGIS 0x813D +#endif + +#ifndef GL_SGIS_multisample +#define GL_MULTISAMPLE_SGIS 0x809D +#define GL_SAMPLE_ALPHA_TO_MASK_SGIS 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE_SGIS 0x809F +#define GL_SAMPLE_MASK_SGIS 0x80A0 +#define GL_1PASS_SGIS 0x80A1 +#define GL_2PASS_0_SGIS 0x80A2 +#define GL_2PASS_1_SGIS 0x80A3 +#define GL_4PASS_0_SGIS 0x80A4 +#define GL_4PASS_1_SGIS 0x80A5 +#define GL_4PASS_2_SGIS 0x80A6 +#define GL_4PASS_3_SGIS 0x80A7 +#define GL_SAMPLE_BUFFERS_SGIS 0x80A8 +#define GL_SAMPLES_SGIS 0x80A9 +#define GL_SAMPLE_MASK_VALUE_SGIS 0x80AA +#define GL_SAMPLE_MASK_INVERT_SGIS 0x80AB +#define GL_SAMPLE_PATTERN_SGIS 0x80AC +#endif + +#ifndef GL_EXT_rescale_normal +#define GL_RESCALE_NORMAL_EXT 0x803A +#endif + +#ifndef GL_EXT_vertex_array +#define GL_VERTEX_ARRAY_EXT 0x8074 +#define GL_NORMAL_ARRAY_EXT 0x8075 +#define GL_COLOR_ARRAY_EXT 0x8076 +#define GL_INDEX_ARRAY_EXT 0x8077 +#define GL_TEXTURE_COORD_ARRAY_EXT 0x8078 +#define GL_EDGE_FLAG_ARRAY_EXT 0x8079 +#define GL_VERTEX_ARRAY_SIZE_EXT 0x807A +#define GL_VERTEX_ARRAY_TYPE_EXT 0x807B +#define GL_VERTEX_ARRAY_STRIDE_EXT 0x807C +#define GL_VERTEX_ARRAY_COUNT_EXT 0x807D +#define GL_NORMAL_ARRAY_TYPE_EXT 0x807E +#define GL_NORMAL_ARRAY_STRIDE_EXT 0x807F +#define GL_NORMAL_ARRAY_COUNT_EXT 0x8080 +#define GL_COLOR_ARRAY_SIZE_EXT 0x8081 +#define GL_COLOR_ARRAY_TYPE_EXT 0x8082 +#define GL_COLOR_ARRAY_STRIDE_EXT 0x8083 +#define GL_COLOR_ARRAY_COUNT_EXT 0x8084 +#define GL_INDEX_ARRAY_TYPE_EXT 0x8085 +#define GL_INDEX_ARRAY_STRIDE_EXT 0x8086 +#define GL_INDEX_ARRAY_COUNT_EXT 0x8087 +#define GL_TEXTURE_COORD_ARRAY_SIZE_EXT 0x8088 +#define GL_TEXTURE_COORD_ARRAY_TYPE_EXT 0x8089 +#define GL_TEXTURE_COORD_ARRAY_STRIDE_EXT 0x808A +#define GL_TEXTURE_COORD_ARRAY_COUNT_EXT 0x808B +#define GL_EDGE_FLAG_ARRAY_STRIDE_EXT 0x808C +#define GL_EDGE_FLAG_ARRAY_COUNT_EXT 0x808D +#define GL_VERTEX_ARRAY_POINTER_EXT 0x808E +#define GL_NORMAL_ARRAY_POINTER_EXT 0x808F +#define GL_COLOR_ARRAY_POINTER_EXT 0x8090 +#define GL_INDEX_ARRAY_POINTER_EXT 0x8091 +#define GL_TEXTURE_COORD_ARRAY_POINTER_EXT 0x8092 +#define GL_EDGE_FLAG_ARRAY_POINTER_EXT 0x8093 +#endif + +#ifndef GL_EXT_misc_attribute +#endif + +#ifndef GL_SGIS_generate_mipmap +#define GL_GENERATE_MIPMAP_SGIS 0x8191 +#define GL_GENERATE_MIPMAP_HINT_SGIS 0x8192 +#endif + +#ifndef GL_SGIX_clipmap +#define GL_LINEAR_CLIPMAP_LINEAR_SGIX 0x8170 +#define GL_TEXTURE_CLIPMAP_CENTER_SGIX 0x8171 +#define GL_TEXTURE_CLIPMAP_FRAME_SGIX 0x8172 +#define GL_TEXTURE_CLIPMAP_OFFSET_SGIX 0x8173 +#define GL_TEXTURE_CLIPMAP_VIRTUAL_DEPTH_SGIX 0x8174 +#define GL_TEXTURE_CLIPMAP_LOD_OFFSET_SGIX 0x8175 +#define GL_TEXTURE_CLIPMAP_DEPTH_SGIX 0x8176 +#define GL_MAX_CLIPMAP_DEPTH_SGIX 0x8177 +#define GL_MAX_CLIPMAP_VIRTUAL_DEPTH_SGIX 0x8178 +#define GL_NEAREST_CLIPMAP_NEAREST_SGIX 0x844D +#define GL_NEAREST_CLIPMAP_LINEAR_SGIX 0x844E +#define GL_LINEAR_CLIPMAP_NEAREST_SGIX 0x844F +#endif + +#ifndef GL_SGIX_shadow +#define GL_TEXTURE_COMPARE_SGIX 0x819A +#define GL_TEXTURE_COMPARE_OPERATOR_SGIX 0x819B +#define GL_TEXTURE_LEQUAL_R_SGIX 0x819C +#define GL_TEXTURE_GEQUAL_R_SGIX 0x819D +#endif + +#ifndef GL_SGIS_texture_edge_clamp +#define GL_CLAMP_TO_EDGE_SGIS 0x812F +#endif + +#ifndef GL_SGIS_texture_border_clamp +#define GL_CLAMP_TO_BORDER_SGIS 0x812D +#endif + +#ifndef GL_EXT_blend_minmax +#define GL_FUNC_ADD_EXT 0x8006 +#define GL_MIN_EXT 0x8007 +#define GL_MAX_EXT 0x8008 +#define GL_BLEND_EQUATION_EXT 0x8009 +#endif + +#ifndef GL_EXT_blend_subtract +#define GL_FUNC_SUBTRACT_EXT 0x800A +#define GL_FUNC_REVERSE_SUBTRACT_EXT 0x800B +#endif + +#ifndef GL_EXT_blend_logic_op +#endif + +#ifndef GL_SGIX_interlace +#define GL_INTERLACE_SGIX 0x8094 +#endif + +#ifndef GL_SGIX_pixel_tiles +#define GL_PIXEL_TILE_BEST_ALIGNMENT_SGIX 0x813E +#define GL_PIXEL_TILE_CACHE_INCREMENT_SGIX 0x813F +#define GL_PIXEL_TILE_WIDTH_SGIX 0x8140 +#define GL_PIXEL_TILE_HEIGHT_SGIX 0x8141 +#define GL_PIXEL_TILE_GRID_WIDTH_SGIX 0x8142 +#define GL_PIXEL_TILE_GRID_HEIGHT_SGIX 0x8143 +#define GL_PIXEL_TILE_GRID_DEPTH_SGIX 0x8144 +#define GL_PIXEL_TILE_CACHE_SIZE_SGIX 0x8145 +#endif + +#ifndef GL_SGIS_texture_select +#define GL_DUAL_ALPHA4_SGIS 0x8110 +#define GL_DUAL_ALPHA8_SGIS 0x8111 +#define GL_DUAL_ALPHA12_SGIS 0x8112 +#define GL_DUAL_ALPHA16_SGIS 0x8113 +#define GL_DUAL_LUMINANCE4_SGIS 0x8114 +#define GL_DUAL_LUMINANCE8_SGIS 0x8115 +#define GL_DUAL_LUMINANCE12_SGIS 0x8116 +#define GL_DUAL_LUMINANCE16_SGIS 0x8117 +#define GL_DUAL_INTENSITY4_SGIS 0x8118 +#define GL_DUAL_INTENSITY8_SGIS 0x8119 +#define GL_DUAL_INTENSITY12_SGIS 0x811A +#define GL_DUAL_INTENSITY16_SGIS 0x811B +#define GL_DUAL_LUMINANCE_ALPHA4_SGIS 0x811C +#define GL_DUAL_LUMINANCE_ALPHA8_SGIS 0x811D +#define GL_QUAD_ALPHA4_SGIS 0x811E +#define GL_QUAD_ALPHA8_SGIS 0x811F +#define GL_QUAD_LUMINANCE4_SGIS 0x8120 +#define GL_QUAD_LUMINANCE8_SGIS 0x8121 +#define GL_QUAD_INTENSITY4_SGIS 0x8122 +#define GL_QUAD_INTENSITY8_SGIS 0x8123 +#define GL_DUAL_TEXTURE_SELECT_SGIS 0x8124 +#define GL_QUAD_TEXTURE_SELECT_SGIS 0x8125 +#endif + +#ifndef GL_SGIX_sprite +#define GL_SPRITE_SGIX 0x8148 +#define GL_SPRITE_MODE_SGIX 0x8149 +#define GL_SPRITE_AXIS_SGIX 0x814A +#define GL_SPRITE_TRANSLATION_SGIX 0x814B +#define GL_SPRITE_AXIAL_SGIX 0x814C +#define GL_SPRITE_OBJECT_ALIGNED_SGIX 0x814D +#define GL_SPRITE_EYE_ALIGNED_SGIX 0x814E +#endif + +#ifndef GL_SGIX_texture_multi_buffer +#define GL_TEXTURE_MULTI_BUFFER_HINT_SGIX 0x812E +#endif + +#ifndef GL_EXT_point_parameters +#define GL_POINT_SIZE_MIN_EXT 0x8126 +#define GL_POINT_SIZE_MAX_EXT 0x8127 +#define GL_POINT_FADE_THRESHOLD_SIZE_EXT 0x8128 +#define GL_DISTANCE_ATTENUATION_EXT 0x8129 +#endif + +#ifndef GL_SGIS_point_parameters +#define GL_POINT_SIZE_MIN_SGIS 0x8126 +#define GL_POINT_SIZE_MAX_SGIS 0x8127 +#define GL_POINT_FADE_THRESHOLD_SIZE_SGIS 0x8128 +#define GL_DISTANCE_ATTENUATION_SGIS 0x8129 +#endif + +#ifndef GL_SGIX_instruments +#define GL_INSTRUMENT_BUFFER_POINTER_SGIX 0x8180 +#define GL_INSTRUMENT_MEASUREMENTS_SGIX 0x8181 +#endif + +#ifndef GL_SGIX_texture_scale_bias +#define GL_POST_TEXTURE_FILTER_BIAS_SGIX 0x8179 +#define GL_POST_TEXTURE_FILTER_SCALE_SGIX 0x817A +#define GL_POST_TEXTURE_FILTER_BIAS_RANGE_SGIX 0x817B +#define GL_POST_TEXTURE_FILTER_SCALE_RANGE_SGIX 0x817C +#endif + +#ifndef GL_SGIX_framezoom +#define GL_FRAMEZOOM_SGIX 0x818B +#define GL_FRAMEZOOM_FACTOR_SGIX 0x818C +#define GL_MAX_FRAMEZOOM_FACTOR_SGIX 0x818D +#endif + +#ifndef GL_SGIX_tag_sample_buffer +#endif + +#ifndef GL_FfdMaskSGIX +#define GL_TEXTURE_DEFORMATION_BIT_SGIX 0x00000001 +#define GL_GEOMETRY_DEFORMATION_BIT_SGIX 0x00000002 +#endif + +#ifndef GL_SGIX_polynomial_ffd +#define GL_GEOMETRY_DEFORMATION_SGIX 0x8194 +#define GL_TEXTURE_DEFORMATION_SGIX 0x8195 +#define GL_DEFORMATIONS_MASK_SGIX 0x8196 +#define GL_MAX_DEFORMATION_ORDER_SGIX 0x8197 +#endif + +#ifndef GL_SGIX_reference_plane +#define GL_REFERENCE_PLANE_SGIX 0x817D +#define GL_REFERENCE_PLANE_EQUATION_SGIX 0x817E +#endif + +#ifndef GL_SGIX_flush_raster +#endif + +#ifndef GL_SGIX_depth_texture +#define GL_DEPTH_COMPONENT16_SGIX 0x81A5 +#define GL_DEPTH_COMPONENT24_SGIX 0x81A6 +#define GL_DEPTH_COMPONENT32_SGIX 0x81A7 +#endif + +#ifndef GL_SGIS_fog_function +#define GL_FOG_FUNC_SGIS 0x812A +#define GL_FOG_FUNC_POINTS_SGIS 0x812B +#define GL_MAX_FOG_FUNC_POINTS_SGIS 0x812C +#endif + +#ifndef GL_SGIX_fog_offset +#define GL_FOG_OFFSET_SGIX 0x8198 +#define GL_FOG_OFFSET_VALUE_SGIX 0x8199 +#endif + +#ifndef GL_HP_image_transform +#define GL_IMAGE_SCALE_X_HP 0x8155 +#define GL_IMAGE_SCALE_Y_HP 0x8156 +#define GL_IMAGE_TRANSLATE_X_HP 0x8157 +#define GL_IMAGE_TRANSLATE_Y_HP 0x8158 +#define GL_IMAGE_ROTATE_ANGLE_HP 0x8159 +#define GL_IMAGE_ROTATE_ORIGIN_X_HP 0x815A +#define GL_IMAGE_ROTATE_ORIGIN_Y_HP 0x815B +#define GL_IMAGE_MAG_FILTER_HP 0x815C +#define GL_IMAGE_MIN_FILTER_HP 0x815D +#define GL_IMAGE_CUBIC_WEIGHT_HP 0x815E +#define GL_CUBIC_HP 0x815F +#define GL_AVERAGE_HP 0x8160 +#define GL_IMAGE_TRANSFORM_2D_HP 0x8161 +#define GL_POST_IMAGE_TRANSFORM_COLOR_TABLE_HP 0x8162 +#define GL_PROXY_POST_IMAGE_TRANSFORM_COLOR_TABLE_HP 0x8163 +#endif + +#ifndef GL_HP_convolution_border_modes +#define GL_IGNORE_BORDER_HP 0x8150 +#define GL_CONSTANT_BORDER_HP 0x8151 +#define GL_REPLICATE_BORDER_HP 0x8153 +#define GL_CONVOLUTION_BORDER_COLOR_HP 0x8154 +#endif + +#ifndef GL_INGR_palette_buffer +#endif + +#ifndef GL_SGIX_texture_add_env +#define GL_TEXTURE_ENV_BIAS_SGIX 0x80BE +#endif + +#ifndef GL_EXT_color_subtable +#endif + +#ifndef GL_PGI_vertex_hints +#define GL_VERTEX_DATA_HINT_PGI 0x1A22A +#define GL_VERTEX_CONSISTENT_HINT_PGI 0x1A22B +#define GL_MATERIAL_SIDE_HINT_PGI 0x1A22C +#define GL_MAX_VERTEX_HINT_PGI 0x1A22D +#define GL_COLOR3_BIT_PGI 0x00010000 +#define GL_COLOR4_BIT_PGI 0x00020000 +#define GL_EDGEFLAG_BIT_PGI 0x00040000 +#define GL_INDEX_BIT_PGI 0x00080000 +#define GL_MAT_AMBIENT_BIT_PGI 0x00100000 +#define GL_MAT_AMBIENT_AND_DIFFUSE_BIT_PGI 0x00200000 +#define GL_MAT_DIFFUSE_BIT_PGI 0x00400000 +#define GL_MAT_EMISSION_BIT_PGI 0x00800000 +#define GL_MAT_COLOR_INDEXES_BIT_PGI 0x01000000 +#define GL_MAT_SHININESS_BIT_PGI 0x02000000 +#define GL_MAT_SPECULAR_BIT_PGI 0x04000000 +#define GL_NORMAL_BIT_PGI 0x08000000 +#define GL_TEXCOORD1_BIT_PGI 0x10000000 +#define GL_TEXCOORD2_BIT_PGI 0x20000000 +#define GL_TEXCOORD3_BIT_PGI 0x40000000 +#define GL_TEXCOORD4_BIT_PGI 0x80000000 +#define GL_VERTEX23_BIT_PGI 0x00000004 +#define GL_VERTEX4_BIT_PGI 0x00000008 +#endif + +#ifndef GL_PGI_misc_hints +#define GL_PREFER_DOUBLEBUFFER_HINT_PGI 0x1A1F8 +#define GL_CONSERVE_MEMORY_HINT_PGI 0x1A1FD +#define GL_RECLAIM_MEMORY_HINT_PGI 0x1A1FE +#define GL_NATIVE_GRAPHICS_HANDLE_PGI 0x1A202 +#define GL_NATIVE_GRAPHICS_BEGIN_HINT_PGI 0x1A203 +#define GL_NATIVE_GRAPHICS_END_HINT_PGI 0x1A204 +#define GL_ALWAYS_FAST_HINT_PGI 0x1A20C +#define GL_ALWAYS_SOFT_HINT_PGI 0x1A20D +#define GL_ALLOW_DRAW_OBJ_HINT_PGI 0x1A20E +#define GL_ALLOW_DRAW_WIN_HINT_PGI 0x1A20F +#define GL_ALLOW_DRAW_FRG_HINT_PGI 0x1A210 +#define GL_ALLOW_DRAW_MEM_HINT_PGI 0x1A211 +#define GL_STRICT_DEPTHFUNC_HINT_PGI 0x1A216 +#define GL_STRICT_LIGHTING_HINT_PGI 0x1A217 +#define GL_STRICT_SCISSOR_HINT_PGI 0x1A218 +#define GL_FULL_STIPPLE_HINT_PGI 0x1A219 +#define GL_CLIP_NEAR_HINT_PGI 0x1A220 +#define GL_CLIP_FAR_HINT_PGI 0x1A221 +#define GL_WIDE_LINE_HINT_PGI 0x1A222 +#define GL_BACK_NORMALS_HINT_PGI 0x1A223 +#endif + +#ifndef GL_EXT_paletted_texture +#define GL_COLOR_INDEX1_EXT 0x80E2 +#define GL_COLOR_INDEX2_EXT 0x80E3 +#define GL_COLOR_INDEX4_EXT 0x80E4 +#define GL_COLOR_INDEX8_EXT 0x80E5 +#define GL_COLOR_INDEX12_EXT 0x80E6 +#define GL_COLOR_INDEX16_EXT 0x80E7 +#define GL_TEXTURE_INDEX_SIZE_EXT 0x80ED +#endif + +#ifndef GL_EXT_clip_volume_hint +#define GL_CLIP_VOLUME_CLIPPING_HINT_EXT 0x80F0 +#endif + +#ifndef GL_SGIX_list_priority +#define GL_LIST_PRIORITY_SGIX 0x8182 +#endif + +#ifndef GL_SGIX_ir_instrument1 +#define GL_IR_INSTRUMENT1_SGIX 0x817F +#endif + +#ifndef GL_SGIX_calligraphic_fragment +#define GL_CALLIGRAPHIC_FRAGMENT_SGIX 0x8183 +#endif + +#ifndef GL_SGIX_texture_lod_bias +#define GL_TEXTURE_LOD_BIAS_S_SGIX 0x818E +#define GL_TEXTURE_LOD_BIAS_T_SGIX 0x818F +#define GL_TEXTURE_LOD_BIAS_R_SGIX 0x8190 +#endif + +#ifndef GL_SGIX_shadow_ambient +#define GL_SHADOW_AMBIENT_SGIX 0x80BF +#endif + +#ifndef GL_EXT_index_texture +#endif + +#ifndef GL_EXT_index_material +#define GL_INDEX_MATERIAL_EXT 0x81B8 +#define GL_INDEX_MATERIAL_PARAMETER_EXT 0x81B9 +#define GL_INDEX_MATERIAL_FACE_EXT 0x81BA +#endif + +#ifndef GL_EXT_index_func +#define GL_INDEX_TEST_EXT 0x81B5 +#define GL_INDEX_TEST_FUNC_EXT 0x81B6 +#define GL_INDEX_TEST_REF_EXT 0x81B7 +#endif + +#ifndef GL_EXT_index_array_formats +#define GL_IUI_V2F_EXT 0x81AD +#define GL_IUI_V3F_EXT 0x81AE +#define GL_IUI_N3F_V2F_EXT 0x81AF +#define GL_IUI_N3F_V3F_EXT 0x81B0 +#define GL_T2F_IUI_V2F_EXT 0x81B1 +#define GL_T2F_IUI_V3F_EXT 0x81B2 +#define GL_T2F_IUI_N3F_V2F_EXT 0x81B3 +#define GL_T2F_IUI_N3F_V3F_EXT 0x81B4 +#endif + +#ifndef GL_EXT_compiled_vertex_array +#define GL_ARRAY_ELEMENT_LOCK_FIRST_EXT 0x81A8 +#define GL_ARRAY_ELEMENT_LOCK_COUNT_EXT 0x81A9 +#endif + +#ifndef GL_EXT_cull_vertex +#define GL_CULL_VERTEX_EXT 0x81AA +#define GL_CULL_VERTEX_EYE_POSITION_EXT 0x81AB +#define GL_CULL_VERTEX_OBJECT_POSITION_EXT 0x81AC +#endif + +#ifndef GL_SGIX_ycrcb +#define GL_YCRCB_422_SGIX 0x81BB +#define GL_YCRCB_444_SGIX 0x81BC +#endif + +#ifndef GL_SGIX_fragment_lighting +#define GL_FRAGMENT_LIGHTING_SGIX 0x8400 +#define GL_FRAGMENT_COLOR_MATERIAL_SGIX 0x8401 +#define GL_FRAGMENT_COLOR_MATERIAL_FACE_SGIX 0x8402 +#define GL_FRAGMENT_COLOR_MATERIAL_PARAMETER_SGIX 0x8403 +#define GL_MAX_FRAGMENT_LIGHTS_SGIX 0x8404 +#define GL_MAX_ACTIVE_LIGHTS_SGIX 0x8405 +#define GL_CURRENT_RASTER_NORMAL_SGIX 0x8406 +#define GL_LIGHT_ENV_MODE_SGIX 0x8407 +#define GL_FRAGMENT_LIGHT_MODEL_LOCAL_VIEWER_SGIX 0x8408 +#define GL_FRAGMENT_LIGHT_MODEL_TWO_SIDE_SGIX 0x8409 +#define GL_FRAGMENT_LIGHT_MODEL_AMBIENT_SGIX 0x840A +#define GL_FRAGMENT_LIGHT_MODEL_NORMAL_INTERPOLATION_SGIX 0x840B +#define GL_FRAGMENT_LIGHT0_SGIX 0x840C +#define GL_FRAGMENT_LIGHT1_SGIX 0x840D +#define GL_FRAGMENT_LIGHT2_SGIX 0x840E +#define GL_FRAGMENT_LIGHT3_SGIX 0x840F +#define GL_FRAGMENT_LIGHT4_SGIX 0x8410 +#define GL_FRAGMENT_LIGHT5_SGIX 0x8411 +#define GL_FRAGMENT_LIGHT6_SGIX 0x8412 +#define GL_FRAGMENT_LIGHT7_SGIX 0x8413 +#endif + +#ifndef GL_IBM_rasterpos_clip +#define GL_RASTER_POSITION_UNCLIPPED_IBM 0x19262 +#endif + +#ifndef GL_HP_texture_lighting +#define GL_TEXTURE_LIGHTING_MODE_HP 0x8167 +#define GL_TEXTURE_POST_SPECULAR_HP 0x8168 +#define GL_TEXTURE_PRE_SPECULAR_HP 0x8169 +#endif + +#ifndef GL_EXT_draw_range_elements +#define GL_MAX_ELEMENTS_VERTICES_EXT 0x80E8 +#define GL_MAX_ELEMENTS_INDICES_EXT 0x80E9 +#endif + +#ifndef GL_WIN_phong_shading +#define GL_PHONG_WIN 0x80EA +#define GL_PHONG_HINT_WIN 0x80EB +#endif + +#ifndef GL_WIN_specular_fog +#define GL_FOG_SPECULAR_TEXTURE_WIN 0x80EC +#endif + +#ifndef GL_EXT_light_texture +#define GL_FRAGMENT_MATERIAL_EXT 0x8349 +#define GL_FRAGMENT_NORMAL_EXT 0x834A +#define GL_FRAGMENT_COLOR_EXT 0x834C +#define GL_ATTENUATION_EXT 0x834D +#define GL_SHADOW_ATTENUATION_EXT 0x834E +#define GL_TEXTURE_APPLICATION_MODE_EXT 0x834F +#define GL_TEXTURE_LIGHT_EXT 0x8350 +#define GL_TEXTURE_MATERIAL_FACE_EXT 0x8351 +#define GL_TEXTURE_MATERIAL_PARAMETER_EXT 0x8352 +/* reuse GL_FRAGMENT_DEPTH_EXT */ +#endif + +#ifndef GL_SGIX_blend_alpha_minmax +#define GL_ALPHA_MIN_SGIX 0x8320 +#define GL_ALPHA_MAX_SGIX 0x8321 +#endif + +#ifndef GL_SGIX_impact_pixel_texture +#define GL_PIXEL_TEX_GEN_Q_CEILING_SGIX 0x8184 +#define GL_PIXEL_TEX_GEN_Q_ROUND_SGIX 0x8185 +#define GL_PIXEL_TEX_GEN_Q_FLOOR_SGIX 0x8186 +#define GL_PIXEL_TEX_GEN_ALPHA_REPLACE_SGIX 0x8187 +#define GL_PIXEL_TEX_GEN_ALPHA_NO_REPLACE_SGIX 0x8188 +#define GL_PIXEL_TEX_GEN_ALPHA_LS_SGIX 0x8189 +#define GL_PIXEL_TEX_GEN_ALPHA_MS_SGIX 0x818A +#endif + +#ifndef GL_EXT_bgra +#define GL_BGR_EXT 0x80E0 +#define GL_BGRA_EXT 0x80E1 +#endif + +#ifndef GL_SGIX_async +#define GL_ASYNC_MARKER_SGIX 0x8329 +#endif + +#ifndef GL_SGIX_async_pixel +#define GL_ASYNC_TEX_IMAGE_SGIX 0x835C +#define GL_ASYNC_DRAW_PIXELS_SGIX 0x835D +#define GL_ASYNC_READ_PIXELS_SGIX 0x835E +#define GL_MAX_ASYNC_TEX_IMAGE_SGIX 0x835F +#define GL_MAX_ASYNC_DRAW_PIXELS_SGIX 0x8360 +#define GL_MAX_ASYNC_READ_PIXELS_SGIX 0x8361 +#endif + +#ifndef GL_SGIX_async_histogram +#define GL_ASYNC_HISTOGRAM_SGIX 0x832C +#define GL_MAX_ASYNC_HISTOGRAM_SGIX 0x832D +#endif + +#ifndef GL_INTEL_texture_scissor +#endif + +#ifndef GL_INTEL_parallel_arrays +#define GL_PARALLEL_ARRAYS_INTEL 0x83F4 +#define GL_VERTEX_ARRAY_PARALLEL_POINTERS_INTEL 0x83F5 +#define GL_NORMAL_ARRAY_PARALLEL_POINTERS_INTEL 0x83F6 +#define GL_COLOR_ARRAY_PARALLEL_POINTERS_INTEL 0x83F7 +#define GL_TEXTURE_COORD_ARRAY_PARALLEL_POINTERS_INTEL 0x83F8 +#endif + +#ifndef GL_HP_occlusion_test +#define GL_OCCLUSION_TEST_HP 0x8165 +#define GL_OCCLUSION_TEST_RESULT_HP 0x8166 +#endif + +#ifndef GL_EXT_pixel_transform +#define GL_PIXEL_TRANSFORM_2D_EXT 0x8330 +#define GL_PIXEL_MAG_FILTER_EXT 0x8331 +#define GL_PIXEL_MIN_FILTER_EXT 0x8332 +#define GL_PIXEL_CUBIC_WEIGHT_EXT 0x8333 +#define GL_CUBIC_EXT 0x8334 +#define GL_AVERAGE_EXT 0x8335 +#define GL_PIXEL_TRANSFORM_2D_STACK_DEPTH_EXT 0x8336 +#define GL_MAX_PIXEL_TRANSFORM_2D_STACK_DEPTH_EXT 0x8337 +#define GL_PIXEL_TRANSFORM_2D_MATRIX_EXT 0x8338 +#endif + +#ifndef GL_EXT_pixel_transform_color_table +#endif + +#ifndef GL_EXT_shared_texture_palette +#define GL_SHARED_TEXTURE_PALETTE_EXT 0x81FB +#endif + +#ifndef GL_EXT_separate_specular_color +#define GL_LIGHT_MODEL_COLOR_CONTROL_EXT 0x81F8 +#define GL_SINGLE_COLOR_EXT 0x81F9 +#define GL_SEPARATE_SPECULAR_COLOR_EXT 0x81FA +#endif + +#ifndef GL_EXT_secondary_color +#define GL_COLOR_SUM_EXT 0x8458 +#define GL_CURRENT_SECONDARY_COLOR_EXT 0x8459 +#define GL_SECONDARY_COLOR_ARRAY_SIZE_EXT 0x845A +#define GL_SECONDARY_COLOR_ARRAY_TYPE_EXT 0x845B +#define GL_SECONDARY_COLOR_ARRAY_STRIDE_EXT 0x845C +#define GL_SECONDARY_COLOR_ARRAY_POINTER_EXT 0x845D +#define GL_SECONDARY_COLOR_ARRAY_EXT 0x845E +#endif + +#ifndef GL_EXT_texture_perturb_normal +#define GL_PERTURB_EXT 0x85AE +#define GL_TEXTURE_NORMAL_EXT 0x85AF +#endif + +#ifndef GL_EXT_multi_draw_arrays +#endif + +#ifndef GL_EXT_fog_coord +#define GL_FOG_COORDINATE_SOURCE_EXT 0x8450 +#define GL_FOG_COORDINATE_EXT 0x8451 +#define GL_FRAGMENT_DEPTH_EXT 0x8452 +#define GL_CURRENT_FOG_COORDINATE_EXT 0x8453 +#define GL_FOG_COORDINATE_ARRAY_TYPE_EXT 0x8454 +#define GL_FOG_COORDINATE_ARRAY_STRIDE_EXT 0x8455 +#define GL_FOG_COORDINATE_ARRAY_POINTER_EXT 0x8456 +#define GL_FOG_COORDINATE_ARRAY_EXT 0x8457 +#endif + +#ifndef GL_REND_screen_coordinates +#define GL_SCREEN_COORDINATES_REND 0x8490 +#define GL_INVERTED_SCREEN_W_REND 0x8491 +#endif + +#ifndef GL_EXT_coordinate_frame +#define GL_TANGENT_ARRAY_EXT 0x8439 +#define GL_BINORMAL_ARRAY_EXT 0x843A +#define GL_CURRENT_TANGENT_EXT 0x843B +#define GL_CURRENT_BINORMAL_EXT 0x843C +#define GL_TANGENT_ARRAY_TYPE_EXT 0x843E +#define GL_TANGENT_ARRAY_STRIDE_EXT 0x843F +#define GL_BINORMAL_ARRAY_TYPE_EXT 0x8440 +#define GL_BINORMAL_ARRAY_STRIDE_EXT 0x8441 +#define GL_TANGENT_ARRAY_POINTER_EXT 0x8442 +#define GL_BINORMAL_ARRAY_POINTER_EXT 0x8443 +#define GL_MAP1_TANGENT_EXT 0x8444 +#define GL_MAP2_TANGENT_EXT 0x8445 +#define GL_MAP1_BINORMAL_EXT 0x8446 +#define GL_MAP2_BINORMAL_EXT 0x8447 +#endif + +#ifndef GL_EXT_texture_env_combine +#define GL_COMBINE_EXT 0x8570 +#define GL_COMBINE_RGB_EXT 0x8571 +#define GL_COMBINE_ALPHA_EXT 0x8572 +#define GL_RGB_SCALE_EXT 0x8573 +#define GL_ADD_SIGNED_EXT 0x8574 +#define GL_INTERPOLATE_EXT 0x8575 +#define GL_CONSTANT_EXT 0x8576 +#define GL_PRIMARY_COLOR_EXT 0x8577 +#define GL_PREVIOUS_EXT 0x8578 +#define GL_SOURCE0_RGB_EXT 0x8580 +#define GL_SOURCE1_RGB_EXT 0x8581 +#define GL_SOURCE2_RGB_EXT 0x8582 +#define GL_SOURCE0_ALPHA_EXT 0x8588 +#define GL_SOURCE1_ALPHA_EXT 0x8589 +#define GL_SOURCE2_ALPHA_EXT 0x858A +#define GL_OPERAND0_RGB_EXT 0x8590 +#define GL_OPERAND1_RGB_EXT 0x8591 +#define GL_OPERAND2_RGB_EXT 0x8592 +#define GL_OPERAND0_ALPHA_EXT 0x8598 +#define GL_OPERAND1_ALPHA_EXT 0x8599 +#define GL_OPERAND2_ALPHA_EXT 0x859A +#endif + +#ifndef GL_APPLE_specular_vector +#define GL_LIGHT_MODEL_SPECULAR_VECTOR_APPLE 0x85B0 +#endif + +#ifndef GL_APPLE_transform_hint +#define GL_TRANSFORM_HINT_APPLE 0x85B1 +#endif + +#ifndef GL_SGIX_fog_scale +#define GL_FOG_SCALE_SGIX 0x81FC +#define GL_FOG_SCALE_VALUE_SGIX 0x81FD +#endif + +#ifndef GL_SUNX_constant_data +#define GL_UNPACK_CONSTANT_DATA_SUNX 0x81D5 +#define GL_TEXTURE_CONSTANT_DATA_SUNX 0x81D6 +#endif + +#ifndef GL_SUN_global_alpha +#define GL_GLOBAL_ALPHA_SUN 0x81D9 +#define GL_GLOBAL_ALPHA_FACTOR_SUN 0x81DA +#endif + +#ifndef GL_SUN_triangle_list +#define GL_RESTART_SUN 0x0001 +#define GL_REPLACE_MIDDLE_SUN 0x0002 +#define GL_REPLACE_OLDEST_SUN 0x0003 +#define GL_TRIANGLE_LIST_SUN 0x81D7 +#define GL_REPLACEMENT_CODE_SUN 0x81D8 +#define GL_REPLACEMENT_CODE_ARRAY_SUN 0x85C0 +#define GL_REPLACEMENT_CODE_ARRAY_TYPE_SUN 0x85C1 +#define GL_REPLACEMENT_CODE_ARRAY_STRIDE_SUN 0x85C2 +#define GL_REPLACEMENT_CODE_ARRAY_POINTER_SUN 0x85C3 +#define GL_R1UI_V3F_SUN 0x85C4 +#define GL_R1UI_C4UB_V3F_SUN 0x85C5 +#define GL_R1UI_C3F_V3F_SUN 0x85C6 +#define GL_R1UI_N3F_V3F_SUN 0x85C7 +#define GL_R1UI_C4F_N3F_V3F_SUN 0x85C8 +#define GL_R1UI_T2F_V3F_SUN 0x85C9 +#define GL_R1UI_T2F_N3F_V3F_SUN 0x85CA +#define GL_R1UI_T2F_C4F_N3F_V3F_SUN 0x85CB +#endif + +#ifndef GL_SUN_vertex +#endif + +#ifndef GL_EXT_blend_func_separate +#define GL_BLEND_DST_RGB_EXT 0x80C8 +#define GL_BLEND_SRC_RGB_EXT 0x80C9 +#define GL_BLEND_DST_ALPHA_EXT 0x80CA +#define GL_BLEND_SRC_ALPHA_EXT 0x80CB +#endif + +#ifndef GL_INGR_color_clamp +#define GL_RED_MIN_CLAMP_INGR 0x8560 +#define GL_GREEN_MIN_CLAMP_INGR 0x8561 +#define GL_BLUE_MIN_CLAMP_INGR 0x8562 +#define GL_ALPHA_MIN_CLAMP_INGR 0x8563 +#define GL_RED_MAX_CLAMP_INGR 0x8564 +#define GL_GREEN_MAX_CLAMP_INGR 0x8565 +#define GL_BLUE_MAX_CLAMP_INGR 0x8566 +#define GL_ALPHA_MAX_CLAMP_INGR 0x8567 +#endif + +#ifndef GL_INGR_interlace_read +#define GL_INTERLACE_READ_INGR 0x8568 +#endif + +#ifndef GL_EXT_stencil_wrap +#define GL_INCR_WRAP_EXT 0x8507 +#define GL_DECR_WRAP_EXT 0x8508 +#endif + +#ifndef GL_EXT_422_pixels +#define GL_422_EXT 0x80CC +#define GL_422_REV_EXT 0x80CD +#define GL_422_AVERAGE_EXT 0x80CE +#define GL_422_REV_AVERAGE_EXT 0x80CF +#endif + +#ifndef GL_NV_texgen_reflection +#define GL_NORMAL_MAP_NV 0x8511 +#define GL_REFLECTION_MAP_NV 0x8512 +#endif + +#ifndef GL_EXT_texture_cube_map +#define GL_NORMAL_MAP_EXT 0x8511 +#define GL_REFLECTION_MAP_EXT 0x8512 +#define GL_TEXTURE_CUBE_MAP_EXT 0x8513 +#define GL_TEXTURE_BINDING_CUBE_MAP_EXT 0x8514 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT 0x8515 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X_EXT 0x8516 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y_EXT 0x8517 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_EXT 0x8518 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z_EXT 0x8519 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_EXT 0x851A +#define GL_PROXY_TEXTURE_CUBE_MAP_EXT 0x851B +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE_EXT 0x851C +#endif + +#ifndef GL_SUN_convolution_border_modes +#define GL_WRAP_BORDER_SUN 0x81D4 +#endif + +#ifndef GL_EXT_texture_env_add +#endif + +#ifndef GL_EXT_texture_lod_bias +#define GL_MAX_TEXTURE_LOD_BIAS_EXT 0x84FD +#define GL_TEXTURE_FILTER_CONTROL_EXT 0x8500 +#define GL_TEXTURE_LOD_BIAS_EXT 0x8501 +#endif + +#ifndef GL_EXT_texture_filter_anisotropic +#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE +#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF +#endif + +#ifndef GL_EXT_vertex_weighting +#define GL_MODELVIEW0_STACK_DEPTH_EXT GL_MODELVIEW_STACK_DEPTH +#define GL_MODELVIEW1_STACK_DEPTH_EXT 0x8502 +#define GL_MODELVIEW0_MATRIX_EXT GL_MODELVIEW_MATRIX +#define GL_MODELVIEW1_MATRIX_EXT 0x8506 +#define GL_VERTEX_WEIGHTING_EXT 0x8509 +#define GL_MODELVIEW0_EXT GL_MODELVIEW +#define GL_MODELVIEW1_EXT 0x850A +#define GL_CURRENT_VERTEX_WEIGHT_EXT 0x850B +#define GL_VERTEX_WEIGHT_ARRAY_EXT 0x850C +#define GL_VERTEX_WEIGHT_ARRAY_SIZE_EXT 0x850D +#define GL_VERTEX_WEIGHT_ARRAY_TYPE_EXT 0x850E +#define GL_VERTEX_WEIGHT_ARRAY_STRIDE_EXT 0x850F +#define GL_VERTEX_WEIGHT_ARRAY_POINTER_EXT 0x8510 +#endif + +#ifndef GL_NV_light_max_exponent +#define GL_MAX_SHININESS_NV 0x8504 +#define GL_MAX_SPOT_EXPONENT_NV 0x8505 +#endif + +#ifndef GL_NV_vertex_array_range +#define GL_VERTEX_ARRAY_RANGE_NV 0x851D +#define GL_VERTEX_ARRAY_RANGE_LENGTH_NV 0x851E +#define GL_VERTEX_ARRAY_RANGE_VALID_NV 0x851F +#define GL_MAX_VERTEX_ARRAY_RANGE_ELEMENT_NV 0x8520 +#define GL_VERTEX_ARRAY_RANGE_POINTER_NV 0x8521 +#endif + +#ifndef GL_NV_register_combiners +#define GL_REGISTER_COMBINERS_NV 0x8522 +#define GL_VARIABLE_A_NV 0x8523 +#define GL_VARIABLE_B_NV 0x8524 +#define GL_VARIABLE_C_NV 0x8525 +#define GL_VARIABLE_D_NV 0x8526 +#define GL_VARIABLE_E_NV 0x8527 +#define GL_VARIABLE_F_NV 0x8528 +#define GL_VARIABLE_G_NV 0x8529 +#define GL_CONSTANT_COLOR0_NV 0x852A +#define GL_CONSTANT_COLOR1_NV 0x852B +#define GL_PRIMARY_COLOR_NV 0x852C +#define GL_SECONDARY_COLOR_NV 0x852D +#define GL_SPARE0_NV 0x852E +#define GL_SPARE1_NV 0x852F +#define GL_DISCARD_NV 0x8530 +#define GL_E_TIMES_F_NV 0x8531 +#define GL_SPARE0_PLUS_SECONDARY_COLOR_NV 0x8532 +#define GL_UNSIGNED_IDENTITY_NV 0x8536 +#define GL_UNSIGNED_INVERT_NV 0x8537 +#define GL_EXPAND_NORMAL_NV 0x8538 +#define GL_EXPAND_NEGATE_NV 0x8539 +#define GL_HALF_BIAS_NORMAL_NV 0x853A +#define GL_HALF_BIAS_NEGATE_NV 0x853B +#define GL_SIGNED_IDENTITY_NV 0x853C +#define GL_SIGNED_NEGATE_NV 0x853D +#define GL_SCALE_BY_TWO_NV 0x853E +#define GL_SCALE_BY_FOUR_NV 0x853F +#define GL_SCALE_BY_ONE_HALF_NV 0x8540 +#define GL_BIAS_BY_NEGATIVE_ONE_HALF_NV 0x8541 +#define GL_COMBINER_INPUT_NV 0x8542 +#define GL_COMBINER_MAPPING_NV 0x8543 +#define GL_COMBINER_COMPONENT_USAGE_NV 0x8544 +#define GL_COMBINER_AB_DOT_PRODUCT_NV 0x8545 +#define GL_COMBINER_CD_DOT_PRODUCT_NV 0x8546 +#define GL_COMBINER_MUX_SUM_NV 0x8547 +#define GL_COMBINER_SCALE_NV 0x8548 +#define GL_COMBINER_BIAS_NV 0x8549 +#define GL_COMBINER_AB_OUTPUT_NV 0x854A +#define GL_COMBINER_CD_OUTPUT_NV 0x854B +#define GL_COMBINER_SUM_OUTPUT_NV 0x854C +#define GL_MAX_GENERAL_COMBINERS_NV 0x854D +#define GL_NUM_GENERAL_COMBINERS_NV 0x854E +#define GL_COLOR_SUM_CLAMP_NV 0x854F +#define GL_COMBINER0_NV 0x8550 +#define GL_COMBINER1_NV 0x8551 +#define GL_COMBINER2_NV 0x8552 +#define GL_COMBINER3_NV 0x8553 +#define GL_COMBINER4_NV 0x8554 +#define GL_COMBINER5_NV 0x8555 +#define GL_COMBINER6_NV 0x8556 +#define GL_COMBINER7_NV 0x8557 +/* reuse GL_TEXTURE0_ARB */ +/* reuse GL_TEXTURE1_ARB */ +/* reuse GL_ZERO */ +/* reuse GL_NONE */ +/* reuse GL_FOG */ +#endif + +#ifndef GL_NV_fog_distance +#define GL_FOG_DISTANCE_MODE_NV 0x855A +#define GL_EYE_RADIAL_NV 0x855B +#define GL_EYE_PLANE_ABSOLUTE_NV 0x855C +/* reuse GL_EYE_PLANE */ +#endif + +#ifndef GL_NV_texgen_emboss +#define GL_EMBOSS_LIGHT_NV 0x855D +#define GL_EMBOSS_CONSTANT_NV 0x855E +#define GL_EMBOSS_MAP_NV 0x855F +#endif + +#ifndef GL_NV_blend_square +#endif + +#ifndef GL_NV_texture_env_combine4 +#define GL_COMBINE4_NV 0x8503 +#define GL_SOURCE3_RGB_NV 0x8583 +#define GL_SOURCE3_ALPHA_NV 0x858B +#define GL_OPERAND3_RGB_NV 0x8593 +#define GL_OPERAND3_ALPHA_NV 0x859B +#endif + +#ifndef GL_MESA_resize_buffers +#endif + +#ifndef GL_MESA_window_pos +#endif + +#ifndef GL_EXT_texture_compression_s3tc +#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 +#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 +#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 +#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 +#endif + +#ifndef GL_IBM_cull_vertex +#define GL_CULL_VERTEX_IBM 103050 +#endif + +#ifndef GL_IBM_multimode_draw_arrays +#endif + +#ifndef GL_IBM_vertex_array_lists +#define GL_VERTEX_ARRAY_LIST_IBM 103070 +#define GL_NORMAL_ARRAY_LIST_IBM 103071 +#define GL_COLOR_ARRAY_LIST_IBM 103072 +#define GL_INDEX_ARRAY_LIST_IBM 103073 +#define GL_TEXTURE_COORD_ARRAY_LIST_IBM 103074 +#define GL_EDGE_FLAG_ARRAY_LIST_IBM 103075 +#define GL_FOG_COORDINATE_ARRAY_LIST_IBM 103076 +#define GL_SECONDARY_COLOR_ARRAY_LIST_IBM 103077 +#define GL_VERTEX_ARRAY_LIST_STRIDE_IBM 103080 +#define GL_NORMAL_ARRAY_LIST_STRIDE_IBM 103081 +#define GL_COLOR_ARRAY_LIST_STRIDE_IBM 103082 +#define GL_INDEX_ARRAY_LIST_STRIDE_IBM 103083 +#define GL_TEXTURE_COORD_ARRAY_LIST_STRIDE_IBM 103084 +#define GL_EDGE_FLAG_ARRAY_LIST_STRIDE_IBM 103085 +#define GL_FOG_COORDINATE_ARRAY_LIST_STRIDE_IBM 103086 +#define GL_SECONDARY_COLOR_ARRAY_LIST_STRIDE_IBM 103087 +#endif + +#ifndef GL_SGIX_subsample +#define GL_PACK_SUBSAMPLE_RATE_SGIX 0x85A0 +#define GL_UNPACK_SUBSAMPLE_RATE_SGIX 0x85A1 +#define GL_PIXEL_SUBSAMPLE_4444_SGIX 0x85A2 +#define GL_PIXEL_SUBSAMPLE_2424_SGIX 0x85A3 +#define GL_PIXEL_SUBSAMPLE_4242_SGIX 0x85A4 +#endif + +#ifndef GL_SGIX_ycrcb_subsample +#endif + +#ifndef GL_SGIX_ycrcba +#define GL_YCRCB_SGIX 0x8318 +#define GL_YCRCBA_SGIX 0x8319 +#endif + +#ifndef GL_SGI_depth_pass_instrument +#define GL_DEPTH_PASS_INSTRUMENT_SGIX 0x8310 +#define GL_DEPTH_PASS_INSTRUMENT_COUNTERS_SGIX 0x8311 +#define GL_DEPTH_PASS_INSTRUMENT_MAX_SGIX 0x8312 +#endif + +#ifndef GL_3DFX_texture_compression_FXT1 +#define GL_COMPRESSED_RGB_FXT1_3DFX 0x86B0 +#define GL_COMPRESSED_RGBA_FXT1_3DFX 0x86B1 +#endif + +#ifndef GL_3DFX_multisample +#define GL_MULTISAMPLE_3DFX 0x86B2 +#define GL_SAMPLE_BUFFERS_3DFX 0x86B3 +#define GL_SAMPLES_3DFX 0x86B4 +#define GL_MULTISAMPLE_BIT_3DFX 0x20000000 +#endif + +#ifndef GL_3DFX_tbuffer +#endif + +#ifndef GL_EXT_multisample +#define GL_MULTISAMPLE_EXT 0x809D +#define GL_SAMPLE_ALPHA_TO_MASK_EXT 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE_EXT 0x809F +#define GL_SAMPLE_MASK_EXT 0x80A0 +#define GL_1PASS_EXT 0x80A1 +#define GL_2PASS_0_EXT 0x80A2 +#define GL_2PASS_1_EXT 0x80A3 +#define GL_4PASS_0_EXT 0x80A4 +#define GL_4PASS_1_EXT 0x80A5 +#define GL_4PASS_2_EXT 0x80A6 +#define GL_4PASS_3_EXT 0x80A7 +#define GL_SAMPLE_BUFFERS_EXT 0x80A8 +#define GL_SAMPLES_EXT 0x80A9 +#define GL_SAMPLE_MASK_VALUE_EXT 0x80AA +#define GL_SAMPLE_MASK_INVERT_EXT 0x80AB +#define GL_SAMPLE_PATTERN_EXT 0x80AC +#define GL_MULTISAMPLE_BIT_EXT 0x20000000 +#endif + +#ifndef GL_SGIX_vertex_preclip +#define GL_VERTEX_PRECLIP_SGIX 0x83EE +#define GL_VERTEX_PRECLIP_HINT_SGIX 0x83EF +#endif + +#ifndef GL_SGIX_convolution_accuracy +#define GL_CONVOLUTION_HINT_SGIX 0x8316 +#endif + +#ifndef GL_SGIX_resample +#define GL_PACK_RESAMPLE_SGIX 0x842C +#define GL_UNPACK_RESAMPLE_SGIX 0x842D +#define GL_RESAMPLE_REPLICATE_SGIX 0x842E +#define GL_RESAMPLE_ZERO_FILL_SGIX 0x842F +#define GL_RESAMPLE_DECIMATE_SGIX 0x8430 +#endif + +#ifndef GL_SGIS_point_line_texgen +#define GL_EYE_DISTANCE_TO_POINT_SGIS 0x81F0 +#define GL_OBJECT_DISTANCE_TO_POINT_SGIS 0x81F1 +#define GL_EYE_DISTANCE_TO_LINE_SGIS 0x81F2 +#define GL_OBJECT_DISTANCE_TO_LINE_SGIS 0x81F3 +#define GL_EYE_POINT_SGIS 0x81F4 +#define GL_OBJECT_POINT_SGIS 0x81F5 +#define GL_EYE_LINE_SGIS 0x81F6 +#define GL_OBJECT_LINE_SGIS 0x81F7 +#endif + +#ifndef GL_SGIS_texture_color_mask +#define GL_TEXTURE_COLOR_WRITEMASK_SGIS 0x81EF +#endif + +#ifndef GL_EXT_texture_env_dot3 +#define GL_DOT3_RGB_EXT 0x8740 +#define GL_DOT3_RGBA_EXT 0x8741 +#endif + +#ifndef GL_ATI_texture_mirror_once +#define GL_MIRROR_CLAMP_ATI 0x8742 +#define GL_MIRROR_CLAMP_TO_EDGE_ATI 0x8743 +#endif + +#ifndef GL_NV_fence +#define GL_ALL_COMPLETED_NV 0x84F2 +#define GL_FENCE_STATUS_NV 0x84F3 +#define GL_FENCE_CONDITION_NV 0x84F4 +#endif + +#ifndef GL_IBM_texture_mirrored_repeat +#define GL_MIRRORED_REPEAT_IBM 0x8370 +#endif + +#ifndef GL_NV_evaluators +#define GL_EVAL_2D_NV 0x86C0 +#define GL_EVAL_TRIANGULAR_2D_NV 0x86C1 +#define GL_MAP_TESSELLATION_NV 0x86C2 +#define GL_MAP_ATTRIB_U_ORDER_NV 0x86C3 +#define GL_MAP_ATTRIB_V_ORDER_NV 0x86C4 +#define GL_EVAL_FRACTIONAL_TESSELLATION_NV 0x86C5 +#define GL_EVAL_VERTEX_ATTRIB0_NV 0x86C6 +#define GL_EVAL_VERTEX_ATTRIB1_NV 0x86C7 +#define GL_EVAL_VERTEX_ATTRIB2_NV 0x86C8 +#define GL_EVAL_VERTEX_ATTRIB3_NV 0x86C9 +#define GL_EVAL_VERTEX_ATTRIB4_NV 0x86CA +#define GL_EVAL_VERTEX_ATTRIB5_NV 0x86CB +#define GL_EVAL_VERTEX_ATTRIB6_NV 0x86CC +#define GL_EVAL_VERTEX_ATTRIB7_NV 0x86CD +#define GL_EVAL_VERTEX_ATTRIB8_NV 0x86CE +#define GL_EVAL_VERTEX_ATTRIB9_NV 0x86CF +#define GL_EVAL_VERTEX_ATTRIB10_NV 0x86D0 +#define GL_EVAL_VERTEX_ATTRIB11_NV 0x86D1 +#define GL_EVAL_VERTEX_ATTRIB12_NV 0x86D2 +#define GL_EVAL_VERTEX_ATTRIB13_NV 0x86D3 +#define GL_EVAL_VERTEX_ATTRIB14_NV 0x86D4 +#define GL_EVAL_VERTEX_ATTRIB15_NV 0x86D5 +#define GL_MAX_MAP_TESSELLATION_NV 0x86D6 +#define GL_MAX_RATIONAL_EVAL_ORDER_NV 0x86D7 +#endif + +#ifndef GL_NV_packed_depth_stencil +#define GL_DEPTH_STENCIL_NV 0x84F9 +#define GL_UNSIGNED_INT_24_8_NV 0x84FA +#endif + +#ifndef GL_NV_register_combiners2 +#define GL_PER_STAGE_CONSTANTS_NV 0x8535 +#endif + +#ifndef GL_NV_texture_compression_vtc +#endif + +#ifndef GL_NV_texture_rectangle +#define GL_TEXTURE_RECTANGLE_NV 0x84F5 +#define GL_TEXTURE_BINDING_RECTANGLE_NV 0x84F6 +#define GL_PROXY_TEXTURE_RECTANGLE_NV 0x84F7 +#define GL_MAX_RECTANGLE_TEXTURE_SIZE_NV 0x84F8 +#endif + +#ifndef GL_NV_texture_shader +#define GL_OFFSET_TEXTURE_RECTANGLE_NV 0x864C +#define GL_OFFSET_TEXTURE_RECTANGLE_SCALE_NV 0x864D +#define GL_DOT_PRODUCT_TEXTURE_RECTANGLE_NV 0x864E +#define GL_RGBA_UNSIGNED_DOT_PRODUCT_MAPPING_NV 0x86D9 +#define GL_UNSIGNED_INT_S8_S8_8_8_NV 0x86DA +#define GL_UNSIGNED_INT_8_8_S8_S8_REV_NV 0x86DB +#define GL_DSDT_MAG_INTENSITY_NV 0x86DC +#define GL_SHADER_CONSISTENT_NV 0x86DD +#define GL_TEXTURE_SHADER_NV 0x86DE +#define GL_SHADER_OPERATION_NV 0x86DF +#define GL_CULL_MODES_NV 0x86E0 +#define GL_OFFSET_TEXTURE_MATRIX_NV 0x86E1 +#define GL_OFFSET_TEXTURE_SCALE_NV 0x86E2 +#define GL_OFFSET_TEXTURE_BIAS_NV 0x86E3 +#define GL_OFFSET_TEXTURE_2D_MATRIX_NV GL_OFFSET_TEXTURE_MATRIX_NV +#define GL_OFFSET_TEXTURE_2D_SCALE_NV GL_OFFSET_TEXTURE_SCALE_NV +#define GL_OFFSET_TEXTURE_2D_BIAS_NV GL_OFFSET_TEXTURE_BIAS_NV +#define GL_PREVIOUS_TEXTURE_INPUT_NV 0x86E4 +#define GL_CONST_EYE_NV 0x86E5 +#define GL_PASS_THROUGH_NV 0x86E6 +#define GL_CULL_FRAGMENT_NV 0x86E7 +#define GL_OFFSET_TEXTURE_2D_NV 0x86E8 +#define GL_DEPENDENT_AR_TEXTURE_2D_NV 0x86E9 +#define GL_DEPENDENT_GB_TEXTURE_2D_NV 0x86EA +#define GL_DOT_PRODUCT_NV 0x86EC +#define GL_DOT_PRODUCT_DEPTH_REPLACE_NV 0x86ED +#define GL_DOT_PRODUCT_TEXTURE_2D_NV 0x86EE +#define GL_DOT_PRODUCT_TEXTURE_CUBE_MAP_NV 0x86F0 +#define GL_DOT_PRODUCT_DIFFUSE_CUBE_MAP_NV 0x86F1 +#define GL_DOT_PRODUCT_REFLECT_CUBE_MAP_NV 0x86F2 +#define GL_DOT_PRODUCT_CONST_EYE_REFLECT_CUBE_MAP_NV 0x86F3 +#define GL_HILO_NV 0x86F4 +#define GL_DSDT_NV 0x86F5 +#define GL_DSDT_MAG_NV 0x86F6 +#define GL_DSDT_MAG_VIB_NV 0x86F7 +#define GL_HILO16_NV 0x86F8 +#define GL_SIGNED_HILO_NV 0x86F9 +#define GL_SIGNED_HILO16_NV 0x86FA +#define GL_SIGNED_RGBA_NV 0x86FB +#define GL_SIGNED_RGBA8_NV 0x86FC +#define GL_SIGNED_RGB_NV 0x86FE +#define GL_SIGNED_RGB8_NV 0x86FF +#define GL_SIGNED_LUMINANCE_NV 0x8701 +#define GL_SIGNED_LUMINANCE8_NV 0x8702 +#define GL_SIGNED_LUMINANCE_ALPHA_NV 0x8703 +#define GL_SIGNED_LUMINANCE8_ALPHA8_NV 0x8704 +#define GL_SIGNED_ALPHA_NV 0x8705 +#define GL_SIGNED_ALPHA8_NV 0x8706 +#define GL_SIGNED_INTENSITY_NV 0x8707 +#define GL_SIGNED_INTENSITY8_NV 0x8708 +#define GL_DSDT8_NV 0x8709 +#define GL_DSDT8_MAG8_NV 0x870A +#define GL_DSDT8_MAG8_INTENSITY8_NV 0x870B +#define GL_SIGNED_RGB_UNSIGNED_ALPHA_NV 0x870C +#define GL_SIGNED_RGB8_UNSIGNED_ALPHA8_NV 0x870D +#define GL_HI_SCALE_NV 0x870E +#define GL_LO_SCALE_NV 0x870F +#define GL_DS_SCALE_NV 0x8710 +#define GL_DT_SCALE_NV 0x8711 +#define GL_MAGNITUDE_SCALE_NV 0x8712 +#define GL_VIBRANCE_SCALE_NV 0x8713 +#define GL_HI_BIAS_NV 0x8714 +#define GL_LO_BIAS_NV 0x8715 +#define GL_DS_BIAS_NV 0x8716 +#define GL_DT_BIAS_NV 0x8717 +#define GL_MAGNITUDE_BIAS_NV 0x8718 +#define GL_VIBRANCE_BIAS_NV 0x8719 +#define GL_TEXTURE_BORDER_VALUES_NV 0x871A +#define GL_TEXTURE_HI_SIZE_NV 0x871B +#define GL_TEXTURE_LO_SIZE_NV 0x871C +#define GL_TEXTURE_DS_SIZE_NV 0x871D +#define GL_TEXTURE_DT_SIZE_NV 0x871E +#define GL_TEXTURE_MAG_SIZE_NV 0x871F +#endif + +#ifndef GL_NV_texture_shader2 +#define GL_DOT_PRODUCT_TEXTURE_3D_NV 0x86EF +#endif + +#ifndef GL_NV_vertex_array_range2 +#define GL_VERTEX_ARRAY_RANGE_WITHOUT_FLUSH_NV 0x8533 +#endif + +#ifndef GL_NV_vertex_program +#define GL_VERTEX_PROGRAM_NV 0x8620 +#define GL_VERTEX_STATE_PROGRAM_NV 0x8621 +#define GL_ATTRIB_ARRAY_SIZE_NV 0x8623 +#define GL_ATTRIB_ARRAY_STRIDE_NV 0x8624 +#define GL_ATTRIB_ARRAY_TYPE_NV 0x8625 +#define GL_CURRENT_ATTRIB_NV 0x8626 +#define GL_PROGRAM_LENGTH_NV 0x8627 +#define GL_PROGRAM_STRING_NV 0x8628 +#define GL_MODELVIEW_PROJECTION_NV 0x8629 +#define GL_IDENTITY_NV 0x862A +#define GL_INVERSE_NV 0x862B +#define GL_TRANSPOSE_NV 0x862C +#define GL_INVERSE_TRANSPOSE_NV 0x862D +#define GL_MAX_TRACK_MATRIX_STACK_DEPTH_NV 0x862E +#define GL_MAX_TRACK_MATRICES_NV 0x862F +#define GL_MATRIX0_NV 0x8630 +#define GL_MATRIX1_NV 0x8631 +#define GL_MATRIX2_NV 0x8632 +#define GL_MATRIX3_NV 0x8633 +#define GL_MATRIX4_NV 0x8634 +#define GL_MATRIX5_NV 0x8635 +#define GL_MATRIX6_NV 0x8636 +#define GL_MATRIX7_NV 0x8637 +#define GL_CURRENT_MATRIX_STACK_DEPTH_NV 0x8640 +#define GL_CURRENT_MATRIX_NV 0x8641 +#define GL_VERTEX_PROGRAM_POINT_SIZE_NV 0x8642 +#define GL_VERTEX_PROGRAM_TWO_SIDE_NV 0x8643 +#define GL_PROGRAM_PARAMETER_NV 0x8644 +#define GL_ATTRIB_ARRAY_POINTER_NV 0x8645 +#define GL_PROGRAM_TARGET_NV 0x8646 +#define GL_PROGRAM_RESIDENT_NV 0x8647 +#define GL_TRACK_MATRIX_NV 0x8648 +#define GL_TRACK_MATRIX_TRANSFORM_NV 0x8649 +#define GL_VERTEX_PROGRAM_BINDING_NV 0x864A +#define GL_PROGRAM_ERROR_POSITION_NV 0x864B +#define GL_VERTEX_ATTRIB_ARRAY0_NV 0x8650 +#define GL_VERTEX_ATTRIB_ARRAY1_NV 0x8651 +#define GL_VERTEX_ATTRIB_ARRAY2_NV 0x8652 +#define GL_VERTEX_ATTRIB_ARRAY3_NV 0x8653 +#define GL_VERTEX_ATTRIB_ARRAY4_NV 0x8654 +#define GL_VERTEX_ATTRIB_ARRAY5_NV 0x8655 +#define GL_VERTEX_ATTRIB_ARRAY6_NV 0x8656 +#define GL_VERTEX_ATTRIB_ARRAY7_NV 0x8657 +#define GL_VERTEX_ATTRIB_ARRAY8_NV 0x8658 +#define GL_VERTEX_ATTRIB_ARRAY9_NV 0x8659 +#define GL_VERTEX_ATTRIB_ARRAY10_NV 0x865A +#define GL_VERTEX_ATTRIB_ARRAY11_NV 0x865B +#define GL_VERTEX_ATTRIB_ARRAY12_NV 0x865C +#define GL_VERTEX_ATTRIB_ARRAY13_NV 0x865D +#define GL_VERTEX_ATTRIB_ARRAY14_NV 0x865E +#define GL_VERTEX_ATTRIB_ARRAY15_NV 0x865F +#define GL_MAP1_VERTEX_ATTRIB0_4_NV 0x8660 +#define GL_MAP1_VERTEX_ATTRIB1_4_NV 0x8661 +#define GL_MAP1_VERTEX_ATTRIB2_4_NV 0x8662 +#define GL_MAP1_VERTEX_ATTRIB3_4_NV 0x8663 +#define GL_MAP1_VERTEX_ATTRIB4_4_NV 0x8664 +#define GL_MAP1_VERTEX_ATTRIB5_4_NV 0x8665 +#define GL_MAP1_VERTEX_ATTRIB6_4_NV 0x8666 +#define GL_MAP1_VERTEX_ATTRIB7_4_NV 0x8667 +#define GL_MAP1_VERTEX_ATTRIB8_4_NV 0x8668 +#define GL_MAP1_VERTEX_ATTRIB9_4_NV 0x8669 +#define GL_MAP1_VERTEX_ATTRIB10_4_NV 0x866A +#define GL_MAP1_VERTEX_ATTRIB11_4_NV 0x866B +#define GL_MAP1_VERTEX_ATTRIB12_4_NV 0x866C +#define GL_MAP1_VERTEX_ATTRIB13_4_NV 0x866D +#define GL_MAP1_VERTEX_ATTRIB14_4_NV 0x866E +#define GL_MAP1_VERTEX_ATTRIB15_4_NV 0x866F +#define GL_MAP2_VERTEX_ATTRIB0_4_NV 0x8670 +#define GL_MAP2_VERTEX_ATTRIB1_4_NV 0x8671 +#define GL_MAP2_VERTEX_ATTRIB2_4_NV 0x8672 +#define GL_MAP2_VERTEX_ATTRIB3_4_NV 0x8673 +#define GL_MAP2_VERTEX_ATTRIB4_4_NV 0x8674 +#define GL_MAP2_VERTEX_ATTRIB5_4_NV 0x8675 +#define GL_MAP2_VERTEX_ATTRIB6_4_NV 0x8676 +#define GL_MAP2_VERTEX_ATTRIB7_4_NV 0x8677 +#define GL_MAP2_VERTEX_ATTRIB8_4_NV 0x8678 +#define GL_MAP2_VERTEX_ATTRIB9_4_NV 0x8679 +#define GL_MAP2_VERTEX_ATTRIB10_4_NV 0x867A +#define GL_MAP2_VERTEX_ATTRIB11_4_NV 0x867B +#define GL_MAP2_VERTEX_ATTRIB12_4_NV 0x867C +#define GL_MAP2_VERTEX_ATTRIB13_4_NV 0x867D +#define GL_MAP2_VERTEX_ATTRIB14_4_NV 0x867E +#define GL_MAP2_VERTEX_ATTRIB15_4_NV 0x867F +#endif + +#ifndef GL_SGIX_texture_coordinate_clamp +#define GL_TEXTURE_MAX_CLAMP_S_SGIX 0x8369 +#define GL_TEXTURE_MAX_CLAMP_T_SGIX 0x836A +#define GL_TEXTURE_MAX_CLAMP_R_SGIX 0x836B +#endif + +#ifndef GL_SGIX_scalebias_hint +#define GL_SCALEBIAS_HINT_SGIX 0x8322 +#endif + +#ifndef GL_OML_interlace +#define GL_INTERLACE_OML 0x8980 +#define GL_INTERLACE_READ_OML 0x8981 +#endif + +#ifndef GL_OML_subsample +#define GL_FORMAT_SUBSAMPLE_24_24_OML 0x8982 +#define GL_FORMAT_SUBSAMPLE_244_244_OML 0x8983 +#endif + +#ifndef GL_OML_resample +#define GL_PACK_RESAMPLE_OML 0x8984 +#define GL_UNPACK_RESAMPLE_OML 0x8985 +#define GL_RESAMPLE_REPLICATE_OML 0x8986 +#define GL_RESAMPLE_ZERO_FILL_OML 0x8987 +#define GL_RESAMPLE_AVERAGE_OML 0x8988 +#define GL_RESAMPLE_DECIMATE_OML 0x8989 +#endif + +#ifndef GL_NV_copy_depth_to_color +#define GL_DEPTH_STENCIL_TO_RGBA_NV 0x886E +#define GL_DEPTH_STENCIL_TO_BGRA_NV 0x886F +#endif + +#ifndef GL_ATI_envmap_bumpmap +#define GL_BUMP_ROT_MATRIX_ATI 0x8775 +#define GL_BUMP_ROT_MATRIX_SIZE_ATI 0x8776 +#define GL_BUMP_NUM_TEX_UNITS_ATI 0x8777 +#define GL_BUMP_TEX_UNITS_ATI 0x8778 +#define GL_DUDV_ATI 0x8779 +#define GL_DU8DV8_ATI 0x877A +#define GL_BUMP_ENVMAP_ATI 0x877B +#define GL_BUMP_TARGET_ATI 0x877C +#endif + +#ifndef GL_ATI_fragment_shader +#define GL_FRAGMENT_SHADER_ATI 0x8920 +#define GL_REG_0_ATI 0x8921 +#define GL_REG_1_ATI 0x8922 +#define GL_REG_2_ATI 0x8923 +#define GL_REG_3_ATI 0x8924 +#define GL_REG_4_ATI 0x8925 +#define GL_REG_5_ATI 0x8926 +#define GL_REG_6_ATI 0x8927 +#define GL_REG_7_ATI 0x8928 +#define GL_REG_8_ATI 0x8929 +#define GL_REG_9_ATI 0x892A +#define GL_REG_10_ATI 0x892B +#define GL_REG_11_ATI 0x892C +#define GL_REG_12_ATI 0x892D +#define GL_REG_13_ATI 0x892E +#define GL_REG_14_ATI 0x892F +#define GL_REG_15_ATI 0x8930 +#define GL_REG_16_ATI 0x8931 +#define GL_REG_17_ATI 0x8932 +#define GL_REG_18_ATI 0x8933 +#define GL_REG_19_ATI 0x8934 +#define GL_REG_20_ATI 0x8935 +#define GL_REG_21_ATI 0x8936 +#define GL_REG_22_ATI 0x8937 +#define GL_REG_23_ATI 0x8938 +#define GL_REG_24_ATI 0x8939 +#define GL_REG_25_ATI 0x893A +#define GL_REG_26_ATI 0x893B +#define GL_REG_27_ATI 0x893C +#define GL_REG_28_ATI 0x893D +#define GL_REG_29_ATI 0x893E +#define GL_REG_30_ATI 0x893F +#define GL_REG_31_ATI 0x8940 +#define GL_CON_0_ATI 0x8941 +#define GL_CON_1_ATI 0x8942 +#define GL_CON_2_ATI 0x8943 +#define GL_CON_3_ATI 0x8944 +#define GL_CON_4_ATI 0x8945 +#define GL_CON_5_ATI 0x8946 +#define GL_CON_6_ATI 0x8947 +#define GL_CON_7_ATI 0x8948 +#define GL_CON_8_ATI 0x8949 +#define GL_CON_9_ATI 0x894A +#define GL_CON_10_ATI 0x894B +#define GL_CON_11_ATI 0x894C +#define GL_CON_12_ATI 0x894D +#define GL_CON_13_ATI 0x894E +#define GL_CON_14_ATI 0x894F +#define GL_CON_15_ATI 0x8950 +#define GL_CON_16_ATI 0x8951 +#define GL_CON_17_ATI 0x8952 +#define GL_CON_18_ATI 0x8953 +#define GL_CON_19_ATI 0x8954 +#define GL_CON_20_ATI 0x8955 +#define GL_CON_21_ATI 0x8956 +#define GL_CON_22_ATI 0x8957 +#define GL_CON_23_ATI 0x8958 +#define GL_CON_24_ATI 0x8959 +#define GL_CON_25_ATI 0x895A +#define GL_CON_26_ATI 0x895B +#define GL_CON_27_ATI 0x895C +#define GL_CON_28_ATI 0x895D +#define GL_CON_29_ATI 0x895E +#define GL_CON_30_ATI 0x895F +#define GL_CON_31_ATI 0x8960 +#define GL_MOV_ATI 0x8961 +#define GL_ADD_ATI 0x8963 +#define GL_MUL_ATI 0x8964 +#define GL_SUB_ATI 0x8965 +#define GL_DOT3_ATI 0x8966 +#define GL_DOT4_ATI 0x8967 +#define GL_MAD_ATI 0x8968 +#define GL_LERP_ATI 0x8969 +#define GL_CND_ATI 0x896A +#define GL_CND0_ATI 0x896B +#define GL_DOT2_ADD_ATI 0x896C +#define GL_SECONDARY_INTERPOLATOR_ATI 0x896D +#define GL_NUM_FRAGMENT_REGISTERS_ATI 0x896E +#define GL_NUM_FRAGMENT_CONSTANTS_ATI 0x896F +#define GL_NUM_PASSES_ATI 0x8970 +#define GL_NUM_INSTRUCTIONS_PER_PASS_ATI 0x8971 +#define GL_NUM_INSTRUCTIONS_TOTAL_ATI 0x8972 +#define GL_NUM_INPUT_INTERPOLATOR_COMPONENTS_ATI 0x8973 +#define GL_NUM_LOOPBACK_COMPONENTS_ATI 0x8974 +#define GL_COLOR_ALPHA_PAIRING_ATI 0x8975 +#define GL_SWIZZLE_STR_ATI 0x8976 +#define GL_SWIZZLE_STQ_ATI 0x8977 +#define GL_SWIZZLE_STR_DR_ATI 0x8978 +#define GL_SWIZZLE_STQ_DQ_ATI 0x8979 +#define GL_SWIZZLE_STRQ_ATI 0x897A +#define GL_SWIZZLE_STRQ_DQ_ATI 0x897B +#define GL_RED_BIT_ATI 0x00000001 +#define GL_GREEN_BIT_ATI 0x00000002 +#define GL_BLUE_BIT_ATI 0x00000004 +#define GL_2X_BIT_ATI 0x00000001 +#define GL_4X_BIT_ATI 0x00000002 +#define GL_8X_BIT_ATI 0x00000004 +#define GL_HALF_BIT_ATI 0x00000008 +#define GL_QUARTER_BIT_ATI 0x00000010 +#define GL_EIGHTH_BIT_ATI 0x00000020 +#define GL_SATURATE_BIT_ATI 0x00000040 +#define GL_COMP_BIT_ATI 0x00000002 +#define GL_NEGATE_BIT_ATI 0x00000004 +#define GL_BIAS_BIT_ATI 0x00000008 +#endif + +#ifndef GL_ATI_pn_triangles +#define GL_PN_TRIANGLES_ATI 0x87F0 +#define GL_MAX_PN_TRIANGLES_TESSELATION_LEVEL_ATI 0x87F1 +#define GL_PN_TRIANGLES_POINT_MODE_ATI 0x87F2 +#define GL_PN_TRIANGLES_NORMAL_MODE_ATI 0x87F3 +#define GL_PN_TRIANGLES_TESSELATION_LEVEL_ATI 0x87F4 +#define GL_PN_TRIANGLES_POINT_MODE_LINEAR_ATI 0x87F5 +#define GL_PN_TRIANGLES_POINT_MODE_CUBIC_ATI 0x87F6 +#define GL_PN_TRIANGLES_NORMAL_MODE_LINEAR_ATI 0x87F7 +#define GL_PN_TRIANGLES_NORMAL_MODE_QUADRATIC_ATI 0x87F8 +#endif + +#ifndef GL_ATI_vertex_array_object +#define GL_STATIC_ATI 0x8760 +#define GL_DYNAMIC_ATI 0x8761 +#define GL_PRESERVE_ATI 0x8762 +#define GL_DISCARD_ATI 0x8763 +#define GL_OBJECT_BUFFER_SIZE_ATI 0x8764 +#define GL_OBJECT_BUFFER_USAGE_ATI 0x8765 +#define GL_ARRAY_OBJECT_BUFFER_ATI 0x8766 +#define GL_ARRAY_OBJECT_OFFSET_ATI 0x8767 +#endif + +#ifndef GL_EXT_vertex_shader +#define GL_VERTEX_SHADER_EXT 0x8780 +#define GL_VERTEX_SHADER_BINDING_EXT 0x8781 +#define GL_OP_INDEX_EXT 0x8782 +#define GL_OP_NEGATE_EXT 0x8783 +#define GL_OP_DOT3_EXT 0x8784 +#define GL_OP_DOT4_EXT 0x8785 +#define GL_OP_MUL_EXT 0x8786 +#define GL_OP_ADD_EXT 0x8787 +#define GL_OP_MADD_EXT 0x8788 +#define GL_OP_FRAC_EXT 0x8789 +#define GL_OP_MAX_EXT 0x878A +#define GL_OP_MIN_EXT 0x878B +#define GL_OP_SET_GE_EXT 0x878C +#define GL_OP_SET_LT_EXT 0x878D +#define GL_OP_CLAMP_EXT 0x878E +#define GL_OP_FLOOR_EXT 0x878F +#define GL_OP_ROUND_EXT 0x8790 +#define GL_OP_EXP_BASE_2_EXT 0x8791 +#define GL_OP_LOG_BASE_2_EXT 0x8792 +#define GL_OP_POWER_EXT 0x8793 +#define GL_OP_RECIP_EXT 0x8794 +#define GL_OP_RECIP_SQRT_EXT 0x8795 +#define GL_OP_SUB_EXT 0x8796 +#define GL_OP_CROSS_PRODUCT_EXT 0x8797 +#define GL_OP_MULTIPLY_MATRIX_EXT 0x8798 +#define GL_OP_MOV_EXT 0x8799 +#define GL_OUTPUT_VERTEX_EXT 0x879A +#define GL_OUTPUT_COLOR0_EXT 0x879B +#define GL_OUTPUT_COLOR1_EXT 0x879C +#define GL_OUTPUT_TEXTURE_COORD0_EXT 0x879D +#define GL_OUTPUT_TEXTURE_COORD1_EXT 0x879E +#define GL_OUTPUT_TEXTURE_COORD2_EXT 0x879F +#define GL_OUTPUT_TEXTURE_COORD3_EXT 0x87A0 +#define GL_OUTPUT_TEXTURE_COORD4_EXT 0x87A1 +#define GL_OUTPUT_TEXTURE_COORD5_EXT 0x87A2 +#define GL_OUTPUT_TEXTURE_COORD6_EXT 0x87A3 +#define GL_OUTPUT_TEXTURE_COORD7_EXT 0x87A4 +#define GL_OUTPUT_TEXTURE_COORD8_EXT 0x87A5 +#define GL_OUTPUT_TEXTURE_COORD9_EXT 0x87A6 +#define GL_OUTPUT_TEXTURE_COORD10_EXT 0x87A7 +#define GL_OUTPUT_TEXTURE_COORD11_EXT 0x87A8 +#define GL_OUTPUT_TEXTURE_COORD12_EXT 0x87A9 +#define GL_OUTPUT_TEXTURE_COORD13_EXT 0x87AA +#define GL_OUTPUT_TEXTURE_COORD14_EXT 0x87AB +#define GL_OUTPUT_TEXTURE_COORD15_EXT 0x87AC +#define GL_OUTPUT_TEXTURE_COORD16_EXT 0x87AD +#define GL_OUTPUT_TEXTURE_COORD17_EXT 0x87AE +#define GL_OUTPUT_TEXTURE_COORD18_EXT 0x87AF +#define GL_OUTPUT_TEXTURE_COORD19_EXT 0x87B0 +#define GL_OUTPUT_TEXTURE_COORD20_EXT 0x87B1 +#define GL_OUTPUT_TEXTURE_COORD21_EXT 0x87B2 +#define GL_OUTPUT_TEXTURE_COORD22_EXT 0x87B3 +#define GL_OUTPUT_TEXTURE_COORD23_EXT 0x87B4 +#define GL_OUTPUT_TEXTURE_COORD24_EXT 0x87B5 +#define GL_OUTPUT_TEXTURE_COORD25_EXT 0x87B6 +#define GL_OUTPUT_TEXTURE_COORD26_EXT 0x87B7 +#define GL_OUTPUT_TEXTURE_COORD27_EXT 0x87B8 +#define GL_OUTPUT_TEXTURE_COORD28_EXT 0x87B9 +#define GL_OUTPUT_TEXTURE_COORD29_EXT 0x87BA +#define GL_OUTPUT_TEXTURE_COORD30_EXT 0x87BB +#define GL_OUTPUT_TEXTURE_COORD31_EXT 0x87BC +#define GL_OUTPUT_FOG_EXT 0x87BD +#define GL_SCALAR_EXT 0x87BE +#define GL_VECTOR_EXT 0x87BF +#define GL_MATRIX_EXT 0x87C0 +#define GL_VARIANT_EXT 0x87C1 +#define GL_INVARIANT_EXT 0x87C2 +#define GL_LOCAL_CONSTANT_EXT 0x87C3 +#define GL_LOCAL_EXT 0x87C4 +#define GL_MAX_VERTEX_SHADER_INSTRUCTIONS_EXT 0x87C5 +#define GL_MAX_VERTEX_SHADER_VARIANTS_EXT 0x87C6 +#define GL_MAX_VERTEX_SHADER_INVARIANTS_EXT 0x87C7 +#define GL_MAX_VERTEX_SHADER_LOCAL_CONSTANTS_EXT 0x87C8 +#define GL_MAX_VERTEX_SHADER_LOCALS_EXT 0x87C9 +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_INSTRUCTIONS_EXT 0x87CA +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_VARIANTS_EXT 0x87CB +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_LOCAL_CONSTANTS_EXT 0x87CC +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_INVARIANTS_EXT 0x87CD +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_LOCALS_EXT 0x87CE +#define GL_VERTEX_SHADER_INSTRUCTIONS_EXT 0x87CF +#define GL_VERTEX_SHADER_VARIANTS_EXT 0x87D0 +#define GL_VERTEX_SHADER_INVARIANTS_EXT 0x87D1 +#define GL_VERTEX_SHADER_LOCAL_CONSTANTS_EXT 0x87D2 +#define GL_VERTEX_SHADER_LOCALS_EXT 0x87D3 +#define GL_VERTEX_SHADER_OPTIMIZED_EXT 0x87D4 +#define GL_X_EXT 0x87D5 +#define GL_Y_EXT 0x87D6 +#define GL_Z_EXT 0x87D7 +#define GL_W_EXT 0x87D8 +#define GL_NEGATIVE_X_EXT 0x87D9 +#define GL_NEGATIVE_Y_EXT 0x87DA +#define GL_NEGATIVE_Z_EXT 0x87DB +#define GL_NEGATIVE_W_EXT 0x87DC +#define GL_ZERO_EXT 0x87DD +#define GL_ONE_EXT 0x87DE +#define GL_NEGATIVE_ONE_EXT 0x87DF +#define GL_NORMALIZED_RANGE_EXT 0x87E0 +#define GL_FULL_RANGE_EXT 0x87E1 +#define GL_CURRENT_VERTEX_EXT 0x87E2 +#define GL_MVP_MATRIX_EXT 0x87E3 +#define GL_VARIANT_VALUE_EXT 0x87E4 +#define GL_VARIANT_DATATYPE_EXT 0x87E5 +#define GL_VARIANT_ARRAY_STRIDE_EXT 0x87E6 +#define GL_VARIANT_ARRAY_TYPE_EXT 0x87E7 +#define GL_VARIANT_ARRAY_EXT 0x87E8 +#define GL_VARIANT_ARRAY_POINTER_EXT 0x87E9 +#define GL_INVARIANT_VALUE_EXT 0x87EA +#define GL_INVARIANT_DATATYPE_EXT 0x87EB +#define GL_LOCAL_CONSTANT_VALUE_EXT 0x87EC +#define GL_LOCAL_CONSTANT_DATATYPE_EXT 0x87ED +#endif + +#ifndef GL_ATI_vertex_streams +#define GL_MAX_VERTEX_STREAMS_ATI 0x876B +#define GL_VERTEX_STREAM0_ATI 0x876C +#define GL_VERTEX_STREAM1_ATI 0x876D +#define GL_VERTEX_STREAM2_ATI 0x876E +#define GL_VERTEX_STREAM3_ATI 0x876F +#define GL_VERTEX_STREAM4_ATI 0x8770 +#define GL_VERTEX_STREAM5_ATI 0x8771 +#define GL_VERTEX_STREAM6_ATI 0x8772 +#define GL_VERTEX_STREAM7_ATI 0x8773 +#define GL_VERTEX_SOURCE_ATI 0x8774 +#endif + +#ifndef GL_ATI_element_array +#define GL_ELEMENT_ARRAY_ATI 0x8768 +#define GL_ELEMENT_ARRAY_TYPE_ATI 0x8769 +#define GL_ELEMENT_ARRAY_POINTER_ATI 0x876A +#endif + +#ifndef GL_SUN_mesh_array +#define GL_QUAD_MESH_SUN 0x8614 +#define GL_TRIANGLE_MESH_SUN 0x8615 +#endif + +#ifndef GL_SUN_slice_accum +#define GL_SLICE_ACCUM_SUN 0x85CC +#endif + +#ifndef GL_NV_multisample_filter_hint +#define GL_MULTISAMPLE_FILTER_HINT_NV 0x8534 +#endif + +#ifndef GL_NV_depth_clamp +#define GL_DEPTH_CLAMP_NV 0x864F +#endif + +#ifndef GL_NV_occlusion_query +#define GL_PIXEL_COUNTER_BITS_NV 0x8864 +#define GL_CURRENT_OCCLUSION_QUERY_ID_NV 0x8865 +#define GL_PIXEL_COUNT_NV 0x8866 +#define GL_PIXEL_COUNT_AVAILABLE_NV 0x8867 +#endif + +#ifndef GL_NV_point_sprite +#define GL_POINT_SPRITE_NV 0x8861 +#define GL_COORD_REPLACE_NV 0x8862 +#define GL_POINT_SPRITE_R_MODE_NV 0x8863 +#endif + +#ifndef GL_NV_texture_shader3 +#define GL_OFFSET_PROJECTIVE_TEXTURE_2D_NV 0x8850 +#define GL_OFFSET_PROJECTIVE_TEXTURE_2D_SCALE_NV 0x8851 +#define GL_OFFSET_PROJECTIVE_TEXTURE_RECTANGLE_NV 0x8852 +#define GL_OFFSET_PROJECTIVE_TEXTURE_RECTANGLE_SCALE_NV 0x8853 +#define GL_OFFSET_HILO_TEXTURE_2D_NV 0x8854 +#define GL_OFFSET_HILO_TEXTURE_RECTANGLE_NV 0x8855 +#define GL_OFFSET_HILO_PROJECTIVE_TEXTURE_2D_NV 0x8856 +#define GL_OFFSET_HILO_PROJECTIVE_TEXTURE_RECTANGLE_NV 0x8857 +#define GL_DEPENDENT_HILO_TEXTURE_2D_NV 0x8858 +#define GL_DEPENDENT_RGB_TEXTURE_3D_NV 0x8859 +#define GL_DEPENDENT_RGB_TEXTURE_CUBE_MAP_NV 0x885A +#define GL_DOT_PRODUCT_PASS_THROUGH_NV 0x885B +#define GL_DOT_PRODUCT_TEXTURE_1D_NV 0x885C +#define GL_DOT_PRODUCT_AFFINE_DEPTH_REPLACE_NV 0x885D +#define GL_HILO8_NV 0x885E +#define GL_SIGNED_HILO8_NV 0x885F +#define GL_FORCE_BLUE_TO_ONE_NV 0x8860 +#endif + +#ifndef GL_NV_vertex_program1_1 +#endif + +#ifndef GL_EXT_shadow_funcs +#endif + +#ifndef GL_EXT_stencil_two_side +#define GL_STENCIL_TEST_TWO_SIDE_EXT 0x8910 +#define GL_ACTIVE_STENCIL_FACE_EXT 0x8911 +#endif + +#ifndef GL_ATI_text_fragment_shader +#define GL_TEXT_FRAGMENT_SHADER_ATI 0x8200 +#endif + +#ifndef GL_APPLE_client_storage +#define GL_UNPACK_CLIENT_STORAGE_APPLE 0x85B2 +#endif + +#ifndef GL_APPLE_element_array +#define GL_ELEMENT_ARRAY_APPLE 0x8A0C +#define GL_ELEMENT_ARRAY_TYPE_APPLE 0x8A0D +#define GL_ELEMENT_ARRAY_POINTER_APPLE 0x8A0E +#endif + +#ifndef GL_APPLE_fence +#define GL_DRAW_PIXELS_APPLE 0x8A0A +#define GL_FENCE_APPLE 0x8A0B +#endif + +#ifndef GL_APPLE_vertex_array_object +#define GL_VERTEX_ARRAY_BINDING_APPLE 0x85B5 +#endif + +#ifndef GL_APPLE_vertex_array_range +#define GL_VERTEX_ARRAY_RANGE_APPLE 0x851D +#define GL_VERTEX_ARRAY_RANGE_LENGTH_APPLE 0x851E +#define GL_VERTEX_ARRAY_STORAGE_HINT_APPLE 0x851F +#define GL_VERTEX_ARRAY_RANGE_POINTER_APPLE 0x8521 +#define GL_STORAGE_CLIENT_APPLE 0x85B4 +#define GL_STORAGE_CACHED_APPLE 0x85BE +#define GL_STORAGE_SHARED_APPLE 0x85BF +#endif + +#ifndef GL_APPLE_ycbcr_422 +#define GL_YCBCR_422_APPLE 0x85B9 +#define GL_UNSIGNED_SHORT_8_8_APPLE 0x85BA +#define GL_UNSIGNED_SHORT_8_8_REV_APPLE 0x85BB +#endif + +#ifndef GL_S3_s3tc +#define GL_RGB_S3TC 0x83A0 +#define GL_RGB4_S3TC 0x83A1 +#define GL_RGBA_S3TC 0x83A2 +#define GL_RGBA4_S3TC 0x83A3 +#endif + +#ifndef GL_ATI_draw_buffers +#define GL_MAX_DRAW_BUFFERS_ATI 0x8824 +#define GL_DRAW_BUFFER0_ATI 0x8825 +#define GL_DRAW_BUFFER1_ATI 0x8826 +#define GL_DRAW_BUFFER2_ATI 0x8827 +#define GL_DRAW_BUFFER3_ATI 0x8828 +#define GL_DRAW_BUFFER4_ATI 0x8829 +#define GL_DRAW_BUFFER5_ATI 0x882A +#define GL_DRAW_BUFFER6_ATI 0x882B +#define GL_DRAW_BUFFER7_ATI 0x882C +#define GL_DRAW_BUFFER8_ATI 0x882D +#define GL_DRAW_BUFFER9_ATI 0x882E +#define GL_DRAW_BUFFER10_ATI 0x882F +#define GL_DRAW_BUFFER11_ATI 0x8830 +#define GL_DRAW_BUFFER12_ATI 0x8831 +#define GL_DRAW_BUFFER13_ATI 0x8832 +#define GL_DRAW_BUFFER14_ATI 0x8833 +#define GL_DRAW_BUFFER15_ATI 0x8834 +#endif + +#ifndef GL_ATI_pixel_format_float +#define GL_TYPE_RGBA_FLOAT_ATI 0x8820 +#define GL_COLOR_CLEAR_UNCLAMPED_VALUE_ATI 0x8835 +#endif + +#ifndef GL_ATI_texture_env_combine3 +#define GL_MODULATE_ADD_ATI 0x8744 +#define GL_MODULATE_SIGNED_ADD_ATI 0x8745 +#define GL_MODULATE_SUBTRACT_ATI 0x8746 +#endif + +#ifndef GL_ATI_texture_float +#define GL_RGBA_FLOAT32_ATI 0x8814 +#define GL_RGB_FLOAT32_ATI 0x8815 +#define GL_ALPHA_FLOAT32_ATI 0x8816 +#define GL_INTENSITY_FLOAT32_ATI 0x8817 +#define GL_LUMINANCE_FLOAT32_ATI 0x8818 +#define GL_LUMINANCE_ALPHA_FLOAT32_ATI 0x8819 +#define GL_RGBA_FLOAT16_ATI 0x881A +#define GL_RGB_FLOAT16_ATI 0x881B +#define GL_ALPHA_FLOAT16_ATI 0x881C +#define GL_INTENSITY_FLOAT16_ATI 0x881D +#define GL_LUMINANCE_FLOAT16_ATI 0x881E +#define GL_LUMINANCE_ALPHA_FLOAT16_ATI 0x881F +#endif + +#ifndef GL_NV_float_buffer +#define GL_FLOAT_R_NV 0x8880 +#define GL_FLOAT_RG_NV 0x8881 +#define GL_FLOAT_RGB_NV 0x8882 +#define GL_FLOAT_RGBA_NV 0x8883 +#define GL_FLOAT_R16_NV 0x8884 +#define GL_FLOAT_R32_NV 0x8885 +#define GL_FLOAT_RG16_NV 0x8886 +#define GL_FLOAT_RG32_NV 0x8887 +#define GL_FLOAT_RGB16_NV 0x8888 +#define GL_FLOAT_RGB32_NV 0x8889 +#define GL_FLOAT_RGBA16_NV 0x888A +#define GL_FLOAT_RGBA32_NV 0x888B +#define GL_TEXTURE_FLOAT_COMPONENTS_NV 0x888C +#define GL_FLOAT_CLEAR_COLOR_VALUE_NV 0x888D +#define GL_FLOAT_RGBA_MODE_NV 0x888E +#endif + +#ifndef GL_NV_fragment_program +#define GL_MAX_FRAGMENT_PROGRAM_LOCAL_PARAMETERS_NV 0x8868 +#define GL_FRAGMENT_PROGRAM_NV 0x8870 +#define GL_MAX_TEXTURE_COORDS_NV 0x8871 +#define GL_MAX_TEXTURE_IMAGE_UNITS_NV 0x8872 +#define GL_FRAGMENT_PROGRAM_BINDING_NV 0x8873 +#define GL_PROGRAM_ERROR_STRING_NV 0x8874 +#endif + +#ifndef GL_NV_half_float +#define GL_HALF_FLOAT_NV 0x140B +#endif + +#ifndef GL_NV_pixel_data_range +#define GL_WRITE_PIXEL_DATA_RANGE_NV 0x8878 +#define GL_READ_PIXEL_DATA_RANGE_NV 0x8879 +#define GL_WRITE_PIXEL_DATA_RANGE_LENGTH_NV 0x887A +#define GL_READ_PIXEL_DATA_RANGE_LENGTH_NV 0x887B +#define GL_WRITE_PIXEL_DATA_RANGE_POINTER_NV 0x887C +#define GL_READ_PIXEL_DATA_RANGE_POINTER_NV 0x887D +#endif + +#ifndef GL_NV_primitive_restart +#define GL_PRIMITIVE_RESTART_NV 0x8558 +#define GL_PRIMITIVE_RESTART_INDEX_NV 0x8559 +#endif + +#ifndef GL_NV_texture_expand_normal +#define GL_TEXTURE_UNSIGNED_REMAP_MODE_NV 0x888F +#endif + +#ifndef GL_NV_vertex_program2 +#endif + +#ifndef GL_ATI_map_object_buffer +#endif + +#ifndef GL_ATI_separate_stencil +#define GL_STENCIL_BACK_FUNC_ATI 0x8800 +#define GL_STENCIL_BACK_FAIL_ATI 0x8801 +#define GL_STENCIL_BACK_PASS_DEPTH_FAIL_ATI 0x8802 +#define GL_STENCIL_BACK_PASS_DEPTH_PASS_ATI 0x8803 +#endif + +#ifndef GL_ATI_vertex_attrib_array_object +#endif + +#ifndef GL_OES_read_format +#define GL_IMPLEMENTATION_COLOR_READ_TYPE_OES 0x8B9A +#define GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES 0x8B9B +#endif + +#ifndef GL_EXT_depth_bounds_test +#define GL_DEPTH_BOUNDS_TEST_EXT 0x8890 +#define GL_DEPTH_BOUNDS_EXT 0x8891 +#endif + +#ifndef GL_EXT_texture_mirror_clamp +#define GL_MIRROR_CLAMP_EXT 0x8742 +#define GL_MIRROR_CLAMP_TO_EDGE_EXT 0x8743 +#define GL_MIRROR_CLAMP_TO_BORDER_EXT 0x8912 +#endif + +#ifndef GL_EXT_blend_equation_separate +#define GL_BLEND_EQUATION_RGB_EXT 0x8009 +#define GL_BLEND_EQUATION_ALPHA_EXT 0x883D +#endif + +#ifndef GL_MESA_pack_invert +#define GL_PACK_INVERT_MESA 0x8758 +#endif + +#ifndef GL_MESA_ycbcr_texture +#define GL_UNSIGNED_SHORT_8_8_MESA 0x85BA +#define GL_UNSIGNED_SHORT_8_8_REV_MESA 0x85BB +#define GL_YCBCR_MESA 0x8757 +#endif + +#ifndef GL_EXT_pixel_buffer_object +#define GL_PIXEL_PACK_BUFFER_EXT 0x88EB +#define GL_PIXEL_UNPACK_BUFFER_EXT 0x88EC +#define GL_PIXEL_PACK_BUFFER_BINDING_EXT 0x88ED +#define GL_PIXEL_UNPACK_BUFFER_BINDING_EXT 0x88EF +#endif + +#ifndef GL_NV_fragment_program_option +#endif + +#ifndef GL_NV_fragment_program2 +#define GL_MAX_PROGRAM_EXEC_INSTRUCTIONS_NV 0x88F4 +#define GL_MAX_PROGRAM_CALL_DEPTH_NV 0x88F5 +#define GL_MAX_PROGRAM_IF_DEPTH_NV 0x88F6 +#define GL_MAX_PROGRAM_LOOP_DEPTH_NV 0x88F7 +#define GL_MAX_PROGRAM_LOOP_COUNT_NV 0x88F8 +#endif + +#ifndef GL_NV_vertex_program2_option +/* reuse GL_MAX_PROGRAM_EXEC_INSTRUCTIONS_NV */ +/* reuse GL_MAX_PROGRAM_CALL_DEPTH_NV */ +#endif + +#ifndef GL_NV_vertex_program3 +/* reuse GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB */ +#endif + +#ifndef GL_EXT_framebuffer_object +#define GL_INVALID_FRAMEBUFFER_OPERATION_EXT 0x0506 +#define GL_MAX_RENDERBUFFER_SIZE_EXT 0x84E8 +#define GL_FRAMEBUFFER_BINDING_EXT 0x8CA6 +#define GL_RENDERBUFFER_BINDING_EXT 0x8CA7 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT 0x8CD0 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT 0x8CD1 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT 0x8CD2 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT 0x8CD3 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT 0x8CD4 +#define GL_FRAMEBUFFER_COMPLETE_EXT 0x8CD5 +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT 0x8CD6 +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT 0x8CD7 +#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT 0x8CD9 +#define GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT 0x8CDA +#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT 0x8CDB +#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT 0x8CDC +#define GL_FRAMEBUFFER_UNSUPPORTED_EXT 0x8CDD +#define GL_MAX_COLOR_ATTACHMENTS_EXT 0x8CDF +#define GL_COLOR_ATTACHMENT0_EXT 0x8CE0 +#define GL_COLOR_ATTACHMENT1_EXT 0x8CE1 +#define GL_COLOR_ATTACHMENT2_EXT 0x8CE2 +#define GL_COLOR_ATTACHMENT3_EXT 0x8CE3 +#define GL_COLOR_ATTACHMENT4_EXT 0x8CE4 +#define GL_COLOR_ATTACHMENT5_EXT 0x8CE5 +#define GL_COLOR_ATTACHMENT6_EXT 0x8CE6 +#define GL_COLOR_ATTACHMENT7_EXT 0x8CE7 +#define GL_COLOR_ATTACHMENT8_EXT 0x8CE8 +#define GL_COLOR_ATTACHMENT9_EXT 0x8CE9 +#define GL_COLOR_ATTACHMENT10_EXT 0x8CEA +#define GL_COLOR_ATTACHMENT11_EXT 0x8CEB +#define GL_COLOR_ATTACHMENT12_EXT 0x8CEC +#define GL_COLOR_ATTACHMENT13_EXT 0x8CED +#define GL_COLOR_ATTACHMENT14_EXT 0x8CEE +#define GL_COLOR_ATTACHMENT15_EXT 0x8CEF +#define GL_DEPTH_ATTACHMENT_EXT 0x8D00 +#define GL_STENCIL_ATTACHMENT_EXT 0x8D20 +#define GL_FRAMEBUFFER_EXT 0x8D40 +#define GL_RENDERBUFFER_EXT 0x8D41 +#define GL_RENDERBUFFER_WIDTH_EXT 0x8D42 +#define GL_RENDERBUFFER_HEIGHT_EXT 0x8D43 +#define GL_RENDERBUFFER_INTERNAL_FORMAT_EXT 0x8D44 +#define GL_STENCIL_INDEX1_EXT 0x8D46 +#define GL_STENCIL_INDEX4_EXT 0x8D47 +#define GL_STENCIL_INDEX8_EXT 0x8D48 +#define GL_STENCIL_INDEX16_EXT 0x8D49 +#define GL_RENDERBUFFER_RED_SIZE_EXT 0x8D50 +#define GL_RENDERBUFFER_GREEN_SIZE_EXT 0x8D51 +#define GL_RENDERBUFFER_BLUE_SIZE_EXT 0x8D52 +#define GL_RENDERBUFFER_ALPHA_SIZE_EXT 0x8D53 +#define GL_RENDERBUFFER_DEPTH_SIZE_EXT 0x8D54 +#define GL_RENDERBUFFER_STENCIL_SIZE_EXT 0x8D55 +#endif + +#ifndef GL_GREMEDY_string_marker +#endif + +#ifndef GL_EXT_packed_depth_stencil +#define GL_DEPTH_STENCIL_EXT 0x84F9 +#define GL_UNSIGNED_INT_24_8_EXT 0x84FA +#define GL_DEPTH24_STENCIL8_EXT 0x88F0 +#define GL_TEXTURE_STENCIL_SIZE_EXT 0x88F1 +#endif + +#ifndef GL_EXT_stencil_clear_tag +#define GL_STENCIL_TAG_BITS_EXT 0x88F2 +#define GL_STENCIL_CLEAR_TAG_VALUE_EXT 0x88F3 +#endif + +#ifndef GL_EXT_texture_sRGB +#define GL_SRGB_EXT 0x8C40 +#define GL_SRGB8_EXT 0x8C41 +#define GL_SRGB_ALPHA_EXT 0x8C42 +#define GL_SRGB8_ALPHA8_EXT 0x8C43 +#define GL_SLUMINANCE_ALPHA_EXT 0x8C44 +#define GL_SLUMINANCE8_ALPHA8_EXT 0x8C45 +#define GL_SLUMINANCE_EXT 0x8C46 +#define GL_SLUMINANCE8_EXT 0x8C47 +#define GL_COMPRESSED_SRGB_EXT 0x8C48 +#define GL_COMPRESSED_SRGB_ALPHA_EXT 0x8C49 +#define GL_COMPRESSED_SLUMINANCE_EXT 0x8C4A +#define GL_COMPRESSED_SLUMINANCE_ALPHA_EXT 0x8C4B +#define GL_COMPRESSED_SRGB_S3TC_DXT1_EXT 0x8C4C +#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT 0x8C4D +#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT 0x8C4E +#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT 0x8C4F +#endif + +#ifndef GL_EXT_framebuffer_blit +#define GL_READ_FRAMEBUFFER_EXT 0x8CA8 +#define GL_DRAW_FRAMEBUFFER_EXT 0x8CA9 +#define GL_DRAW_FRAMEBUFFER_BINDING_EXT GL_FRAMEBUFFER_BINDING_EXT +#define GL_READ_FRAMEBUFFER_BINDING_EXT 0x8CAA +#endif + +#ifndef GL_EXT_framebuffer_multisample +#define GL_RENDERBUFFER_SAMPLES_EXT 0x8CAB +#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT 0x8D56 +#define GL_MAX_SAMPLES_EXT 0x8D57 +#endif + +#ifndef GL_MESAX_texture_stack +#define GL_TEXTURE_1D_STACK_MESAX 0x8759 +#define GL_TEXTURE_2D_STACK_MESAX 0x875A +#define GL_PROXY_TEXTURE_1D_STACK_MESAX 0x875B +#define GL_PROXY_TEXTURE_2D_STACK_MESAX 0x875C +#define GL_TEXTURE_1D_STACK_BINDING_MESAX 0x875D +#define GL_TEXTURE_2D_STACK_BINDING_MESAX 0x875E +#endif + +#ifndef GL_EXT_timer_query +#define GL_TIME_ELAPSED_EXT 0x88BF +#endif + +#ifndef GL_EXT_gpu_program_parameters +#endif + +#ifndef GL_APPLE_flush_buffer_range +#define GL_BUFFER_SERIALIZED_MODIFY_APPLE 0x8A12 +#define GL_BUFFER_FLUSHING_UNMAP_APPLE 0x8A13 +#endif + +#ifndef GL_NV_gpu_program4 +#define GL_MIN_PROGRAM_TEXEL_OFFSET_NV 0x8904 +#define GL_MAX_PROGRAM_TEXEL_OFFSET_NV 0x8905 +#define GL_PROGRAM_ATTRIB_COMPONENTS_NV 0x8906 +#define GL_PROGRAM_RESULT_COMPONENTS_NV 0x8907 +#define GL_MAX_PROGRAM_ATTRIB_COMPONENTS_NV 0x8908 +#define GL_MAX_PROGRAM_RESULT_COMPONENTS_NV 0x8909 +#define GL_MAX_PROGRAM_GENERIC_ATTRIBS_NV 0x8DA5 +#define GL_MAX_PROGRAM_GENERIC_RESULTS_NV 0x8DA6 +#endif + +#ifndef GL_NV_geometry_program4 +#define GL_LINES_ADJACENCY_EXT 0x000A +#define GL_LINE_STRIP_ADJACENCY_EXT 0x000B +#define GL_TRIANGLES_ADJACENCY_EXT 0x000C +#define GL_TRIANGLE_STRIP_ADJACENCY_EXT 0x000D +#define GL_GEOMETRY_PROGRAM_NV 0x8C26 +#define GL_MAX_PROGRAM_OUTPUT_VERTICES_NV 0x8C27 +#define GL_MAX_PROGRAM_TOTAL_OUTPUT_COMPONENTS_NV 0x8C28 +#define GL_GEOMETRY_VERTICES_OUT_EXT 0x8DDA +#define GL_GEOMETRY_INPUT_TYPE_EXT 0x8DDB +#define GL_GEOMETRY_OUTPUT_TYPE_EXT 0x8DDC +#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_EXT 0x8C29 +#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED_EXT 0x8DA7 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT 0x8DA8 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_EXT 0x8DA9 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER_EXT 0x8CD4 +#define GL_PROGRAM_POINT_SIZE_EXT 0x8642 +#endif + +#ifndef GL_EXT_geometry_shader4 +#define GL_GEOMETRY_SHADER_EXT 0x8DD9 +/* reuse GL_GEOMETRY_VERTICES_OUT_EXT */ +/* reuse GL_GEOMETRY_INPUT_TYPE_EXT */ +/* reuse GL_GEOMETRY_OUTPUT_TYPE_EXT */ +/* reuse GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_EXT */ +#define GL_MAX_GEOMETRY_VARYING_COMPONENTS_EXT 0x8DDD +#define GL_MAX_VERTEX_VARYING_COMPONENTS_EXT 0x8DDE +#define GL_MAX_VARYING_COMPONENTS_EXT 0x8B4B +#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_EXT 0x8DDF +#define GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT 0x8DE0 +#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_EXT 0x8DE1 +/* reuse GL_LINES_ADJACENCY_EXT */ +/* reuse GL_LINE_STRIP_ADJACENCY_EXT */ +/* reuse GL_TRIANGLES_ADJACENCY_EXT */ +/* reuse GL_TRIANGLE_STRIP_ADJACENCY_EXT */ +/* reuse GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT */ +/* reuse GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_EXT */ +/* reuse GL_FRAMEBUFFER_ATTACHMENT_LAYERED_EXT */ +/* reuse GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER_EXT */ +/* reuse GL_PROGRAM_POINT_SIZE_EXT */ +#endif + +#ifndef GL_NV_vertex_program4 +#define GL_VERTEX_ATTRIB_ARRAY_INTEGER_NV 0x88FD +#endif + +#ifndef GL_EXT_gpu_shader4 +#define GL_SAMPLER_1D_ARRAY_EXT 0x8DC0 +#define GL_SAMPLER_2D_ARRAY_EXT 0x8DC1 +#define GL_SAMPLER_BUFFER_EXT 0x8DC2 +#define GL_SAMPLER_1D_ARRAY_SHADOW_EXT 0x8DC3 +#define GL_SAMPLER_2D_ARRAY_SHADOW_EXT 0x8DC4 +#define GL_SAMPLER_CUBE_SHADOW_EXT 0x8DC5 +#define GL_UNSIGNED_INT_VEC2_EXT 0x8DC6 +#define GL_UNSIGNED_INT_VEC3_EXT 0x8DC7 +#define GL_UNSIGNED_INT_VEC4_EXT 0x8DC8 +#define GL_INT_SAMPLER_1D_EXT 0x8DC9 +#define GL_INT_SAMPLER_2D_EXT 0x8DCA +#define GL_INT_SAMPLER_3D_EXT 0x8DCB +#define GL_INT_SAMPLER_CUBE_EXT 0x8DCC +#define GL_INT_SAMPLER_2D_RECT_EXT 0x8DCD +#define GL_INT_SAMPLER_1D_ARRAY_EXT 0x8DCE +#define GL_INT_SAMPLER_2D_ARRAY_EXT 0x8DCF +#define GL_INT_SAMPLER_BUFFER_EXT 0x8DD0 +#define GL_UNSIGNED_INT_SAMPLER_1D_EXT 0x8DD1 +#define GL_UNSIGNED_INT_SAMPLER_2D_EXT 0x8DD2 +#define GL_UNSIGNED_INT_SAMPLER_3D_EXT 0x8DD3 +#define GL_UNSIGNED_INT_SAMPLER_CUBE_EXT 0x8DD4 +#define GL_UNSIGNED_INT_SAMPLER_2D_RECT_EXT 0x8DD5 +#define GL_UNSIGNED_INT_SAMPLER_1D_ARRAY_EXT 0x8DD6 +#define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY_EXT 0x8DD7 +#define GL_UNSIGNED_INT_SAMPLER_BUFFER_EXT 0x8DD8 +#endif + +#ifndef GL_EXT_draw_instanced +#endif + +#ifndef GL_EXT_packed_float +#define GL_R11F_G11F_B10F_EXT 0x8C3A +#define GL_UNSIGNED_INT_10F_11F_11F_REV_EXT 0x8C3B +#define GL_RGBA_SIGNED_COMPONENTS_EXT 0x8C3C +#endif + +#ifndef GL_EXT_texture_array +#define GL_TEXTURE_1D_ARRAY_EXT 0x8C18 +#define GL_PROXY_TEXTURE_1D_ARRAY_EXT 0x8C19 +#define GL_TEXTURE_2D_ARRAY_EXT 0x8C1A +#define GL_PROXY_TEXTURE_2D_ARRAY_EXT 0x8C1B +#define GL_TEXTURE_BINDING_1D_ARRAY_EXT 0x8C1C +#define GL_TEXTURE_BINDING_2D_ARRAY_EXT 0x8C1D +#define GL_MAX_ARRAY_TEXTURE_LAYERS_EXT 0x88FF +#define GL_COMPARE_REF_DEPTH_TO_TEXTURE_EXT 0x884E +/* reuse GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER_EXT */ +#endif + +#ifndef GL_EXT_texture_buffer_object +#define GL_TEXTURE_BUFFER_EXT 0x8C2A +#define GL_MAX_TEXTURE_BUFFER_SIZE_EXT 0x8C2B +#define GL_TEXTURE_BINDING_BUFFER_EXT 0x8C2C +#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING_EXT 0x8C2D +#define GL_TEXTURE_BUFFER_FORMAT_EXT 0x8C2E +#endif + +#ifndef GL_EXT_texture_compression_latc +#define GL_COMPRESSED_LUMINANCE_LATC1_EXT 0x8C70 +#define GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT 0x8C71 +#define GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT 0x8C72 +#define GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT 0x8C73 +#endif + +#ifndef GL_EXT_texture_compression_rgtc +#define GL_COMPRESSED_RED_RGTC1_EXT 0x8DBB +#define GL_COMPRESSED_SIGNED_RED_RGTC1_EXT 0x8DBC +#define GL_COMPRESSED_RED_GREEN_RGTC2_EXT 0x8DBD +#define GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT 0x8DBE +#endif + +#ifndef GL_EXT_texture_shared_exponent +#define GL_RGB9_E5_EXT 0x8C3D +#define GL_UNSIGNED_INT_5_9_9_9_REV_EXT 0x8C3E +#define GL_TEXTURE_SHARED_SIZE_EXT 0x8C3F +#endif + +#ifndef GL_NV_depth_buffer_float +#define GL_DEPTH_COMPONENT32F_NV 0x8DAB +#define GL_DEPTH32F_STENCIL8_NV 0x8DAC +#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV_NV 0x8DAD +#define GL_DEPTH_BUFFER_FLOAT_MODE_NV 0x8DAF +#endif + +#ifndef GL_NV_fragment_program4 +#endif + +#ifndef GL_NV_framebuffer_multisample_coverage +#define GL_RENDERBUFFER_COVERAGE_SAMPLES_NV 0x8CAB +#define GL_RENDERBUFFER_COLOR_SAMPLES_NV 0x8E10 +#define GL_MAX_MULTISAMPLE_COVERAGE_MODES_NV 0x8E11 +#define GL_MULTISAMPLE_COVERAGE_MODES_NV 0x8E12 +#endif + +#ifndef GL_EXT_framebuffer_sRGB +#define GL_FRAMEBUFFER_SRGB_EXT 0x8DB9 +#define GL_FRAMEBUFFER_SRGB_CAPABLE_EXT 0x8DBA +#endif + +#ifndef GL_NV_geometry_shader4 +#endif + +#ifndef GL_NV_parameter_buffer_object +#define GL_MAX_PROGRAM_PARAMETER_BUFFER_BINDINGS_NV 0x8DA0 +#define GL_MAX_PROGRAM_PARAMETER_BUFFER_SIZE_NV 0x8DA1 +#define GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV 0x8DA2 +#define GL_GEOMETRY_PROGRAM_PARAMETER_BUFFER_NV 0x8DA3 +#define GL_FRAGMENT_PROGRAM_PARAMETER_BUFFER_NV 0x8DA4 +#endif + +#ifndef GL_EXT_draw_buffers2 +#endif + +#ifndef GL_NV_transform_feedback +#define GL_BACK_PRIMARY_COLOR_NV 0x8C77 +#define GL_BACK_SECONDARY_COLOR_NV 0x8C78 +#define GL_TEXTURE_COORD_NV 0x8C79 +#define GL_CLIP_DISTANCE_NV 0x8C7A +#define GL_VERTEX_ID_NV 0x8C7B +#define GL_PRIMITIVE_ID_NV 0x8C7C +#define GL_GENERIC_ATTRIB_NV 0x8C7D +#define GL_TRANSFORM_FEEDBACK_ATTRIBS_NV 0x8C7E +#define GL_TRANSFORM_FEEDBACK_BUFFER_MODE_NV 0x8C7F +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS_NV 0x8C80 +#define GL_ACTIVE_VARYINGS_NV 0x8C81 +#define GL_ACTIVE_VARYING_MAX_LENGTH_NV 0x8C82 +#define GL_TRANSFORM_FEEDBACK_VARYINGS_NV 0x8C83 +#define GL_TRANSFORM_FEEDBACK_BUFFER_START_NV 0x8C84 +#define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE_NV 0x8C85 +#define GL_TRANSFORM_FEEDBACK_RECORD_NV 0x8C86 +#define GL_PRIMITIVES_GENERATED_NV 0x8C87 +#define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN_NV 0x8C88 +#define GL_RASTERIZER_DISCARD_NV 0x8C89 +#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_ATTRIBS_NV 0x8C8A +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS_NV 0x8C8B +#define GL_INTERLEAVED_ATTRIBS_NV 0x8C8C +#define GL_SEPARATE_ATTRIBS_NV 0x8C8D +#define GL_TRANSFORM_FEEDBACK_BUFFER_NV 0x8C8E +#define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING_NV 0x8C8F +#define GL_LAYER_NV 0x8DAA +#define GL_NEXT_BUFFER_NV -2 +#define GL_SKIP_COMPONENTS4_NV -3 +#define GL_SKIP_COMPONENTS3_NV -4 +#define GL_SKIP_COMPONENTS2_NV -5 +#define GL_SKIP_COMPONENTS1_NV -6 +#endif + +#ifndef GL_EXT_bindable_uniform +#define GL_MAX_VERTEX_BINDABLE_UNIFORMS_EXT 0x8DE2 +#define GL_MAX_FRAGMENT_BINDABLE_UNIFORMS_EXT 0x8DE3 +#define GL_MAX_GEOMETRY_BINDABLE_UNIFORMS_EXT 0x8DE4 +#define GL_MAX_BINDABLE_UNIFORM_SIZE_EXT 0x8DED +#define GL_UNIFORM_BUFFER_EXT 0x8DEE +#define GL_UNIFORM_BUFFER_BINDING_EXT 0x8DEF +#endif + +#ifndef GL_EXT_texture_integer +#define GL_RGBA32UI_EXT 0x8D70 +#define GL_RGB32UI_EXT 0x8D71 +#define GL_ALPHA32UI_EXT 0x8D72 +#define GL_INTENSITY32UI_EXT 0x8D73 +#define GL_LUMINANCE32UI_EXT 0x8D74 +#define GL_LUMINANCE_ALPHA32UI_EXT 0x8D75 +#define GL_RGBA16UI_EXT 0x8D76 +#define GL_RGB16UI_EXT 0x8D77 +#define GL_ALPHA16UI_EXT 0x8D78 +#define GL_INTENSITY16UI_EXT 0x8D79 +#define GL_LUMINANCE16UI_EXT 0x8D7A +#define GL_LUMINANCE_ALPHA16UI_EXT 0x8D7B +#define GL_RGBA8UI_EXT 0x8D7C +#define GL_RGB8UI_EXT 0x8D7D +#define GL_ALPHA8UI_EXT 0x8D7E +#define GL_INTENSITY8UI_EXT 0x8D7F +#define GL_LUMINANCE8UI_EXT 0x8D80 +#define GL_LUMINANCE_ALPHA8UI_EXT 0x8D81 +#define GL_RGBA32I_EXT 0x8D82 +#define GL_RGB32I_EXT 0x8D83 +#define GL_ALPHA32I_EXT 0x8D84 +#define GL_INTENSITY32I_EXT 0x8D85 +#define GL_LUMINANCE32I_EXT 0x8D86 +#define GL_LUMINANCE_ALPHA32I_EXT 0x8D87 +#define GL_RGBA16I_EXT 0x8D88 +#define GL_RGB16I_EXT 0x8D89 +#define GL_ALPHA16I_EXT 0x8D8A +#define GL_INTENSITY16I_EXT 0x8D8B +#define GL_LUMINANCE16I_EXT 0x8D8C +#define GL_LUMINANCE_ALPHA16I_EXT 0x8D8D +#define GL_RGBA8I_EXT 0x8D8E +#define GL_RGB8I_EXT 0x8D8F +#define GL_ALPHA8I_EXT 0x8D90 +#define GL_INTENSITY8I_EXT 0x8D91 +#define GL_LUMINANCE8I_EXT 0x8D92 +#define GL_LUMINANCE_ALPHA8I_EXT 0x8D93 +#define GL_RED_INTEGER_EXT 0x8D94 +#define GL_GREEN_INTEGER_EXT 0x8D95 +#define GL_BLUE_INTEGER_EXT 0x8D96 +#define GL_ALPHA_INTEGER_EXT 0x8D97 +#define GL_RGB_INTEGER_EXT 0x8D98 +#define GL_RGBA_INTEGER_EXT 0x8D99 +#define GL_BGR_INTEGER_EXT 0x8D9A +#define GL_BGRA_INTEGER_EXT 0x8D9B +#define GL_LUMINANCE_INTEGER_EXT 0x8D9C +#define GL_LUMINANCE_ALPHA_INTEGER_EXT 0x8D9D +#define GL_RGBA_INTEGER_MODE_EXT 0x8D9E +#endif + +#ifndef GL_GREMEDY_frame_terminator +#endif + +#ifndef GL_NV_conditional_render +#define GL_QUERY_WAIT_NV 0x8E13 +#define GL_QUERY_NO_WAIT_NV 0x8E14 +#define GL_QUERY_BY_REGION_WAIT_NV 0x8E15 +#define GL_QUERY_BY_REGION_NO_WAIT_NV 0x8E16 +#endif + +#ifndef GL_NV_present_video +#define GL_FRAME_NV 0x8E26 +#define GL_FIELDS_NV 0x8E27 +#define GL_CURRENT_TIME_NV 0x8E28 +#define GL_NUM_FILL_STREAMS_NV 0x8E29 +#define GL_PRESENT_TIME_NV 0x8E2A +#define GL_PRESENT_DURATION_NV 0x8E2B +#endif + +#ifndef GL_EXT_transform_feedback +#define GL_TRANSFORM_FEEDBACK_BUFFER_EXT 0x8C8E +#define GL_TRANSFORM_FEEDBACK_BUFFER_START_EXT 0x8C84 +#define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE_EXT 0x8C85 +#define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING_EXT 0x8C8F +#define GL_INTERLEAVED_ATTRIBS_EXT 0x8C8C +#define GL_SEPARATE_ATTRIBS_EXT 0x8C8D +#define GL_PRIMITIVES_GENERATED_EXT 0x8C87 +#define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN_EXT 0x8C88 +#define GL_RASTERIZER_DISCARD_EXT 0x8C89 +#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS_EXT 0x8C8A +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS_EXT 0x8C8B +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS_EXT 0x8C80 +#define GL_TRANSFORM_FEEDBACK_VARYINGS_EXT 0x8C83 +#define GL_TRANSFORM_FEEDBACK_BUFFER_MODE_EXT 0x8C7F +#define GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH_EXT 0x8C76 +#endif + +#ifndef GL_EXT_direct_state_access +#define GL_PROGRAM_MATRIX_EXT 0x8E2D +#define GL_TRANSPOSE_PROGRAM_MATRIX_EXT 0x8E2E +#define GL_PROGRAM_MATRIX_STACK_DEPTH_EXT 0x8E2F +#endif + +#ifndef GL_EXT_vertex_array_bgra +/* reuse GL_BGRA */ +#endif + +#ifndef GL_EXT_texture_swizzle +#define GL_TEXTURE_SWIZZLE_R_EXT 0x8E42 +#define GL_TEXTURE_SWIZZLE_G_EXT 0x8E43 +#define GL_TEXTURE_SWIZZLE_B_EXT 0x8E44 +#define GL_TEXTURE_SWIZZLE_A_EXT 0x8E45 +#define GL_TEXTURE_SWIZZLE_RGBA_EXT 0x8E46 +#endif + +#ifndef GL_NV_explicit_multisample +#define GL_SAMPLE_POSITION_NV 0x8E50 +#define GL_SAMPLE_MASK_NV 0x8E51 +#define GL_SAMPLE_MASK_VALUE_NV 0x8E52 +#define GL_TEXTURE_BINDING_RENDERBUFFER_NV 0x8E53 +#define GL_TEXTURE_RENDERBUFFER_DATA_STORE_BINDING_NV 0x8E54 +#define GL_TEXTURE_RENDERBUFFER_NV 0x8E55 +#define GL_SAMPLER_RENDERBUFFER_NV 0x8E56 +#define GL_INT_SAMPLER_RENDERBUFFER_NV 0x8E57 +#define GL_UNSIGNED_INT_SAMPLER_RENDERBUFFER_NV 0x8E58 +#define GL_MAX_SAMPLE_MASK_WORDS_NV 0x8E59 +#endif + +#ifndef GL_NV_transform_feedback2 +#define GL_TRANSFORM_FEEDBACK_NV 0x8E22 +#define GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED_NV 0x8E23 +#define GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE_NV 0x8E24 +#define GL_TRANSFORM_FEEDBACK_BINDING_NV 0x8E25 +#endif + +#ifndef GL_ATI_meminfo +#define GL_VBO_FREE_MEMORY_ATI 0x87FB +#define GL_TEXTURE_FREE_MEMORY_ATI 0x87FC +#define GL_RENDERBUFFER_FREE_MEMORY_ATI 0x87FD +#endif + +#ifndef GL_AMD_performance_monitor +#define GL_COUNTER_TYPE_AMD 0x8BC0 +#define GL_COUNTER_RANGE_AMD 0x8BC1 +#define GL_UNSIGNED_INT64_AMD 0x8BC2 +#define GL_PERCENTAGE_AMD 0x8BC3 +#define GL_PERFMON_RESULT_AVAILABLE_AMD 0x8BC4 +#define GL_PERFMON_RESULT_SIZE_AMD 0x8BC5 +#define GL_PERFMON_RESULT_AMD 0x8BC6 +#endif + +#ifndef GL_AMD_texture_texture4 +#endif + +#ifndef GL_AMD_vertex_shader_tesselator +#define GL_SAMPLER_BUFFER_AMD 0x9001 +#define GL_INT_SAMPLER_BUFFER_AMD 0x9002 +#define GL_UNSIGNED_INT_SAMPLER_BUFFER_AMD 0x9003 +#define GL_TESSELLATION_MODE_AMD 0x9004 +#define GL_TESSELLATION_FACTOR_AMD 0x9005 +#define GL_DISCRETE_AMD 0x9006 +#define GL_CONTINUOUS_AMD 0x9007 +#endif + +#ifndef GL_EXT_provoking_vertex +#define GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION_EXT 0x8E4C +#define GL_FIRST_VERTEX_CONVENTION_EXT 0x8E4D +#define GL_LAST_VERTEX_CONVENTION_EXT 0x8E4E +#define GL_PROVOKING_VERTEX_EXT 0x8E4F +#endif + +#ifndef GL_EXT_texture_snorm +#define GL_ALPHA_SNORM 0x9010 +#define GL_LUMINANCE_SNORM 0x9011 +#define GL_LUMINANCE_ALPHA_SNORM 0x9012 +#define GL_INTENSITY_SNORM 0x9013 +#define GL_ALPHA8_SNORM 0x9014 +#define GL_LUMINANCE8_SNORM 0x9015 +#define GL_LUMINANCE8_ALPHA8_SNORM 0x9016 +#define GL_INTENSITY8_SNORM 0x9017 +#define GL_ALPHA16_SNORM 0x9018 +#define GL_LUMINANCE16_SNORM 0x9019 +#define GL_LUMINANCE16_ALPHA16_SNORM 0x901A +#define GL_INTENSITY16_SNORM 0x901B +/* reuse GL_RED_SNORM */ +/* reuse GL_RG_SNORM */ +/* reuse GL_RGB_SNORM */ +/* reuse GL_RGBA_SNORM */ +/* reuse GL_R8_SNORM */ +/* reuse GL_RG8_SNORM */ +/* reuse GL_RGB8_SNORM */ +/* reuse GL_RGBA8_SNORM */ +/* reuse GL_R16_SNORM */ +/* reuse GL_RG16_SNORM */ +/* reuse GL_RGB16_SNORM */ +/* reuse GL_RGBA16_SNORM */ +/* reuse GL_SIGNED_NORMALIZED */ +#endif + +#ifndef GL_AMD_draw_buffers_blend +#endif + +#ifndef GL_APPLE_texture_range +#define GL_TEXTURE_RANGE_LENGTH_APPLE 0x85B7 +#define GL_TEXTURE_RANGE_POINTER_APPLE 0x85B8 +#define GL_TEXTURE_STORAGE_HINT_APPLE 0x85BC +#define GL_STORAGE_PRIVATE_APPLE 0x85BD +/* reuse GL_STORAGE_CACHED_APPLE */ +/* reuse GL_STORAGE_SHARED_APPLE */ +#endif + +#ifndef GL_APPLE_float_pixels +#define GL_HALF_APPLE 0x140B +#define GL_RGBA_FLOAT32_APPLE 0x8814 +#define GL_RGB_FLOAT32_APPLE 0x8815 +#define GL_ALPHA_FLOAT32_APPLE 0x8816 +#define GL_INTENSITY_FLOAT32_APPLE 0x8817 +#define GL_LUMINANCE_FLOAT32_APPLE 0x8818 +#define GL_LUMINANCE_ALPHA_FLOAT32_APPLE 0x8819 +#define GL_RGBA_FLOAT16_APPLE 0x881A +#define GL_RGB_FLOAT16_APPLE 0x881B +#define GL_ALPHA_FLOAT16_APPLE 0x881C +#define GL_INTENSITY_FLOAT16_APPLE 0x881D +#define GL_LUMINANCE_FLOAT16_APPLE 0x881E +#define GL_LUMINANCE_ALPHA_FLOAT16_APPLE 0x881F +#define GL_COLOR_FLOAT_APPLE 0x8A0F +#endif + +#ifndef GL_APPLE_vertex_program_evaluators +#define GL_VERTEX_ATTRIB_MAP1_APPLE 0x8A00 +#define GL_VERTEX_ATTRIB_MAP2_APPLE 0x8A01 +#define GL_VERTEX_ATTRIB_MAP1_SIZE_APPLE 0x8A02 +#define GL_VERTEX_ATTRIB_MAP1_COEFF_APPLE 0x8A03 +#define GL_VERTEX_ATTRIB_MAP1_ORDER_APPLE 0x8A04 +#define GL_VERTEX_ATTRIB_MAP1_DOMAIN_APPLE 0x8A05 +#define GL_VERTEX_ATTRIB_MAP2_SIZE_APPLE 0x8A06 +#define GL_VERTEX_ATTRIB_MAP2_COEFF_APPLE 0x8A07 +#define GL_VERTEX_ATTRIB_MAP2_ORDER_APPLE 0x8A08 +#define GL_VERTEX_ATTRIB_MAP2_DOMAIN_APPLE 0x8A09 +#endif + +#ifndef GL_APPLE_aux_depth_stencil +#define GL_AUX_DEPTH_STENCIL_APPLE 0x8A14 +#endif + +#ifndef GL_APPLE_object_purgeable +#define GL_BUFFER_OBJECT_APPLE 0x85B3 +#define GL_RELEASED_APPLE 0x8A19 +#define GL_VOLATILE_APPLE 0x8A1A +#define GL_RETAINED_APPLE 0x8A1B +#define GL_UNDEFINED_APPLE 0x8A1C +#define GL_PURGEABLE_APPLE 0x8A1D +#endif + +#ifndef GL_APPLE_row_bytes +#define GL_PACK_ROW_BYTES_APPLE 0x8A15 +#define GL_UNPACK_ROW_BYTES_APPLE 0x8A16 +#endif + +#ifndef GL_APPLE_rgb_422 +#define GL_RGB_422_APPLE 0x8A1F +/* reuse GL_UNSIGNED_SHORT_8_8_APPLE */ +/* reuse GL_UNSIGNED_SHORT_8_8_REV_APPLE */ +#endif + +#ifndef GL_NV_video_capture +#define GL_VIDEO_BUFFER_NV 0x9020 +#define GL_VIDEO_BUFFER_BINDING_NV 0x9021 +#define GL_FIELD_UPPER_NV 0x9022 +#define GL_FIELD_LOWER_NV 0x9023 +#define GL_NUM_VIDEO_CAPTURE_STREAMS_NV 0x9024 +#define GL_NEXT_VIDEO_CAPTURE_BUFFER_STATUS_NV 0x9025 +#define GL_VIDEO_CAPTURE_TO_422_SUPPORTED_NV 0x9026 +#define GL_LAST_VIDEO_CAPTURE_STATUS_NV 0x9027 +#define GL_VIDEO_BUFFER_PITCH_NV 0x9028 +#define GL_VIDEO_COLOR_CONVERSION_MATRIX_NV 0x9029 +#define GL_VIDEO_COLOR_CONVERSION_MAX_NV 0x902A +#define GL_VIDEO_COLOR_CONVERSION_MIN_NV 0x902B +#define GL_VIDEO_COLOR_CONVERSION_OFFSET_NV 0x902C +#define GL_VIDEO_BUFFER_INTERNAL_FORMAT_NV 0x902D +#define GL_PARTIAL_SUCCESS_NV 0x902E +#define GL_SUCCESS_NV 0x902F +#define GL_FAILURE_NV 0x9030 +#define GL_YCBYCR8_422_NV 0x9031 +#define GL_YCBAYCR8A_4224_NV 0x9032 +#define GL_Z6Y10Z6CB10Z6Y10Z6CR10_422_NV 0x9033 +#define GL_Z6Y10Z6CB10Z6A10Z6Y10Z6CR10Z6A10_4224_NV 0x9034 +#define GL_Z4Y12Z4CB12Z4Y12Z4CR12_422_NV 0x9035 +#define GL_Z4Y12Z4CB12Z4A12Z4Y12Z4CR12Z4A12_4224_NV 0x9036 +#define GL_Z4Y12Z4CB12Z4CR12_444_NV 0x9037 +#define GL_VIDEO_CAPTURE_FRAME_WIDTH_NV 0x9038 +#define GL_VIDEO_CAPTURE_FRAME_HEIGHT_NV 0x9039 +#define GL_VIDEO_CAPTURE_FIELD_UPPER_HEIGHT_NV 0x903A +#define GL_VIDEO_CAPTURE_FIELD_LOWER_HEIGHT_NV 0x903B +#define GL_VIDEO_CAPTURE_SURFACE_ORIGIN_NV 0x903C +#endif + +#ifndef GL_NV_copy_image +#endif + +#ifndef GL_EXT_separate_shader_objects +#define GL_ACTIVE_PROGRAM_EXT 0x8B8D +#endif + +#ifndef GL_NV_parameter_buffer_object2 +#endif + +#ifndef GL_NV_shader_buffer_load +#define GL_BUFFER_GPU_ADDRESS_NV 0x8F1D +#define GL_GPU_ADDRESS_NV 0x8F34 +#define GL_MAX_SHADER_BUFFER_ADDRESS_NV 0x8F35 +#endif + +#ifndef GL_NV_vertex_buffer_unified_memory +#define GL_VERTEX_ATTRIB_ARRAY_UNIFIED_NV 0x8F1E +#define GL_ELEMENT_ARRAY_UNIFIED_NV 0x8F1F +#define GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV 0x8F20 +#define GL_VERTEX_ARRAY_ADDRESS_NV 0x8F21 +#define GL_NORMAL_ARRAY_ADDRESS_NV 0x8F22 +#define GL_COLOR_ARRAY_ADDRESS_NV 0x8F23 +#define GL_INDEX_ARRAY_ADDRESS_NV 0x8F24 +#define GL_TEXTURE_COORD_ARRAY_ADDRESS_NV 0x8F25 +#define GL_EDGE_FLAG_ARRAY_ADDRESS_NV 0x8F26 +#define GL_SECONDARY_COLOR_ARRAY_ADDRESS_NV 0x8F27 +#define GL_FOG_COORD_ARRAY_ADDRESS_NV 0x8F28 +#define GL_ELEMENT_ARRAY_ADDRESS_NV 0x8F29 +#define GL_VERTEX_ATTRIB_ARRAY_LENGTH_NV 0x8F2A +#define GL_VERTEX_ARRAY_LENGTH_NV 0x8F2B +#define GL_NORMAL_ARRAY_LENGTH_NV 0x8F2C +#define GL_COLOR_ARRAY_LENGTH_NV 0x8F2D +#define GL_INDEX_ARRAY_LENGTH_NV 0x8F2E +#define GL_TEXTURE_COORD_ARRAY_LENGTH_NV 0x8F2F +#define GL_EDGE_FLAG_ARRAY_LENGTH_NV 0x8F30 +#define GL_SECONDARY_COLOR_ARRAY_LENGTH_NV 0x8F31 +#define GL_FOG_COORD_ARRAY_LENGTH_NV 0x8F32 +#define GL_ELEMENT_ARRAY_LENGTH_NV 0x8F33 +#define GL_DRAW_INDIRECT_UNIFIED_NV 0x8F40 +#define GL_DRAW_INDIRECT_ADDRESS_NV 0x8F41 +#define GL_DRAW_INDIRECT_LENGTH_NV 0x8F42 +#endif + +#ifndef GL_NV_texture_barrier +#endif + +#ifndef GL_AMD_shader_stencil_export +#endif + +#ifndef GL_AMD_seamless_cubemap_per_texture +/* reuse GL_TEXTURE_CUBE_MAP_SEAMLESS_ARB */ +#endif + +#ifndef GL_AMD_conservative_depth +#endif + +#ifndef GL_EXT_shader_image_load_store +#define GL_MAX_IMAGE_UNITS_EXT 0x8F38 +#define GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS_EXT 0x8F39 +#define GL_IMAGE_BINDING_NAME_EXT 0x8F3A +#define GL_IMAGE_BINDING_LEVEL_EXT 0x8F3B +#define GL_IMAGE_BINDING_LAYERED_EXT 0x8F3C +#define GL_IMAGE_BINDING_LAYER_EXT 0x8F3D +#define GL_IMAGE_BINDING_ACCESS_EXT 0x8F3E +#define GL_IMAGE_1D_EXT 0x904C +#define GL_IMAGE_2D_EXT 0x904D +#define GL_IMAGE_3D_EXT 0x904E +#define GL_IMAGE_2D_RECT_EXT 0x904F +#define GL_IMAGE_CUBE_EXT 0x9050 +#define GL_IMAGE_BUFFER_EXT 0x9051 +#define GL_IMAGE_1D_ARRAY_EXT 0x9052 +#define GL_IMAGE_2D_ARRAY_EXT 0x9053 +#define GL_IMAGE_CUBE_MAP_ARRAY_EXT 0x9054 +#define GL_IMAGE_2D_MULTISAMPLE_EXT 0x9055 +#define GL_IMAGE_2D_MULTISAMPLE_ARRAY_EXT 0x9056 +#define GL_INT_IMAGE_1D_EXT 0x9057 +#define GL_INT_IMAGE_2D_EXT 0x9058 +#define GL_INT_IMAGE_3D_EXT 0x9059 +#define GL_INT_IMAGE_2D_RECT_EXT 0x905A +#define GL_INT_IMAGE_CUBE_EXT 0x905B +#define GL_INT_IMAGE_BUFFER_EXT 0x905C +#define GL_INT_IMAGE_1D_ARRAY_EXT 0x905D +#define GL_INT_IMAGE_2D_ARRAY_EXT 0x905E +#define GL_INT_IMAGE_CUBE_MAP_ARRAY_EXT 0x905F +#define GL_INT_IMAGE_2D_MULTISAMPLE_EXT 0x9060 +#define GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY_EXT 0x9061 +#define GL_UNSIGNED_INT_IMAGE_1D_EXT 0x9062 +#define GL_UNSIGNED_INT_IMAGE_2D_EXT 0x9063 +#define GL_UNSIGNED_INT_IMAGE_3D_EXT 0x9064 +#define GL_UNSIGNED_INT_IMAGE_2D_RECT_EXT 0x9065 +#define GL_UNSIGNED_INT_IMAGE_CUBE_EXT 0x9066 +#define GL_UNSIGNED_INT_IMAGE_BUFFER_EXT 0x9067 +#define GL_UNSIGNED_INT_IMAGE_1D_ARRAY_EXT 0x9068 +#define GL_UNSIGNED_INT_IMAGE_2D_ARRAY_EXT 0x9069 +#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY_EXT 0x906A +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_EXT 0x906B +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY_EXT 0x906C +#define GL_MAX_IMAGE_SAMPLES_EXT 0x906D +#define GL_IMAGE_BINDING_FORMAT_EXT 0x906E +#define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT_EXT 0x00000001 +#define GL_ELEMENT_ARRAY_BARRIER_BIT_EXT 0x00000002 +#define GL_UNIFORM_BARRIER_BIT_EXT 0x00000004 +#define GL_TEXTURE_FETCH_BARRIER_BIT_EXT 0x00000008 +#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT_EXT 0x00000020 +#define GL_COMMAND_BARRIER_BIT_EXT 0x00000040 +#define GL_PIXEL_BUFFER_BARRIER_BIT_EXT 0x00000080 +#define GL_TEXTURE_UPDATE_BARRIER_BIT_EXT 0x00000100 +#define GL_BUFFER_UPDATE_BARRIER_BIT_EXT 0x00000200 +#define GL_FRAMEBUFFER_BARRIER_BIT_EXT 0x00000400 +#define GL_TRANSFORM_FEEDBACK_BARRIER_BIT_EXT 0x00000800 +#define GL_ATOMIC_COUNTER_BARRIER_BIT_EXT 0x00001000 +#define GL_ALL_BARRIER_BITS_EXT 0xFFFFFFFF +#endif + +#ifndef GL_EXT_vertex_attrib_64bit +/* reuse GL_DOUBLE */ +#define GL_DOUBLE_VEC2_EXT 0x8FFC +#define GL_DOUBLE_VEC3_EXT 0x8FFD +#define GL_DOUBLE_VEC4_EXT 0x8FFE +#define GL_DOUBLE_MAT2_EXT 0x8F46 +#define GL_DOUBLE_MAT3_EXT 0x8F47 +#define GL_DOUBLE_MAT4_EXT 0x8F48 +#define GL_DOUBLE_MAT2x3_EXT 0x8F49 +#define GL_DOUBLE_MAT2x4_EXT 0x8F4A +#define GL_DOUBLE_MAT3x2_EXT 0x8F4B +#define GL_DOUBLE_MAT3x4_EXT 0x8F4C +#define GL_DOUBLE_MAT4x2_EXT 0x8F4D +#define GL_DOUBLE_MAT4x3_EXT 0x8F4E +#endif + +#ifndef GL_NV_gpu_program5 +#define GL_MAX_GEOMETRY_PROGRAM_INVOCATIONS_NV 0x8E5A +#define GL_MIN_FRAGMENT_INTERPOLATION_OFFSET_NV 0x8E5B +#define GL_MAX_FRAGMENT_INTERPOLATION_OFFSET_NV 0x8E5C +#define GL_FRAGMENT_PROGRAM_INTERPOLATION_OFFSET_BITS_NV 0x8E5D +#define GL_MAX_PROGRAM_SUBROUTINE_PARAMETERS_NV 0x8F44 +#define GL_MAX_PROGRAM_SUBROUTINE_NUM_NV 0x8F45 +#endif + +#ifndef GL_NV_gpu_shader5 +#define GL_INT64_NV 0x140E +#define GL_UNSIGNED_INT64_NV 0x140F +#define GL_INT8_NV 0x8FE0 +#define GL_INT8_VEC2_NV 0x8FE1 +#define GL_INT8_VEC3_NV 0x8FE2 +#define GL_INT8_VEC4_NV 0x8FE3 +#define GL_INT16_NV 0x8FE4 +#define GL_INT16_VEC2_NV 0x8FE5 +#define GL_INT16_VEC3_NV 0x8FE6 +#define GL_INT16_VEC4_NV 0x8FE7 +#define GL_INT64_VEC2_NV 0x8FE9 +#define GL_INT64_VEC3_NV 0x8FEA +#define GL_INT64_VEC4_NV 0x8FEB +#define GL_UNSIGNED_INT8_NV 0x8FEC +#define GL_UNSIGNED_INT8_VEC2_NV 0x8FED +#define GL_UNSIGNED_INT8_VEC3_NV 0x8FEE +#define GL_UNSIGNED_INT8_VEC4_NV 0x8FEF +#define GL_UNSIGNED_INT16_NV 0x8FF0 +#define GL_UNSIGNED_INT16_VEC2_NV 0x8FF1 +#define GL_UNSIGNED_INT16_VEC3_NV 0x8FF2 +#define GL_UNSIGNED_INT16_VEC4_NV 0x8FF3 +#define GL_UNSIGNED_INT64_VEC2_NV 0x8FF5 +#define GL_UNSIGNED_INT64_VEC3_NV 0x8FF6 +#define GL_UNSIGNED_INT64_VEC4_NV 0x8FF7 +#define GL_FLOAT16_NV 0x8FF8 +#define GL_FLOAT16_VEC2_NV 0x8FF9 +#define GL_FLOAT16_VEC3_NV 0x8FFA +#define GL_FLOAT16_VEC4_NV 0x8FFB +/* reuse GL_PATCHES */ +#endif + +#ifndef GL_NV_shader_buffer_store +#define GL_SHADER_GLOBAL_ACCESS_BARRIER_BIT_NV 0x00000010 +/* reuse GL_READ_WRITE */ +/* reuse GL_WRITE_ONLY */ +#endif + +#ifndef GL_NV_tessellation_program5 +#define GL_MAX_PROGRAM_PATCH_ATTRIBS_NV 0x86D8 +#define GL_TESS_CONTROL_PROGRAM_NV 0x891E +#define GL_TESS_EVALUATION_PROGRAM_NV 0x891F +#define GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV 0x8C74 +#define GL_TESS_EVALUATION_PROGRAM_PARAMETER_BUFFER_NV 0x8C75 +#endif + +#ifndef GL_NV_vertex_attrib_integer_64bit +/* reuse GL_INT64_NV */ +/* reuse GL_UNSIGNED_INT64_NV */ +#endif + +#ifndef GL_NV_multisample_coverage +#define GL_COVERAGE_SAMPLES_NV 0x80A9 +#define GL_COLOR_SAMPLES_NV 0x8E20 +#endif + +#ifndef GL_AMD_name_gen_delete +#define GL_DATA_BUFFER_AMD 0x9151 +#define GL_PERFORMANCE_MONITOR_AMD 0x9152 +#define GL_QUERY_OBJECT_AMD 0x9153 +#define GL_VERTEX_ARRAY_OBJECT_AMD 0x9154 +#define GL_SAMPLER_OBJECT_AMD 0x9155 +#endif + +#ifndef GL_AMD_debug_output +#define GL_MAX_DEBUG_LOGGED_MESSAGES_AMD 0x9144 +#define GL_DEBUG_LOGGED_MESSAGES_AMD 0x9145 +#define GL_DEBUG_SEVERITY_HIGH_AMD 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM_AMD 0x9147 +#define GL_DEBUG_SEVERITY_LOW_AMD 0x9148 +#define GL_DEBUG_CATEGORY_API_ERROR_AMD 0x9149 +#define GL_DEBUG_CATEGORY_WINDOW_SYSTEM_AMD 0x914A +#define GL_DEBUG_CATEGORY_DEPRECATION_AMD 0x914B +#define GL_DEBUG_CATEGORY_UNDEFINED_BEHAVIOR_AMD 0x914C +#define GL_DEBUG_CATEGORY_PERFORMANCE_AMD 0x914D +#define GL_DEBUG_CATEGORY_SHADER_COMPILER_AMD 0x914E +#define GL_DEBUG_CATEGORY_APPLICATION_AMD 0x914F +#define GL_DEBUG_CATEGORY_OTHER_AMD 0x9150 +#endif + +#ifndef GL_NV_vdpau_interop +#define GL_SURFACE_STATE_NV 0x86EB +#define GL_SURFACE_REGISTERED_NV 0x86FD +#define GL_SURFACE_MAPPED_NV 0x8700 +#define GL_WRITE_DISCARD_NV 0x88BE +#endif + +#ifndef GL_AMD_transform_feedback3_lines_triangles +#endif + + +/*************************************************************/ + +#include +#ifndef GL_VERSION_2_0 +/* GL type for program/shader text */ +typedef char GLchar; +#endif + +#ifndef GL_VERSION_1_5 +/* GL types for handling large vertex buffer objects */ +typedef ptrdiff_t GLintptr; +typedef ptrdiff_t GLsizeiptr; +#endif + +#ifndef GL_ARB_vertex_buffer_object +/* GL types for handling large vertex buffer objects */ +typedef ptrdiff_t GLintptrARB; +typedef ptrdiff_t GLsizeiptrARB; +#endif + +#ifndef GL_ARB_shader_objects +/* GL types for program/shader text and shader object handles */ +typedef char GLcharARB; +typedef unsigned int GLhandleARB; +#endif + +/* GL type for "half" precision (s10e5) float data in host memory */ +#ifndef GL_ARB_half_float_pixel +typedef unsigned short GLhalfARB; +#endif + +#ifndef GL_NV_half_float +typedef unsigned short GLhalfNV; +#endif + +#ifndef GLEXT_64_TYPES_DEFINED +/* This code block is duplicated in glxext.h, so must be protected */ +#define GLEXT_64_TYPES_DEFINED +/* Define int32_t, int64_t, and uint64_t types for UST/MSC */ +/* (as used in the GL_EXT_timer_query extension). */ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#include +#elif defined(__sun__) || defined(__digital__) +#include +#if defined(__STDC__) +#if defined(__arch64__) || defined(_LP64) +typedef long int int64_t; +typedef unsigned long int uint64_t; +#else +typedef long long int int64_t; +typedef unsigned long long int uint64_t; +#endif /* __arch64__ */ +#endif /* __STDC__ */ +#elif defined( __VMS ) || defined(__sgi) +#include +#elif defined(__SCO__) || defined(__USLC__) +#include +#elif defined(__UNIXOS2__) || defined(__SOL64__) +typedef long int int32_t; +typedef long long int int64_t; +typedef unsigned long long int uint64_t; +#elif defined(_WIN32) && defined(__GNUC__) +#include +#elif defined(_WIN32) +typedef __int32 int32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#else +/* Fallback if nothing above works */ +#include +#endif +#endif + +#ifndef GL_EXT_timer_query +typedef int64_t GLint64EXT; +typedef uint64_t GLuint64EXT; +#endif + +#ifndef GL_ARB_sync +typedef int64_t GLint64; +typedef uint64_t GLuint64; +typedef struct __GLsync *GLsync; +#endif + +#ifndef GL_ARB_cl_event +/* These incomplete types let us declare types compatible with OpenCL's cl_context and cl_event */ +struct _cl_context; +struct _cl_event; +#endif + +#ifndef GL_ARB_debug_output +typedef void (APIENTRY *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,GLvoid *userParam); +#endif + +#ifndef GL_AMD_debug_output +typedef void (APIENTRY *GLDEBUGPROCAMD)(GLuint id,GLenum category,GLenum severity,GLsizei length,const GLchar *message,GLvoid *userParam); +#endif + +#ifndef GL_NV_vdpau_interop +typedef GLintptr GLvdpauSurfaceNV; +#endif + +#ifndef GL_VERSION_1_2 +#define GL_VERSION_1_2 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendColor (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); +GLAPI void APIENTRY glBlendEquation (GLenum mode); +GLAPI void APIENTRY glDrawRangeElements (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices); +GLAPI void APIENTRY glTexImage3D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +GLAPI void APIENTRY glTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels); +GLAPI void APIENTRY glCopyTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBLENDCOLORPROC) (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); +typedef void (APIENTRYP PFNGLBLENDEQUATIONPROC) (GLenum mode); +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices); +typedef void (APIENTRYP PFNGLTEXIMAGE3DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels); +typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +#endif + +#ifndef GL_VERSION_1_2_DEPRECATED +#define GL_VERSION_1_2_DEPRECATED 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColorTable (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const GLvoid *table); +GLAPI void APIENTRY glColorTableParameterfv (GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glColorTableParameteriv (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glCopyColorTable (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glGetColorTable (GLenum target, GLenum format, GLenum type, GLvoid *table); +GLAPI void APIENTRY glGetColorTableParameterfv (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetColorTableParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glColorSubTable (GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const GLvoid *data); +GLAPI void APIENTRY glCopyColorSubTable (GLenum target, GLsizei start, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glConvolutionFilter1D (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const GLvoid *image); +GLAPI void APIENTRY glConvolutionFilter2D (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *image); +GLAPI void APIENTRY glConvolutionParameterf (GLenum target, GLenum pname, GLfloat params); +GLAPI void APIENTRY glConvolutionParameterfv (GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glConvolutionParameteri (GLenum target, GLenum pname, GLint params); +GLAPI void APIENTRY glConvolutionParameteriv (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glCopyConvolutionFilter1D (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glCopyConvolutionFilter2D (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetConvolutionFilter (GLenum target, GLenum format, GLenum type, GLvoid *image); +GLAPI void APIENTRY glGetConvolutionParameterfv (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetConvolutionParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetSeparableFilter (GLenum target, GLenum format, GLenum type, GLvoid *row, GLvoid *column, GLvoid *span); +GLAPI void APIENTRY glSeparableFilter2D (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *row, const GLvoid *column); +GLAPI void APIENTRY glGetHistogram (GLenum target, GLboolean reset, GLenum format, GLenum type, GLvoid *values); +GLAPI void APIENTRY glGetHistogramParameterfv (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetHistogramParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetMinmax (GLenum target, GLboolean reset, GLenum format, GLenum type, GLvoid *values); +GLAPI void APIENTRY glGetMinmaxParameterfv (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetMinmaxParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glHistogram (GLenum target, GLsizei width, GLenum internalformat, GLboolean sink); +GLAPI void APIENTRY glMinmax (GLenum target, GLenum internalformat, GLboolean sink); +GLAPI void APIENTRY glResetHistogram (GLenum target); +GLAPI void APIENTRY glResetMinmax (GLenum target); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLCOLORTABLEPROC) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const GLvoid *table); +typedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERFVPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERIVPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLCOPYCOLORTABLEPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPROC) (GLenum target, GLenum format, GLenum type, GLvoid *table); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLCOLORSUBTABLEPROC) (GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const GLvoid *data); +typedef void (APIENTRYP PFNGLCOPYCOLORSUBTABLEPROC) (GLenum target, GLsizei start, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCONVOLUTIONFILTER1DPROC) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const GLvoid *image); +typedef void (APIENTRYP PFNGLCONVOLUTIONFILTER2DPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *image); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFPROC) (GLenum target, GLenum pname, GLfloat params); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFVPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIPROC) (GLenum target, GLenum pname, GLint params); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIVPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER1DPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER2DPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONFILTERPROC) (GLenum target, GLenum format, GLenum type, GLvoid *image); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETSEPARABLEFILTERPROC) (GLenum target, GLenum format, GLenum type, GLvoid *row, GLvoid *column, GLvoid *span); +typedef void (APIENTRYP PFNGLSEPARABLEFILTER2DPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *row, const GLvoid *column); +typedef void (APIENTRYP PFNGLGETHISTOGRAMPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, GLvoid *values); +typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETMINMAXPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, GLvoid *values); +typedef void (APIENTRYP PFNGLGETMINMAXPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETMINMAXPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLHISTOGRAMPROC) (GLenum target, GLsizei width, GLenum internalformat, GLboolean sink); +typedef void (APIENTRYP PFNGLMINMAXPROC) (GLenum target, GLenum internalformat, GLboolean sink); +typedef void (APIENTRYP PFNGLRESETHISTOGRAMPROC) (GLenum target); +typedef void (APIENTRYP PFNGLRESETMINMAXPROC) (GLenum target); +#endif + +#ifndef GL_VERSION_1_3 +#define GL_VERSION_1_3 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glActiveTexture (GLenum texture); +GLAPI void APIENTRY glSampleCoverage (GLclampf value, GLboolean invert); +GLAPI void APIENTRY glCompressedTexImage3D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid *data); +GLAPI void APIENTRY glCompressedTexImage2D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *data); +GLAPI void APIENTRY glCompressedTexImage1D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const GLvoid *data); +GLAPI void APIENTRY glCompressedTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid *data); +GLAPI void APIENTRY glCompressedTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *data); +GLAPI void APIENTRY glCompressedTexSubImage1D (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const GLvoid *data); +GLAPI void APIENTRY glGetCompressedTexImage (GLenum target, GLint level, GLvoid *img); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLACTIVETEXTUREPROC) (GLenum texture); +typedef void (APIENTRYP PFNGLSAMPLECOVERAGEPROC) (GLclampf value, GLboolean invert); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE1DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const GLvoid *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const GLvoid *data); +typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXIMAGEPROC) (GLenum target, GLint level, GLvoid *img); +#endif + +#ifndef GL_VERSION_1_3_DEPRECATED +#define GL_VERSION_1_3_DEPRECATED 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glClientActiveTexture (GLenum texture); +GLAPI void APIENTRY glMultiTexCoord1d (GLenum target, GLdouble s); +GLAPI void APIENTRY glMultiTexCoord1dv (GLenum target, const GLdouble *v); +GLAPI void APIENTRY glMultiTexCoord1f (GLenum target, GLfloat s); +GLAPI void APIENTRY glMultiTexCoord1fv (GLenum target, const GLfloat *v); +GLAPI void APIENTRY glMultiTexCoord1i (GLenum target, GLint s); +GLAPI void APIENTRY glMultiTexCoord1iv (GLenum target, const GLint *v); +GLAPI void APIENTRY glMultiTexCoord1s (GLenum target, GLshort s); +GLAPI void APIENTRY glMultiTexCoord1sv (GLenum target, const GLshort *v); +GLAPI void APIENTRY glMultiTexCoord2d (GLenum target, GLdouble s, GLdouble t); +GLAPI void APIENTRY glMultiTexCoord2dv (GLenum target, const GLdouble *v); +GLAPI void APIENTRY glMultiTexCoord2f (GLenum target, GLfloat s, GLfloat t); +GLAPI void APIENTRY glMultiTexCoord2fv (GLenum target, const GLfloat *v); +GLAPI void APIENTRY glMultiTexCoord2i (GLenum target, GLint s, GLint t); +GLAPI void APIENTRY glMultiTexCoord2iv (GLenum target, const GLint *v); +GLAPI void APIENTRY glMultiTexCoord2s (GLenum target, GLshort s, GLshort t); +GLAPI void APIENTRY glMultiTexCoord2sv (GLenum target, const GLshort *v); +GLAPI void APIENTRY glMultiTexCoord3d (GLenum target, GLdouble s, GLdouble t, GLdouble r); +GLAPI void APIENTRY glMultiTexCoord3dv (GLenum target, const GLdouble *v); +GLAPI void APIENTRY glMultiTexCoord3f (GLenum target, GLfloat s, GLfloat t, GLfloat r); +GLAPI void APIENTRY glMultiTexCoord3fv (GLenum target, const GLfloat *v); +GLAPI void APIENTRY glMultiTexCoord3i (GLenum target, GLint s, GLint t, GLint r); +GLAPI void APIENTRY glMultiTexCoord3iv (GLenum target, const GLint *v); +GLAPI void APIENTRY glMultiTexCoord3s (GLenum target, GLshort s, GLshort t, GLshort r); +GLAPI void APIENTRY glMultiTexCoord3sv (GLenum target, const GLshort *v); +GLAPI void APIENTRY glMultiTexCoord4d (GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); +GLAPI void APIENTRY glMultiTexCoord4dv (GLenum target, const GLdouble *v); +GLAPI void APIENTRY glMultiTexCoord4f (GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); +GLAPI void APIENTRY glMultiTexCoord4fv (GLenum target, const GLfloat *v); +GLAPI void APIENTRY glMultiTexCoord4i (GLenum target, GLint s, GLint t, GLint r, GLint q); +GLAPI void APIENTRY glMultiTexCoord4iv (GLenum target, const GLint *v); +GLAPI void APIENTRY glMultiTexCoord4s (GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); +GLAPI void APIENTRY glMultiTexCoord4sv (GLenum target, const GLshort *v); +GLAPI void APIENTRY glLoadTransposeMatrixf (const GLfloat *m); +GLAPI void APIENTRY glLoadTransposeMatrixd (const GLdouble *m); +GLAPI void APIENTRY glMultTransposeMatrixf (const GLfloat *m); +GLAPI void APIENTRY glMultTransposeMatrixd (const GLdouble *m); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLCLIENTACTIVETEXTUREPROC) (GLenum texture); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1DPROC) (GLenum target, GLdouble s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1DVPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1FPROC) (GLenum target, GLfloat s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1FVPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1IPROC) (GLenum target, GLint s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1IVPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1SPROC) (GLenum target, GLshort s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1SVPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2DPROC) (GLenum target, GLdouble s, GLdouble t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2DVPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2FPROC) (GLenum target, GLfloat s, GLfloat t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2FVPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2IPROC) (GLenum target, GLint s, GLint t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2IVPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2SPROC) (GLenum target, GLshort s, GLshort t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2SVPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3DPROC) (GLenum target, GLdouble s, GLdouble t, GLdouble r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3DVPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3FPROC) (GLenum target, GLfloat s, GLfloat t, GLfloat r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3FVPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3IPROC) (GLenum target, GLint s, GLint t, GLint r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3IVPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3SPROC) (GLenum target, GLshort s, GLshort t, GLshort r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3SVPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4DPROC) (GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4DVPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4FPROC) (GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4FVPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4IPROC) (GLenum target, GLint s, GLint t, GLint r, GLint q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4IVPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4SPROC) (GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4SVPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXFPROC) (const GLfloat *m); +typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXDPROC) (const GLdouble *m); +typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXFPROC) (const GLfloat *m); +typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXDPROC) (const GLdouble *m); +#endif + +#ifndef GL_VERSION_1_4 +#define GL_VERSION_1_4 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendFuncSeparate (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +GLAPI void APIENTRY glMultiDrawArrays (GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount); +GLAPI void APIENTRY glMultiDrawElements (GLenum mode, const GLsizei *count, GLenum type, const GLvoid* *indices, GLsizei primcount); +GLAPI void APIENTRY glPointParameterf (GLenum pname, GLfloat param); +GLAPI void APIENTRY glPointParameterfv (GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glPointParameteri (GLenum pname, GLint param); +GLAPI void APIENTRY glPointParameteriv (GLenum pname, const GLint *params); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSPROC) (GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSPROC) (GLenum mode, const GLsizei *count, GLenum type, const GLvoid* *indices, GLsizei primcount); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFVPROC) (GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLPOINTPARAMETERIPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERIVPROC) (GLenum pname, const GLint *params); +#endif + +#ifndef GL_VERSION_1_4_DEPRECATED +#define GL_VERSION_1_4_DEPRECATED 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFogCoordf (GLfloat coord); +GLAPI void APIENTRY glFogCoordfv (const GLfloat *coord); +GLAPI void APIENTRY glFogCoordd (GLdouble coord); +GLAPI void APIENTRY glFogCoorddv (const GLdouble *coord); +GLAPI void APIENTRY glFogCoordPointer (GLenum type, GLsizei stride, const GLvoid *pointer); +GLAPI void APIENTRY glSecondaryColor3b (GLbyte red, GLbyte green, GLbyte blue); +GLAPI void APIENTRY glSecondaryColor3bv (const GLbyte *v); +GLAPI void APIENTRY glSecondaryColor3d (GLdouble red, GLdouble green, GLdouble blue); +GLAPI void APIENTRY glSecondaryColor3dv (const GLdouble *v); +GLAPI void APIENTRY glSecondaryColor3f (GLfloat red, GLfloat green, GLfloat blue); +GLAPI void APIENTRY glSecondaryColor3fv (const GLfloat *v); +GLAPI void APIENTRY glSecondaryColor3i (GLint red, GLint green, GLint blue); +GLAPI void APIENTRY glSecondaryColor3iv (const GLint *v); +GLAPI void APIENTRY glSecondaryColor3s (GLshort red, GLshort green, GLshort blue); +GLAPI void APIENTRY glSecondaryColor3sv (const GLshort *v); +GLAPI void APIENTRY glSecondaryColor3ub (GLubyte red, GLubyte green, GLubyte blue); +GLAPI void APIENTRY glSecondaryColor3ubv (const GLubyte *v); +GLAPI void APIENTRY glSecondaryColor3ui (GLuint red, GLuint green, GLuint blue); +GLAPI void APIENTRY glSecondaryColor3uiv (const GLuint *v); +GLAPI void APIENTRY glSecondaryColor3us (GLushort red, GLushort green, GLushort blue); +GLAPI void APIENTRY glSecondaryColor3usv (const GLushort *v); +GLAPI void APIENTRY glSecondaryColorPointer (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +GLAPI void APIENTRY glWindowPos2d (GLdouble x, GLdouble y); +GLAPI void APIENTRY glWindowPos2dv (const GLdouble *v); +GLAPI void APIENTRY glWindowPos2f (GLfloat x, GLfloat y); +GLAPI void APIENTRY glWindowPos2fv (const GLfloat *v); +GLAPI void APIENTRY glWindowPos2i (GLint x, GLint y); +GLAPI void APIENTRY glWindowPos2iv (const GLint *v); +GLAPI void APIENTRY glWindowPos2s (GLshort x, GLshort y); +GLAPI void APIENTRY glWindowPos2sv (const GLshort *v); +GLAPI void APIENTRY glWindowPos3d (GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glWindowPos3dv (const GLdouble *v); +GLAPI void APIENTRY glWindowPos3f (GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glWindowPos3fv (const GLfloat *v); +GLAPI void APIENTRY glWindowPos3i (GLint x, GLint y, GLint z); +GLAPI void APIENTRY glWindowPos3iv (const GLint *v); +GLAPI void APIENTRY glWindowPos3s (GLshort x, GLshort y, GLshort z); +GLAPI void APIENTRY glWindowPos3sv (const GLshort *v); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLFOGCOORDFPROC) (GLfloat coord); +typedef void (APIENTRYP PFNGLFOGCOORDFVPROC) (const GLfloat *coord); +typedef void (APIENTRYP PFNGLFOGCOORDDPROC) (GLdouble coord); +typedef void (APIENTRYP PFNGLFOGCOORDDVPROC) (const GLdouble *coord); +typedef void (APIENTRYP PFNGLFOGCOORDPOINTERPROC) (GLenum type, GLsizei stride, const GLvoid *pointer); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BPROC) (GLbyte red, GLbyte green, GLbyte blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BVPROC) (const GLbyte *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DPROC) (GLdouble red, GLdouble green, GLdouble blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DVPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FPROC) (GLfloat red, GLfloat green, GLfloat blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FVPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IPROC) (GLint red, GLint green, GLint blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IVPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SPROC) (GLshort red, GLshort green, GLshort blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SVPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBPROC) (GLubyte red, GLubyte green, GLubyte blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBVPROC) (const GLubyte *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIPROC) (GLuint red, GLuint green, GLuint blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIVPROC) (const GLuint *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USPROC) (GLushort red, GLushort green, GLushort blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USVPROC) (const GLushort *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLORPOINTERPROC) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +typedef void (APIENTRYP PFNGLWINDOWPOS2DPROC) (GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLWINDOWPOS2DVPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2FPROC) (GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLWINDOWPOS2FVPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2IPROC) (GLint x, GLint y); +typedef void (APIENTRYP PFNGLWINDOWPOS2IVPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2SPROC) (GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLWINDOWPOS2SVPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3DPROC) (GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLWINDOWPOS3DVPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3FPROC) (GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLWINDOWPOS3FVPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3IPROC) (GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLWINDOWPOS3IVPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3SPROC) (GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLWINDOWPOS3SVPROC) (const GLshort *v); +#endif + +#ifndef GL_VERSION_1_5 +#define GL_VERSION_1_5 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGenQueries (GLsizei n, GLuint *ids); +GLAPI void APIENTRY glDeleteQueries (GLsizei n, const GLuint *ids); +GLAPI GLboolean APIENTRY glIsQuery (GLuint id); +GLAPI void APIENTRY glBeginQuery (GLenum target, GLuint id); +GLAPI void APIENTRY glEndQuery (GLenum target); +GLAPI void APIENTRY glGetQueryiv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetQueryObjectiv (GLuint id, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetQueryObjectuiv (GLuint id, GLenum pname, GLuint *params); +GLAPI void APIENTRY glBindBuffer (GLenum target, GLuint buffer); +GLAPI void APIENTRY glDeleteBuffers (GLsizei n, const GLuint *buffers); +GLAPI void APIENTRY glGenBuffers (GLsizei n, GLuint *buffers); +GLAPI GLboolean APIENTRY glIsBuffer (GLuint buffer); +GLAPI void APIENTRY glBufferData (GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage); +GLAPI void APIENTRY glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid *data); +GLAPI void APIENTRY glGetBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, GLvoid *data); +GLAPI GLvoid* APIENTRY glMapBuffer (GLenum target, GLenum access); +GLAPI GLboolean APIENTRY glUnmapBuffer (GLenum target); +GLAPI void APIENTRY glGetBufferParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetBufferPointerv (GLenum target, GLenum pname, GLvoid* *params); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLGENQUERIESPROC) (GLsizei n, GLuint *ids); +typedef void (APIENTRYP PFNGLDELETEQUERIESPROC) (GLsizei n, const GLuint *ids); +typedef GLboolean (APIENTRYP PFNGLISQUERYPROC) (GLuint id); +typedef void (APIENTRYP PFNGLBEGINQUERYPROC) (GLenum target, GLuint id); +typedef void (APIENTRYP PFNGLENDQUERYPROC) (GLenum target); +typedef void (APIENTRYP PFNGLGETQUERYIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTIVPROC) (GLuint id, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTUIVPROC) (GLuint id, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLBINDBUFFERPROC) (GLenum target, GLuint buffer); +typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC) (GLsizei n, const GLuint *buffers); +typedef void (APIENTRYP PFNGLGENBUFFERSPROC) (GLsizei n, GLuint *buffers); +typedef GLboolean (APIENTRYP PFNGLISBUFFERPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLBUFFERDATAPROC) (GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage); +typedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid *data); +typedef void (APIENTRYP PFNGLGETBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, GLvoid *data); +typedef GLvoid* (APIENTRYP PFNGLMAPBUFFERPROC) (GLenum target, GLenum access); +typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERPROC) (GLenum target); +typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETBUFFERPOINTERVPROC) (GLenum target, GLenum pname, GLvoid* *params); +#endif + +#ifndef GL_VERSION_2_0 +#define GL_VERSION_2_0 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendEquationSeparate (GLenum modeRGB, GLenum modeAlpha); +GLAPI void APIENTRY glDrawBuffers (GLsizei n, const GLenum *bufs); +GLAPI void APIENTRY glStencilOpSeparate (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); +GLAPI void APIENTRY glStencilFuncSeparate (GLenum face, GLenum func, GLint ref, GLuint mask); +GLAPI void APIENTRY glStencilMaskSeparate (GLenum face, GLuint mask); +GLAPI void APIENTRY glAttachShader (GLuint program, GLuint shader); +GLAPI void APIENTRY glBindAttribLocation (GLuint program, GLuint index, const GLchar *name); +GLAPI void APIENTRY glCompileShader (GLuint shader); +GLAPI GLuint APIENTRY glCreateProgram (void); +GLAPI GLuint APIENTRY glCreateShader (GLenum type); +GLAPI void APIENTRY glDeleteProgram (GLuint program); +GLAPI void APIENTRY glDeleteShader (GLuint shader); +GLAPI void APIENTRY glDetachShader (GLuint program, GLuint shader); +GLAPI void APIENTRY glDisableVertexAttribArray (GLuint index); +GLAPI void APIENTRY glEnableVertexAttribArray (GLuint index); +GLAPI void APIENTRY glGetActiveAttrib (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); +GLAPI void APIENTRY glGetActiveUniform (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); +GLAPI void APIENTRY glGetAttachedShaders (GLuint program, GLsizei maxCount, GLsizei *count, GLuint *obj); +GLAPI GLint APIENTRY glGetAttribLocation (GLuint program, const GLchar *name); +GLAPI void APIENTRY glGetProgramiv (GLuint program, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetProgramInfoLog (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +GLAPI void APIENTRY glGetShaderiv (GLuint shader, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetShaderInfoLog (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +GLAPI void APIENTRY glGetShaderSource (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source); +GLAPI GLint APIENTRY glGetUniformLocation (GLuint program, const GLchar *name); +GLAPI void APIENTRY glGetUniformfv (GLuint program, GLint location, GLfloat *params); +GLAPI void APIENTRY glGetUniformiv (GLuint program, GLint location, GLint *params); +GLAPI void APIENTRY glGetVertexAttribdv (GLuint index, GLenum pname, GLdouble *params); +GLAPI void APIENTRY glGetVertexAttribfv (GLuint index, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetVertexAttribiv (GLuint index, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVertexAttribPointerv (GLuint index, GLenum pname, GLvoid* *pointer); +GLAPI GLboolean APIENTRY glIsProgram (GLuint program); +GLAPI GLboolean APIENTRY glIsShader (GLuint shader); +GLAPI void APIENTRY glLinkProgram (GLuint program); +GLAPI void APIENTRY glShaderSource (GLuint shader, GLsizei count, const GLchar* *string, const GLint *length); +GLAPI void APIENTRY glUseProgram (GLuint program); +GLAPI void APIENTRY glUniform1f (GLint location, GLfloat v0); +GLAPI void APIENTRY glUniform2f (GLint location, GLfloat v0, GLfloat v1); +GLAPI void APIENTRY glUniform3f (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +GLAPI void APIENTRY glUniform4f (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +GLAPI void APIENTRY glUniform1i (GLint location, GLint v0); +GLAPI void APIENTRY glUniform2i (GLint location, GLint v0, GLint v1); +GLAPI void APIENTRY glUniform3i (GLint location, GLint v0, GLint v1, GLint v2); +GLAPI void APIENTRY glUniform4i (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +GLAPI void APIENTRY glUniform1fv (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform2fv (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform3fv (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform4fv (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform1iv (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniform2iv (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniform3iv (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniform4iv (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniformMatrix2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glValidateProgram (GLuint program); +GLAPI void APIENTRY glVertexAttrib1d (GLuint index, GLdouble x); +GLAPI void APIENTRY glVertexAttrib1dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib1f (GLuint index, GLfloat x); +GLAPI void APIENTRY glVertexAttrib1fv (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib1s (GLuint index, GLshort x); +GLAPI void APIENTRY glVertexAttrib1sv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib2d (GLuint index, GLdouble x, GLdouble y); +GLAPI void APIENTRY glVertexAttrib2dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib2f (GLuint index, GLfloat x, GLfloat y); +GLAPI void APIENTRY glVertexAttrib2fv (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib2s (GLuint index, GLshort x, GLshort y); +GLAPI void APIENTRY glVertexAttrib2sv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib3d (GLuint index, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glVertexAttrib3dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib3f (GLuint index, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glVertexAttrib3fv (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib3s (GLuint index, GLshort x, GLshort y, GLshort z); +GLAPI void APIENTRY glVertexAttrib3sv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4Nbv (GLuint index, const GLbyte *v); +GLAPI void APIENTRY glVertexAttrib4Niv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttrib4Nsv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4Nub (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +GLAPI void APIENTRY glVertexAttrib4Nubv (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttrib4Nuiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttrib4Nusv (GLuint index, const GLushort *v); +GLAPI void APIENTRY glVertexAttrib4bv (GLuint index, const GLbyte *v); +GLAPI void APIENTRY glVertexAttrib4d (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glVertexAttrib4dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib4f (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glVertexAttrib4fv (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib4iv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttrib4s (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +GLAPI void APIENTRY glVertexAttrib4sv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4ubv (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttrib4uiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttrib4usv (GLuint index, const GLushort *v); +GLAPI void APIENTRY glVertexAttribPointer (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEPROC) (GLenum modeRGB, GLenum modeAlpha); +typedef void (APIENTRYP PFNGLDRAWBUFFERSPROC) (GLsizei n, const GLenum *bufs); +typedef void (APIENTRYP PFNGLSTENCILOPSEPARATEPROC) (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); +typedef void (APIENTRYP PFNGLSTENCILFUNCSEPARATEPROC) (GLenum face, GLenum func, GLint ref, GLuint mask); +typedef void (APIENTRYP PFNGLSTENCILMASKSEPARATEPROC) (GLenum face, GLuint mask); +typedef void (APIENTRYP PFNGLATTACHSHADERPROC) (GLuint program, GLuint shader); +typedef void (APIENTRYP PFNGLBINDATTRIBLOCATIONPROC) (GLuint program, GLuint index, const GLchar *name); +typedef void (APIENTRYP PFNGLCOMPILESHADERPROC) (GLuint shader); +typedef GLuint (APIENTRYP PFNGLCREATEPROGRAMPROC) (void); +typedef GLuint (APIENTRYP PFNGLCREATESHADERPROC) (GLenum type); +typedef void (APIENTRYP PFNGLDELETEPROGRAMPROC) (GLuint program); +typedef void (APIENTRYP PFNGLDELETESHADERPROC) (GLuint shader); +typedef void (APIENTRYP PFNGLDETACHSHADERPROC) (GLuint program, GLuint shader); +typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYPROC) (GLuint index); +typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYPROC) (GLuint index); +typedef void (APIENTRYP PFNGLGETACTIVEATTRIBPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); +typedef void (APIENTRYP PFNGLGETATTACHEDSHADERSPROC) (GLuint program, GLsizei maxCount, GLsizei *count, GLuint *obj); +typedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLGETPROGRAMIVPROC) (GLuint program, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMINFOLOGPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +typedef void (APIENTRYP PFNGLGETSHADERIVPROC) (GLuint shader, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETSHADERINFOLOGPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +typedef void (APIENTRYP PFNGLGETSHADERSOURCEPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source); +typedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLGETUNIFORMFVPROC) (GLuint program, GLint location, GLfloat *params); +typedef void (APIENTRYP PFNGLGETUNIFORMIVPROC) (GLuint program, GLint location, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVPROC) (GLuint index, GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVPROC) (GLuint index, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVPROC) (GLuint index, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVPROC) (GLuint index, GLenum pname, GLvoid* *pointer); +typedef GLboolean (APIENTRYP PFNGLISPROGRAMPROC) (GLuint program); +typedef GLboolean (APIENTRYP PFNGLISSHADERPROC) (GLuint shader); +typedef void (APIENTRYP PFNGLLINKPROGRAMPROC) (GLuint program); +typedef void (APIENTRYP PFNGLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const GLchar* *string, const GLint *length); +typedef void (APIENTRYP PFNGLUSEPROGRAMPROC) (GLuint program); +typedef void (APIENTRYP PFNGLUNIFORM1FPROC) (GLint location, GLfloat v0); +typedef void (APIENTRYP PFNGLUNIFORM2FPROC) (GLint location, GLfloat v0, GLfloat v1); +typedef void (APIENTRYP PFNGLUNIFORM3FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (APIENTRYP PFNGLUNIFORM4FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (APIENTRYP PFNGLUNIFORM1IPROC) (GLint location, GLint v0); +typedef void (APIENTRYP PFNGLUNIFORM2IPROC) (GLint location, GLint v0, GLint v1); +typedef void (APIENTRYP PFNGLUNIFORM3IPROC) (GLint location, GLint v0, GLint v1, GLint v2); +typedef void (APIENTRYP PFNGLUNIFORM4IPROC) (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (APIENTRYP PFNGLUNIFORM1FVPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM2FVPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM3FVPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM4FVPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM1IVPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM2IVPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM3IVPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM4IVPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLVALIDATEPROGRAMPROC) (GLuint program); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DPROC) (GLuint index, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FPROC) (GLuint index, GLfloat x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SPROC) (GLuint index, GLshort x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DPROC) (GLuint index, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FPROC) (GLuint index, GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SPROC) (GLuint index, GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SPROC) (GLuint index, GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NBVPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NIVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NSVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBPROC) (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBVPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUSVPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4BVPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4IVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SPROC) (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4USVPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer); +#endif + +#ifndef GL_VERSION_2_1 +#define GL_VERSION_2_1 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glUniformMatrix2x3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix3x2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix2x4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix4x2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix3x4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix4x3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +#endif + +#ifndef GL_VERSION_3_0 +#define GL_VERSION_3_0 1 +/* OpenGL 3.0 also reuses entry points from these extensions: */ +/* ARB_framebuffer_object */ +/* ARB_map_buffer_range */ +/* ARB_vertex_array_object */ +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColorMaski (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); +GLAPI void APIENTRY glGetBooleani_v (GLenum target, GLuint index, GLboolean *data); +GLAPI void APIENTRY glGetIntegeri_v (GLenum target, GLuint index, GLint *data); +GLAPI void APIENTRY glEnablei (GLenum target, GLuint index); +GLAPI void APIENTRY glDisablei (GLenum target, GLuint index); +GLAPI GLboolean APIENTRY glIsEnabledi (GLenum target, GLuint index); +GLAPI void APIENTRY glBeginTransformFeedback (GLenum primitiveMode); +GLAPI void APIENTRY glEndTransformFeedback (void); +GLAPI void APIENTRY glBindBufferRange (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +GLAPI void APIENTRY glBindBufferBase (GLenum target, GLuint index, GLuint buffer); +GLAPI void APIENTRY glTransformFeedbackVaryings (GLuint program, GLsizei count, const GLchar* *varyings, GLenum bufferMode); +GLAPI void APIENTRY glGetTransformFeedbackVarying (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); +GLAPI void APIENTRY glClampColor (GLenum target, GLenum clamp); +GLAPI void APIENTRY glBeginConditionalRender (GLuint id, GLenum mode); +GLAPI void APIENTRY glEndConditionalRender (void); +GLAPI void APIENTRY glVertexAttribIPointer (GLuint index, GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +GLAPI void APIENTRY glGetVertexAttribIiv (GLuint index, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVertexAttribIuiv (GLuint index, GLenum pname, GLuint *params); +GLAPI void APIENTRY glVertexAttribI1i (GLuint index, GLint x); +GLAPI void APIENTRY glVertexAttribI2i (GLuint index, GLint x, GLint y); +GLAPI void APIENTRY glVertexAttribI3i (GLuint index, GLint x, GLint y, GLint z); +GLAPI void APIENTRY glVertexAttribI4i (GLuint index, GLint x, GLint y, GLint z, GLint w); +GLAPI void APIENTRY glVertexAttribI1ui (GLuint index, GLuint x); +GLAPI void APIENTRY glVertexAttribI2ui (GLuint index, GLuint x, GLuint y); +GLAPI void APIENTRY glVertexAttribI3ui (GLuint index, GLuint x, GLuint y, GLuint z); +GLAPI void APIENTRY glVertexAttribI4ui (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +GLAPI void APIENTRY glVertexAttribI1iv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI2iv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI3iv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI4iv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI1uiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI2uiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI3uiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI4uiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI4bv (GLuint index, const GLbyte *v); +GLAPI void APIENTRY glVertexAttribI4sv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttribI4ubv (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttribI4usv (GLuint index, const GLushort *v); +GLAPI void APIENTRY glGetUniformuiv (GLuint program, GLint location, GLuint *params); +GLAPI void APIENTRY glBindFragDataLocation (GLuint program, GLuint color, const GLchar *name); +GLAPI GLint APIENTRY glGetFragDataLocation (GLuint program, const GLchar *name); +GLAPI void APIENTRY glUniform1ui (GLint location, GLuint v0); +GLAPI void APIENTRY glUniform2ui (GLint location, GLuint v0, GLuint v1); +GLAPI void APIENTRY glUniform3ui (GLint location, GLuint v0, GLuint v1, GLuint v2); +GLAPI void APIENTRY glUniform4ui (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +GLAPI void APIENTRY glUniform1uiv (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glUniform2uiv (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glUniform3uiv (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glUniform4uiv (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glTexParameterIiv (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glTexParameterIuiv (GLenum target, GLenum pname, const GLuint *params); +GLAPI void APIENTRY glGetTexParameterIiv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetTexParameterIuiv (GLenum target, GLenum pname, GLuint *params); +GLAPI void APIENTRY glClearBufferiv (GLenum buffer, GLint drawbuffer, const GLint *value); +GLAPI void APIENTRY glClearBufferuiv (GLenum buffer, GLint drawbuffer, const GLuint *value); +GLAPI void APIENTRY glClearBufferfv (GLenum buffer, GLint drawbuffer, const GLfloat *value); +GLAPI void APIENTRY glClearBufferfi (GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); +GLAPI const GLubyte * APIENTRY glGetStringi (GLenum name, GLuint index); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLCOLORMASKIPROC) (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); +typedef void (APIENTRYP PFNGLGETBOOLEANI_VPROC) (GLenum target, GLuint index, GLboolean *data); +typedef void (APIENTRYP PFNGLGETINTEGERI_VPROC) (GLenum target, GLuint index, GLint *data); +typedef void (APIENTRYP PFNGLENABLEIPROC) (GLenum target, GLuint index); +typedef void (APIENTRYP PFNGLDISABLEIPROC) (GLenum target, GLuint index); +typedef GLboolean (APIENTRYP PFNGLISENABLEDIPROC) (GLenum target, GLuint index); +typedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKPROC) (GLenum primitiveMode); +typedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKPROC) (void); +typedef void (APIENTRYP PFNGLBINDBUFFERRANGEPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLBINDBUFFERBASEPROC) (GLenum target, GLuint index, GLuint buffer); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSPROC) (GLuint program, GLsizei count, const GLchar* *varyings, GLenum bufferMode); +typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); +typedef void (APIENTRYP PFNGLCLAMPCOLORPROC) (GLenum target, GLenum clamp); +typedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERPROC) (GLuint id, GLenum mode); +typedef void (APIENTRYP PFNGLENDCONDITIONALRENDERPROC) (void); +typedef void (APIENTRYP PFNGLVERTEXATTRIBIPOINTERPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIIVPROC) (GLuint index, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIUIVPROC) (GLuint index, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IPROC) (GLuint index, GLint x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IPROC) (GLuint index, GLint x, GLint y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IPROC) (GLuint index, GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IPROC) (GLuint index, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIPROC) (GLuint index, GLuint x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIPROC) (GLuint index, GLuint x, GLuint y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIPROC) (GLuint index, GLuint x, GLuint y, GLuint z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIPROC) (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4BVPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UBVPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4USVPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLGETUNIFORMUIVPROC) (GLuint program, GLint location, GLuint *params); +typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONPROC) (GLuint program, GLuint color, const GLchar *name); +typedef GLint (APIENTRYP PFNGLGETFRAGDATALOCATIONPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLUNIFORM1UIPROC) (GLint location, GLuint v0); +typedef void (APIENTRYP PFNGLUNIFORM2UIPROC) (GLint location, GLuint v0, GLuint v1); +typedef void (APIENTRYP PFNGLUNIFORM3UIPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2); +typedef void (APIENTRYP PFNGLUNIFORM4UIPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +typedef void (APIENTRYP PFNGLUNIFORM1UIVPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLUNIFORM2UIVPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLUNIFORM3UIVPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLUNIFORM4UIVPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLTEXPARAMETERIIVPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLTEXPARAMETERIUIVPROC) (GLenum target, GLenum pname, const GLuint *params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERIIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERIUIVPROC) (GLenum target, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLCLEARBUFFERIVPROC) (GLenum buffer, GLint drawbuffer, const GLint *value); +typedef void (APIENTRYP PFNGLCLEARBUFFERUIVPROC) (GLenum buffer, GLint drawbuffer, const GLuint *value); +typedef void (APIENTRYP PFNGLCLEARBUFFERFVPROC) (GLenum buffer, GLint drawbuffer, const GLfloat *value); +typedef void (APIENTRYP PFNGLCLEARBUFFERFIPROC) (GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); +typedef const GLubyte * (APIENTRYP PFNGLGETSTRINGIPROC) (GLenum name, GLuint index); +#endif + +#ifndef GL_VERSION_3_1 +#define GL_VERSION_3_1 1 +/* OpenGL 3.1 also reuses entry points from these extensions: */ +/* ARB_copy_buffer */ +/* ARB_uniform_buffer_object */ +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawArraysInstanced (GLenum mode, GLint first, GLsizei count, GLsizei primcount); +GLAPI void APIENTRY glDrawElementsInstanced (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLsizei primcount); +GLAPI void APIENTRY glTexBuffer (GLenum target, GLenum internalformat, GLuint buffer); +GLAPI void APIENTRY glPrimitiveRestartIndex (GLuint index); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDPROC) (GLenum mode, GLint first, GLsizei count, GLsizei primcount); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDPROC) (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLsizei primcount); +typedef void (APIENTRYP PFNGLTEXBUFFERPROC) (GLenum target, GLenum internalformat, GLuint buffer); +typedef void (APIENTRYP PFNGLPRIMITIVERESTARTINDEXPROC) (GLuint index); +#endif + +#ifndef GL_VERSION_3_2 +#define GL_VERSION_3_2 1 +/* OpenGL 3.2 also reuses entry points from these extensions: */ +/* ARB_draw_elements_base_vertex */ +/* ARB_provoking_vertex */ +/* ARB_sync */ +/* ARB_texture_multisample */ +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetInteger64i_v (GLenum target, GLuint index, GLint64 *data); +GLAPI void APIENTRY glGetBufferParameteri64v (GLenum target, GLenum pname, GLint64 *params); +GLAPI void APIENTRY glFramebufferTexture (GLenum target, GLenum attachment, GLuint texture, GLint level); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLGETINTEGER64I_VPROC) (GLenum target, GLuint index, GLint64 *data); +typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERI64VPROC) (GLenum target, GLenum pname, GLint64 *params); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level); +#endif + +#ifndef GL_VERSION_3_3 +#define GL_VERSION_3_3 1 +/* OpenGL 3.3 also reuses entry points from these extensions: */ +/* ARB_blend_func_extended */ +/* ARB_sampler_objects */ +/* ARB_explicit_attrib_location, but it has none */ +/* ARB_occlusion_query2 (no entry points) */ +/* ARB_shader_bit_encoding (no entry points) */ +/* ARB_texture_rgb10_a2ui (no entry points) */ +/* ARB_texture_swizzle (no entry points) */ +/* ARB_timer_query */ +/* ARB_vertex_type_2_10_10_10_rev */ +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexAttribDivisor (GLuint index, GLuint divisor); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLVERTEXATTRIBDIVISORPROC) (GLuint index, GLuint divisor); +#endif + +#ifndef GL_VERSION_4_0 +#define GL_VERSION_4_0 1 +/* OpenGL 4.0 also reuses entry points from these extensions: */ +/* ARB_texture_query_lod (no entry points) */ +/* ARB_draw_indirect */ +/* ARB_gpu_shader5 (no entry points) */ +/* ARB_gpu_shader_fp64 */ +/* ARB_shader_subroutine */ +/* ARB_tessellation_shader */ +/* ARB_texture_buffer_object_rgb32 (no entry points) */ +/* ARB_texture_cube_map_array (no entry points) */ +/* ARB_texture_gather (no entry points) */ +/* ARB_transform_feedback2 */ +/* ARB_transform_feedback3 */ +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMinSampleShading (GLclampf value); +GLAPI void APIENTRY glBlendEquationi (GLuint buf, GLenum mode); +GLAPI void APIENTRY glBlendEquationSeparatei (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +GLAPI void APIENTRY glBlendFunci (GLuint buf, GLenum src, GLenum dst); +GLAPI void APIENTRY glBlendFuncSeparatei (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLMINSAMPLESHADINGPROC) (GLclampf value); +typedef void (APIENTRYP PFNGLBLENDEQUATIONIPROC) (GLuint buf, GLenum mode); +typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEIPROC) (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +typedef void (APIENTRYP PFNGLBLENDFUNCIPROC) (GLuint buf, GLenum src, GLenum dst); +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEIPROC) (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +#endif + +#ifndef GL_VERSION_4_1 +#define GL_VERSION_4_1 1 +/* OpenGL 4.1 also reuses entry points from these extensions: */ +/* ARB_ES2_compatibility */ +/* ARB_get_program_binary */ +/* ARB_separate_shader_objects */ +/* ARB_shader_precision (no entry points) */ +/* ARB_vertex_attrib_64bit */ +/* ARB_viewport_array */ +#endif + +#ifndef GL_ARB_multitexture +#define GL_ARB_multitexture 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glActiveTextureARB (GLenum texture); +GLAPI void APIENTRY glClientActiveTextureARB (GLenum texture); +GLAPI void APIENTRY glMultiTexCoord1dARB (GLenum target, GLdouble s); +GLAPI void APIENTRY glMultiTexCoord1dvARB (GLenum target, const GLdouble *v); +GLAPI void APIENTRY glMultiTexCoord1fARB (GLenum target, GLfloat s); +GLAPI void APIENTRY glMultiTexCoord1fvARB (GLenum target, const GLfloat *v); +GLAPI void APIENTRY glMultiTexCoord1iARB (GLenum target, GLint s); +GLAPI void APIENTRY glMultiTexCoord1ivARB (GLenum target, const GLint *v); +GLAPI void APIENTRY glMultiTexCoord1sARB (GLenum target, GLshort s); +GLAPI void APIENTRY glMultiTexCoord1svARB (GLenum target, const GLshort *v); +GLAPI void APIENTRY glMultiTexCoord2dARB (GLenum target, GLdouble s, GLdouble t); +GLAPI void APIENTRY glMultiTexCoord2dvARB (GLenum target, const GLdouble *v); +GLAPI void APIENTRY glMultiTexCoord2fARB (GLenum target, GLfloat s, GLfloat t); +GLAPI void APIENTRY glMultiTexCoord2fvARB (GLenum target, const GLfloat *v); +GLAPI void APIENTRY glMultiTexCoord2iARB (GLenum target, GLint s, GLint t); +GLAPI void APIENTRY glMultiTexCoord2ivARB (GLenum target, const GLint *v); +GLAPI void APIENTRY glMultiTexCoord2sARB (GLenum target, GLshort s, GLshort t); +GLAPI void APIENTRY glMultiTexCoord2svARB (GLenum target, const GLshort *v); +GLAPI void APIENTRY glMultiTexCoord3dARB (GLenum target, GLdouble s, GLdouble t, GLdouble r); +GLAPI void APIENTRY glMultiTexCoord3dvARB (GLenum target, const GLdouble *v); +GLAPI void APIENTRY glMultiTexCoord3fARB (GLenum target, GLfloat s, GLfloat t, GLfloat r); +GLAPI void APIENTRY glMultiTexCoord3fvARB (GLenum target, const GLfloat *v); +GLAPI void APIENTRY glMultiTexCoord3iARB (GLenum target, GLint s, GLint t, GLint r); +GLAPI void APIENTRY glMultiTexCoord3ivARB (GLenum target, const GLint *v); +GLAPI void APIENTRY glMultiTexCoord3sARB (GLenum target, GLshort s, GLshort t, GLshort r); +GLAPI void APIENTRY glMultiTexCoord3svARB (GLenum target, const GLshort *v); +GLAPI void APIENTRY glMultiTexCoord4dARB (GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); +GLAPI void APIENTRY glMultiTexCoord4dvARB (GLenum target, const GLdouble *v); +GLAPI void APIENTRY glMultiTexCoord4fARB (GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); +GLAPI void APIENTRY glMultiTexCoord4fvARB (GLenum target, const GLfloat *v); +GLAPI void APIENTRY glMultiTexCoord4iARB (GLenum target, GLint s, GLint t, GLint r, GLint q); +GLAPI void APIENTRY glMultiTexCoord4ivARB (GLenum target, const GLint *v); +GLAPI void APIENTRY glMultiTexCoord4sARB (GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); +GLAPI void APIENTRY glMultiTexCoord4svARB (GLenum target, const GLshort *v); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLACTIVETEXTUREARBPROC) (GLenum texture); +typedef void (APIENTRYP PFNGLCLIENTACTIVETEXTUREARBPROC) (GLenum texture); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1DARBPROC) (GLenum target, GLdouble s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1DVARBPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1FARBPROC) (GLenum target, GLfloat s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1FVARBPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1IARBPROC) (GLenum target, GLint s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1IVARBPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1SARBPROC) (GLenum target, GLshort s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1SVARBPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2DARBPROC) (GLenum target, GLdouble s, GLdouble t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2DVARBPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2FARBPROC) (GLenum target, GLfloat s, GLfloat t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2FVARBPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2IARBPROC) (GLenum target, GLint s, GLint t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2IVARBPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2SARBPROC) (GLenum target, GLshort s, GLshort t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2SVARBPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3DARBPROC) (GLenum target, GLdouble s, GLdouble t, GLdouble r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3DVARBPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3FARBPROC) (GLenum target, GLfloat s, GLfloat t, GLfloat r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3FVARBPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3IARBPROC) (GLenum target, GLint s, GLint t, GLint r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3IVARBPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3SARBPROC) (GLenum target, GLshort s, GLshort t, GLshort r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3SVARBPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4DARBPROC) (GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4DVARBPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4FARBPROC) (GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4FVARBPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4IARBPROC) (GLenum target, GLint s, GLint t, GLint r, GLint q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4IVARBPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4SARBPROC) (GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4SVARBPROC) (GLenum target, const GLshort *v); +#endif + +#ifndef GL_ARB_transpose_matrix +#define GL_ARB_transpose_matrix 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glLoadTransposeMatrixfARB (const GLfloat *m); +GLAPI void APIENTRY glLoadTransposeMatrixdARB (const GLdouble *m); +GLAPI void APIENTRY glMultTransposeMatrixfARB (const GLfloat *m); +GLAPI void APIENTRY glMultTransposeMatrixdARB (const GLdouble *m); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXFARBPROC) (const GLfloat *m); +typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXDARBPROC) (const GLdouble *m); +typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXFARBPROC) (const GLfloat *m); +typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXDARBPROC) (const GLdouble *m); +#endif + +#ifndef GL_ARB_multisample +#define GL_ARB_multisample 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSampleCoverageARB (GLclampf value, GLboolean invert); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLSAMPLECOVERAGEARBPROC) (GLclampf value, GLboolean invert); +#endif + +#ifndef GL_ARB_texture_env_add +#define GL_ARB_texture_env_add 1 +#endif + +#ifndef GL_ARB_texture_cube_map +#define GL_ARB_texture_cube_map 1 +#endif + +#ifndef GL_ARB_texture_compression +#define GL_ARB_texture_compression 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCompressedTexImage3DARB (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid *data); +GLAPI void APIENTRY glCompressedTexImage2DARB (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *data); +GLAPI void APIENTRY glCompressedTexImage1DARB (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const GLvoid *data); +GLAPI void APIENTRY glCompressedTexSubImage3DARB (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid *data); +GLAPI void APIENTRY glCompressedTexSubImage2DARB (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *data); +GLAPI void APIENTRY glCompressedTexSubImage1DARB (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const GLvoid *data); +GLAPI void APIENTRY glGetCompressedTexImageARB (GLenum target, GLint level, GLvoid *img); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DARBPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DARBPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE1DARBPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const GLvoid *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DARBPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DARBPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE1DARBPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const GLvoid *data); +typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXIMAGEARBPROC) (GLenum target, GLint level, GLvoid *img); +#endif + +#ifndef GL_ARB_texture_border_clamp +#define GL_ARB_texture_border_clamp 1 +#endif + +#ifndef GL_ARB_point_parameters +#define GL_ARB_point_parameters 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPointParameterfARB (GLenum pname, GLfloat param); +GLAPI void APIENTRY glPointParameterfvARB (GLenum pname, const GLfloat *params); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLPOINTPARAMETERFARBPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFVARBPROC) (GLenum pname, const GLfloat *params); +#endif + +#ifndef GL_ARB_vertex_blend +#define GL_ARB_vertex_blend 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glWeightbvARB (GLint size, const GLbyte *weights); +GLAPI void APIENTRY glWeightsvARB (GLint size, const GLshort *weights); +GLAPI void APIENTRY glWeightivARB (GLint size, const GLint *weights); +GLAPI void APIENTRY glWeightfvARB (GLint size, const GLfloat *weights); +GLAPI void APIENTRY glWeightdvARB (GLint size, const GLdouble *weights); +GLAPI void APIENTRY glWeightubvARB (GLint size, const GLubyte *weights); +GLAPI void APIENTRY glWeightusvARB (GLint size, const GLushort *weights); +GLAPI void APIENTRY glWeightuivARB (GLint size, const GLuint *weights); +GLAPI void APIENTRY glWeightPointerARB (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +GLAPI void APIENTRY glVertexBlendARB (GLint count); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLWEIGHTBVARBPROC) (GLint size, const GLbyte *weights); +typedef void (APIENTRYP PFNGLWEIGHTSVARBPROC) (GLint size, const GLshort *weights); +typedef void (APIENTRYP PFNGLWEIGHTIVARBPROC) (GLint size, const GLint *weights); +typedef void (APIENTRYP PFNGLWEIGHTFVARBPROC) (GLint size, const GLfloat *weights); +typedef void (APIENTRYP PFNGLWEIGHTDVARBPROC) (GLint size, const GLdouble *weights); +typedef void (APIENTRYP PFNGLWEIGHTUBVARBPROC) (GLint size, const GLubyte *weights); +typedef void (APIENTRYP PFNGLWEIGHTUSVARBPROC) (GLint size, const GLushort *weights); +typedef void (APIENTRYP PFNGLWEIGHTUIVARBPROC) (GLint size, const GLuint *weights); +typedef void (APIENTRYP PFNGLWEIGHTPOINTERARBPROC) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +typedef void (APIENTRYP PFNGLVERTEXBLENDARBPROC) (GLint count); +#endif + +#ifndef GL_ARB_matrix_palette +#define GL_ARB_matrix_palette 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCurrentPaletteMatrixARB (GLint index); +GLAPI void APIENTRY glMatrixIndexubvARB (GLint size, const GLubyte *indices); +GLAPI void APIENTRY glMatrixIndexusvARB (GLint size, const GLushort *indices); +GLAPI void APIENTRY glMatrixIndexuivARB (GLint size, const GLuint *indices); +GLAPI void APIENTRY glMatrixIndexPointerARB (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLCURRENTPALETTEMATRIXARBPROC) (GLint index); +typedef void (APIENTRYP PFNGLMATRIXINDEXUBVARBPROC) (GLint size, const GLubyte *indices); +typedef void (APIENTRYP PFNGLMATRIXINDEXUSVARBPROC) (GLint size, const GLushort *indices); +typedef void (APIENTRYP PFNGLMATRIXINDEXUIVARBPROC) (GLint size, const GLuint *indices); +typedef void (APIENTRYP PFNGLMATRIXINDEXPOINTERARBPROC) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +#endif + +#ifndef GL_ARB_texture_env_combine +#define GL_ARB_texture_env_combine 1 +#endif + +#ifndef GL_ARB_texture_env_crossbar +#define GL_ARB_texture_env_crossbar 1 +#endif + +#ifndef GL_ARB_texture_env_dot3 +#define GL_ARB_texture_env_dot3 1 +#endif + +#ifndef GL_ARB_texture_mirrored_repeat +#define GL_ARB_texture_mirrored_repeat 1 +#endif + +#ifndef GL_ARB_depth_texture +#define GL_ARB_depth_texture 1 +#endif + +#ifndef GL_ARB_shadow +#define GL_ARB_shadow 1 +#endif + +#ifndef GL_ARB_shadow_ambient +#define GL_ARB_shadow_ambient 1 +#endif + +#ifndef GL_ARB_window_pos +#define GL_ARB_window_pos 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glWindowPos2dARB (GLdouble x, GLdouble y); +GLAPI void APIENTRY glWindowPos2dvARB (const GLdouble *v); +GLAPI void APIENTRY glWindowPos2fARB (GLfloat x, GLfloat y); +GLAPI void APIENTRY glWindowPos2fvARB (const GLfloat *v); +GLAPI void APIENTRY glWindowPos2iARB (GLint x, GLint y); +GLAPI void APIENTRY glWindowPos2ivARB (const GLint *v); +GLAPI void APIENTRY glWindowPos2sARB (GLshort x, GLshort y); +GLAPI void APIENTRY glWindowPos2svARB (const GLshort *v); +GLAPI void APIENTRY glWindowPos3dARB (GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glWindowPos3dvARB (const GLdouble *v); +GLAPI void APIENTRY glWindowPos3fARB (GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glWindowPos3fvARB (const GLfloat *v); +GLAPI void APIENTRY glWindowPos3iARB (GLint x, GLint y, GLint z); +GLAPI void APIENTRY glWindowPos3ivARB (const GLint *v); +GLAPI void APIENTRY glWindowPos3sARB (GLshort x, GLshort y, GLshort z); +GLAPI void APIENTRY glWindowPos3svARB (const GLshort *v); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLWINDOWPOS2DARBPROC) (GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLWINDOWPOS2DVARBPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2FARBPROC) (GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLWINDOWPOS2FVARBPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2IARBPROC) (GLint x, GLint y); +typedef void (APIENTRYP PFNGLWINDOWPOS2IVARBPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2SARBPROC) (GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLWINDOWPOS2SVARBPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3DARBPROC) (GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLWINDOWPOS3DVARBPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3FARBPROC) (GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLWINDOWPOS3FVARBPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3IARBPROC) (GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLWINDOWPOS3IVARBPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3SARBPROC) (GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLWINDOWPOS3SVARBPROC) (const GLshort *v); +#endif + +#ifndef GL_ARB_vertex_program +#define GL_ARB_vertex_program 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexAttrib1dARB (GLuint index, GLdouble x); +GLAPI void APIENTRY glVertexAttrib1dvARB (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib1fARB (GLuint index, GLfloat x); +GLAPI void APIENTRY glVertexAttrib1fvARB (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib1sARB (GLuint index, GLshort x); +GLAPI void APIENTRY glVertexAttrib1svARB (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib2dARB (GLuint index, GLdouble x, GLdouble y); +GLAPI void APIENTRY glVertexAttrib2dvARB (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib2fARB (GLuint index, GLfloat x, GLfloat y); +GLAPI void APIENTRY glVertexAttrib2fvARB (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib2sARB (GLuint index, GLshort x, GLshort y); +GLAPI void APIENTRY glVertexAttrib2svARB (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib3dARB (GLuint index, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glVertexAttrib3dvARB (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib3fARB (GLuint index, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glVertexAttrib3fvARB (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib3sARB (GLuint index, GLshort x, GLshort y, GLshort z); +GLAPI void APIENTRY glVertexAttrib3svARB (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4NbvARB (GLuint index, const GLbyte *v); +GLAPI void APIENTRY glVertexAttrib4NivARB (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttrib4NsvARB (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4NubARB (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +GLAPI void APIENTRY glVertexAttrib4NubvARB (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttrib4NuivARB (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttrib4NusvARB (GLuint index, const GLushort *v); +GLAPI void APIENTRY glVertexAttrib4bvARB (GLuint index, const GLbyte *v); +GLAPI void APIENTRY glVertexAttrib4dARB (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glVertexAttrib4dvARB (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib4fARB (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glVertexAttrib4fvARB (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib4ivARB (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttrib4sARB (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +GLAPI void APIENTRY glVertexAttrib4svARB (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4ubvARB (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttrib4uivARB (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttrib4usvARB (GLuint index, const GLushort *v); +GLAPI void APIENTRY glVertexAttribPointerARB (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer); +GLAPI void APIENTRY glEnableVertexAttribArrayARB (GLuint index); +GLAPI void APIENTRY glDisableVertexAttribArrayARB (GLuint index); +GLAPI void APIENTRY glProgramStringARB (GLenum target, GLenum format, GLsizei len, const GLvoid *string); +GLAPI void APIENTRY glBindProgramARB (GLenum target, GLuint program); +GLAPI void APIENTRY glDeleteProgramsARB (GLsizei n, const GLuint *programs); +GLAPI void APIENTRY glGenProgramsARB (GLsizei n, GLuint *programs); +GLAPI void APIENTRY glProgramEnvParameter4dARB (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glProgramEnvParameter4dvARB (GLenum target, GLuint index, const GLdouble *params); +GLAPI void APIENTRY glProgramEnvParameter4fARB (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glProgramEnvParameter4fvARB (GLenum target, GLuint index, const GLfloat *params); +GLAPI void APIENTRY glProgramLocalParameter4dARB (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glProgramLocalParameter4dvARB (GLenum target, GLuint index, const GLdouble *params); +GLAPI void APIENTRY glProgramLocalParameter4fARB (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glProgramLocalParameter4fvARB (GLenum target, GLuint index, const GLfloat *params); +GLAPI void APIENTRY glGetProgramEnvParameterdvARB (GLenum target, GLuint index, GLdouble *params); +GLAPI void APIENTRY glGetProgramEnvParameterfvARB (GLenum target, GLuint index, GLfloat *params); +GLAPI void APIENTRY glGetProgramLocalParameterdvARB (GLenum target, GLuint index, GLdouble *params); +GLAPI void APIENTRY glGetProgramLocalParameterfvARB (GLenum target, GLuint index, GLfloat *params); +GLAPI void APIENTRY glGetProgramivARB (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetProgramStringARB (GLenum target, GLenum pname, GLvoid *string); +GLAPI void APIENTRY glGetVertexAttribdvARB (GLuint index, GLenum pname, GLdouble *params); +GLAPI void APIENTRY glGetVertexAttribfvARB (GLuint index, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetVertexAttribivARB (GLuint index, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVertexAttribPointervARB (GLuint index, GLenum pname, GLvoid* *pointer); +GLAPI GLboolean APIENTRY glIsProgramARB (GLuint program); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DARBPROC) (GLuint index, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DVARBPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FARBPROC) (GLuint index, GLfloat x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVARBPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SARBPROC) (GLuint index, GLshort x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SVARBPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DARBPROC) (GLuint index, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DVARBPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FARBPROC) (GLuint index, GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVARBPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SARBPROC) (GLuint index, GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SVARBPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DARBPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DVARBPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FARBPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVARBPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SARBPROC) (GLuint index, GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SVARBPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NBVARBPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NIVARBPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NSVARBPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBARBPROC) (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBVARBPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUIVARBPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUSVARBPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4BVARBPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DARBPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DVARBPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FARBPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVARBPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4IVARBPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SARBPROC) (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SVARBPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVARBPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UIVARBPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4USVARBPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERARBPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer); +typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYARBPROC) (GLuint index); +typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYARBPROC) (GLuint index); +typedef void (APIENTRYP PFNGLPROGRAMSTRINGARBPROC) (GLenum target, GLenum format, GLsizei len, const GLvoid *string); +typedef void (APIENTRYP PFNGLBINDPROGRAMARBPROC) (GLenum target, GLuint program); +typedef void (APIENTRYP PFNGLDELETEPROGRAMSARBPROC) (GLsizei n, const GLuint *programs); +typedef void (APIENTRYP PFNGLGENPROGRAMSARBPROC) (GLsizei n, GLuint *programs); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4DARBPROC) (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4DVARBPROC) (GLenum target, GLuint index, const GLdouble *params); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4FARBPROC) (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4FVARBPROC) (GLenum target, GLuint index, const GLfloat *params); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4DARBPROC) (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4DVARBPROC) (GLenum target, GLuint index, const GLdouble *params); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4FARBPROC) (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4FVARBPROC) (GLenum target, GLuint index, const GLfloat *params); +typedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERDVARBPROC) (GLenum target, GLuint index, GLdouble *params); +typedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERFVARBPROC) (GLenum target, GLuint index, GLfloat *params); +typedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC) (GLenum target, GLuint index, GLdouble *params); +typedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERFVARBPROC) (GLenum target, GLuint index, GLfloat *params); +typedef void (APIENTRYP PFNGLGETPROGRAMIVARBPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMSTRINGARBPROC) (GLenum target, GLenum pname, GLvoid *string); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVARBPROC) (GLuint index, GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVARBPROC) (GLuint index, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVARBPROC) (GLuint index, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVARBPROC) (GLuint index, GLenum pname, GLvoid* *pointer); +typedef GLboolean (APIENTRYP PFNGLISPROGRAMARBPROC) (GLuint program); +#endif + +#ifndef GL_ARB_fragment_program +#define GL_ARB_fragment_program 1 +/* All ARB_fragment_program entry points are shared with ARB_vertex_program. */ +#endif + +#ifndef GL_ARB_vertex_buffer_object +#define GL_ARB_vertex_buffer_object 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBindBufferARB (GLenum target, GLuint buffer); +GLAPI void APIENTRY glDeleteBuffersARB (GLsizei n, const GLuint *buffers); +GLAPI void APIENTRY glGenBuffersARB (GLsizei n, GLuint *buffers); +GLAPI GLboolean APIENTRY glIsBufferARB (GLuint buffer); +GLAPI void APIENTRY glBufferDataARB (GLenum target, GLsizeiptrARB size, const GLvoid *data, GLenum usage); +GLAPI void APIENTRY glBufferSubDataARB (GLenum target, GLintptrARB offset, GLsizeiptrARB size, const GLvoid *data); +GLAPI void APIENTRY glGetBufferSubDataARB (GLenum target, GLintptrARB offset, GLsizeiptrARB size, GLvoid *data); +GLAPI GLvoid* APIENTRY glMapBufferARB (GLenum target, GLenum access); +GLAPI GLboolean APIENTRY glUnmapBufferARB (GLenum target); +GLAPI void APIENTRY glGetBufferParameterivARB (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetBufferPointervARB (GLenum target, GLenum pname, GLvoid* *params); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBINDBUFFERARBPROC) (GLenum target, GLuint buffer); +typedef void (APIENTRYP PFNGLDELETEBUFFERSARBPROC) (GLsizei n, const GLuint *buffers); +typedef void (APIENTRYP PFNGLGENBUFFERSARBPROC) (GLsizei n, GLuint *buffers); +typedef GLboolean (APIENTRYP PFNGLISBUFFERARBPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLBUFFERDATAARBPROC) (GLenum target, GLsizeiptrARB size, const GLvoid *data, GLenum usage); +typedef void (APIENTRYP PFNGLBUFFERSUBDATAARBPROC) (GLenum target, GLintptrARB offset, GLsizeiptrARB size, const GLvoid *data); +typedef void (APIENTRYP PFNGLGETBUFFERSUBDATAARBPROC) (GLenum target, GLintptrARB offset, GLsizeiptrARB size, GLvoid *data); +typedef GLvoid* (APIENTRYP PFNGLMAPBUFFERARBPROC) (GLenum target, GLenum access); +typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERARBPROC) (GLenum target); +typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIVARBPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETBUFFERPOINTERVARBPROC) (GLenum target, GLenum pname, GLvoid* *params); +#endif + +#ifndef GL_ARB_occlusion_query +#define GL_ARB_occlusion_query 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGenQueriesARB (GLsizei n, GLuint *ids); +GLAPI void APIENTRY glDeleteQueriesARB (GLsizei n, const GLuint *ids); +GLAPI GLboolean APIENTRY glIsQueryARB (GLuint id); +GLAPI void APIENTRY glBeginQueryARB (GLenum target, GLuint id); +GLAPI void APIENTRY glEndQueryARB (GLenum target); +GLAPI void APIENTRY glGetQueryivARB (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetQueryObjectivARB (GLuint id, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetQueryObjectuivARB (GLuint id, GLenum pname, GLuint *params); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLGENQUERIESARBPROC) (GLsizei n, GLuint *ids); +typedef void (APIENTRYP PFNGLDELETEQUERIESARBPROC) (GLsizei n, const GLuint *ids); +typedef GLboolean (APIENTRYP PFNGLISQUERYARBPROC) (GLuint id); +typedef void (APIENTRYP PFNGLBEGINQUERYARBPROC) (GLenum target, GLuint id); +typedef void (APIENTRYP PFNGLENDQUERYARBPROC) (GLenum target); +typedef void (APIENTRYP PFNGLGETQUERYIVARBPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTIVARBPROC) (GLuint id, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTUIVARBPROC) (GLuint id, GLenum pname, GLuint *params); +#endif + +#ifndef GL_ARB_shader_objects +#define GL_ARB_shader_objects 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDeleteObjectARB (GLhandleARB obj); +GLAPI GLhandleARB APIENTRY glGetHandleARB (GLenum pname); +GLAPI void APIENTRY glDetachObjectARB (GLhandleARB containerObj, GLhandleARB attachedObj); +GLAPI GLhandleARB APIENTRY glCreateShaderObjectARB (GLenum shaderType); +GLAPI void APIENTRY glShaderSourceARB (GLhandleARB shaderObj, GLsizei count, const GLcharARB* *string, const GLint *length); +GLAPI void APIENTRY glCompileShaderARB (GLhandleARB shaderObj); +GLAPI GLhandleARB APIENTRY glCreateProgramObjectARB (void); +GLAPI void APIENTRY glAttachObjectARB (GLhandleARB containerObj, GLhandleARB obj); +GLAPI void APIENTRY glLinkProgramARB (GLhandleARB programObj); +GLAPI void APIENTRY glUseProgramObjectARB (GLhandleARB programObj); +GLAPI void APIENTRY glValidateProgramARB (GLhandleARB programObj); +GLAPI void APIENTRY glUniform1fARB (GLint location, GLfloat v0); +GLAPI void APIENTRY glUniform2fARB (GLint location, GLfloat v0, GLfloat v1); +GLAPI void APIENTRY glUniform3fARB (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +GLAPI void APIENTRY glUniform4fARB (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +GLAPI void APIENTRY glUniform1iARB (GLint location, GLint v0); +GLAPI void APIENTRY glUniform2iARB (GLint location, GLint v0, GLint v1); +GLAPI void APIENTRY glUniform3iARB (GLint location, GLint v0, GLint v1, GLint v2); +GLAPI void APIENTRY glUniform4iARB (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +GLAPI void APIENTRY glUniform1fvARB (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform2fvARB (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform3fvARB (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform4fvARB (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform1ivARB (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniform2ivARB (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniform3ivARB (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniform4ivARB (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniformMatrix2fvARB (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix3fvARB (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix4fvARB (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glGetObjectParameterfvARB (GLhandleARB obj, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetObjectParameterivARB (GLhandleARB obj, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetInfoLogARB (GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *infoLog); +GLAPI void APIENTRY glGetAttachedObjectsARB (GLhandleARB containerObj, GLsizei maxCount, GLsizei *count, GLhandleARB *obj); +GLAPI GLint APIENTRY glGetUniformLocationARB (GLhandleARB programObj, const GLcharARB *name); +GLAPI void APIENTRY glGetActiveUniformARB (GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei *length, GLint *size, GLenum *type, GLcharARB *name); +GLAPI void APIENTRY glGetUniformfvARB (GLhandleARB programObj, GLint location, GLfloat *params); +GLAPI void APIENTRY glGetUniformivARB (GLhandleARB programObj, GLint location, GLint *params); +GLAPI void APIENTRY glGetShaderSourceARB (GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *source); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLDELETEOBJECTARBPROC) (GLhandleARB obj); +typedef GLhandleARB (APIENTRYP PFNGLGETHANDLEARBPROC) (GLenum pname); +typedef void (APIENTRYP PFNGLDETACHOBJECTARBPROC) (GLhandleARB containerObj, GLhandleARB attachedObj); +typedef GLhandleARB (APIENTRYP PFNGLCREATESHADEROBJECTARBPROC) (GLenum shaderType); +typedef void (APIENTRYP PFNGLSHADERSOURCEARBPROC) (GLhandleARB shaderObj, GLsizei count, const GLcharARB* *string, const GLint *length); +typedef void (APIENTRYP PFNGLCOMPILESHADERARBPROC) (GLhandleARB shaderObj); +typedef GLhandleARB (APIENTRYP PFNGLCREATEPROGRAMOBJECTARBPROC) (void); +typedef void (APIENTRYP PFNGLATTACHOBJECTARBPROC) (GLhandleARB containerObj, GLhandleARB obj); +typedef void (APIENTRYP PFNGLLINKPROGRAMARBPROC) (GLhandleARB programObj); +typedef void (APIENTRYP PFNGLUSEPROGRAMOBJECTARBPROC) (GLhandleARB programObj); +typedef void (APIENTRYP PFNGLVALIDATEPROGRAMARBPROC) (GLhandleARB programObj); +typedef void (APIENTRYP PFNGLUNIFORM1FARBPROC) (GLint location, GLfloat v0); +typedef void (APIENTRYP PFNGLUNIFORM2FARBPROC) (GLint location, GLfloat v0, GLfloat v1); +typedef void (APIENTRYP PFNGLUNIFORM3FARBPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (APIENTRYP PFNGLUNIFORM4FARBPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (APIENTRYP PFNGLUNIFORM1IARBPROC) (GLint location, GLint v0); +typedef void (APIENTRYP PFNGLUNIFORM2IARBPROC) (GLint location, GLint v0, GLint v1); +typedef void (APIENTRYP PFNGLUNIFORM3IARBPROC) (GLint location, GLint v0, GLint v1, GLint v2); +typedef void (APIENTRYP PFNGLUNIFORM4IARBPROC) (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (APIENTRYP PFNGLUNIFORM1FVARBPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM2FVARBPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM3FVARBPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM4FVARBPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM1IVARBPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM2IVARBPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM3IVARBPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM4IVARBPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2FVARBPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3FVARBPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVARBPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLGETOBJECTPARAMETERFVARBPROC) (GLhandleARB obj, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETOBJECTPARAMETERIVARBPROC) (GLhandleARB obj, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETINFOLOGARBPROC) (GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *infoLog); +typedef void (APIENTRYP PFNGLGETATTACHEDOBJECTSARBPROC) (GLhandleARB containerObj, GLsizei maxCount, GLsizei *count, GLhandleARB *obj); +typedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONARBPROC) (GLhandleARB programObj, const GLcharARB *name); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMARBPROC) (GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei *length, GLint *size, GLenum *type, GLcharARB *name); +typedef void (APIENTRYP PFNGLGETUNIFORMFVARBPROC) (GLhandleARB programObj, GLint location, GLfloat *params); +typedef void (APIENTRYP PFNGLGETUNIFORMIVARBPROC) (GLhandleARB programObj, GLint location, GLint *params); +typedef void (APIENTRYP PFNGLGETSHADERSOURCEARBPROC) (GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *source); +#endif + +#ifndef GL_ARB_vertex_shader +#define GL_ARB_vertex_shader 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBindAttribLocationARB (GLhandleARB programObj, GLuint index, const GLcharARB *name); +GLAPI void APIENTRY glGetActiveAttribARB (GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei *length, GLint *size, GLenum *type, GLcharARB *name); +GLAPI GLint APIENTRY glGetAttribLocationARB (GLhandleARB programObj, const GLcharARB *name); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBINDATTRIBLOCATIONARBPROC) (GLhandleARB programObj, GLuint index, const GLcharARB *name); +typedef void (APIENTRYP PFNGLGETACTIVEATTRIBARBPROC) (GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei *length, GLint *size, GLenum *type, GLcharARB *name); +typedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONARBPROC) (GLhandleARB programObj, const GLcharARB *name); +#endif + +#ifndef GL_ARB_fragment_shader +#define GL_ARB_fragment_shader 1 +#endif + +#ifndef GL_ARB_shading_language_100 +#define GL_ARB_shading_language_100 1 +#endif + +#ifndef GL_ARB_texture_non_power_of_two +#define GL_ARB_texture_non_power_of_two 1 +#endif + +#ifndef GL_ARB_point_sprite +#define GL_ARB_point_sprite 1 +#endif + +#ifndef GL_ARB_fragment_program_shadow +#define GL_ARB_fragment_program_shadow 1 +#endif + +#ifndef GL_ARB_draw_buffers +#define GL_ARB_draw_buffers 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawBuffersARB (GLsizei n, const GLenum *bufs); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLDRAWBUFFERSARBPROC) (GLsizei n, const GLenum *bufs); +#endif + +#ifndef GL_ARB_texture_rectangle +#define GL_ARB_texture_rectangle 1 +#endif + +#ifndef GL_ARB_color_buffer_float +#define GL_ARB_color_buffer_float 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glClampColorARB (GLenum target, GLenum clamp); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLCLAMPCOLORARBPROC) (GLenum target, GLenum clamp); +#endif + +#ifndef GL_ARB_half_float_pixel +#define GL_ARB_half_float_pixel 1 +#endif + +#ifndef GL_ARB_texture_float +#define GL_ARB_texture_float 1 +#endif + +#ifndef GL_ARB_pixel_buffer_object +#define GL_ARB_pixel_buffer_object 1 +#endif + +#ifndef GL_ARB_depth_buffer_float +#define GL_ARB_depth_buffer_float 1 +#endif + +#ifndef GL_ARB_draw_instanced +#define GL_ARB_draw_instanced 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawArraysInstancedARB (GLenum mode, GLint first, GLsizei count, GLsizei primcount); +GLAPI void APIENTRY glDrawElementsInstancedARB (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLsizei primcount); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDARBPROC) (GLenum mode, GLint first, GLsizei count, GLsizei primcount); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDARBPROC) (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLsizei primcount); +#endif + +#ifndef GL_ARB_framebuffer_object +#define GL_ARB_framebuffer_object 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLboolean APIENTRY glIsRenderbuffer (GLuint renderbuffer); +GLAPI void APIENTRY glBindRenderbuffer (GLenum target, GLuint renderbuffer); +GLAPI void APIENTRY glDeleteRenderbuffers (GLsizei n, const GLuint *renderbuffers); +GLAPI void APIENTRY glGenRenderbuffers (GLsizei n, GLuint *renderbuffers); +GLAPI void APIENTRY glRenderbufferStorage (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetRenderbufferParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI GLboolean APIENTRY glIsFramebuffer (GLuint framebuffer); +GLAPI void APIENTRY glBindFramebuffer (GLenum target, GLuint framebuffer); +GLAPI void APIENTRY glDeleteFramebuffers (GLsizei n, const GLuint *framebuffers); +GLAPI void APIENTRY glGenFramebuffers (GLsizei n, GLuint *framebuffers); +GLAPI GLenum APIENTRY glCheckFramebufferStatus (GLenum target); +GLAPI void APIENTRY glFramebufferTexture1D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GLAPI void APIENTRY glFramebufferTexture2D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GLAPI void APIENTRY glFramebufferTexture3D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +GLAPI void APIENTRY glFramebufferRenderbuffer (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +GLAPI void APIENTRY glGetFramebufferAttachmentParameteriv (GLenum target, GLenum attachment, GLenum pname, GLint *params); +GLAPI void APIENTRY glGenerateMipmap (GLenum target); +GLAPI void APIENTRY glBlitFramebuffer (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +GLAPI void APIENTRY glRenderbufferStorageMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glFramebufferTextureLayer (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef GLboolean (APIENTRYP PFNGLISRENDERBUFFERPROC) (GLuint renderbuffer); +typedef void (APIENTRYP PFNGLBINDRENDERBUFFERPROC) (GLenum target, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLDELETERENDERBUFFERSPROC) (GLsizei n, const GLuint *renderbuffers); +typedef void (APIENTRYP PFNGLGENRENDERBUFFERSPROC) (GLsizei n, GLuint *renderbuffers); +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef GLboolean (APIENTRYP PFNGLISFRAMEBUFFERPROC) (GLuint framebuffer); +typedef void (APIENTRYP PFNGLBINDFRAMEBUFFERPROC) (GLenum target, GLuint framebuffer); +typedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSPROC) (GLsizei n, const GLuint *framebuffers); +typedef void (APIENTRYP PFNGLGENFRAMEBUFFERSPROC) (GLsizei n, GLuint *framebuffers); +typedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSPROC) (GLenum target); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE1DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +typedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFERPROC) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC) (GLenum target, GLenum attachment, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGENERATEMIPMAPPROC) (GLenum target); +typedef void (APIENTRYP PFNGLBLITFRAMEBUFFERPROC) (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYERPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +#endif + +#ifndef GL_ARB_framebuffer_sRGB +#define GL_ARB_framebuffer_sRGB 1 +#endif + +#ifndef GL_ARB_geometry_shader4 +#define GL_ARB_geometry_shader4 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramParameteriARB (GLuint program, GLenum pname, GLint value); +GLAPI void APIENTRY glFramebufferTextureARB (GLenum target, GLenum attachment, GLuint texture, GLint level); +GLAPI void APIENTRY glFramebufferTextureLayerARB (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +GLAPI void APIENTRY glFramebufferTextureFaceARB (GLenum target, GLenum attachment, GLuint texture, GLint level, GLenum face); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLPROGRAMPARAMETERIARBPROC) (GLuint program, GLenum pname, GLint value); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREARBPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYERARBPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREFACEARBPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLenum face); +#endif + +#ifndef GL_ARB_half_float_vertex +#define GL_ARB_half_float_vertex 1 +#endif + +#ifndef GL_ARB_instanced_arrays +#define GL_ARB_instanced_arrays 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexAttribDivisorARB (GLuint index, GLuint divisor); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLVERTEXATTRIBDIVISORARBPROC) (GLuint index, GLuint divisor); +#endif + +#ifndef GL_ARB_map_buffer_range +#define GL_ARB_map_buffer_range 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLvoid* APIENTRY glMapBufferRange (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); +GLAPI void APIENTRY glFlushMappedBufferRange (GLenum target, GLintptr offset, GLsizeiptr length); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef GLvoid* (APIENTRYP PFNGLMAPBUFFERRANGEPROC) (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); +typedef void (APIENTRYP PFNGLFLUSHMAPPEDBUFFERRANGEPROC) (GLenum target, GLintptr offset, GLsizeiptr length); +#endif + +#ifndef GL_ARB_texture_buffer_object +#define GL_ARB_texture_buffer_object 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexBufferARB (GLenum target, GLenum internalformat, GLuint buffer); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLTEXBUFFERARBPROC) (GLenum target, GLenum internalformat, GLuint buffer); +#endif + +#ifndef GL_ARB_texture_compression_rgtc +#define GL_ARB_texture_compression_rgtc 1 +#endif + +#ifndef GL_ARB_texture_rg +#define GL_ARB_texture_rg 1 +#endif + +#ifndef GL_ARB_vertex_array_object +#define GL_ARB_vertex_array_object 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBindVertexArray (GLuint array); +GLAPI void APIENTRY glDeleteVertexArrays (GLsizei n, const GLuint *arrays); +GLAPI void APIENTRY glGenVertexArrays (GLsizei n, GLuint *arrays); +GLAPI GLboolean APIENTRY glIsVertexArray (GLuint array); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBINDVERTEXARRAYPROC) (GLuint array); +typedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSPROC) (GLsizei n, const GLuint *arrays); +typedef void (APIENTRYP PFNGLGENVERTEXARRAYSPROC) (GLsizei n, GLuint *arrays); +typedef GLboolean (APIENTRYP PFNGLISVERTEXARRAYPROC) (GLuint array); +#endif + +#ifndef GL_ARB_uniform_buffer_object +#define GL_ARB_uniform_buffer_object 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetUniformIndices (GLuint program, GLsizei uniformCount, const GLchar* *uniformNames, GLuint *uniformIndices); +GLAPI void APIENTRY glGetActiveUniformsiv (GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetActiveUniformName (GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformName); +GLAPI GLuint APIENTRY glGetUniformBlockIndex (GLuint program, const GLchar *uniformBlockName); +GLAPI void APIENTRY glGetActiveUniformBlockiv (GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetActiveUniformBlockName (GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName); +GLAPI void APIENTRY glUniformBlockBinding (GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLGETUNIFORMINDICESPROC) (GLuint program, GLsizei uniformCount, const GLchar* *uniformNames, GLuint *uniformIndices); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMSIVPROC) (GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMNAMEPROC) (GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformName); +typedef GLuint (APIENTRYP PFNGLGETUNIFORMBLOCKINDEXPROC) (GLuint program, const GLchar *uniformBlockName); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKIVPROC) (GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC) (GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName); +typedef void (APIENTRYP PFNGLUNIFORMBLOCKBINDINGPROC) (GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); +#endif + +#ifndef GL_ARB_compatibility +#define GL_ARB_compatibility 1 +#endif + +#ifndef GL_ARB_copy_buffer +#define GL_ARB_copy_buffer 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCopyBufferSubData (GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLCOPYBUFFERSUBDATAPROC) (GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +#endif + +#ifndef GL_ARB_shader_texture_lod +#define GL_ARB_shader_texture_lod 1 +#endif + +#ifndef GL_ARB_depth_clamp +#define GL_ARB_depth_clamp 1 +#endif + +#ifndef GL_ARB_draw_elements_base_vertex +#define GL_ARB_draw_elements_base_vertex 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawElementsBaseVertex (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLint basevertex); +GLAPI void APIENTRY glDrawRangeElementsBaseVertex (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices, GLint basevertex); +GLAPI void APIENTRY glDrawElementsInstancedBaseVertex (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLsizei primcount, GLint basevertex); +GLAPI void APIENTRY glMultiDrawElementsBaseVertex (GLenum mode, const GLsizei *count, GLenum type, const GLvoid* *indices, GLsizei primcount, const GLint *basevertex); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLDRAWELEMENTSBASEVERTEXPROC) (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLint basevertex); +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices, GLint basevertex); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC) (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLsizei primcount, GLint basevertex); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC) (GLenum mode, const GLsizei *count, GLenum type, const GLvoid* *indices, GLsizei primcount, const GLint *basevertex); +#endif + +#ifndef GL_ARB_fragment_coord_conventions +#define GL_ARB_fragment_coord_conventions 1 +#endif + +#ifndef GL_ARB_provoking_vertex +#define GL_ARB_provoking_vertex 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProvokingVertex (GLenum mode); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLPROVOKINGVERTEXPROC) (GLenum mode); +#endif + +#ifndef GL_ARB_seamless_cube_map +#define GL_ARB_seamless_cube_map 1 +#endif + +#ifndef GL_ARB_sync +#define GL_ARB_sync 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLsync APIENTRY glFenceSync (GLenum condition, GLbitfield flags); +GLAPI GLboolean APIENTRY glIsSync (GLsync sync); +GLAPI void APIENTRY glDeleteSync (GLsync sync); +GLAPI GLenum APIENTRY glClientWaitSync (GLsync sync, GLbitfield flags, GLuint64 timeout); +GLAPI void APIENTRY glWaitSync (GLsync sync, GLbitfield flags, GLuint64 timeout); +GLAPI void APIENTRY glGetInteger64v (GLenum pname, GLint64 *params); +GLAPI void APIENTRY glGetSynciv (GLsync sync, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef GLsync (APIENTRYP PFNGLFENCESYNCPROC) (GLenum condition, GLbitfield flags); +typedef GLboolean (APIENTRYP PFNGLISSYNCPROC) (GLsync sync); +typedef void (APIENTRYP PFNGLDELETESYNCPROC) (GLsync sync); +typedef GLenum (APIENTRYP PFNGLCLIENTWAITSYNCPROC) (GLsync sync, GLbitfield flags, GLuint64 timeout); +typedef void (APIENTRYP PFNGLWAITSYNCPROC) (GLsync sync, GLbitfield flags, GLuint64 timeout); +typedef void (APIENTRYP PFNGLGETINTEGER64VPROC) (GLenum pname, GLint64 *params); +typedef void (APIENTRYP PFNGLGETSYNCIVPROC) (GLsync sync, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values); +#endif + +#ifndef GL_ARB_texture_multisample +#define GL_ARB_texture_multisample 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexImage2DMultisample (GLenum target, GLsizei samples, GLint internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glTexImage3DMultisample (GLenum target, GLsizei samples, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glGetMultisamplefv (GLenum pname, GLuint index, GLfloat *val); +GLAPI void APIENTRY glSampleMaski (GLuint index, GLbitfield mask); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLTEXIMAGE2DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLint internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXIMAGE3DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLGETMULTISAMPLEFVPROC) (GLenum pname, GLuint index, GLfloat *val); +typedef void (APIENTRYP PFNGLSAMPLEMASKIPROC) (GLuint index, GLbitfield mask); +#endif + +#ifndef GL_ARB_vertex_array_bgra +#define GL_ARB_vertex_array_bgra 1 +#endif + +#ifndef GL_ARB_draw_buffers_blend +#define GL_ARB_draw_buffers_blend 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendEquationiARB (GLuint buf, GLenum mode); +GLAPI void APIENTRY glBlendEquationSeparateiARB (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +GLAPI void APIENTRY glBlendFunciARB (GLuint buf, GLenum src, GLenum dst); +GLAPI void APIENTRY glBlendFuncSeparateiARB (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBLENDEQUATIONIARBPROC) (GLuint buf, GLenum mode); +typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEIARBPROC) (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +typedef void (APIENTRYP PFNGLBLENDFUNCIARBPROC) (GLuint buf, GLenum src, GLenum dst); +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEIARBPROC) (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +#endif + +#ifndef GL_ARB_sample_shading +#define GL_ARB_sample_shading 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMinSampleShadingARB (GLclampf value); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLMINSAMPLESHADINGARBPROC) (GLclampf value); +#endif + +#ifndef GL_ARB_texture_cube_map_array +#define GL_ARB_texture_cube_map_array 1 +#endif + +#ifndef GL_ARB_texture_gather +#define GL_ARB_texture_gather 1 +#endif + +#ifndef GL_ARB_texture_query_lod +#define GL_ARB_texture_query_lod 1 +#endif + +#ifndef GL_ARB_shading_language_include +#define GL_ARB_shading_language_include 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glNamedStringARB (GLenum type, GLint namelen, const GLchar *name, GLint stringlen, const GLchar *string); +GLAPI void APIENTRY glDeleteNamedStringARB (GLint namelen, const GLchar *name); +GLAPI void APIENTRY glCompileShaderIncludeARB (GLuint shader, GLsizei count, const GLchar* *path, const GLint *length); +GLAPI GLboolean APIENTRY glIsNamedStringARB (GLint namelen, const GLchar *name); +GLAPI void APIENTRY glGetNamedStringARB (GLint namelen, const GLchar *name, GLsizei bufSize, GLint *stringlen, GLchar *string); +GLAPI void APIENTRY glGetNamedStringivARB (GLint namelen, const GLchar *name, GLenum pname, GLint *params); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLNAMEDSTRINGARBPROC) (GLenum type, GLint namelen, const GLchar *name, GLint stringlen, const GLchar *string); +typedef void (APIENTRYP PFNGLDELETENAMEDSTRINGARBPROC) (GLint namelen, const GLchar *name); +typedef void (APIENTRYP PFNGLCOMPILESHADERINCLUDEARBPROC) (GLuint shader, GLsizei count, const GLchar* *path, const GLint *length); +typedef GLboolean (APIENTRYP PFNGLISNAMEDSTRINGARBPROC) (GLint namelen, const GLchar *name); +typedef void (APIENTRYP PFNGLGETNAMEDSTRINGARBPROC) (GLint namelen, const GLchar *name, GLsizei bufSize, GLint *stringlen, GLchar *string); +typedef void (APIENTRYP PFNGLGETNAMEDSTRINGIVARBPROC) (GLint namelen, const GLchar *name, GLenum pname, GLint *params); +#endif + +#ifndef GL_ARB_texture_compression_bptc +#define GL_ARB_texture_compression_bptc 1 +#endif + +#ifndef GL_ARB_blend_func_extended +#define GL_ARB_blend_func_extended 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBindFragDataLocationIndexed (GLuint program, GLuint colorNumber, GLuint index, const GLchar *name); +GLAPI GLint APIENTRY glGetFragDataIndex (GLuint program, const GLchar *name); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONINDEXEDPROC) (GLuint program, GLuint colorNumber, GLuint index, const GLchar *name); +typedef GLint (APIENTRYP PFNGLGETFRAGDATAINDEXPROC) (GLuint program, const GLchar *name); +#endif + +#ifndef GL_ARB_explicit_attrib_location +#define GL_ARB_explicit_attrib_location 1 +#endif + +#ifndef GL_ARB_occlusion_query2 +#define GL_ARB_occlusion_query2 1 +#endif + +#ifndef GL_ARB_sampler_objects +#define GL_ARB_sampler_objects 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGenSamplers (GLsizei count, GLuint *samplers); +GLAPI void APIENTRY glDeleteSamplers (GLsizei count, const GLuint *samplers); +GLAPI GLboolean APIENTRY glIsSampler (GLuint sampler); +GLAPI void APIENTRY glBindSampler (GLuint unit, GLuint sampler); +GLAPI void APIENTRY glSamplerParameteri (GLuint sampler, GLenum pname, GLint param); +GLAPI void APIENTRY glSamplerParameteriv (GLuint sampler, GLenum pname, const GLint *param); +GLAPI void APIENTRY glSamplerParameterf (GLuint sampler, GLenum pname, GLfloat param); +GLAPI void APIENTRY glSamplerParameterfv (GLuint sampler, GLenum pname, const GLfloat *param); +GLAPI void APIENTRY glSamplerParameterIiv (GLuint sampler, GLenum pname, const GLint *param); +GLAPI void APIENTRY glSamplerParameterIuiv (GLuint sampler, GLenum pname, const GLuint *param); +GLAPI void APIENTRY glGetSamplerParameteriv (GLuint sampler, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetSamplerParameterIiv (GLuint sampler, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetSamplerParameterfv (GLuint sampler, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetSamplerParameterIuiv (GLuint sampler, GLenum pname, GLuint *params); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLGENSAMPLERSPROC) (GLsizei count, GLuint *samplers); +typedef void (APIENTRYP PFNGLDELETESAMPLERSPROC) (GLsizei count, const GLuint *samplers); +typedef GLboolean (APIENTRYP PFNGLISSAMPLERPROC) (GLuint sampler); +typedef void (APIENTRYP PFNGLBINDSAMPLERPROC) (GLuint unit, GLuint sampler); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIPROC) (GLuint sampler, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIVPROC) (GLuint sampler, GLenum pname, const GLint *param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERFPROC) (GLuint sampler, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERFVPROC) (GLuint sampler, GLenum pname, const GLfloat *param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIIVPROC) (GLuint sampler, GLenum pname, const GLint *param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIUIVPROC) (GLuint sampler, GLenum pname, const GLuint *param); +typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIVPROC) (GLuint sampler, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIIVPROC) (GLuint sampler, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERFVPROC) (GLuint sampler, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIUIVPROC) (GLuint sampler, GLenum pname, GLuint *params); +#endif + +#ifndef GL_ARB_texture_rgb10_a2ui +#define GL_ARB_texture_rgb10_a2ui 1 +#endif + +#ifndef GL_ARB_texture_swizzle +#define GL_ARB_texture_swizzle 1 +#endif + +#ifndef GL_ARB_timer_query +#define GL_ARB_timer_query 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glQueryCounter (GLuint id, GLenum target); +GLAPI void APIENTRY glGetQueryObjecti64v (GLuint id, GLenum pname, GLint64 *params); +GLAPI void APIENTRY glGetQueryObjectui64v (GLuint id, GLenum pname, GLuint64 *params); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLQUERYCOUNTERPROC) (GLuint id, GLenum target); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTI64VPROC) (GLuint id, GLenum pname, GLint64 *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTUI64VPROC) (GLuint id, GLenum pname, GLuint64 *params); +#endif + +#ifndef GL_ARB_vertex_type_2_10_10_10_rev +#define GL_ARB_vertex_type_2_10_10_10_rev 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexP2ui (GLenum type, GLuint value); +GLAPI void APIENTRY glVertexP2uiv (GLenum type, const GLuint *value); +GLAPI void APIENTRY glVertexP3ui (GLenum type, GLuint value); +GLAPI void APIENTRY glVertexP3uiv (GLenum type, const GLuint *value); +GLAPI void APIENTRY glVertexP4ui (GLenum type, GLuint value); +GLAPI void APIENTRY glVertexP4uiv (GLenum type, const GLuint *value); +GLAPI void APIENTRY glTexCoordP1ui (GLenum type, GLuint coords); +GLAPI void APIENTRY glTexCoordP1uiv (GLenum type, const GLuint *coords); +GLAPI void APIENTRY glTexCoordP2ui (GLenum type, GLuint coords); +GLAPI void APIENTRY glTexCoordP2uiv (GLenum type, const GLuint *coords); +GLAPI void APIENTRY glTexCoordP3ui (GLenum type, GLuint coords); +GLAPI void APIENTRY glTexCoordP3uiv (GLenum type, const GLuint *coords); +GLAPI void APIENTRY glTexCoordP4ui (GLenum type, GLuint coords); +GLAPI void APIENTRY glTexCoordP4uiv (GLenum type, const GLuint *coords); +GLAPI void APIENTRY glMultiTexCoordP1ui (GLenum texture, GLenum type, GLuint coords); +GLAPI void APIENTRY glMultiTexCoordP1uiv (GLenum texture, GLenum type, const GLuint *coords); +GLAPI void APIENTRY glMultiTexCoordP2ui (GLenum texture, GLenum type, GLuint coords); +GLAPI void APIENTRY glMultiTexCoordP2uiv (GLenum texture, GLenum type, const GLuint *coords); +GLAPI void APIENTRY glMultiTexCoordP3ui (GLenum texture, GLenum type, GLuint coords); +GLAPI void APIENTRY glMultiTexCoordP3uiv (GLenum texture, GLenum type, const GLuint *coords); +GLAPI void APIENTRY glMultiTexCoordP4ui (GLenum texture, GLenum type, GLuint coords); +GLAPI void APIENTRY glMultiTexCoordP4uiv (GLenum texture, GLenum type, const GLuint *coords); +GLAPI void APIENTRY glNormalP3ui (GLenum type, GLuint coords); +GLAPI void APIENTRY glNormalP3uiv (GLenum type, const GLuint *coords); +GLAPI void APIENTRY glColorP3ui (GLenum type, GLuint color); +GLAPI void APIENTRY glColorP3uiv (GLenum type, const GLuint *color); +GLAPI void APIENTRY glColorP4ui (GLenum type, GLuint color); +GLAPI void APIENTRY glColorP4uiv (GLenum type, const GLuint *color); +GLAPI void APIENTRY glSecondaryColorP3ui (GLenum type, GLuint color); +GLAPI void APIENTRY glSecondaryColorP3uiv (GLenum type, const GLuint *color); +GLAPI void APIENTRY glVertexAttribP1ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); +GLAPI void APIENTRY glVertexAttribP1uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +GLAPI void APIENTRY glVertexAttribP2ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); +GLAPI void APIENTRY glVertexAttribP2uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +GLAPI void APIENTRY glVertexAttribP3ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); +GLAPI void APIENTRY glVertexAttribP3uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +GLAPI void APIENTRY glVertexAttribP4ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); +GLAPI void APIENTRY glVertexAttribP4uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLVERTEXP2UIPROC) (GLenum type, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXP2UIVPROC) (GLenum type, const GLuint *value); +typedef void (APIENTRYP PFNGLVERTEXP3UIPROC) (GLenum type, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXP3UIVPROC) (GLenum type, const GLuint *value); +typedef void (APIENTRYP PFNGLVERTEXP4UIPROC) (GLenum type, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXP4UIVPROC) (GLenum type, const GLuint *value); +typedef void (APIENTRYP PFNGLTEXCOORDP1UIPROC) (GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLTEXCOORDP1UIVPROC) (GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLTEXCOORDP2UIPROC) (GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLTEXCOORDP2UIVPROC) (GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLTEXCOORDP3UIPROC) (GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLTEXCOORDP3UIVPROC) (GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLTEXCOORDP4UIPROC) (GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLTEXCOORDP4UIVPROC) (GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP1UIPROC) (GLenum texture, GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP1UIVPROC) (GLenum texture, GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP2UIPROC) (GLenum texture, GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP2UIVPROC) (GLenum texture, GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP3UIPROC) (GLenum texture, GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP3UIVPROC) (GLenum texture, GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP4UIPROC) (GLenum texture, GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP4UIVPROC) (GLenum texture, GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLNORMALP3UIPROC) (GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLNORMALP3UIVPROC) (GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLCOLORP3UIPROC) (GLenum type, GLuint color); +typedef void (APIENTRYP PFNGLCOLORP3UIVPROC) (GLenum type, const GLuint *color); +typedef void (APIENTRYP PFNGLCOLORP4UIPROC) (GLenum type, GLuint color); +typedef void (APIENTRYP PFNGLCOLORP4UIVPROC) (GLenum type, const GLuint *color); +typedef void (APIENTRYP PFNGLSECONDARYCOLORP3UIPROC) (GLenum type, GLuint color); +typedef void (APIENTRYP PFNGLSECONDARYCOLORP3UIVPROC) (GLenum type, const GLuint *color); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP1UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP1UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP2UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP2UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP3UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP3UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP4UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP4UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +#endif + +#ifndef GL_ARB_draw_indirect +#define GL_ARB_draw_indirect 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawArraysIndirect (GLenum mode, const GLvoid *indirect); +GLAPI void APIENTRY glDrawElementsIndirect (GLenum mode, GLenum type, const GLvoid *indirect); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLDRAWARRAYSINDIRECTPROC) (GLenum mode, const GLvoid *indirect); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINDIRECTPROC) (GLenum mode, GLenum type, const GLvoid *indirect); +#endif + +#ifndef GL_ARB_gpu_shader5 +#define GL_ARB_gpu_shader5 1 +#endif + +#ifndef GL_ARB_gpu_shader_fp64 +#define GL_ARB_gpu_shader_fp64 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glUniform1d (GLint location, GLdouble x); +GLAPI void APIENTRY glUniform2d (GLint location, GLdouble x, GLdouble y); +GLAPI void APIENTRY glUniform3d (GLint location, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glUniform4d (GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glUniform1dv (GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glUniform2dv (GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glUniform3dv (GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glUniform4dv (GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix2dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix3dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix4dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix2x3dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix2x4dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix3x2dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix3x4dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix4x2dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix4x3dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glGetUniformdv (GLuint program, GLint location, GLdouble *params); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLUNIFORM1DPROC) (GLint location, GLdouble x); +typedef void (APIENTRYP PFNGLUNIFORM2DPROC) (GLint location, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLUNIFORM3DPROC) (GLint location, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLUNIFORM4DPROC) (GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLUNIFORM1DVPROC) (GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORM2DVPROC) (GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORM3DVPROC) (GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORM4DVPROC) (GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X3DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X4DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X2DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X4DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X2DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X3DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLGETUNIFORMDVPROC) (GLuint program, GLint location, GLdouble *params); +#endif + +#ifndef GL_ARB_shader_subroutine +#define GL_ARB_shader_subroutine 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLint APIENTRY glGetSubroutineUniformLocation (GLuint program, GLenum shadertype, const GLchar *name); +GLAPI GLuint APIENTRY glGetSubroutineIndex (GLuint program, GLenum shadertype, const GLchar *name); +GLAPI void APIENTRY glGetActiveSubroutineUniformiv (GLuint program, GLenum shadertype, GLuint index, GLenum pname, GLint *values); +GLAPI void APIENTRY glGetActiveSubroutineUniformName (GLuint program, GLenum shadertype, GLuint index, GLsizei bufsize, GLsizei *length, GLchar *name); +GLAPI void APIENTRY glGetActiveSubroutineName (GLuint program, GLenum shadertype, GLuint index, GLsizei bufsize, GLsizei *length, GLchar *name); +GLAPI void APIENTRY glUniformSubroutinesuiv (GLenum shadertype, GLsizei count, const GLuint *indices); +GLAPI void APIENTRY glGetUniformSubroutineuiv (GLenum shadertype, GLint location, GLuint *params); +GLAPI void APIENTRY glGetProgramStageiv (GLuint program, GLenum shadertype, GLenum pname, GLint *values); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef GLint (APIENTRYP PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC) (GLuint program, GLenum shadertype, const GLchar *name); +typedef GLuint (APIENTRYP PFNGLGETSUBROUTINEINDEXPROC) (GLuint program, GLenum shadertype, const GLchar *name); +typedef void (APIENTRYP PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC) (GLuint program, GLenum shadertype, GLuint index, GLenum pname, GLint *values); +typedef void (APIENTRYP PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC) (GLuint program, GLenum shadertype, GLuint index, GLsizei bufsize, GLsizei *length, GLchar *name); +typedef void (APIENTRYP PFNGLGETACTIVESUBROUTINENAMEPROC) (GLuint program, GLenum shadertype, GLuint index, GLsizei bufsize, GLsizei *length, GLchar *name); +typedef void (APIENTRYP PFNGLUNIFORMSUBROUTINESUIVPROC) (GLenum shadertype, GLsizei count, const GLuint *indices); +typedef void (APIENTRYP PFNGLGETUNIFORMSUBROUTINEUIVPROC) (GLenum shadertype, GLint location, GLuint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMSTAGEIVPROC) (GLuint program, GLenum shadertype, GLenum pname, GLint *values); +#endif + +#ifndef GL_ARB_tessellation_shader +#define GL_ARB_tessellation_shader 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPatchParameteri (GLenum pname, GLint value); +GLAPI void APIENTRY glPatchParameterfv (GLenum pname, const GLfloat *values); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLPATCHPARAMETERIPROC) (GLenum pname, GLint value); +typedef void (APIENTRYP PFNGLPATCHPARAMETERFVPROC) (GLenum pname, const GLfloat *values); +#endif + +#ifndef GL_ARB_texture_buffer_object_rgb32 +#define GL_ARB_texture_buffer_object_rgb32 1 +#endif + +#ifndef GL_ARB_transform_feedback2 +#define GL_ARB_transform_feedback2 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBindTransformFeedback (GLenum target, GLuint id); +GLAPI void APIENTRY glDeleteTransformFeedbacks (GLsizei n, const GLuint *ids); +GLAPI void APIENTRY glGenTransformFeedbacks (GLsizei n, GLuint *ids); +GLAPI GLboolean APIENTRY glIsTransformFeedback (GLuint id); +GLAPI void APIENTRY glPauseTransformFeedback (void); +GLAPI void APIENTRY glResumeTransformFeedback (void); +GLAPI void APIENTRY glDrawTransformFeedback (GLenum mode, GLuint id); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBINDTRANSFORMFEEDBACKPROC) (GLenum target, GLuint id); +typedef void (APIENTRYP PFNGLDELETETRANSFORMFEEDBACKSPROC) (GLsizei n, const GLuint *ids); +typedef void (APIENTRYP PFNGLGENTRANSFORMFEEDBACKSPROC) (GLsizei n, GLuint *ids); +typedef GLboolean (APIENTRYP PFNGLISTRANSFORMFEEDBACKPROC) (GLuint id); +typedef void (APIENTRYP PFNGLPAUSETRANSFORMFEEDBACKPROC) (void); +typedef void (APIENTRYP PFNGLRESUMETRANSFORMFEEDBACKPROC) (void); +typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKPROC) (GLenum mode, GLuint id); +#endif + +#ifndef GL_ARB_transform_feedback3 +#define GL_ARB_transform_feedback3 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawTransformFeedbackStream (GLenum mode, GLuint id, GLuint stream); +GLAPI void APIENTRY glBeginQueryIndexed (GLenum target, GLuint index, GLuint id); +GLAPI void APIENTRY glEndQueryIndexed (GLenum target, GLuint index); +GLAPI void APIENTRY glGetQueryIndexediv (GLenum target, GLuint index, GLenum pname, GLint *params); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC) (GLenum mode, GLuint id, GLuint stream); +typedef void (APIENTRYP PFNGLBEGINQUERYINDEXEDPROC) (GLenum target, GLuint index, GLuint id); +typedef void (APIENTRYP PFNGLENDQUERYINDEXEDPROC) (GLenum target, GLuint index); +typedef void (APIENTRYP PFNGLGETQUERYINDEXEDIVPROC) (GLenum target, GLuint index, GLenum pname, GLint *params); +#endif + +#ifndef GL_ARB_ES2_compatibility +#define GL_ARB_ES2_compatibility 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glReleaseShaderCompiler (void); +GLAPI void APIENTRY glShaderBinary (GLsizei count, const GLuint *shaders, GLenum binaryformat, const GLvoid *binary, GLsizei length); +GLAPI void APIENTRY glGetShaderPrecisionFormat (GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision); +GLAPI void APIENTRY glDepthRangef (GLclampf n, GLclampf f); +GLAPI void APIENTRY glClearDepthf (GLclampf d); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLRELEASESHADERCOMPILERPROC) (void); +typedef void (APIENTRYP PFNGLSHADERBINARYPROC) (GLsizei count, const GLuint *shaders, GLenum binaryformat, const GLvoid *binary, GLsizei length); +typedef void (APIENTRYP PFNGLGETSHADERPRECISIONFORMATPROC) (GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision); +typedef void (APIENTRYP PFNGLDEPTHRANGEFPROC) (GLclampf n, GLclampf f); +typedef void (APIENTRYP PFNGLCLEARDEPTHFPROC) (GLclampf d); +#endif + +#ifndef GL_ARB_get_program_binary +#define GL_ARB_get_program_binary 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetProgramBinary (GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, GLvoid *binary); +GLAPI void APIENTRY glProgramBinary (GLuint program, GLenum binaryFormat, const GLvoid *binary, GLsizei length); +GLAPI void APIENTRY glProgramParameteri (GLuint program, GLenum pname, GLint value); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLGETPROGRAMBINARYPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, GLvoid *binary); +typedef void (APIENTRYP PFNGLPROGRAMBINARYPROC) (GLuint program, GLenum binaryFormat, const GLvoid *binary, GLsizei length); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETERIPROC) (GLuint program, GLenum pname, GLint value); +#endif + +#ifndef GL_ARB_separate_shader_objects +#define GL_ARB_separate_shader_objects 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glUseProgramStages (GLuint pipeline, GLbitfield stages, GLuint program); +GLAPI void APIENTRY glActiveShaderProgram (GLuint pipeline, GLuint program); +GLAPI GLuint APIENTRY glCreateShaderProgramv (GLenum type, GLsizei count, const GLchar* *strings); +GLAPI void APIENTRY glBindProgramPipeline (GLuint pipeline); +GLAPI void APIENTRY glDeleteProgramPipelines (GLsizei n, const GLuint *pipelines); +GLAPI void APIENTRY glGenProgramPipelines (GLsizei n, GLuint *pipelines); +GLAPI GLboolean APIENTRY glIsProgramPipeline (GLuint pipeline); +GLAPI void APIENTRY glGetProgramPipelineiv (GLuint pipeline, GLenum pname, GLint *params); +GLAPI void APIENTRY glProgramUniform1i (GLuint program, GLint location, GLint v0); +GLAPI void APIENTRY glProgramUniform1iv (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform1f (GLuint program, GLint location, GLfloat v0); +GLAPI void APIENTRY glProgramUniform1fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform1d (GLuint program, GLint location, GLdouble v0); +GLAPI void APIENTRY glProgramUniform1dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform1ui (GLuint program, GLint location, GLuint v0); +GLAPI void APIENTRY glProgramUniform1uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniform2i (GLuint program, GLint location, GLint v0, GLint v1); +GLAPI void APIENTRY glProgramUniform2iv (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform2f (GLuint program, GLint location, GLfloat v0, GLfloat v1); +GLAPI void APIENTRY glProgramUniform2fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform2d (GLuint program, GLint location, GLdouble v0, GLdouble v1); +GLAPI void APIENTRY glProgramUniform2dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform2ui (GLuint program, GLint location, GLuint v0, GLuint v1); +GLAPI void APIENTRY glProgramUniform2uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniform3i (GLuint program, GLint location, GLint v0, GLint v1, GLint v2); +GLAPI void APIENTRY glProgramUniform3iv (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform3f (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +GLAPI void APIENTRY glProgramUniform3fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform3d (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2); +GLAPI void APIENTRY glProgramUniform3dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform3ui (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); +GLAPI void APIENTRY glProgramUniform3uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniform4i (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +GLAPI void APIENTRY glProgramUniform4iv (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform4f (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +GLAPI void APIENTRY glProgramUniform4fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform4d (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2, GLdouble v3); +GLAPI void APIENTRY glProgramUniform4dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform4ui (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +GLAPI void APIENTRY glProgramUniform4uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniformMatrix2fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix3fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix4fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix2dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix3dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix4dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix2x3fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix3x2fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix2x4fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix4x2fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix3x4fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix4x3fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix2x3dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix3x2dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix2x4dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix4x2dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix3x4dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix4x3dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glValidateProgramPipeline (GLuint pipeline); +GLAPI void APIENTRY glGetProgramPipelineInfoLog (GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLUSEPROGRAMSTAGESPROC) (GLuint pipeline, GLbitfield stages, GLuint program); +typedef void (APIENTRYP PFNGLACTIVESHADERPROGRAMPROC) (GLuint pipeline, GLuint program); +typedef GLuint (APIENTRYP PFNGLCREATESHADERPROGRAMVPROC) (GLenum type, GLsizei count, const GLchar* *strings); +typedef void (APIENTRYP PFNGLBINDPROGRAMPIPELINEPROC) (GLuint pipeline); +typedef void (APIENTRYP PFNGLDELETEPROGRAMPIPELINESPROC) (GLsizei n, const GLuint *pipelines); +typedef void (APIENTRYP PFNGLGENPROGRAMPIPELINESPROC) (GLsizei n, GLuint *pipelines); +typedef GLboolean (APIENTRYP PFNGLISPROGRAMPIPELINEPROC) (GLuint pipeline); +typedef void (APIENTRYP PFNGLGETPROGRAMPIPELINEIVPROC) (GLuint pipeline, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IPROC) (GLuint program, GLint location, GLint v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FPROC) (GLuint program, GLint location, GLfloat v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DPROC) (GLuint program, GLint location, GLdouble v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIPROC) (GLuint program, GLint location, GLuint v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IPROC) (GLuint program, GLint location, GLint v0, GLint v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DPROC) (GLuint program, GLint location, GLdouble v0, GLdouble v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIPROC) (GLuint program, GLint location, GLuint v0, GLuint v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DPROC) (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DPROC) (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2, GLdouble v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLVALIDATEPROGRAMPIPELINEPROC) (GLuint pipeline); +typedef void (APIENTRYP PFNGLGETPROGRAMPIPELINEINFOLOGPROC) (GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +#endif + +#ifndef GL_ARB_vertex_attrib_64bit +#define GL_ARB_vertex_attrib_64bit 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexAttribL1d (GLuint index, GLdouble x); +GLAPI void APIENTRY glVertexAttribL2d (GLuint index, GLdouble x, GLdouble y); +GLAPI void APIENTRY glVertexAttribL3d (GLuint index, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glVertexAttribL4d (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glVertexAttribL1dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribL2dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribL3dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribL4dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribLPointer (GLuint index, GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +GLAPI void APIENTRY glGetVertexAttribLdv (GLuint index, GLenum pname, GLdouble *params); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1DPROC) (GLuint index, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2DPROC) (GLuint index, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBLPOINTERPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLDVPROC) (GLuint index, GLenum pname, GLdouble *params); +#endif + +#ifndef GL_ARB_viewport_array +#define GL_ARB_viewport_array 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glViewportArrayv (GLuint first, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glViewportIndexedf (GLuint index, GLfloat x, GLfloat y, GLfloat w, GLfloat h); +GLAPI void APIENTRY glViewportIndexedfv (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glScissorArrayv (GLuint first, GLsizei count, const GLint *v); +GLAPI void APIENTRY glScissorIndexed (GLuint index, GLint left, GLint bottom, GLsizei width, GLsizei height); +GLAPI void APIENTRY glScissorIndexedv (GLuint index, const GLint *v); +GLAPI void APIENTRY glDepthRangeArrayv (GLuint first, GLsizei count, const GLclampd *v); +GLAPI void APIENTRY glDepthRangeIndexed (GLuint index, GLclampd n, GLclampd f); +GLAPI void APIENTRY glGetFloati_v (GLenum target, GLuint index, GLfloat *data); +GLAPI void APIENTRY glGetDoublei_v (GLenum target, GLuint index, GLdouble *data); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLVIEWPORTARRAYVPROC) (GLuint first, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLVIEWPORTINDEXEDFPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat w, GLfloat h); +typedef void (APIENTRYP PFNGLVIEWPORTINDEXEDFVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLSCISSORARRAYVPROC) (GLuint first, GLsizei count, const GLint *v); +typedef void (APIENTRYP PFNGLSCISSORINDEXEDPROC) (GLuint index, GLint left, GLint bottom, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLSCISSORINDEXEDVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLDEPTHRANGEARRAYVPROC) (GLuint first, GLsizei count, const GLclampd *v); +typedef void (APIENTRYP PFNGLDEPTHRANGEINDEXEDPROC) (GLuint index, GLclampd n, GLclampd f); +typedef void (APIENTRYP PFNGLGETFLOATI_VPROC) (GLenum target, GLuint index, GLfloat *data); +typedef void (APIENTRYP PFNGLGETDOUBLEI_VPROC) (GLenum target, GLuint index, GLdouble *data); +#endif + +#ifndef GL_ARB_cl_event +#define GL_ARB_cl_event 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLsync APIENTRY glCreateSyncFromCLeventARB (struct _cl_context * context, struct _cl_event * event, GLbitfield flags); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef GLsync (APIENTRYP PFNGLCREATESYNCFROMCLEVENTARBPROC) (struct _cl_context * context, struct _cl_event * event, GLbitfield flags); +#endif + +#ifndef GL_ARB_debug_output +#define GL_ARB_debug_output 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDebugMessageControlARB (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +GLAPI void APIENTRY glDebugMessageInsertARB (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); +GLAPI void APIENTRY glDebugMessageCallbackARB (GLDEBUGPROCARB callback, const GLvoid *userParam); +GLAPI GLuint APIENTRY glGetDebugMessageLogARB (GLuint count, GLsizei bufsize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLARBPROC) (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTARBPROC) (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); +typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKARBPROC) (GLDEBUGPROCARB callback, const GLvoid *userParam); +typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGARBPROC) (GLuint count, GLsizei bufsize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +#endif + +#ifndef GL_ARB_robustness +#define GL_ARB_robustness 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLenum APIENTRY glGetGraphicsResetStatusARB (void); +GLAPI void APIENTRY glGetnMapdvARB (GLenum target, GLenum query, GLsizei bufSize, GLdouble *v); +GLAPI void APIENTRY glGetnMapfvARB (GLenum target, GLenum query, GLsizei bufSize, GLfloat *v); +GLAPI void APIENTRY glGetnMapivARB (GLenum target, GLenum query, GLsizei bufSize, GLint *v); +GLAPI void APIENTRY glGetnPixelMapfvARB (GLenum map, GLsizei bufSize, GLfloat *values); +GLAPI void APIENTRY glGetnPixelMapuivARB (GLenum map, GLsizei bufSize, GLuint *values); +GLAPI void APIENTRY glGetnPixelMapusvARB (GLenum map, GLsizei bufSize, GLushort *values); +GLAPI void APIENTRY glGetnPolygonStippleARB (GLsizei bufSize, GLubyte *pattern); +GLAPI void APIENTRY glGetnColorTableARB (GLenum target, GLenum format, GLenum type, GLsizei bufSize, GLvoid *table); +GLAPI void APIENTRY glGetnConvolutionFilterARB (GLenum target, GLenum format, GLenum type, GLsizei bufSize, GLvoid *image); +GLAPI void APIENTRY glGetnSeparableFilterARB (GLenum target, GLenum format, GLenum type, GLsizei rowBufSize, GLvoid *row, GLsizei columnBufSize, GLvoid *column, GLvoid *span); +GLAPI void APIENTRY glGetnHistogramARB (GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, GLvoid *values); +GLAPI void APIENTRY glGetnMinmaxARB (GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, GLvoid *values); +GLAPI void APIENTRY glGetnTexImageARB (GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, GLvoid *img); +GLAPI void APIENTRY glReadnPixelsARB (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, GLvoid *data); +GLAPI void APIENTRY glGetnCompressedTexImageARB (GLenum target, GLint lod, GLsizei bufSize, GLvoid *img); +GLAPI void APIENTRY glGetnUniformfvARB (GLuint program, GLint location, GLsizei bufSize, GLfloat *params); +GLAPI void APIENTRY glGetnUniformivARB (GLuint program, GLint location, GLsizei bufSize, GLint *params); +GLAPI void APIENTRY glGetnUniformuivARB (GLuint program, GLint location, GLsizei bufSize, GLuint *params); +GLAPI void APIENTRY glGetnUniformdvARB (GLuint program, GLint location, GLsizei bufSize, GLdouble *params); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef GLenum (APIENTRYP PFNGLGETGRAPHICSRESETSTATUSARBPROC) (void); +typedef void (APIENTRYP PFNGLGETNMAPDVARBPROC) (GLenum target, GLenum query, GLsizei bufSize, GLdouble *v); +typedef void (APIENTRYP PFNGLGETNMAPFVARBPROC) (GLenum target, GLenum query, GLsizei bufSize, GLfloat *v); +typedef void (APIENTRYP PFNGLGETNMAPIVARBPROC) (GLenum target, GLenum query, GLsizei bufSize, GLint *v); +typedef void (APIENTRYP PFNGLGETNPIXELMAPFVARBPROC) (GLenum map, GLsizei bufSize, GLfloat *values); +typedef void (APIENTRYP PFNGLGETNPIXELMAPUIVARBPROC) (GLenum map, GLsizei bufSize, GLuint *values); +typedef void (APIENTRYP PFNGLGETNPIXELMAPUSVARBPROC) (GLenum map, GLsizei bufSize, GLushort *values); +typedef void (APIENTRYP PFNGLGETNPOLYGONSTIPPLEARBPROC) (GLsizei bufSize, GLubyte *pattern); +typedef void (APIENTRYP PFNGLGETNCOLORTABLEARBPROC) (GLenum target, GLenum format, GLenum type, GLsizei bufSize, GLvoid *table); +typedef void (APIENTRYP PFNGLGETNCONVOLUTIONFILTERARBPROC) (GLenum target, GLenum format, GLenum type, GLsizei bufSize, GLvoid *image); +typedef void (APIENTRYP PFNGLGETNSEPARABLEFILTERARBPROC) (GLenum target, GLenum format, GLenum type, GLsizei rowBufSize, GLvoid *row, GLsizei columnBufSize, GLvoid *column, GLvoid *span); +typedef void (APIENTRYP PFNGLGETNHISTOGRAMARBPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, GLvoid *values); +typedef void (APIENTRYP PFNGLGETNMINMAXARBPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, GLvoid *values); +typedef void (APIENTRYP PFNGLGETNTEXIMAGEARBPROC) (GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, GLvoid *img); +typedef void (APIENTRYP PFNGLREADNPIXELSARBPROC) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, GLvoid *data); +typedef void (APIENTRYP PFNGLGETNCOMPRESSEDTEXIMAGEARBPROC) (GLenum target, GLint lod, GLsizei bufSize, GLvoid *img); +typedef void (APIENTRYP PFNGLGETNUNIFORMFVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLfloat *params); +typedef void (APIENTRYP PFNGLGETNUNIFORMIVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLint *params); +typedef void (APIENTRYP PFNGLGETNUNIFORMUIVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLuint *params); +typedef void (APIENTRYP PFNGLGETNUNIFORMDVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLdouble *params); +#endif + +#ifndef GL_ARB_shader_stencil_export +#define GL_ARB_shader_stencil_export 1 +#endif + +#ifndef GL_EXT_abgr +#define GL_EXT_abgr 1 +#endif + +#ifndef GL_EXT_blend_color +#define GL_EXT_blend_color 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendColorEXT (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBLENDCOLOREXTPROC) (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); +#endif + +#ifndef GL_EXT_polygon_offset +#define GL_EXT_polygon_offset 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPolygonOffsetEXT (GLfloat factor, GLfloat bias); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLPOLYGONOFFSETEXTPROC) (GLfloat factor, GLfloat bias); +#endif + +#ifndef GL_EXT_texture +#define GL_EXT_texture 1 +#endif + +#ifndef GL_EXT_texture3D +#define GL_EXT_texture3D 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexImage3DEXT (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +GLAPI void APIENTRY glTexSubImage3DEXT (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLTEXIMAGE3DEXTPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE3DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels); +#endif + +#ifndef GL_SGIS_texture_filter4 +#define GL_SGIS_texture_filter4 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetTexFilterFuncSGIS (GLenum target, GLenum filter, GLfloat *weights); +GLAPI void APIENTRY glTexFilterFuncSGIS (GLenum target, GLenum filter, GLsizei n, const GLfloat *weights); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLGETTEXFILTERFUNCSGISPROC) (GLenum target, GLenum filter, GLfloat *weights); +typedef void (APIENTRYP PFNGLTEXFILTERFUNCSGISPROC) (GLenum target, GLenum filter, GLsizei n, const GLfloat *weights); +#endif + +#ifndef GL_EXT_subtexture +#define GL_EXT_subtexture 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexSubImage1DEXT (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const GLvoid *pixels); +GLAPI void APIENTRY glTexSubImage2DEXT (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLTEXSUBIMAGE1DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const GLvoid *pixels); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE2DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels); +#endif + +#ifndef GL_EXT_copy_texture +#define GL_EXT_copy_texture 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCopyTexImage1DEXT (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +GLAPI void APIENTRY glCopyTexImage2DEXT (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +GLAPI void APIENTRY glCopyTexSubImage1DEXT (GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glCopyTexSubImage2DEXT (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glCopyTexSubImage3DEXT (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLCOPYTEXIMAGE1DEXTPROC) (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +typedef void (APIENTRYP PFNGLCOPYTEXIMAGE2DEXTPROC) (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE1DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE2DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE3DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +#endif + +#ifndef GL_EXT_histogram +#define GL_EXT_histogram 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetHistogramEXT (GLenum target, GLboolean reset, GLenum format, GLenum type, GLvoid *values); +GLAPI void APIENTRY glGetHistogramParameterfvEXT (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetHistogramParameterivEXT (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetMinmaxEXT (GLenum target, GLboolean reset, GLenum format, GLenum type, GLvoid *values); +GLAPI void APIENTRY glGetMinmaxParameterfvEXT (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetMinmaxParameterivEXT (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glHistogramEXT (GLenum target, GLsizei width, GLenum internalformat, GLboolean sink); +GLAPI void APIENTRY glMinmaxEXT (GLenum target, GLenum internalformat, GLboolean sink); +GLAPI void APIENTRY glResetHistogramEXT (GLenum target); +GLAPI void APIENTRY glResetMinmaxEXT (GLenum target); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLGETHISTOGRAMEXTPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, GLvoid *values); +typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETMINMAXEXTPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, GLvoid *values); +typedef void (APIENTRYP PFNGLGETMINMAXPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETMINMAXPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLHISTOGRAMEXTPROC) (GLenum target, GLsizei width, GLenum internalformat, GLboolean sink); +typedef void (APIENTRYP PFNGLMINMAXEXTPROC) (GLenum target, GLenum internalformat, GLboolean sink); +typedef void (APIENTRYP PFNGLRESETHISTOGRAMEXTPROC) (GLenum target); +typedef void (APIENTRYP PFNGLRESETMINMAXEXTPROC) (GLenum target); +#endif + +#ifndef GL_EXT_convolution +#define GL_EXT_convolution 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glConvolutionFilter1DEXT (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const GLvoid *image); +GLAPI void APIENTRY glConvolutionFilter2DEXT (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *image); +GLAPI void APIENTRY glConvolutionParameterfEXT (GLenum target, GLenum pname, GLfloat params); +GLAPI void APIENTRY glConvolutionParameterfvEXT (GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glConvolutionParameteriEXT (GLenum target, GLenum pname, GLint params); +GLAPI void APIENTRY glConvolutionParameterivEXT (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glCopyConvolutionFilter1DEXT (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glCopyConvolutionFilter2DEXT (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetConvolutionFilterEXT (GLenum target, GLenum format, GLenum type, GLvoid *image); +GLAPI void APIENTRY glGetConvolutionParameterfvEXT (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetConvolutionParameterivEXT (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetSeparableFilterEXT (GLenum target, GLenum format, GLenum type, GLvoid *row, GLvoid *column, GLvoid *span); +GLAPI void APIENTRY glSeparableFilter2DEXT (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *row, const GLvoid *column); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLCONVOLUTIONFILTER1DEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const GLvoid *image); +typedef void (APIENTRYP PFNGLCONVOLUTIONFILTER2DEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *image); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFEXTPROC) (GLenum target, GLenum pname, GLfloat params); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFVEXTPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIEXTPROC) (GLenum target, GLenum pname, GLint params); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIVEXTPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER1DEXTPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER2DEXTPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONFILTEREXTPROC) (GLenum target, GLenum format, GLenum type, GLvoid *image); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETSEPARABLEFILTEREXTPROC) (GLenum target, GLenum format, GLenum type, GLvoid *row, GLvoid *column, GLvoid *span); +typedef void (APIENTRYP PFNGLSEPARABLEFILTER2DEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *row, const GLvoid *column); +#endif + +#ifndef GL_SGI_color_matrix +#define GL_SGI_color_matrix 1 +#endif + +#ifndef GL_SGI_color_table +#define GL_SGI_color_table 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColorTableSGI (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const GLvoid *table); +GLAPI void APIENTRY glColorTableParameterfvSGI (GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glColorTableParameterivSGI (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glCopyColorTableSGI (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glGetColorTableSGI (GLenum target, GLenum format, GLenum type, GLvoid *table); +GLAPI void APIENTRY glGetColorTableParameterfvSGI (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetColorTableParameterivSGI (GLenum target, GLenum pname, GLint *params); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLCOLORTABLESGIPROC) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const GLvoid *table); +typedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERFVSGIPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERIVSGIPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLCOPYCOLORTABLESGIPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLGETCOLORTABLESGIPROC) (GLenum target, GLenum format, GLenum type, GLvoid *table); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERFVSGIPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERIVSGIPROC) (GLenum target, GLenum pname, GLint *params); +#endif + +#ifndef GL_SGIX_pixel_texture +#define GL_SGIX_pixel_texture 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPixelTexGenSGIX (GLenum mode); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLPIXELTEXGENSGIXPROC) (GLenum mode); +#endif + +#ifndef GL_SGIS_pixel_texture +#define GL_SGIS_pixel_texture 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPixelTexGenParameteriSGIS (GLenum pname, GLint param); +GLAPI void APIENTRY glPixelTexGenParameterivSGIS (GLenum pname, const GLint *params); +GLAPI void APIENTRY glPixelTexGenParameterfSGIS (GLenum pname, GLfloat param); +GLAPI void APIENTRY glPixelTexGenParameterfvSGIS (GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glGetPixelTexGenParameterivSGIS (GLenum pname, GLint *params); +GLAPI void APIENTRY glGetPixelTexGenParameterfvSGIS (GLenum pname, GLfloat *params); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERISGISPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERIVSGISPROC) (GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERFSGISPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERFVSGISPROC) (GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLGETPIXELTEXGENPARAMETERIVSGISPROC) (GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETPIXELTEXGENPARAMETERFVSGISPROC) (GLenum pname, GLfloat *params); +#endif + +#ifndef GL_SGIS_texture4D +#define GL_SGIS_texture4D 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexImage4DSGIS (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLsizei size4d, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +GLAPI void APIENTRY glTexSubImage4DSGIS (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint woffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei size4d, GLenum format, GLenum type, const GLvoid *pixels); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLTEXIMAGE4DSGISPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLsizei size4d, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE4DSGISPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint woffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei size4d, GLenum format, GLenum type, const GLvoid *pixels); +#endif + +#ifndef GL_SGI_texture_color_table +#define GL_SGI_texture_color_table 1 +#endif + +#ifndef GL_EXT_cmyka +#define GL_EXT_cmyka 1 +#endif + +#ifndef GL_EXT_texture_object +#define GL_EXT_texture_object 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLboolean APIENTRY glAreTexturesResidentEXT (GLsizei n, const GLuint *textures, GLboolean *residences); +GLAPI void APIENTRY glBindTextureEXT (GLenum target, GLuint texture); +GLAPI void APIENTRY glDeleteTexturesEXT (GLsizei n, const GLuint *textures); +GLAPI void APIENTRY glGenTexturesEXT (GLsizei n, GLuint *textures); +GLAPI GLboolean APIENTRY glIsTextureEXT (GLuint texture); +GLAPI void APIENTRY glPrioritizeTexturesEXT (GLsizei n, const GLuint *textures, const GLclampf *priorities); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef GLboolean (APIENTRYP PFNGLARETEXTURESRESIDENTEXTPROC) (GLsizei n, const GLuint *textures, GLboolean *residences); +typedef void (APIENTRYP PFNGLBINDTEXTUREEXTPROC) (GLenum target, GLuint texture); +typedef void (APIENTRYP PFNGLDELETETEXTURESEXTPROC) (GLsizei n, const GLuint *textures); +typedef void (APIENTRYP PFNGLGENTEXTURESEXTPROC) (GLsizei n, GLuint *textures); +typedef GLboolean (APIENTRYP PFNGLISTEXTUREEXTPROC) (GLuint texture); +typedef void (APIENTRYP PFNGLPRIORITIZETEXTURESEXTPROC) (GLsizei n, const GLuint *textures, const GLclampf *priorities); +#endif + +#ifndef GL_SGIS_detail_texture +#define GL_SGIS_detail_texture 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDetailTexFuncSGIS (GLenum target, GLsizei n, const GLfloat *points); +GLAPI void APIENTRY glGetDetailTexFuncSGIS (GLenum target, GLfloat *points); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLDETAILTEXFUNCSGISPROC) (GLenum target, GLsizei n, const GLfloat *points); +typedef void (APIENTRYP PFNGLGETDETAILTEXFUNCSGISPROC) (GLenum target, GLfloat *points); +#endif + +#ifndef GL_SGIS_sharpen_texture +#define GL_SGIS_sharpen_texture 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSharpenTexFuncSGIS (GLenum target, GLsizei n, const GLfloat *points); +GLAPI void APIENTRY glGetSharpenTexFuncSGIS (GLenum target, GLfloat *points); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLSHARPENTEXFUNCSGISPROC) (GLenum target, GLsizei n, const GLfloat *points); +typedef void (APIENTRYP PFNGLGETSHARPENTEXFUNCSGISPROC) (GLenum target, GLfloat *points); +#endif + +#ifndef GL_EXT_packed_pixels +#define GL_EXT_packed_pixels 1 +#endif + +#ifndef GL_SGIS_texture_lod +#define GL_SGIS_texture_lod 1 +#endif + +#ifndef GL_SGIS_multisample +#define GL_SGIS_multisample 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSampleMaskSGIS (GLclampf value, GLboolean invert); +GLAPI void APIENTRY glSamplePatternSGIS (GLenum pattern); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLSAMPLEMASKSGISPROC) (GLclampf value, GLboolean invert); +typedef void (APIENTRYP PFNGLSAMPLEPATTERNSGISPROC) (GLenum pattern); +#endif + +#ifndef GL_EXT_rescale_normal +#define GL_EXT_rescale_normal 1 +#endif + +#ifndef GL_EXT_vertex_array +#define GL_EXT_vertex_array 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glArrayElementEXT (GLint i); +GLAPI void APIENTRY glColorPointerEXT (GLint size, GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer); +GLAPI void APIENTRY glDrawArraysEXT (GLenum mode, GLint first, GLsizei count); +GLAPI void APIENTRY glEdgeFlagPointerEXT (GLsizei stride, GLsizei count, const GLboolean *pointer); +GLAPI void APIENTRY glGetPointervEXT (GLenum pname, GLvoid* *params); +GLAPI void APIENTRY glIndexPointerEXT (GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer); +GLAPI void APIENTRY glNormalPointerEXT (GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer); +GLAPI void APIENTRY glTexCoordPointerEXT (GLint size, GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer); +GLAPI void APIENTRY glVertexPointerEXT (GLint size, GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLARRAYELEMENTEXTPROC) (GLint i); +typedef void (APIENTRYP PFNGLCOLORPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer); +typedef void (APIENTRYP PFNGLDRAWARRAYSEXTPROC) (GLenum mode, GLint first, GLsizei count); +typedef void (APIENTRYP PFNGLEDGEFLAGPOINTEREXTPROC) (GLsizei stride, GLsizei count, const GLboolean *pointer); +typedef void (APIENTRYP PFNGLGETPOINTERVEXTPROC) (GLenum pname, GLvoid* *params); +typedef void (APIENTRYP PFNGLINDEXPOINTEREXTPROC) (GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer); +typedef void (APIENTRYP PFNGLNORMALPOINTEREXTPROC) (GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer); +typedef void (APIENTRYP PFNGLTEXCOORDPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer); +typedef void (APIENTRYP PFNGLVERTEXPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer); +#endif + +#ifndef GL_EXT_misc_attribute +#define GL_EXT_misc_attribute 1 +#endif + +#ifndef GL_SGIS_generate_mipmap +#define GL_SGIS_generate_mipmap 1 +#endif + +#ifndef GL_SGIX_clipmap +#define GL_SGIX_clipmap 1 +#endif + +#ifndef GL_SGIX_shadow +#define GL_SGIX_shadow 1 +#endif + +#ifndef GL_SGIS_texture_edge_clamp +#define GL_SGIS_texture_edge_clamp 1 +#endif + +#ifndef GL_SGIS_texture_border_clamp +#define GL_SGIS_texture_border_clamp 1 +#endif + +#ifndef GL_EXT_blend_minmax +#define GL_EXT_blend_minmax 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendEquationEXT (GLenum mode); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBLENDEQUATIONEXTPROC) (GLenum mode); +#endif + +#ifndef GL_EXT_blend_subtract +#define GL_EXT_blend_subtract 1 +#endif + +#ifndef GL_EXT_blend_logic_op +#define GL_EXT_blend_logic_op 1 +#endif + +#ifndef GL_SGIX_interlace +#define GL_SGIX_interlace 1 +#endif + +#ifndef GL_SGIX_pixel_tiles +#define GL_SGIX_pixel_tiles 1 +#endif + +#ifndef GL_SGIX_texture_select +#define GL_SGIX_texture_select 1 +#endif + +#ifndef GL_SGIX_sprite +#define GL_SGIX_sprite 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSpriteParameterfSGIX (GLenum pname, GLfloat param); +GLAPI void APIENTRY glSpriteParameterfvSGIX (GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glSpriteParameteriSGIX (GLenum pname, GLint param); +GLAPI void APIENTRY glSpriteParameterivSGIX (GLenum pname, const GLint *params); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLSPRITEPARAMETERFSGIXPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLSPRITEPARAMETERFVSGIXPROC) (GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLSPRITEPARAMETERISGIXPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLSPRITEPARAMETERIVSGIXPROC) (GLenum pname, const GLint *params); +#endif + +#ifndef GL_SGIX_texture_multi_buffer +#define GL_SGIX_texture_multi_buffer 1 +#endif + +#ifndef GL_EXT_point_parameters +#define GL_EXT_point_parameters 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPointParameterfEXT (GLenum pname, GLfloat param); +GLAPI void APIENTRY glPointParameterfvEXT (GLenum pname, const GLfloat *params); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLPOINTPARAMETERFEXTPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFVEXTPROC) (GLenum pname, const GLfloat *params); +#endif + +#ifndef GL_SGIS_point_parameters +#define GL_SGIS_point_parameters 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPointParameterfSGIS (GLenum pname, GLfloat param); +GLAPI void APIENTRY glPointParameterfvSGIS (GLenum pname, const GLfloat *params); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLPOINTPARAMETERFSGISPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFVSGISPROC) (GLenum pname, const GLfloat *params); +#endif + +#ifndef GL_SGIX_instruments +#define GL_SGIX_instruments 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLint APIENTRY glGetInstrumentsSGIX (void); +GLAPI void APIENTRY glInstrumentsBufferSGIX (GLsizei size, GLint *buffer); +GLAPI GLint APIENTRY glPollInstrumentsSGIX (GLint *marker_p); +GLAPI void APIENTRY glReadInstrumentsSGIX (GLint marker); +GLAPI void APIENTRY glStartInstrumentsSGIX (void); +GLAPI void APIENTRY glStopInstrumentsSGIX (GLint marker); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef GLint (APIENTRYP PFNGLGETINSTRUMENTSSGIXPROC) (void); +typedef void (APIENTRYP PFNGLINSTRUMENTSBUFFERSGIXPROC) (GLsizei size, GLint *buffer); +typedef GLint (APIENTRYP PFNGLPOLLINSTRUMENTSSGIXPROC) (GLint *marker_p); +typedef void (APIENTRYP PFNGLREADINSTRUMENTSSGIXPROC) (GLint marker); +typedef void (APIENTRYP PFNGLSTARTINSTRUMENTSSGIXPROC) (void); +typedef void (APIENTRYP PFNGLSTOPINSTRUMENTSSGIXPROC) (GLint marker); +#endif + +#ifndef GL_SGIX_texture_scale_bias +#define GL_SGIX_texture_scale_bias 1 +#endif + +#ifndef GL_SGIX_framezoom +#define GL_SGIX_framezoom 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFrameZoomSGIX (GLint factor); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLFRAMEZOOMSGIXPROC) (GLint factor); +#endif + +#ifndef GL_SGIX_tag_sample_buffer +#define GL_SGIX_tag_sample_buffer 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTagSampleBufferSGIX (void); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLTAGSAMPLEBUFFERSGIXPROC) (void); +#endif + +#ifndef GL_SGIX_polynomial_ffd +#define GL_SGIX_polynomial_ffd 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDeformationMap3dSGIX (GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, GLdouble w1, GLdouble w2, GLint wstride, GLint worder, const GLdouble *points); +GLAPI void APIENTRY glDeformationMap3fSGIX (GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, GLfloat w1, GLfloat w2, GLint wstride, GLint worder, const GLfloat *points); +GLAPI void APIENTRY glDeformSGIX (GLbitfield mask); +GLAPI void APIENTRY glLoadIdentityDeformationMapSGIX (GLbitfield mask); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLDEFORMATIONMAP3DSGIXPROC) (GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, GLdouble w1, GLdouble w2, GLint wstride, GLint worder, const GLdouble *points); +typedef void (APIENTRYP PFNGLDEFORMATIONMAP3FSGIXPROC) (GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, GLfloat w1, GLfloat w2, GLint wstride, GLint worder, const GLfloat *points); +typedef void (APIENTRYP PFNGLDEFORMSGIXPROC) (GLbitfield mask); +typedef void (APIENTRYP PFNGLLOADIDENTITYDEFORMATIONMAPSGIXPROC) (GLbitfield mask); +#endif + +#ifndef GL_SGIX_reference_plane +#define GL_SGIX_reference_plane 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glReferencePlaneSGIX (const GLdouble *equation); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLREFERENCEPLANESGIXPROC) (const GLdouble *equation); +#endif + +#ifndef GL_SGIX_flush_raster +#define GL_SGIX_flush_raster 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFlushRasterSGIX (void); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLFLUSHRASTERSGIXPROC) (void); +#endif + +#ifndef GL_SGIX_depth_texture +#define GL_SGIX_depth_texture 1 +#endif + +#ifndef GL_SGIS_fog_function +#define GL_SGIS_fog_function 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFogFuncSGIS (GLsizei n, const GLfloat *points); +GLAPI void APIENTRY glGetFogFuncSGIS (GLfloat *points); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLFOGFUNCSGISPROC) (GLsizei n, const GLfloat *points); +typedef void (APIENTRYP PFNGLGETFOGFUNCSGISPROC) (GLfloat *points); +#endif + +#ifndef GL_SGIX_fog_offset +#define GL_SGIX_fog_offset 1 +#endif + +#ifndef GL_HP_image_transform +#define GL_HP_image_transform 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glImageTransformParameteriHP (GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glImageTransformParameterfHP (GLenum target, GLenum pname, GLfloat param); +GLAPI void APIENTRY glImageTransformParameterivHP (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glImageTransformParameterfvHP (GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glGetImageTransformParameterivHP (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetImageTransformParameterfvHP (GLenum target, GLenum pname, GLfloat *params); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERIHPPROC) (GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERFHPPROC) (GLenum target, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERIVHPPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERFVHPPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLGETIMAGETRANSFORMPARAMETERIVHPPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETIMAGETRANSFORMPARAMETERFVHPPROC) (GLenum target, GLenum pname, GLfloat *params); +#endif + +#ifndef GL_HP_convolution_border_modes +#define GL_HP_convolution_border_modes 1 +#endif + +#ifndef GL_SGIX_texture_add_env +#define GL_SGIX_texture_add_env 1 +#endif + +#ifndef GL_EXT_color_subtable +#define GL_EXT_color_subtable 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColorSubTableEXT (GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const GLvoid *data); +GLAPI void APIENTRY glCopyColorSubTableEXT (GLenum target, GLsizei start, GLint x, GLint y, GLsizei width); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLCOLORSUBTABLEEXTPROC) (GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const GLvoid *data); +typedef void (APIENTRYP PFNGLCOPYCOLORSUBTABLEEXTPROC) (GLenum target, GLsizei start, GLint x, GLint y, GLsizei width); +#endif + +#ifndef GL_PGI_vertex_hints +#define GL_PGI_vertex_hints 1 +#endif + +#ifndef GL_PGI_misc_hints +#define GL_PGI_misc_hints 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glHintPGI (GLenum target, GLint mode); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLHINTPGIPROC) (GLenum target, GLint mode); +#endif + +#ifndef GL_EXT_paletted_texture +#define GL_EXT_paletted_texture 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColorTableEXT (GLenum target, GLenum internalFormat, GLsizei width, GLenum format, GLenum type, const GLvoid *table); +GLAPI void APIENTRY glGetColorTableEXT (GLenum target, GLenum format, GLenum type, GLvoid *data); +GLAPI void APIENTRY glGetColorTableParameterivEXT (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetColorTableParameterfvEXT (GLenum target, GLenum pname, GLfloat *params); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLCOLORTABLEEXTPROC) (GLenum target, GLenum internalFormat, GLsizei width, GLenum format, GLenum type, const GLvoid *table); +typedef void (APIENTRYP PFNGLGETCOLORTABLEEXTPROC) (GLenum target, GLenum format, GLenum type, GLvoid *data); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params); +#endif + +#ifndef GL_EXT_clip_volume_hint +#define GL_EXT_clip_volume_hint 1 +#endif + +#ifndef GL_SGIX_list_priority +#define GL_SGIX_list_priority 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetListParameterfvSGIX (GLuint list, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetListParameterivSGIX (GLuint list, GLenum pname, GLint *params); +GLAPI void APIENTRY glListParameterfSGIX (GLuint list, GLenum pname, GLfloat param); +GLAPI void APIENTRY glListParameterfvSGIX (GLuint list, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glListParameteriSGIX (GLuint list, GLenum pname, GLint param); +GLAPI void APIENTRY glListParameterivSGIX (GLuint list, GLenum pname, const GLint *params); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLGETLISTPARAMETERFVSGIXPROC) (GLuint list, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETLISTPARAMETERIVSGIXPROC) (GLuint list, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLLISTPARAMETERFSGIXPROC) (GLuint list, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLLISTPARAMETERFVSGIXPROC) (GLuint list, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLLISTPARAMETERISGIXPROC) (GLuint list, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLLISTPARAMETERIVSGIXPROC) (GLuint list, GLenum pname, const GLint *params); +#endif + +#ifndef GL_SGIX_ir_instrument1 +#define GL_SGIX_ir_instrument1 1 +#endif + +#ifndef GL_SGIX_calligraphic_fragment +#define GL_SGIX_calligraphic_fragment 1 +#endif + +#ifndef GL_SGIX_texture_lod_bias +#define GL_SGIX_texture_lod_bias 1 +#endif + +#ifndef GL_SGIX_shadow_ambient +#define GL_SGIX_shadow_ambient 1 +#endif + +#ifndef GL_EXT_index_texture +#define GL_EXT_index_texture 1 +#endif + +#ifndef GL_EXT_index_material +#define GL_EXT_index_material 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glIndexMaterialEXT (GLenum face, GLenum mode); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLINDEXMATERIALEXTPROC) (GLenum face, GLenum mode); +#endif + +#ifndef GL_EXT_index_func +#define GL_EXT_index_func 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glIndexFuncEXT (GLenum func, GLclampf ref); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLINDEXFUNCEXTPROC) (GLenum func, GLclampf ref); +#endif + +#ifndef GL_EXT_index_array_formats +#define GL_EXT_index_array_formats 1 +#endif + +#ifndef GL_EXT_compiled_vertex_array +#define GL_EXT_compiled_vertex_array 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glLockArraysEXT (GLint first, GLsizei count); +GLAPI void APIENTRY glUnlockArraysEXT (void); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLLOCKARRAYSEXTPROC) (GLint first, GLsizei count); +typedef void (APIENTRYP PFNGLUNLOCKARRAYSEXTPROC) (void); +#endif + +#ifndef GL_EXT_cull_vertex +#define GL_EXT_cull_vertex 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCullParameterdvEXT (GLenum pname, GLdouble *params); +GLAPI void APIENTRY glCullParameterfvEXT (GLenum pname, GLfloat *params); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLCULLPARAMETERDVEXTPROC) (GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLCULLPARAMETERFVEXTPROC) (GLenum pname, GLfloat *params); +#endif + +#ifndef GL_SGIX_ycrcb +#define GL_SGIX_ycrcb 1 +#endif + +#ifndef GL_SGIX_fragment_lighting +#define GL_SGIX_fragment_lighting 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFragmentColorMaterialSGIX (GLenum face, GLenum mode); +GLAPI void APIENTRY glFragmentLightfSGIX (GLenum light, GLenum pname, GLfloat param); +GLAPI void APIENTRY glFragmentLightfvSGIX (GLenum light, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glFragmentLightiSGIX (GLenum light, GLenum pname, GLint param); +GLAPI void APIENTRY glFragmentLightivSGIX (GLenum light, GLenum pname, const GLint *params); +GLAPI void APIENTRY glFragmentLightModelfSGIX (GLenum pname, GLfloat param); +GLAPI void APIENTRY glFragmentLightModelfvSGIX (GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glFragmentLightModeliSGIX (GLenum pname, GLint param); +GLAPI void APIENTRY glFragmentLightModelivSGIX (GLenum pname, const GLint *params); +GLAPI void APIENTRY glFragmentMaterialfSGIX (GLenum face, GLenum pname, GLfloat param); +GLAPI void APIENTRY glFragmentMaterialfvSGIX (GLenum face, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glFragmentMaterialiSGIX (GLenum face, GLenum pname, GLint param); +GLAPI void APIENTRY glFragmentMaterialivSGIX (GLenum face, GLenum pname, const GLint *params); +GLAPI void APIENTRY glGetFragmentLightfvSGIX (GLenum light, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetFragmentLightivSGIX (GLenum light, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetFragmentMaterialfvSGIX (GLenum face, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetFragmentMaterialivSGIX (GLenum face, GLenum pname, GLint *params); +GLAPI void APIENTRY glLightEnviSGIX (GLenum pname, GLint param); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLFRAGMENTCOLORMATERIALSGIXPROC) (GLenum face, GLenum mode); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTFSGIXPROC) (GLenum light, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTFVSGIXPROC) (GLenum light, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTISGIXPROC) (GLenum light, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTIVSGIXPROC) (GLenum light, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELFSGIXPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELFVSGIXPROC) (GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELISGIXPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELIVSGIXPROC) (GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLFRAGMENTMATERIALFSGIXPROC) (GLenum face, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLFRAGMENTMATERIALFVSGIXPROC) (GLenum face, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLFRAGMENTMATERIALISGIXPROC) (GLenum face, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLFRAGMENTMATERIALIVSGIXPROC) (GLenum face, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLGETFRAGMENTLIGHTFVSGIXPROC) (GLenum light, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETFRAGMENTLIGHTIVSGIXPROC) (GLenum light, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETFRAGMENTMATERIALFVSGIXPROC) (GLenum face, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETFRAGMENTMATERIALIVSGIXPROC) (GLenum face, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLLIGHTENVISGIXPROC) (GLenum pname, GLint param); +#endif + +#ifndef GL_IBM_rasterpos_clip +#define GL_IBM_rasterpos_clip 1 +#endif + +#ifndef GL_HP_texture_lighting +#define GL_HP_texture_lighting 1 +#endif + +#ifndef GL_EXT_draw_range_elements +#define GL_EXT_draw_range_elements 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawRangeElementsEXT (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSEXTPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices); +#endif + +#ifndef GL_WIN_phong_shading +#define GL_WIN_phong_shading 1 +#endif + +#ifndef GL_WIN_specular_fog +#define GL_WIN_specular_fog 1 +#endif + +#ifndef GL_EXT_light_texture +#define GL_EXT_light_texture 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glApplyTextureEXT (GLenum mode); +GLAPI void APIENTRY glTextureLightEXT (GLenum pname); +GLAPI void APIENTRY glTextureMaterialEXT (GLenum face, GLenum mode); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLAPPLYTEXTUREEXTPROC) (GLenum mode); +typedef void (APIENTRYP PFNGLTEXTURELIGHTEXTPROC) (GLenum pname); +typedef void (APIENTRYP PFNGLTEXTUREMATERIALEXTPROC) (GLenum face, GLenum mode); +#endif + +#ifndef GL_SGIX_blend_alpha_minmax +#define GL_SGIX_blend_alpha_minmax 1 +#endif + +#ifndef GL_EXT_bgra +#define GL_EXT_bgra 1 +#endif + +#ifndef GL_SGIX_async +#define GL_SGIX_async 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glAsyncMarkerSGIX (GLuint marker); +GLAPI GLint APIENTRY glFinishAsyncSGIX (GLuint *markerp); +GLAPI GLint APIENTRY glPollAsyncSGIX (GLuint *markerp); +GLAPI GLuint APIENTRY glGenAsyncMarkersSGIX (GLsizei range); +GLAPI void APIENTRY glDeleteAsyncMarkersSGIX (GLuint marker, GLsizei range); +GLAPI GLboolean APIENTRY glIsAsyncMarkerSGIX (GLuint marker); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLASYNCMARKERSGIXPROC) (GLuint marker); +typedef GLint (APIENTRYP PFNGLFINISHASYNCSGIXPROC) (GLuint *markerp); +typedef GLint (APIENTRYP PFNGLPOLLASYNCSGIXPROC) (GLuint *markerp); +typedef GLuint (APIENTRYP PFNGLGENASYNCMARKERSSGIXPROC) (GLsizei range); +typedef void (APIENTRYP PFNGLDELETEASYNCMARKERSSGIXPROC) (GLuint marker, GLsizei range); +typedef GLboolean (APIENTRYP PFNGLISASYNCMARKERSGIXPROC) (GLuint marker); +#endif + +#ifndef GL_SGIX_async_pixel +#define GL_SGIX_async_pixel 1 +#endif + +#ifndef GL_SGIX_async_histogram +#define GL_SGIX_async_histogram 1 +#endif + +#ifndef GL_INTEL_parallel_arrays +#define GL_INTEL_parallel_arrays 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexPointervINTEL (GLint size, GLenum type, const GLvoid* *pointer); +GLAPI void APIENTRY glNormalPointervINTEL (GLenum type, const GLvoid* *pointer); +GLAPI void APIENTRY glColorPointervINTEL (GLint size, GLenum type, const GLvoid* *pointer); +GLAPI void APIENTRY glTexCoordPointervINTEL (GLint size, GLenum type, const GLvoid* *pointer); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLVERTEXPOINTERVINTELPROC) (GLint size, GLenum type, const GLvoid* *pointer); +typedef void (APIENTRYP PFNGLNORMALPOINTERVINTELPROC) (GLenum type, const GLvoid* *pointer); +typedef void (APIENTRYP PFNGLCOLORPOINTERVINTELPROC) (GLint size, GLenum type, const GLvoid* *pointer); +typedef void (APIENTRYP PFNGLTEXCOORDPOINTERVINTELPROC) (GLint size, GLenum type, const GLvoid* *pointer); +#endif + +#ifndef GL_HP_occlusion_test +#define GL_HP_occlusion_test 1 +#endif + +#ifndef GL_EXT_pixel_transform +#define GL_EXT_pixel_transform 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPixelTransformParameteriEXT (GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glPixelTransformParameterfEXT (GLenum target, GLenum pname, GLfloat param); +GLAPI void APIENTRY glPixelTransformParameterivEXT (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glPixelTransformParameterfvEXT (GLenum target, GLenum pname, const GLfloat *params); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERIEXTPROC) (GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERFEXTPROC) (GLenum target, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERIVEXTPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERFVEXTPROC) (GLenum target, GLenum pname, const GLfloat *params); +#endif + +#ifndef GL_EXT_pixel_transform_color_table +#define GL_EXT_pixel_transform_color_table 1 +#endif + +#ifndef GL_EXT_shared_texture_palette +#define GL_EXT_shared_texture_palette 1 +#endif + +#ifndef GL_EXT_separate_specular_color +#define GL_EXT_separate_specular_color 1 +#endif + +#ifndef GL_EXT_secondary_color +#define GL_EXT_secondary_color 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSecondaryColor3bEXT (GLbyte red, GLbyte green, GLbyte blue); +GLAPI void APIENTRY glSecondaryColor3bvEXT (const GLbyte *v); +GLAPI void APIENTRY glSecondaryColor3dEXT (GLdouble red, GLdouble green, GLdouble blue); +GLAPI void APIENTRY glSecondaryColor3dvEXT (const GLdouble *v); +GLAPI void APIENTRY glSecondaryColor3fEXT (GLfloat red, GLfloat green, GLfloat blue); +GLAPI void APIENTRY glSecondaryColor3fvEXT (const GLfloat *v); +GLAPI void APIENTRY glSecondaryColor3iEXT (GLint red, GLint green, GLint blue); +GLAPI void APIENTRY glSecondaryColor3ivEXT (const GLint *v); +GLAPI void APIENTRY glSecondaryColor3sEXT (GLshort red, GLshort green, GLshort blue); +GLAPI void APIENTRY glSecondaryColor3svEXT (const GLshort *v); +GLAPI void APIENTRY glSecondaryColor3ubEXT (GLubyte red, GLubyte green, GLubyte blue); +GLAPI void APIENTRY glSecondaryColor3ubvEXT (const GLubyte *v); +GLAPI void APIENTRY glSecondaryColor3uiEXT (GLuint red, GLuint green, GLuint blue); +GLAPI void APIENTRY glSecondaryColor3uivEXT (const GLuint *v); +GLAPI void APIENTRY glSecondaryColor3usEXT (GLushort red, GLushort green, GLushort blue); +GLAPI void APIENTRY glSecondaryColor3usvEXT (const GLushort *v); +GLAPI void APIENTRY glSecondaryColorPointerEXT (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BEXTPROC) (GLbyte red, GLbyte green, GLbyte blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BVEXTPROC) (const GLbyte *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DEXTPROC) (GLdouble red, GLdouble green, GLdouble blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DVEXTPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FEXTPROC) (GLfloat red, GLfloat green, GLfloat blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FVEXTPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IEXTPROC) (GLint red, GLint green, GLint blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IVEXTPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SEXTPROC) (GLshort red, GLshort green, GLshort blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SVEXTPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBEXTPROC) (GLubyte red, GLubyte green, GLubyte blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBVEXTPROC) (const GLubyte *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIEXTPROC) (GLuint red, GLuint green, GLuint blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIVEXTPROC) (const GLuint *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USEXTPROC) (GLushort red, GLushort green, GLushort blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USVEXTPROC) (const GLushort *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLORPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +#endif + +#ifndef GL_EXT_texture_perturb_normal +#define GL_EXT_texture_perturb_normal 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTextureNormalEXT (GLenum mode); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLTEXTURENORMALEXTPROC) (GLenum mode); +#endif + +#ifndef GL_EXT_multi_draw_arrays +#define GL_EXT_multi_draw_arrays 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMultiDrawArraysEXT (GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount); +GLAPI void APIENTRY glMultiDrawElementsEXT (GLenum mode, const GLsizei *count, GLenum type, const GLvoid* *indices, GLsizei primcount); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSEXTPROC) (GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSEXTPROC) (GLenum mode, const GLsizei *count, GLenum type, const GLvoid* *indices, GLsizei primcount); +#endif + +#ifndef GL_EXT_fog_coord +#define GL_EXT_fog_coord 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFogCoordfEXT (GLfloat coord); +GLAPI void APIENTRY glFogCoordfvEXT (const GLfloat *coord); +GLAPI void APIENTRY glFogCoorddEXT (GLdouble coord); +GLAPI void APIENTRY glFogCoorddvEXT (const GLdouble *coord); +GLAPI void APIENTRY glFogCoordPointerEXT (GLenum type, GLsizei stride, const GLvoid *pointer); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLFOGCOORDFEXTPROC) (GLfloat coord); +typedef void (APIENTRYP PFNGLFOGCOORDFVEXTPROC) (const GLfloat *coord); +typedef void (APIENTRYP PFNGLFOGCOORDDEXTPROC) (GLdouble coord); +typedef void (APIENTRYP PFNGLFOGCOORDDVEXTPROC) (const GLdouble *coord); +typedef void (APIENTRYP PFNGLFOGCOORDPOINTEREXTPROC) (GLenum type, GLsizei stride, const GLvoid *pointer); +#endif + +#ifndef GL_REND_screen_coordinates +#define GL_REND_screen_coordinates 1 +#endif + +#ifndef GL_EXT_coordinate_frame +#define GL_EXT_coordinate_frame 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTangent3bEXT (GLbyte tx, GLbyte ty, GLbyte tz); +GLAPI void APIENTRY glTangent3bvEXT (const GLbyte *v); +GLAPI void APIENTRY glTangent3dEXT (GLdouble tx, GLdouble ty, GLdouble tz); +GLAPI void APIENTRY glTangent3dvEXT (const GLdouble *v); +GLAPI void APIENTRY glTangent3fEXT (GLfloat tx, GLfloat ty, GLfloat tz); +GLAPI void APIENTRY glTangent3fvEXT (const GLfloat *v); +GLAPI void APIENTRY glTangent3iEXT (GLint tx, GLint ty, GLint tz); +GLAPI void APIENTRY glTangent3ivEXT (const GLint *v); +GLAPI void APIENTRY glTangent3sEXT (GLshort tx, GLshort ty, GLshort tz); +GLAPI void APIENTRY glTangent3svEXT (const GLshort *v); +GLAPI void APIENTRY glBinormal3bEXT (GLbyte bx, GLbyte by, GLbyte bz); +GLAPI void APIENTRY glBinormal3bvEXT (const GLbyte *v); +GLAPI void APIENTRY glBinormal3dEXT (GLdouble bx, GLdouble by, GLdouble bz); +GLAPI void APIENTRY glBinormal3dvEXT (const GLdouble *v); +GLAPI void APIENTRY glBinormal3fEXT (GLfloat bx, GLfloat by, GLfloat bz); +GLAPI void APIENTRY glBinormal3fvEXT (const GLfloat *v); +GLAPI void APIENTRY glBinormal3iEXT (GLint bx, GLint by, GLint bz); +GLAPI void APIENTRY glBinormal3ivEXT (const GLint *v); +GLAPI void APIENTRY glBinormal3sEXT (GLshort bx, GLshort by, GLshort bz); +GLAPI void APIENTRY glBinormal3svEXT (const GLshort *v); +GLAPI void APIENTRY glTangentPointerEXT (GLenum type, GLsizei stride, const GLvoid *pointer); +GLAPI void APIENTRY glBinormalPointerEXT (GLenum type, GLsizei stride, const GLvoid *pointer); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLTANGENT3BEXTPROC) (GLbyte tx, GLbyte ty, GLbyte tz); +typedef void (APIENTRYP PFNGLTANGENT3BVEXTPROC) (const GLbyte *v); +typedef void (APIENTRYP PFNGLTANGENT3DEXTPROC) (GLdouble tx, GLdouble ty, GLdouble tz); +typedef void (APIENTRYP PFNGLTANGENT3DVEXTPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLTANGENT3FEXTPROC) (GLfloat tx, GLfloat ty, GLfloat tz); +typedef void (APIENTRYP PFNGLTANGENT3FVEXTPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLTANGENT3IEXTPROC) (GLint tx, GLint ty, GLint tz); +typedef void (APIENTRYP PFNGLTANGENT3IVEXTPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLTANGENT3SEXTPROC) (GLshort tx, GLshort ty, GLshort tz); +typedef void (APIENTRYP PFNGLTANGENT3SVEXTPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLBINORMAL3BEXTPROC) (GLbyte bx, GLbyte by, GLbyte bz); +typedef void (APIENTRYP PFNGLBINORMAL3BVEXTPROC) (const GLbyte *v); +typedef void (APIENTRYP PFNGLBINORMAL3DEXTPROC) (GLdouble bx, GLdouble by, GLdouble bz); +typedef void (APIENTRYP PFNGLBINORMAL3DVEXTPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLBINORMAL3FEXTPROC) (GLfloat bx, GLfloat by, GLfloat bz); +typedef void (APIENTRYP PFNGLBINORMAL3FVEXTPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLBINORMAL3IEXTPROC) (GLint bx, GLint by, GLint bz); +typedef void (APIENTRYP PFNGLBINORMAL3IVEXTPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLBINORMAL3SEXTPROC) (GLshort bx, GLshort by, GLshort bz); +typedef void (APIENTRYP PFNGLBINORMAL3SVEXTPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLTANGENTPOINTEREXTPROC) (GLenum type, GLsizei stride, const GLvoid *pointer); +typedef void (APIENTRYP PFNGLBINORMALPOINTEREXTPROC) (GLenum type, GLsizei stride, const GLvoid *pointer); +#endif + +#ifndef GL_EXT_texture_env_combine +#define GL_EXT_texture_env_combine 1 +#endif + +#ifndef GL_APPLE_specular_vector +#define GL_APPLE_specular_vector 1 +#endif + +#ifndef GL_APPLE_transform_hint +#define GL_APPLE_transform_hint 1 +#endif + +#ifndef GL_SGIX_fog_scale +#define GL_SGIX_fog_scale 1 +#endif + +#ifndef GL_SUNX_constant_data +#define GL_SUNX_constant_data 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFinishTextureSUNX (void); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLFINISHTEXTURESUNXPROC) (void); +#endif + +#ifndef GL_SUN_global_alpha +#define GL_SUN_global_alpha 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGlobalAlphaFactorbSUN (GLbyte factor); +GLAPI void APIENTRY glGlobalAlphaFactorsSUN (GLshort factor); +GLAPI void APIENTRY glGlobalAlphaFactoriSUN (GLint factor); +GLAPI void APIENTRY glGlobalAlphaFactorfSUN (GLfloat factor); +GLAPI void APIENTRY glGlobalAlphaFactordSUN (GLdouble factor); +GLAPI void APIENTRY glGlobalAlphaFactorubSUN (GLubyte factor); +GLAPI void APIENTRY glGlobalAlphaFactorusSUN (GLushort factor); +GLAPI void APIENTRY glGlobalAlphaFactoruiSUN (GLuint factor); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORBSUNPROC) (GLbyte factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORSSUNPROC) (GLshort factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORISUNPROC) (GLint factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORFSUNPROC) (GLfloat factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORDSUNPROC) (GLdouble factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORUBSUNPROC) (GLubyte factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORUSSUNPROC) (GLushort factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORUISUNPROC) (GLuint factor); +#endif + +#ifndef GL_SUN_triangle_list +#define GL_SUN_triangle_list 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glReplacementCodeuiSUN (GLuint code); +GLAPI void APIENTRY glReplacementCodeusSUN (GLushort code); +GLAPI void APIENTRY glReplacementCodeubSUN (GLubyte code); +GLAPI void APIENTRY glReplacementCodeuivSUN (const GLuint *code); +GLAPI void APIENTRY glReplacementCodeusvSUN (const GLushort *code); +GLAPI void APIENTRY glReplacementCodeubvSUN (const GLubyte *code); +GLAPI void APIENTRY glReplacementCodePointerSUN (GLenum type, GLsizei stride, const GLvoid* *pointer); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUISUNPROC) (GLuint code); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUSSUNPROC) (GLushort code); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUBSUNPROC) (GLubyte code); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUIVSUNPROC) (const GLuint *code); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUSVSUNPROC) (const GLushort *code); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUBVSUNPROC) (const GLubyte *code); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEPOINTERSUNPROC) (GLenum type, GLsizei stride, const GLvoid* *pointer); +#endif + +#ifndef GL_SUN_vertex +#define GL_SUN_vertex 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColor4ubVertex2fSUN (GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y); +GLAPI void APIENTRY glColor4ubVertex2fvSUN (const GLubyte *c, const GLfloat *v); +GLAPI void APIENTRY glColor4ubVertex3fSUN (GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glColor4ubVertex3fvSUN (const GLubyte *c, const GLfloat *v); +GLAPI void APIENTRY glColor3fVertex3fSUN (GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glColor3fVertex3fvSUN (const GLfloat *c, const GLfloat *v); +GLAPI void APIENTRY glNormal3fVertex3fSUN (GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glNormal3fVertex3fvSUN (const GLfloat *n, const GLfloat *v); +GLAPI void APIENTRY glColor4fNormal3fVertex3fSUN (GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glColor4fNormal3fVertex3fvSUN (const GLfloat *c, const GLfloat *n, const GLfloat *v); +GLAPI void APIENTRY glTexCoord2fVertex3fSUN (GLfloat s, GLfloat t, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glTexCoord2fVertex3fvSUN (const GLfloat *tc, const GLfloat *v); +GLAPI void APIENTRY glTexCoord4fVertex4fSUN (GLfloat s, GLfloat t, GLfloat p, GLfloat q, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glTexCoord4fVertex4fvSUN (const GLfloat *tc, const GLfloat *v); +GLAPI void APIENTRY glTexCoord2fColor4ubVertex3fSUN (GLfloat s, GLfloat t, GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glTexCoord2fColor4ubVertex3fvSUN (const GLfloat *tc, const GLubyte *c, const GLfloat *v); +GLAPI void APIENTRY glTexCoord2fColor3fVertex3fSUN (GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glTexCoord2fColor3fVertex3fvSUN (const GLfloat *tc, const GLfloat *c, const GLfloat *v); +GLAPI void APIENTRY glTexCoord2fNormal3fVertex3fSUN (GLfloat s, GLfloat t, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glTexCoord2fNormal3fVertex3fvSUN (const GLfloat *tc, const GLfloat *n, const GLfloat *v); +GLAPI void APIENTRY glTexCoord2fColor4fNormal3fVertex3fSUN (GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glTexCoord2fColor4fNormal3fVertex3fvSUN (const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +GLAPI void APIENTRY glTexCoord4fColor4fNormal3fVertex4fSUN (GLfloat s, GLfloat t, GLfloat p, GLfloat q, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glTexCoord4fColor4fNormal3fVertex4fvSUN (const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +GLAPI void APIENTRY glReplacementCodeuiVertex3fSUN (GLuint rc, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glReplacementCodeuiVertex3fvSUN (const GLuint *rc, const GLfloat *v); +GLAPI void APIENTRY glReplacementCodeuiColor4ubVertex3fSUN (GLuint rc, GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glReplacementCodeuiColor4ubVertex3fvSUN (const GLuint *rc, const GLubyte *c, const GLfloat *v); +GLAPI void APIENTRY glReplacementCodeuiColor3fVertex3fSUN (GLuint rc, GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glReplacementCodeuiColor3fVertex3fvSUN (const GLuint *rc, const GLfloat *c, const GLfloat *v); +GLAPI void APIENTRY glReplacementCodeuiNormal3fVertex3fSUN (GLuint rc, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glReplacementCodeuiNormal3fVertex3fvSUN (const GLuint *rc, const GLfloat *n, const GLfloat *v); +GLAPI void APIENTRY glReplacementCodeuiColor4fNormal3fVertex3fSUN (GLuint rc, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glReplacementCodeuiColor4fNormal3fVertex3fvSUN (const GLuint *rc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +GLAPI void APIENTRY glReplacementCodeuiTexCoord2fVertex3fSUN (GLuint rc, GLfloat s, GLfloat t, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glReplacementCodeuiTexCoord2fVertex3fvSUN (const GLuint *rc, const GLfloat *tc, const GLfloat *v); +GLAPI void APIENTRY glReplacementCodeuiTexCoord2fNormal3fVertex3fSUN (GLuint rc, GLfloat s, GLfloat t, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glReplacementCodeuiTexCoord2fNormal3fVertex3fvSUN (const GLuint *rc, const GLfloat *tc, const GLfloat *n, const GLfloat *v); +GLAPI void APIENTRY glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fSUN (GLuint rc, GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fvSUN (const GLuint *rc, const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLCOLOR4UBVERTEX2FSUNPROC) (GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLCOLOR4UBVERTEX2FVSUNPROC) (const GLubyte *c, const GLfloat *v); +typedef void (APIENTRYP PFNGLCOLOR4UBVERTEX3FSUNPROC) (GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLCOLOR4UBVERTEX3FVSUNPROC) (const GLubyte *c, const GLfloat *v); +typedef void (APIENTRYP PFNGLCOLOR3FVERTEX3FSUNPROC) (GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLCOLOR3FVERTEX3FVSUNPROC) (const GLfloat *c, const GLfloat *v); +typedef void (APIENTRYP PFNGLNORMAL3FVERTEX3FSUNPROC) (GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLNORMAL3FVERTEX3FVSUNPROC) (const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLCOLOR4FNORMAL3FVERTEX3FSUNPROC) (GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLCOLOR4FNORMAL3FVERTEX3FVSUNPROC) (const GLfloat *c, const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLTEXCOORD2FVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLTEXCOORD2FVERTEX3FVSUNPROC) (const GLfloat *tc, const GLfloat *v); +typedef void (APIENTRYP PFNGLTEXCOORD4FVERTEX4FSUNPROC) (GLfloat s, GLfloat t, GLfloat p, GLfloat q, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLTEXCOORD4FVERTEX4FVSUNPROC) (const GLfloat *tc, const GLfloat *v); +typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4UBVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4UBVERTEX3FVSUNPROC) (const GLfloat *tc, const GLubyte *c, const GLfloat *v); +typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR3FVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR3FVERTEX3FVSUNPROC) (const GLfloat *tc, const GLfloat *c, const GLfloat *v); +typedef void (APIENTRYP PFNGLTEXCOORD2FNORMAL3FVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLTEXCOORD2FNORMAL3FVERTEX3FVSUNPROC) (const GLfloat *tc, const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4FNORMAL3FVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4FNORMAL3FVERTEX3FVSUNPROC) (const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLTEXCOORD4FCOLOR4FNORMAL3FVERTEX4FSUNPROC) (GLfloat s, GLfloat t, GLfloat p, GLfloat q, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLTEXCOORD4FCOLOR4FNORMAL3FVERTEX4FVSUNPROC) (const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUIVERTEX3FSUNPROC) (GLuint rc, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUIVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4UBVERTEX3FSUNPROC) (GLuint rc, GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4UBVERTEX3FVSUNPROC) (const GLuint *rc, const GLubyte *c, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR3FVERTEX3FSUNPROC) (GLuint rc, GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *c, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUINORMAL3FVERTEX3FSUNPROC) (GLuint rc, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUINORMAL3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4FNORMAL3FVERTEX3FSUNPROC) (GLuint rc, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4FNORMAL3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FVERTEX3FSUNPROC) (GLuint rc, GLfloat s, GLfloat t, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *tc, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FNORMAL3FVERTEX3FSUNPROC) (GLuint rc, GLfloat s, GLfloat t, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FNORMAL3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *tc, const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FCOLOR4FNORMAL3FVERTEX3FSUNPROC) (GLuint rc, GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FCOLOR4FNORMAL3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +#endif + +#ifndef GL_EXT_blend_func_separate +#define GL_EXT_blend_func_separate 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendFuncSeparateEXT (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEEXTPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +#endif + +#ifndef GL_INGR_blend_func_separate +#define GL_INGR_blend_func_separate 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendFuncSeparateINGR (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEINGRPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +#endif + +#ifndef GL_INGR_color_clamp +#define GL_INGR_color_clamp 1 +#endif + +#ifndef GL_INGR_interlace_read +#define GL_INGR_interlace_read 1 +#endif + +#ifndef GL_EXT_stencil_wrap +#define GL_EXT_stencil_wrap 1 +#endif + +#ifndef GL_EXT_422_pixels +#define GL_EXT_422_pixels 1 +#endif + +#ifndef GL_NV_texgen_reflection +#define GL_NV_texgen_reflection 1 +#endif + +#ifndef GL_SUN_convolution_border_modes +#define GL_SUN_convolution_border_modes 1 +#endif + +#ifndef GL_EXT_texture_env_add +#define GL_EXT_texture_env_add 1 +#endif + +#ifndef GL_EXT_texture_lod_bias +#define GL_EXT_texture_lod_bias 1 +#endif + +#ifndef GL_EXT_texture_filter_anisotropic +#define GL_EXT_texture_filter_anisotropic 1 +#endif + +#ifndef GL_EXT_vertex_weighting +#define GL_EXT_vertex_weighting 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexWeightfEXT (GLfloat weight); +GLAPI void APIENTRY glVertexWeightfvEXT (const GLfloat *weight); +GLAPI void APIENTRY glVertexWeightPointerEXT (GLsizei size, GLenum type, GLsizei stride, const GLvoid *pointer); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLVERTEXWEIGHTFEXTPROC) (GLfloat weight); +typedef void (APIENTRYP PFNGLVERTEXWEIGHTFVEXTPROC) (const GLfloat *weight); +typedef void (APIENTRYP PFNGLVERTEXWEIGHTPOINTEREXTPROC) (GLsizei size, GLenum type, GLsizei stride, const GLvoid *pointer); +#endif + +#ifndef GL_NV_light_max_exponent +#define GL_NV_light_max_exponent 1 +#endif + +#ifndef GL_NV_vertex_array_range +#define GL_NV_vertex_array_range 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFlushVertexArrayRangeNV (void); +GLAPI void APIENTRY glVertexArrayRangeNV (GLsizei length, const GLvoid *pointer); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLFLUSHVERTEXARRAYRANGENVPROC) (void); +typedef void (APIENTRYP PFNGLVERTEXARRAYRANGENVPROC) (GLsizei length, const GLvoid *pointer); +#endif + +#ifndef GL_NV_register_combiners +#define GL_NV_register_combiners 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCombinerParameterfvNV (GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glCombinerParameterfNV (GLenum pname, GLfloat param); +GLAPI void APIENTRY glCombinerParameterivNV (GLenum pname, const GLint *params); +GLAPI void APIENTRY glCombinerParameteriNV (GLenum pname, GLint param); +GLAPI void APIENTRY glCombinerInputNV (GLenum stage, GLenum portion, GLenum variable, GLenum input, GLenum mapping, GLenum componentUsage); +GLAPI void APIENTRY glCombinerOutputNV (GLenum stage, GLenum portion, GLenum abOutput, GLenum cdOutput, GLenum sumOutput, GLenum scale, GLenum bias, GLboolean abDotProduct, GLboolean cdDotProduct, GLboolean muxSum); +GLAPI void APIENTRY glFinalCombinerInputNV (GLenum variable, GLenum input, GLenum mapping, GLenum componentUsage); +GLAPI void APIENTRY glGetCombinerInputParameterfvNV (GLenum stage, GLenum portion, GLenum variable, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetCombinerInputParameterivNV (GLenum stage, GLenum portion, GLenum variable, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetCombinerOutputParameterfvNV (GLenum stage, GLenum portion, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetCombinerOutputParameterivNV (GLenum stage, GLenum portion, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetFinalCombinerInputParameterfvNV (GLenum variable, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetFinalCombinerInputParameterivNV (GLenum variable, GLenum pname, GLint *params); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLCOMBINERPARAMETERFVNVPROC) (GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLCOMBINERPARAMETERFNVPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLCOMBINERPARAMETERIVNVPROC) (GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLCOMBINERPARAMETERINVPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLCOMBINERINPUTNVPROC) (GLenum stage, GLenum portion, GLenum variable, GLenum input, GLenum mapping, GLenum componentUsage); +typedef void (APIENTRYP PFNGLCOMBINEROUTPUTNVPROC) (GLenum stage, GLenum portion, GLenum abOutput, GLenum cdOutput, GLenum sumOutput, GLenum scale, GLenum bias, GLboolean abDotProduct, GLboolean cdDotProduct, GLboolean muxSum); +typedef void (APIENTRYP PFNGLFINALCOMBINERINPUTNVPROC) (GLenum variable, GLenum input, GLenum mapping, GLenum componentUsage); +typedef void (APIENTRYP PFNGLGETCOMBINERINPUTPARAMETERFVNVPROC) (GLenum stage, GLenum portion, GLenum variable, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETCOMBINERINPUTPARAMETERIVNVPROC) (GLenum stage, GLenum portion, GLenum variable, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETCOMBINEROUTPUTPARAMETERFVNVPROC) (GLenum stage, GLenum portion, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETCOMBINEROUTPUTPARAMETERIVNVPROC) (GLenum stage, GLenum portion, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETFINALCOMBINERINPUTPARAMETERFVNVPROC) (GLenum variable, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETFINALCOMBINERINPUTPARAMETERIVNVPROC) (GLenum variable, GLenum pname, GLint *params); +#endif + +#ifndef GL_NV_fog_distance +#define GL_NV_fog_distance 1 +#endif + +#ifndef GL_NV_texgen_emboss +#define GL_NV_texgen_emboss 1 +#endif + +#ifndef GL_NV_blend_square +#define GL_NV_blend_square 1 +#endif + +#ifndef GL_NV_texture_env_combine4 +#define GL_NV_texture_env_combine4 1 +#endif + +#ifndef GL_MESA_resize_buffers +#define GL_MESA_resize_buffers 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glResizeBuffersMESA (void); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLRESIZEBUFFERSMESAPROC) (void); +#endif + +#ifndef GL_MESA_window_pos +#define GL_MESA_window_pos 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glWindowPos2dMESA (GLdouble x, GLdouble y); +GLAPI void APIENTRY glWindowPos2dvMESA (const GLdouble *v); +GLAPI void APIENTRY glWindowPos2fMESA (GLfloat x, GLfloat y); +GLAPI void APIENTRY glWindowPos2fvMESA (const GLfloat *v); +GLAPI void APIENTRY glWindowPos2iMESA (GLint x, GLint y); +GLAPI void APIENTRY glWindowPos2ivMESA (const GLint *v); +GLAPI void APIENTRY glWindowPos2sMESA (GLshort x, GLshort y); +GLAPI void APIENTRY glWindowPos2svMESA (const GLshort *v); +GLAPI void APIENTRY glWindowPos3dMESA (GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glWindowPos3dvMESA (const GLdouble *v); +GLAPI void APIENTRY glWindowPos3fMESA (GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glWindowPos3fvMESA (const GLfloat *v); +GLAPI void APIENTRY glWindowPos3iMESA (GLint x, GLint y, GLint z); +GLAPI void APIENTRY glWindowPos3ivMESA (const GLint *v); +GLAPI void APIENTRY glWindowPos3sMESA (GLshort x, GLshort y, GLshort z); +GLAPI void APIENTRY glWindowPos3svMESA (const GLshort *v); +GLAPI void APIENTRY glWindowPos4dMESA (GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glWindowPos4dvMESA (const GLdouble *v); +GLAPI void APIENTRY glWindowPos4fMESA (GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glWindowPos4fvMESA (const GLfloat *v); +GLAPI void APIENTRY glWindowPos4iMESA (GLint x, GLint y, GLint z, GLint w); +GLAPI void APIENTRY glWindowPos4ivMESA (const GLint *v); +GLAPI void APIENTRY glWindowPos4sMESA (GLshort x, GLshort y, GLshort z, GLshort w); +GLAPI void APIENTRY glWindowPos4svMESA (const GLshort *v); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLWINDOWPOS2DMESAPROC) (GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLWINDOWPOS2DVMESAPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2FMESAPROC) (GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLWINDOWPOS2FVMESAPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2IMESAPROC) (GLint x, GLint y); +typedef void (APIENTRYP PFNGLWINDOWPOS2IVMESAPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2SMESAPROC) (GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLWINDOWPOS2SVMESAPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3DMESAPROC) (GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLWINDOWPOS3DVMESAPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3FMESAPROC) (GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLWINDOWPOS3FVMESAPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3IMESAPROC) (GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLWINDOWPOS3IVMESAPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3SMESAPROC) (GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLWINDOWPOS3SVMESAPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLWINDOWPOS4DMESAPROC) (GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLWINDOWPOS4DVMESAPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLWINDOWPOS4FMESAPROC) (GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLWINDOWPOS4FVMESAPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLWINDOWPOS4IMESAPROC) (GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLWINDOWPOS4IVMESAPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLWINDOWPOS4SMESAPROC) (GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (APIENTRYP PFNGLWINDOWPOS4SVMESAPROC) (const GLshort *v); +#endif + +#ifndef GL_IBM_cull_vertex +#define GL_IBM_cull_vertex 1 +#endif + +#ifndef GL_IBM_multimode_draw_arrays +#define GL_IBM_multimode_draw_arrays 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMultiModeDrawArraysIBM (const GLenum *mode, const GLint *first, const GLsizei *count, GLsizei primcount, GLint modestride); +GLAPI void APIENTRY glMultiModeDrawElementsIBM (const GLenum *mode, const GLsizei *count, GLenum type, const GLvoid* const *indices, GLsizei primcount, GLint modestride); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLMULTIMODEDRAWARRAYSIBMPROC) (const GLenum *mode, const GLint *first, const GLsizei *count, GLsizei primcount, GLint modestride); +typedef void (APIENTRYP PFNGLMULTIMODEDRAWELEMENTSIBMPROC) (const GLenum *mode, const GLsizei *count, GLenum type, const GLvoid* const *indices, GLsizei primcount, GLint modestride); +#endif + +#ifndef GL_IBM_vertex_array_lists +#define GL_IBM_vertex_array_lists 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColorPointerListIBM (GLint size, GLenum type, GLint stride, const GLvoid* *pointer, GLint ptrstride); +GLAPI void APIENTRY glSecondaryColorPointerListIBM (GLint size, GLenum type, GLint stride, const GLvoid* *pointer, GLint ptrstride); +GLAPI void APIENTRY glEdgeFlagPointerListIBM (GLint stride, const GLboolean* *pointer, GLint ptrstride); +GLAPI void APIENTRY glFogCoordPointerListIBM (GLenum type, GLint stride, const GLvoid* *pointer, GLint ptrstride); +GLAPI void APIENTRY glIndexPointerListIBM (GLenum type, GLint stride, const GLvoid* *pointer, GLint ptrstride); +GLAPI void APIENTRY glNormalPointerListIBM (GLenum type, GLint stride, const GLvoid* *pointer, GLint ptrstride); +GLAPI void APIENTRY glTexCoordPointerListIBM (GLint size, GLenum type, GLint stride, const GLvoid* *pointer, GLint ptrstride); +GLAPI void APIENTRY glVertexPointerListIBM (GLint size, GLenum type, GLint stride, const GLvoid* *pointer, GLint ptrstride); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLCOLORPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const GLvoid* *pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLSECONDARYCOLORPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const GLvoid* *pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLEDGEFLAGPOINTERLISTIBMPROC) (GLint stride, const GLboolean* *pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLFOGCOORDPOINTERLISTIBMPROC) (GLenum type, GLint stride, const GLvoid* *pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLINDEXPOINTERLISTIBMPROC) (GLenum type, GLint stride, const GLvoid* *pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLNORMALPOINTERLISTIBMPROC) (GLenum type, GLint stride, const GLvoid* *pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLTEXCOORDPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const GLvoid* *pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLVERTEXPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const GLvoid* *pointer, GLint ptrstride); +#endif + +#ifndef GL_SGIX_subsample +#define GL_SGIX_subsample 1 +#endif + +#ifndef GL_SGIX_ycrcba +#define GL_SGIX_ycrcba 1 +#endif + +#ifndef GL_SGIX_ycrcb_subsample +#define GL_SGIX_ycrcb_subsample 1 +#endif + +#ifndef GL_SGIX_depth_pass_instrument +#define GL_SGIX_depth_pass_instrument 1 +#endif + +#ifndef GL_3DFX_texture_compression_FXT1 +#define GL_3DFX_texture_compression_FXT1 1 +#endif + +#ifndef GL_3DFX_multisample +#define GL_3DFX_multisample 1 +#endif + +#ifndef GL_3DFX_tbuffer +#define GL_3DFX_tbuffer 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTbufferMask3DFX (GLuint mask); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLTBUFFERMASK3DFXPROC) (GLuint mask); +#endif + +#ifndef GL_EXT_multisample +#define GL_EXT_multisample 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSampleMaskEXT (GLclampf value, GLboolean invert); +GLAPI void APIENTRY glSamplePatternEXT (GLenum pattern); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLSAMPLEMASKEXTPROC) (GLclampf value, GLboolean invert); +typedef void (APIENTRYP PFNGLSAMPLEPATTERNEXTPROC) (GLenum pattern); +#endif + +#ifndef GL_SGIX_vertex_preclip +#define GL_SGIX_vertex_preclip 1 +#endif + +#ifndef GL_SGIX_convolution_accuracy +#define GL_SGIX_convolution_accuracy 1 +#endif + +#ifndef GL_SGIX_resample +#define GL_SGIX_resample 1 +#endif + +#ifndef GL_SGIS_point_line_texgen +#define GL_SGIS_point_line_texgen 1 +#endif + +#ifndef GL_SGIS_texture_color_mask +#define GL_SGIS_texture_color_mask 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTextureColorMaskSGIS (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLTEXTURECOLORMASKSGISPROC) (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); +#endif + +#ifndef GL_SGIX_igloo_interface +#define GL_SGIX_igloo_interface 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glIglooInterfaceSGIX (GLenum pname, const GLvoid *params); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLIGLOOINTERFACESGIXPROC) (GLenum pname, const GLvoid *params); +#endif + +#ifndef GL_EXT_texture_env_dot3 +#define GL_EXT_texture_env_dot3 1 +#endif + +#ifndef GL_ATI_texture_mirror_once +#define GL_ATI_texture_mirror_once 1 +#endif + +#ifndef GL_NV_fence +#define GL_NV_fence 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDeleteFencesNV (GLsizei n, const GLuint *fences); +GLAPI void APIENTRY glGenFencesNV (GLsizei n, GLuint *fences); +GLAPI GLboolean APIENTRY glIsFenceNV (GLuint fence); +GLAPI GLboolean APIENTRY glTestFenceNV (GLuint fence); +GLAPI void APIENTRY glGetFenceivNV (GLuint fence, GLenum pname, GLint *params); +GLAPI void APIENTRY glFinishFenceNV (GLuint fence); +GLAPI void APIENTRY glSetFenceNV (GLuint fence, GLenum condition); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLDELETEFENCESNVPROC) (GLsizei n, const GLuint *fences); +typedef void (APIENTRYP PFNGLGENFENCESNVPROC) (GLsizei n, GLuint *fences); +typedef GLboolean (APIENTRYP PFNGLISFENCENVPROC) (GLuint fence); +typedef GLboolean (APIENTRYP PFNGLTESTFENCENVPROC) (GLuint fence); +typedef void (APIENTRYP PFNGLGETFENCEIVNVPROC) (GLuint fence, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLFINISHFENCENVPROC) (GLuint fence); +typedef void (APIENTRYP PFNGLSETFENCENVPROC) (GLuint fence, GLenum condition); +#endif + +#ifndef GL_NV_evaluators +#define GL_NV_evaluators 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMapControlPointsNV (GLenum target, GLuint index, GLenum type, GLsizei ustride, GLsizei vstride, GLint uorder, GLint vorder, GLboolean packed, const GLvoid *points); +GLAPI void APIENTRY glMapParameterivNV (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glMapParameterfvNV (GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glGetMapControlPointsNV (GLenum target, GLuint index, GLenum type, GLsizei ustride, GLsizei vstride, GLboolean packed, GLvoid *points); +GLAPI void APIENTRY glGetMapParameterivNV (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetMapParameterfvNV (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetMapAttribParameterivNV (GLenum target, GLuint index, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetMapAttribParameterfvNV (GLenum target, GLuint index, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glEvalMapsNV (GLenum target, GLenum mode); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLMAPCONTROLPOINTSNVPROC) (GLenum target, GLuint index, GLenum type, GLsizei ustride, GLsizei vstride, GLint uorder, GLint vorder, GLboolean packed, const GLvoid *points); +typedef void (APIENTRYP PFNGLMAPPARAMETERIVNVPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLMAPPARAMETERFVNVPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLGETMAPCONTROLPOINTSNVPROC) (GLenum target, GLuint index, GLenum type, GLsizei ustride, GLsizei vstride, GLboolean packed, GLvoid *points); +typedef void (APIENTRYP PFNGLGETMAPPARAMETERIVNVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETMAPPARAMETERFVNVPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETMAPATTRIBPARAMETERIVNVPROC) (GLenum target, GLuint index, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETMAPATTRIBPARAMETERFVNVPROC) (GLenum target, GLuint index, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLEVALMAPSNVPROC) (GLenum target, GLenum mode); +#endif + +#ifndef GL_NV_packed_depth_stencil +#define GL_NV_packed_depth_stencil 1 +#endif + +#ifndef GL_NV_register_combiners2 +#define GL_NV_register_combiners2 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCombinerStageParameterfvNV (GLenum stage, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glGetCombinerStageParameterfvNV (GLenum stage, GLenum pname, GLfloat *params); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLCOMBINERSTAGEPARAMETERFVNVPROC) (GLenum stage, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLGETCOMBINERSTAGEPARAMETERFVNVPROC) (GLenum stage, GLenum pname, GLfloat *params); +#endif + +#ifndef GL_NV_texture_compression_vtc +#define GL_NV_texture_compression_vtc 1 +#endif + +#ifndef GL_NV_texture_rectangle +#define GL_NV_texture_rectangle 1 +#endif + +#ifndef GL_NV_texture_shader +#define GL_NV_texture_shader 1 +#endif + +#ifndef GL_NV_texture_shader2 +#define GL_NV_texture_shader2 1 +#endif + +#ifndef GL_NV_vertex_array_range2 +#define GL_NV_vertex_array_range2 1 +#endif + +#ifndef GL_NV_vertex_program +#define GL_NV_vertex_program 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLboolean APIENTRY glAreProgramsResidentNV (GLsizei n, const GLuint *programs, GLboolean *residences); +GLAPI void APIENTRY glBindProgramNV (GLenum target, GLuint id); +GLAPI void APIENTRY glDeleteProgramsNV (GLsizei n, const GLuint *programs); +GLAPI void APIENTRY glExecuteProgramNV (GLenum target, GLuint id, const GLfloat *params); +GLAPI void APIENTRY glGenProgramsNV (GLsizei n, GLuint *programs); +GLAPI void APIENTRY glGetProgramParameterdvNV (GLenum target, GLuint index, GLenum pname, GLdouble *params); +GLAPI void APIENTRY glGetProgramParameterfvNV (GLenum target, GLuint index, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetProgramivNV (GLuint id, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetProgramStringNV (GLuint id, GLenum pname, GLubyte *program); +GLAPI void APIENTRY glGetTrackMatrixivNV (GLenum target, GLuint address, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVertexAttribdvNV (GLuint index, GLenum pname, GLdouble *params); +GLAPI void APIENTRY glGetVertexAttribfvNV (GLuint index, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetVertexAttribivNV (GLuint index, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVertexAttribPointervNV (GLuint index, GLenum pname, GLvoid* *pointer); +GLAPI GLboolean APIENTRY glIsProgramNV (GLuint id); +GLAPI void APIENTRY glLoadProgramNV (GLenum target, GLuint id, GLsizei len, const GLubyte *program); +GLAPI void APIENTRY glProgramParameter4dNV (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glProgramParameter4dvNV (GLenum target, GLuint index, const GLdouble *v); +GLAPI void APIENTRY glProgramParameter4fNV (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glProgramParameter4fvNV (GLenum target, GLuint index, const GLfloat *v); +GLAPI void APIENTRY glProgramParameters4dvNV (GLenum target, GLuint index, GLuint count, const GLdouble *v); +GLAPI void APIENTRY glProgramParameters4fvNV (GLenum target, GLuint index, GLuint count, const GLfloat *v); +GLAPI void APIENTRY glRequestResidentProgramsNV (GLsizei n, const GLuint *programs); +GLAPI void APIENTRY glTrackMatrixNV (GLenum target, GLuint address, GLenum matrix, GLenum transform); +GLAPI void APIENTRY glVertexAttribPointerNV (GLuint index, GLint fsize, GLenum type, GLsizei stride, const GLvoid *pointer); +GLAPI void APIENTRY glVertexAttrib1dNV (GLuint index, GLdouble x); +GLAPI void APIENTRY glVertexAttrib1dvNV (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib1fNV (GLuint index, GLfloat x); +GLAPI void APIENTRY glVertexAttrib1fvNV (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib1sNV (GLuint index, GLshort x); +GLAPI void APIENTRY glVertexAttrib1svNV (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib2dNV (GLuint index, GLdouble x, GLdouble y); +GLAPI void APIENTRY glVertexAttrib2dvNV (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib2fNV (GLuint index, GLfloat x, GLfloat y); +GLAPI void APIENTRY glVertexAttrib2fvNV (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib2sNV (GLuint index, GLshort x, GLshort y); +GLAPI void APIENTRY glVertexAttrib2svNV (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib3dNV (GLuint index, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glVertexAttrib3dvNV (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib3fNV (GLuint index, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glVertexAttrib3fvNV (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib3sNV (GLuint index, GLshort x, GLshort y, GLshort z); +GLAPI void APIENTRY glVertexAttrib3svNV (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4dNV (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glVertexAttrib4dvNV (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib4fNV (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glVertexAttrib4fvNV (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib4sNV (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +GLAPI void APIENTRY glVertexAttrib4svNV (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4ubNV (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +GLAPI void APIENTRY glVertexAttrib4ubvNV (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttribs1dvNV (GLuint index, GLsizei count, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribs1fvNV (GLuint index, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glVertexAttribs1svNV (GLuint index, GLsizei count, const GLshort *v); +GLAPI void APIENTRY glVertexAttribs2dvNV (GLuint index, GLsizei count, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribs2fvNV (GLuint index, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glVertexAttribs2svNV (GLuint index, GLsizei count, const GLshort *v); +GLAPI void APIENTRY glVertexAttribs3dvNV (GLuint index, GLsizei count, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribs3fvNV (GLuint index, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glVertexAttribs3svNV (GLuint index, GLsizei count, const GLshort *v); +GLAPI void APIENTRY glVertexAttribs4dvNV (GLuint index, GLsizei count, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribs4fvNV (GLuint index, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glVertexAttribs4svNV (GLuint index, GLsizei count, const GLshort *v); +GLAPI void APIENTRY glVertexAttribs4ubvNV (GLuint index, GLsizei count, const GLubyte *v); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef GLboolean (APIENTRYP PFNGLAREPROGRAMSRESIDENTNVPROC) (GLsizei n, const GLuint *programs, GLboolean *residences); +typedef void (APIENTRYP PFNGLBINDPROGRAMNVPROC) (GLenum target, GLuint id); +typedef void (APIENTRYP PFNGLDELETEPROGRAMSNVPROC) (GLsizei n, const GLuint *programs); +typedef void (APIENTRYP PFNGLEXECUTEPROGRAMNVPROC) (GLenum target, GLuint id, const GLfloat *params); +typedef void (APIENTRYP PFNGLGENPROGRAMSNVPROC) (GLsizei n, GLuint *programs); +typedef void (APIENTRYP PFNGLGETPROGRAMPARAMETERDVNVPROC) (GLenum target, GLuint index, GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLGETPROGRAMPARAMETERFVNVPROC) (GLenum target, GLuint index, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETPROGRAMIVNVPROC) (GLuint id, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMSTRINGNVPROC) (GLuint id, GLenum pname, GLubyte *program); +typedef void (APIENTRYP PFNGLGETTRACKMATRIXIVNVPROC) (GLenum target, GLuint address, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVNVPROC) (GLuint index, GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVNVPROC) (GLuint index, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVNVPROC) (GLuint index, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVNVPROC) (GLuint index, GLenum pname, GLvoid* *pointer); +typedef GLboolean (APIENTRYP PFNGLISPROGRAMNVPROC) (GLuint id); +typedef void (APIENTRYP PFNGLLOADPROGRAMNVPROC) (GLenum target, GLuint id, GLsizei len, const GLubyte *program); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETER4DNVPROC) (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETER4DVNVPROC) (GLenum target, GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETER4FNVPROC) (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETER4FVNVPROC) (GLenum target, GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETERS4DVNVPROC) (GLenum target, GLuint index, GLuint count, const GLdouble *v); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETERS4FVNVPROC) (GLenum target, GLuint index, GLuint count, const GLfloat *v); +typedef void (APIENTRYP PFNGLREQUESTRESIDENTPROGRAMSNVPROC) (GLsizei n, const GLuint *programs); +typedef void (APIENTRYP PFNGLTRACKMATRIXNVPROC) (GLenum target, GLuint address, GLenum matrix, GLenum transform); +typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERNVPROC) (GLuint index, GLint fsize, GLenum type, GLsizei stride, const GLvoid *pointer); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DNVPROC) (GLuint index, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DVNVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FNVPROC) (GLuint index, GLfloat x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVNVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SNVPROC) (GLuint index, GLshort x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SVNVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DNVPROC) (GLuint index, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DVNVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FNVPROC) (GLuint index, GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVNVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SNVPROC) (GLuint index, GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SVNVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DNVPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DVNVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FNVPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVNVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SNVPROC) (GLuint index, GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SVNVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DNVPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DVNVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FNVPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVNVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SNVPROC) (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SVNVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBNVPROC) (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVNVPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS1DVNVPROC) (GLuint index, GLsizei count, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS1FVNVPROC) (GLuint index, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS1SVNVPROC) (GLuint index, GLsizei count, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS2DVNVPROC) (GLuint index, GLsizei count, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS2FVNVPROC) (GLuint index, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS2SVNVPROC) (GLuint index, GLsizei count, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS3DVNVPROC) (GLuint index, GLsizei count, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS3FVNVPROC) (GLuint index, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS3SVNVPROC) (GLuint index, GLsizei count, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS4DVNVPROC) (GLuint index, GLsizei count, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS4FVNVPROC) (GLuint index, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS4SVNVPROC) (GLuint index, GLsizei count, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS4UBVNVPROC) (GLuint index, GLsizei count, const GLubyte *v); +#endif + +#ifndef GL_SGIX_texture_coordinate_clamp +#define GL_SGIX_texture_coordinate_clamp 1 +#endif + +#ifndef GL_SGIX_scalebias_hint +#define GL_SGIX_scalebias_hint 1 +#endif + +#ifndef GL_OML_interlace +#define GL_OML_interlace 1 +#endif + +#ifndef GL_OML_subsample +#define GL_OML_subsample 1 +#endif + +#ifndef GL_OML_resample +#define GL_OML_resample 1 +#endif + +#ifndef GL_NV_copy_depth_to_color +#define GL_NV_copy_depth_to_color 1 +#endif + +#ifndef GL_ATI_envmap_bumpmap +#define GL_ATI_envmap_bumpmap 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexBumpParameterivATI (GLenum pname, const GLint *param); +GLAPI void APIENTRY glTexBumpParameterfvATI (GLenum pname, const GLfloat *param); +GLAPI void APIENTRY glGetTexBumpParameterivATI (GLenum pname, GLint *param); +GLAPI void APIENTRY glGetTexBumpParameterfvATI (GLenum pname, GLfloat *param); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLTEXBUMPPARAMETERIVATIPROC) (GLenum pname, const GLint *param); +typedef void (APIENTRYP PFNGLTEXBUMPPARAMETERFVATIPROC) (GLenum pname, const GLfloat *param); +typedef void (APIENTRYP PFNGLGETTEXBUMPPARAMETERIVATIPROC) (GLenum pname, GLint *param); +typedef void (APIENTRYP PFNGLGETTEXBUMPPARAMETERFVATIPROC) (GLenum pname, GLfloat *param); +#endif + +#ifndef GL_ATI_fragment_shader +#define GL_ATI_fragment_shader 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLuint APIENTRY glGenFragmentShadersATI (GLuint range); +GLAPI void APIENTRY glBindFragmentShaderATI (GLuint id); +GLAPI void APIENTRY glDeleteFragmentShaderATI (GLuint id); +GLAPI void APIENTRY glBeginFragmentShaderATI (void); +GLAPI void APIENTRY glEndFragmentShaderATI (void); +GLAPI void APIENTRY glPassTexCoordATI (GLuint dst, GLuint coord, GLenum swizzle); +GLAPI void APIENTRY glSampleMapATI (GLuint dst, GLuint interp, GLenum swizzle); +GLAPI void APIENTRY glColorFragmentOp1ATI (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod); +GLAPI void APIENTRY glColorFragmentOp2ATI (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod); +GLAPI void APIENTRY glColorFragmentOp3ATI (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod, GLuint arg3, GLuint arg3Rep, GLuint arg3Mod); +GLAPI void APIENTRY glAlphaFragmentOp1ATI (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod); +GLAPI void APIENTRY glAlphaFragmentOp2ATI (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod); +GLAPI void APIENTRY glAlphaFragmentOp3ATI (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod, GLuint arg3, GLuint arg3Rep, GLuint arg3Mod); +GLAPI void APIENTRY glSetFragmentShaderConstantATI (GLuint dst, const GLfloat *value); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef GLuint (APIENTRYP PFNGLGENFRAGMENTSHADERSATIPROC) (GLuint range); +typedef void (APIENTRYP PFNGLBINDFRAGMENTSHADERATIPROC) (GLuint id); +typedef void (APIENTRYP PFNGLDELETEFRAGMENTSHADERATIPROC) (GLuint id); +typedef void (APIENTRYP PFNGLBEGINFRAGMENTSHADERATIPROC) (void); +typedef void (APIENTRYP PFNGLENDFRAGMENTSHADERATIPROC) (void); +typedef void (APIENTRYP PFNGLPASSTEXCOORDATIPROC) (GLuint dst, GLuint coord, GLenum swizzle); +typedef void (APIENTRYP PFNGLSAMPLEMAPATIPROC) (GLuint dst, GLuint interp, GLenum swizzle); +typedef void (APIENTRYP PFNGLCOLORFRAGMENTOP1ATIPROC) (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod); +typedef void (APIENTRYP PFNGLCOLORFRAGMENTOP2ATIPROC) (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod); +typedef void (APIENTRYP PFNGLCOLORFRAGMENTOP3ATIPROC) (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod, GLuint arg3, GLuint arg3Rep, GLuint arg3Mod); +typedef void (APIENTRYP PFNGLALPHAFRAGMENTOP1ATIPROC) (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod); +typedef void (APIENTRYP PFNGLALPHAFRAGMENTOP2ATIPROC) (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod); +typedef void (APIENTRYP PFNGLALPHAFRAGMENTOP3ATIPROC) (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod, GLuint arg3, GLuint arg3Rep, GLuint arg3Mod); +typedef void (APIENTRYP PFNGLSETFRAGMENTSHADERCONSTANTATIPROC) (GLuint dst, const GLfloat *value); +#endif + +#ifndef GL_ATI_pn_triangles +#define GL_ATI_pn_triangles 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPNTrianglesiATI (GLenum pname, GLint param); +GLAPI void APIENTRY glPNTrianglesfATI (GLenum pname, GLfloat param); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLPNTRIANGLESIATIPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLPNTRIANGLESFATIPROC) (GLenum pname, GLfloat param); +#endif + +#ifndef GL_ATI_vertex_array_object +#define GL_ATI_vertex_array_object 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLuint APIENTRY glNewObjectBufferATI (GLsizei size, const GLvoid *pointer, GLenum usage); +GLAPI GLboolean APIENTRY glIsObjectBufferATI (GLuint buffer); +GLAPI void APIENTRY glUpdateObjectBufferATI (GLuint buffer, GLuint offset, GLsizei size, const GLvoid *pointer, GLenum preserve); +GLAPI void APIENTRY glGetObjectBufferfvATI (GLuint buffer, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetObjectBufferivATI (GLuint buffer, GLenum pname, GLint *params); +GLAPI void APIENTRY glFreeObjectBufferATI (GLuint buffer); +GLAPI void APIENTRY glArrayObjectATI (GLenum array, GLint size, GLenum type, GLsizei stride, GLuint buffer, GLuint offset); +GLAPI void APIENTRY glGetArrayObjectfvATI (GLenum array, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetArrayObjectivATI (GLenum array, GLenum pname, GLint *params); +GLAPI void APIENTRY glVariantArrayObjectATI (GLuint id, GLenum type, GLsizei stride, GLuint buffer, GLuint offset); +GLAPI void APIENTRY glGetVariantArrayObjectfvATI (GLuint id, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetVariantArrayObjectivATI (GLuint id, GLenum pname, GLint *params); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef GLuint (APIENTRYP PFNGLNEWOBJECTBUFFERATIPROC) (GLsizei size, const GLvoid *pointer, GLenum usage); +typedef GLboolean (APIENTRYP PFNGLISOBJECTBUFFERATIPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLUPDATEOBJECTBUFFERATIPROC) (GLuint buffer, GLuint offset, GLsizei size, const GLvoid *pointer, GLenum preserve); +typedef void (APIENTRYP PFNGLGETOBJECTBUFFERFVATIPROC) (GLuint buffer, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETOBJECTBUFFERIVATIPROC) (GLuint buffer, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLFREEOBJECTBUFFERATIPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLARRAYOBJECTATIPROC) (GLenum array, GLint size, GLenum type, GLsizei stride, GLuint buffer, GLuint offset); +typedef void (APIENTRYP PFNGLGETARRAYOBJECTFVATIPROC) (GLenum array, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETARRAYOBJECTIVATIPROC) (GLenum array, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLVARIANTARRAYOBJECTATIPROC) (GLuint id, GLenum type, GLsizei stride, GLuint buffer, GLuint offset); +typedef void (APIENTRYP PFNGLGETVARIANTARRAYOBJECTFVATIPROC) (GLuint id, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETVARIANTARRAYOBJECTIVATIPROC) (GLuint id, GLenum pname, GLint *params); +#endif + +#ifndef GL_EXT_vertex_shader +#define GL_EXT_vertex_shader 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBeginVertexShaderEXT (void); +GLAPI void APIENTRY glEndVertexShaderEXT (void); +GLAPI void APIENTRY glBindVertexShaderEXT (GLuint id); +GLAPI GLuint APIENTRY glGenVertexShadersEXT (GLuint range); +GLAPI void APIENTRY glDeleteVertexShaderEXT (GLuint id); +GLAPI void APIENTRY glShaderOp1EXT (GLenum op, GLuint res, GLuint arg1); +GLAPI void APIENTRY glShaderOp2EXT (GLenum op, GLuint res, GLuint arg1, GLuint arg2); +GLAPI void APIENTRY glShaderOp3EXT (GLenum op, GLuint res, GLuint arg1, GLuint arg2, GLuint arg3); +GLAPI void APIENTRY glSwizzleEXT (GLuint res, GLuint in, GLenum outX, GLenum outY, GLenum outZ, GLenum outW); +GLAPI void APIENTRY glWriteMaskEXT (GLuint res, GLuint in, GLenum outX, GLenum outY, GLenum outZ, GLenum outW); +GLAPI void APIENTRY glInsertComponentEXT (GLuint res, GLuint src, GLuint num); +GLAPI void APIENTRY glExtractComponentEXT (GLuint res, GLuint src, GLuint num); +GLAPI GLuint APIENTRY glGenSymbolsEXT (GLenum datatype, GLenum storagetype, GLenum range, GLuint components); +GLAPI void APIENTRY glSetInvariantEXT (GLuint id, GLenum type, const GLvoid *addr); +GLAPI void APIENTRY glSetLocalConstantEXT (GLuint id, GLenum type, const GLvoid *addr); +GLAPI void APIENTRY glVariantbvEXT (GLuint id, const GLbyte *addr); +GLAPI void APIENTRY glVariantsvEXT (GLuint id, const GLshort *addr); +GLAPI void APIENTRY glVariantivEXT (GLuint id, const GLint *addr); +GLAPI void APIENTRY glVariantfvEXT (GLuint id, const GLfloat *addr); +GLAPI void APIENTRY glVariantdvEXT (GLuint id, const GLdouble *addr); +GLAPI void APIENTRY glVariantubvEXT (GLuint id, const GLubyte *addr); +GLAPI void APIENTRY glVariantusvEXT (GLuint id, const GLushort *addr); +GLAPI void APIENTRY glVariantuivEXT (GLuint id, const GLuint *addr); +GLAPI void APIENTRY glVariantPointerEXT (GLuint id, GLenum type, GLuint stride, const GLvoid *addr); +GLAPI void APIENTRY glEnableVariantClientStateEXT (GLuint id); +GLAPI void APIENTRY glDisableVariantClientStateEXT (GLuint id); +GLAPI GLuint APIENTRY glBindLightParameterEXT (GLenum light, GLenum value); +GLAPI GLuint APIENTRY glBindMaterialParameterEXT (GLenum face, GLenum value); +GLAPI GLuint APIENTRY glBindTexGenParameterEXT (GLenum unit, GLenum coord, GLenum value); +GLAPI GLuint APIENTRY glBindTextureUnitParameterEXT (GLenum unit, GLenum value); +GLAPI GLuint APIENTRY glBindParameterEXT (GLenum value); +GLAPI GLboolean APIENTRY glIsVariantEnabledEXT (GLuint id, GLenum cap); +GLAPI void APIENTRY glGetVariantBooleanvEXT (GLuint id, GLenum value, GLboolean *data); +GLAPI void APIENTRY glGetVariantIntegervEXT (GLuint id, GLenum value, GLint *data); +GLAPI void APIENTRY glGetVariantFloatvEXT (GLuint id, GLenum value, GLfloat *data); +GLAPI void APIENTRY glGetVariantPointervEXT (GLuint id, GLenum value, GLvoid* *data); +GLAPI void APIENTRY glGetInvariantBooleanvEXT (GLuint id, GLenum value, GLboolean *data); +GLAPI void APIENTRY glGetInvariantIntegervEXT (GLuint id, GLenum value, GLint *data); +GLAPI void APIENTRY glGetInvariantFloatvEXT (GLuint id, GLenum value, GLfloat *data); +GLAPI void APIENTRY glGetLocalConstantBooleanvEXT (GLuint id, GLenum value, GLboolean *data); +GLAPI void APIENTRY glGetLocalConstantIntegervEXT (GLuint id, GLenum value, GLint *data); +GLAPI void APIENTRY glGetLocalConstantFloatvEXT (GLuint id, GLenum value, GLfloat *data); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBEGINVERTEXSHADEREXTPROC) (void); +typedef void (APIENTRYP PFNGLENDVERTEXSHADEREXTPROC) (void); +typedef void (APIENTRYP PFNGLBINDVERTEXSHADEREXTPROC) (GLuint id); +typedef GLuint (APIENTRYP PFNGLGENVERTEXSHADERSEXTPROC) (GLuint range); +typedef void (APIENTRYP PFNGLDELETEVERTEXSHADEREXTPROC) (GLuint id); +typedef void (APIENTRYP PFNGLSHADEROP1EXTPROC) (GLenum op, GLuint res, GLuint arg1); +typedef void (APIENTRYP PFNGLSHADEROP2EXTPROC) (GLenum op, GLuint res, GLuint arg1, GLuint arg2); +typedef void (APIENTRYP PFNGLSHADEROP3EXTPROC) (GLenum op, GLuint res, GLuint arg1, GLuint arg2, GLuint arg3); +typedef void (APIENTRYP PFNGLSWIZZLEEXTPROC) (GLuint res, GLuint in, GLenum outX, GLenum outY, GLenum outZ, GLenum outW); +typedef void (APIENTRYP PFNGLWRITEMASKEXTPROC) (GLuint res, GLuint in, GLenum outX, GLenum outY, GLenum outZ, GLenum outW); +typedef void (APIENTRYP PFNGLINSERTCOMPONENTEXTPROC) (GLuint res, GLuint src, GLuint num); +typedef void (APIENTRYP PFNGLEXTRACTCOMPONENTEXTPROC) (GLuint res, GLuint src, GLuint num); +typedef GLuint (APIENTRYP PFNGLGENSYMBOLSEXTPROC) (GLenum datatype, GLenum storagetype, GLenum range, GLuint components); +typedef void (APIENTRYP PFNGLSETINVARIANTEXTPROC) (GLuint id, GLenum type, const GLvoid *addr); +typedef void (APIENTRYP PFNGLSETLOCALCONSTANTEXTPROC) (GLuint id, GLenum type, const GLvoid *addr); +typedef void (APIENTRYP PFNGLVARIANTBVEXTPROC) (GLuint id, const GLbyte *addr); +typedef void (APIENTRYP PFNGLVARIANTSVEXTPROC) (GLuint id, const GLshort *addr); +typedef void (APIENTRYP PFNGLVARIANTIVEXTPROC) (GLuint id, const GLint *addr); +typedef void (APIENTRYP PFNGLVARIANTFVEXTPROC) (GLuint id, const GLfloat *addr); +typedef void (APIENTRYP PFNGLVARIANTDVEXTPROC) (GLuint id, const GLdouble *addr); +typedef void (APIENTRYP PFNGLVARIANTUBVEXTPROC) (GLuint id, const GLubyte *addr); +typedef void (APIENTRYP PFNGLVARIANTUSVEXTPROC) (GLuint id, const GLushort *addr); +typedef void (APIENTRYP PFNGLVARIANTUIVEXTPROC) (GLuint id, const GLuint *addr); +typedef void (APIENTRYP PFNGLVARIANTPOINTEREXTPROC) (GLuint id, GLenum type, GLuint stride, const GLvoid *addr); +typedef void (APIENTRYP PFNGLENABLEVARIANTCLIENTSTATEEXTPROC) (GLuint id); +typedef void (APIENTRYP PFNGLDISABLEVARIANTCLIENTSTATEEXTPROC) (GLuint id); +typedef GLuint (APIENTRYP PFNGLBINDLIGHTPARAMETEREXTPROC) (GLenum light, GLenum value); +typedef GLuint (APIENTRYP PFNGLBINDMATERIALPARAMETEREXTPROC) (GLenum face, GLenum value); +typedef GLuint (APIENTRYP PFNGLBINDTEXGENPARAMETEREXTPROC) (GLenum unit, GLenum coord, GLenum value); +typedef GLuint (APIENTRYP PFNGLBINDTEXTUREUNITPARAMETEREXTPROC) (GLenum unit, GLenum value); +typedef GLuint (APIENTRYP PFNGLBINDPARAMETEREXTPROC) (GLenum value); +typedef GLboolean (APIENTRYP PFNGLISVARIANTENABLEDEXTPROC) (GLuint id, GLenum cap); +typedef void (APIENTRYP PFNGLGETVARIANTBOOLEANVEXTPROC) (GLuint id, GLenum value, GLboolean *data); +typedef void (APIENTRYP PFNGLGETVARIANTINTEGERVEXTPROC) (GLuint id, GLenum value, GLint *data); +typedef void (APIENTRYP PFNGLGETVARIANTFLOATVEXTPROC) (GLuint id, GLenum value, GLfloat *data); +typedef void (APIENTRYP PFNGLGETVARIANTPOINTERVEXTPROC) (GLuint id, GLenum value, GLvoid* *data); +typedef void (APIENTRYP PFNGLGETINVARIANTBOOLEANVEXTPROC) (GLuint id, GLenum value, GLboolean *data); +typedef void (APIENTRYP PFNGLGETINVARIANTINTEGERVEXTPROC) (GLuint id, GLenum value, GLint *data); +typedef void (APIENTRYP PFNGLGETINVARIANTFLOATVEXTPROC) (GLuint id, GLenum value, GLfloat *data); +typedef void (APIENTRYP PFNGLGETLOCALCONSTANTBOOLEANVEXTPROC) (GLuint id, GLenum value, GLboolean *data); +typedef void (APIENTRYP PFNGLGETLOCALCONSTANTINTEGERVEXTPROC) (GLuint id, GLenum value, GLint *data); +typedef void (APIENTRYP PFNGLGETLOCALCONSTANTFLOATVEXTPROC) (GLuint id, GLenum value, GLfloat *data); +#endif + +#ifndef GL_ATI_vertex_streams +#define GL_ATI_vertex_streams 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexStream1sATI (GLenum stream, GLshort x); +GLAPI void APIENTRY glVertexStream1svATI (GLenum stream, const GLshort *coords); +GLAPI void APIENTRY glVertexStream1iATI (GLenum stream, GLint x); +GLAPI void APIENTRY glVertexStream1ivATI (GLenum stream, const GLint *coords); +GLAPI void APIENTRY glVertexStream1fATI (GLenum stream, GLfloat x); +GLAPI void APIENTRY glVertexStream1fvATI (GLenum stream, const GLfloat *coords); +GLAPI void APIENTRY glVertexStream1dATI (GLenum stream, GLdouble x); +GLAPI void APIENTRY glVertexStream1dvATI (GLenum stream, const GLdouble *coords); +GLAPI void APIENTRY glVertexStream2sATI (GLenum stream, GLshort x, GLshort y); +GLAPI void APIENTRY glVertexStream2svATI (GLenum stream, const GLshort *coords); +GLAPI void APIENTRY glVertexStream2iATI (GLenum stream, GLint x, GLint y); +GLAPI void APIENTRY glVertexStream2ivATI (GLenum stream, const GLint *coords); +GLAPI void APIENTRY glVertexStream2fATI (GLenum stream, GLfloat x, GLfloat y); +GLAPI void APIENTRY glVertexStream2fvATI (GLenum stream, const GLfloat *coords); +GLAPI void APIENTRY glVertexStream2dATI (GLenum stream, GLdouble x, GLdouble y); +GLAPI void APIENTRY glVertexStream2dvATI (GLenum stream, const GLdouble *coords); +GLAPI void APIENTRY glVertexStream3sATI (GLenum stream, GLshort x, GLshort y, GLshort z); +GLAPI void APIENTRY glVertexStream3svATI (GLenum stream, const GLshort *coords); +GLAPI void APIENTRY glVertexStream3iATI (GLenum stream, GLint x, GLint y, GLint z); +GLAPI void APIENTRY glVertexStream3ivATI (GLenum stream, const GLint *coords); +GLAPI void APIENTRY glVertexStream3fATI (GLenum stream, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glVertexStream3fvATI (GLenum stream, const GLfloat *coords); +GLAPI void APIENTRY glVertexStream3dATI (GLenum stream, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glVertexStream3dvATI (GLenum stream, const GLdouble *coords); +GLAPI void APIENTRY glVertexStream4sATI (GLenum stream, GLshort x, GLshort y, GLshort z, GLshort w); +GLAPI void APIENTRY glVertexStream4svATI (GLenum stream, const GLshort *coords); +GLAPI void APIENTRY glVertexStream4iATI (GLenum stream, GLint x, GLint y, GLint z, GLint w); +GLAPI void APIENTRY glVertexStream4ivATI (GLenum stream, const GLint *coords); +GLAPI void APIENTRY glVertexStream4fATI (GLenum stream, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glVertexStream4fvATI (GLenum stream, const GLfloat *coords); +GLAPI void APIENTRY glVertexStream4dATI (GLenum stream, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glVertexStream4dvATI (GLenum stream, const GLdouble *coords); +GLAPI void APIENTRY glNormalStream3bATI (GLenum stream, GLbyte nx, GLbyte ny, GLbyte nz); +GLAPI void APIENTRY glNormalStream3bvATI (GLenum stream, const GLbyte *coords); +GLAPI void APIENTRY glNormalStream3sATI (GLenum stream, GLshort nx, GLshort ny, GLshort nz); +GLAPI void APIENTRY glNormalStream3svATI (GLenum stream, const GLshort *coords); +GLAPI void APIENTRY glNormalStream3iATI (GLenum stream, GLint nx, GLint ny, GLint nz); +GLAPI void APIENTRY glNormalStream3ivATI (GLenum stream, const GLint *coords); +GLAPI void APIENTRY glNormalStream3fATI (GLenum stream, GLfloat nx, GLfloat ny, GLfloat nz); +GLAPI void APIENTRY glNormalStream3fvATI (GLenum stream, const GLfloat *coords); +GLAPI void APIENTRY glNormalStream3dATI (GLenum stream, GLdouble nx, GLdouble ny, GLdouble nz); +GLAPI void APIENTRY glNormalStream3dvATI (GLenum stream, const GLdouble *coords); +GLAPI void APIENTRY glClientActiveVertexStreamATI (GLenum stream); +GLAPI void APIENTRY glVertexBlendEnviATI (GLenum pname, GLint param); +GLAPI void APIENTRY glVertexBlendEnvfATI (GLenum pname, GLfloat param); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLVERTEXSTREAM1SATIPROC) (GLenum stream, GLshort x); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1SVATIPROC) (GLenum stream, const GLshort *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1IATIPROC) (GLenum stream, GLint x); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1IVATIPROC) (GLenum stream, const GLint *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1FATIPROC) (GLenum stream, GLfloat x); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1FVATIPROC) (GLenum stream, const GLfloat *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1DATIPROC) (GLenum stream, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1DVATIPROC) (GLenum stream, const GLdouble *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2SATIPROC) (GLenum stream, GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2SVATIPROC) (GLenum stream, const GLshort *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2IATIPROC) (GLenum stream, GLint x, GLint y); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2IVATIPROC) (GLenum stream, const GLint *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2FATIPROC) (GLenum stream, GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2FVATIPROC) (GLenum stream, const GLfloat *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2DATIPROC) (GLenum stream, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2DVATIPROC) (GLenum stream, const GLdouble *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3SATIPROC) (GLenum stream, GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3SVATIPROC) (GLenum stream, const GLshort *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3IATIPROC) (GLenum stream, GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3IVATIPROC) (GLenum stream, const GLint *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3FATIPROC) (GLenum stream, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3FVATIPROC) (GLenum stream, const GLfloat *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3DATIPROC) (GLenum stream, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3DVATIPROC) (GLenum stream, const GLdouble *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4SATIPROC) (GLenum stream, GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4SVATIPROC) (GLenum stream, const GLshort *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4IATIPROC) (GLenum stream, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4IVATIPROC) (GLenum stream, const GLint *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4FATIPROC) (GLenum stream, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4FVATIPROC) (GLenum stream, const GLfloat *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4DATIPROC) (GLenum stream, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4DVATIPROC) (GLenum stream, const GLdouble *coords); +typedef void (APIENTRYP PFNGLNORMALSTREAM3BATIPROC) (GLenum stream, GLbyte nx, GLbyte ny, GLbyte nz); +typedef void (APIENTRYP PFNGLNORMALSTREAM3BVATIPROC) (GLenum stream, const GLbyte *coords); +typedef void (APIENTRYP PFNGLNORMALSTREAM3SATIPROC) (GLenum stream, GLshort nx, GLshort ny, GLshort nz); +typedef void (APIENTRYP PFNGLNORMALSTREAM3SVATIPROC) (GLenum stream, const GLshort *coords); +typedef void (APIENTRYP PFNGLNORMALSTREAM3IATIPROC) (GLenum stream, GLint nx, GLint ny, GLint nz); +typedef void (APIENTRYP PFNGLNORMALSTREAM3IVATIPROC) (GLenum stream, const GLint *coords); +typedef void (APIENTRYP PFNGLNORMALSTREAM3FATIPROC) (GLenum stream, GLfloat nx, GLfloat ny, GLfloat nz); +typedef void (APIENTRYP PFNGLNORMALSTREAM3FVATIPROC) (GLenum stream, const GLfloat *coords); +typedef void (APIENTRYP PFNGLNORMALSTREAM3DATIPROC) (GLenum stream, GLdouble nx, GLdouble ny, GLdouble nz); +typedef void (APIENTRYP PFNGLNORMALSTREAM3DVATIPROC) (GLenum stream, const GLdouble *coords); +typedef void (APIENTRYP PFNGLCLIENTACTIVEVERTEXSTREAMATIPROC) (GLenum stream); +typedef void (APIENTRYP PFNGLVERTEXBLENDENVIATIPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLVERTEXBLENDENVFATIPROC) (GLenum pname, GLfloat param); +#endif + +#ifndef GL_ATI_element_array +#define GL_ATI_element_array 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glElementPointerATI (GLenum type, const GLvoid *pointer); +GLAPI void APIENTRY glDrawElementArrayATI (GLenum mode, GLsizei count); +GLAPI void APIENTRY glDrawRangeElementArrayATI (GLenum mode, GLuint start, GLuint end, GLsizei count); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLELEMENTPOINTERATIPROC) (GLenum type, const GLvoid *pointer); +typedef void (APIENTRYP PFNGLDRAWELEMENTARRAYATIPROC) (GLenum mode, GLsizei count); +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTARRAYATIPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count); +#endif + +#ifndef GL_SUN_mesh_array +#define GL_SUN_mesh_array 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawMeshArraysSUN (GLenum mode, GLint first, GLsizei count, GLsizei width); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLDRAWMESHARRAYSSUNPROC) (GLenum mode, GLint first, GLsizei count, GLsizei width); +#endif + +#ifndef GL_SUN_slice_accum +#define GL_SUN_slice_accum 1 +#endif + +#ifndef GL_NV_multisample_filter_hint +#define GL_NV_multisample_filter_hint 1 +#endif + +#ifndef GL_NV_depth_clamp +#define GL_NV_depth_clamp 1 +#endif + +#ifndef GL_NV_occlusion_query +#define GL_NV_occlusion_query 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGenOcclusionQueriesNV (GLsizei n, GLuint *ids); +GLAPI void APIENTRY glDeleteOcclusionQueriesNV (GLsizei n, const GLuint *ids); +GLAPI GLboolean APIENTRY glIsOcclusionQueryNV (GLuint id); +GLAPI void APIENTRY glBeginOcclusionQueryNV (GLuint id); +GLAPI void APIENTRY glEndOcclusionQueryNV (void); +GLAPI void APIENTRY glGetOcclusionQueryivNV (GLuint id, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetOcclusionQueryuivNV (GLuint id, GLenum pname, GLuint *params); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLGENOCCLUSIONQUERIESNVPROC) (GLsizei n, GLuint *ids); +typedef void (APIENTRYP PFNGLDELETEOCCLUSIONQUERIESNVPROC) (GLsizei n, const GLuint *ids); +typedef GLboolean (APIENTRYP PFNGLISOCCLUSIONQUERYNVPROC) (GLuint id); +typedef void (APIENTRYP PFNGLBEGINOCCLUSIONQUERYNVPROC) (GLuint id); +typedef void (APIENTRYP PFNGLENDOCCLUSIONQUERYNVPROC) (void); +typedef void (APIENTRYP PFNGLGETOCCLUSIONQUERYIVNVPROC) (GLuint id, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETOCCLUSIONQUERYUIVNVPROC) (GLuint id, GLenum pname, GLuint *params); +#endif + +#ifndef GL_NV_point_sprite +#define GL_NV_point_sprite 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPointParameteriNV (GLenum pname, GLint param); +GLAPI void APIENTRY glPointParameterivNV (GLenum pname, const GLint *params); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLPOINTPARAMETERINVPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERIVNVPROC) (GLenum pname, const GLint *params); +#endif + +#ifndef GL_NV_texture_shader3 +#define GL_NV_texture_shader3 1 +#endif + +#ifndef GL_NV_vertex_program1_1 +#define GL_NV_vertex_program1_1 1 +#endif + +#ifndef GL_EXT_shadow_funcs +#define GL_EXT_shadow_funcs 1 +#endif + +#ifndef GL_EXT_stencil_two_side +#define GL_EXT_stencil_two_side 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glActiveStencilFaceEXT (GLenum face); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLACTIVESTENCILFACEEXTPROC) (GLenum face); +#endif + +#ifndef GL_ATI_text_fragment_shader +#define GL_ATI_text_fragment_shader 1 +#endif + +#ifndef GL_APPLE_client_storage +#define GL_APPLE_client_storage 1 +#endif + +#ifndef GL_APPLE_element_array +#define GL_APPLE_element_array 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glElementPointerAPPLE (GLenum type, const GLvoid *pointer); +GLAPI void APIENTRY glDrawElementArrayAPPLE (GLenum mode, GLint first, GLsizei count); +GLAPI void APIENTRY glDrawRangeElementArrayAPPLE (GLenum mode, GLuint start, GLuint end, GLint first, GLsizei count); +GLAPI void APIENTRY glMultiDrawElementArrayAPPLE (GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount); +GLAPI void APIENTRY glMultiDrawRangeElementArrayAPPLE (GLenum mode, GLuint start, GLuint end, const GLint *first, const GLsizei *count, GLsizei primcount); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLELEMENTPOINTERAPPLEPROC) (GLenum type, const GLvoid *pointer); +typedef void (APIENTRYP PFNGLDRAWELEMENTARRAYAPPLEPROC) (GLenum mode, GLint first, GLsizei count); +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTARRAYAPPLEPROC) (GLenum mode, GLuint start, GLuint end, GLint first, GLsizei count); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTARRAYAPPLEPROC) (GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount); +typedef void (APIENTRYP PFNGLMULTIDRAWRANGEELEMENTARRAYAPPLEPROC) (GLenum mode, GLuint start, GLuint end, const GLint *first, const GLsizei *count, GLsizei primcount); +#endif + +#ifndef GL_APPLE_fence +#define GL_APPLE_fence 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGenFencesAPPLE (GLsizei n, GLuint *fences); +GLAPI void APIENTRY glDeleteFencesAPPLE (GLsizei n, const GLuint *fences); +GLAPI void APIENTRY glSetFenceAPPLE (GLuint fence); +GLAPI GLboolean APIENTRY glIsFenceAPPLE (GLuint fence); +GLAPI GLboolean APIENTRY glTestFenceAPPLE (GLuint fence); +GLAPI void APIENTRY glFinishFenceAPPLE (GLuint fence); +GLAPI GLboolean APIENTRY glTestObjectAPPLE (GLenum object, GLuint name); +GLAPI void APIENTRY glFinishObjectAPPLE (GLenum object, GLint name); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLGENFENCESAPPLEPROC) (GLsizei n, GLuint *fences); +typedef void (APIENTRYP PFNGLDELETEFENCESAPPLEPROC) (GLsizei n, const GLuint *fences); +typedef void (APIENTRYP PFNGLSETFENCEAPPLEPROC) (GLuint fence); +typedef GLboolean (APIENTRYP PFNGLISFENCEAPPLEPROC) (GLuint fence); +typedef GLboolean (APIENTRYP PFNGLTESTFENCEAPPLEPROC) (GLuint fence); +typedef void (APIENTRYP PFNGLFINISHFENCEAPPLEPROC) (GLuint fence); +typedef GLboolean (APIENTRYP PFNGLTESTOBJECTAPPLEPROC) (GLenum object, GLuint name); +typedef void (APIENTRYP PFNGLFINISHOBJECTAPPLEPROC) (GLenum object, GLint name); +#endif + +#ifndef GL_APPLE_vertex_array_object +#define GL_APPLE_vertex_array_object 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBindVertexArrayAPPLE (GLuint array); +GLAPI void APIENTRY glDeleteVertexArraysAPPLE (GLsizei n, const GLuint *arrays); +GLAPI void APIENTRY glGenVertexArraysAPPLE (GLsizei n, GLuint *arrays); +GLAPI GLboolean APIENTRY glIsVertexArrayAPPLE (GLuint array); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBINDVERTEXARRAYAPPLEPROC) (GLuint array); +typedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSAPPLEPROC) (GLsizei n, const GLuint *arrays); +typedef void (APIENTRYP PFNGLGENVERTEXARRAYSAPPLEPROC) (GLsizei n, GLuint *arrays); +typedef GLboolean (APIENTRYP PFNGLISVERTEXARRAYAPPLEPROC) (GLuint array); +#endif + +#ifndef GL_APPLE_vertex_array_range +#define GL_APPLE_vertex_array_range 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexArrayRangeAPPLE (GLsizei length, GLvoid *pointer); +GLAPI void APIENTRY glFlushVertexArrayRangeAPPLE (GLsizei length, GLvoid *pointer); +GLAPI void APIENTRY glVertexArrayParameteriAPPLE (GLenum pname, GLint param); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLVERTEXARRAYRANGEAPPLEPROC) (GLsizei length, GLvoid *pointer); +typedef void (APIENTRYP PFNGLFLUSHVERTEXARRAYRANGEAPPLEPROC) (GLsizei length, GLvoid *pointer); +typedef void (APIENTRYP PFNGLVERTEXARRAYPARAMETERIAPPLEPROC) (GLenum pname, GLint param); +#endif + +#ifndef GL_APPLE_ycbcr_422 +#define GL_APPLE_ycbcr_422 1 +#endif + +#ifndef GL_S3_s3tc +#define GL_S3_s3tc 1 +#endif + +#ifndef GL_ATI_draw_buffers +#define GL_ATI_draw_buffers 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawBuffersATI (GLsizei n, const GLenum *bufs); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLDRAWBUFFERSATIPROC) (GLsizei n, const GLenum *bufs); +#endif + +#ifndef GL_ATI_pixel_format_float +#define GL_ATI_pixel_format_float 1 +/* This is really a WGL extension, but defines some associated GL enums. + * ATI does not export "GL_ATI_pixel_format_float" in the GL_EXTENSIONS string. + */ +#endif + +#ifndef GL_ATI_texture_env_combine3 +#define GL_ATI_texture_env_combine3 1 +#endif + +#ifndef GL_ATI_texture_float +#define GL_ATI_texture_float 1 +#endif + +#ifndef GL_NV_float_buffer +#define GL_NV_float_buffer 1 +#endif + +#ifndef GL_NV_fragment_program +#define GL_NV_fragment_program 1 +/* Some NV_fragment_program entry points are shared with ARB_vertex_program. */ +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramNamedParameter4fNV (GLuint id, GLsizei len, const GLubyte *name, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glProgramNamedParameter4dNV (GLuint id, GLsizei len, const GLubyte *name, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glProgramNamedParameter4fvNV (GLuint id, GLsizei len, const GLubyte *name, const GLfloat *v); +GLAPI void APIENTRY glProgramNamedParameter4dvNV (GLuint id, GLsizei len, const GLubyte *name, const GLdouble *v); +GLAPI void APIENTRY glGetProgramNamedParameterfvNV (GLuint id, GLsizei len, const GLubyte *name, GLfloat *params); +GLAPI void APIENTRY glGetProgramNamedParameterdvNV (GLuint id, GLsizei len, const GLubyte *name, GLdouble *params); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4FNVPROC) (GLuint id, GLsizei len, const GLubyte *name, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4DNVPROC) (GLuint id, GLsizei len, const GLubyte *name, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4FVNVPROC) (GLuint id, GLsizei len, const GLubyte *name, const GLfloat *v); +typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4DVNVPROC) (GLuint id, GLsizei len, const GLubyte *name, const GLdouble *v); +typedef void (APIENTRYP PFNGLGETPROGRAMNAMEDPARAMETERFVNVPROC) (GLuint id, GLsizei len, const GLubyte *name, GLfloat *params); +typedef void (APIENTRYP PFNGLGETPROGRAMNAMEDPARAMETERDVNVPROC) (GLuint id, GLsizei len, const GLubyte *name, GLdouble *params); +#endif + +#ifndef GL_NV_half_float +#define GL_NV_half_float 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertex2hNV (GLhalfNV x, GLhalfNV y); +GLAPI void APIENTRY glVertex2hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glVertex3hNV (GLhalfNV x, GLhalfNV y, GLhalfNV z); +GLAPI void APIENTRY glVertex3hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glVertex4hNV (GLhalfNV x, GLhalfNV y, GLhalfNV z, GLhalfNV w); +GLAPI void APIENTRY glVertex4hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glNormal3hNV (GLhalfNV nx, GLhalfNV ny, GLhalfNV nz); +GLAPI void APIENTRY glNormal3hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glColor3hNV (GLhalfNV red, GLhalfNV green, GLhalfNV blue); +GLAPI void APIENTRY glColor3hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glColor4hNV (GLhalfNV red, GLhalfNV green, GLhalfNV blue, GLhalfNV alpha); +GLAPI void APIENTRY glColor4hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glTexCoord1hNV (GLhalfNV s); +GLAPI void APIENTRY glTexCoord1hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glTexCoord2hNV (GLhalfNV s, GLhalfNV t); +GLAPI void APIENTRY glTexCoord2hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glTexCoord3hNV (GLhalfNV s, GLhalfNV t, GLhalfNV r); +GLAPI void APIENTRY glTexCoord3hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glTexCoord4hNV (GLhalfNV s, GLhalfNV t, GLhalfNV r, GLhalfNV q); +GLAPI void APIENTRY glTexCoord4hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glMultiTexCoord1hNV (GLenum target, GLhalfNV s); +GLAPI void APIENTRY glMultiTexCoord1hvNV (GLenum target, const GLhalfNV *v); +GLAPI void APIENTRY glMultiTexCoord2hNV (GLenum target, GLhalfNV s, GLhalfNV t); +GLAPI void APIENTRY glMultiTexCoord2hvNV (GLenum target, const GLhalfNV *v); +GLAPI void APIENTRY glMultiTexCoord3hNV (GLenum target, GLhalfNV s, GLhalfNV t, GLhalfNV r); +GLAPI void APIENTRY glMultiTexCoord3hvNV (GLenum target, const GLhalfNV *v); +GLAPI void APIENTRY glMultiTexCoord4hNV (GLenum target, GLhalfNV s, GLhalfNV t, GLhalfNV r, GLhalfNV q); +GLAPI void APIENTRY glMultiTexCoord4hvNV (GLenum target, const GLhalfNV *v); +GLAPI void APIENTRY glFogCoordhNV (GLhalfNV fog); +GLAPI void APIENTRY glFogCoordhvNV (const GLhalfNV *fog); +GLAPI void APIENTRY glSecondaryColor3hNV (GLhalfNV red, GLhalfNV green, GLhalfNV blue); +GLAPI void APIENTRY glSecondaryColor3hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glVertexWeighthNV (GLhalfNV weight); +GLAPI void APIENTRY glVertexWeighthvNV (const GLhalfNV *weight); +GLAPI void APIENTRY glVertexAttrib1hNV (GLuint index, GLhalfNV x); +GLAPI void APIENTRY glVertexAttrib1hvNV (GLuint index, const GLhalfNV *v); +GLAPI void APIENTRY glVertexAttrib2hNV (GLuint index, GLhalfNV x, GLhalfNV y); +GLAPI void APIENTRY glVertexAttrib2hvNV (GLuint index, const GLhalfNV *v); +GLAPI void APIENTRY glVertexAttrib3hNV (GLuint index, GLhalfNV x, GLhalfNV y, GLhalfNV z); +GLAPI void APIENTRY glVertexAttrib3hvNV (GLuint index, const GLhalfNV *v); +GLAPI void APIENTRY glVertexAttrib4hNV (GLuint index, GLhalfNV x, GLhalfNV y, GLhalfNV z, GLhalfNV w); +GLAPI void APIENTRY glVertexAttrib4hvNV (GLuint index, const GLhalfNV *v); +GLAPI void APIENTRY glVertexAttribs1hvNV (GLuint index, GLsizei n, const GLhalfNV *v); +GLAPI void APIENTRY glVertexAttribs2hvNV (GLuint index, GLsizei n, const GLhalfNV *v); +GLAPI void APIENTRY glVertexAttribs3hvNV (GLuint index, GLsizei n, const GLhalfNV *v); +GLAPI void APIENTRY glVertexAttribs4hvNV (GLuint index, GLsizei n, const GLhalfNV *v); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLVERTEX2HNVPROC) (GLhalfNV x, GLhalfNV y); +typedef void (APIENTRYP PFNGLVERTEX2HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEX3HNVPROC) (GLhalfNV x, GLhalfNV y, GLhalfNV z); +typedef void (APIENTRYP PFNGLVERTEX3HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEX4HNVPROC) (GLhalfNV x, GLhalfNV y, GLhalfNV z, GLhalfNV w); +typedef void (APIENTRYP PFNGLVERTEX4HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLNORMAL3HNVPROC) (GLhalfNV nx, GLhalfNV ny, GLhalfNV nz); +typedef void (APIENTRYP PFNGLNORMAL3HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLCOLOR3HNVPROC) (GLhalfNV red, GLhalfNV green, GLhalfNV blue); +typedef void (APIENTRYP PFNGLCOLOR3HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLCOLOR4HNVPROC) (GLhalfNV red, GLhalfNV green, GLhalfNV blue, GLhalfNV alpha); +typedef void (APIENTRYP PFNGLCOLOR4HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLTEXCOORD1HNVPROC) (GLhalfNV s); +typedef void (APIENTRYP PFNGLTEXCOORD1HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLTEXCOORD2HNVPROC) (GLhalfNV s, GLhalfNV t); +typedef void (APIENTRYP PFNGLTEXCOORD2HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLTEXCOORD3HNVPROC) (GLhalfNV s, GLhalfNV t, GLhalfNV r); +typedef void (APIENTRYP PFNGLTEXCOORD3HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLTEXCOORD4HNVPROC) (GLhalfNV s, GLhalfNV t, GLhalfNV r, GLhalfNV q); +typedef void (APIENTRYP PFNGLTEXCOORD4HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1HNVPROC) (GLenum target, GLhalfNV s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1HVNVPROC) (GLenum target, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2HNVPROC) (GLenum target, GLhalfNV s, GLhalfNV t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2HVNVPROC) (GLenum target, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3HNVPROC) (GLenum target, GLhalfNV s, GLhalfNV t, GLhalfNV r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3HVNVPROC) (GLenum target, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4HNVPROC) (GLenum target, GLhalfNV s, GLhalfNV t, GLhalfNV r, GLhalfNV q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4HVNVPROC) (GLenum target, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLFOGCOORDHNVPROC) (GLhalfNV fog); +typedef void (APIENTRYP PFNGLFOGCOORDHVNVPROC) (const GLhalfNV *fog); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3HNVPROC) (GLhalfNV red, GLhalfNV green, GLhalfNV blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXWEIGHTHNVPROC) (GLhalfNV weight); +typedef void (APIENTRYP PFNGLVERTEXWEIGHTHVNVPROC) (const GLhalfNV *weight); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1HNVPROC) (GLuint index, GLhalfNV x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1HVNVPROC) (GLuint index, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2HNVPROC) (GLuint index, GLhalfNV x, GLhalfNV y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2HVNVPROC) (GLuint index, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3HNVPROC) (GLuint index, GLhalfNV x, GLhalfNV y, GLhalfNV z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3HVNVPROC) (GLuint index, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4HNVPROC) (GLuint index, GLhalfNV x, GLhalfNV y, GLhalfNV z, GLhalfNV w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4HVNVPROC) (GLuint index, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS1HVNVPROC) (GLuint index, GLsizei n, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS2HVNVPROC) (GLuint index, GLsizei n, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS3HVNVPROC) (GLuint index, GLsizei n, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS4HVNVPROC) (GLuint index, GLsizei n, const GLhalfNV *v); +#endif + +#ifndef GL_NV_pixel_data_range +#define GL_NV_pixel_data_range 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPixelDataRangeNV (GLenum target, GLsizei length, GLvoid *pointer); +GLAPI void APIENTRY glFlushPixelDataRangeNV (GLenum target); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLPIXELDATARANGENVPROC) (GLenum target, GLsizei length, GLvoid *pointer); +typedef void (APIENTRYP PFNGLFLUSHPIXELDATARANGENVPROC) (GLenum target); +#endif + +#ifndef GL_NV_primitive_restart +#define GL_NV_primitive_restart 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPrimitiveRestartNV (void); +GLAPI void APIENTRY glPrimitiveRestartIndexNV (GLuint index); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLPRIMITIVERESTARTNVPROC) (void); +typedef void (APIENTRYP PFNGLPRIMITIVERESTARTINDEXNVPROC) (GLuint index); +#endif + +#ifndef GL_NV_texture_expand_normal +#define GL_NV_texture_expand_normal 1 +#endif + +#ifndef GL_NV_vertex_program2 +#define GL_NV_vertex_program2 1 +#endif + +#ifndef GL_ATI_map_object_buffer +#define GL_ATI_map_object_buffer 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLvoid* APIENTRY glMapObjectBufferATI (GLuint buffer); +GLAPI void APIENTRY glUnmapObjectBufferATI (GLuint buffer); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef GLvoid* (APIENTRYP PFNGLMAPOBJECTBUFFERATIPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLUNMAPOBJECTBUFFERATIPROC) (GLuint buffer); +#endif + +#ifndef GL_ATI_separate_stencil +#define GL_ATI_separate_stencil 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glStencilOpSeparateATI (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); +GLAPI void APIENTRY glStencilFuncSeparateATI (GLenum frontfunc, GLenum backfunc, GLint ref, GLuint mask); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLSTENCILOPSEPARATEATIPROC) (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); +typedef void (APIENTRYP PFNGLSTENCILFUNCSEPARATEATIPROC) (GLenum frontfunc, GLenum backfunc, GLint ref, GLuint mask); +#endif + +#ifndef GL_ATI_vertex_attrib_array_object +#define GL_ATI_vertex_attrib_array_object 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexAttribArrayObjectATI (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLuint buffer, GLuint offset); +GLAPI void APIENTRY glGetVertexAttribArrayObjectfvATI (GLuint index, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetVertexAttribArrayObjectivATI (GLuint index, GLenum pname, GLint *params); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLVERTEXATTRIBARRAYOBJECTATIPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLuint buffer, GLuint offset); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBARRAYOBJECTFVATIPROC) (GLuint index, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBARRAYOBJECTIVATIPROC) (GLuint index, GLenum pname, GLint *params); +#endif + +#ifndef GL_OES_read_format +#define GL_OES_read_format 1 +#endif + +#ifndef GL_EXT_depth_bounds_test +#define GL_EXT_depth_bounds_test 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDepthBoundsEXT (GLclampd zmin, GLclampd zmax); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLDEPTHBOUNDSEXTPROC) (GLclampd zmin, GLclampd zmax); +#endif + +#ifndef GL_EXT_texture_mirror_clamp +#define GL_EXT_texture_mirror_clamp 1 +#endif + +#ifndef GL_EXT_blend_equation_separate +#define GL_EXT_blend_equation_separate 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendEquationSeparateEXT (GLenum modeRGB, GLenum modeAlpha); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEEXTPROC) (GLenum modeRGB, GLenum modeAlpha); +#endif + +#ifndef GL_MESA_pack_invert +#define GL_MESA_pack_invert 1 +#endif + +#ifndef GL_MESA_ycbcr_texture +#define GL_MESA_ycbcr_texture 1 +#endif + +#ifndef GL_EXT_pixel_buffer_object +#define GL_EXT_pixel_buffer_object 1 +#endif + +#ifndef GL_NV_fragment_program_option +#define GL_NV_fragment_program_option 1 +#endif + +#ifndef GL_NV_fragment_program2 +#define GL_NV_fragment_program2 1 +#endif + +#ifndef GL_NV_vertex_program2_option +#define GL_NV_vertex_program2_option 1 +#endif + +#ifndef GL_NV_vertex_program3 +#define GL_NV_vertex_program3 1 +#endif + +#ifndef GL_EXT_framebuffer_object +#define GL_EXT_framebuffer_object 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLboolean APIENTRY glIsRenderbufferEXT (GLuint renderbuffer); +GLAPI void APIENTRY glBindRenderbufferEXT (GLenum target, GLuint renderbuffer); +GLAPI void APIENTRY glDeleteRenderbuffersEXT (GLsizei n, const GLuint *renderbuffers); +GLAPI void APIENTRY glGenRenderbuffersEXT (GLsizei n, GLuint *renderbuffers); +GLAPI void APIENTRY glRenderbufferStorageEXT (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetRenderbufferParameterivEXT (GLenum target, GLenum pname, GLint *params); +GLAPI GLboolean APIENTRY glIsFramebufferEXT (GLuint framebuffer); +GLAPI void APIENTRY glBindFramebufferEXT (GLenum target, GLuint framebuffer); +GLAPI void APIENTRY glDeleteFramebuffersEXT (GLsizei n, const GLuint *framebuffers); +GLAPI void APIENTRY glGenFramebuffersEXT (GLsizei n, GLuint *framebuffers); +GLAPI GLenum APIENTRY glCheckFramebufferStatusEXT (GLenum target); +GLAPI void APIENTRY glFramebufferTexture1DEXT (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GLAPI void APIENTRY glFramebufferTexture2DEXT (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GLAPI void APIENTRY glFramebufferTexture3DEXT (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +GLAPI void APIENTRY glFramebufferRenderbufferEXT (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +GLAPI void APIENTRY glGetFramebufferAttachmentParameterivEXT (GLenum target, GLenum attachment, GLenum pname, GLint *params); +GLAPI void APIENTRY glGenerateMipmapEXT (GLenum target); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef GLboolean (APIENTRYP PFNGLISRENDERBUFFEREXTPROC) (GLuint renderbuffer); +typedef void (APIENTRYP PFNGLBINDRENDERBUFFEREXTPROC) (GLenum target, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLDELETERENDERBUFFERSEXTPROC) (GLsizei n, const GLuint *renderbuffers); +typedef void (APIENTRYP PFNGLGENRENDERBUFFERSEXTPROC) (GLsizei n, GLuint *renderbuffers); +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef GLboolean (APIENTRYP PFNGLISFRAMEBUFFEREXTPROC) (GLuint framebuffer); +typedef void (APIENTRYP PFNGLBINDFRAMEBUFFEREXTPROC) (GLenum target, GLuint framebuffer); +typedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSEXTPROC) (GLsizei n, const GLuint *framebuffers); +typedef void (APIENTRYP PFNGLGENFRAMEBUFFERSEXTPROC) (GLsizei n, GLuint *framebuffers); +typedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC) (GLenum target); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE1DEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +typedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC) (GLenum target, GLenum attachment, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGENERATEMIPMAPEXTPROC) (GLenum target); +#endif + +#ifndef GL_GREMEDY_string_marker +#define GL_GREMEDY_string_marker 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glStringMarkerGREMEDY (GLsizei len, const GLvoid *string); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLSTRINGMARKERGREMEDYPROC) (GLsizei len, const GLvoid *string); +#endif + +#ifndef GL_EXT_packed_depth_stencil +#define GL_EXT_packed_depth_stencil 1 +#endif + +#ifndef GL_EXT_stencil_clear_tag +#define GL_EXT_stencil_clear_tag 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glStencilClearTagEXT (GLsizei stencilTagBits, GLuint stencilClearTag); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLSTENCILCLEARTAGEXTPROC) (GLsizei stencilTagBits, GLuint stencilClearTag); +#endif + +#ifndef GL_EXT_texture_sRGB +#define GL_EXT_texture_sRGB 1 +#endif + +#ifndef GL_EXT_framebuffer_blit +#define GL_EXT_framebuffer_blit 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlitFramebufferEXT (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBLITFRAMEBUFFEREXTPROC) (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +#endif + +#ifndef GL_EXT_framebuffer_multisample +#define GL_EXT_framebuffer_multisample 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glRenderbufferStorageMultisampleEXT (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +#endif + +#ifndef GL_MESAX_texture_stack +#define GL_MESAX_texture_stack 1 +#endif + +#ifndef GL_EXT_timer_query +#define GL_EXT_timer_query 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetQueryObjecti64vEXT (GLuint id, GLenum pname, GLint64EXT *params); +GLAPI void APIENTRY glGetQueryObjectui64vEXT (GLuint id, GLenum pname, GLuint64EXT *params); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLGETQUERYOBJECTI64VEXTPROC) (GLuint id, GLenum pname, GLint64EXT *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTUI64VEXTPROC) (GLuint id, GLenum pname, GLuint64EXT *params); +#endif + +#ifndef GL_EXT_gpu_program_parameters +#define GL_EXT_gpu_program_parameters 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramEnvParameters4fvEXT (GLenum target, GLuint index, GLsizei count, const GLfloat *params); +GLAPI void APIENTRY glProgramLocalParameters4fvEXT (GLenum target, GLuint index, GLsizei count, const GLfloat *params); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERS4FVEXTPROC) (GLenum target, GLuint index, GLsizei count, const GLfloat *params); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERS4FVEXTPROC) (GLenum target, GLuint index, GLsizei count, const GLfloat *params); +#endif + +#ifndef GL_APPLE_flush_buffer_range +#define GL_APPLE_flush_buffer_range 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBufferParameteriAPPLE (GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glFlushMappedBufferRangeAPPLE (GLenum target, GLintptr offset, GLsizeiptr size); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBUFFERPARAMETERIAPPLEPROC) (GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLFLUSHMAPPEDBUFFERRANGEAPPLEPROC) (GLenum target, GLintptr offset, GLsizeiptr size); +#endif + +#ifndef GL_NV_gpu_program4 +#define GL_NV_gpu_program4 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramLocalParameterI4iNV (GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); +GLAPI void APIENTRY glProgramLocalParameterI4ivNV (GLenum target, GLuint index, const GLint *params); +GLAPI void APIENTRY glProgramLocalParametersI4ivNV (GLenum target, GLuint index, GLsizei count, const GLint *params); +GLAPI void APIENTRY glProgramLocalParameterI4uiNV (GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +GLAPI void APIENTRY glProgramLocalParameterI4uivNV (GLenum target, GLuint index, const GLuint *params); +GLAPI void APIENTRY glProgramLocalParametersI4uivNV (GLenum target, GLuint index, GLsizei count, const GLuint *params); +GLAPI void APIENTRY glProgramEnvParameterI4iNV (GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); +GLAPI void APIENTRY glProgramEnvParameterI4ivNV (GLenum target, GLuint index, const GLint *params); +GLAPI void APIENTRY glProgramEnvParametersI4ivNV (GLenum target, GLuint index, GLsizei count, const GLint *params); +GLAPI void APIENTRY glProgramEnvParameterI4uiNV (GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +GLAPI void APIENTRY glProgramEnvParameterI4uivNV (GLenum target, GLuint index, const GLuint *params); +GLAPI void APIENTRY glProgramEnvParametersI4uivNV (GLenum target, GLuint index, GLsizei count, const GLuint *params); +GLAPI void APIENTRY glGetProgramLocalParameterIivNV (GLenum target, GLuint index, GLint *params); +GLAPI void APIENTRY glGetProgramLocalParameterIuivNV (GLenum target, GLuint index, GLuint *params); +GLAPI void APIENTRY glGetProgramEnvParameterIivNV (GLenum target, GLuint index, GLint *params); +GLAPI void APIENTRY glGetProgramEnvParameterIuivNV (GLenum target, GLuint index, GLuint *params); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERI4INVPROC) (GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERI4IVNVPROC) (GLenum target, GLuint index, const GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERSI4IVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERI4UINVPROC) (GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERI4UIVNVPROC) (GLenum target, GLuint index, const GLuint *params); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERSI4UIVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLuint *params); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERI4INVPROC) (GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERI4IVNVPROC) (GLenum target, GLuint index, const GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERSI4IVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERI4UINVPROC) (GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERI4UIVNVPROC) (GLenum target, GLuint index, const GLuint *params); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERSI4UIVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLuint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERIIVNVPROC) (GLenum target, GLuint index, GLint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERIUIVNVPROC) (GLenum target, GLuint index, GLuint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERIIVNVPROC) (GLenum target, GLuint index, GLint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERIUIVNVPROC) (GLenum target, GLuint index, GLuint *params); +#endif + +#ifndef GL_NV_geometry_program4 +#define GL_NV_geometry_program4 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramVertexLimitNV (GLenum target, GLint limit); +GLAPI void APIENTRY glFramebufferTextureEXT (GLenum target, GLenum attachment, GLuint texture, GLint level); +GLAPI void APIENTRY glFramebufferTextureLayerEXT (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +GLAPI void APIENTRY glFramebufferTextureFaceEXT (GLenum target, GLenum attachment, GLuint texture, GLint level, GLenum face); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLPROGRAMVERTEXLIMITNVPROC) (GLenum target, GLint limit); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREEXTPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYEREXTPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREFACEEXTPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLenum face); +#endif + +#ifndef GL_EXT_geometry_shader4 +#define GL_EXT_geometry_shader4 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramParameteriEXT (GLuint program, GLenum pname, GLint value); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLPROGRAMPARAMETERIEXTPROC) (GLuint program, GLenum pname, GLint value); +#endif + +#ifndef GL_NV_vertex_program4 +#define GL_NV_vertex_program4 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexAttribI1iEXT (GLuint index, GLint x); +GLAPI void APIENTRY glVertexAttribI2iEXT (GLuint index, GLint x, GLint y); +GLAPI void APIENTRY glVertexAttribI3iEXT (GLuint index, GLint x, GLint y, GLint z); +GLAPI void APIENTRY glVertexAttribI4iEXT (GLuint index, GLint x, GLint y, GLint z, GLint w); +GLAPI void APIENTRY glVertexAttribI1uiEXT (GLuint index, GLuint x); +GLAPI void APIENTRY glVertexAttribI2uiEXT (GLuint index, GLuint x, GLuint y); +GLAPI void APIENTRY glVertexAttribI3uiEXT (GLuint index, GLuint x, GLuint y, GLuint z); +GLAPI void APIENTRY glVertexAttribI4uiEXT (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +GLAPI void APIENTRY glVertexAttribI1ivEXT (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI2ivEXT (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI3ivEXT (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI4ivEXT (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI1uivEXT (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI2uivEXT (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI3uivEXT (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI4uivEXT (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI4bvEXT (GLuint index, const GLbyte *v); +GLAPI void APIENTRY glVertexAttribI4svEXT (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttribI4ubvEXT (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttribI4usvEXT (GLuint index, const GLushort *v); +GLAPI void APIENTRY glVertexAttribIPointerEXT (GLuint index, GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +GLAPI void APIENTRY glGetVertexAttribIivEXT (GLuint index, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVertexAttribIuivEXT (GLuint index, GLenum pname, GLuint *params); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IEXTPROC) (GLuint index, GLint x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IEXTPROC) (GLuint index, GLint x, GLint y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IEXTPROC) (GLuint index, GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IEXTPROC) (GLuint index, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIEXTPROC) (GLuint index, GLuint x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIEXTPROC) (GLuint index, GLuint x, GLuint y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIEXTPROC) (GLuint index, GLuint x, GLuint y, GLuint z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIEXTPROC) (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IVEXTPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IVEXTPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IVEXTPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IVEXTPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIVEXTPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIVEXTPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIVEXTPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIVEXTPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4BVEXTPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4SVEXTPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UBVEXTPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4USVEXTPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBIPOINTEREXTPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIIVEXTPROC) (GLuint index, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIUIVEXTPROC) (GLuint index, GLenum pname, GLuint *params); +#endif + +#ifndef GL_EXT_gpu_shader4 +#define GL_EXT_gpu_shader4 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetUniformuivEXT (GLuint program, GLint location, GLuint *params); +GLAPI void APIENTRY glBindFragDataLocationEXT (GLuint program, GLuint color, const GLchar *name); +GLAPI GLint APIENTRY glGetFragDataLocationEXT (GLuint program, const GLchar *name); +GLAPI void APIENTRY glUniform1uiEXT (GLint location, GLuint v0); +GLAPI void APIENTRY glUniform2uiEXT (GLint location, GLuint v0, GLuint v1); +GLAPI void APIENTRY glUniform3uiEXT (GLint location, GLuint v0, GLuint v1, GLuint v2); +GLAPI void APIENTRY glUniform4uiEXT (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +GLAPI void APIENTRY glUniform1uivEXT (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glUniform2uivEXT (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glUniform3uivEXT (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glUniform4uivEXT (GLint location, GLsizei count, const GLuint *value); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLGETUNIFORMUIVEXTPROC) (GLuint program, GLint location, GLuint *params); +typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONEXTPROC) (GLuint program, GLuint color, const GLchar *name); +typedef GLint (APIENTRYP PFNGLGETFRAGDATALOCATIONEXTPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLUNIFORM1UIEXTPROC) (GLint location, GLuint v0); +typedef void (APIENTRYP PFNGLUNIFORM2UIEXTPROC) (GLint location, GLuint v0, GLuint v1); +typedef void (APIENTRYP PFNGLUNIFORM3UIEXTPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2); +typedef void (APIENTRYP PFNGLUNIFORM4UIEXTPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +typedef void (APIENTRYP PFNGLUNIFORM1UIVEXTPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLUNIFORM2UIVEXTPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLUNIFORM3UIVEXTPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLUNIFORM4UIVEXTPROC) (GLint location, GLsizei count, const GLuint *value); +#endif + +#ifndef GL_EXT_draw_instanced +#define GL_EXT_draw_instanced 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawArraysInstancedEXT (GLenum mode, GLint start, GLsizei count, GLsizei primcount); +GLAPI void APIENTRY glDrawElementsInstancedEXT (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLsizei primcount); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDEXTPROC) (GLenum mode, GLint start, GLsizei count, GLsizei primcount); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDEXTPROC) (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLsizei primcount); +#endif + +#ifndef GL_EXT_packed_float +#define GL_EXT_packed_float 1 +#endif + +#ifndef GL_EXT_texture_array +#define GL_EXT_texture_array 1 +#endif + +#ifndef GL_EXT_texture_buffer_object +#define GL_EXT_texture_buffer_object 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexBufferEXT (GLenum target, GLenum internalformat, GLuint buffer); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLTEXBUFFEREXTPROC) (GLenum target, GLenum internalformat, GLuint buffer); +#endif + +#ifndef GL_EXT_texture_compression_latc +#define GL_EXT_texture_compression_latc 1 +#endif + +#ifndef GL_EXT_texture_compression_rgtc +#define GL_EXT_texture_compression_rgtc 1 +#endif + +#ifndef GL_EXT_texture_shared_exponent +#define GL_EXT_texture_shared_exponent 1 +#endif + +#ifndef GL_NV_depth_buffer_float +#define GL_NV_depth_buffer_float 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDepthRangedNV (GLdouble zNear, GLdouble zFar); +GLAPI void APIENTRY glClearDepthdNV (GLdouble depth); +GLAPI void APIENTRY glDepthBoundsdNV (GLdouble zmin, GLdouble zmax); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLDEPTHRANGEDNVPROC) (GLdouble zNear, GLdouble zFar); +typedef void (APIENTRYP PFNGLCLEARDEPTHDNVPROC) (GLdouble depth); +typedef void (APIENTRYP PFNGLDEPTHBOUNDSDNVPROC) (GLdouble zmin, GLdouble zmax); +#endif + +#ifndef GL_NV_fragment_program4 +#define GL_NV_fragment_program4 1 +#endif + +#ifndef GL_NV_framebuffer_multisample_coverage +#define GL_NV_framebuffer_multisample_coverage 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glRenderbufferStorageMultisampleCoverageNV (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLECOVERAGENVPROC) (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height); +#endif + +#ifndef GL_EXT_framebuffer_sRGB +#define GL_EXT_framebuffer_sRGB 1 +#endif + +#ifndef GL_NV_geometry_shader4 +#define GL_NV_geometry_shader4 1 +#endif + +#ifndef GL_NV_parameter_buffer_object +#define GL_NV_parameter_buffer_object 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramBufferParametersfvNV (GLenum target, GLuint buffer, GLuint index, GLsizei count, const GLfloat *params); +GLAPI void APIENTRY glProgramBufferParametersIivNV (GLenum target, GLuint buffer, GLuint index, GLsizei count, const GLint *params); +GLAPI void APIENTRY glProgramBufferParametersIuivNV (GLenum target, GLuint buffer, GLuint index, GLsizei count, const GLuint *params); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLPROGRAMBUFFERPARAMETERSFVNVPROC) (GLenum target, GLuint buffer, GLuint index, GLsizei count, const GLfloat *params); +typedef void (APIENTRYP PFNGLPROGRAMBUFFERPARAMETERSIIVNVPROC) (GLenum target, GLuint buffer, GLuint index, GLsizei count, const GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMBUFFERPARAMETERSIUIVNVPROC) (GLenum target, GLuint buffer, GLuint index, GLsizei count, const GLuint *params); +#endif + +#ifndef GL_EXT_draw_buffers2 +#define GL_EXT_draw_buffers2 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColorMaskIndexedEXT (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); +GLAPI void APIENTRY glGetBooleanIndexedvEXT (GLenum target, GLuint index, GLboolean *data); +GLAPI void APIENTRY glGetIntegerIndexedvEXT (GLenum target, GLuint index, GLint *data); +GLAPI void APIENTRY glEnableIndexedEXT (GLenum target, GLuint index); +GLAPI void APIENTRY glDisableIndexedEXT (GLenum target, GLuint index); +GLAPI GLboolean APIENTRY glIsEnabledIndexedEXT (GLenum target, GLuint index); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLCOLORMASKINDEXEDEXTPROC) (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); +typedef void (APIENTRYP PFNGLGETBOOLEANINDEXEDVEXTPROC) (GLenum target, GLuint index, GLboolean *data); +typedef void (APIENTRYP PFNGLGETINTEGERINDEXEDVEXTPROC) (GLenum target, GLuint index, GLint *data); +typedef void (APIENTRYP PFNGLENABLEINDEXEDEXTPROC) (GLenum target, GLuint index); +typedef void (APIENTRYP PFNGLDISABLEINDEXEDEXTPROC) (GLenum target, GLuint index); +typedef GLboolean (APIENTRYP PFNGLISENABLEDINDEXEDEXTPROC) (GLenum target, GLuint index); +#endif + +#ifndef GL_NV_transform_feedback +#define GL_NV_transform_feedback 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBeginTransformFeedbackNV (GLenum primitiveMode); +GLAPI void APIENTRY glEndTransformFeedbackNV (void); +GLAPI void APIENTRY glTransformFeedbackAttribsNV (GLuint count, const GLint *attribs, GLenum bufferMode); +GLAPI void APIENTRY glBindBufferRangeNV (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +GLAPI void APIENTRY glBindBufferOffsetNV (GLenum target, GLuint index, GLuint buffer, GLintptr offset); +GLAPI void APIENTRY glBindBufferBaseNV (GLenum target, GLuint index, GLuint buffer); +GLAPI void APIENTRY glTransformFeedbackVaryingsNV (GLuint program, GLsizei count, const GLint *locations, GLenum bufferMode); +GLAPI void APIENTRY glActiveVaryingNV (GLuint program, const GLchar *name); +GLAPI GLint APIENTRY glGetVaryingLocationNV (GLuint program, const GLchar *name); +GLAPI void APIENTRY glGetActiveVaryingNV (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); +GLAPI void APIENTRY glGetTransformFeedbackVaryingNV (GLuint program, GLuint index, GLint *location); +GLAPI void APIENTRY glTransformFeedbackStreamAttribsNV (GLsizei count, const GLint *attribs, GLsizei nbuffers, const GLint *bufstreams, GLenum bufferMode); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKNVPROC) (GLenum primitiveMode); +typedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKNVPROC) (void); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKATTRIBSNVPROC) (GLuint count, const GLint *attribs, GLenum bufferMode); +typedef void (APIENTRYP PFNGLBINDBUFFERRANGENVPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLBINDBUFFEROFFSETNVPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset); +typedef void (APIENTRYP PFNGLBINDBUFFERBASENVPROC) (GLenum target, GLuint index, GLuint buffer); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSNVPROC) (GLuint program, GLsizei count, const GLint *locations, GLenum bufferMode); +typedef void (APIENTRYP PFNGLACTIVEVARYINGNVPROC) (GLuint program, const GLchar *name); +typedef GLint (APIENTRYP PFNGLGETVARYINGLOCATIONNVPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLGETACTIVEVARYINGNVPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); +typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGNVPROC) (GLuint program, GLuint index, GLint *location); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKSTREAMATTRIBSNVPROC) (GLsizei count, const GLint *attribs, GLsizei nbuffers, const GLint *bufstreams, GLenum bufferMode); +#endif + +#ifndef GL_EXT_bindable_uniform +#define GL_EXT_bindable_uniform 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glUniformBufferEXT (GLuint program, GLint location, GLuint buffer); +GLAPI GLint APIENTRY glGetUniformBufferSizeEXT (GLuint program, GLint location); +GLAPI GLintptr APIENTRY glGetUniformOffsetEXT (GLuint program, GLint location); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLUNIFORMBUFFEREXTPROC) (GLuint program, GLint location, GLuint buffer); +typedef GLint (APIENTRYP PFNGLGETUNIFORMBUFFERSIZEEXTPROC) (GLuint program, GLint location); +typedef GLintptr (APIENTRYP PFNGLGETUNIFORMOFFSETEXTPROC) (GLuint program, GLint location); +#endif + +#ifndef GL_EXT_texture_integer +#define GL_EXT_texture_integer 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexParameterIivEXT (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glTexParameterIuivEXT (GLenum target, GLenum pname, const GLuint *params); +GLAPI void APIENTRY glGetTexParameterIivEXT (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetTexParameterIuivEXT (GLenum target, GLenum pname, GLuint *params); +GLAPI void APIENTRY glClearColorIiEXT (GLint red, GLint green, GLint blue, GLint alpha); +GLAPI void APIENTRY glClearColorIuiEXT (GLuint red, GLuint green, GLuint blue, GLuint alpha); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLTEXPARAMETERIIVEXTPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLTEXPARAMETERIUIVEXTPROC) (GLenum target, GLenum pname, const GLuint *params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERIIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERIUIVEXTPROC) (GLenum target, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLCLEARCOLORIIEXTPROC) (GLint red, GLint green, GLint blue, GLint alpha); +typedef void (APIENTRYP PFNGLCLEARCOLORIUIEXTPROC) (GLuint red, GLuint green, GLuint blue, GLuint alpha); +#endif + +#ifndef GL_GREMEDY_frame_terminator +#define GL_GREMEDY_frame_terminator 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFrameTerminatorGREMEDY (void); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLFRAMETERMINATORGREMEDYPROC) (void); +#endif + +#ifndef GL_NV_conditional_render +#define GL_NV_conditional_render 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBeginConditionalRenderNV (GLuint id, GLenum mode); +GLAPI void APIENTRY glEndConditionalRenderNV (void); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERNVPROC) (GLuint id, GLenum mode); +typedef void (APIENTRYP PFNGLENDCONDITIONALRENDERNVPROC) (void); +#endif + +#ifndef GL_NV_present_video +#define GL_NV_present_video 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPresentFrameKeyedNV (GLuint video_slot, GLuint64EXT minPresentTime, GLuint beginPresentTimeId, GLuint presentDurationId, GLenum type, GLenum target0, GLuint fill0, GLuint key0, GLenum target1, GLuint fill1, GLuint key1); +GLAPI void APIENTRY glPresentFrameDualFillNV (GLuint video_slot, GLuint64EXT minPresentTime, GLuint beginPresentTimeId, GLuint presentDurationId, GLenum type, GLenum target0, GLuint fill0, GLenum target1, GLuint fill1, GLenum target2, GLuint fill2, GLenum target3, GLuint fill3); +GLAPI void APIENTRY glGetVideoivNV (GLuint video_slot, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVideouivNV (GLuint video_slot, GLenum pname, GLuint *params); +GLAPI void APIENTRY glGetVideoi64vNV (GLuint video_slot, GLenum pname, GLint64EXT *params); +GLAPI void APIENTRY glGetVideoui64vNV (GLuint video_slot, GLenum pname, GLuint64EXT *params); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLPRESENTFRAMEKEYEDNVPROC) (GLuint video_slot, GLuint64EXT minPresentTime, GLuint beginPresentTimeId, GLuint presentDurationId, GLenum type, GLenum target0, GLuint fill0, GLuint key0, GLenum target1, GLuint fill1, GLuint key1); +typedef void (APIENTRYP PFNGLPRESENTFRAMEDUALFILLNVPROC) (GLuint video_slot, GLuint64EXT minPresentTime, GLuint beginPresentTimeId, GLuint presentDurationId, GLenum type, GLenum target0, GLuint fill0, GLenum target1, GLuint fill1, GLenum target2, GLuint fill2, GLenum target3, GLuint fill3); +typedef void (APIENTRYP PFNGLGETVIDEOIVNVPROC) (GLuint video_slot, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVIDEOUIVNVPROC) (GLuint video_slot, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLGETVIDEOI64VNVPROC) (GLuint video_slot, GLenum pname, GLint64EXT *params); +typedef void (APIENTRYP PFNGLGETVIDEOUI64VNVPROC) (GLuint video_slot, GLenum pname, GLuint64EXT *params); +#endif + +#ifndef GL_EXT_transform_feedback +#define GL_EXT_transform_feedback 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBeginTransformFeedbackEXT (GLenum primitiveMode); +GLAPI void APIENTRY glEndTransformFeedbackEXT (void); +GLAPI void APIENTRY glBindBufferRangeEXT (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +GLAPI void APIENTRY glBindBufferOffsetEXT (GLenum target, GLuint index, GLuint buffer, GLintptr offset); +GLAPI void APIENTRY glBindBufferBaseEXT (GLenum target, GLuint index, GLuint buffer); +GLAPI void APIENTRY glTransformFeedbackVaryingsEXT (GLuint program, GLsizei count, const GLchar* *varyings, GLenum bufferMode); +GLAPI void APIENTRY glGetTransformFeedbackVaryingEXT (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKEXTPROC) (GLenum primitiveMode); +typedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKEXTPROC) (void); +typedef void (APIENTRYP PFNGLBINDBUFFERRANGEEXTPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLBINDBUFFEROFFSETEXTPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset); +typedef void (APIENTRYP PFNGLBINDBUFFERBASEEXTPROC) (GLenum target, GLuint index, GLuint buffer); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSEXTPROC) (GLuint program, GLsizei count, const GLchar* *varyings, GLenum bufferMode); +typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGEXTPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); +#endif + +#ifndef GL_EXT_direct_state_access +#define GL_EXT_direct_state_access 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glClientAttribDefaultEXT (GLbitfield mask); +GLAPI void APIENTRY glPushClientAttribDefaultEXT (GLbitfield mask); +GLAPI void APIENTRY glMatrixLoadfEXT (GLenum mode, const GLfloat *m); +GLAPI void APIENTRY glMatrixLoaddEXT (GLenum mode, const GLdouble *m); +GLAPI void APIENTRY glMatrixMultfEXT (GLenum mode, const GLfloat *m); +GLAPI void APIENTRY glMatrixMultdEXT (GLenum mode, const GLdouble *m); +GLAPI void APIENTRY glMatrixLoadIdentityEXT (GLenum mode); +GLAPI void APIENTRY glMatrixRotatefEXT (GLenum mode, GLfloat angle, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glMatrixRotatedEXT (GLenum mode, GLdouble angle, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glMatrixScalefEXT (GLenum mode, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glMatrixScaledEXT (GLenum mode, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glMatrixTranslatefEXT (GLenum mode, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glMatrixTranslatedEXT (GLenum mode, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glMatrixFrustumEXT (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +GLAPI void APIENTRY glMatrixOrthoEXT (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +GLAPI void APIENTRY glMatrixPopEXT (GLenum mode); +GLAPI void APIENTRY glMatrixPushEXT (GLenum mode); +GLAPI void APIENTRY glMatrixLoadTransposefEXT (GLenum mode, const GLfloat *m); +GLAPI void APIENTRY glMatrixLoadTransposedEXT (GLenum mode, const GLdouble *m); +GLAPI void APIENTRY glMatrixMultTransposefEXT (GLenum mode, const GLfloat *m); +GLAPI void APIENTRY glMatrixMultTransposedEXT (GLenum mode, const GLdouble *m); +GLAPI void APIENTRY glTextureParameterfEXT (GLuint texture, GLenum target, GLenum pname, GLfloat param); +GLAPI void APIENTRY glTextureParameterfvEXT (GLuint texture, GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glTextureParameteriEXT (GLuint texture, GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glTextureParameterivEXT (GLuint texture, GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glTextureImage1DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +GLAPI void APIENTRY glTextureImage2DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +GLAPI void APIENTRY glTextureSubImage1DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const GLvoid *pixels); +GLAPI void APIENTRY glTextureSubImage2DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels); +GLAPI void APIENTRY glCopyTextureImage1DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +GLAPI void APIENTRY glCopyTextureImage2DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +GLAPI void APIENTRY glCopyTextureSubImage1DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glCopyTextureSubImage2DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetTextureImageEXT (GLuint texture, GLenum target, GLint level, GLenum format, GLenum type, GLvoid *pixels); +GLAPI void APIENTRY glGetTextureParameterfvEXT (GLuint texture, GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetTextureParameterivEXT (GLuint texture, GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetTextureLevelParameterfvEXT (GLuint texture, GLenum target, GLint level, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetTextureLevelParameterivEXT (GLuint texture, GLenum target, GLint level, GLenum pname, GLint *params); +GLAPI void APIENTRY glTextureImage3DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +GLAPI void APIENTRY glTextureSubImage3DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels); +GLAPI void APIENTRY glCopyTextureSubImage3DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glMultiTexParameterfEXT (GLenum texunit, GLenum target, GLenum pname, GLfloat param); +GLAPI void APIENTRY glMultiTexParameterfvEXT (GLenum texunit, GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glMultiTexParameteriEXT (GLenum texunit, GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glMultiTexParameterivEXT (GLenum texunit, GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glMultiTexImage1DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +GLAPI void APIENTRY glMultiTexImage2DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +GLAPI void APIENTRY glMultiTexSubImage1DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const GLvoid *pixels); +GLAPI void APIENTRY glMultiTexSubImage2DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels); +GLAPI void APIENTRY glCopyMultiTexImage1DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +GLAPI void APIENTRY glCopyMultiTexImage2DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +GLAPI void APIENTRY glCopyMultiTexSubImage1DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glCopyMultiTexSubImage2DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetMultiTexImageEXT (GLenum texunit, GLenum target, GLint level, GLenum format, GLenum type, GLvoid *pixels); +GLAPI void APIENTRY glGetMultiTexParameterfvEXT (GLenum texunit, GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetMultiTexParameterivEXT (GLenum texunit, GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetMultiTexLevelParameterfvEXT (GLenum texunit, GLenum target, GLint level, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetMultiTexLevelParameterivEXT (GLenum texunit, GLenum target, GLint level, GLenum pname, GLint *params); +GLAPI void APIENTRY glMultiTexImage3DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +GLAPI void APIENTRY glMultiTexSubImage3DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels); +GLAPI void APIENTRY glCopyMultiTexSubImage3DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glBindMultiTextureEXT (GLenum texunit, GLenum target, GLuint texture); +GLAPI void APIENTRY glEnableClientStateIndexedEXT (GLenum array, GLuint index); +GLAPI void APIENTRY glDisableClientStateIndexedEXT (GLenum array, GLuint index); +GLAPI void APIENTRY glMultiTexCoordPointerEXT (GLenum texunit, GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +GLAPI void APIENTRY glMultiTexEnvfEXT (GLenum texunit, GLenum target, GLenum pname, GLfloat param); +GLAPI void APIENTRY glMultiTexEnvfvEXT (GLenum texunit, GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glMultiTexEnviEXT (GLenum texunit, GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glMultiTexEnvivEXT (GLenum texunit, GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glMultiTexGendEXT (GLenum texunit, GLenum coord, GLenum pname, GLdouble param); +GLAPI void APIENTRY glMultiTexGendvEXT (GLenum texunit, GLenum coord, GLenum pname, const GLdouble *params); +GLAPI void APIENTRY glMultiTexGenfEXT (GLenum texunit, GLenum coord, GLenum pname, GLfloat param); +GLAPI void APIENTRY glMultiTexGenfvEXT (GLenum texunit, GLenum coord, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glMultiTexGeniEXT (GLenum texunit, GLenum coord, GLenum pname, GLint param); +GLAPI void APIENTRY glMultiTexGenivEXT (GLenum texunit, GLenum coord, GLenum pname, const GLint *params); +GLAPI void APIENTRY glGetMultiTexEnvfvEXT (GLenum texunit, GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetMultiTexEnvivEXT (GLenum texunit, GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetMultiTexGendvEXT (GLenum texunit, GLenum coord, GLenum pname, GLdouble *params); +GLAPI void APIENTRY glGetMultiTexGenfvEXT (GLenum texunit, GLenum coord, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetMultiTexGenivEXT (GLenum texunit, GLenum coord, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetFloatIndexedvEXT (GLenum target, GLuint index, GLfloat *data); +GLAPI void APIENTRY glGetDoubleIndexedvEXT (GLenum target, GLuint index, GLdouble *data); +GLAPI void APIENTRY glGetPointerIndexedvEXT (GLenum target, GLuint index, GLvoid* *data); +GLAPI void APIENTRY glCompressedTextureImage3DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid *bits); +GLAPI void APIENTRY glCompressedTextureImage2DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *bits); +GLAPI void APIENTRY glCompressedTextureImage1DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const GLvoid *bits); +GLAPI void APIENTRY glCompressedTextureSubImage3DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid *bits); +GLAPI void APIENTRY glCompressedTextureSubImage2DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *bits); +GLAPI void APIENTRY glCompressedTextureSubImage1DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const GLvoid *bits); +GLAPI void APIENTRY glGetCompressedTextureImageEXT (GLuint texture, GLenum target, GLint lod, GLvoid *img); +GLAPI void APIENTRY glCompressedMultiTexImage3DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid *bits); +GLAPI void APIENTRY glCompressedMultiTexImage2DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *bits); +GLAPI void APIENTRY glCompressedMultiTexImage1DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const GLvoid *bits); +GLAPI void APIENTRY glCompressedMultiTexSubImage3DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid *bits); +GLAPI void APIENTRY glCompressedMultiTexSubImage2DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *bits); +GLAPI void APIENTRY glCompressedMultiTexSubImage1DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const GLvoid *bits); +GLAPI void APIENTRY glGetCompressedMultiTexImageEXT (GLenum texunit, GLenum target, GLint lod, GLvoid *img); +GLAPI void APIENTRY glNamedProgramStringEXT (GLuint program, GLenum target, GLenum format, GLsizei len, const GLvoid *string); +GLAPI void APIENTRY glNamedProgramLocalParameter4dEXT (GLuint program, GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glNamedProgramLocalParameter4dvEXT (GLuint program, GLenum target, GLuint index, const GLdouble *params); +GLAPI void APIENTRY glNamedProgramLocalParameter4fEXT (GLuint program, GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glNamedProgramLocalParameter4fvEXT (GLuint program, GLenum target, GLuint index, const GLfloat *params); +GLAPI void APIENTRY glGetNamedProgramLocalParameterdvEXT (GLuint program, GLenum target, GLuint index, GLdouble *params); +GLAPI void APIENTRY glGetNamedProgramLocalParameterfvEXT (GLuint program, GLenum target, GLuint index, GLfloat *params); +GLAPI void APIENTRY glGetNamedProgramivEXT (GLuint program, GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetNamedProgramStringEXT (GLuint program, GLenum target, GLenum pname, GLvoid *string); +GLAPI void APIENTRY glNamedProgramLocalParameters4fvEXT (GLuint program, GLenum target, GLuint index, GLsizei count, const GLfloat *params); +GLAPI void APIENTRY glNamedProgramLocalParameterI4iEXT (GLuint program, GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); +GLAPI void APIENTRY glNamedProgramLocalParameterI4ivEXT (GLuint program, GLenum target, GLuint index, const GLint *params); +GLAPI void APIENTRY glNamedProgramLocalParametersI4ivEXT (GLuint program, GLenum target, GLuint index, GLsizei count, const GLint *params); +GLAPI void APIENTRY glNamedProgramLocalParameterI4uiEXT (GLuint program, GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +GLAPI void APIENTRY glNamedProgramLocalParameterI4uivEXT (GLuint program, GLenum target, GLuint index, const GLuint *params); +GLAPI void APIENTRY glNamedProgramLocalParametersI4uivEXT (GLuint program, GLenum target, GLuint index, GLsizei count, const GLuint *params); +GLAPI void APIENTRY glGetNamedProgramLocalParameterIivEXT (GLuint program, GLenum target, GLuint index, GLint *params); +GLAPI void APIENTRY glGetNamedProgramLocalParameterIuivEXT (GLuint program, GLenum target, GLuint index, GLuint *params); +GLAPI void APIENTRY glTextureParameterIivEXT (GLuint texture, GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glTextureParameterIuivEXT (GLuint texture, GLenum target, GLenum pname, const GLuint *params); +GLAPI void APIENTRY glGetTextureParameterIivEXT (GLuint texture, GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetTextureParameterIuivEXT (GLuint texture, GLenum target, GLenum pname, GLuint *params); +GLAPI void APIENTRY glMultiTexParameterIivEXT (GLenum texunit, GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glMultiTexParameterIuivEXT (GLenum texunit, GLenum target, GLenum pname, const GLuint *params); +GLAPI void APIENTRY glGetMultiTexParameterIivEXT (GLenum texunit, GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetMultiTexParameterIuivEXT (GLenum texunit, GLenum target, GLenum pname, GLuint *params); +GLAPI void APIENTRY glProgramUniform1fEXT (GLuint program, GLint location, GLfloat v0); +GLAPI void APIENTRY glProgramUniform2fEXT (GLuint program, GLint location, GLfloat v0, GLfloat v1); +GLAPI void APIENTRY glProgramUniform3fEXT (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +GLAPI void APIENTRY glProgramUniform4fEXT (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +GLAPI void APIENTRY glProgramUniform1iEXT (GLuint program, GLint location, GLint v0); +GLAPI void APIENTRY glProgramUniform2iEXT (GLuint program, GLint location, GLint v0, GLint v1); +GLAPI void APIENTRY glProgramUniform3iEXT (GLuint program, GLint location, GLint v0, GLint v1, GLint v2); +GLAPI void APIENTRY glProgramUniform4iEXT (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +GLAPI void APIENTRY glProgramUniform1fvEXT (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform2fvEXT (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform3fvEXT (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform4fvEXT (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform1ivEXT (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform2ivEXT (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform3ivEXT (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform4ivEXT (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniformMatrix2fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix3fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix4fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix2x3fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix3x2fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix2x4fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix4x2fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix3x4fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix4x3fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform1uiEXT (GLuint program, GLint location, GLuint v0); +GLAPI void APIENTRY glProgramUniform2uiEXT (GLuint program, GLint location, GLuint v0, GLuint v1); +GLAPI void APIENTRY glProgramUniform3uiEXT (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); +GLAPI void APIENTRY glProgramUniform4uiEXT (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +GLAPI void APIENTRY glProgramUniform1uivEXT (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniform2uivEXT (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniform3uivEXT (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniform4uivEXT (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glNamedBufferDataEXT (GLuint buffer, GLsizeiptr size, const GLvoid *data, GLenum usage); +GLAPI void APIENTRY glNamedBufferSubDataEXT (GLuint buffer, GLintptr offset, GLsizeiptr size, const GLvoid *data); +GLAPI GLvoid* APIENTRY glMapNamedBufferEXT (GLuint buffer, GLenum access); +GLAPI GLboolean APIENTRY glUnmapNamedBufferEXT (GLuint buffer); +GLAPI GLvoid* APIENTRY glMapNamedBufferRangeEXT (GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access); +GLAPI void APIENTRY glFlushMappedNamedBufferRangeEXT (GLuint buffer, GLintptr offset, GLsizeiptr length); +GLAPI void APIENTRY glNamedCopyBufferSubDataEXT (GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +GLAPI void APIENTRY glGetNamedBufferParameterivEXT (GLuint buffer, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetNamedBufferPointervEXT (GLuint buffer, GLenum pname, GLvoid* *params); +GLAPI void APIENTRY glGetNamedBufferSubDataEXT (GLuint buffer, GLintptr offset, GLsizeiptr size, GLvoid *data); +GLAPI void APIENTRY glTextureBufferEXT (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer); +GLAPI void APIENTRY glMultiTexBufferEXT (GLenum texunit, GLenum target, GLenum internalformat, GLuint buffer); +GLAPI void APIENTRY glNamedRenderbufferStorageEXT (GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetNamedRenderbufferParameterivEXT (GLuint renderbuffer, GLenum pname, GLint *params); +GLAPI GLenum APIENTRY glCheckNamedFramebufferStatusEXT (GLuint framebuffer, GLenum target); +GLAPI void APIENTRY glNamedFramebufferTexture1DEXT (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GLAPI void APIENTRY glNamedFramebufferTexture2DEXT (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GLAPI void APIENTRY glNamedFramebufferTexture3DEXT (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +GLAPI void APIENTRY glNamedFramebufferRenderbufferEXT (GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +GLAPI void APIENTRY glGetNamedFramebufferAttachmentParameterivEXT (GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params); +GLAPI void APIENTRY glGenerateTextureMipmapEXT (GLuint texture, GLenum target); +GLAPI void APIENTRY glGenerateMultiTexMipmapEXT (GLenum texunit, GLenum target); +GLAPI void APIENTRY glFramebufferDrawBufferEXT (GLuint framebuffer, GLenum mode); +GLAPI void APIENTRY glFramebufferDrawBuffersEXT (GLuint framebuffer, GLsizei n, const GLenum *bufs); +GLAPI void APIENTRY glFramebufferReadBufferEXT (GLuint framebuffer, GLenum mode); +GLAPI void APIENTRY glGetFramebufferParameterivEXT (GLuint framebuffer, GLenum pname, GLint *params); +GLAPI void APIENTRY glNamedRenderbufferStorageMultisampleEXT (GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glNamedRenderbufferStorageMultisampleCoverageEXT (GLuint renderbuffer, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glNamedFramebufferTextureEXT (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level); +GLAPI void APIENTRY glNamedFramebufferTextureLayerEXT (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer); +GLAPI void APIENTRY glNamedFramebufferTextureFaceEXT (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLenum face); +GLAPI void APIENTRY glTextureRenderbufferEXT (GLuint texture, GLenum target, GLuint renderbuffer); +GLAPI void APIENTRY glMultiTexRenderbufferEXT (GLenum texunit, GLenum target, GLuint renderbuffer); +GLAPI void APIENTRY glProgramUniform1dEXT (GLuint program, GLint location, GLdouble x); +GLAPI void APIENTRY glProgramUniform2dEXT (GLuint program, GLint location, GLdouble x, GLdouble y); +GLAPI void APIENTRY glProgramUniform3dEXT (GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glProgramUniform4dEXT (GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glProgramUniform1dvEXT (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform2dvEXT (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform3dvEXT (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform4dvEXT (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix2dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix3dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix4dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix2x3dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix2x4dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix3x2dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix3x4dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix4x2dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix4x3dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLCLIENTATTRIBDEFAULTEXTPROC) (GLbitfield mask); +typedef void (APIENTRYP PFNGLPUSHCLIENTATTRIBDEFAULTEXTPROC) (GLbitfield mask); +typedef void (APIENTRYP PFNGLMATRIXLOADFEXTPROC) (GLenum mode, const GLfloat *m); +typedef void (APIENTRYP PFNGLMATRIXLOADDEXTPROC) (GLenum mode, const GLdouble *m); +typedef void (APIENTRYP PFNGLMATRIXMULTFEXTPROC) (GLenum mode, const GLfloat *m); +typedef void (APIENTRYP PFNGLMATRIXMULTDEXTPROC) (GLenum mode, const GLdouble *m); +typedef void (APIENTRYP PFNGLMATRIXLOADIDENTITYEXTPROC) (GLenum mode); +typedef void (APIENTRYP PFNGLMATRIXROTATEFEXTPROC) (GLenum mode, GLfloat angle, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLMATRIXROTATEDEXTPROC) (GLenum mode, GLdouble angle, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLMATRIXSCALEFEXTPROC) (GLenum mode, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLMATRIXSCALEDEXTPROC) (GLenum mode, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLMATRIXTRANSLATEFEXTPROC) (GLenum mode, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLMATRIXTRANSLATEDEXTPROC) (GLenum mode, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLMATRIXFRUSTUMEXTPROC) (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +typedef void (APIENTRYP PFNGLMATRIXORTHOEXTPROC) (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +typedef void (APIENTRYP PFNGLMATRIXPOPEXTPROC) (GLenum mode); +typedef void (APIENTRYP PFNGLMATRIXPUSHEXTPROC) (GLenum mode); +typedef void (APIENTRYP PFNGLMATRIXLOADTRANSPOSEFEXTPROC) (GLenum mode, const GLfloat *m); +typedef void (APIENTRYP PFNGLMATRIXLOADTRANSPOSEDEXTPROC) (GLenum mode, const GLdouble *m); +typedef void (APIENTRYP PFNGLMATRIXMULTTRANSPOSEFEXTPROC) (GLenum mode, const GLfloat *m); +typedef void (APIENTRYP PFNGLMATRIXMULTTRANSPOSEDEXTPROC) (GLenum mode, const GLdouble *m); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERFEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERFVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLTEXTUREIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +typedef void (APIENTRYP PFNGLTEXTUREIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const GLvoid *pixels); +typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels); +typedef void (APIENTRYP PFNGLCOPYTEXTUREIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +typedef void (APIENTRYP PFNGLCOPYTEXTUREIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETTEXTUREIMAGEEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum format, GLenum type, GLvoid *pixels); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERFVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERFVEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERIVEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLTEXTUREIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels); +typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLMULTITEXPARAMETERFEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLMULTITEXPARAMETERFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLMULTITEXIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +typedef void (APIENTRYP PFNGLMULTITEXIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +typedef void (APIENTRYP PFNGLMULTITEXSUBIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const GLvoid *pixels); +typedef void (APIENTRYP PFNGLMULTITEXSUBIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels); +typedef void (APIENTRYP PFNGLCOPYMULTITEXIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +typedef void (APIENTRYP PFNGLCOPYMULTITEXIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +typedef void (APIENTRYP PFNGLCOPYMULTITEXSUBIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYMULTITEXSUBIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETMULTITEXIMAGEEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum format, GLenum type, GLvoid *pixels); +typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETMULTITEXLEVELPARAMETERFVEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETMULTITEXLEVELPARAMETERIVEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLMULTITEXIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +typedef void (APIENTRYP PFNGLMULTITEXSUBIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels); +typedef void (APIENTRYP PFNGLCOPYMULTITEXSUBIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLBINDMULTITEXTUREEXTPROC) (GLenum texunit, GLenum target, GLuint texture); +typedef void (APIENTRYP PFNGLENABLECLIENTSTATEINDEXEDEXTPROC) (GLenum array, GLuint index); +typedef void (APIENTRYP PFNGLDISABLECLIENTSTATEINDEXEDEXTPROC) (GLenum array, GLuint index); +typedef void (APIENTRYP PFNGLMULTITEXCOORDPOINTEREXTPROC) (GLenum texunit, GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +typedef void (APIENTRYP PFNGLMULTITEXENVFEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLMULTITEXENVFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLMULTITEXENVIEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLMULTITEXENVIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLMULTITEXGENDEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLdouble param); +typedef void (APIENTRYP PFNGLMULTITEXGENDVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, const GLdouble *params); +typedef void (APIENTRYP PFNGLMULTITEXGENFEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLMULTITEXGENFVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLMULTITEXGENIEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLMULTITEXGENIVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLGETMULTITEXENVFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETMULTITEXENVIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETMULTITEXGENDVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLGETMULTITEXGENFVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETMULTITEXGENIVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETFLOATINDEXEDVEXTPROC) (GLenum target, GLuint index, GLfloat *data); +typedef void (APIENTRYP PFNGLGETDOUBLEINDEXEDVEXTPROC) (GLenum target, GLuint index, GLdouble *data); +typedef void (APIENTRYP PFNGLGETPOINTERINDEXEDVEXTPROC) (GLenum target, GLuint index, GLvoid* *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTUREIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTUREIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTUREIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const GLvoid *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const GLvoid *bits); +typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXTUREIMAGEEXTPROC) (GLuint texture, GLenum target, GLint lod, GLvoid *img); +typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const GLvoid *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXSUBIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXSUBIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXSUBIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const GLvoid *bits); +typedef void (APIENTRYP PFNGLGETCOMPRESSEDMULTITEXIMAGEEXTPROC) (GLenum texunit, GLenum target, GLint lod, GLvoid *img); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMSTRINGEXTPROC) (GLuint program, GLenum target, GLenum format, GLsizei len, const GLvoid *string); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4DEXTPROC) (GLuint program, GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4DVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLdouble *params); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4FEXTPROC) (GLuint program, GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4FVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLfloat *params); +typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERDVEXTPROC) (GLuint program, GLenum target, GLuint index, GLdouble *params); +typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERFVEXTPROC) (GLuint program, GLenum target, GLuint index, GLfloat *params); +typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMIVEXTPROC) (GLuint program, GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMSTRINGEXTPROC) (GLuint program, GLenum target, GLenum pname, GLvoid *string); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERS4FVEXTPROC) (GLuint program, GLenum target, GLuint index, GLsizei count, const GLfloat *params); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4IEXTPROC) (GLuint program, GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4IVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLint *params); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERSI4IVEXTPROC) (GLuint program, GLenum target, GLuint index, GLsizei count, const GLint *params); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4UIEXTPROC) (GLuint program, GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4UIVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLuint *params); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERSI4UIVEXTPROC) (GLuint program, GLenum target, GLuint index, GLsizei count, const GLuint *params); +typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERIIVEXTPROC) (GLuint program, GLenum target, GLuint index, GLint *params); +typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERIUIVEXTPROC) (GLuint program, GLenum target, GLuint index, GLuint *params); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIUIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLuint *params); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIUIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIUIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLuint *params); +typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERIIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERIUIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FEXTPROC) (GLuint program, GLint location, GLfloat v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FEXTPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FEXTPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FEXTPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IEXTPROC) (GLuint program, GLint location, GLint v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IEXTPROC) (GLuint program, GLint location, GLint v0, GLint v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IEXTPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IEXTPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIEXTPROC) (GLuint program, GLint location, GLuint v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIEXTPROC) (GLuint program, GLint location, GLuint v0, GLuint v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIEXTPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIEXTPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLNAMEDBUFFERDATAEXTPROC) (GLuint buffer, GLsizeiptr size, const GLvoid *data, GLenum usage); +typedef void (APIENTRYP PFNGLNAMEDBUFFERSUBDATAEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, const GLvoid *data); +typedef GLvoid* (APIENTRYP PFNGLMAPNAMEDBUFFEREXTPROC) (GLuint buffer, GLenum access); +typedef GLboolean (APIENTRYP PFNGLUNMAPNAMEDBUFFEREXTPROC) (GLuint buffer); +typedef GLvoid* (APIENTRYP PFNGLMAPNAMEDBUFFERRANGEEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access); +typedef void (APIENTRYP PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length); +typedef void (APIENTRYP PFNGLNAMEDCOPYBUFFERSUBDATAEXTPROC) (GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERIVEXTPROC) (GLuint buffer, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPOINTERVEXTPROC) (GLuint buffer, GLenum pname, GLvoid* *params); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERSUBDATAEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, GLvoid *data); +typedef void (APIENTRYP PFNGLTEXTUREBUFFEREXTPROC) (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer); +typedef void (APIENTRYP PFNGLMULTITEXBUFFEREXTPROC) (GLenum texunit, GLenum target, GLenum internalformat, GLuint buffer); +typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEEXTPROC) (GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETNAMEDRENDERBUFFERPARAMETERIVEXTPROC) (GLuint renderbuffer, GLenum pname, GLint *params); +typedef GLenum (APIENTRYP PFNGLCHECKNAMEDFRAMEBUFFERSTATUSEXTPROC) (GLuint framebuffer, GLenum target); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURE1DEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURE2DEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURE3DEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERRENDERBUFFEREXTPROC) (GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGENERATETEXTUREMIPMAPEXTPROC) (GLuint texture, GLenum target); +typedef void (APIENTRYP PFNGLGENERATEMULTITEXMIPMAPEXTPROC) (GLenum texunit, GLenum target); +typedef void (APIENTRYP PFNGLFRAMEBUFFERDRAWBUFFEREXTPROC) (GLuint framebuffer, GLenum mode); +typedef void (APIENTRYP PFNGLFRAMEBUFFERDRAWBUFFERSEXTPROC) (GLuint framebuffer, GLsizei n, const GLenum *bufs); +typedef void (APIENTRYP PFNGLFRAMEBUFFERREADBUFFEREXTPROC) (GLuint framebuffer, GLenum mode); +typedef void (APIENTRYP PFNGLGETFRAMEBUFFERPARAMETERIVEXTPROC) (GLuint framebuffer, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC) (GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLECOVERAGEEXTPROC) (GLuint renderbuffer, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTUREEXTPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURELAYEREXTPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTUREFACEEXTPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLenum face); +typedef void (APIENTRYP PFNGLTEXTURERENDERBUFFEREXTPROC) (GLuint texture, GLenum target, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLMULTITEXRENDERBUFFEREXTPROC) (GLenum texunit, GLenum target, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DEXTPROC) (GLuint program, GLint location, GLdouble x); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DEXTPROC) (GLuint program, GLint location, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DEXTPROC) (GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DEXTPROC) (GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +#endif + +#ifndef GL_EXT_vertex_array_bgra +#define GL_EXT_vertex_array_bgra 1 +#endif + +#ifndef GL_EXT_texture_swizzle +#define GL_EXT_texture_swizzle 1 +#endif + +#ifndef GL_NV_explicit_multisample +#define GL_NV_explicit_multisample 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetMultisamplefvNV (GLenum pname, GLuint index, GLfloat *val); +GLAPI void APIENTRY glSampleMaskIndexedNV (GLuint index, GLbitfield mask); +GLAPI void APIENTRY glTexRenderbufferNV (GLenum target, GLuint renderbuffer); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLGETMULTISAMPLEFVNVPROC) (GLenum pname, GLuint index, GLfloat *val); +typedef void (APIENTRYP PFNGLSAMPLEMASKINDEXEDNVPROC) (GLuint index, GLbitfield mask); +typedef void (APIENTRYP PFNGLTEXRENDERBUFFERNVPROC) (GLenum target, GLuint renderbuffer); +#endif + +#ifndef GL_NV_transform_feedback2 +#define GL_NV_transform_feedback2 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBindTransformFeedbackNV (GLenum target, GLuint id); +GLAPI void APIENTRY glDeleteTransformFeedbacksNV (GLsizei n, const GLuint *ids); +GLAPI void APIENTRY glGenTransformFeedbacksNV (GLsizei n, GLuint *ids); +GLAPI GLboolean APIENTRY glIsTransformFeedbackNV (GLuint id); +GLAPI void APIENTRY glPauseTransformFeedbackNV (void); +GLAPI void APIENTRY glResumeTransformFeedbackNV (void); +GLAPI void APIENTRY glDrawTransformFeedbackNV (GLenum mode, GLuint id); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBINDTRANSFORMFEEDBACKNVPROC) (GLenum target, GLuint id); +typedef void (APIENTRYP PFNGLDELETETRANSFORMFEEDBACKSNVPROC) (GLsizei n, const GLuint *ids); +typedef void (APIENTRYP PFNGLGENTRANSFORMFEEDBACKSNVPROC) (GLsizei n, GLuint *ids); +typedef GLboolean (APIENTRYP PFNGLISTRANSFORMFEEDBACKNVPROC) (GLuint id); +typedef void (APIENTRYP PFNGLPAUSETRANSFORMFEEDBACKNVPROC) (void); +typedef void (APIENTRYP PFNGLRESUMETRANSFORMFEEDBACKNVPROC) (void); +typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKNVPROC) (GLenum mode, GLuint id); +#endif + +#ifndef GL_ATI_meminfo +#define GL_ATI_meminfo 1 +#endif + +#ifndef GL_AMD_performance_monitor +#define GL_AMD_performance_monitor 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetPerfMonitorGroupsAMD (GLint *numGroups, GLsizei groupsSize, GLuint *groups); +GLAPI void APIENTRY glGetPerfMonitorCountersAMD (GLuint group, GLint *numCounters, GLint *maxActiveCounters, GLsizei counterSize, GLuint *counters); +GLAPI void APIENTRY glGetPerfMonitorGroupStringAMD (GLuint group, GLsizei bufSize, GLsizei *length, GLchar *groupString); +GLAPI void APIENTRY glGetPerfMonitorCounterStringAMD (GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, GLchar *counterString); +GLAPI void APIENTRY glGetPerfMonitorCounterInfoAMD (GLuint group, GLuint counter, GLenum pname, GLvoid *data); +GLAPI void APIENTRY glGenPerfMonitorsAMD (GLsizei n, GLuint *monitors); +GLAPI void APIENTRY glDeletePerfMonitorsAMD (GLsizei n, GLuint *monitors); +GLAPI void APIENTRY glSelectPerfMonitorCountersAMD (GLuint monitor, GLboolean enable, GLuint group, GLint numCounters, GLuint *counterList); +GLAPI void APIENTRY glBeginPerfMonitorAMD (GLuint monitor); +GLAPI void APIENTRY glEndPerfMonitorAMD (GLuint monitor); +GLAPI void APIENTRY glGetPerfMonitorCounterDataAMD (GLuint monitor, GLenum pname, GLsizei dataSize, GLuint *data, GLint *bytesWritten); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLGETPERFMONITORGROUPSAMDPROC) (GLint *numGroups, GLsizei groupsSize, GLuint *groups); +typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERSAMDPROC) (GLuint group, GLint *numCounters, GLint *maxActiveCounters, GLsizei counterSize, GLuint *counters); +typedef void (APIENTRYP PFNGLGETPERFMONITORGROUPSTRINGAMDPROC) (GLuint group, GLsizei bufSize, GLsizei *length, GLchar *groupString); +typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERSTRINGAMDPROC) (GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, GLchar *counterString); +typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERINFOAMDPROC) (GLuint group, GLuint counter, GLenum pname, GLvoid *data); +typedef void (APIENTRYP PFNGLGENPERFMONITORSAMDPROC) (GLsizei n, GLuint *monitors); +typedef void (APIENTRYP PFNGLDELETEPERFMONITORSAMDPROC) (GLsizei n, GLuint *monitors); +typedef void (APIENTRYP PFNGLSELECTPERFMONITORCOUNTERSAMDPROC) (GLuint monitor, GLboolean enable, GLuint group, GLint numCounters, GLuint *counterList); +typedef void (APIENTRYP PFNGLBEGINPERFMONITORAMDPROC) (GLuint monitor); +typedef void (APIENTRYP PFNGLENDPERFMONITORAMDPROC) (GLuint monitor); +typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERDATAAMDPROC) (GLuint monitor, GLenum pname, GLsizei dataSize, GLuint *data, GLint *bytesWritten); +#endif + +#ifndef GL_AMD_texture_texture4 +#define GL_AMD_texture_texture4 1 +#endif + +#ifndef GL_AMD_vertex_shader_tesselator +#define GL_AMD_vertex_shader_tesselator 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTessellationFactorAMD (GLfloat factor); +GLAPI void APIENTRY glTessellationModeAMD (GLenum mode); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLTESSELLATIONFACTORAMDPROC) (GLfloat factor); +typedef void (APIENTRYP PFNGLTESSELLATIONMODEAMDPROC) (GLenum mode); +#endif + +#ifndef GL_EXT_provoking_vertex +#define GL_EXT_provoking_vertex 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProvokingVertexEXT (GLenum mode); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLPROVOKINGVERTEXEXTPROC) (GLenum mode); +#endif + +#ifndef GL_EXT_texture_snorm +#define GL_EXT_texture_snorm 1 +#endif + +#ifndef GL_AMD_draw_buffers_blend +#define GL_AMD_draw_buffers_blend 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendFuncIndexedAMD (GLuint buf, GLenum src, GLenum dst); +GLAPI void APIENTRY glBlendFuncSeparateIndexedAMD (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +GLAPI void APIENTRY glBlendEquationIndexedAMD (GLuint buf, GLenum mode); +GLAPI void APIENTRY glBlendEquationSeparateIndexedAMD (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBLENDFUNCINDEXEDAMDPROC) (GLuint buf, GLenum src, GLenum dst); +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEINDEXEDAMDPROC) (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +typedef void (APIENTRYP PFNGLBLENDEQUATIONINDEXEDAMDPROC) (GLuint buf, GLenum mode); +typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEINDEXEDAMDPROC) (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +#endif + +#ifndef GL_APPLE_texture_range +#define GL_APPLE_texture_range 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTextureRangeAPPLE (GLenum target, GLsizei length, const GLvoid *pointer); +GLAPI void APIENTRY glGetTexParameterPointervAPPLE (GLenum target, GLenum pname, GLvoid* *params); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLTEXTURERANGEAPPLEPROC) (GLenum target, GLsizei length, const GLvoid *pointer); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERPOINTERVAPPLEPROC) (GLenum target, GLenum pname, GLvoid* *params); +#endif + +#ifndef GL_APPLE_float_pixels +#define GL_APPLE_float_pixels 1 +#endif + +#ifndef GL_APPLE_vertex_program_evaluators +#define GL_APPLE_vertex_program_evaluators 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glEnableVertexAttribAPPLE (GLuint index, GLenum pname); +GLAPI void APIENTRY glDisableVertexAttribAPPLE (GLuint index, GLenum pname); +GLAPI GLboolean APIENTRY glIsVertexAttribEnabledAPPLE (GLuint index, GLenum pname); +GLAPI void APIENTRY glMapVertexAttrib1dAPPLE (GLuint index, GLuint size, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble *points); +GLAPI void APIENTRY glMapVertexAttrib1fAPPLE (GLuint index, GLuint size, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat *points); +GLAPI void APIENTRY glMapVertexAttrib2dAPPLE (GLuint index, GLuint size, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble *points); +GLAPI void APIENTRY glMapVertexAttrib2fAPPLE (GLuint index, GLuint size, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat *points); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBAPPLEPROC) (GLuint index, GLenum pname); +typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBAPPLEPROC) (GLuint index, GLenum pname); +typedef GLboolean (APIENTRYP PFNGLISVERTEXATTRIBENABLEDAPPLEPROC) (GLuint index, GLenum pname); +typedef void (APIENTRYP PFNGLMAPVERTEXATTRIB1DAPPLEPROC) (GLuint index, GLuint size, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble *points); +typedef void (APIENTRYP PFNGLMAPVERTEXATTRIB1FAPPLEPROC) (GLuint index, GLuint size, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat *points); +typedef void (APIENTRYP PFNGLMAPVERTEXATTRIB2DAPPLEPROC) (GLuint index, GLuint size, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble *points); +typedef void (APIENTRYP PFNGLMAPVERTEXATTRIB2FAPPLEPROC) (GLuint index, GLuint size, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat *points); +#endif + +#ifndef GL_APPLE_aux_depth_stencil +#define GL_APPLE_aux_depth_stencil 1 +#endif + +#ifndef GL_APPLE_object_purgeable +#define GL_APPLE_object_purgeable 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLenum APIENTRY glObjectPurgeableAPPLE (GLenum objectType, GLuint name, GLenum option); +GLAPI GLenum APIENTRY glObjectUnpurgeableAPPLE (GLenum objectType, GLuint name, GLenum option); +GLAPI void APIENTRY glGetObjectParameterivAPPLE (GLenum objectType, GLuint name, GLenum pname, GLint *params); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef GLenum (APIENTRYP PFNGLOBJECTPURGEABLEAPPLEPROC) (GLenum objectType, GLuint name, GLenum option); +typedef GLenum (APIENTRYP PFNGLOBJECTUNPURGEABLEAPPLEPROC) (GLenum objectType, GLuint name, GLenum option); +typedef void (APIENTRYP PFNGLGETOBJECTPARAMETERIVAPPLEPROC) (GLenum objectType, GLuint name, GLenum pname, GLint *params); +#endif + +#ifndef GL_APPLE_row_bytes +#define GL_APPLE_row_bytes 1 +#endif + +#ifndef GL_APPLE_rgb_422 +#define GL_APPLE_rgb_422 1 +#endif + +#ifndef GL_NV_video_capture +#define GL_NV_video_capture 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBeginVideoCaptureNV (GLuint video_capture_slot); +GLAPI void APIENTRY glBindVideoCaptureStreamBufferNV (GLuint video_capture_slot, GLuint stream, GLenum frame_region, GLintptrARB offset); +GLAPI void APIENTRY glBindVideoCaptureStreamTextureNV (GLuint video_capture_slot, GLuint stream, GLenum frame_region, GLenum target, GLuint texture); +GLAPI void APIENTRY glEndVideoCaptureNV (GLuint video_capture_slot); +GLAPI void APIENTRY glGetVideoCaptureivNV (GLuint video_capture_slot, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVideoCaptureStreamivNV (GLuint video_capture_slot, GLuint stream, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVideoCaptureStreamfvNV (GLuint video_capture_slot, GLuint stream, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetVideoCaptureStreamdvNV (GLuint video_capture_slot, GLuint stream, GLenum pname, GLdouble *params); +GLAPI GLenum APIENTRY glVideoCaptureNV (GLuint video_capture_slot, GLuint *sequence_num, GLuint64EXT *capture_time); +GLAPI void APIENTRY glVideoCaptureStreamParameterivNV (GLuint video_capture_slot, GLuint stream, GLenum pname, const GLint *params); +GLAPI void APIENTRY glVideoCaptureStreamParameterfvNV (GLuint video_capture_slot, GLuint stream, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glVideoCaptureStreamParameterdvNV (GLuint video_capture_slot, GLuint stream, GLenum pname, const GLdouble *params); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBEGINVIDEOCAPTURENVPROC) (GLuint video_capture_slot); +typedef void (APIENTRYP PFNGLBINDVIDEOCAPTURESTREAMBUFFERNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum frame_region, GLintptrARB offset); +typedef void (APIENTRYP PFNGLBINDVIDEOCAPTURESTREAMTEXTURENVPROC) (GLuint video_capture_slot, GLuint stream, GLenum frame_region, GLenum target, GLuint texture); +typedef void (APIENTRYP PFNGLENDVIDEOCAPTURENVPROC) (GLuint video_capture_slot); +typedef void (APIENTRYP PFNGLGETVIDEOCAPTUREIVNVPROC) (GLuint video_capture_slot, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVIDEOCAPTURESTREAMIVNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVIDEOCAPTURESTREAMFVNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETVIDEOCAPTURESTREAMDVNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum pname, GLdouble *params); +typedef GLenum (APIENTRYP PFNGLVIDEOCAPTURENVPROC) (GLuint video_capture_slot, GLuint *sequence_num, GLuint64EXT *capture_time); +typedef void (APIENTRYP PFNGLVIDEOCAPTURESTREAMPARAMETERIVNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLVIDEOCAPTURESTREAMPARAMETERFVNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLVIDEOCAPTURESTREAMPARAMETERDVNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum pname, const GLdouble *params); +#endif + +#ifndef GL_NV_copy_image +#define GL_NV_copy_image 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCopyImageSubDataNV (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLCOPYIMAGESUBDATANVPROC) (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth); +#endif + +#ifndef GL_EXT_separate_shader_objects +#define GL_EXT_separate_shader_objects 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glUseShaderProgramEXT (GLenum type, GLuint program); +GLAPI void APIENTRY glActiveProgramEXT (GLuint program); +GLAPI GLuint APIENTRY glCreateShaderProgramEXT (GLenum type, const GLchar *string); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLUSESHADERPROGRAMEXTPROC) (GLenum type, GLuint program); +typedef void (APIENTRYP PFNGLACTIVEPROGRAMEXTPROC) (GLuint program); +typedef GLuint (APIENTRYP PFNGLCREATESHADERPROGRAMEXTPROC) (GLenum type, const GLchar *string); +#endif + +#ifndef GL_NV_parameter_buffer_object2 +#define GL_NV_parameter_buffer_object2 1 +#endif + +#ifndef GL_NV_shader_buffer_load +#define GL_NV_shader_buffer_load 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMakeBufferResidentNV (GLenum target, GLenum access); +GLAPI void APIENTRY glMakeBufferNonResidentNV (GLenum target); +GLAPI GLboolean APIENTRY glIsBufferResidentNV (GLenum target); +GLAPI void APIENTRY glMakeNamedBufferResidentNV (GLuint buffer, GLenum access); +GLAPI void APIENTRY glMakeNamedBufferNonResidentNV (GLuint buffer); +GLAPI GLboolean APIENTRY glIsNamedBufferResidentNV (GLuint buffer); +GLAPI void APIENTRY glGetBufferParameterui64vNV (GLenum target, GLenum pname, GLuint64EXT *params); +GLAPI void APIENTRY glGetNamedBufferParameterui64vNV (GLuint buffer, GLenum pname, GLuint64EXT *params); +GLAPI void APIENTRY glGetIntegerui64vNV (GLenum value, GLuint64EXT *result); +GLAPI void APIENTRY glUniformui64NV (GLint location, GLuint64EXT value); +GLAPI void APIENTRY glUniformui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glGetUniformui64vNV (GLuint program, GLint location, GLuint64EXT *params); +GLAPI void APIENTRY glProgramUniformui64NV (GLuint program, GLint location, GLuint64EXT value); +GLAPI void APIENTRY glProgramUniformui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLMAKEBUFFERRESIDENTNVPROC) (GLenum target, GLenum access); +typedef void (APIENTRYP PFNGLMAKEBUFFERNONRESIDENTNVPROC) (GLenum target); +typedef GLboolean (APIENTRYP PFNGLISBUFFERRESIDENTNVPROC) (GLenum target); +typedef void (APIENTRYP PFNGLMAKENAMEDBUFFERRESIDENTNVPROC) (GLuint buffer, GLenum access); +typedef void (APIENTRYP PFNGLMAKENAMEDBUFFERNONRESIDENTNVPROC) (GLuint buffer); +typedef GLboolean (APIENTRYP PFNGLISNAMEDBUFFERRESIDENTNVPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERUI64VNVPROC) (GLenum target, GLenum pname, GLuint64EXT *params); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERUI64VNVPROC) (GLuint buffer, GLenum pname, GLuint64EXT *params); +typedef void (APIENTRYP PFNGLGETINTEGERUI64VNVPROC) (GLenum value, GLuint64EXT *result); +typedef void (APIENTRYP PFNGLUNIFORMUI64NVPROC) (GLint location, GLuint64EXT value); +typedef void (APIENTRYP PFNGLUNIFORMUI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLGETUNIFORMUI64VNVPROC) (GLuint program, GLint location, GLuint64EXT *params); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMUI64NVPROC) (GLuint program, GLint location, GLuint64EXT value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMUI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +#endif + +#ifndef GL_NV_vertex_buffer_unified_memory +#define GL_NV_vertex_buffer_unified_memory 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBufferAddressRangeNV (GLenum pname, GLuint index, GLuint64EXT address, GLsizeiptr length); +GLAPI void APIENTRY glVertexFormatNV (GLint size, GLenum type, GLsizei stride); +GLAPI void APIENTRY glNormalFormatNV (GLenum type, GLsizei stride); +GLAPI void APIENTRY glColorFormatNV (GLint size, GLenum type, GLsizei stride); +GLAPI void APIENTRY glIndexFormatNV (GLenum type, GLsizei stride); +GLAPI void APIENTRY glTexCoordFormatNV (GLint size, GLenum type, GLsizei stride); +GLAPI void APIENTRY glEdgeFlagFormatNV (GLsizei stride); +GLAPI void APIENTRY glSecondaryColorFormatNV (GLint size, GLenum type, GLsizei stride); +GLAPI void APIENTRY glFogCoordFormatNV (GLenum type, GLsizei stride); +GLAPI void APIENTRY glVertexAttribFormatNV (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride); +GLAPI void APIENTRY glVertexAttribIFormatNV (GLuint index, GLint size, GLenum type, GLsizei stride); +GLAPI void APIENTRY glGetIntegerui64i_vNV (GLenum value, GLuint index, GLuint64EXT *result); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBUFFERADDRESSRANGENVPROC) (GLenum pname, GLuint index, GLuint64EXT address, GLsizeiptr length); +typedef void (APIENTRYP PFNGLVERTEXFORMATNVPROC) (GLint size, GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLNORMALFORMATNVPROC) (GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLCOLORFORMATNVPROC) (GLint size, GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLINDEXFORMATNVPROC) (GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLTEXCOORDFORMATNVPROC) (GLint size, GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLEDGEFLAGFORMATNVPROC) (GLsizei stride); +typedef void (APIENTRYP PFNGLSECONDARYCOLORFORMATNVPROC) (GLint size, GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLFOGCOORDFORMATNVPROC) (GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLVERTEXATTRIBFORMATNVPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride); +typedef void (APIENTRYP PFNGLVERTEXATTRIBIFORMATNVPROC) (GLuint index, GLint size, GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLGETINTEGERUI64I_VNVPROC) (GLenum value, GLuint index, GLuint64EXT *result); +#endif + +#ifndef GL_NV_texture_barrier +#define GL_NV_texture_barrier 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTextureBarrierNV (void); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLTEXTUREBARRIERNVPROC) (void); +#endif + +#ifndef GL_AMD_shader_stencil_export +#define GL_AMD_shader_stencil_export 1 +#endif + +#ifndef GL_AMD_seamless_cubemap_per_texture +#define GL_AMD_seamless_cubemap_per_texture 1 +#endif + +#ifndef GL_AMD_conservative_depth +#define GL_AMD_conservative_depth 1 +#endif + +#ifndef GL_EXT_shader_image_load_store +#define GL_EXT_shader_image_load_store 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBindImageTextureEXT (GLuint index, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLint format); +GLAPI void APIENTRY glMemoryBarrierEXT (GLbitfield barriers); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBINDIMAGETEXTUREEXTPROC) (GLuint index, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLint format); +typedef void (APIENTRYP PFNGLMEMORYBARRIEREXTPROC) (GLbitfield barriers); +#endif + +#ifndef GL_EXT_vertex_attrib_64bit +#define GL_EXT_vertex_attrib_64bit 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexAttribL1dEXT (GLuint index, GLdouble x); +GLAPI void APIENTRY glVertexAttribL2dEXT (GLuint index, GLdouble x, GLdouble y); +GLAPI void APIENTRY glVertexAttribL3dEXT (GLuint index, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glVertexAttribL4dEXT (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glVertexAttribL1dvEXT (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribL2dvEXT (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribL3dvEXT (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribL4dvEXT (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribLPointerEXT (GLuint index, GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +GLAPI void APIENTRY glGetVertexAttribLdvEXT (GLuint index, GLenum pname, GLdouble *params); +GLAPI void APIENTRY glVertexArrayVertexAttribLOffsetEXT (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1DEXTPROC) (GLuint index, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2DEXTPROC) (GLuint index, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3DEXTPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4DEXTPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1DVEXTPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2DVEXTPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3DVEXTPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4DVEXTPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBLPOINTEREXTPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLDVEXTPROC) (GLuint index, GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBLOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset); +#endif + +#ifndef GL_NV_gpu_program5 +#define GL_NV_gpu_program5 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramSubroutineParametersuivNV (GLenum target, GLsizei count, const GLuint *params); +GLAPI void APIENTRY glGetProgramSubroutineParameteruivNV (GLenum target, GLuint index, GLuint *param); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLPROGRAMSUBROUTINEPARAMETERSUIVNVPROC) (GLenum target, GLsizei count, const GLuint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMSUBROUTINEPARAMETERUIVNVPROC) (GLenum target, GLuint index, GLuint *param); +#endif + +#ifndef GL_NV_gpu_shader5 +#define GL_NV_gpu_shader5 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glUniform1i64NV (GLint location, GLint64EXT x); +GLAPI void APIENTRY glUniform2i64NV (GLint location, GLint64EXT x, GLint64EXT y); +GLAPI void APIENTRY glUniform3i64NV (GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z); +GLAPI void APIENTRY glUniform4i64NV (GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); +GLAPI void APIENTRY glUniform1i64vNV (GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glUniform2i64vNV (GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glUniform3i64vNV (GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glUniform4i64vNV (GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glUniform1ui64NV (GLint location, GLuint64EXT x); +GLAPI void APIENTRY glUniform2ui64NV (GLint location, GLuint64EXT x, GLuint64EXT y); +GLAPI void APIENTRY glUniform3ui64NV (GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); +GLAPI void APIENTRY glUniform4ui64NV (GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); +GLAPI void APIENTRY glUniform1ui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glUniform2ui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glUniform3ui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glUniform4ui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glGetUniformi64vNV (GLuint program, GLint location, GLint64EXT *params); +GLAPI void APIENTRY glProgramUniform1i64NV (GLuint program, GLint location, GLint64EXT x); +GLAPI void APIENTRY glProgramUniform2i64NV (GLuint program, GLint location, GLint64EXT x, GLint64EXT y); +GLAPI void APIENTRY glProgramUniform3i64NV (GLuint program, GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z); +GLAPI void APIENTRY glProgramUniform4i64NV (GLuint program, GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); +GLAPI void APIENTRY glProgramUniform1i64vNV (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glProgramUniform2i64vNV (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glProgramUniform3i64vNV (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glProgramUniform4i64vNV (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glProgramUniform1ui64NV (GLuint program, GLint location, GLuint64EXT x); +GLAPI void APIENTRY glProgramUniform2ui64NV (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y); +GLAPI void APIENTRY glProgramUniform3ui64NV (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); +GLAPI void APIENTRY glProgramUniform4ui64NV (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); +GLAPI void APIENTRY glProgramUniform1ui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glProgramUniform2ui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glProgramUniform3ui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glProgramUniform4ui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLUNIFORM1I64NVPROC) (GLint location, GLint64EXT x); +typedef void (APIENTRYP PFNGLUNIFORM2I64NVPROC) (GLint location, GLint64EXT x, GLint64EXT y); +typedef void (APIENTRYP PFNGLUNIFORM3I64NVPROC) (GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z); +typedef void (APIENTRYP PFNGLUNIFORM4I64NVPROC) (GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); +typedef void (APIENTRYP PFNGLUNIFORM1I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLUNIFORM2I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLUNIFORM3I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLUNIFORM4I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLUNIFORM1UI64NVPROC) (GLint location, GLuint64EXT x); +typedef void (APIENTRYP PFNGLUNIFORM2UI64NVPROC) (GLint location, GLuint64EXT x, GLuint64EXT y); +typedef void (APIENTRYP PFNGLUNIFORM3UI64NVPROC) (GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); +typedef void (APIENTRYP PFNGLUNIFORM4UI64NVPROC) (GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); +typedef void (APIENTRYP PFNGLUNIFORM1UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLUNIFORM2UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLUNIFORM3UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLUNIFORM4UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLGETUNIFORMI64VNVPROC) (GLuint program, GLint location, GLint64EXT *params); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1I64NVPROC) (GLuint program, GLint location, GLint64EXT x); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2I64NVPROC) (GLuint program, GLint location, GLint64EXT x, GLint64EXT y); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3I64NVPROC) (GLuint program, GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4I64NVPROC) (GLuint program, GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +#endif + +#ifndef GL_NV_shader_buffer_store +#define GL_NV_shader_buffer_store 1 +#endif + +#ifndef GL_NV_tessellation_program5 +#define GL_NV_tessellation_program5 1 +#endif + +#ifndef GL_NV_vertex_attrib_integer_64bit +#define GL_NV_vertex_attrib_integer_64bit 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexAttribL1i64NV (GLuint index, GLint64EXT x); +GLAPI void APIENTRY glVertexAttribL2i64NV (GLuint index, GLint64EXT x, GLint64EXT y); +GLAPI void APIENTRY glVertexAttribL3i64NV (GLuint index, GLint64EXT x, GLint64EXT y, GLint64EXT z); +GLAPI void APIENTRY glVertexAttribL4i64NV (GLuint index, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); +GLAPI void APIENTRY glVertexAttribL1i64vNV (GLuint index, const GLint64EXT *v); +GLAPI void APIENTRY glVertexAttribL2i64vNV (GLuint index, const GLint64EXT *v); +GLAPI void APIENTRY glVertexAttribL3i64vNV (GLuint index, const GLint64EXT *v); +GLAPI void APIENTRY glVertexAttribL4i64vNV (GLuint index, const GLint64EXT *v); +GLAPI void APIENTRY glVertexAttribL1ui64NV (GLuint index, GLuint64EXT x); +GLAPI void APIENTRY glVertexAttribL2ui64NV (GLuint index, GLuint64EXT x, GLuint64EXT y); +GLAPI void APIENTRY glVertexAttribL3ui64NV (GLuint index, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); +GLAPI void APIENTRY glVertexAttribL4ui64NV (GLuint index, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); +GLAPI void APIENTRY glVertexAttribL1ui64vNV (GLuint index, const GLuint64EXT *v); +GLAPI void APIENTRY glVertexAttribL2ui64vNV (GLuint index, const GLuint64EXT *v); +GLAPI void APIENTRY glVertexAttribL3ui64vNV (GLuint index, const GLuint64EXT *v); +GLAPI void APIENTRY glVertexAttribL4ui64vNV (GLuint index, const GLuint64EXT *v); +GLAPI void APIENTRY glGetVertexAttribLi64vNV (GLuint index, GLenum pname, GLint64EXT *params); +GLAPI void APIENTRY glGetVertexAttribLui64vNV (GLuint index, GLenum pname, GLuint64EXT *params); +GLAPI void APIENTRY glVertexAttribLFormatNV (GLuint index, GLint size, GLenum type, GLsizei stride); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1I64NVPROC) (GLuint index, GLint64EXT x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2I64NVPROC) (GLuint index, GLint64EXT x, GLint64EXT y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3I64NVPROC) (GLuint index, GLint64EXT x, GLint64EXT y, GLint64EXT z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4I64NVPROC) (GLuint index, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1I64VNVPROC) (GLuint index, const GLint64EXT *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2I64VNVPROC) (GLuint index, const GLint64EXT *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3I64VNVPROC) (GLuint index, const GLint64EXT *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4I64VNVPROC) (GLuint index, const GLint64EXT *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64NVPROC) (GLuint index, GLuint64EXT x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2UI64NVPROC) (GLuint index, GLuint64EXT x, GLuint64EXT y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3UI64NVPROC) (GLuint index, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4UI64NVPROC) (GLuint index, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64VNVPROC) (GLuint index, const GLuint64EXT *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2UI64VNVPROC) (GLuint index, const GLuint64EXT *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3UI64VNVPROC) (GLuint index, const GLuint64EXT *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4UI64VNVPROC) (GLuint index, const GLuint64EXT *v); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLI64VNVPROC) (GLuint index, GLenum pname, GLint64EXT *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLUI64VNVPROC) (GLuint index, GLenum pname, GLuint64EXT *params); +typedef void (APIENTRYP PFNGLVERTEXATTRIBLFORMATNVPROC) (GLuint index, GLint size, GLenum type, GLsizei stride); +#endif + +#ifndef GL_NV_multisample_coverage +#define GL_NV_multisample_coverage 1 +#endif + +#ifndef GL_AMD_name_gen_delete +#define GL_AMD_name_gen_delete 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGenNamesAMD (GLenum identifier, GLuint num, GLuint *names); +GLAPI void APIENTRY glDeleteNamesAMD (GLenum identifier, GLuint num, const GLuint *names); +GLAPI GLboolean APIENTRY glIsNameAMD (GLenum identifier, GLuint name); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLGENNAMESAMDPROC) (GLenum identifier, GLuint num, GLuint *names); +typedef void (APIENTRYP PFNGLDELETENAMESAMDPROC) (GLenum identifier, GLuint num, const GLuint *names); +typedef GLboolean (APIENTRYP PFNGLISNAMEAMDPROC) (GLenum identifier, GLuint name); +#endif + +#ifndef GL_AMD_debug_output +#define GL_AMD_debug_output 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDebugMessageEnableAMD (GLenum category, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +GLAPI void APIENTRY glDebugMessageInsertAMD (GLenum category, GLenum severity, GLuint id, GLsizei length, const GLchar *buf); +GLAPI void APIENTRY glDebugMessageCallbackAMD (GLDEBUGPROCAMD callback, GLvoid *userParam); +GLAPI GLuint APIENTRY glGetDebugMessageLogAMD (GLuint count, GLsizei bufsize, GLenum *categories, GLuint *severities, GLuint *ids, GLsizei *lengths, GLchar *message); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLDEBUGMESSAGEENABLEAMDPROC) (GLenum category, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTAMDPROC) (GLenum category, GLenum severity, GLuint id, GLsizei length, const GLchar *buf); +typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKAMDPROC) (GLDEBUGPROCAMD callback, GLvoid *userParam); +typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGAMDPROC) (GLuint count, GLsizei bufsize, GLenum *categories, GLuint *severities, GLuint *ids, GLsizei *lengths, GLchar *message); +#endif + +#ifndef GL_NV_vdpau_interop +#define GL_NV_vdpau_interop 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVDPAUInitNV (const GLvoid *vdpDevice, const GLvoid *getProcAddress); +GLAPI void APIENTRY glVDPAUFiniNV (void); +GLAPI GLvdpauSurfaceNV APIENTRY glVDPAURegisterVideoSurfaceNV (GLvoid *vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint *textureNames); +GLAPI GLvdpauSurfaceNV APIENTRY glVDPAURegisterOutputSurfaceNV (GLvoid *vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint *textureNames); +GLAPI void APIENTRY glVDPAUIsSurfaceNV (GLvdpauSurfaceNV surface); +GLAPI void APIENTRY glVDPAUUnregisterSurfaceNV (GLvdpauSurfaceNV surface); +GLAPI void APIENTRY glVDPAUGetSurfaceivNV (GLvdpauSurfaceNV surface, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values); +GLAPI void APIENTRY glVDPAUSurfaceAccessNV (GLvdpauSurfaceNV surface, GLenum access); +GLAPI void APIENTRY glVDPAUMapSurfacesNV (GLsizei numSurfaces, const GLvdpauSurfaceNV *surfaces); +GLAPI void APIENTRY glVDPAUUnmapSurfacesNV (GLsizei numSurface, const GLvdpauSurfaceNV *surfaces); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLVDPAUINITNVPROC) (const GLvoid *vdpDevice, const GLvoid *getProcAddress); +typedef void (APIENTRYP PFNGLVDPAUFININVPROC) (void); +typedef GLvdpauSurfaceNV (APIENTRYP PFNGLVDPAUREGISTERVIDEOSURFACENVPROC) (GLvoid *vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint *textureNames); +typedef GLvdpauSurfaceNV (APIENTRYP PFNGLVDPAUREGISTEROUTPUTSURFACENVPROC) (GLvoid *vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint *textureNames); +typedef void (APIENTRYP PFNGLVDPAUISSURFACENVPROC) (GLvdpauSurfaceNV surface); +typedef void (APIENTRYP PFNGLVDPAUUNREGISTERSURFACENVPROC) (GLvdpauSurfaceNV surface); +typedef void (APIENTRYP PFNGLVDPAUGETSURFACEIVNVPROC) (GLvdpauSurfaceNV surface, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values); +typedef void (APIENTRYP PFNGLVDPAUSURFACEACCESSNVPROC) (GLvdpauSurfaceNV surface, GLenum access); +typedef void (APIENTRYP PFNGLVDPAUMAPSURFACESNVPROC) (GLsizei numSurfaces, const GLvdpauSurfaceNV *surfaces); +typedef void (APIENTRYP PFNGLVDPAUUNMAPSURFACESNVPROC) (GLsizei numSurface, const GLvdpauSurfaceNV *surfaces); +#endif + +#ifndef GL_AMD_transform_feedback3_lines_triangles +#define GL_AMD_transform_feedback3_lines_triangles 1 +#endif + + +#ifdef __cplusplus +} +#endif + +#endif +/* *INDENT-ON* */ +#endif /* NO_SDL_GLEXT */ + +#endif /* _SDL_opengl_h */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_pixels.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_pixels.h new file mode 100644 index 0000000..ede9e29 --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_pixels.h @@ -0,0 +1,423 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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. +*/ + +/** + * \file SDL_pixels.h + * + * Header for the enumerated pixel format definitions. + */ + +#ifndef _SDL_pixels_h +#define _SDL_pixels_h + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +/** + * \name Transparency definitions + * + * These define alpha as the opacity of a surface. + */ +/*@{*/ +#define SDL_ALPHA_OPAQUE 255 +#define SDL_ALPHA_TRANSPARENT 0 +/*@}*/ + +/** Pixel type. */ +enum +{ + SDL_PIXELTYPE_UNKNOWN, + SDL_PIXELTYPE_INDEX1, + SDL_PIXELTYPE_INDEX4, + SDL_PIXELTYPE_INDEX8, + SDL_PIXELTYPE_PACKED8, + SDL_PIXELTYPE_PACKED16, + SDL_PIXELTYPE_PACKED32, + SDL_PIXELTYPE_ARRAYU8, + SDL_PIXELTYPE_ARRAYU16, + SDL_PIXELTYPE_ARRAYU32, + SDL_PIXELTYPE_ARRAYF16, + SDL_PIXELTYPE_ARRAYF32 +}; + +/** Bitmap pixel order, high bit -> low bit. */ +enum +{ + SDL_BITMAPORDER_NONE, + SDL_BITMAPORDER_4321, + SDL_BITMAPORDER_1234 +}; + +/** Packed component order, high bit -> low bit. */ +enum +{ + SDL_PACKEDORDER_NONE, + SDL_PACKEDORDER_XRGB, + SDL_PACKEDORDER_RGBX, + SDL_PACKEDORDER_ARGB, + SDL_PACKEDORDER_RGBA, + SDL_PACKEDORDER_XBGR, + SDL_PACKEDORDER_BGRX, + SDL_PACKEDORDER_ABGR, + SDL_PACKEDORDER_BGRA +}; + +/** Array component order, low byte -> high byte. */ +enum +{ + SDL_ARRAYORDER_NONE, + SDL_ARRAYORDER_RGB, + SDL_ARRAYORDER_RGBA, + SDL_ARRAYORDER_ARGB, + SDL_ARRAYORDER_BGR, + SDL_ARRAYORDER_BGRA, + SDL_ARRAYORDER_ABGR +}; + +/** Packed component layout. */ +enum +{ + SDL_PACKEDLAYOUT_NONE, + SDL_PACKEDLAYOUT_332, + SDL_PACKEDLAYOUT_4444, + SDL_PACKEDLAYOUT_1555, + SDL_PACKEDLAYOUT_5551, + SDL_PACKEDLAYOUT_565, + SDL_PACKEDLAYOUT_8888, + SDL_PACKEDLAYOUT_2101010, + SDL_PACKEDLAYOUT_1010102 +}; + +#define SDL_DEFINE_PIXELFOURCC(A, B, C, D) SDL_FOURCC(A, B, C, D) + +#define SDL_DEFINE_PIXELFORMAT(type, order, layout, bits, bytes) \ + ((1 << 31) | ((type) << 24) | ((order) << 20) | ((layout) << 16) | \ + ((bits) << 8) | ((bytes) << 0)) + +#define SDL_PIXELTYPE(X) (((X) >> 24) & 0x0F) +#define SDL_PIXELORDER(X) (((X) >> 20) & 0x0F) +#define SDL_PIXELLAYOUT(X) (((X) >> 16) & 0x0F) +#define SDL_BITSPERPIXEL(X) (((X) >> 8) & 0xFF) +#define SDL_BYTESPERPIXEL(X) \ + (SDL_ISPIXELFORMAT_FOURCC(X) ? \ + ((((X) == SDL_PIXELFORMAT_YUY2) || \ + ((X) == SDL_PIXELFORMAT_UYVY) || \ + ((X) == SDL_PIXELFORMAT_YVYU)) ? 2 : 1) : (((X) >> 0) & 0xFF)) + +#define SDL_ISPIXELFORMAT_INDEXED(format) \ + (!SDL_ISPIXELFORMAT_FOURCC(format) && \ + ((SDL_PIXELTYPE(format) == SDL_PIXELTYPE_INDEX1) || \ + (SDL_PIXELTYPE(format) == SDL_PIXELTYPE_INDEX4) || \ + (SDL_PIXELTYPE(format) == SDL_PIXELTYPE_INDEX8))) + +#define SDL_ISPIXELFORMAT_ALPHA(format) \ + (!SDL_ISPIXELFORMAT_FOURCC(format) && \ + ((SDL_PIXELORDER(format) == SDL_PACKEDORDER_ARGB) || \ + (SDL_PIXELORDER(format) == SDL_PACKEDORDER_RGBA) || \ + (SDL_PIXELORDER(format) == SDL_PACKEDORDER_ABGR) || \ + (SDL_PIXELORDER(format) == SDL_PACKEDORDER_BGRA))) + +#define SDL_ISPIXELFORMAT_FOURCC(format) \ + ((format) && !((format) & 0x80000000)) + +/* Note: If you modify this list, update SDL_GetPixelFormatName() */ +enum +{ + SDL_PIXELFORMAT_UNKNOWN, + SDL_PIXELFORMAT_INDEX1LSB = + SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_INDEX1, SDL_BITMAPORDER_4321, 0, + 1, 0), + SDL_PIXELFORMAT_INDEX1MSB = + SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_INDEX1, SDL_BITMAPORDER_1234, 0, + 1, 0), + SDL_PIXELFORMAT_INDEX4LSB = + SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_INDEX4, SDL_BITMAPORDER_4321, 0, + 4, 0), + SDL_PIXELFORMAT_INDEX4MSB = + SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_INDEX4, SDL_BITMAPORDER_1234, 0, + 4, 0), + SDL_PIXELFORMAT_INDEX8 = + SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_INDEX8, 0, 0, 8, 1), + SDL_PIXELFORMAT_RGB332 = + SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED8, SDL_PACKEDORDER_XRGB, + SDL_PACKEDLAYOUT_332, 8, 1), + SDL_PIXELFORMAT_RGB444 = + SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_XRGB, + SDL_PACKEDLAYOUT_4444, 12, 2), + SDL_PIXELFORMAT_RGB555 = + SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_XRGB, + SDL_PACKEDLAYOUT_1555, 15, 2), + SDL_PIXELFORMAT_BGR555 = + SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_XBGR, + SDL_PACKEDLAYOUT_1555, 15, 2), + SDL_PIXELFORMAT_ARGB4444 = + SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_ARGB, + SDL_PACKEDLAYOUT_4444, 16, 2), + SDL_PIXELFORMAT_RGBA4444 = + SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_RGBA, + SDL_PACKEDLAYOUT_4444, 16, 2), + SDL_PIXELFORMAT_ABGR4444 = + SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_ABGR, + SDL_PACKEDLAYOUT_4444, 16, 2), + SDL_PIXELFORMAT_BGRA4444 = + SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_BGRA, + SDL_PACKEDLAYOUT_4444, 16, 2), + SDL_PIXELFORMAT_ARGB1555 = + SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_ARGB, + SDL_PACKEDLAYOUT_1555, 16, 2), + SDL_PIXELFORMAT_RGBA5551 = + SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_RGBA, + SDL_PACKEDLAYOUT_5551, 16, 2), + SDL_PIXELFORMAT_ABGR1555 = + SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_ABGR, + SDL_PACKEDLAYOUT_1555, 16, 2), + SDL_PIXELFORMAT_BGRA5551 = + SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_BGRA, + SDL_PACKEDLAYOUT_5551, 16, 2), + SDL_PIXELFORMAT_RGB565 = + SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_XRGB, + SDL_PACKEDLAYOUT_565, 16, 2), + SDL_PIXELFORMAT_BGR565 = + SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_XBGR, + SDL_PACKEDLAYOUT_565, 16, 2), + SDL_PIXELFORMAT_RGB24 = + SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_ARRAYU8, SDL_ARRAYORDER_RGB, 0, + 24, 3), + SDL_PIXELFORMAT_BGR24 = + SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_ARRAYU8, SDL_ARRAYORDER_BGR, 0, + 24, 3), + SDL_PIXELFORMAT_RGB888 = + SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_XRGB, + SDL_PACKEDLAYOUT_8888, 24, 4), + SDL_PIXELFORMAT_BGR888 = + SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_XBGR, + SDL_PACKEDLAYOUT_8888, 24, 4), + SDL_PIXELFORMAT_ARGB8888 = + SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_ARGB, + SDL_PACKEDLAYOUT_8888, 32, 4), + SDL_PIXELFORMAT_RGBA8888 = + SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_RGBA, + SDL_PACKEDLAYOUT_8888, 32, 4), + SDL_PIXELFORMAT_ABGR8888 = + SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_ABGR, + SDL_PACKEDLAYOUT_8888, 32, 4), + SDL_PIXELFORMAT_BGRA8888 = + SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_BGRA, + SDL_PACKEDLAYOUT_8888, 32, 4), + SDL_PIXELFORMAT_ARGB2101010 = + SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_ARGB, + SDL_PACKEDLAYOUT_2101010, 32, 4), + + SDL_PIXELFORMAT_YV12 = /**< Planar mode: Y + V + U (3 planes) */ + SDL_DEFINE_PIXELFOURCC('Y', 'V', '1', '2'), + SDL_PIXELFORMAT_IYUV = /**< Planar mode: Y + U + V (3 planes) */ + SDL_DEFINE_PIXELFOURCC('I', 'Y', 'U', 'V'), + SDL_PIXELFORMAT_YUY2 = /**< Packed mode: Y0+U0+Y1+V0 (1 plane) */ + SDL_DEFINE_PIXELFOURCC('Y', 'U', 'Y', '2'), + SDL_PIXELFORMAT_UYVY = /**< Packed mode: U0+Y0+V0+Y1 (1 plane) */ + SDL_DEFINE_PIXELFOURCC('U', 'Y', 'V', 'Y'), + SDL_PIXELFORMAT_YVYU = /**< Packed mode: Y0+V0+Y1+U0 (1 plane) */ + SDL_DEFINE_PIXELFOURCC('Y', 'V', 'Y', 'U') +}; + +typedef struct SDL_Color +{ + Uint8 r; + Uint8 g; + Uint8 b; + Uint8 unused; +} SDL_Color; +#define SDL_Colour SDL_Color + +typedef struct SDL_Palette +{ + int ncolors; + SDL_Color *colors; + Uint32 version; + int refcount; +} SDL_Palette; + +/** + * \note Everything in the pixel format structure is read-only. + */ +typedef struct SDL_PixelFormat +{ + Uint32 format; + SDL_Palette *palette; + Uint8 BitsPerPixel; + Uint8 BytesPerPixel; + Uint8 padding[2]; + Uint32 Rmask; + Uint32 Gmask; + Uint32 Bmask; + Uint32 Amask; + Uint8 Rloss; + Uint8 Gloss; + Uint8 Bloss; + Uint8 Aloss; + Uint8 Rshift; + Uint8 Gshift; + Uint8 Bshift; + Uint8 Ashift; + int refcount; + struct SDL_PixelFormat *next; +} SDL_PixelFormat; + +/** + * \brief Get the human readable name of a pixel format + */ +extern DECLSPEC const char* SDLCALL SDL_GetPixelFormatName(Uint32 format); + +/** + * \brief Convert one of the enumerated pixel formats to a bpp and RGBA masks. + * + * \return SDL_TRUE, or SDL_FALSE if the conversion wasn't possible. + * + * \sa SDL_MasksToPixelFormatEnum() + */ +extern DECLSPEC SDL_bool SDLCALL SDL_PixelFormatEnumToMasks(Uint32 format, + int *bpp, + Uint32 * Rmask, + Uint32 * Gmask, + Uint32 * Bmask, + Uint32 * Amask); + +/** + * \brief Convert a bpp and RGBA masks to an enumerated pixel format. + * + * \return The pixel format, or ::SDL_PIXELFORMAT_UNKNOWN if the conversion + * wasn't possible. + * + * \sa SDL_PixelFormatEnumToMasks() + */ +extern DECLSPEC Uint32 SDLCALL SDL_MasksToPixelFormatEnum(int bpp, + Uint32 Rmask, + Uint32 Gmask, + Uint32 Bmask, + Uint32 Amask); + +/** + * \brief Create an SDL_PixelFormat structure from a pixel format enum. + */ +extern DECLSPEC SDL_PixelFormat * SDLCALL SDL_AllocFormat(Uint32 pixel_format); + +/** + * \brief Free an SDL_PixelFormat structure. + */ +extern DECLSPEC void SDLCALL SDL_FreeFormat(SDL_PixelFormat *format); + +/** + * \brief Create a palette structure with the specified number of color + * entries. + * + * \return A new palette, or NULL if there wasn't enough memory. + * + * \note The palette entries are initialized to white. + * + * \sa SDL_FreePalette() + */ +extern DECLSPEC SDL_Palette *SDLCALL SDL_AllocPalette(int ncolors); + +/** + * \brief Set the palette for a pixel format structure. + */ +extern DECLSPEC int SDLCALL SDL_SetPixelFormatPalette(SDL_PixelFormat * format, + SDL_Palette *palette); + +/** + * \brief Set a range of colors in a palette. + * + * \param palette The palette to modify. + * \param colors An array of colors to copy into the palette. + * \param firstcolor The index of the first palette entry to modify. + * \param ncolors The number of entries to modify. + * + * \return 0 on success, or -1 if not all of the colors could be set. + */ +extern DECLSPEC int SDLCALL SDL_SetPaletteColors(SDL_Palette * palette, + const SDL_Color * colors, + int firstcolor, int ncolors); + +/** + * \brief Free a palette created with SDL_AllocPalette(). + * + * \sa SDL_AllocPalette() + */ +extern DECLSPEC void SDLCALL SDL_FreePalette(SDL_Palette * palette); + +/** + * \brief Maps an RGB triple to an opaque pixel value for a given pixel format. + * + * \sa SDL_MapRGBA + */ +extern DECLSPEC Uint32 SDLCALL SDL_MapRGB(const SDL_PixelFormat * format, + Uint8 r, Uint8 g, Uint8 b); + +/** + * \brief Maps an RGBA quadruple to a pixel value for a given pixel format. + * + * \sa SDL_MapRGB + */ +extern DECLSPEC Uint32 SDLCALL SDL_MapRGBA(const SDL_PixelFormat * format, + Uint8 r, Uint8 g, Uint8 b, + Uint8 a); + +/** + * \brief Get the RGB components from a pixel of the specified format. + * + * \sa SDL_GetRGBA + */ +extern DECLSPEC void SDLCALL SDL_GetRGB(Uint32 pixel, + const SDL_PixelFormat * format, + Uint8 * r, Uint8 * g, Uint8 * b); + +/** + * \brief Get the RGBA components from a pixel of the specified format. + * + * \sa SDL_GetRGB + */ +extern DECLSPEC void SDLCALL SDL_GetRGBA(Uint32 pixel, + const SDL_PixelFormat * format, + Uint8 * r, Uint8 * g, Uint8 * b, + Uint8 * a); + +/** + * \brief Calculate a 256 entry gamma ramp for a gamma value. + */ +extern DECLSPEC void SDLCALL SDL_CalculateGammaRamp(float gamma, Uint16 * ramp); + + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif +#include "close_code.h" + +#endif /* _SDL_pixels_h */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_platform.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_platform.h new file mode 100644 index 0000000..9e7a33b --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_platform.h @@ -0,0 +1,160 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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. +*/ + +/** + * \file SDL_platform.h + * + * Try to get a standard set of platform defines. + */ + +#ifndef _SDL_platform_h +#define _SDL_platform_h + +#if defined(_AIX) +#undef __AIX__ +#define __AIX__ 1 +#endif +#if defined(__BEOS__) +#undef __BEOS__ +#define __BEOS__ 1 +#endif +#if defined(__HAIKU__) +#undef __HAIKU__ +#define __HAIKU__ 1 +#endif +#if defined(bsdi) || defined(__bsdi) || defined(__bsdi__) +#undef __BSDI__ +#define __BSDI__ 1 +#endif +#if defined(_arch_dreamcast) +#undef __DREAMCAST__ +#define __DREAMCAST__ 1 +#endif +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) +#undef __FREEBSD__ +#define __FREEBSD__ 1 +#endif +#if defined(hpux) || defined(__hpux) || defined(__hpux__) +#undef __HPUX__ +#define __HPUX__ 1 +#endif +#if defined(sgi) || defined(__sgi) || defined(__sgi__) || defined(_SGI_SOURCE) +#undef __IRIX__ +#define __IRIX__ 1 +#endif +#if defined(linux) || defined(__linux) || defined(__linux__) +#undef __LINUX__ +#define __LINUX__ 1 +#endif +#if defined(ANDROID) +#undef __ANDROID__ +#undef __LINUX__ /*do we need to do this?*/ +#define __ANDROID__ 1 +#endif + +#if defined(__APPLE__) +/* lets us know what version of Mac OS X we're compiling on */ +#include "AvailabilityMacros.h" +#include "TargetConditionals.h" +#ifndef MAC_OS_X_VERSION_10_4 +#define MAC_OS_X_VERSION_10_4 1040 +#endif +#ifndef MAC_OS_X_VERSION_10_5 +#define MAC_OS_X_VERSION_10_5 1050 +#endif +#ifndef MAC_OS_X_VERSION_10_6 +#define MAC_OS_X_VERSION_10_6 1060 +#endif +#if TARGET_OS_IPHONE +/* if compiling for iPhone */ +#undef __IPHONEOS__ +#define __IPHONEOS__ 1 +#undef __MACOSX__ +#else +/* if not compiling for iPhone */ +#undef __MACOSX__ +#define __MACOSX__ 1 +#endif /* TARGET_OS_IPHONE */ +#endif /* defined(__APPLE__) */ + +#if defined(__NetBSD__) +#undef __NETBSD__ +#define __NETBSD__ 1 +#endif +#if defined(__OpenBSD__) +#undef __OPENBSD__ +#define __OPENBSD__ 1 +#endif +#if defined(__OS2__) +#undef __OS2__ +#define __OS2__ 1 +#endif +#if defined(osf) || defined(__osf) || defined(__osf__) || defined(_OSF_SOURCE) +#undef __OSF__ +#define __OSF__ 1 +#endif +#if defined(__QNXNTO__) +#undef __QNXNTO__ +#define __QNXNTO__ 1 +#endif +#if defined(riscos) || defined(__riscos) || defined(__riscos__) +#undef __RISCOS__ +#define __RISCOS__ 1 +#endif +#if defined(__SVR4) +#undef __SOLARIS__ +#define __SOLARIS__ 1 +#endif +#if defined(WIN32) || defined(_WIN32) +#undef __WIN32__ +#define __WIN32__ 1 +#endif + +#if defined(__NDS__) +#undef __NINTENDODS__ +#define __NINTENDODS__ 1 +#endif + + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +/** + * \brief Gets the name of the platform. + */ +extern DECLSPEC const char * SDLCALL SDL_GetPlatform (void); + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif +#include "close_code.h" + +#endif /* _SDL_platform_h */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_power.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_power.h new file mode 100644 index 0000000..6cc1197 --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_power.h @@ -0,0 +1,79 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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 _SDL_power_h +#define _SDL_power_h + +/** + * \file SDL_power.h + * + * Header for the SDL power management routines. + */ + +#include "SDL_stdinc.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +/** + * \brief The basic state for the system's power supply. + */ +typedef enum +{ + SDL_POWERSTATE_UNKNOWN, /**< cannot determine power status */ + SDL_POWERSTATE_ON_BATTERY, /**< Not plugged in, running on the battery */ + SDL_POWERSTATE_NO_BATTERY, /**< Plugged in, no battery available */ + SDL_POWERSTATE_CHARGING, /**< Plugged in, charging battery */ + SDL_POWERSTATE_CHARGED /**< Plugged in, battery charged */ +} SDL_PowerState; + + +/** + * \brief Get the current power supply details. + * + * \param secs Seconds of battery life left. You can pass a NULL here if + * you don't care. Will return -1 if we can't determine a + * value, or we're not running on a battery. + * + * \param pct Percentage of battery life left, between 0 and 100. You can + * pass a NULL here if you don't care. Will return -1 if we + * can't determine a value, or we're not running on a battery. + * + * \return The state of the battery (if any). + */ +extern DECLSPEC SDL_PowerState SDLCALL SDL_GetPowerInfo(int *secs, int *pct); + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif +#include "close_code.h" + +#endif /* _SDL_power_h */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_quit.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_quit.h new file mode 100644 index 0000000..5529ce4 --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_quit.h @@ -0,0 +1,58 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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. +*/ + +/** + * \file SDL_quit.h + * + * Include file for SDL quit event handling. + */ + +#ifndef _SDL_quit_h +#define _SDL_quit_h + +#include "SDL_stdinc.h" +#include "SDL_error.h" + +/** + * \file SDL_quit.h + * + * An ::SDL_QUIT event is generated when the user tries to close the application + * window. If it is ignored or filtered out, the window will remain open. + * If it is not ignored or filtered, it is queued normally and the window + * is allowed to close. When the window is closed, screen updates will + * complete, but have no effect. + * + * SDL_Init() installs signal handlers for SIGINT (keyboard interrupt) + * and SIGTERM (system termination request), if handlers do not already + * exist, that generate ::SDL_QUIT events as well. There is no way + * to determine the cause of an ::SDL_QUIT event, but setting a signal + * handler in your application will override the default generation of + * quit events for that signal. + * + * \sa SDL_Quit() + */ + +/* There are no functions directly affecting the quit event */ + +#define SDL_QuitRequested() \ + (SDL_PumpEvents(), SDL_PeepEvents(NULL,0,SDL_PEEKEVENT,SDL_QUITMASK)) + +#endif /* _SDL_quit_h */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_rect.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_rect.h new file mode 100644 index 0000000..ee3dbff --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_rect.h @@ -0,0 +1,136 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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. +*/ + +/** + * \file SDL_rect.h + * + * Header file for SDL_rect definition and management functions. + */ + +#ifndef _SDL_rect_h +#define _SDL_rect_h + +#include "SDL_stdinc.h" +#include "SDL_error.h" +#include "SDL_pixels.h" +#include "SDL_rwops.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +/** + * \brief The structure that defines a point + * + * \sa SDL_EnclosePoints + */ +typedef struct +{ + int x; + int y; +} SDL_Point; + +/** + * \brief A rectangle, with the origin at the upper left. + * + * \sa SDL_RectEmpty + * \sa SDL_RectEquals + * \sa SDL_HasIntersection + * \sa SDL_IntersectRect + * \sa SDL_UnionRect + * \sa SDL_EnclosePoints + */ +typedef struct SDL_Rect +{ + int x, y; + int w, h; +} SDL_Rect; + +/** + * \brief Returns true if the rectangle has no area. + */ +#define SDL_RectEmpty(X) (((X)->w <= 0) || ((X)->h <= 0)) + +/** + * \brief Returns true if the two rectangles are equal. + */ +#define SDL_RectEquals(A, B) (((A)->x == (B)->x) && ((A)->y == (B)->y) && \ + ((A)->w == (B)->w) && ((A)->h == (B)->h)) + +/** + * \brief Determine whether two rectangles intersect. + * + * \return SDL_TRUE if there is an intersection, SDL_FALSE otherwise. + */ +extern DECLSPEC SDL_bool SDLCALL SDL_HasIntersection(const SDL_Rect * A, + const SDL_Rect * B); + +/** + * \brief Calculate the intersection of two rectangles. + * + * \return SDL_TRUE if there is an intersection, SDL_FALSE otherwise. + */ +extern DECLSPEC SDL_bool SDLCALL SDL_IntersectRect(const SDL_Rect * A, + const SDL_Rect * B, + SDL_Rect * result); + +/** + * \brief Calculate the union of two rectangles. + */ +extern DECLSPEC void SDLCALL SDL_UnionRect(const SDL_Rect * A, + const SDL_Rect * B, + SDL_Rect * result); + +/** + * \brief Calculate a minimal rectangle enclosing a set of points + * + * \return SDL_TRUE if any points were within the clipping rect + */ +extern DECLSPEC SDL_bool SDLCALL SDL_EnclosePoints(const SDL_Point * points, + int count, + const SDL_Rect * clip, + SDL_Rect * result); + +/** + * \brief Calculate the intersection of a rectangle and line segment. + * + * \return SDL_TRUE if there is an intersection, SDL_FALSE otherwise. + */ +extern DECLSPEC SDL_bool SDLCALL SDL_IntersectRectAndLine(const SDL_Rect * + rect, int *X1, + int *Y1, int *X2, + int *Y2); + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif +#include "close_code.h" + +#endif /* _SDL_rect_h */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_render.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_render.h new file mode 100644 index 0000000..bf940d0 --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_render.h @@ -0,0 +1,615 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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. +*/ + +/** + * \file SDL_render.h + * + * Header file for SDL 2D rendering functions. + * + * This API supports the following features: + * * single pixel points + * * single pixel lines + * * filled rectangles + * * texture images + * + * The primitives may be drawn in opaque, blended, or additive modes. + * + * The texture images may be drawn in opaque, blended, or additive modes. + * They can have an additional color tint or alpha modulation applied to + * them, and may also be stretched with linear interpolation. + * + * This API is designed to accelerate simple 2D operations. You may + * want more functionality such as rotation and particle effects and + * in that case you should use SDL's OpenGL/Direct3D support or one + * of the many good 3D engines. + */ + +#ifndef _SDL_render_h +#define _SDL_render_h + +#include "SDL_stdinc.h" +#include "SDL_rect.h" +#include "SDL_video.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +/** + * \brief Flags used when creating a rendering context + */ +typedef enum +{ + SDL_RENDERER_SOFTWARE = 0x00000001, /**< The renderer is a software fallback */ + SDL_RENDERER_ACCELERATED = 0x00000002, /**< The renderer uses hardware + acceleration */ + SDL_RENDERER_PRESENTVSYNC = 0x00000004 /**< Present is synchronized + with the refresh rate */ +} SDL_RendererFlags; + +/** + * \brief Information on the capabilities of a render driver or context. + */ +typedef struct SDL_RendererInfo +{ + const char *name; /**< The name of the renderer */ + Uint32 flags; /**< Supported ::SDL_RendererFlags */ + Uint32 num_texture_formats; /**< The number of available texture formats */ + Uint32 texture_formats[16]; /**< The available texture formats */ + int max_texture_width; /**< The maximimum texture width */ + int max_texture_height; /**< The maximimum texture height */ +} SDL_RendererInfo; + +/** + * \brief The access pattern allowed for a texture. + */ +typedef enum +{ + SDL_TEXTUREACCESS_STATIC, /**< Changes rarely, not lockable */ + SDL_TEXTUREACCESS_STREAMING /**< Changes frequently, lockable */ +} SDL_TextureAccess; + +/** + * \brief The texture channel modulation used in SDL_RenderCopy(). + */ +typedef enum +{ + SDL_TEXTUREMODULATE_NONE = 0x00000000, /**< No modulation */ + SDL_TEXTUREMODULATE_COLOR = 0x00000001, /**< srcC = srcC * color */ + SDL_TEXTUREMODULATE_ALPHA = 0x00000002 /**< srcA = srcA * alpha */ +} SDL_TextureModulate; + +/** + * \brief A structure representing rendering state + */ +struct SDL_Renderer; +typedef struct SDL_Renderer SDL_Renderer; + +/** + * \brief An efficient driver-specific representation of pixel data + */ +struct SDL_Texture; +typedef struct SDL_Texture SDL_Texture; + + +/* Function prototypes */ + +/** + * \brief Get the number of 2D rendering drivers available for the current + * display. + * + * A render driver is a set of code that handles rendering and texture + * management on a particular display. Normally there is only one, but + * some drivers may have several available with different capabilities. + * + * \sa SDL_GetRenderDriverInfo() + * \sa SDL_CreateRenderer() + */ +extern DECLSPEC int SDLCALL SDL_GetNumRenderDrivers(void); + +/** + * \brief Get information about a specific 2D rendering driver for the current + * display. + * + * \param index The index of the driver to query information about. + * \param info A pointer to an SDL_RendererInfo struct to be filled with + * information on the rendering driver. + * + * \return 0 on success, -1 if the index was out of range. + * + * \sa SDL_CreateRenderer() + */ +extern DECLSPEC int SDLCALL SDL_GetRenderDriverInfo(int index, + SDL_RendererInfo * info); + +/** + * \brief Create a 2D rendering context for a window. + * + * \param window The window where rendering is displayed. + * \param index The index of the rendering driver to initialize, or -1 to + * initialize the first one supporting the requested flags. + * \param flags ::SDL_RendererFlags. + * + * \return A valid rendering context or NULL if there was an error. + * + * \sa SDL_CreateSoftwareRenderer() + * \sa SDL_GetRendererInfo() + * \sa SDL_DestroyRenderer() + */ +extern DECLSPEC SDL_Renderer * SDLCALL SDL_CreateRenderer(SDL_Window * window, + int index, Uint32 flags); + +/** + * \brief Create a 2D software rendering context for a surface. + * + * \param surface The surface where rendering is done. + * + * \return A valid rendering context or NULL if there was an error. + * + * \sa SDL_CreateRenderer() + * \sa SDL_DestroyRenderer() + */ +extern DECLSPEC SDL_Renderer * SDLCALL SDL_CreateSoftwareRenderer(SDL_Surface * surface); + +/** + * \brief Get the renderer associated with a window. + */ +extern DECLSPEC SDL_Renderer * SDLCALL SDL_GetRenderer(SDL_Window * window); + +/** + * \brief Get information about a rendering context. + */ +extern DECLSPEC int SDLCALL SDL_GetRendererInfo(SDL_Renderer * renderer, + SDL_RendererInfo * info); + +/** + * \brief Create a texture for a rendering context. + * + * \param format The format of the texture. + * \param access One of the enumerated values in ::SDL_TextureAccess. + * \param w The width of the texture in pixels. + * \param h The height of the texture in pixels. + * + * \return The created texture is returned, or 0 if no rendering context was + * active, the format was unsupported, or the width or height were out + * of range. + * + * \sa SDL_QueryTexture() + * \sa SDL_UpdateTexture() + * \sa SDL_DestroyTexture() + */ +extern DECLSPEC SDL_Texture * SDLCALL SDL_CreateTexture(SDL_Renderer * renderer, + Uint32 format, + int access, int w, + int h); + +/** + * \brief Create a texture from an existing surface. + * + * \param surface The surface containing pixel data used to fill the texture. + * + * \return The created texture is returned, or 0 on error. + * + * \note The surface is not modified or freed by this function. + * + * \sa SDL_QueryTexture() + * \sa SDL_DestroyTexture() + */ +extern DECLSPEC SDL_Texture * SDLCALL SDL_CreateTextureFromSurface(SDL_Renderer * renderer, SDL_Surface * surface); + +/** + * \brief Query the attributes of a texture + * + * \param texture A texture to be queried. + * \param format A pointer filled in with the raw format of the texture. The + * actual format may differ, but pixel transfers will use this + * format. + * \param access A pointer filled in with the actual access to the texture. + * \param w A pointer filled in with the width of the texture in pixels. + * \param h A pointer filled in with the height of the texture in pixels. + * + * \return 0 on success, or -1 if the texture is not valid. + */ +extern DECLSPEC int SDLCALL SDL_QueryTexture(SDL_Texture * texture, + Uint32 * format, int *access, + int *w, int *h); + +/** + * \brief Set an additional color value used in render copy operations. + * + * \param texture The texture to update. + * \param r The red color value multiplied into copy operations. + * \param g The green color value multiplied into copy operations. + * \param b The blue color value multiplied into copy operations. + * + * \return 0 on success, or -1 if the texture is not valid or color modulation + * is not supported. + * + * \sa SDL_GetTextureColorMod() + */ +extern DECLSPEC int SDLCALL SDL_SetTextureColorMod(SDL_Texture * texture, + Uint8 r, Uint8 g, Uint8 b); + + +/** + * \brief Get the additional color value used in render copy operations. + * + * \param texture The texture to query. + * \param r A pointer filled in with the current red color value. + * \param g A pointer filled in with the current green color value. + * \param b A pointer filled in with the current blue color value. + * + * \return 0 on success, or -1 if the texture is not valid. + * + * \sa SDL_SetTextureColorMod() + */ +extern DECLSPEC int SDLCALL SDL_GetTextureColorMod(SDL_Texture * texture, + Uint8 * r, Uint8 * g, + Uint8 * b); + +/** + * \brief Set an additional alpha value used in render copy operations. + * + * \param texture The texture to update. + * \param alpha The alpha value multiplied into copy operations. + * + * \return 0 on success, or -1 if the texture is not valid or alpha modulation + * is not supported. + * + * \sa SDL_GetTextureAlphaMod() + */ +extern DECLSPEC int SDLCALL SDL_SetTextureAlphaMod(SDL_Texture * texture, + Uint8 alpha); + +/** + * \brief Get the additional alpha value used in render copy operations. + * + * \param texture The texture to query. + * \param alpha A pointer filled in with the current alpha value. + * + * \return 0 on success, or -1 if the texture is not valid. + * + * \sa SDL_SetTextureAlphaMod() + */ +extern DECLSPEC int SDLCALL SDL_GetTextureAlphaMod(SDL_Texture * texture, + Uint8 * alpha); + +/** + * \brief Set the blend mode used for texture copy operations. + * + * \param texture The texture to update. + * \param blendMode ::SDL_BlendMode to use for texture blending. + * + * \return 0 on success, or -1 if the texture is not valid or the blend mode is + * not supported. + * + * \note If the blend mode is not supported, the closest supported mode is + * chosen. + * + * \sa SDL_GetTextureBlendMode() + */ +extern DECLSPEC int SDLCALL SDL_SetTextureBlendMode(SDL_Texture * texture, + SDL_BlendMode blendMode); + +/** + * \brief Get the blend mode used for texture copy operations. + * + * \param texture The texture to query. + * \param blendMode A pointer filled in with the current blend mode. + * + * \return 0 on success, or -1 if the texture is not valid. + * + * \sa SDL_SetTextureBlendMode() + */ +extern DECLSPEC int SDLCALL SDL_GetTextureBlendMode(SDL_Texture * texture, + SDL_BlendMode *blendMode); + +/** + * \brief Update the given texture rectangle with new pixel data. + * + * \param texture The texture to update + * \param rect A pointer to the rectangle of pixels to update, or NULL to + * update the entire texture. + * \param pixels The raw pixel data. + * \param pitch The number of bytes between rows of pixel data. + * + * \return 0 on success, or -1 if the texture is not valid. + * + * \note This is a fairly slow function. + */ +extern DECLSPEC int SDLCALL SDL_UpdateTexture(SDL_Texture * texture, + const SDL_Rect * rect, + const void *pixels, int pitch); + +/** + * \brief Lock a portion of the texture for pixel access. + * + * \param texture The texture to lock for access, which was created with + * ::SDL_TEXTUREACCESS_STREAMING. + * \param rect A pointer to the rectangle to lock for access. If the rect + * is NULL, the entire texture will be locked. + * \param pixels This is filled in with a pointer to the locked pixels, + * appropriately offset by the locked area. + * \param pitch This is filled in with the pitch of the locked pixels. + * + * \return 0 on success, or -1 if the texture is not valid or was not created with ::SDL_TEXTUREACCESS_STREAMING. + * + * \sa SDL_UnlockTexture() + */ +extern DECLSPEC int SDLCALL SDL_LockTexture(SDL_Texture * texture, + const SDL_Rect * rect, + void **pixels, int *pitch); + +/** + * \brief Unlock a texture, uploading the changes to video memory, if needed. + * + * \sa SDL_LockTexture() + */ +extern DECLSPEC void SDLCALL SDL_UnlockTexture(SDL_Texture * texture); + +/** + * \brief Set the drawing area for rendering on the current target. + * + * \param rect The rectangle representing the drawing area, or NULL to set the viewport to the entire target. + * + * The x,y of the viewport rect represents the origin for rendering. + * + * \note When the window is resized, the current viewport is automatically + * centered within the new window size. + */ +extern DECLSPEC int SDLCALL SDL_RenderSetViewport(SDL_Renderer * renderer, + const SDL_Rect * rect); + +/** + * \brief Get the drawing area for the current target. + */ +extern DECLSPEC void SDLCALL SDL_RenderGetViewport(SDL_Renderer * renderer, + SDL_Rect * rect); + +/** + * \brief Set the color used for drawing operations (Fill and Line). + * + * \param r The red value used to draw on the rendering target. + * \param g The green value used to draw on the rendering target. + * \param b The blue value used to draw on the rendering target. + * \param a The alpha value used to draw on the rendering target, usually + * ::SDL_ALPHA_OPAQUE (255). + * + * \return 0 on success, or -1 on error + */ +extern DECLSPEC int SDL_SetRenderDrawColor(SDL_Renderer * renderer, + Uint8 r, Uint8 g, Uint8 b, + Uint8 a); + +/** + * \brief Get the color used for drawing operations (Fill and Line). + * + * \param r A pointer to the red value used to draw on the rendering target. + * \param g A pointer to the green value used to draw on the rendering target. + * \param b A pointer to the blue value used to draw on the rendering target. + * \param a A pointer to the alpha value used to draw on the rendering target, + * usually ::SDL_ALPHA_OPAQUE (255). + * + * \return 0 on success, or -1 on error + */ +extern DECLSPEC int SDL_GetRenderDrawColor(SDL_Renderer * renderer, + Uint8 * r, Uint8 * g, Uint8 * b, + Uint8 * a); + +/** + * \brief Set the blend mode used for drawing operations (Fill and Line). + * + * \param blendMode ::SDL_BlendMode to use for blending. + * + * \return 0 on success, or -1 on error + * + * \note If the blend mode is not supported, the closest supported mode is + * chosen. + * + * \sa SDL_GetRenderDrawBlendMode() + */ +extern DECLSPEC int SDLCALL SDL_SetRenderDrawBlendMode(SDL_Renderer * renderer, + SDL_BlendMode blendMode); + +/** + * \brief Get the blend mode used for drawing operations. + * + * \param blendMode A pointer filled in with the current blend mode. + * + * \return 0 on success, or -1 on error + * + * \sa SDL_SetRenderDrawBlendMode() + */ +extern DECLSPEC int SDLCALL SDL_GetRenderDrawBlendMode(SDL_Renderer * renderer, + SDL_BlendMode *blendMode); + +/** + * \brief Clear the current rendering target with the drawing color + * + * This function clears the entire rendering target, ignoring the viewport. + */ +extern DECLSPEC int SDLCALL SDL_RenderClear(SDL_Renderer * renderer); + +/** + * \brief Draw a point on the current rendering target. + * + * \param x The x coordinate of the point. + * \param y The y coordinate of the point. + * + * \return 0 on success, or -1 on error + */ +extern DECLSPEC int SDLCALL SDL_RenderDrawPoint(SDL_Renderer * renderer, + int x, int y); + +/** + * \brief Draw multiple points on the current rendering target. + * + * \param points The points to draw + * \param count The number of points to draw + * + * \return 0 on success, or -1 on error + */ +extern DECLSPEC int SDLCALL SDL_RenderDrawPoints(SDL_Renderer * renderer, + const SDL_Point * points, + int count); + +/** + * \brief Draw a line on the current rendering target. + * + * \param x1 The x coordinate of the start point. + * \param y1 The y coordinate of the start point. + * \param x2 The x coordinate of the end point. + * \param y2 The y coordinate of the end point. + * + * \return 0 on success, or -1 on error + */ +extern DECLSPEC int SDLCALL SDL_RenderDrawLine(SDL_Renderer * renderer, + int x1, int y1, int x2, int y2); + +/** + * \brief Draw a series of connected lines on the current rendering target. + * + * \param points The points along the lines + * \param count The number of points, drawing count-1 lines + * + * \return 0 on success, or -1 on error + */ +extern DECLSPEC int SDLCALL SDL_RenderDrawLines(SDL_Renderer * renderer, + const SDL_Point * points, + int count); + +/** + * \brief Draw a rectangle on the current rendering target. + * + * \param rect A pointer to the destination rectangle, or NULL to outline the entire rendering target. + * + * \return 0 on success, or -1 on error + */ +extern DECLSPEC int SDLCALL SDL_RenderDrawRect(SDL_Renderer * renderer, + const SDL_Rect * rect); + +/** + * \brief Draw some number of rectangles on the current rendering target. + * + * \param rects A pointer to an array of destination rectangles. + * \param count The number of rectangles. + * + * \return 0 on success, or -1 on error + */ +extern DECLSPEC int SDLCALL SDL_RenderDrawRects(SDL_Renderer * renderer, + const SDL_Rect * rects, + int count); + +/** + * \brief Fill a rectangle on the current rendering target with the drawing color. + * + * \param rect A pointer to the destination rectangle, or NULL for the entire + * rendering target. + * + * \return 0 on success, or -1 on error + */ +extern DECLSPEC int SDLCALL SDL_RenderFillRect(SDL_Renderer * renderer, + const SDL_Rect * rect); + +/** + * \brief Fill some number of rectangles on the current rendering target with the drawing color. + * + * \param rects A pointer to an array of destination rectangles. + * \param count The number of rectangles. + * + * \return 0 on success, or -1 on error + */ +extern DECLSPEC int SDLCALL SDL_RenderFillRects(SDL_Renderer * renderer, + const SDL_Rect * rects, + int count); + +/** + * \brief Copy a portion of the texture to the current rendering target. + * + * \param texture The source texture. + * \param srcrect A pointer to the source rectangle, or NULL for the entire + * texture. + * \param dstrect A pointer to the destination rectangle, or NULL for the + * entire rendering target. + * + * \return 0 on success, or -1 on error + */ +extern DECLSPEC int SDLCALL SDL_RenderCopy(SDL_Renderer * renderer, + SDL_Texture * texture, + const SDL_Rect * srcrect, + const SDL_Rect * dstrect); + +/** + * \brief Read pixels from the current rendering target. + * + * \param rect A pointer to the rectangle to read, or NULL for the entire + * render target. + * \param format The desired format of the pixel data, or 0 to use the format + * of the rendering target + * \param pixels A pointer to be filled in with the pixel data + * \param pitch The pitch of the pixels parameter. + * + * \return 0 on success, or -1 if pixel reading is not supported. + * + * \warning This is a very slow operation, and should not be used frequently. + */ +extern DECLSPEC int SDLCALL SDL_RenderReadPixels(SDL_Renderer * renderer, + const SDL_Rect * rect, + Uint32 format, + void *pixels, int pitch); + +/** + * \brief Update the screen with rendering performed. + */ +extern DECLSPEC void SDLCALL SDL_RenderPresent(SDL_Renderer * renderer); + +/** + * \brief Destroy the specified texture. + * + * \sa SDL_CreateTexture() + * \sa SDL_CreateTextureFromSurface() + */ +extern DECLSPEC void SDLCALL SDL_DestroyTexture(SDL_Texture * texture); + +/** + * \brief Destroy the rendering context for a window and free associated + * textures. + * + * \sa SDL_CreateRenderer() + */ +extern DECLSPEC void SDLCALL SDL_DestroyRenderer(SDL_Renderer * renderer); + + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif +#include "close_code.h" + +#endif /* _SDL_render_h */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_revision.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_revision.h new file mode 100644 index 0000000..1cf3f74 --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_revision.h @@ -0,0 +1,2 @@ +#define SDL_REVISION "hg-5538:b395a72329c3" +#define SDL_REVISION_NUMBER 5538 diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_rwops.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_rwops.h new file mode 100644 index 0000000..f2a8d4f --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_rwops.h @@ -0,0 +1,205 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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. +*/ + +/** + * \file SDL_rwops.h + * + * This file provides a general interface for SDL to read and write + * data streams. It can easily be extended to files, memory, etc. + */ + +#ifndef _SDL_rwops_h +#define _SDL_rwops_h + +#include "SDL_stdinc.h" +#include "SDL_error.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +/** + * This is the read/write operation structure -- very basic. + */ +typedef struct SDL_RWops +{ + /** + * Seek to \c offset relative to \c whence, one of stdio's whence values: + * RW_SEEK_SET, RW_SEEK_CUR, RW_SEEK_END + * + * \return the final offset in the data stream. + */ + long (SDLCALL * seek) (struct SDL_RWops * context, long offset, + int whence); + + /** + * Read up to \c maxnum objects each of size \c size from the data + * stream to the area pointed at by \c ptr. + * + * \return the number of objects read, or 0 at error or end of file. + */ + size_t(SDLCALL * read) (struct SDL_RWops * context, void *ptr, + size_t size, size_t maxnum); + + /** + * Write exactly \c num objects each of size \c size from the area + * pointed at by \c ptr to data stream. + * + * \return the number of objects written, or 0 at error or end of file. + */ + size_t(SDLCALL * write) (struct SDL_RWops * context, const void *ptr, + size_t size, size_t num); + + /** + * Close and free an allocated SDL_RWops structure. + * + * \return 0 if successful or -1 on write error when flushing data. + */ + int (SDLCALL * close) (struct SDL_RWops * context); + + Uint32 type; + union + { +#ifdef __WIN32__ + struct + { + SDL_bool append; + void *h; + struct + { + void *data; + size_t size; + size_t left; + } buffer; + } windowsio; +#endif +#ifdef HAVE_STDIO_H + struct + { + SDL_bool autoclose; + FILE *fp; + } stdio; +#endif + struct + { + Uint8 *base; + Uint8 *here; + Uint8 *stop; + } mem; + struct + { + void *data1; + } unknown; + } hidden; + +} SDL_RWops; + + +/** + * \name RWFrom functions + * + * Functions to create SDL_RWops structures from various data streams. + */ +/*@{*/ + +extern DECLSPEC SDL_RWops *SDLCALL SDL_RWFromFile(const char *file, + const char *mode); + +#ifdef HAVE_STDIO_H +extern DECLSPEC SDL_RWops *SDLCALL SDL_RWFromFP(FILE * fp, + SDL_bool autoclose); +#else +extern DECLSPEC SDL_RWops *SDLCALL SDL_RWFromFP(void * fp, + SDL_bool autoclose); +#endif + +extern DECLSPEC SDL_RWops *SDLCALL SDL_RWFromMem(void *mem, int size); +extern DECLSPEC SDL_RWops *SDLCALL SDL_RWFromConstMem(const void *mem, + int size); + +/*@}*//*RWFrom functions*/ + + +extern DECLSPEC SDL_RWops *SDLCALL SDL_AllocRW(void); +extern DECLSPEC void SDLCALL SDL_FreeRW(SDL_RWops * area); + +#define RW_SEEK_SET 0 /**< Seek from the beginning of data */ +#define RW_SEEK_CUR 1 /**< Seek relative to current read point */ +#define RW_SEEK_END 2 /**< Seek relative to the end of data */ + +/** + * \name Read/write macros + * + * Macros to easily read and write from an SDL_RWops structure. + */ +/*@{*/ +#define SDL_RWseek(ctx, offset, whence) (ctx)->seek(ctx, offset, whence) +#define SDL_RWtell(ctx) (ctx)->seek(ctx, 0, RW_SEEK_CUR) +#define SDL_RWread(ctx, ptr, size, n) (ctx)->read(ctx, ptr, size, n) +#define SDL_RWwrite(ctx, ptr, size, n) (ctx)->write(ctx, ptr, size, n) +#define SDL_RWclose(ctx) (ctx)->close(ctx) +/*@}*//*Read/write macros*/ + + +/** + * \name Read endian functions + * + * Read an item of the specified endianness and return in native format. + */ +/*@{*/ +extern DECLSPEC Uint16 SDLCALL SDL_ReadLE16(SDL_RWops * src); +extern DECLSPEC Uint16 SDLCALL SDL_ReadBE16(SDL_RWops * src); +extern DECLSPEC Uint32 SDLCALL SDL_ReadLE32(SDL_RWops * src); +extern DECLSPEC Uint32 SDLCALL SDL_ReadBE32(SDL_RWops * src); +extern DECLSPEC Uint64 SDLCALL SDL_ReadLE64(SDL_RWops * src); +extern DECLSPEC Uint64 SDLCALL SDL_ReadBE64(SDL_RWops * src); +/*@}*//*Read endian functions*/ + +/** + * \name Write endian functions + * + * Write an item of native format to the specified endianness. + */ +/*@{*/ +extern DECLSPEC size_t SDLCALL SDL_WriteLE16(SDL_RWops * dst, Uint16 value); +extern DECLSPEC size_t SDLCALL SDL_WriteBE16(SDL_RWops * dst, Uint16 value); +extern DECLSPEC size_t SDLCALL SDL_WriteLE32(SDL_RWops * dst, Uint32 value); +extern DECLSPEC size_t SDLCALL SDL_WriteBE32(SDL_RWops * dst, Uint32 value); +extern DECLSPEC size_t SDLCALL SDL_WriteLE64(SDL_RWops * dst, Uint64 value); +extern DECLSPEC size_t SDLCALL SDL_WriteBE64(SDL_RWops * dst, Uint64 value); +/*@}*//*Write endian functions*/ + + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif +#include "close_code.h" + +#endif /* _SDL_rwops_h */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_scancode.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_scancode.h new file mode 100644 index 0000000..a9aea66 --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_scancode.h @@ -0,0 +1,398 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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. +*/ + +/** + * \file SDL_scancode.h + * + * Defines keyboard scancodes. + */ + +#ifndef _SDL_scancode_h +#define _SDL_scancode_h + +#include "SDL_stdinc.h" + +/** + * \brief The SDL keyboard scancode representation. + * + * Values of this type are used to represent keyboard keys, among other places + * in the \link SDL_Keysym::scancode key.keysym.scancode \endlink field of the + * SDL_Event structure. + * + * The values in this enumeration are based on the USB usage page standard: + * http://www.usb.org/developers/devclass_docs/Hut1_12.pdf + */ +typedef enum +{ + SDL_SCANCODE_UNKNOWN = 0, + + /** + * \name Usage page 0x07 + * + * These values are from usage page 0x07 (USB keyboard page). + */ + /*@{*/ + + SDL_SCANCODE_A = 4, + SDL_SCANCODE_B = 5, + SDL_SCANCODE_C = 6, + SDL_SCANCODE_D = 7, + SDL_SCANCODE_E = 8, + SDL_SCANCODE_F = 9, + SDL_SCANCODE_G = 10, + SDL_SCANCODE_H = 11, + SDL_SCANCODE_I = 12, + SDL_SCANCODE_J = 13, + SDL_SCANCODE_K = 14, + SDL_SCANCODE_L = 15, + SDL_SCANCODE_M = 16, + SDL_SCANCODE_N = 17, + SDL_SCANCODE_O = 18, + SDL_SCANCODE_P = 19, + SDL_SCANCODE_Q = 20, + SDL_SCANCODE_R = 21, + SDL_SCANCODE_S = 22, + SDL_SCANCODE_T = 23, + SDL_SCANCODE_U = 24, + SDL_SCANCODE_V = 25, + SDL_SCANCODE_W = 26, + SDL_SCANCODE_X = 27, + SDL_SCANCODE_Y = 28, + SDL_SCANCODE_Z = 29, + + SDL_SCANCODE_1 = 30, + SDL_SCANCODE_2 = 31, + SDL_SCANCODE_3 = 32, + SDL_SCANCODE_4 = 33, + SDL_SCANCODE_5 = 34, + SDL_SCANCODE_6 = 35, + SDL_SCANCODE_7 = 36, + SDL_SCANCODE_8 = 37, + SDL_SCANCODE_9 = 38, + SDL_SCANCODE_0 = 39, + + SDL_SCANCODE_RETURN = 40, + SDL_SCANCODE_ESCAPE = 41, + SDL_SCANCODE_BACKSPACE = 42, + SDL_SCANCODE_TAB = 43, + SDL_SCANCODE_SPACE = 44, + + SDL_SCANCODE_MINUS = 45, + SDL_SCANCODE_EQUALS = 46, + SDL_SCANCODE_LEFTBRACKET = 47, + SDL_SCANCODE_RIGHTBRACKET = 48, + SDL_SCANCODE_BACKSLASH = 49, /**< Located at the lower left of the return + * key on ISO keyboards and at the right end + * of the QWERTY row on ANSI keyboards. + * Produces REVERSE SOLIDUS (backslash) and + * VERTICAL LINE in a US layout, REVERSE + * SOLIDUS and VERTICAL LINE in a UK Mac + * layout, NUMBER SIGN and TILDE in a UK + * Windows layout, DOLLAR SIGN and POUND SIGN + * in a Swiss German layout, NUMBER SIGN and + * APOSTROPHE in a German layout, GRAVE + * ACCENT and POUND SIGN in a French Mac + * layout, and ASTERISK and MICRO SIGN in a + * French Windows layout. + */ + SDL_SCANCODE_NONUSHASH = 50, /**< ISO USB keyboards actually use this code + * instead of 49 for the same key, but all + * OSes I've seen treat the two codes + * identically. So, as an implementor, unless + * your keyboard generates both of those + * codes and your OS treats them differently, + * you should generate SDL_SCANCODE_BACKSLASH + * instead of this code. As a user, you + * should not rely on this code because SDL + * will never generate it with most (all?) + * keyboards. + */ + SDL_SCANCODE_SEMICOLON = 51, + SDL_SCANCODE_APOSTROPHE = 52, + SDL_SCANCODE_GRAVE = 53, /**< Located in the top left corner (on both ANSI + * and ISO keyboards). Produces GRAVE ACCENT and + * TILDE in a US Windows layout and in US and UK + * Mac layouts on ANSI keyboards, GRAVE ACCENT + * and NOT SIGN in a UK Windows layout, SECTION + * SIGN and PLUS-MINUS SIGN in US and UK Mac + * layouts on ISO keyboards, SECTION SIGN and + * DEGREE SIGN in a Swiss German layout (Mac: + * only on ISO keyboards), CIRCUMFLEX ACCENT and + * DEGREE SIGN in a German layout (Mac: only on + * ISO keyboards), SUPERSCRIPT TWO and TILDE in a + * French Windows layout, COMMERCIAL AT and + * NUMBER SIGN in a French Mac layout on ISO + * keyboards, and LESS-THAN SIGN and GREATER-THAN + * SIGN in a Swiss German, German, or French Mac + * layout on ANSI keyboards. + */ + SDL_SCANCODE_COMMA = 54, + SDL_SCANCODE_PERIOD = 55, + SDL_SCANCODE_SLASH = 56, + + SDL_SCANCODE_CAPSLOCK = 57, + + SDL_SCANCODE_F1 = 58, + SDL_SCANCODE_F2 = 59, + SDL_SCANCODE_F3 = 60, + SDL_SCANCODE_F4 = 61, + SDL_SCANCODE_F5 = 62, + SDL_SCANCODE_F6 = 63, + SDL_SCANCODE_F7 = 64, + SDL_SCANCODE_F8 = 65, + SDL_SCANCODE_F9 = 66, + SDL_SCANCODE_F10 = 67, + SDL_SCANCODE_F11 = 68, + SDL_SCANCODE_F12 = 69, + + SDL_SCANCODE_PRINTSCREEN = 70, + SDL_SCANCODE_SCROLLLOCK = 71, + SDL_SCANCODE_PAUSE = 72, + SDL_SCANCODE_INSERT = 73, /**< insert on PC, help on some Mac keyboards (but + does send code 73, not 117) */ + SDL_SCANCODE_HOME = 74, + SDL_SCANCODE_PAGEUP = 75, + SDL_SCANCODE_DELETE = 76, + SDL_SCANCODE_END = 77, + SDL_SCANCODE_PAGEDOWN = 78, + SDL_SCANCODE_RIGHT = 79, + SDL_SCANCODE_LEFT = 80, + SDL_SCANCODE_DOWN = 81, + SDL_SCANCODE_UP = 82, + + SDL_SCANCODE_NUMLOCKCLEAR = 83, /**< num lock on PC, clear on Mac keyboards + */ + SDL_SCANCODE_KP_DIVIDE = 84, + SDL_SCANCODE_KP_MULTIPLY = 85, + SDL_SCANCODE_KP_MINUS = 86, + SDL_SCANCODE_KP_PLUS = 87, + SDL_SCANCODE_KP_ENTER = 88, + SDL_SCANCODE_KP_1 = 89, + SDL_SCANCODE_KP_2 = 90, + SDL_SCANCODE_KP_3 = 91, + SDL_SCANCODE_KP_4 = 92, + SDL_SCANCODE_KP_5 = 93, + SDL_SCANCODE_KP_6 = 94, + SDL_SCANCODE_KP_7 = 95, + SDL_SCANCODE_KP_8 = 96, + SDL_SCANCODE_KP_9 = 97, + SDL_SCANCODE_KP_0 = 98, + SDL_SCANCODE_KP_PERIOD = 99, + + SDL_SCANCODE_NONUSBACKSLASH = 100, /**< This is the additional key that ISO + * keyboards have over ANSI ones, + * located between left shift and Y. + * Produces GRAVE ACCENT and TILDE in a + * US or UK Mac layout, REVERSE SOLIDUS + * (backslash) and VERTICAL LINE in a + * US or UK Windows layout, and + * LESS-THAN SIGN and GREATER-THAN SIGN + * in a Swiss German, German, or French + * layout. */ + SDL_SCANCODE_APPLICATION = 101, /**< windows contextual menu, compose */ + SDL_SCANCODE_POWER = 102, /**< The USB document says this is a status flag, + * not a physical key - but some Mac keyboards + * do have a power key. */ + SDL_SCANCODE_KP_EQUALS = 103, + SDL_SCANCODE_F13 = 104, + SDL_SCANCODE_F14 = 105, + SDL_SCANCODE_F15 = 106, + SDL_SCANCODE_F16 = 107, + SDL_SCANCODE_F17 = 108, + SDL_SCANCODE_F18 = 109, + SDL_SCANCODE_F19 = 110, + SDL_SCANCODE_F20 = 111, + SDL_SCANCODE_F21 = 112, + SDL_SCANCODE_F22 = 113, + SDL_SCANCODE_F23 = 114, + SDL_SCANCODE_F24 = 115, + SDL_SCANCODE_EXECUTE = 116, + SDL_SCANCODE_HELP = 117, + SDL_SCANCODE_MENU = 118, + SDL_SCANCODE_SELECT = 119, + SDL_SCANCODE_STOP = 120, + SDL_SCANCODE_AGAIN = 121, /**< redo */ + SDL_SCANCODE_UNDO = 122, + SDL_SCANCODE_CUT = 123, + SDL_SCANCODE_COPY = 124, + SDL_SCANCODE_PASTE = 125, + SDL_SCANCODE_FIND = 126, + SDL_SCANCODE_MUTE = 127, + SDL_SCANCODE_VOLUMEUP = 128, + SDL_SCANCODE_VOLUMEDOWN = 129, +/* not sure whether there's a reason to enable these */ +/* SDL_SCANCODE_LOCKINGCAPSLOCK = 130, */ +/* SDL_SCANCODE_LOCKINGNUMLOCK = 131, */ +/* SDL_SCANCODE_LOCKINGSCROLLLOCK = 132, */ + SDL_SCANCODE_KP_COMMA = 133, + SDL_SCANCODE_KP_EQUALSAS400 = 134, + + SDL_SCANCODE_INTERNATIONAL1 = 135, /**< used on Asian keyboards, see + footnotes in USB doc */ + SDL_SCANCODE_INTERNATIONAL2 = 136, + SDL_SCANCODE_INTERNATIONAL3 = 137, /**< Yen */ + SDL_SCANCODE_INTERNATIONAL4 = 138, + SDL_SCANCODE_INTERNATIONAL5 = 139, + SDL_SCANCODE_INTERNATIONAL6 = 140, + SDL_SCANCODE_INTERNATIONAL7 = 141, + SDL_SCANCODE_INTERNATIONAL8 = 142, + SDL_SCANCODE_INTERNATIONAL9 = 143, + SDL_SCANCODE_LANG1 = 144, /**< Hangul/English toggle */ + SDL_SCANCODE_LANG2 = 145, /**< Hanja conversion */ + SDL_SCANCODE_LANG3 = 146, /**< Katakana */ + SDL_SCANCODE_LANG4 = 147, /**< Hiragana */ + SDL_SCANCODE_LANG5 = 148, /**< Zenkaku/Hankaku */ + SDL_SCANCODE_LANG6 = 149, /**< reserved */ + SDL_SCANCODE_LANG7 = 150, /**< reserved */ + SDL_SCANCODE_LANG8 = 151, /**< reserved */ + SDL_SCANCODE_LANG9 = 152, /**< reserved */ + + SDL_SCANCODE_ALTERASE = 153, /**< Erase-Eaze */ + SDL_SCANCODE_SYSREQ = 154, + SDL_SCANCODE_CANCEL = 155, + SDL_SCANCODE_CLEAR = 156, + SDL_SCANCODE_PRIOR = 157, + SDL_SCANCODE_RETURN2 = 158, + SDL_SCANCODE_SEPARATOR = 159, + SDL_SCANCODE_OUT = 160, + SDL_SCANCODE_OPER = 161, + SDL_SCANCODE_CLEARAGAIN = 162, + SDL_SCANCODE_CRSEL = 163, + SDL_SCANCODE_EXSEL = 164, + + SDL_SCANCODE_KP_00 = 176, + SDL_SCANCODE_KP_000 = 177, + SDL_SCANCODE_THOUSANDSSEPARATOR = 178, + SDL_SCANCODE_DECIMALSEPARATOR = 179, + SDL_SCANCODE_CURRENCYUNIT = 180, + SDL_SCANCODE_CURRENCYSUBUNIT = 181, + SDL_SCANCODE_KP_LEFTPAREN = 182, + SDL_SCANCODE_KP_RIGHTPAREN = 183, + SDL_SCANCODE_KP_LEFTBRACE = 184, + SDL_SCANCODE_KP_RIGHTBRACE = 185, + SDL_SCANCODE_KP_TAB = 186, + SDL_SCANCODE_KP_BACKSPACE = 187, + SDL_SCANCODE_KP_A = 188, + SDL_SCANCODE_KP_B = 189, + SDL_SCANCODE_KP_C = 190, + SDL_SCANCODE_KP_D = 191, + SDL_SCANCODE_KP_E = 192, + SDL_SCANCODE_KP_F = 193, + SDL_SCANCODE_KP_XOR = 194, + SDL_SCANCODE_KP_POWER = 195, + SDL_SCANCODE_KP_PERCENT = 196, + SDL_SCANCODE_KP_LESS = 197, + SDL_SCANCODE_KP_GREATER = 198, + SDL_SCANCODE_KP_AMPERSAND = 199, + SDL_SCANCODE_KP_DBLAMPERSAND = 200, + SDL_SCANCODE_KP_VERTICALBAR = 201, + SDL_SCANCODE_KP_DBLVERTICALBAR = 202, + SDL_SCANCODE_KP_COLON = 203, + SDL_SCANCODE_KP_HASH = 204, + SDL_SCANCODE_KP_SPACE = 205, + SDL_SCANCODE_KP_AT = 206, + SDL_SCANCODE_KP_EXCLAM = 207, + SDL_SCANCODE_KP_MEMSTORE = 208, + SDL_SCANCODE_KP_MEMRECALL = 209, + SDL_SCANCODE_KP_MEMCLEAR = 210, + SDL_SCANCODE_KP_MEMADD = 211, + SDL_SCANCODE_KP_MEMSUBTRACT = 212, + SDL_SCANCODE_KP_MEMMULTIPLY = 213, + SDL_SCANCODE_KP_MEMDIVIDE = 214, + SDL_SCANCODE_KP_PLUSMINUS = 215, + SDL_SCANCODE_KP_CLEAR = 216, + SDL_SCANCODE_KP_CLEARENTRY = 217, + SDL_SCANCODE_KP_BINARY = 218, + SDL_SCANCODE_KP_OCTAL = 219, + SDL_SCANCODE_KP_DECIMAL = 220, + SDL_SCANCODE_KP_HEXADECIMAL = 221, + + SDL_SCANCODE_LCTRL = 224, + SDL_SCANCODE_LSHIFT = 225, + SDL_SCANCODE_LALT = 226, /**< alt, option */ + SDL_SCANCODE_LGUI = 227, /**< windows, command (apple), meta */ + SDL_SCANCODE_RCTRL = 228, + SDL_SCANCODE_RSHIFT = 229, + SDL_SCANCODE_RALT = 230, /**< alt gr, option */ + SDL_SCANCODE_RGUI = 231, /**< windows, command (apple), meta */ + + SDL_SCANCODE_MODE = 257, /**< I'm not sure if this is really not covered + * by any of the above, but since there's a + * special KMOD_MODE for it I'm adding it here + */ + + /*@}*//*Usage page 0x07*/ + + /** + * \name Usage page 0x0C + * + * These values are mapped from usage page 0x0C (USB consumer page). + */ + /*@{*/ + + SDL_SCANCODE_AUDIONEXT = 258, + SDL_SCANCODE_AUDIOPREV = 259, + SDL_SCANCODE_AUDIOSTOP = 260, + SDL_SCANCODE_AUDIOPLAY = 261, + SDL_SCANCODE_AUDIOMUTE = 262, + SDL_SCANCODE_MEDIASELECT = 263, + SDL_SCANCODE_WWW = 264, + SDL_SCANCODE_MAIL = 265, + SDL_SCANCODE_CALCULATOR = 266, + SDL_SCANCODE_COMPUTER = 267, + SDL_SCANCODE_AC_SEARCH = 268, + SDL_SCANCODE_AC_HOME = 269, + SDL_SCANCODE_AC_BACK = 270, + SDL_SCANCODE_AC_FORWARD = 271, + SDL_SCANCODE_AC_STOP = 272, + SDL_SCANCODE_AC_REFRESH = 273, + SDL_SCANCODE_AC_BOOKMARKS = 274, + + /*@}*//*Usage page 0x0C*/ + + /** + * \name Walther keys + * + * These are values that Christian Walther added (for mac keyboard?). + */ + /*@{*/ + + SDL_SCANCODE_BRIGHTNESSDOWN = 275, + SDL_SCANCODE_BRIGHTNESSUP = 276, + SDL_SCANCODE_DISPLAYSWITCH = 277, /**< display mirroring/dual display + switch, video mode switch */ + SDL_SCANCODE_KBDILLUMTOGGLE = 278, + SDL_SCANCODE_KBDILLUMDOWN = 279, + SDL_SCANCODE_KBDILLUMUP = 280, + SDL_SCANCODE_EJECT = 281, + SDL_SCANCODE_SLEEP = 282, + + /*@}*//*Walther keys*/ + + /* Add any other keys here. */ + + SDL_NUM_SCANCODES = 512 /**< not a key, just marks the number of scancodes + for array bounds */ +} SDL_Scancode; + +#endif /* _SDL_scancode_h */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_shape.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_shape.h new file mode 100644 index 0000000..fc56afa --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_shape.h @@ -0,0 +1,147 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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 _SDL_shape_h +#define _SDL_shape_h + +#include "SDL_stdinc.h" +#include "SDL_pixels.h" +#include "SDL_rect.h" +#include "SDL_surface.h" +#include "SDL_video.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +/** \file SDL_shape.h + * + * Header file for the shaped window API. + */ + +#define SDL_NONSHAPEABLE_WINDOW -1 +#define SDL_INVALID_SHAPE_ARGUMENT -2 +#define SDL_WINDOW_LACKS_SHAPE -3 + +/** + * \brief Create a window that can be shaped with the specified position, dimensions, and flags. + * + * \param title The title of the window, in UTF-8 encoding. + * \param x The x position of the window, ::SDL_WINDOWPOS_CENTERED, or + * ::SDL_WINDOWPOS_UNDEFINED. + * \param y The y position of the window, ::SDL_WINDOWPOS_CENTERED, or + * ::SDL_WINDOWPOS_UNDEFINED. + * \param w The width of the window. + * \param h The height of the window. + * \param flags The flags for the window, a mask of SDL_WINDOW_BORDERLESS with any of the following: + * ::SDL_WINDOW_OPENGL, ::SDL_WINDOW_INPUT_GRABBED, + * ::SDL_WINDOW_SHOWN, ::SDL_WINDOW_RESIZABLE, + * ::SDL_WINDOW_MAXIMIZED, ::SDL_WINDOW_MINIMIZED, + * ::SDL_WINDOW_BORDERLESS is always set, and ::SDL_WINDOW_FULLSCREEN is always unset. + * + * \return The window created, or NULL if window creation failed. + * + * \sa SDL_DestroyWindow() + */ +extern DECLSPEC SDL_Window * SDLCALL SDL_CreateShapedWindow(const char *title,unsigned int x,unsigned int y,unsigned int w,unsigned int h,Uint32 flags); + +/** + * \brief Return whether the given window is a shaped window. + * + * \param window The window to query for being shaped. + * + * \return SDL_TRUE if the window is a window that can be shaped, SDL_FALSE if the window is unshaped or NULL. + * \sa SDL_CreateShapedWindow + */ +extern DECLSPEC SDL_bool SDLCALL SDL_IsShapedWindow(const SDL_Window *window); + +/** \brief An enum denoting the specific type of contents present in an SDL_WindowShapeParams union. */ +typedef enum { + /** \brief The default mode, a binarized alpha cutoff of 1. */ + ShapeModeDefault, + /** \brief A binarized alpha cutoff with a given integer value. */ + ShapeModeBinarizeAlpha, + /** \brief A binarized alpha cutoff with a given integer value, but with the opposite comparison. */ + ShapeModeReverseBinarizeAlpha, + /** \brief A color key is applied. */ + ShapeModeColorKey +} WindowShapeMode; + +#define SDL_SHAPEMODEALPHA(mode) (mode == ShapeModeDefault || mode == ShapeModeBinarizeAlpha || mode == ShapeModeReverseBinarizeAlpha) + +/** \brief A union containing parameters for shaped windows. */ +typedef union { + /** \brief a cutoff alpha value for binarization of the window shape's alpha channel. */ + Uint8 binarizationCutoff; + SDL_Color colorKey; +} SDL_WindowShapeParams; + +/** \brief A struct that tags the SDL_WindowShapeParams union with an enum describing the type of its contents. */ +typedef struct SDL_WindowShapeMode { + /** \brief The mode of these window-shape parameters. */ + WindowShapeMode mode; + /** \brief Window-shape parameters. */ + SDL_WindowShapeParams parameters; +} SDL_WindowShapeMode; + +/** + * \brief Set the shape and parameters of a shaped window. + * + * \param window The shaped window whose parameters should be set. + * \param shape A surface encoding the desired shape for the window. + * \param shape_mode The parameters to set for the shaped window. + * + * \return 0 on success, SDL_INVALID_SHAPE_ARGUMENT on invalid an invalid shape argument, or SDL_NONSHAPEABLE_WINDOW + * if the SDL_Window* given does not reference a valid shaped window. + * + * \sa SDL_WindowShapeMode + * \sa SDL_GetShapedWindowMode. + */ +extern DECLSPEC int SDLCALL SDL_SetWindowShape(SDL_Window *window,SDL_Surface *shape,SDL_WindowShapeMode *shape_mode); + +/** + * \brief Get the shape parameters of a shaped window. + * + * \param window The shaped window whose parameters should be retrieved. + * \param shape_mode An empty shape-mode structure to fill, or NULL to check whether the window has a shape. + * + * \return 0 if the window has a shape and, provided shape_mode was not NULL, shape_mode has been filled with the mode + * data, SDL_NONSHAPEABLE_WINDOW if the SDL_Window given is not a shaped window, or SDL_WINDOW_LACKS_SHAPE if + * the SDL_Window* given is a shapeable window currently lacking a shape. + * + * \sa SDL_WindowShapeMode + * \sa SDL_SetWindowShape + */ +extern DECLSPEC int SDLCALL SDL_GetShapedWindowMode(SDL_Window *window,SDL_WindowShapeMode *shape_mode); + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif +#include "close_code.h" + +#endif /* _SDL_shape_h */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_stdinc.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_stdinc.h new file mode 100644 index 0000000..89cbc3c --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_stdinc.h @@ -0,0 +1,764 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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. +*/ + +/** + * \file SDL_stdinc.h + * + * This is a general header that includes C language support. + */ + +#ifndef _SDL_stdinc_h +#define _SDL_stdinc_h + +#include "SDL_config.h" + + +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_STDIO_H +#include +#endif +#if defined(STDC_HEADERS) +# include +# include +# include +#else +# if defined(HAVE_STDLIB_H) +# include +# elif defined(HAVE_MALLOC_H) +# include +# endif +# if defined(HAVE_STDDEF_H) +# include +# endif +# if defined(HAVE_STDARG_H) +# include +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined(STDC_HEADERS) && defined(HAVE_MEMORY_H) +# include +# endif +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#if defined(HAVE_INTTYPES_H) +# include +#elif defined(HAVE_STDINT_H) +# include +#endif +#ifdef HAVE_CTYPE_H +# include +#endif +#ifdef HAVE_MATH_H +# include +#endif +#if defined(HAVE_ICONV) && defined(HAVE_ICONV_H) +# include +#endif + +/** + * The number of elements in an array. + */ +#define SDL_arraysize(array) (sizeof(array)/sizeof(array[0])) +#define SDL_TABLESIZE(table) SDL_arraysize(table) + +/** + * \name Cast operators + * + * Use proper C++ casts when compiled as C++ to be compatible with the option + * -Wold-style-cast of GCC (and -Werror=old-style-cast in GCC 4.2 and above). + */ +/*@{*/ +#ifdef __cplusplus +#define SDL_reinterpret_cast(type, expression) reinterpret_cast(expression) +#define SDL_static_cast(type, expression) static_cast(expression) +#else +#define SDL_reinterpret_cast(type, expression) ((type)(expression)) +#define SDL_static_cast(type, expression) ((type)(expression)) +#endif +/*@}*//*Cast operators*/ + +/* Define a four character code as a Uint32 */ +#define SDL_FOURCC(A, B, C, D) \ + ((SDL_static_cast(Uint32, SDL_static_cast(Uint8, (A))) << 0) | \ + (SDL_static_cast(Uint32, SDL_static_cast(Uint8, (B))) << 8) | \ + (SDL_static_cast(Uint32, SDL_static_cast(Uint8, (C))) << 16) | \ + (SDL_static_cast(Uint32, SDL_static_cast(Uint8, (D))) << 24)) + +/** + * \name Basic data types + */ +/*@{*/ + +typedef enum +{ + SDL_FALSE = 0, + SDL_TRUE = 1 +} SDL_bool; + +/** + * \brief A signed 8-bit integer type. + */ +typedef int8_t Sint8; +/** + * \brief An unsigned 8-bit integer type. + */ +typedef uint8_t Uint8; +/** + * \brief A signed 16-bit integer type. + */ +typedef int16_t Sint16; +/** + * \brief An unsigned 16-bit integer type. + */ +typedef uint16_t Uint16; +/** + * \brief A signed 32-bit integer type. + */ +typedef int32_t Sint32; +/** + * \brief An unsigned 32-bit integer type. + */ +typedef uint32_t Uint32; + +/** + * \brief A signed 64-bit integer type. + */ +typedef int64_t Sint64; +/** + * \brief An unsigned 64-bit integer type. + */ +typedef uint64_t Uint64; + +/*@}*//*Basic data types*/ + + +#define SDL_COMPILE_TIME_ASSERT(name, x) \ + typedef int SDL_dummy_ ## name[(x) * 2 - 1] +/** \cond */ +#ifndef DOXYGEN_SHOULD_IGNORE_THIS +SDL_COMPILE_TIME_ASSERT(uint8, sizeof(Uint8) == 1); +SDL_COMPILE_TIME_ASSERT(sint8, sizeof(Sint8) == 1); +SDL_COMPILE_TIME_ASSERT(uint16, sizeof(Uint16) == 2); +SDL_COMPILE_TIME_ASSERT(sint16, sizeof(Sint16) == 2); +SDL_COMPILE_TIME_ASSERT(uint32, sizeof(Uint32) == 4); +SDL_COMPILE_TIME_ASSERT(sint32, sizeof(Sint32) == 4); +SDL_COMPILE_TIME_ASSERT(uint64, sizeof(Uint64) == 8); +SDL_COMPILE_TIME_ASSERT(sint64, sizeof(Sint64) == 8); +#endif /* DOXYGEN_SHOULD_IGNORE_THIS */ +/** \endcond */ + +/* Check to make sure enums are the size of ints, for structure packing. + For both Watcom C/C++ and Borland C/C++ the compiler option that makes + enums having the size of an int must be enabled. + This is "-b" for Borland C/C++ and "-ei" for Watcom C/C++ (v11). +*/ +/* Enable enums always int in CodeWarrior (for MPW use "-enum int") */ +#ifdef __MWERKS__ +#pragma enumsalwaysint on +#endif + +/** \cond */ +#ifndef DOXYGEN_SHOULD_IGNORE_THIS +#if !defined(__NINTENDODS__) && !defined(__ANDROID__) + /* TODO: include/SDL_stdinc.h:174: error: size of array 'SDL_dummy_enum' is negative */ +typedef enum +{ + DUMMY_ENUM_VALUE +} SDL_DUMMY_ENUM; + +SDL_COMPILE_TIME_ASSERT(enum, sizeof(SDL_DUMMY_ENUM) == sizeof(int)); +#endif +#endif /* DOXYGEN_SHOULD_IGNORE_THIS */ +/** \endcond */ + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +#ifdef HAVE_MALLOC +#define SDL_malloc malloc +#else +extern DECLSPEC void *SDLCALL SDL_malloc(size_t size); +#endif + +#ifdef HAVE_CALLOC +#define SDL_calloc calloc +#else +extern DECLSPEC void *SDLCALL SDL_calloc(size_t nmemb, size_t size); +#endif + +#ifdef HAVE_REALLOC +#define SDL_realloc realloc +#else +extern DECLSPEC void *SDLCALL SDL_realloc(void *mem, size_t size); +#endif + +#ifdef HAVE_FREE +#define SDL_free free +#else +extern DECLSPEC void SDLCALL SDL_free(void *mem); +#endif + +#if defined(HAVE_ALLOCA) && !defined(alloca) +# if defined(HAVE_ALLOCA_H) +# include +# elif defined(__GNUC__) +# define alloca __builtin_alloca +# elif defined(_MSC_VER) +# include +# define alloca _alloca +# elif defined(__WATCOMC__) +# include +# elif defined(__BORLANDC__) +# include +# elif defined(__DMC__) +# include +# elif defined(__AIX__) +#pragma alloca +# elif defined(__MRC__) +void *alloca(unsigned); +# else +char *alloca(); +# endif +#endif +#ifdef HAVE_ALLOCA +#define SDL_stack_alloc(type, count) (type*)alloca(sizeof(type)*(count)) +#define SDL_stack_free(data) +#else +#define SDL_stack_alloc(type, count) (type*)SDL_malloc(sizeof(type)*(count)) +#define SDL_stack_free(data) SDL_free(data) +#endif + +#ifdef HAVE_GETENV +#define SDL_getenv getenv +#else +extern DECLSPEC char *SDLCALL SDL_getenv(const char *name); +#endif + +/* SDL_putenv() has moved to SDL_compat. */ +#ifdef HAVE_SETENV +#define SDL_setenv setenv +#else +extern DECLSPEC int SDLCALL SDL_setenv(const char *name, const char *value, + int overwrite); +#endif + +#ifdef HAVE_QSORT +#define SDL_qsort qsort +#else +extern DECLSPEC void SDLCALL SDL_qsort(void *base, size_t nmemb, size_t size, + int (*compare) (const void *, + const void *)); +#endif + +#ifdef HAVE_ABS +#define SDL_abs abs +#else +#define SDL_abs(X) ((X) < 0 ? -(X) : (X)) +#endif + +#define SDL_min(x, y) (((x) < (y)) ? (x) : (y)) +#define SDL_max(x, y) (((x) > (y)) ? (x) : (y)) + +#ifdef HAVE_CTYPE_H +#define SDL_isdigit(X) isdigit(X) +#define SDL_isspace(X) isspace(X) +#define SDL_toupper(X) toupper(X) +#define SDL_tolower(X) tolower(X) +#else +#define SDL_isdigit(X) (((X) >= '0') && ((X) <= '9')) +#define SDL_isspace(X) (((X) == ' ') || ((X) == '\t') || ((X) == '\r') || ((X) == '\n')) +#define SDL_toupper(X) (((X) >= 'a') && ((X) <= 'z') ? ('A'+((X)-'a')) : (X)) +#define SDL_tolower(X) (((X) >= 'A') && ((X) <= 'Z') ? ('a'+((X)-'A')) : (X)) +#endif + +#ifdef HAVE_MEMSET +#define SDL_memset memset +#else +extern DECLSPEC void *SDLCALL SDL_memset(void *dst, int c, size_t len); +#endif +#define SDL_zero(x) SDL_memset(&(x), 0, sizeof((x))) +#define SDL_zerop(x) SDL_memset((x), 0, sizeof(*(x))) + +#if defined(__GNUC__) && defined(i386) +#define SDL_memset4(dst, val, len) \ +do { \ + int u0, u1, u2; \ + __asm__ __volatile__ ( \ + "cld\n\t" \ + "rep ; stosl\n\t" \ + : "=&D" (u0), "=&a" (u1), "=&c" (u2) \ + : "0" (dst), "1" (val), "2" (SDL_static_cast(Uint32, len)) \ + : "memory" ); \ +} while(0) +#endif +#ifndef SDL_memset4 +#define SDL_memset4(dst, val, len) \ +do { \ + unsigned _count = (len); \ + unsigned _n = (_count + 3) / 4; \ + Uint32 *_p = SDL_static_cast(Uint32 *, dst); \ + Uint32 _val = (val); \ + if (len == 0) break; \ + switch (_count % 4) { \ + case 0: do { *_p++ = _val; \ + case 3: *_p++ = _val; \ + case 2: *_p++ = _val; \ + case 1: *_p++ = _val; \ + } while ( --_n ); \ + } \ +} while(0) +#endif + +/* We can count on memcpy existing on Mac OS X and being well-tuned. */ +#if defined(__MACOSX__) +#define SDL_memcpy memcpy +#elif defined(__GNUC__) && defined(i386) +#define SDL_memcpy(dst, src, len) \ +do { \ + int u0, u1, u2; \ + __asm__ __volatile__ ( \ + "cld\n\t" \ + "rep ; movsl\n\t" \ + "testb $2,%b4\n\t" \ + "je 1f\n\t" \ + "movsw\n" \ + "1:\ttestb $1,%b4\n\t" \ + "je 2f\n\t" \ + "movsb\n" \ + "2:" \ + : "=&c" (u0), "=&D" (u1), "=&S" (u2) \ + : "0" (SDL_static_cast(unsigned, len)/4), "q" (len), "1" (dst),"2" (src) \ + : "memory" ); \ +} while(0) +#endif +#ifndef SDL_memcpy +#ifdef HAVE_MEMCPY +#define SDL_memcpy memcpy +#elif defined(HAVE_BCOPY) +#define SDL_memcpy(d, s, n) bcopy((s), (d), (n)) +#else +extern DECLSPEC void *SDLCALL SDL_memcpy(void *dst, const void *src, + size_t len); +#endif +#endif + +/* We can count on memcpy existing on Mac OS X and being well-tuned. */ +#if defined(__MACOSX__) +#define SDL_memcpy4(dst, src, len) SDL_memcpy((dst), (src), (len) << 2) +#elif defined(__GNUC__) && defined(i386) +#define SDL_memcpy4(dst, src, len) \ +do { \ + int ecx, edi, esi; \ + __asm__ __volatile__ ( \ + "cld\n\t" \ + "rep ; movsl" \ + : "=&c" (ecx), "=&D" (edi), "=&S" (esi) \ + : "0" (SDL_static_cast(unsigned, len)), "1" (dst), "2" (src) \ + : "memory" ); \ +} while(0) +#endif +#ifndef SDL_memcpy4 +#define SDL_memcpy4(dst, src, len) SDL_memcpy((dst), (src), (len) << 2) +#endif + +#ifdef HAVE_MEMMOVE +#define SDL_memmove memmove +#else +extern DECLSPEC void *SDLCALL SDL_memmove(void *dst, const void *src, + size_t len); +#endif + +#ifdef HAVE_MEMCMP +#define SDL_memcmp memcmp +#else +extern DECLSPEC int SDLCALL SDL_memcmp(const void *s1, const void *s2, + size_t len); +#endif + +#ifdef HAVE_STRLEN +#define SDL_strlen strlen +#else +extern DECLSPEC size_t SDLCALL SDL_strlen(const char *string); +#endif + +#ifdef HAVE_WCSLEN +#define SDL_wcslen wcslen +#else +#if !defined(wchar_t) && defined(__NINTENDODS__) +#define wchar_t short /* TODO: figure out why libnds doesn't have this */ +#endif +extern DECLSPEC size_t SDLCALL SDL_wcslen(const wchar_t * string); +#endif + +#ifdef HAVE_WCSLCPY +#define SDL_wcslcpy wcslcpy +#else +extern DECLSPEC size_t SDLCALL SDL_wcslcpy(wchar_t *dst, const wchar_t *src, size_t maxlen); +#endif + +#ifdef HAVE_WCSLCAT +#define SDL_wcslcat wcslcat +#else +extern DECLSPEC size_t SDLCALL SDL_wcslcat(wchar_t *dst, const wchar_t *src, size_t maxlen); +#endif + + +#ifdef HAVE_STRLCPY +#define SDL_strlcpy strlcpy +#else +extern DECLSPEC size_t SDLCALL SDL_strlcpy(char *dst, const char *src, + size_t maxlen); +#endif + +extern DECLSPEC size_t SDLCALL SDL_utf8strlcpy(char *dst, const char *src, + size_t dst_bytes); + +#ifdef HAVE_STRLCAT +#define SDL_strlcat strlcat +#else +extern DECLSPEC size_t SDLCALL SDL_strlcat(char *dst, const char *src, + size_t maxlen); +#endif + +#ifdef HAVE_STRDUP +#define SDL_strdup strdup +#else +extern DECLSPEC char *SDLCALL SDL_strdup(const char *string); +#endif + +#ifdef HAVE__STRREV +#define SDL_strrev _strrev +#else +extern DECLSPEC char *SDLCALL SDL_strrev(char *string); +#endif + +#ifdef HAVE__STRUPR +#define SDL_strupr _strupr +#else +extern DECLSPEC char *SDLCALL SDL_strupr(char *string); +#endif + +#ifdef HAVE__STRLWR +#define SDL_strlwr _strlwr +#else +extern DECLSPEC char *SDLCALL SDL_strlwr(char *string); +#endif + +#ifdef HAVE_STRCHR +#define SDL_strchr strchr +#elif defined(HAVE_INDEX) +#define SDL_strchr index +#else +extern DECLSPEC char *SDLCALL SDL_strchr(const char *string, int c); +#endif + +#ifdef HAVE_STRRCHR +#define SDL_strrchr strrchr +#elif defined(HAVE_RINDEX) +#define SDL_strrchr rindex +#else +extern DECLSPEC char *SDLCALL SDL_strrchr(const char *string, int c); +#endif + +#ifdef HAVE_STRSTR +#define SDL_strstr strstr +#else +extern DECLSPEC char *SDLCALL SDL_strstr(const char *haystack, + const char *needle); +#endif + +#ifdef HAVE_ITOA +#define SDL_itoa itoa +#else +#define SDL_itoa(value, string, radix) SDL_ltoa((long)value, string, radix) +#endif + +#ifdef HAVE__LTOA +#define SDL_ltoa _ltoa +#else +extern DECLSPEC char *SDLCALL SDL_ltoa(long value, char *string, int radix); +#endif + +#ifdef HAVE__UITOA +#define SDL_uitoa _uitoa +#else +#define SDL_uitoa(value, string, radix) SDL_ultoa((long)value, string, radix) +#endif + +#ifdef HAVE__ULTOA +#define SDL_ultoa _ultoa +#else +extern DECLSPEC char *SDLCALL SDL_ultoa(unsigned long value, char *string, + int radix); +#endif + +#ifdef HAVE_STRTOL +#define SDL_strtol strtol +#else +extern DECLSPEC long SDLCALL SDL_strtol(const char *string, char **endp, + int base); +#endif + +#ifdef HAVE_STRTOUL +#define SDL_strtoul strtoul +#else +extern DECLSPEC unsigned long SDLCALL SDL_strtoul(const char *string, + char **endp, int base); +#endif + +#ifdef HAVE__I64TOA +#define SDL_lltoa _i64toa +#else +extern DECLSPEC char *SDLCALL SDL_lltoa(Sint64 value, char *string, + int radix); +#endif + +#ifdef HAVE__UI64TOA +#define SDL_ulltoa _ui64toa +#else +extern DECLSPEC char *SDLCALL SDL_ulltoa(Uint64 value, char *string, + int radix); +#endif + +#ifdef HAVE_STRTOLL +#define SDL_strtoll strtoll +#else +extern DECLSPEC Sint64 SDLCALL SDL_strtoll(const char *string, char **endp, + int base); +#endif + +#ifdef HAVE_STRTOULL +#define SDL_strtoull strtoull +#else +extern DECLSPEC Uint64 SDLCALL SDL_strtoull(const char *string, char **endp, + int base); +#endif + +#ifdef HAVE_STRTOD +#define SDL_strtod strtod +#else +extern DECLSPEC double SDLCALL SDL_strtod(const char *string, char **endp); +#endif + +#ifdef HAVE_ATOI +#define SDL_atoi atoi +#else +#define SDL_atoi(X) SDL_strtol(X, NULL, 0) +#endif + +#ifdef HAVE_ATOF +#define SDL_atof atof +#else +#define SDL_atof(X) SDL_strtod(X, NULL) +#endif + +#ifdef HAVE_STRCMP +#define SDL_strcmp strcmp +#else +extern DECLSPEC int SDLCALL SDL_strcmp(const char *str1, const char *str2); +#endif + +#ifdef HAVE_STRNCMP +#define SDL_strncmp strncmp +#else +extern DECLSPEC int SDLCALL SDL_strncmp(const char *str1, const char *str2, + size_t maxlen); +#endif + +#ifdef HAVE_STRCASECMP +#define SDL_strcasecmp strcasecmp +#elif defined(HAVE__STRICMP) +#define SDL_strcasecmp _stricmp +#else +extern DECLSPEC int SDLCALL SDL_strcasecmp(const char *str1, + const char *str2); +#endif + +#ifdef HAVE_STRNCASECMP +#define SDL_strncasecmp strncasecmp +#elif defined(HAVE__STRNICMP) +#define SDL_strncasecmp _strnicmp +#else +extern DECLSPEC int SDLCALL SDL_strncasecmp(const char *str1, + const char *str2, size_t maxlen); +#endif + +#ifdef HAVE_SSCANF +#define SDL_sscanf sscanf +#else +extern DECLSPEC int SDLCALL SDL_sscanf(const char *text, const char *fmt, + ...); +#endif + +#ifdef HAVE_SNPRINTF +#define SDL_snprintf snprintf +#else +extern DECLSPEC int SDLCALL SDL_snprintf(char *text, size_t maxlen, + const char *fmt, ...); +#endif + +#ifdef HAVE_VSNPRINTF +#define SDL_vsnprintf vsnprintf +#else +extern DECLSPEC int SDLCALL SDL_vsnprintf(char *text, size_t maxlen, + const char *fmt, va_list ap); +#endif + +#ifndef HAVE_M_PI +#define M_PI 3.14159265358979323846264338327950288 /* pi */ +#endif + +#ifdef HAVE_ATAN +#define SDL_atan atan +#else +extern DECLSPEC double SDLCALL SDL_atan(double x); +#endif + +#ifdef HAVE_ATAN2 +#define SDL_atan2 atan2 +#else +extern DECLSPEC double SDLCALL SDL_atan2(double y, double x); +#endif + +#ifdef HAVE_CEIL +#define SDL_ceil ceil +#else +#define SDL_ceil(x) ((double)(int)((x)+0.5)) +#endif + +#ifdef HAVE_COPYSIGN +#define SDL_copysign copysign +#else +extern DECLSPEC double SDLCALL SDL_copysign(double x, double y); +#endif + +#ifdef HAVE_COS +#define SDL_cos cos +#else +extern DECLSPEC double SDLCALL SDL_cos(double x); +#endif + +#ifdef HAVE_COSF +#define SDL_cosf cosf +#else +#define SDL_cosf(x) (float)SDL_cos((double)x) +#endif + +#ifdef HAVE_FABS +#define SDL_fabs fabs +#else +extern DECLSPEC double SDLCALL SDL_fabs(double x); +#endif + +#ifdef HAVE_FLOOR +#define SDL_floor floor +#else +extern DECLSPEC double SDLCALL SDL_floor(double x); +#endif + +#ifdef HAVE_LOG +#define SDL_log log +#else +extern DECLSPEC double SDLCALL SDL_log(double x); +#endif + +#ifdef HAVE_POW +#define SDL_pow pow +#else +extern DECLSPEC double SDLCALL SDL_pow(double x, double y); +#endif + +#ifdef HAVE_SCALBN +#define SDL_scalbn scalbn +#else +extern DECLSPEC double SDLCALL SDL_scalbn(double x, int n); +#endif + +#ifdef HAVE_SIN +#define SDL_sin sin +#else +extern DECLSPEC double SDLCALL SDL_sin(double x); +#endif + +#ifdef HAVE_SINF +#define SDL_sinf sinf +#else +#define SDL_sinf(x) (float)SDL_sin((double)x) +#endif + +#ifdef HAVE_SQRT +#define SDL_sqrt sqrt +#else +extern DECLSPEC double SDLCALL SDL_sqrt(double x); +#endif + +/* The SDL implementation of iconv() returns these error codes */ +#define SDL_ICONV_ERROR (size_t)-1 +#define SDL_ICONV_E2BIG (size_t)-2 +#define SDL_ICONV_EILSEQ (size_t)-3 +#define SDL_ICONV_EINVAL (size_t)-4 + +#if defined(HAVE_ICONV) && defined(HAVE_ICONV_H) +#define SDL_iconv_t iconv_t +#define SDL_iconv_open iconv_open +#define SDL_iconv_close iconv_close +#else +typedef struct _SDL_iconv_t *SDL_iconv_t; +extern DECLSPEC SDL_iconv_t SDLCALL SDL_iconv_open(const char *tocode, + const char *fromcode); +extern DECLSPEC int SDLCALL SDL_iconv_close(SDL_iconv_t cd); +#endif +extern DECLSPEC size_t SDLCALL SDL_iconv(SDL_iconv_t cd, const char **inbuf, + size_t * inbytesleft, char **outbuf, + size_t * outbytesleft); +/** + * This function converts a string between encodings in one pass, returning a + * string that must be freed with SDL_free() or NULL on error. + */ +extern DECLSPEC char *SDLCALL SDL_iconv_string(const char *tocode, + const char *fromcode, + const char *inbuf, + size_t inbytesleft); +#define SDL_iconv_utf8_locale(S) SDL_iconv_string("", "UTF-8", S, SDL_strlen(S)+1) +#define SDL_iconv_utf8_ucs2(S) (Uint16 *)SDL_iconv_string("UCS-2", "UTF-8", S, SDL_strlen(S)+1) +#define SDL_iconv_utf8_ucs4(S) (Uint32 *)SDL_iconv_string("UCS-4", "UTF-8", S, SDL_strlen(S)+1) + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif +#include "close_code.h" + +#endif /* _SDL_stdinc_h */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_surface.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_surface.h new file mode 100644 index 0000000..77b5825 --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_surface.h @@ -0,0 +1,497 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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. +*/ + +/** + * \file SDL_surface.h + * + * Header file for ::SDL_surface definition and management functions. + */ + +#ifndef _SDL_surface_h +#define _SDL_surface_h + +#include "SDL_stdinc.h" +#include "SDL_pixels.h" +#include "SDL_rect.h" +#include "SDL_blendmode.h" +#include "SDL_rwops.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +/** + * \name Surface flags + * + * These are the currently supported flags for the ::SDL_surface. + * + * \internal + * Used internally (read-only). + */ +/*@{*/ +#define SDL_PREALLOC 0x00000001 /**< Surface uses preallocated memory */ +#define SDL_RLEACCEL 0x00000002 /**< Surface is RLE encoded */ +#define SDL_DONTFREE 0x00000004 /**< Surface is referenced internally */ +/*@}*//*Surface flags*/ + +/** + * Evaluates to true if the surface needs to be locked before access. + */ +#define SDL_MUSTLOCK(S) (((S)->flags & SDL_RLEACCEL) != 0) + +/** + * \brief A collection of pixels used in software blitting. + * + * \note This structure should be treated as read-only, except for \c pixels, + * which, if not NULL, contains the raw pixel data for the surface. + */ +typedef struct SDL_Surface +{ + Uint32 flags; /**< Read-only */ + SDL_PixelFormat *format; /**< Read-only */ + int w, h; /**< Read-only */ + int pitch; /**< Read-only */ + void *pixels; /**< Read-write */ + + /** Application data associated with the surface */ + void *userdata; /**< Read-write */ + + /** information needed for surfaces requiring locks */ + int locked; /**< Read-only */ + void *lock_data; /**< Read-only */ + + /** clipping information */ + SDL_Rect clip_rect; /**< Read-only */ + + /** info for fast blit mapping to other surfaces */ + struct SDL_BlitMap *map; /**< Private */ + + /** Reference count -- used when freeing surface */ + int refcount; /**< Read-mostly */ +} SDL_Surface; + +/** + * \brief The type of function used for surface blitting functions. + */ +typedef int (*SDL_blit) (struct SDL_Surface * src, SDL_Rect * srcrect, + struct SDL_Surface * dst, SDL_Rect * dstrect); + +/** + * Allocate and free an RGB surface. + * + * If the depth is 4 or 8 bits, an empty palette is allocated for the surface. + * If the depth is greater than 8 bits, the pixel format is set using the + * flags '[RGB]mask'. + * + * If the function runs out of memory, it will return NULL. + * + * \param flags The \c flags are obsolete and should be set to 0. + */ +extern DECLSPEC SDL_Surface *SDLCALL SDL_CreateRGBSurface + (Uint32 flags, int width, int height, int depth, + Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask); +extern DECLSPEC SDL_Surface *SDLCALL SDL_CreateRGBSurfaceFrom(void *pixels, + int width, + int height, + int depth, + int pitch, + Uint32 Rmask, + Uint32 Gmask, + Uint32 Bmask, + Uint32 Amask); +extern DECLSPEC void SDLCALL SDL_FreeSurface(SDL_Surface * surface); + +/** + * \brief Set the palette used by a surface. + * + * \return 0, or -1 if the surface format doesn't use a palette. + * + * \note A single palette can be shared with many surfaces. + */ +extern DECLSPEC int SDLCALL SDL_SetSurfacePalette(SDL_Surface * surface, + SDL_Palette * palette); + +/** + * \brief Sets up a surface for directly accessing the pixels. + * + * Between calls to SDL_LockSurface() / SDL_UnlockSurface(), you can write + * to and read from \c surface->pixels, using the pixel format stored in + * \c surface->format. Once you are done accessing the surface, you should + * use SDL_UnlockSurface() to release it. + * + * Not all surfaces require locking. If SDL_MUSTLOCK(surface) evaluates + * to 0, then you can read and write to the surface at any time, and the + * pixel format of the surface will not change. + * + * No operating system or library calls should be made between lock/unlock + * pairs, as critical system locks may be held during this time. + * + * SDL_LockSurface() returns 0, or -1 if the surface couldn't be locked. + * + * \sa SDL_UnlockSurface() + */ +extern DECLSPEC int SDLCALL SDL_LockSurface(SDL_Surface * surface); +/** \sa SDL_LockSurface() */ +extern DECLSPEC void SDLCALL SDL_UnlockSurface(SDL_Surface * surface); + +/** + * Load a surface from a seekable SDL data stream (memory or file). + * + * If \c freesrc is non-zero, the stream will be closed after being read. + * + * The new surface should be freed with SDL_FreeSurface(). + * + * \return the new surface, or NULL if there was an error. + */ +extern DECLSPEC SDL_Surface *SDLCALL SDL_LoadBMP_RW(SDL_RWops * src, + int freesrc); + +/** + * Load a surface from a file. + * + * Convenience macro. + */ +#define SDL_LoadBMP(file) SDL_LoadBMP_RW(SDL_RWFromFile(file, "rb"), 1) + +/** + * Save a surface to a seekable SDL data stream (memory or file). + * + * If \c freedst is non-zero, the stream will be closed after being written. + * + * \return 0 if successful or -1 if there was an error. + */ +extern DECLSPEC int SDLCALL SDL_SaveBMP_RW + (SDL_Surface * surface, SDL_RWops * dst, int freedst); + +/** + * Save a surface to a file. + * + * Convenience macro. + */ +#define SDL_SaveBMP(surface, file) \ + SDL_SaveBMP_RW(surface, SDL_RWFromFile(file, "wb"), 1) + +/** + * \brief Sets the RLE acceleration hint for a surface. + * + * \return 0 on success, or -1 if the surface is not valid + * + * \note If RLE is enabled, colorkey and alpha blending blits are much faster, + * but the surface must be locked before directly accessing the pixels. + */ +extern DECLSPEC int SDLCALL SDL_SetSurfaceRLE(SDL_Surface * surface, + int flag); + +/** + * \brief Sets the color key (transparent pixel) in a blittable surface. + * + * \param surface The surface to update + * \param flag Non-zero to enable colorkey and 0 to disable colorkey + * \param key The transparent pixel in the native surface format + * + * \return 0 on success, or -1 if the surface is not valid + */ +extern DECLSPEC int SDLCALL SDL_SetColorKey(SDL_Surface * surface, + int flag, Uint32 key); + +/** + * \brief Gets the color key (transparent pixel) in a blittable surface. + * + * \param surface The surface to update + * \param key A pointer filled in with the transparent pixel in the native + * surface format + * + * \return 0 on success, or -1 if the surface is not valid or colorkey is not + * enabled. + */ +extern DECLSPEC int SDLCALL SDL_GetColorKey(SDL_Surface * surface, + Uint32 * key); + +/** + * \brief Set an additional color value used in blit operations. + * + * \param surface The surface to update. + * \param r The red color value multiplied into blit operations. + * \param g The green color value multiplied into blit operations. + * \param b The blue color value multiplied into blit operations. + * + * \return 0 on success, or -1 if the surface is not valid. + * + * \sa SDL_GetSurfaceColorMod() + */ +extern DECLSPEC int SDLCALL SDL_SetSurfaceColorMod(SDL_Surface * surface, + Uint8 r, Uint8 g, Uint8 b); + + +/** + * \brief Get the additional color value used in blit operations. + * + * \param surface The surface to query. + * \param r A pointer filled in with the current red color value. + * \param g A pointer filled in with the current green color value. + * \param b A pointer filled in with the current blue color value. + * + * \return 0 on success, or -1 if the surface is not valid. + * + * \sa SDL_SetSurfaceColorMod() + */ +extern DECLSPEC int SDLCALL SDL_GetSurfaceColorMod(SDL_Surface * surface, + Uint8 * r, Uint8 * g, + Uint8 * b); + +/** + * \brief Set an additional alpha value used in blit operations. + * + * \param surface The surface to update. + * \param alpha The alpha value multiplied into blit operations. + * + * \return 0 on success, or -1 if the surface is not valid. + * + * \sa SDL_GetSurfaceAlphaMod() + */ +extern DECLSPEC int SDLCALL SDL_SetSurfaceAlphaMod(SDL_Surface * surface, + Uint8 alpha); + +/** + * \brief Get the additional alpha value used in blit operations. + * + * \param surface The surface to query. + * \param alpha A pointer filled in with the current alpha value. + * + * \return 0 on success, or -1 if the surface is not valid. + * + * \sa SDL_SetSurfaceAlphaMod() + */ +extern DECLSPEC int SDLCALL SDL_GetSurfaceAlphaMod(SDL_Surface * surface, + Uint8 * alpha); + +/** + * \brief Set the blend mode used for blit operations. + * + * \param surface The surface to update. + * \param blendMode ::SDL_BlendMode to use for blit blending. + * + * \return 0 on success, or -1 if the parameters are not valid. + * + * \sa SDL_GetSurfaceBlendMode() + */ +extern DECLSPEC int SDLCALL SDL_SetSurfaceBlendMode(SDL_Surface * surface, + SDL_BlendMode blendMode); + +/** + * \brief Get the blend mode used for blit operations. + * + * \param surface The surface to query. + * \param blendMode A pointer filled in with the current blend mode. + * + * \return 0 on success, or -1 if the surface is not valid. + * + * \sa SDL_SetSurfaceBlendMode() + */ +extern DECLSPEC int SDLCALL SDL_GetSurfaceBlendMode(SDL_Surface * surface, + SDL_BlendMode *blendMode); + +/** + * Sets the clipping rectangle for the destination surface in a blit. + * + * If the clip rectangle is NULL, clipping will be disabled. + * + * If the clip rectangle doesn't intersect the surface, the function will + * return SDL_FALSE and blits will be completely clipped. Otherwise the + * function returns SDL_TRUE and blits to the surface will be clipped to + * the intersection of the surface area and the clipping rectangle. + * + * Note that blits are automatically clipped to the edges of the source + * and destination surfaces. + */ +extern DECLSPEC SDL_bool SDLCALL SDL_SetClipRect(SDL_Surface * surface, + const SDL_Rect * rect); + +/** + * Gets the clipping rectangle for the destination surface in a blit. + * + * \c rect must be a pointer to a valid rectangle which will be filled + * with the correct values. + */ +extern DECLSPEC void SDLCALL SDL_GetClipRect(SDL_Surface * surface, + SDL_Rect * rect); + +/** + * Creates a new surface of the specified format, and then copies and maps + * the given surface to it so the blit of the converted surface will be as + * fast as possible. If this function fails, it returns NULL. + * + * The \c flags parameter is passed to SDL_CreateRGBSurface() and has those + * semantics. You can also pass ::SDL_RLEACCEL in the flags parameter and + * SDL will try to RLE accelerate colorkey and alpha blits in the resulting + * surface. + */ +extern DECLSPEC SDL_Surface *SDLCALL SDL_ConvertSurface + (SDL_Surface * src, SDL_PixelFormat * fmt, Uint32 flags); +extern DECLSPEC SDL_Surface *SDLCALL SDL_ConvertSurfaceFormat + (SDL_Surface * src, Uint32 pixel_format, Uint32 flags); + +/** + * \brief Copy a block of pixels of one format to another format + */ +extern DECLSPEC int SDLCALL SDL_ConvertPixels(int width, int height, + Uint32 src_format, + const void * src, int src_pitch, + Uint32 dst_format, + void * dst, int dst_pitch); + +/** + * Performs a fast fill of the given rectangle with \c color. + * + * If \c rect is NULL, the whole surface will be filled with \c color. + * + * The color should be a pixel of the format used by the surface, and + * can be generated by the SDL_MapRGB() function. + * + * \return 0 on success, or -1 on error. + */ +extern DECLSPEC int SDLCALL SDL_FillRect + (SDL_Surface * dst, const SDL_Rect * rect, Uint32 color); +extern DECLSPEC int SDLCALL SDL_FillRects + (SDL_Surface * dst, const SDL_Rect * rects, int count, Uint32 color); + +/** + * Performs a fast blit from the source surface to the destination surface. + * + * This assumes that the source and destination rectangles are + * the same size. If either \c srcrect or \c dstrect are NULL, the entire + * surface (\c src or \c dst) is copied. The final blit rectangles are saved + * in \c srcrect and \c dstrect after all clipping is performed. + * + * \return If the blit is successful, it returns 0, otherwise it returns -1. + * + * The blit function should not be called on a locked surface. + * + * The blit semantics for surfaces with and without alpha and colorkey + * are defined as follows: + * \verbatim + RGBA->RGB: + SDL_SRCALPHA set: + alpha-blend (using alpha-channel). + SDL_SRCCOLORKEY ignored. + SDL_SRCALPHA not set: + copy RGB. + if SDL_SRCCOLORKEY set, only copy the pixels matching the + RGB values of the source colour key, ignoring alpha in the + comparison. + + RGB->RGBA: + SDL_SRCALPHA set: + alpha-blend (using the source per-surface alpha value); + set destination alpha to opaque. + SDL_SRCALPHA not set: + copy RGB, set destination alpha to source per-surface alpha value. + both: + if SDL_SRCCOLORKEY set, only copy the pixels matching the + source colour key. + + RGBA->RGBA: + SDL_SRCALPHA set: + alpha-blend (using the source alpha channel) the RGB values; + leave destination alpha untouched. [Note: is this correct?] + SDL_SRCCOLORKEY ignored. + SDL_SRCALPHA not set: + copy all of RGBA to the destination. + if SDL_SRCCOLORKEY set, only copy the pixels matching the + RGB values of the source colour key, ignoring alpha in the + comparison. + + RGB->RGB: + SDL_SRCALPHA set: + alpha-blend (using the source per-surface alpha value). + SDL_SRCALPHA not set: + copy RGB. + both: + if SDL_SRCCOLORKEY set, only copy the pixels matching the + source colour key. + \endverbatim + * + * You should call SDL_BlitSurface() unless you know exactly how SDL + * blitting works internally and how to use the other blit functions. + */ +#define SDL_BlitSurface SDL_UpperBlit + +/** + * This is the public blit function, SDL_BlitSurface(), and it performs + * rectangle validation and clipping before passing it to SDL_LowerBlit() + */ +extern DECLSPEC int SDLCALL SDL_UpperBlit + (SDL_Surface * src, const SDL_Rect * srcrect, + SDL_Surface * dst, SDL_Rect * dstrect); + +/** + * This is a semi-private blit function and it performs low-level surface + * blitting only. + */ +extern DECLSPEC int SDLCALL SDL_LowerBlit + (SDL_Surface * src, SDL_Rect * srcrect, + SDL_Surface * dst, SDL_Rect * dstrect); + +/** + * \brief Perform a fast, low quality, stretch blit between two surfaces of the + * same pixel format. + * + * \note This function uses a static buffer, and is not thread-safe. + */ +extern DECLSPEC int SDLCALL SDL_SoftStretch(SDL_Surface * src, + const SDL_Rect * srcrect, + SDL_Surface * dst, + const SDL_Rect * dstrect); + +#define SDL_BlitScaled SDL_UpperBlitScaled + +/** + * This is the public scaled blit function, SDL_BlitScaled(), and it performs + * rectangle validation and clipping before passing it to SDL_LowerBlitScaled() + */ +extern DECLSPEC int SDLCALL SDL_UpperBlitScaled + (SDL_Surface * src, const SDL_Rect * srcrect, + SDL_Surface * dst, SDL_Rect * dstrect); + +/** + * This is a semi-private blit function and it performs low-level surface + * scaled blitting only. + */ +extern DECLSPEC int SDLCALL SDL_LowerBlitScaled + (SDL_Surface * src, SDL_Rect * srcrect, + SDL_Surface * dst, SDL_Rect * dstrect); + + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif +#include "close_code.h" + +#endif /* _SDL_surface_h */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_syswm.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_syswm.h new file mode 100644 index 0000000..677e398 --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_syswm.h @@ -0,0 +1,241 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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. +*/ + +/** + * \file SDL_syswm.h + * + * Include file for SDL custom system window manager hooks. + */ + +#ifndef _SDL_syswm_h +#define _SDL_syswm_h + +#include "SDL_stdinc.h" +#include "SDL_error.h" +#include "SDL_video.h" +#include "SDL_version.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +/** + * \file SDL_syswm.h + * + * Your application has access to a special type of event ::SDL_SYSWMEVENT, + * which contains window-manager specific information and arrives whenever + * an unhandled window event occurs. This event is ignored by default, but + * you can enable it with SDL_EventState(). + */ +#ifdef SDL_PROTOTYPES_ONLY +struct SDL_SysWMinfo; +#else + +#if defined(SDL_VIDEO_DRIVER_WINDOWS) +#define WIN32_LEAN_AND_MEAN +#include +#endif + +/* This is the structure for custom window manager events */ +#if defined(SDL_VIDEO_DRIVER_X11) +#if defined(__APPLE__) && defined(__MACH__) +/* conflicts with Quickdraw.h */ +#define Cursor X11Cursor +#endif + +#include +#include + +#if defined(__APPLE__) && defined(__MACH__) +/* matches the re-define above */ +#undef Cursor +#endif + +#endif /* defined(SDL_VIDEO_DRIVER_X11) */ + +#if defined(SDL_VIDEO_DRIVER_DIRECTFB) +#include +#endif + +#if defined(SDL_VIDEO_DRIVER_COCOA) +#ifdef __OBJC__ +#include +#else +typedef struct _NSWindow NSWindow; +#endif +#endif + +#if defined(SDL_VIDEO_DRIVER_UIKIT) +#ifdef __OBJC__ +#include +#else +typedef struct _UIWindow UIWindow; +#endif +#endif + +/** + * These are the various supported windowing subsystems + */ +typedef enum +{ + SDL_SYSWM_UNKNOWN, + SDL_SYSWM_WINDOWS, + SDL_SYSWM_X11, + SDL_SYSWM_DIRECTFB, + SDL_SYSWM_COCOA, + SDL_SYSWM_UIKIT, +} SDL_SYSWM_TYPE; + +/** + * The custom event structure. + */ +struct SDL_SysWMmsg +{ + SDL_version version; + SDL_SYSWM_TYPE subsystem; + union + { +#if defined(SDL_VIDEO_DRIVER_WINDOWS) + struct { + HWND hwnd; /**< The window for the message */ + UINT msg; /**< The type of message */ + WPARAM wParam; /**< WORD message parameter */ + LPARAM lParam; /**< LONG message parameter */ + } win; +#endif +#if defined(SDL_VIDEO_DRIVER_X11) + struct { + XEvent event; + } x11; +#endif +#if defined(SDL_VIDEO_DRIVER_DIRECTFB) + struct { + DFBEvent event; + } dfb; +#endif +#if defined(SDL_VIDEO_DRIVER_COCOA) + struct + { + /* No Cocoa window events yet */ + } cocoa; +#endif +#if defined(SDL_VIDEO_DRIVER_UIKIT) + struct + { + /* No UIKit window events yet */ + } uikit; +#endif + /* Can't have an empty union */ + int dummy; + } msg; +}; + +/** + * The custom window manager information structure. + * + * When this structure is returned, it holds information about which + * low level system it is using, and will be one of SDL_SYSWM_TYPE. + */ +struct SDL_SysWMinfo +{ + SDL_version version; + SDL_SYSWM_TYPE subsystem; + union + { +#if defined(SDL_VIDEO_DRIVER_WINDOWS) + struct + { + HWND window; /**< The window handle */ + } win; +#endif +#if defined(SDL_VIDEO_DRIVER_X11) + struct + { + Display *display; /**< The X11 display */ + Window window; /**< The X11 window */ + } x11; +#endif +#if defined(SDL_VIDEO_DRIVER_DIRECTFB) + struct + { + IDirectFB *dfb; /**< The directfb main interface */ + IDirectFBWindow *window; /**< The directfb window handle */ + IDirectFBSurface *surface; /**< The directfb client surface */ + } dfb; +#endif +#if defined(SDL_VIDEO_DRIVER_COCOA) + struct + { + NSWindow *window; /* The Cocoa window */ + } cocoa; +#endif +#if defined(SDL_VIDEO_DRIVER_UIKIT) + struct + { + UIWindow *window; /* The UIKit window */ + } uikit; +#endif + /* Can't have an empty union */ + int dummy; + } info; +}; + +#endif /* SDL_PROTOTYPES_ONLY */ + +typedef struct SDL_SysWMinfo SDL_SysWMinfo; + +/* Function prototypes */ +/** + * \brief This function allows access to driver-dependent window information. + * + * \param window The window about which information is being requested + * \param info This structure must be initialized with the SDL version, and is + * then filled in with information about the given window. + * + * \return SDL_TRUE if the function is implemented and the version member of + * the \c info struct is valid, SDL_FALSE otherwise. + * + * You typically use this function like this: + * \code + * SDL_SysWMinfo info; + * SDL_VERSION(&info.version); + * if ( SDL_GetWindowWMInfo(&info) ) { ... } + * \endcode + */ +extern DECLSPEC SDL_bool SDLCALL SDL_GetWindowWMInfo(SDL_Window * window, + SDL_SysWMinfo * info); + + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif +#include "close_code.h" + +#endif /* _SDL_syswm_h */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_thread.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_thread.h new file mode 100644 index 0000000..c771a39 --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_thread.h @@ -0,0 +1,182 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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 _SDL_thread_h +#define _SDL_thread_h + +/** + * \file SDL_thread.h + * + * Header for the SDL thread management routines. + */ + +#include "SDL_stdinc.h" +#include "SDL_error.h" + +/* Thread synchronization primitives */ +#include "SDL_mutex.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +/* The SDL thread structure, defined in SDL_thread.c */ +struct SDL_Thread; +typedef struct SDL_Thread SDL_Thread; + +/* The SDL thread ID */ +typedef unsigned long SDL_threadID; + +/* The SDL thread priority + * + * Note: On many systems you require special privileges to set high priority. + */ +typedef enum { + SDL_THREAD_PRIORITY_LOW, + SDL_THREAD_PRIORITY_NORMAL, + SDL_THREAD_PRIORITY_HIGH +} SDL_ThreadPriority; + +/* The function passed to SDL_CreateThread() + It is passed a void* user context parameter and returns an int. + */ +typedef int (SDLCALL * SDL_ThreadFunction) (void *data); + +#if defined(__WIN32__) && !defined(HAVE_LIBC) +/** + * \file SDL_thread.h + * + * We compile SDL into a DLL. This means, that it's the DLL which + * creates a new thread for the calling process with the SDL_CreateThread() + * API. There is a problem with this, that only the RTL of the SDL.DLL will + * be initialized for those threads, and not the RTL of the calling + * application! + * + * To solve this, we make a little hack here. + * + * We'll always use the caller's _beginthread() and _endthread() APIs to + * start a new thread. This way, if it's the SDL.DLL which uses this API, + * then the RTL of SDL.DLL will be used to create the new thread, and if it's + * the application, then the RTL of the application will be used. + * + * So, in short: + * Always use the _beginthread() and _endthread() of the calling runtime + * library! + */ +#define SDL_PASSED_BEGINTHREAD_ENDTHREAD +#ifndef _WIN32_WCE +#include /* This has _beginthread() and _endthread() defined! */ +#endif + +#ifdef __GNUC__ +typedef unsigned long (__cdecl * pfnSDL_CurrentBeginThread) (void *, unsigned, + unsigned + (__stdcall * + func) (void *), + void *arg, + unsigned, + unsigned + *threadID); +typedef void (__cdecl * pfnSDL_CurrentEndThread) (unsigned code); +#else +typedef uintptr_t(__cdecl * pfnSDL_CurrentBeginThread) (void *, unsigned, + unsigned (__stdcall * + func) (void + *), + void *arg, unsigned, + unsigned *threadID); +typedef void (__cdecl * pfnSDL_CurrentEndThread) (unsigned code); +#endif + +/** + * Create a thread. + */ +extern DECLSPEC SDL_Thread *SDLCALL +SDL_CreateThread(SDL_ThreadFunction fn, void *data, + pfnSDL_CurrentBeginThread pfnBeginThread, + pfnSDL_CurrentEndThread pfnEndThread); + +#if defined(_WIN32_WCE) + +/** + * Create a thread. + */ +#define SDL_CreateThread(fn, data) SDL_CreateThread(fn, data, NULL, NULL) + +#else + +/** + * Create a thread. + */ +#define SDL_CreateThread(fn, data) SDL_CreateThread(fn, data, _beginthreadex, _endthreadex) + +#endif +#else + +/** + * Create a thread. + */ +extern DECLSPEC SDL_Thread *SDLCALL +SDL_CreateThread(SDL_ThreadFunction fn, void *data); + +#endif + +/** + * Get the thread identifier for the current thread. + */ +extern DECLSPEC SDL_threadID SDLCALL SDL_ThreadID(void); + +/** + * Get the thread identifier for the specified thread. + * + * Equivalent to SDL_ThreadID() if the specified thread is NULL. + */ +extern DECLSPEC SDL_threadID SDLCALL SDL_GetThreadID(SDL_Thread * thread); + +/** + * Set the priority for the current thread + */ +extern DECLSPEC int SDLCALL SDL_SetThreadPriority(SDL_ThreadPriority priority); + +/** + * Wait for a thread to finish. + * + * The return code for the thread function is placed in the area + * pointed to by \c status, if \c status is not NULL. + */ +extern DECLSPEC void SDLCALL SDL_WaitThread(SDL_Thread * thread, int *status); + + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif +#include "close_code.h" + +#endif /* _SDL_thread_h */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_timer.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_timer.h new file mode 100644 index 0000000..bb1abae --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_timer.h @@ -0,0 +1,108 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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 _SDL_timer_h +#define _SDL_timer_h + +/** + * \file SDL_timer.h + * + * Header for the SDL time management routines. + */ + +#include "SDL_stdinc.h" +#include "SDL_error.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +/** + * \brief Get the number of milliseconds since the SDL library initialization. + * + * \note This value wraps if the program runs for more than ~49 days. + */ +extern DECLSPEC Uint32 SDLCALL SDL_GetTicks(void); + +/** + * \brief Get the current value of the high resolution counter + */ +extern DECLSPEC Uint64 SDLCALL SDL_GetPerformanceCounter(void); + +/** + * \brief Get the count per second of the high resolution counter + */ +extern DECLSPEC Uint64 SDLCALL SDL_GetPerformanceFrequency(void); + +/** + * \brief Wait a specified number of milliseconds before returning. + */ +extern DECLSPEC void SDLCALL SDL_Delay(Uint32 ms); + +/** + * Function prototype for the timer callback function. + * + * The callback function is passed the current timer interval and returns + * the next timer interval. If the returned value is the same as the one + * passed in, the periodic alarm continues, otherwise a new alarm is + * scheduled. If the callback returns 0, the periodic alarm is cancelled. + */ +typedef Uint32 (SDLCALL * SDL_TimerCallback) (Uint32 interval, void *param); + +/** + * Definition of the timer ID type. + */ +typedef int SDL_TimerID; + +/** + * \brief Add a new timer to the pool of timers already running. + * + * \return A timer ID, or NULL when an error occurs. + */ +extern DECLSPEC SDL_TimerID SDLCALL SDL_AddTimer(Uint32 interval, + SDL_TimerCallback callback, + void *param); + +/** + * \brief Remove a timer knowing its ID. + * + * \return A boolean value indicating success or failure. + * + * \warning It is not safe to remove a timer multiple times. + */ +extern DECLSPEC SDL_bool SDLCALL SDL_RemoveTimer(SDL_TimerID t); + + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif +#include "close_code.h" + +#endif /* _SDL_timer_h */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_touch.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_touch.h new file mode 100644 index 0000000..587efcb --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_touch.h @@ -0,0 +1,124 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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. +*/ + +/** + * \file SDL_touch.h + * + * Include file for SDL touch event handling. + */ + +#ifndef _SDL_touch_h +#define _SDL_touch_h + +#include "SDL_stdinc.h" +#include "SDL_error.h" +#include "SDL_video.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + + +typedef Sint64 SDL_TouchID; +typedef Sint64 SDL_FingerID; + + +struct SDL_Finger { + SDL_FingerID id; + Uint16 x; + Uint16 y; + Uint16 pressure; + Uint16 xdelta; + Uint16 ydelta; + Uint16 last_x, last_y,last_pressure; /* the last reported coordinates */ + SDL_bool down; +}; + +typedef struct SDL_Touch SDL_Touch; +typedef struct SDL_Finger SDL_Finger; + + +struct SDL_Touch { + + /* Free the touch when it's time */ + void (*FreeTouch) (SDL_Touch * touch); + + /* data common for tablets */ + float pressure_max, pressure_min; + float x_max,x_min; + float y_max,y_min; + Uint16 xres,yres,pressureres; + float native_xres,native_yres,native_pressureres; + float tilt; /* for future use */ + float rotation; /* for future use */ + + /* Data common to all touch */ + SDL_TouchID id; + SDL_Window *focus; + + char *name; + Uint8 buttonstate; + SDL_bool relative_mode; + SDL_bool flush_motion; + + int num_fingers; + int max_fingers; + SDL_Finger** fingers; + + void *driverdata; +}; + + + +/* Function prototypes */ + +/** + * \brief Get the touch object at the given id. + * + * + */ + extern DECLSPEC SDL_Touch* SDLCALL SDL_GetTouch(SDL_TouchID id); + + + +/** + * \brief Get the finger object of the given touch, at the given id. + * + * + */ + extern + DECLSPEC SDL_Finger* SDLCALL SDL_GetFinger(SDL_Touch *touch, SDL_FingerID id); + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif +#include "close_code.h" + +#endif /* _SDL_mouse_h */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_types.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_types.h new file mode 100644 index 0000000..7b1217e --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_types.h @@ -0,0 +1,29 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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. +*/ + +/** + * \file SDL_types.h + * + * \deprecated + */ + +/* DEPRECATED */ +#include "SDL_stdinc.h" diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_version.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_version.h new file mode 100644 index 0000000..3d7ce28 --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_version.h @@ -0,0 +1,166 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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. +*/ + +/** + * \file SDL_version.h + * + * This header defines the current SDL version. + */ + +#ifndef _SDL_version_h +#define _SDL_version_h + +#include "SDL_stdinc.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +/** + * \brief Information the version of SDL in use. + * + * Represents the library's version as three levels: major revision + * (increments with massive changes, additions, and enhancements), + * minor revision (increments with backwards-compatible changes to the + * major revision), and patchlevel (increments with fixes to the minor + * revision). + * + * \sa SDL_VERSION + * \sa SDL_GetVersion + */ +typedef struct SDL_version +{ + Uint8 major; /**< major version */ + Uint8 minor; /**< minor version */ + Uint8 patch; /**< update version */ +} SDL_version; + +/* Printable format: "%d.%d.%d", MAJOR, MINOR, PATCHLEVEL +*/ +#define SDL_MAJOR_VERSION 1 +#define SDL_MINOR_VERSION 3 +#define SDL_PATCHLEVEL 0 + +/** + * \brief Macro to determine SDL version program was compiled against. + * + * This macro fills in a SDL_version structure with the version of the + * library you compiled against. This is determined by what header the + * compiler uses. Note that if you dynamically linked the library, you might + * have a slightly newer or older version at runtime. That version can be + * determined with SDL_GetVersion(), which, unlike SDL_VERSION(), + * is not a macro. + * + * \param x A pointer to a SDL_version struct to initialize. + * + * \sa SDL_version + * \sa SDL_GetVersion + */ +#define SDL_VERSION(x) \ +{ \ + (x)->major = SDL_MAJOR_VERSION; \ + (x)->minor = SDL_MINOR_VERSION; \ + (x)->patch = SDL_PATCHLEVEL; \ +} + +/** + * This macro turns the version numbers into a numeric value: + * \verbatim + (1,2,3) -> (1203) + \endverbatim + * + * This assumes that there will never be more than 100 patchlevels. + */ +#define SDL_VERSIONNUM(X, Y, Z) \ + ((X)*1000 + (Y)*100 + (Z)) + +/** + * This is the version number macro for the current SDL version. + */ +#define SDL_COMPILEDVERSION \ + SDL_VERSIONNUM(SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL) + +/** + * This macro will evaluate to true if compiled with SDL at least X.Y.Z. + */ +#define SDL_VERSION_ATLEAST(X, Y, Z) \ + (SDL_COMPILEDVERSION >= SDL_VERSIONNUM(X, Y, Z)) + +/** + * \brief Get the version of SDL that is linked against your program. + * + * If you are linking to SDL dynamically, then it is possible that the + * current version will be different than the version you compiled against. + * This function returns the current version, while SDL_VERSION() is a + * macro that tells you what version you compiled with. + * + * \code + * SDL_version compiled; + * SDL_version linked; + * + * SDL_VERSION(&compiled); + * SDL_GetVersion(&linked); + * printf("We compiled against SDL version %d.%d.%d ...\n", + * compiled.major, compiled.minor, compiled.patch); + * printf("But we linked against SDL version %d.%d.%d.\n", + * linked.major, linked.minor, linked.patch); + * \endcode + * + * This function may be called safely at any time, even before SDL_Init(). + * + * \sa SDL_VERSION + */ +extern DECLSPEC void SDLCALL SDL_GetVersion(SDL_version * ver); + +/** + * \brief Get the code revision of SDL that is linked against your program. + * + * Returns an arbitrary string (a hash value) uniquely identifying the + * exact revision of the SDL library in use, and is only useful in comparing + * against other revisions. It is NOT an incrementing number. + */ +extern DECLSPEC const char *SDLCALL SDL_GetRevision(void); + +/** + * \brief Get the revision number of SDL that is linked against your program. + * + * Returns a number uniquely identifying the exact revision of the SDL + * library in use. It is an incrementing number based on commits to + * hg.libsdl.org. + */ +extern DECLSPEC int SDLCALL SDL_GetRevisionNumber(void); + + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif +#include "close_code.h" + +#endif /* _SDL_version_h */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_video.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_video.h new file mode 100644 index 0000000..a8dc6f6 --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/SDL_video.h @@ -0,0 +1,817 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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. +*/ + +/** + * \file SDL_video.h + * + * Header file for SDL video functions. + */ + +#ifndef _SDL_video_h +#define _SDL_video_h + +#include "SDL_stdinc.h" +#include "SDL_pixels.h" +#include "SDL_rect.h" +#include "SDL_surface.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +/** + * \brief The structure that defines a display mode + * + * \sa SDL_GetNumDisplayModes() + * \sa SDL_GetDisplayMode() + * \sa SDL_GetDesktopDisplayMode() + * \sa SDL_GetCurrentDisplayMode() + * \sa SDL_GetClosestDisplayMode() + * \sa SDL_SetWindowDisplayMode() + * \sa SDL_GetWindowDisplayMode() + */ +typedef struct +{ + Uint32 format; /**< pixel format */ + int w; /**< width */ + int h; /**< height */ + int refresh_rate; /**< refresh rate (or zero for unspecified) */ + void *driverdata; /**< driver-specific data, initialize to 0 */ +} SDL_DisplayMode; + +/** + * \brief The type used to identify a window + * + * \sa SDL_CreateWindow() + * \sa SDL_CreateWindowFrom() + * \sa SDL_DestroyWindow() + * \sa SDL_GetWindowData() + * \sa SDL_GetWindowFlags() + * \sa SDL_GetWindowGrab() + * \sa SDL_GetWindowPosition() + * \sa SDL_GetWindowSize() + * \sa SDL_GetWindowTitle() + * \sa SDL_HideWindow() + * \sa SDL_MaximizeWindow() + * \sa SDL_MinimizeWindow() + * \sa SDL_RaiseWindow() + * \sa SDL_RestoreWindow() + * \sa SDL_SetWindowData() + * \sa SDL_SetWindowFullscreen() + * \sa SDL_SetWindowGrab() + * \sa SDL_SetWindowIcon() + * \sa SDL_SetWindowPosition() + * \sa SDL_SetWindowSize() + * \sa SDL_SetWindowTitle() + * \sa SDL_ShowWindow() + */ +typedef struct SDL_Window SDL_Window; + +/** + * \brief The flags on a window + * + * \sa SDL_GetWindowFlags() + */ +typedef enum +{ + SDL_WINDOW_FULLSCREEN = 0x00000001, /**< fullscreen window */ + SDL_WINDOW_OPENGL = 0x00000002, /**< window usable with OpenGL context */ + SDL_WINDOW_SHOWN = 0x00000004, /**< window is visible */ + SDL_WINDOW_HIDDEN = 0x00000008, /**< window is not visible */ + SDL_WINDOW_BORDERLESS = 0x00000010, /**< no window decoration */ + SDL_WINDOW_RESIZABLE = 0x00000020, /**< window can be resized */ + SDL_WINDOW_MINIMIZED = 0x00000040, /**< window is minimized */ + SDL_WINDOW_MAXIMIZED = 0x00000080, /**< window is maximized */ + SDL_WINDOW_INPUT_GRABBED = 0x00000100, /**< window has grabbed input focus */ + SDL_WINDOW_INPUT_FOCUS = 0x00000200, /**< window has input focus */ + SDL_WINDOW_MOUSE_FOCUS = 0x00000400, /**< window has mouse focus */ + SDL_WINDOW_FOREIGN = 0x00000800 /**< window not created by SDL */ +} SDL_WindowFlags; + +/** + * \brief Used to indicate that you don't care what the window position is. + */ +#define SDL_WINDOWPOS_UNDEFINED_MASK 0x1FFF0000 +#define SDL_WINDOWPOS_UNDEFINED_DISPLAY(X) (SDL_WINDOWPOS_UNDEFINED_MASK|(X)) +#define SDL_WINDOWPOS_UNDEFINED SDL_WINDOWPOS_UNDEFINED_DISPLAY(0) +#define SDL_WINDOWPOS_ISUNDEFINED(X) \ + (((X)&0xFFFF0000) == SDL_WINDOWPOS_UNDEFINED_MASK) + +/** + * \brief Used to indicate that the window position should be centered. + */ +#define SDL_WINDOWPOS_CENTERED_MASK 0x2FFF0000 +#define SDL_WINDOWPOS_CENTERED_DISPLAY(X) (SDL_WINDOWPOS_CENTERED_MASK|(X)) +#define SDL_WINDOWPOS_CENTERED SDL_WINDOWPOS_CENTERED_DISPLAY(0) +#define SDL_WINDOWPOS_ISCENTERED(X) \ + (((X)&0xFFFF0000) == SDL_WINDOWPOS_CENTERED_MASK) + +/** + * \brief Event subtype for window events + */ +typedef enum +{ + SDL_WINDOWEVENT_NONE, /**< Never used */ + SDL_WINDOWEVENT_SHOWN, /**< Window has been shown */ + SDL_WINDOWEVENT_HIDDEN, /**< Window has been hidden */ + SDL_WINDOWEVENT_EXPOSED, /**< Window has been exposed and should be + redrawn */ + SDL_WINDOWEVENT_MOVED, /**< Window has been moved to data1, data2 + */ + SDL_WINDOWEVENT_RESIZED, /**< Window has been resized to data1xdata2 */ + SDL_WINDOWEVENT_SIZE_CHANGED, /**< The window size has changed, either as a result of an API call or through the system or user changing the window size. */ + SDL_WINDOWEVENT_MINIMIZED, /**< Window has been minimized */ + SDL_WINDOWEVENT_MAXIMIZED, /**< Window has been maximized */ + SDL_WINDOWEVENT_RESTORED, /**< Window has been restored to normal size + and position */ + SDL_WINDOWEVENT_ENTER, /**< Window has gained mouse focus */ + SDL_WINDOWEVENT_LEAVE, /**< Window has lost mouse focus */ + SDL_WINDOWEVENT_FOCUS_GAINED, /**< Window has gained keyboard focus */ + SDL_WINDOWEVENT_FOCUS_LOST, /**< Window has lost keyboard focus */ + SDL_WINDOWEVENT_CLOSE /**< The window manager requests that the + window be closed */ +} SDL_WindowEventID; + +/** + * \brief An opaque handle to an OpenGL context. + */ +typedef void *SDL_GLContext; + +/** + * \brief OpenGL configuration attributes + */ +typedef enum +{ + SDL_GL_RED_SIZE, + SDL_GL_GREEN_SIZE, + SDL_GL_BLUE_SIZE, + SDL_GL_ALPHA_SIZE, + SDL_GL_BUFFER_SIZE, + SDL_GL_DOUBLEBUFFER, + SDL_GL_DEPTH_SIZE, + SDL_GL_STENCIL_SIZE, + SDL_GL_ACCUM_RED_SIZE, + SDL_GL_ACCUM_GREEN_SIZE, + SDL_GL_ACCUM_BLUE_SIZE, + SDL_GL_ACCUM_ALPHA_SIZE, + SDL_GL_STEREO, + SDL_GL_MULTISAMPLEBUFFERS, + SDL_GL_MULTISAMPLESAMPLES, + SDL_GL_ACCELERATED_VISUAL, + SDL_GL_RETAINED_BACKING, + SDL_GL_CONTEXT_MAJOR_VERSION, + SDL_GL_CONTEXT_MINOR_VERSION +} SDL_GLattr; + + +/* Function prototypes */ + +/** + * \brief Get the number of video drivers compiled into SDL + * + * \sa SDL_GetVideoDriver() + */ +extern DECLSPEC int SDLCALL SDL_GetNumVideoDrivers(void); + +/** + * \brief Get the name of a built in video driver. + * + * \note The video drivers are presented in the order in which they are + * normally checked during initialization. + * + * \sa SDL_GetNumVideoDrivers() + */ +extern DECLSPEC const char *SDLCALL SDL_GetVideoDriver(int index); + +/** + * \brief Initialize the video subsystem, optionally specifying a video driver. + * + * \param driver_name Initialize a specific driver by name, or NULL for the + * default video driver. + * + * \return 0 on success, -1 on error + * + * This function initializes the video subsystem; setting up a connection + * to the window manager, etc, and determines the available display modes + * and pixel formats, but does not initialize a window or graphics mode. + * + * \sa SDL_VideoQuit() + */ +extern DECLSPEC int SDLCALL SDL_VideoInit(const char *driver_name); + +/** + * \brief Shuts down the video subsystem. + * + * This function closes all windows, and restores the original video mode. + * + * \sa SDL_VideoInit() + */ +extern DECLSPEC void SDLCALL SDL_VideoQuit(void); + +/** + * \brief Returns the name of the currently initialized video driver. + * + * \return The name of the current video driver or NULL if no driver + * has been initialized + * + * \sa SDL_GetNumVideoDrivers() + * \sa SDL_GetVideoDriver() + */ +extern DECLSPEC const char *SDLCALL SDL_GetCurrentVideoDriver(void); + +/** + * \brief Returns the number of available video displays. + * + * \sa SDL_GetDisplayBounds() + */ +extern DECLSPEC int SDLCALL SDL_GetNumVideoDisplays(void); + +/** + * \brief Get the desktop area represented by a display, with the primary + * display located at 0,0 + * + * \return 0 on success, or -1 if the index is out of range. + * + * \sa SDL_GetNumVideoDisplays() + */ +extern DECLSPEC int SDLCALL SDL_GetDisplayBounds(int displayIndex, SDL_Rect * rect); + +/** + * \brief Returns the number of available display modes. + * + * \sa SDL_GetDisplayMode() + */ +extern DECLSPEC int SDLCALL SDL_GetNumDisplayModes(int displayIndex); + +/** + * \brief Fill in information about a specific display mode. + * + * \note The display modes are sorted in this priority: + * \li bits per pixel -> more colors to fewer colors + * \li width -> largest to smallest + * \li height -> largest to smallest + * \li refresh rate -> highest to lowest + * + * \sa SDL_GetNumDisplayModes() + */ +extern DECLSPEC int SDLCALL SDL_GetDisplayMode(int displayIndex, int modeIndex, + SDL_DisplayMode * mode); + +/** + * \brief Fill in information about the desktop display mode. + */ +extern DECLSPEC int SDLCALL SDL_GetDesktopDisplayMode(int displayIndex, SDL_DisplayMode * mode); + +/** + * \brief Fill in information about the current display mode. + */ +extern DECLSPEC int SDLCALL SDL_GetCurrentDisplayMode(int displayIndex, SDL_DisplayMode * mode); + + +/** + * \brief Get the closest match to the requested display mode. + * + * \param mode The desired display mode + * \param closest A pointer to a display mode to be filled in with the closest + * match of the available display modes. + * + * \return The passed in value \c closest, or NULL if no matching video mode + * was available. + * + * The available display modes are scanned, and \c closest is filled in with the + * closest mode matching the requested mode and returned. The mode format and + * refresh_rate default to the desktop mode if they are 0. The modes are + * scanned with size being first priority, format being second priority, and + * finally checking the refresh_rate. If all the available modes are too + * small, then NULL is returned. + * + * \sa SDL_GetNumDisplayModes() + * \sa SDL_GetDisplayMode() + */ +extern DECLSPEC SDL_DisplayMode * SDLCALL SDL_GetClosestDisplayMode(int displayIndex, const SDL_DisplayMode * mode, SDL_DisplayMode * closest); + +/** + * \brief Get the display index associated with a window. + * + * \return the display index of the display containing the center of the + * window, or -1 on error. + */ +extern DECLSPEC int SDLCALL SDL_GetWindowDisplay(SDL_Window * window); + +/** + * \brief Set the display mode used when a fullscreen window is visible. + * + * By default the window's dimensions and the desktop format and refresh rate + * are used. + * + * \param mode The mode to use, or NULL for the default mode. + * + * \return 0 on success, or -1 if setting the display mode failed. + * + * \sa SDL_GetWindowDisplayMode() + * \sa SDL_SetWindowFullscreen() + */ +extern DECLSPEC int SDLCALL SDL_SetWindowDisplayMode(SDL_Window * window, + const SDL_DisplayMode + * mode); + +/** + * \brief Fill in information about the display mode used when a fullscreen + * window is visible. + * + * \sa SDL_SetWindowDisplayMode() + * \sa SDL_SetWindowFullscreen() + */ +extern DECLSPEC int SDLCALL SDL_GetWindowDisplayMode(SDL_Window * window, + SDL_DisplayMode * mode); + +/** + * \brief Get the pixel format associated with the window. + */ +extern DECLSPEC Uint32 SDLCALL SDL_GetWindowPixelFormat(SDL_Window * window); + +/** + * \brief Create a window with the specified position, dimensions, and flags. + * + * \param title The title of the window, in UTF-8 encoding. + * \param x The x position of the window, ::SDL_WINDOWPOS_CENTERED, or + * ::SDL_WINDOWPOS_UNDEFINED. + * \param y The y position of the window, ::SDL_WINDOWPOS_CENTERED, or + * ::SDL_WINDOWPOS_UNDEFINED. + * \param w The width of the window. + * \param h The height of the window. + * \param flags The flags for the window, a mask of any of the following: + * ::SDL_WINDOW_FULLSCREEN, ::SDL_WINDOW_OPENGL, + * ::SDL_WINDOW_SHOWN, ::SDL_WINDOW_BORDERLESS, + * ::SDL_WINDOW_RESIZABLE, ::SDL_WINDOW_MAXIMIZED, + * ::SDL_WINDOW_MINIMIZED, ::SDL_WINDOW_INPUT_GRABBED. + * + * \return The id of the window created, or zero if window creation failed. + * + * \sa SDL_DestroyWindow() + */ +extern DECLSPEC SDL_Window * SDLCALL SDL_CreateWindow(const char *title, + int x, int y, int w, + int h, Uint32 flags); + +/** + * \brief Create an SDL window from an existing native window. + * + * \param data A pointer to driver-dependent window creation data + * + * \return The id of the window created, or zero if window creation failed. + * + * \sa SDL_DestroyWindow() + */ +extern DECLSPEC SDL_Window * SDLCALL SDL_CreateWindowFrom(const void *data); + +/** + * \brief Get the numeric ID of a window, for logging purposes. + */ +extern DECLSPEC Uint32 SDLCALL SDL_GetWindowID(SDL_Window * window); + +/** + * \brief Get a window from a stored ID, or NULL if it doesn't exist. + */ +extern DECLSPEC SDL_Window * SDLCALL SDL_GetWindowFromID(Uint32 id); + +/** + * \brief Get the window flags. + */ +extern DECLSPEC Uint32 SDLCALL SDL_GetWindowFlags(SDL_Window * window); + +/** + * \brief Set the title of a window, in UTF-8 format. + * + * \sa SDL_GetWindowTitle() + */ +extern DECLSPEC void SDLCALL SDL_SetWindowTitle(SDL_Window * window, + const char *title); + +/** + * \brief Get the title of a window, in UTF-8 format. + * + * \sa SDL_SetWindowTitle() + */ +extern DECLSPEC const char *SDLCALL SDL_GetWindowTitle(SDL_Window * window); + +/** + * \brief Set the icon for a window. + * + * \param icon The icon for the window. + */ +extern DECLSPEC void SDLCALL SDL_SetWindowIcon(SDL_Window * window, + SDL_Surface * icon); + +/** + * \brief Associate an arbitrary named pointer with a window. + * + * \param window The window to associate with the pointer. + * \param name The name of the pointer. + * \param userdata The associated pointer. + * + * \return The previous value associated with 'name' + * + * \note The name is case-sensitive. + * + * \sa SDL_GetWindowData() + */ +extern DECLSPEC void* SDLCALL SDL_SetWindowData(SDL_Window * window, + const char *name, + void *userdata); + +/** + * \brief Retrieve the data pointer associated with a window. + * + * \param window The window to query. + * \param name The name of the pointer. + * + * \return The value associated with 'name' + * + * \sa SDL_SetWindowData() + */ +extern DECLSPEC void *SDLCALL SDL_GetWindowData(SDL_Window * window, + const char *name); + +/** + * \brief Set the position of a window. + * + * \param window The window to reposition. + * \param x The x coordinate of the window, ::SDL_WINDOWPOS_CENTERED, or + ::SDL_WINDOWPOS_UNDEFINED. + * \param y The y coordinate of the window, ::SDL_WINDOWPOS_CENTERED, or + ::SDL_WINDOWPOS_UNDEFINED. + * + * \note The window coordinate origin is the upper left of the display. + * + * \sa SDL_GetWindowPosition() + */ +extern DECLSPEC void SDLCALL SDL_SetWindowPosition(SDL_Window * window, + int x, int y); + +/** + * \brief Get the position of a window. + * + * \sa SDL_SetWindowPosition() + */ +extern DECLSPEC void SDLCALL SDL_GetWindowPosition(SDL_Window * window, + int *x, int *y); + +/** + * \brief Set the size of a window's client area. + * + * \note You can't change the size of a fullscreen window, it automatically + * matches the size of the display mode. + * + * \sa SDL_GetWindowSize() + */ +extern DECLSPEC void SDLCALL SDL_SetWindowSize(SDL_Window * window, int w, + int h); + +/** + * \brief Get the size of a window's client area. + * + * \sa SDL_SetWindowSize() + */ +extern DECLSPEC void SDLCALL SDL_GetWindowSize(SDL_Window * window, int *w, + int *h); + +/** + * \brief Show a window. + * + * \sa SDL_HideWindow() + */ +extern DECLSPEC void SDLCALL SDL_ShowWindow(SDL_Window * window); + +/** + * \brief Hide a window. + * + * \sa SDL_ShowWindow() + */ +extern DECLSPEC void SDLCALL SDL_HideWindow(SDL_Window * window); + +/** + * \brief Raise a window above other windows and set the input focus. + */ +extern DECLSPEC void SDLCALL SDL_RaiseWindow(SDL_Window * window); + +/** + * \brief Make a window as large as possible. + * + * \sa SDL_RestoreWindow() + */ +extern DECLSPEC void SDLCALL SDL_MaximizeWindow(SDL_Window * window); + +/** + * \brief Minimize a window to an iconic representation. + * + * \sa SDL_RestoreWindow() + */ +extern DECLSPEC void SDLCALL SDL_MinimizeWindow(SDL_Window * window); + +/** + * \brief Restore the size and position of a minimized or maximized window. + * + * \sa SDL_MaximizeWindow() + * \sa SDL_MinimizeWindow() + */ +extern DECLSPEC void SDLCALL SDL_RestoreWindow(SDL_Window * window); + +/** + * \brief Set a window's fullscreen state. + * + * \return 0 on success, or -1 if setting the display mode failed. + * + * \sa SDL_SetWindowDisplayMode() + * \sa SDL_GetWindowDisplayMode() + */ +extern DECLSPEC int SDLCALL SDL_SetWindowFullscreen(SDL_Window * window, + SDL_bool fullscreen); + +/** + * \brief Get an SDL surface associated with the window. + * + * \return A surface in the optimal format for the window, or NULL on error. + * + * \note You may not combine this with 3D or the rendering API on this window. + * + * \sa SDL_UpdateWindowSurface() + * \sa SDL_UpdateWindowSurfaceRects() + */ +extern DECLSPEC SDL_Surface * SDLCALL SDL_GetWindowSurface(SDL_Window * window); + +/** + * \brief Copy the window surface to the screen. + * + * \return 0 on success, or -1 on error. + * + * \sa SDL_GetWindowSurface() + * \sa SDL_UpdateWindowSurfaceRects() + */ +extern DECLSPEC int SDLCALL SDL_UpdateWindowSurface(SDL_Window * window); + +/** + * \brief Copy a number of rectangles on the window surface to the screen. + * + * \return 0 on success, or -1 on error. + * + * \sa SDL_GetWindowSurface() + * \sa SDL_UpdateWindowSurfaceRect() + */ +extern DECLSPEC int SDLCALL SDL_UpdateWindowSurfaceRects(SDL_Window * window, + SDL_Rect * rects, + int numrects); + +/** + * \brief Set a window's input grab mode. + * + * \param grabbed This is SDL_TRUE to grab input, and SDL_FALSE to release input. + * + * \sa SDL_GetWindowGrab() + */ +extern DECLSPEC void SDLCALL SDL_SetWindowGrab(SDL_Window * window, + SDL_bool grabbed); + +/** + * \brief Get a window's input grab mode. + * + * \return This returns SDL_TRUE if input is grabbed, and SDL_FALSE otherwise. + * + * \sa SDL_SetWindowGrab() + */ +extern DECLSPEC SDL_bool SDLCALL SDL_GetWindowGrab(SDL_Window * window); + +/** + * \brief Set the brightness (gamma correction) for a window. + * + * \return 0 on success, or -1 if setting the brightness isn't supported. + * + * \sa SDL_GetWindowBrightness() + * \sa SDL_SetWindowGammaRamp() + */ +extern DECLSPEC int SDLCALL SDL_SetWindowBrightness(SDL_Window * window, float brightness); + +/** + * \brief Get the brightness (gamma correction) for a window. + * + * \return The last brightness value passed to SDL_SetWindowBrightness() + * + * \sa SDL_SetWindowBrightness() + */ +extern DECLSPEC float SDLCALL SDL_GetWindowBrightness(SDL_Window * window); + +/** + * \brief Set the gamma ramp for a window. + * + * \param red The translation table for the red channel, or NULL. + * \param green The translation table for the green channel, or NULL. + * \param blue The translation table for the blue channel, or NULL. + * + * \return 0 on success, or -1 if gamma ramps are unsupported. + * + * Set the gamma translation table for the red, green, and blue channels + * of the video hardware. Each table is an array of 256 16-bit quantities, + * representing a mapping between the input and output for that channel. + * The input is the index into the array, and the output is the 16-bit + * gamma value at that index, scaled to the output color precision. + * + * \sa SDL_GetWindowGammaRamp() + */ +extern DECLSPEC int SDLCALL SDL_SetWindowGammaRamp(SDL_Window * window, + const Uint16 * red, + const Uint16 * green, + const Uint16 * blue); + +/** + * \brief Get the gamma ramp for a window. + * + * \param red A pointer to a 256 element array of 16-bit quantities to hold + * the translation table for the red channel, or NULL. + * \param green A pointer to a 256 element array of 16-bit quantities to hold + * the translation table for the green channel, or NULL. + * \param blue A pointer to a 256 element array of 16-bit quantities to hold + * the translation table for the blue channel, or NULL. + * + * \return 0 on success, or -1 if gamma ramps are unsupported. + * + * \sa SDL_SetWindowGammaRamp() + */ +extern DECLSPEC int SDLCALL SDL_GetWindowGammaRamp(SDL_Window * window, + Uint16 * red, + Uint16 * green, + Uint16 * blue); + +/** + * \brief Destroy a window. + */ +extern DECLSPEC void SDLCALL SDL_DestroyWindow(SDL_Window * window); + + +/** + * \brief Returns whether the screensaver is currently enabled (default on). + * + * \sa SDL_EnableScreenSaver() + * \sa SDL_DisableScreenSaver() + */ +extern DECLSPEC SDL_bool SDLCALL SDL_IsScreenSaverEnabled(void); + +/** + * \brief Allow the screen to be blanked by a screensaver + * + * \sa SDL_IsScreenSaverEnabled() + * \sa SDL_DisableScreenSaver() + */ +extern DECLSPEC void SDLCALL SDL_EnableScreenSaver(void); + +/** + * \brief Prevent the screen from being blanked by a screensaver + * + * \sa SDL_IsScreenSaverEnabled() + * \sa SDL_EnableScreenSaver() + */ +extern DECLSPEC void SDLCALL SDL_DisableScreenSaver(void); + + +/** + * \name OpenGL support functions + */ +/*@{*/ + +/** + * \brief Dynamically load an OpenGL library. + * + * \param path The platform dependent OpenGL library name, or NULL to open the + * default OpenGL library. + * + * \return 0 on success, or -1 if the library couldn't be loaded. + * + * This should be done after initializing the video driver, but before + * creating any OpenGL windows. If no OpenGL library is loaded, the default + * library will be loaded upon creation of the first OpenGL window. + * + * \note If you do this, you need to retrieve all of the GL functions used in + * your program from the dynamic library using SDL_GL_GetProcAddress(). + * + * \sa SDL_GL_GetProcAddress() + * \sa SDL_GL_UnloadLibrary() + */ +extern DECLSPEC int SDLCALL SDL_GL_LoadLibrary(const char *path); + +/** + * \brief Get the address of an OpenGL function. + */ +extern DECLSPEC void *SDLCALL SDL_GL_GetProcAddress(const char *proc); + +/** + * \brief Unload the OpenGL library previously loaded by SDL_GL_LoadLibrary(). + * + * \sa SDL_GL_LoadLibrary() + */ +extern DECLSPEC void SDLCALL SDL_GL_UnloadLibrary(void); + +/** + * \brief Return true if an OpenGL extension is supported for the current + * context. + */ +extern DECLSPEC SDL_bool SDLCALL SDL_GL_ExtensionSupported(const char + *extension); + +/** + * \brief Set an OpenGL window attribute before window creation. + */ +extern DECLSPEC int SDLCALL SDL_GL_SetAttribute(SDL_GLattr attr, int value); + +/** + * \brief Get the actual value for an attribute from the current context. + */ +extern DECLSPEC int SDLCALL SDL_GL_GetAttribute(SDL_GLattr attr, int *value); + +/** + * \brief Create an OpenGL context for use with an OpenGL window, and make it + * current. + * + * \sa SDL_GL_DeleteContext() + */ +extern DECLSPEC SDL_GLContext SDLCALL SDL_GL_CreateContext(SDL_Window * + window); + +/** + * \brief Set up an OpenGL context for rendering into an OpenGL window. + * + * \note The context must have been created with a compatible window. + */ +extern DECLSPEC int SDLCALL SDL_GL_MakeCurrent(SDL_Window * window, + SDL_GLContext context); + +/** + * \brief Set the swap interval for the current OpenGL context. + * + * \param interval 0 for immediate updates, 1 for updates synchronized with the + * vertical retrace. + * + * \return 0 on success, or -1 if setting the swap interval is not supported. + * + * \sa SDL_GL_GetSwapInterval() + */ +extern DECLSPEC int SDLCALL SDL_GL_SetSwapInterval(int interval); + +/** + * \brief Get the swap interval for the current OpenGL context. + * + * \return 0 if there is no vertical retrace synchronization, 1 if the buffer + * swap is synchronized with the vertical retrace, and -1 if getting + * the swap interval is not supported. + * + * \sa SDL_GL_SetSwapInterval() + */ +extern DECLSPEC int SDLCALL SDL_GL_GetSwapInterval(void); + +/** + * \brief Swap the OpenGL buffers for a window, if double-buffering is + * supported. + */ +extern DECLSPEC void SDLCALL SDL_GL_SwapWindow(SDL_Window * window); + +/** + * \brief Delete an OpenGL context. + * + * \sa SDL_GL_CreateContext() + */ +extern DECLSPEC void SDLCALL SDL_GL_DeleteContext(SDL_GLContext context); + +/*@}*//*OpenGL support functions*/ + + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif +#include "close_code.h" + +#endif /* _SDL_video_h */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/begin_code.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/begin_code.h new file mode 100644 index 0000000..851fd92 --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/begin_code.h @@ -0,0 +1,135 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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. +*/ + +/** + * \file begin_code.h + * + * This file sets things up for C dynamic library function definitions, + * static inlined functions, and structures aligned at 4-byte alignment. + * If you don't like ugly C preprocessor code, don't look at this file. :) + */ + +/* This shouldn't be nested -- included it around code only. */ +#ifdef _begin_code_h +#error Nested inclusion of begin_code.h +#endif +#define _begin_code_h + +/* Some compilers use a special export keyword */ +#ifndef DECLSPEC +# if defined(__BEOS__) || defined(__HAIKU__) +# if defined(__GNUC__) +# define DECLSPEC __declspec(dllexport) +# else +# define DECLSPEC __declspec(export) +# endif +# elif defined(__WIN32__) +# ifdef __BORLANDC__ +# ifdef BUILD_SDL +# define DECLSPEC +# else +# define DECLSPEC __declspec(dllimport) +# endif +# else +# define DECLSPEC __declspec(dllexport) +# endif +# else +# if defined(__GNUC__) && __GNUC__ >= 4 +# define DECLSPEC __attribute__ ((visibility("default"))) +# else +# define DECLSPEC +# endif +# endif +#endif + +/* By default SDL uses the C calling convention */ +#ifndef SDLCALL +#if defined(__WIN32__) && !defined(__GNUC__) +#define SDLCALL __cdecl +#else +#define SDLCALL +#endif +#endif /* SDLCALL */ + +/* Removed DECLSPEC on Symbian OS because SDL cannot be a DLL in EPOC */ +#ifdef __SYMBIAN32__ +#undef DECLSPEC +#define DECLSPEC +#endif /* __SYMBIAN32__ */ + +/* Force structure packing at 4 byte alignment. + This is necessary if the header is included in code which has structure + packing set to an alternate value, say for loading structures from disk. + The packing is reset to the previous value in close_code.h + */ +#if defined(_MSC_VER) || defined(__MWERKS__) || defined(__BORLANDC__) +#ifdef _MSC_VER +#pragma warning(disable: 4103) +#endif +#ifdef __BORLANDC__ +#pragma nopackwarning +#endif +#pragma pack(push,4) +#endif /* Compiler needs structure packing set */ + +/* Set up compiler-specific options for inlining functions */ +#ifndef SDL_INLINE_OKAY +#ifdef __GNUC__ +#define SDL_INLINE_OKAY +#else +/* Add any special compiler-specific cases here */ +#if defined(_MSC_VER) || defined(__BORLANDC__) || \ + defined(__DMC__) || defined(__SC__) || \ + defined(__WATCOMC__) || defined(__LCC__) || \ + defined(__DECC) +#ifndef __inline__ +#define __inline__ __inline +#endif +#define SDL_INLINE_OKAY +#else +#if !defined(__MRC__) && !defined(_SGI_SOURCE) +#ifndef __inline__ +#define __inline__ inline +#endif +#define SDL_INLINE_OKAY +#endif /* Not a funky compiler */ +#endif /* Visual C++ */ +#endif /* GNU C */ +#endif /* SDL_INLINE_OKAY */ + +/* If inlining isn't supported, remove "__inline__", turning static + inlined functions into static functions (resulting in code bloat + in all files which include the offending header files) +*/ +#ifndef SDL_INLINE_OKAY +#define __inline__ +#endif + +/* Apparently this is needed by several Windows compilers */ +#if !defined(__MACH__) +#ifndef NULL +#ifdef __cplusplus +#define NULL 0 +#else +#define NULL ((void *)0) +#endif +#endif /* NULL */ +#endif /* ! Mac OS X - breaks precompiled headers */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Headers/close_code.h b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/close_code.h new file mode 100644 index 0000000..7ecf255 --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Headers/close_code.h @@ -0,0 +1,37 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2011 Sam Lantinga + + 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. +*/ + +/** + * \file close_code.h + * + * This file reverses the effects of begin_code.h and should be included + * after you finish any function and structure declarations in your headers + */ + +#undef _begin_code_h + +/* Reset structure packing at previous byte alignment */ +#if defined(_MSC_VER) || defined(__MWERKS__) || defined(__WATCOMC__) || defined(__BORLANDC__) +#ifdef __BORLANDC__ +#pragma nopackwarning +#endif +#pragma pack(pop) +#endif /* Compiler needs structure packing set */ diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/Resources/Info.plist b/game/sdl/mac/SDL.framework/Versions/1.3/Resources/Info.plist new file mode 100644 index 0000000..65d4a61 --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/1.3/Resources/Info.plist @@ -0,0 +1,42 @@ + + + + + BuildMachineOSBuild + 10J869 + CFBundleDevelopmentRegion + English + CFBundleExecutable + SDL + CFBundleGetInfoString + http://www.libsdl.org + CFBundleIdentifier + SDL + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Simple DirectMedia Layer + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.2.14 + CFBundleSignature + SDLX + CFBundleVersion + 1.3.0 + DTCompiler + + DTPlatformBuild + 4A2002a + DTPlatformVersion + GM + DTSDKBuild + 4A2002a + DTSDKName + macosx10.6 + DTXcode + 0402 + DTXcodeBuild + 4A2002a + + diff --git a/game/sdl/mac/SDL.framework/Versions/1.3/SDL b/game/sdl/mac/SDL.framework/Versions/1.3/SDL new file mode 100755 index 0000000..a440471 Binary files /dev/null and b/game/sdl/mac/SDL.framework/Versions/1.3/SDL differ diff --git a/game/sdl/mac/SDL.framework/Versions/Current b/game/sdl/mac/SDL.framework/Versions/Current new file mode 120000 index 0000000..a58941b --- /dev/null +++ b/game/sdl/mac/SDL.framework/Versions/Current @@ -0,0 +1 @@ +1.3 \ No newline at end of file diff --git a/game/sdl/mac/SDLMain.h b/game/sdl/mac/SDLMain.h new file mode 100644 index 0000000..c56d90c --- /dev/null +++ b/game/sdl/mac/SDLMain.h @@ -0,0 +1,16 @@ +/* SDLMain.m - main entry point for our Cocoa-ized SDL app + Initial Version: Darrell Walisser + Non-NIB-Code & other changes: Max Horn + + Feel free to customize this file to suit your needs +*/ + +#ifndef _SDLMain_h_ +#define _SDLMain_h_ + +#import + +@interface SDLMain : NSObject +@end + +#endif /* _SDLMain_h_ */ diff --git a/game/sdl/mac/SDLMain.m b/game/sdl/mac/SDLMain.m new file mode 100644 index 0000000..98cba92 --- /dev/null +++ b/game/sdl/mac/SDLMain.m @@ -0,0 +1,381 @@ +/* SDLMain.m - main entry point for our Cocoa-ized SDL app + Initial Version: Darrell Walisser + Non-NIB-Code & other changes: Max Horn + + Feel free to customize this file to suit your needs +*/ + +#include +#include "SDLMain.h" +#include /* for MAXPATHLEN */ +#include + +/* For some reaon, Apple removed setAppleMenu from the headers in 10.4, + but the method still is there and works. To avoid warnings, we declare + it ourselves here. */ +@interface NSApplication(SDL_Missing_Methods) +- (void)setAppleMenu:(NSMenu *)menu; +@end + +/* Use this flag to determine whether we use SDLMain.nib or not */ +#define SDL_USE_NIB_FILE 0 + +/* Use this flag to determine whether we use CPS (docking) or not */ +#define SDL_USE_CPS 1 +#ifdef SDL_USE_CPS +/* Portions of CPS.h */ +typedef struct CPSProcessSerNum +{ + UInt32 lo; + UInt32 hi; +} CPSProcessSerNum; + +extern OSErr CPSGetCurrentProcess( CPSProcessSerNum *psn); +extern OSErr CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5); +extern OSErr CPSSetFrontProcess( CPSProcessSerNum *psn); + +#endif /* SDL_USE_CPS */ + +static int gArgc; +static char **gArgv; +static BOOL gFinderLaunch; +static BOOL gCalledAppMainline = FALSE; + +static NSString *getApplicationName(void) +{ + const NSDictionary *dict; + NSString *appName = 0; + + /* Determine the application name */ + dict = (const NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle()); + if (dict) + appName = [dict objectForKey: @"CFBundleName"]; + + if (![appName length]) + appName = [[NSProcessInfo processInfo] processName]; + + return appName; +} + +#if SDL_USE_NIB_FILE +/* A helper category for NSString */ +@interface NSString (ReplaceSubString) +- (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString; +@end +#endif + +@interface NSApplication (SDLApplication) +@end + +@implementation NSApplication (SDLApplication) +/* Invoked from the Quit menu item */ +- (void)terminate:(id)sender +{ + /* Post a SDL_QUIT event */ + SDL_Event event; + event.type = SDL_QUIT; + SDL_PushEvent(&event); +} +@end + +/* The main class of the application, the application's delegate */ +@implementation SDLMain + +/* Set the working directory to the .app's parent directory */ +- (void) setupWorkingDirectory:(BOOL)shouldChdir +{ + if (shouldChdir) + { + char parentdir[MAXPATHLEN]; + CFURLRef url = CFBundleCopyBundleURL(CFBundleGetMainBundle()); + CFURLRef url2 = CFURLCreateCopyDeletingLastPathComponent(0, url); + if (CFURLGetFileSystemRepresentation(url2, 1, (UInt8 *)parentdir, MAXPATHLEN)) { + chdir(parentdir); /* chdir to the binary app's parent */ + } + CFRelease(url); + CFRelease(url2); + } +} + +#if SDL_USE_NIB_FILE + +/* Fix menu to contain the real app name instead of "SDL App" */ +- (void)fixMenu:(NSMenu *)aMenu withAppName:(NSString *)appName +{ + NSRange aRange; + NSEnumerator *enumerator; + NSMenuItem *menuItem; + + aRange = [[aMenu title] rangeOfString:@"SDL App"]; + if (aRange.length != 0) + [aMenu setTitle: [[aMenu title] stringByReplacingRange:aRange with:appName]]; + + enumerator = [[aMenu itemArray] objectEnumerator]; + while ((menuItem = [enumerator nextObject])) + { + aRange = [[menuItem title] rangeOfString:@"SDL App"]; + if (aRange.length != 0) + [menuItem setTitle: [[menuItem title] stringByReplacingRange:aRange with:appName]]; + if ([menuItem hasSubmenu]) + [self fixMenu:[menuItem submenu] withAppName:appName]; + } +} + +#else + +static void setApplicationMenu(void) +{ + /* warning: this code is very odd */ + NSMenu *appleMenu; + NSMenuItem *menuItem; + NSString *title; + NSString *appName; + + appName = getApplicationName(); + appleMenu = [[NSMenu alloc] initWithTitle:@""]; + + /* Add menu items */ + title = [@"About " stringByAppendingString:appName]; + [appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""]; + + [appleMenu addItem:[NSMenuItem separatorItem]]; + + title = [@"Hide " stringByAppendingString:appName]; + [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"]; + + menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; + [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)]; + + [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; + + [appleMenu addItem:[NSMenuItem separatorItem]]; + + title = [@"Quit " stringByAppendingString:appName]; + [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"]; + + + /* Put menu into the menubar */ + menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""]; + [menuItem setSubmenu:appleMenu]; + [[NSApp mainMenu] addItem:menuItem]; + + /* Tell the application object that this is now the application menu */ + [NSApp setAppleMenu:appleMenu]; + + /* Finally give up our references to the objects */ + [appleMenu release]; + [menuItem release]; +} + +/* Create a window menu */ +static void setupWindowMenu(void) +{ + NSMenu *windowMenu; + NSMenuItem *windowMenuItem; + NSMenuItem *menuItem; + + windowMenu = [[NSMenu alloc] initWithTitle:@"Window"]; + + /* "Minimize" item */ + menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"]; + [windowMenu addItem:menuItem]; + [menuItem release]; + + /* Put menu into the menubar */ + windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""]; + [windowMenuItem setSubmenu:windowMenu]; + [[NSApp mainMenu] addItem:windowMenuItem]; + + /* Tell the application object that this is now the window menu */ + [NSApp setWindowsMenu:windowMenu]; + + /* Finally give up our references to the objects */ + [windowMenu release]; + [windowMenuItem release]; +} + +/* Replacement for NSApplicationMain */ +static void CustomApplicationMain (int argc, char **argv) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + SDLMain *sdlMain; + + /* Ensure the application object is initialised */ + [NSApplication sharedApplication]; + +#ifdef SDL_USE_CPS + { + CPSProcessSerNum PSN; + /* Tell the dock about us */ + if (!CPSGetCurrentProcess(&PSN)) + if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103)) + if (!CPSSetFrontProcess(&PSN)) + [NSApplication sharedApplication]; + } +#endif /* SDL_USE_CPS */ + + /* Set up the menubar */ + [NSApp setMainMenu:[[NSMenu alloc] init]]; + setApplicationMenu(); + setupWindowMenu(); + + /* Create SDLMain and make it the app delegate */ + sdlMain = [[SDLMain alloc] init]; + [NSApp setDelegate:sdlMain]; + + /* Start the main event loop */ + [NSApp run]; + + [sdlMain release]; + [pool release]; +} + +#endif + + +/* + * Catch document open requests...this lets us notice files when the app + * was launched by double-clicking a document, or when a document was + * dragged/dropped on the app's icon. You need to have a + * CFBundleDocumentsType section in your Info.plist to get this message, + * apparently. + * + * Files are added to gArgv, so to the app, they'll look like command line + * arguments. Previously, apps launched from the finder had nothing but + * an argv[0]. + * + * This message may be received multiple times to open several docs on launch. + * + * This message is ignored once the app's mainline has been called. + */ +- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename +{ + const char *temparg; + size_t arglen; + char *arg; + char **newargv; + + if (!gFinderLaunch) /* MacOS is passing command line args. */ + return FALSE; + + if (gCalledAppMainline) /* app has started, ignore this document. */ + return FALSE; + + temparg = [filename UTF8String]; + arglen = SDL_strlen(temparg) + 1; + arg = (char *) SDL_malloc(arglen); + if (arg == NULL) + return FALSE; + + newargv = (char **) realloc(gArgv, sizeof (char *) * (gArgc + 2)); + if (newargv == NULL) + { + SDL_free(arg); + return FALSE; + } + gArgv = newargv; + + SDL_strlcpy(arg, temparg, arglen); + gArgv[gArgc++] = arg; + gArgv[gArgc] = NULL; + return TRUE; +} + + +/* Called when the internal event loop has just started running */ +- (void) applicationDidFinishLaunching: (NSNotification *) note +{ + int status; + + /* Set the working directory to the .app's parent directory */ + [self setupWorkingDirectory:gFinderLaunch]; + +#if SDL_USE_NIB_FILE + /* Set the main menu to contain the real app name instead of "SDL App" */ + [self fixMenu:[NSApp mainMenu] withAppName:getApplicationName()]; +#endif + + /* Hand off to main application code */ + gCalledAppMainline = TRUE; + status = SDL_main (gArgc, gArgv); + + /* We're done, thank you for playing */ + exit(status); +} +@end + + +@implementation NSString (ReplaceSubString) + +- (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString +{ + unsigned int bufferSize; + unsigned int selfLen = (unsigned int)[self length]; + unsigned int aStringLen = (unsigned int)[aString length]; + unichar *buffer; + NSRange localRange; + NSString *result; + + bufferSize = selfLen + aStringLen - (unsigned int)aRange.length; + buffer = (unichar *)NSAllocateMemoryPages(bufferSize*sizeof(unichar)); + + /* Get first part into buffer */ + localRange.location = 0; + localRange.length = aRange.location; + [self getCharacters:buffer range:localRange]; + + /* Get middle part into buffer */ + localRange.location = 0; + localRange.length = aStringLen; + [aString getCharacters:(buffer+aRange.location) range:localRange]; + + /* Get last part into buffer */ + localRange.location = aRange.location + aRange.length; + localRange.length = selfLen - localRange.location; + [self getCharacters:(buffer+aRange.location+aStringLen) range:localRange]; + + /* Build output string */ + result = [NSString stringWithCharacters:buffer length:bufferSize]; + + NSDeallocateMemoryPages(buffer, bufferSize); + + return result; +} + +@end + + + +#ifdef main +# undef main +#endif + + +/* Main entry point to executable - should *not* be SDL_main! */ +int main (int argc, char **argv) +{ + /* Copy the arguments into a global variable */ + /* This is passed if we are launched by double-clicking */ + if ( argc >= 2 && strncmp (argv[1], "-psn", 4) == 0 ) { + gArgv = (char **) SDL_malloc(sizeof (char *) * 2); + gArgv[0] = argv[0]; + gArgv[1] = NULL; + gArgc = 1; + gFinderLaunch = YES; + } else { + int i; + gArgc = argc; + gArgv = (char **) SDL_malloc(sizeof (char *) * (argc+1)); + for (i = 0; i <= argc; i++) + gArgv[i] = argv[i]; + gFinderLaunch = NO; + } + +#if SDL_USE_NIB_FILE + NSApplicationMain (argc, argv); +#else + CustomApplicationMain (argc, argv); +#endif + return 0; +} + diff --git a/game/sdl/mac/en.lproj/Credits.rtf b/game/sdl/mac/en.lproj/Credits.rtf new file mode 100644 index 0000000..46576ef --- /dev/null +++ b/game/sdl/mac/en.lproj/Credits.rtf @@ -0,0 +1,29 @@ +{\rtf0\ansi{\fonttbl\f0\fswiss Helvetica;} +{\colortbl;\red255\green255\blue255;} +\paperw9840\paperh8400 +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural + +\f0\b\fs24 \cf0 Engineering: +\b0 \ + Some people\ +\ + +\b Human Interface Design: +\b0 \ + Some other people\ +\ + +\b Testing: +\b0 \ + Hopefully not nobody\ +\ + +\b Documentation: +\b0 \ + Whoever\ +\ + +\b With special thanks to: +\b0 \ + Mom\ +} diff --git a/game/sdl/mac/en.lproj/InfoPlist.strings b/game/sdl/mac/en.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/game/sdl/mac/en.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/game/sdl/mac/en.lproj/MainMenu.xib b/game/sdl/mac/en.lproj/MainMenu.xib new file mode 100644 index 0000000..c550708 --- /dev/null +++ b/game/sdl/mac/en.lproj/MainMenu.xib @@ -0,0 +1,4119 @@ + + + + 1060 + 10A324 + 719 + 1015 + 418.00 + + com.apple.InterfaceBuilder.CocoaPlugin + 719 + + + YES + + + + + YES + com.apple.InterfaceBuilder.CocoaPlugin + + + YES + + YES + + + YES + + + + YES + + NSApplication + + + FirstResponder + + + NSApplication + + + AMainMenu + + YES + + + Hostile Takeover + + 1048576 + 2147483647 + + NSImage + NSMenuCheckmark + + + NSImage + NSMenuMixedState + + submenuAction: + + Hostile Takeover + + YES + + + About Hostile Takeover + + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Preferences… + , + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Services + + 1048576 + 2147483647 + + + submenuAction: + + Services + + YES + + _NSServicesMenu + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Hide Hostile Takeover + h + 1048576 + 2147483647 + + + + + + Hide Others + h + 1572864 + 2147483647 + + + + + + Show All + + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Quit Hostile Takeover + q + 1048576 + 2147483647 + + + + + _NSAppleMenu + + + + + File + + 1048576 + 2147483647 + + + submenuAction: + + File + + YES + + + New + n + 1048576 + 2147483647 + + + + + + Open… + o + 1048576 + 2147483647 + + + + + + Open Recent + + 1048576 + 2147483647 + + + submenuAction: + + Open Recent + + YES + + + Clear Menu + + 1048576 + 2147483647 + + + + + _NSRecentDocumentsMenu + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Close + w + 1048576 + 2147483647 + + + + + + Save + s + 1048576 + 2147483647 + + + + + + Save As… + S + 1179648 + 2147483647 + + + + + + Revert to Saved + + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Page Setup... + P + 1179648 + 2147483647 + + + + + + + Print… + p + 1048576 + 2147483647 + + + + + + + + + Edit + + 1048576 + 2147483647 + + + submenuAction: + + Edit + + YES + + + Undo + z + 1048576 + 2147483647 + + + + + + Redo + Z + 1179648 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Cut + x + 1048576 + 2147483647 + + + + + + Copy + c + 1048576 + 2147483647 + + + + + + Paste + v + 1048576 + 2147483647 + + + + + + Paste and Match Style + V + 1572864 + 2147483647 + + + + + + Delete + + 1048576 + 2147483647 + + + + + + Select All + a + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Find + + 1048576 + 2147483647 + + + submenuAction: + + Find + + YES + + + Find… + f + 1048576 + 2147483647 + + + 1 + + + + Find Next + g + 1048576 + 2147483647 + + + 2 + + + + Find Previous + G + 1179648 + 2147483647 + + + 3 + + + + Use Selection for Find + e + 1048576 + 2147483647 + + + 7 + + + + Jump to Selection + j + 1048576 + 2147483647 + + + + + + + + + Spelling and Grammar + + 1048576 + 2147483647 + + + submenuAction: + + Spelling and Grammar + + YES + + + Show Spelling and Grammar + : + 1048576 + 2147483647 + + + + + + Check Document Now + ; + 1048576 + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + Check Spelling While Typing + + 1048576 + 2147483647 + + + + + + Check Grammar With Spelling + + 1048576 + 2147483647 + + + + + + Correct Spelling Automatically + + 2147483647 + + + + + + + + + Substitutions + + 1048576 + 2147483647 + + + submenuAction: + + Substitutions + + YES + + + Show Substitutions + + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + Smart Copy/Paste + f + 1048576 + 2147483647 + + + 1 + + + + Smart Quotes + g + 1048576 + 2147483647 + + + 2 + + + + Smart Dashes + + 2147483647 + + + + + + Smart Links + G + 1179648 + 2147483647 + + + 3 + + + + Text Replacement + + 2147483647 + + + + + + + + + Transformations + + 2147483647 + + + submenuAction: + + Transformations + + YES + + + Make Upper Case + + 2147483647 + + + + + + Make Lower Case + + 2147483647 + + + + + + Capitalize + + 2147483647 + + + + + + + + + Speech + + 1048576 + 2147483647 + + + submenuAction: + + Speech + + YES + + + Start Speaking + + 1048576 + 2147483647 + + + + + + Stop Speaking + + 1048576 + 2147483647 + + + + + + + + + + + + Format + + 2147483647 + + + submenuAction: + + Format + + YES + + + Font + + 2147483647 + + + submenuAction: + + Font + + YES + + + Show Fonts + t + 1048576 + 2147483647 + + + + + + Bold + b + 1048576 + 2147483647 + + + 2 + + + + Italic + i + 1048576 + 2147483647 + + + 1 + + + + Underline + u + 1048576 + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + Bigger + + + 1048576 + 2147483647 + + + 3 + + + + Smaller + - + 1048576 + 2147483647 + + + 4 + + + + YES + YES + + + 2147483647 + + + + + + Kern + + 2147483647 + + + submenuAction: + + Kern + + YES + + + Use Default + + 2147483647 + + + + + + Use None + + 2147483647 + + + + + + Tighten + + 2147483647 + + + + + + Loosen + + 2147483647 + + + + + + + + + Ligature + + 2147483647 + + + submenuAction: + + Ligature + + YES + + + Use Default + + 2147483647 + + + + + + Use None + + 2147483647 + + + + + + Use All + + 2147483647 + + + + + + + + + Baseline + + 2147483647 + + + submenuAction: + + Baseline + + YES + + + Use Default + + 2147483647 + + + + + + Superscript + + 2147483647 + + + + + + Subscript + + 2147483647 + + + + + + Raise + + 2147483647 + + + + + + Lower + + 2147483647 + + + + + + + + + YES + YES + + + 2147483647 + + + + + + Show Colors + C + 1048576 + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + Copy Style + c + 1572864 + 2147483647 + + + + + + Paste Style + v + 1572864 + 2147483647 + + + + + _NSFontMenu + + + + + Text + + 2147483647 + + + submenuAction: + + Text + + YES + + + Align Left + { + 1048576 + 2147483647 + + + + + + Center + | + 1048576 + 2147483647 + + + + + + Justify + + 2147483647 + + + + + + Align Right + } + 1048576 + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + Writing Direction + + 2147483647 + + + submenuAction: + + Writing Direction + + YES + + + YES + Paragraph + + 2147483647 + + + + + + CURlZmF1bHQ + + 2147483647 + + + + + + CUxlZnQgdG8gUmlnaHQ + + 2147483647 + + + + + + CVJpZ2h0IHRvIExlZnQ + + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + YES + Selection + + 2147483647 + + + + + + CURlZmF1bHQ + + 2147483647 + + + + + + CUxlZnQgdG8gUmlnaHQ + + 2147483647 + + + + + + CVJpZ2h0IHRvIExlZnQ + + 2147483647 + + + + + + + + + YES + YES + + + 2147483647 + + + + + + Show Ruler + + 2147483647 + + + + + + Copy Ruler + c + 1310720 + 2147483647 + + + + + + Paste Ruler + v + 1310720 + 2147483647 + + + + + + + + + + + + View + + 1048576 + 2147483647 + + + submenuAction: + + View + + YES + + + Show Toolbar + t + 1572864 + 2147483647 + + + + + + Customize Toolbar… + + 1048576 + 2147483647 + + + + + + + + + Window + + 1048576 + 2147483647 + + + submenuAction: + + Window + + YES + + + Minimize + m + 1048576 + 2147483647 + + + + + + Zoom + + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Bring All to Front + + 1048576 + 2147483647 + + + + + _NSWindowsMenu + + + + + Help + + 2147483647 + + + submenuAction: + + Help + + YES + + + Hostile Takeover Help + ? + 1048576 + 2147483647 + + + + + _NSHelpMenu + + + + _NSMainMenu + + + 15 + 2 + {{335, 390}, {480, 360}} + 1954021376 + Hostile Takeover + NSWindow + + {1.79769e+308, 1.79769e+308} + + + 256 + {480, 360} + + + {{0, 0}, {1920, 1178}} + {1.79769e+308, 1.79769e+308} + + + Warfare_IncorporatedAppDelegate + + + NSFontManager + + + + + YES + + + performMiniaturize: + + + + 37 + + + + arrangeInFront: + + + + 39 + + + + print: + + + + 86 + + + + runPageLayout: + + + + 87 + + + + clearRecentDocuments: + + + + 127 + + + + orderFrontStandardAboutPanel: + + + + 142 + + + + performClose: + + + + 193 + + + + toggleContinuousSpellChecking: + + + + 222 + + + + undo: + + + + 223 + + + + copy: + + + + 224 + + + + checkSpelling: + + + + 225 + + + + paste: + + + + 226 + + + + stopSpeaking: + + + + 227 + + + + cut: + + + + 228 + + + + showGuessPanel: + + + + 230 + + + + redo: + + + + 231 + + + + selectAll: + + + + 232 + + + + startSpeaking: + + + + 233 + + + + delete: + + + + 235 + + + + performZoom: + + + + 240 + + + + performFindPanelAction: + + + + 241 + + + + centerSelectionInVisibleArea: + + + + 245 + + + + toggleGrammarChecking: + + + + 347 + + + + toggleSmartInsertDelete: + + + + 355 + + + + toggleAutomaticQuoteSubstitution: + + + + 356 + + + + toggleAutomaticLinkDetection: + + + + 357 + + + + saveDocument: + + + + 362 + + + + saveDocumentAs: + + + + 363 + + + + revertDocumentToSaved: + + + + 364 + + + + runToolbarCustomizationPalette: + + + + 365 + + + + toggleToolbarShown: + + + + 366 + + + + hide: + + + + 367 + + + + hideOtherApplications: + + + + 368 + + + + unhideAllApplications: + + + + 370 + + + + newDocument: + + + + 373 + + + + openDocument: + + + + 374 + + + + addFontTrait: + + + + 421 + + + + addFontTrait: + + + + 422 + + + + modifyFont: + + + + 423 + + + + orderFrontFontPanel: + + + + 424 + + + + modifyFont: + + + + 425 + + + + raiseBaseline: + + + + 426 + + + + lowerBaseline: + + + + 427 + + + + copyFont: + + + + 428 + + + + subscript: + + + + 429 + + + + superscript: + + + + 430 + + + + tightenKerning: + + + + 431 + + + + underline: + + + + 432 + + + + orderFrontColorPanel: + + + + 433 + + + + useAllLigatures: + + + + 434 + + + + loosenKerning: + + + + 435 + + + + pasteFont: + + + + 436 + + + + unscript: + + + + 437 + + + + useStandardKerning: + + + + 438 + + + + useStandardLigatures: + + + + 439 + + + + turnOffLigatures: + + + + 440 + + + + turnOffKerning: + + + + 441 + + + + terminate: + + + + 449 + + + + toggleAutomaticSpellingCorrection: + + + + 456 + + + + orderFrontSubstitutionsPanel: + + + + 458 + + + + toggleAutomaticDashSubstitution: + + + + 461 + + + + toggleAutomaticTextReplacement: + + + + 463 + + + + uppercaseWord: + + + + 464 + + + + capitalizeWord: + + + + 467 + + + + lowercaseWord: + + + + 468 + + + + pasteAsPlainText: + + + + 486 + + + + performFindPanelAction: + + + + 487 + + + + performFindPanelAction: + + + + 488 + + + + performFindPanelAction: + + + + 489 + + + + showHelp: + + + + 493 + + + + delegate + + + + 495 + + + + alignCenter: + + + + 518 + + + + pasteRuler: + + + + 519 + + + + toggleRuler: + + + + 520 + + + + alignRight: + + + + 521 + + + + copyRuler: + + + + 522 + + + + alignJustified: + + + + 523 + + + + alignLeft: + + + + 524 + + + + makeBaseWritingDirectionNatural: + + + + 525 + + + + makeBaseWritingDirectionLeftToRight: + + + + 526 + + + + makeBaseWritingDirectionRightToLeft: + + + + 527 + + + + makeTextWritingDirectionNatural: + + + + 528 + + + + makeTextWritingDirectionLeftToRight: + + + + 529 + + + + makeTextWritingDirectionRightToLeft: + + + + 530 + + + + window + + + + 532 + + + + + YES + + 0 + + + + + + -2 + + + File's Owner + + + -1 + + + First Responder + + + -3 + + + Application + + + 29 + + + YES + + + + + + + + + + + + 19 + + + YES + + + + + + 56 + + + YES + + + + + + 217 + + + YES + + + + + + 83 + + + YES + + + + + + 81 + + + YES + + + + + + + + + + + + + + + + 75 + + + + + 80 + + + + + 78 + + + + + 72 + + + + + 82 + + + + + 124 + + + YES + + + + + + 77 + + + + + 73 + + + + + 79 + + + + + 112 + + + + + 74 + + + + + 125 + + + YES + + + + + + 126 + + + + + 205 + + + YES + + + + + + + + + + + + + + + + + + + + 202 + + + + + 198 + + + + + 207 + + + + + 214 + + + + + 199 + + + + + 203 + + + + + 197 + + + + + 206 + + + + + 215 + + + + + 218 + + + YES + + + + + + 216 + + + YES + + + + + + 200 + + + YES + + + + + + + + + + + 219 + + + + + 201 + + + + + 204 + + + + + 220 + + + YES + + + + + + + + + + 213 + + + + + 210 + + + + + 221 + + + + + 208 + + + + + 209 + + + + + 57 + + + YES + + + + + + + + + + + + + + + + 58 + + + + + 134 + + + + + 150 + + + + + 136 + + + + + 144 + + + + + 129 + + + + + 143 + + + + + 236 + + + + + 131 + + + YES + + + + + + 149 + + + + + 145 + + + + + 130 + + + + + 24 + + + YES + + + + + + + + + 92 + + + + + 5 + + + + + 239 + + + + + 23 + + + + + 295 + + + YES + + + + + + 296 + + + YES + + + + + + + 297 + + + + + 298 + + + + + 211 + + + YES + + + + + + 212 + + + YES + + + + + + + 195 + + + + + 196 + + + + + 346 + + + + + 348 + + + YES + + + + + + 349 + + + YES + + + + + + + + + + + + 350 + + + + + 351 + + + + + 354 + + + + + 371 + + + YES + + + + + + 372 + + + + + 375 + + + YES + + + + + + 376 + + + YES + + + + + + + 377 + + + YES + + + + + + 388 + + + YES + + + + + + + + + + + + + + + + + + + + + 389 + + + + + 390 + + + + + 391 + + + + + 392 + + + + + 393 + + + + + 394 + + + + + 395 + + + + + 396 + + + + + 397 + + + YES + + + + + + 398 + + + YES + + + + + + 399 + + + YES + + + + + + 400 + + + + + 401 + + + + + 402 + + + + + 403 + + + + + 404 + + + + + 405 + + + YES + + + + + + + + + + 406 + + + + + 407 + + + + + 408 + + + + + 409 + + + + + 410 + + + + + 411 + + + YES + + + + + + + + 412 + + + + + 413 + + + + + 414 + + + + + 415 + + + YES + + + + + + + + + 416 + + + + + 417 + + + + + 418 + + + + + 419 + + + + + 420 + + + + + 450 + + + YES + + + + + + 451 + + + YES + + + + + + + + 452 + + + + + 453 + + + + + 454 + + + + + 457 + + + + + 459 + + + + + 460 + + + + + 462 + + + + + 465 + + + + + 466 + + + + + 485 + + + + + 490 + + + YES + + + + + + 491 + + + YES + + + + + + 492 + + + + + 494 + + + + + 496 + + + YES + + + + + + 497 + + + YES + + + + + + + + + + + + + + + 498 + + + + + 499 + + + + + 500 + + + + + 501 + + + + + 502 + + + + + 503 + + + YES + + + + + + 504 + + + + + 505 + + + + + 506 + + + + + 507 + + + + + 508 + + + YES + + + + + + + + + + + + + + 509 + + + + + 510 + + + + + 511 + + + + + 512 + + + + + 513 + + + + + 514 + + + + + 515 + + + + + 516 + + + + + 517 + + + + + + + YES + + YES + -3.IBPluginDependency + 112.IBPluginDependency + 112.ImportedFromIB2 + 124.IBPluginDependency + 124.ImportedFromIB2 + 125.IBPluginDependency + 125.ImportedFromIB2 + 125.editorWindowContentRectSynchronizationRect + 126.IBPluginDependency + 126.ImportedFromIB2 + 129.IBPluginDependency + 129.ImportedFromIB2 + 130.IBPluginDependency + 130.ImportedFromIB2 + 130.editorWindowContentRectSynchronizationRect + 131.IBPluginDependency + 131.ImportedFromIB2 + 134.IBPluginDependency + 134.ImportedFromIB2 + 136.IBPluginDependency + 136.ImportedFromIB2 + 143.IBPluginDependency + 143.ImportedFromIB2 + 144.IBPluginDependency + 144.ImportedFromIB2 + 145.IBPluginDependency + 145.ImportedFromIB2 + 149.IBPluginDependency + 149.ImportedFromIB2 + 150.IBPluginDependency + 150.ImportedFromIB2 + 19.IBPluginDependency + 19.ImportedFromIB2 + 195.IBPluginDependency + 195.ImportedFromIB2 + 196.IBPluginDependency + 196.ImportedFromIB2 + 197.IBPluginDependency + 197.ImportedFromIB2 + 198.IBPluginDependency + 198.ImportedFromIB2 + 199.IBPluginDependency + 199.ImportedFromIB2 + 200.IBEditorWindowLastContentRect + 200.IBPluginDependency + 200.ImportedFromIB2 + 200.editorWindowContentRectSynchronizationRect + 201.IBPluginDependency + 201.ImportedFromIB2 + 202.IBPluginDependency + 202.ImportedFromIB2 + 203.IBPluginDependency + 203.ImportedFromIB2 + 204.IBPluginDependency + 204.ImportedFromIB2 + 205.IBEditorWindowLastContentRect + 205.IBPluginDependency + 205.ImportedFromIB2 + 205.editorWindowContentRectSynchronizationRect + 206.IBPluginDependency + 206.ImportedFromIB2 + 207.IBPluginDependency + 207.ImportedFromIB2 + 208.IBPluginDependency + 208.ImportedFromIB2 + 209.IBPluginDependency + 209.ImportedFromIB2 + 210.IBPluginDependency + 210.ImportedFromIB2 + 211.IBPluginDependency + 211.ImportedFromIB2 + 212.IBPluginDependency + 212.ImportedFromIB2 + 212.editorWindowContentRectSynchronizationRect + 213.IBPluginDependency + 213.ImportedFromIB2 + 214.IBPluginDependency + 214.ImportedFromIB2 + 215.IBPluginDependency + 215.ImportedFromIB2 + 216.IBPluginDependency + 216.ImportedFromIB2 + 217.IBPluginDependency + 217.ImportedFromIB2 + 218.IBPluginDependency + 218.ImportedFromIB2 + 219.IBPluginDependency + 219.ImportedFromIB2 + 220.IBEditorWindowLastContentRect + 220.IBPluginDependency + 220.ImportedFromIB2 + 220.editorWindowContentRectSynchronizationRect + 221.IBPluginDependency + 221.ImportedFromIB2 + 23.IBPluginDependency + 23.ImportedFromIB2 + 236.IBPluginDependency + 236.ImportedFromIB2 + 239.IBPluginDependency + 239.ImportedFromIB2 + 24.IBEditorWindowLastContentRect + 24.IBPluginDependency + 24.ImportedFromIB2 + 24.editorWindowContentRectSynchronizationRect + 29.IBEditorWindowLastContentRect + 29.IBPluginDependency + 29.ImportedFromIB2 + 29.WindowOrigin + 29.editorWindowContentRectSynchronizationRect + 295.IBPluginDependency + 296.IBEditorWindowLastContentRect + 296.IBPluginDependency + 296.editorWindowContentRectSynchronizationRect + 297.IBPluginDependency + 298.IBPluginDependency + 346.IBPluginDependency + 346.ImportedFromIB2 + 348.IBPluginDependency + 348.ImportedFromIB2 + 349.IBEditorWindowLastContentRect + 349.IBPluginDependency + 349.ImportedFromIB2 + 349.editorWindowContentRectSynchronizationRect + 350.IBPluginDependency + 350.ImportedFromIB2 + 351.IBPluginDependency + 351.ImportedFromIB2 + 354.IBPluginDependency + 354.ImportedFromIB2 + 371.IBEditorWindowLastContentRect + 371.IBPluginDependency + 371.IBWindowTemplateEditedContentRect + 371.NSWindowTemplate.visibleAtLaunch + 371.editorWindowContentRectSynchronizationRect + 371.windowTemplate.maxSize + 372.IBPluginDependency + 375.IBPluginDependency + 376.IBEditorWindowLastContentRect + 376.IBPluginDependency + 377.IBPluginDependency + 388.IBEditorWindowLastContentRect + 388.IBPluginDependency + 389.IBPluginDependency + 390.IBPluginDependency + 391.IBPluginDependency + 392.IBPluginDependency + 393.IBPluginDependency + 394.IBPluginDependency + 395.IBPluginDependency + 396.IBPluginDependency + 397.IBPluginDependency + 398.IBPluginDependency + 399.IBPluginDependency + 400.IBPluginDependency + 401.IBPluginDependency + 402.IBPluginDependency + 403.IBPluginDependency + 404.IBPluginDependency + 405.IBPluginDependency + 406.IBPluginDependency + 407.IBPluginDependency + 408.IBPluginDependency + 409.IBPluginDependency + 410.IBPluginDependency + 411.IBPluginDependency + 412.IBPluginDependency + 413.IBPluginDependency + 414.IBPluginDependency + 415.IBPluginDependency + 416.IBPluginDependency + 417.IBPluginDependency + 418.IBPluginDependency + 419.IBPluginDependency + 450.IBPluginDependency + 451.IBEditorWindowLastContentRect + 451.IBPluginDependency + 452.IBPluginDependency + 453.IBPluginDependency + 454.IBPluginDependency + 457.IBPluginDependency + 459.IBPluginDependency + 460.IBPluginDependency + 462.IBPluginDependency + 465.IBPluginDependency + 466.IBPluginDependency + 485.IBPluginDependency + 490.IBPluginDependency + 491.IBEditorWindowLastContentRect + 491.IBPluginDependency + 492.IBPluginDependency + 496.IBPluginDependency + 497.IBEditorWindowLastContentRect + 497.IBPluginDependency + 498.IBPluginDependency + 499.IBPluginDependency + 5.IBPluginDependency + 5.ImportedFromIB2 + 500.IBPluginDependency + 501.IBPluginDependency + 502.IBPluginDependency + 503.IBPluginDependency + 504.IBPluginDependency + 505.IBPluginDependency + 506.IBPluginDependency + 507.IBPluginDependency + 508.IBEditorWindowLastContentRect + 508.IBPluginDependency + 509.IBPluginDependency + 510.IBPluginDependency + 511.IBPluginDependency + 512.IBPluginDependency + 513.IBPluginDependency + 514.IBPluginDependency + 515.IBPluginDependency + 516.IBPluginDependency + 517.IBPluginDependency + 56.IBPluginDependency + 56.ImportedFromIB2 + 57.IBEditorWindowLastContentRect + 57.IBPluginDependency + 57.ImportedFromIB2 + 57.editorWindowContentRectSynchronizationRect + 58.IBPluginDependency + 58.ImportedFromIB2 + 72.IBPluginDependency + 72.ImportedFromIB2 + 73.IBPluginDependency + 73.ImportedFromIB2 + 74.IBPluginDependency + 74.ImportedFromIB2 + 75.IBPluginDependency + 75.ImportedFromIB2 + 77.IBPluginDependency + 77.ImportedFromIB2 + 78.IBPluginDependency + 78.ImportedFromIB2 + 79.IBPluginDependency + 79.ImportedFromIB2 + 80.IBPluginDependency + 80.ImportedFromIB2 + 81.IBEditorWindowLastContentRect + 81.IBPluginDependency + 81.ImportedFromIB2 + 81.editorWindowContentRectSynchronizationRect + 82.IBPluginDependency + 82.ImportedFromIB2 + 83.IBPluginDependency + 83.ImportedFromIB2 + 92.IBPluginDependency + 92.ImportedFromIB2 + + + YES + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{522, 812}, {146, 23}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{436, 809}, {64, 6}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{753, 187}, {275, 113}} + com.apple.InterfaceBuilder.CocoaPlugin + + {{608, 612}, {275, 83}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{547, 180}, {254, 283}} + com.apple.InterfaceBuilder.CocoaPlugin + + {{187, 434}, {243, 243}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{608, 612}, {167, 43}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{753, 217}, {238, 103}} + com.apple.InterfaceBuilder.CocoaPlugin + + {{608, 612}, {241, 103}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{654, 239}, {194, 73}} + com.apple.InterfaceBuilder.CocoaPlugin + + {{525, 802}, {197, 73}} + {{380, 836}, {512, 20}} + com.apple.InterfaceBuilder.CocoaPlugin + + {74, 862} + {{6, 978}, {478, 20}} + com.apple.InterfaceBuilder.CocoaPlugin + {{604, 269}, {231, 43}} + com.apple.InterfaceBuilder.CocoaPlugin + {{475, 832}, {234, 43}} + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{746, 287}, {220, 133}} + com.apple.InterfaceBuilder.CocoaPlugin + + {{608, 612}, {215, 63}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{380, 496}, {480, 360}} + com.apple.InterfaceBuilder.CocoaPlugin + {{380, 496}, {480, 360}} + + {{33, 99}, {480, 360}} + {3.40282e+38, 3.40282e+38} + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + {{591, 420}, {83, 43}} + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + {{523, 2}, {178, 283}} + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + {{753, 197}, {170, 63}} + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + {{725, 289}, {246, 23}} + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + {{674, 260}, {204, 183}} + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + {{878, 180}, {164, 173}} + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + {{286, 129}, {275, 183}} + com.apple.InterfaceBuilder.CocoaPlugin + + {{23, 794}, {245, 183}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{452, 109}, {196, 203}} + com.apple.InterfaceBuilder.CocoaPlugin + + {{145, 474}, {199, 203}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + + + + YES + + + YES + + + + + YES + + + YES + + + + 532 + + + + YES + + Warfare_IncorporatedAppDelegate + NSObject + + window + NSWindow + + + IBProjectSource + Warfare_IncorporatedAppDelegate.h + + + + + YES + + NSApplication + NSResponder + + IBFrameworkSource + AppKit.framework/Headers/NSApplication.h + + + + NSApplication + + IBFrameworkSource + AppKit.framework/Headers/NSApplicationScripting.h + + + + NSApplication + + IBFrameworkSource + AppKit.framework/Headers/NSColorPanel.h + + + + NSApplication + + IBFrameworkSource + AppKit.framework/Headers/NSHelpManager.h + + + + NSApplication + + IBFrameworkSource + AppKit.framework/Headers/NSPageLayout.h + + + + NSApplication + + IBFrameworkSource + AppKit.framework/Headers/NSUserInterfaceItemSearching.h + + + + NSBrowser + NSControl + + IBFrameworkSource + AppKit.framework/Headers/NSBrowser.h + + + + NSControl + NSView + + IBFrameworkSource + AppKit.framework/Headers/NSControl.h + + + + NSDocument + NSObject + + YES + + YES + printDocument: + revertDocumentToSaved: + runPageLayout: + saveDocument: + saveDocumentAs: + saveDocumentTo: + + + YES + id + id + id + id + id + id + + + + IBFrameworkSource + AppKit.framework/Headers/NSDocument.h + + + + NSDocument + + IBFrameworkSource + AppKit.framework/Headers/NSDocumentScripting.h + + + + NSDocumentController + NSObject + + YES + + YES + clearRecentDocuments: + newDocument: + openDocument: + saveAllDocuments: + + + YES + id + id + id + id + + + + IBFrameworkSource + AppKit.framework/Headers/NSDocumentController.h + + + + NSFontManager + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSFontManager.h + + + + NSFormatter + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSFormatter.h + + + + NSMatrix + NSControl + + IBFrameworkSource + AppKit.framework/Headers/NSMatrix.h + + + + NSMenu + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSMenu.h + + + + NSMenuItem + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSMenuItem.h + + + + NSMovieView + NSView + + IBFrameworkSource + AppKit.framework/Headers/NSMovieView.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSAccessibility.h + + + + NSObject + + + + NSObject + + + + NSObject + + + + NSObject + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSDictionaryController.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSDragging.h + + + + NSObject + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSFontPanel.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSKeyValueBinding.h + + + + NSObject + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSNibLoading.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSOutlineView.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSPasteboard.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSSavePanel.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSTableView.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSToolbarItem.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSView.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSArchiver.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSClassDescription.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSError.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSFileManager.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSKeyValueCoding.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSKeyValueObserving.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSKeyedArchiver.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSObject.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSObjectScripting.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSPortCoder.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSRunLoop.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSScriptClassDescription.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSScriptKeyValueCoding.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSScriptObjectSpecifiers.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSScriptWhoseTests.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSThread.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSURL.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSURLConnection.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSURLDownload.h + + + + NSResponder + + IBFrameworkSource + AppKit.framework/Headers/NSInterfaceStyle.h + + + + NSResponder + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSResponder.h + + + + NSTableView + NSControl + + + + NSText + NSView + + IBFrameworkSource + AppKit.framework/Headers/NSText.h + + + + NSTextView + NSText + + IBFrameworkSource + AppKit.framework/Headers/NSTextView.h + + + + NSView + + IBFrameworkSource + AppKit.framework/Headers/NSClipView.h + + + + NSView + + + + NSView + + IBFrameworkSource + AppKit.framework/Headers/NSRulerView.h + + + + NSView + NSResponder + + + + NSWindow + + IBFrameworkSource + AppKit.framework/Headers/NSDrawer.h + + + + NSWindow + NSResponder + + IBFrameworkSource + AppKit.framework/Headers/NSWindow.h + + + + NSWindow + + IBFrameworkSource + AppKit.framework/Headers/NSWindowScripting.h + + + + + 0 + + com.apple.InterfaceBuilder.CocoaPlugin.macosx + + + + com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3 + + + YES + ../Warfare Incorporated.xcodeproj + 3 + + diff --git a/game/sdl/mac/hosthelpers.mm b/game/sdl/mac/hosthelpers.mm new file mode 100644 index 0000000..eb623cb --- /dev/null +++ b/game/sdl/mac/hosthelpers.mm @@ -0,0 +1,521 @@ +#import +#import +#include + +#if 0 +#import +#import +#import +#include + +#include +#include "iphone.h" +#include "input.h" +#include "base/log.h" +#include "base/thread.h" + +#import "game/iphone/wiviewcontroller.h" +#import "game/iphone/chatviewcontroller.h" +#include "game/ht.h" + +@interface IPhoneAppDelegate : NSObject +{ + UIWindow *m_window; + WiViewController *m_vcwi; + ChatViewController *m_vcchat; + wi::IChatController *m_pchat; + id m_view; + base::Thread *m_game_thread; + bool m_fExiting; + char *m_pszUdid; +} +@end + +- (void)initiateAsk:(const char *)title max:(int)max default:(const char *)def + keyboard:(int)keyboard secure:(BOOL)secure +{ + return [m_vcwi initiateAsk:title max:max default:def keyboard:keyboard + secure:secure]; +} + +- (void)getAskString:(char *)psz size:(int)cb +{ + return [m_vcwi getAskString:psz size:cb]; +} + +- (wi::IChatController *)getChatController +{ + if (m_pchat == NULL) { + m_pchat = new wi::ChatController(m_vcwi, m_vcchat); + } + return m_pchat; +} + +- (void)initiateWebView:(const char *)title withUrl:(const char *)url +{ + [m_vcwi initiateWebView:title withUrl:url]; +} + +#if 0 +- (void)application:(UIApplication *)app + willChangeStatusBarOrientation:(UIInterfaceOrientation)orientation + duration:(NSTimeInterval)duration +{ + // This prevents the view from autorotating to portrait in the simulator + if ((orientation == UIInterfaceOrientationPortrait) || + (orientation== UIInterfaceOrientationPortraitUpsideDown)) { + [app setStatusBarOrientation: + UIInterfaceOrientationLandscapeRight animated:NO]; + } +} +#endif + +- (void)applicationDidFinishLaunching:(UIApplication *)application +{ + // Create the window and view + + wi::g_appDelegate = self; + m_fExiting = false; + + // Set these here rather than in Info.plist, because devices with OS's + // before iPhone OS 2.1 don't honor the Info.plist settings. + // Hide the status bar. Unfortunately, the status bar area still + // eat events. No known workaround currently. + [application setStatusBarHidden:YES animated:NO]; + + // Tell the application object to turn off the screen dimming idle + // timer. + application.idleTimerDisabled = YES; + + CGRect frame = [[UIScreen mainScreen] bounds]; + m_window = [[UIWindow alloc] initWithFrame: frame]; + + // Create the Wi view controller, remember the view for shortcut purposes + m_vcwi = [[WiViewController alloc] initWithNibName:nil bundle:nil]; + m_view = [[m_vcwi view] retain]; + + m_vcchat = [[ChatViewController alloc] init:nil parent:m_view]; + m_pchat = NULL; + + // Show the window with table view + [m_window addSubview:m_view]; + [m_window makeKeyAndVisible]; + + // Must do this after makeKeyAndVisible, in order for it all to rotate + [application setStatusBarOrientation:UIInterfaceOrientationLandscapeRight + animated:NO]; + + // Alloc the key directories + [self allocPaths]; + + // Grab the udid + NSString *udid = [[UIDevice currentDevice] uniqueIdentifier]; + const char *pszUdid = [udid cStringUsingEncoding: + [NSString defaultCStringEncoding]]; + m_pszUdid = (char *)malloc(strlen(pszUdid) + 1); + strcpy(m_pszUdid, pszUdid); + + // Spin off a thread to run the game + m_game_thread = new base::Thread(); + m_game_thread->Start(wi::HostHelpers::GameThreadStart, NULL); + [m_vcwi setGameThread: m_game_thread]; +} + +- (wi::SpriteManager *)getSpriteManager +{ + return [m_view getSpriteManager]; +} + +- (void)setFormMgrs:(wi::FormMgr *)pfrmmSim input:(wi::FormMgr *)pfrmmInput +{ + [m_view setFormMgrs:pfrmmSim input:pfrmmInput]; +} + +- (const char *)getMissionPacksDir +{ + return m_pszMissionPacksDir; +} + +- (const char *)getMissionPackInfosDir +{ + return m_pszMissionPackInfosDir; +} + +- (const char *)getSaveGamesDir +{ + return m_pszSaveGamesDir; +} + +- (const char *)getPrefsFilename +{ + return m_pszPrefsFilename; +} + +- (const char *)getMainDataDir +{ + return m_pszMainDataDir; +} + +- (const char *)getTempDir +{ + return m_pszTempDir; +} + +- (const char *)getCompletesDir +{ + return m_pszCompletesDir; +} + +- (const char *)getUdid +{ + return m_pszUdid; +} + +- (bool)isExiting +{ + return m_fExiting; +} + +- (void)applicationDidBecomeActive:(UIApplication *)application +{ + // WI is now active (app launch, device turned on / unlocked) + + m_game_thread->Post(kidmAppSetFocus, NULL); +} + +- (void)applicationWillResignActive:(UIApplication *)application +{ + // WI is not active (app terminating, device turned off / locked) + // If already exiting, don't do anything special. + + if (m_fExiting) { + return; + } + + m_game_thread->Post(kidmAppKillFocus, NULL); +} + +- (void)applicationWillTerminate:(UIApplication *)application +{ + // This method is called when the user presses the device exit button. + // Ask the game thread to exit. + + m_fExiting = true; + m_game_thread->Post(kidmAppTerminate, NULL); + + // This will tell the thread to exit, and block until it does + delete m_game_thread; + m_game_thread = NULL; +} + +- (void)getSurfaceProperties:(wi::SurfaceProperties *)pprops +{ + [m_view getSurfaceProperties:pprops]; +} + +- (void)frameStart +{ + [m_view frameStart]; +} + +- (void)frameComplete:(int)cfrmm maps:(wi::UpdateMap **)apupd + rects:(wi::Rect *)arc scrolled:(bool)fScrolled +{ + [m_view frameComplete:cfrmm maps:apupd rects:arc scrolled:fScrolled]; +} + +- (void)resetScrollOffset +{ + [m_view resetScrollOffset]; +} + +- (wi::DibBitmap *)createFrontDibWithOrientation:(int)nDegreeOrientation + width:(int)cx height:(int)cy +{ + return [m_view createFrontDibWithOrientation:nDegreeOrientation + width:cx height:cy]; +} + +- (void)setPalette:(wi::Palette *)ppal +{ + [m_view setPalette:ppal]; +} +@end + +// +// C++ wrapper class around obj-c IPhoneApp class +// +#endif + +#include "game/sdl/hosthelpers.h" +#include "game/sdl/mac/machttpservice.h" + +namespace wi { + +char gszMainDataDir[PATH_MAX]; // data file (htdata832.pdb, htsfx.pdb) dir +char gszTempDir[PATH_MAX]; // temp file directory +char gszMissionPacksDir[PATH_MAX]; // downloaded mission packs +char gszMissionPackInfosDir[PATH_MAX]; +char gszSaveGamesDir[PATH_MAX]; // saved games +char gszPrefsFilename[PATH_MAX]; // game prefs +char gszCompletesDir[PATH_MAX]; // for "mission completed" tracking +HttpService *gphttp; + +bool HostHelpers::Init() { + // Get directories off .app + NSBundle *bundle = [NSBundle mainBundle]; + NSString *appDir = [bundle bundlePath]; + const char *pszAppDir = [appDir cStringUsingEncoding: + [NSString defaultCStringEncoding]]; + sprintf(gszMainDataDir, "%s/Contents/Resources", pszAppDir); + + // This returns the user's home directory; Library, and Documents are in it + NSString *homeDir = NSHomeDirectory(); + const char *pszHomeDir = [homeDir cStringUsingEncoding: + [NSString defaultCStringEncoding]]; + + strcpy(gszTempDir, "/tmp"); + sprintf(gszMissionPacksDir, "%s/Library/MissionPacks", pszHomeDir); + sprintf(gszMissionPackInfosDir, "%s/Library/MissionPackInfos", pszHomeDir); + sprintf(gszSaveGamesDir, "%s/Library/SaveGames", pszHomeDir); + sprintf(gszCompletesDir, "%s/Library/Completes", pszHomeDir); + sprintf(gszPrefsFilename, "%s/Library/prefs.bin", pszHomeDir); + + // Make the directories under Library + mkdir(gszMissionPacksDir, 0755); + mkdir(gszMissionPackInfosDir, 0755); + mkdir(gszSaveGamesDir, 0755); + mkdir(gszCompletesDir, 0755); + + // Mac specific http service + extern HttpService *gphttp; + gphttp = (HttpService *)new MacHttpService; + + return true; +} + +void HostHelpers::Cleanup() { + delete gphttp; +} + +const char *HostHelpers::GetMainDataDir() { + return gszMainDataDir; +} + +const char *HostHelpers::GetTempDir() { + return gszTempDir; +} + +const char *HostHelpers::GetMissionPacksDir() { + return gszMissionPacksDir; +} + +const char *HostHelpers::GetMissionPackInfosDir() { + return gszMissionPackInfosDir; +} + +const char *HostHelpers::GetSaveGamesDir() { + return gszSaveGamesDir; +} + +const char *HostHelpers::GetCompletesDir() { + return gszCompletesDir; +} + +const char *HostHelpers::GetPrefsFilename() { + return gszPrefsFilename; +} + +void HostHelpers::OpenUrl(const char *pszUrl) { +#if 0 + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + NSString *s = [NSString stringWithCString:pszUrl + encoding:[NSString defaultCStringEncoding]]; + [[UIApplication sharedApplication] openURL:[NSURL URLWithString:s]]; + [pool release]; +#else + Log("HostHelpers::OpenUrl not implemented yet"); +#endif +} + +void HostHelpers::Log(const char *pszFormat, va_list va) +{ +#if 0 + LOGX() << base::Log::vFormat(pszFormat, va); +#endif + + char sz[512]; + vsnprintf(sz, sizeof(sz), pszFormat, va); + + printf("%s\n", sz); +} + +void HostHelpers::Log(const char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + Log(pszFormat, va); + va_end(va); +} + +void HostHelpers::MessageBox(const char *pszFormat, va_list va) +{ + Log(pszFormat, va); +} + +void HostHelpers::Break() +{ + Log("BREAK!!"); +} + +void HostHelpers::GetSurfaceProperties(SurfaceProperties *pprops) +{ +#if 0 + [g_appDelegate getSurfaceProperties: pprops]; +#else + Log("HostHelpers::GetSurfaceProperties not implemented yet"); +#endif +} + +void HostHelpers::FrameStart() +{ +#if 0 + [g_appDelegate frameStart]; +#else + Log("HostHelpers::FrameStart not implemented yet"); +#endif +} + +void HostHelpers::FrameComplete(int cfrmm, UpdateMap **apupd, Rect *arc, + bool fScrolled) +{ +#if 0 + [g_appDelegate frameComplete:cfrmm maps:apupd rects:arc scrolled:fScrolled]; +#else + Log("HostHelpers::FrameComplete not implemented yet"); +#endif +} + +void HostHelpers::ResetScrollOffset() +{ +#if 0 + [g_appDelegate resetScrollOffset]; +#else + Log("HostHelpers::ResetScrollOffset not implemented yet"); +#endif +} + +void HostHelpers::SetFormMgrs(FormMgr *pfrmmSim, FormMgr *pfrmmInput) +{ +#if 0 + return [g_appDelegate setFormMgrs:pfrmmSim input:pfrmmInput]; +#else + Log("HostHelpers::SetFormMgrs not implemented yet"); +#endif +} + +DibBitmap *HostHelpers::CreateFrontDib(int cx, int cy, int nDegreeOrientation) +{ +#if 0 + return [g_appDelegate createFrontDibWithOrientation:nDegreeOrientation width:cx height:cy]; +#else + Log("HostHelpers::CreateFrontDib not implemented yet"); + return NULL; +#endif +} + +void HostHelpers::SetPalette(Palette *ppal) +{ +#if 0 + [g_appDelegate setPalette:ppal]; +#else + Log("HostHelpers::SetPalette not implemented yet"); +#endif +} + +static char gszUdid[20]; + +const char *HostHelpers::GetUdid() +{ +#if 0 + return [g_appDelegate getUdid]; +#else + // TODO(darrinm): talk to Scott about Udid requirements + char *pch = gszUdid; + if (*pch == 0) { + for (int i = 0; i < 19; i++) + *pch++ = '0' + GetAsyncRandom() % 10; + *pch = 0; + } + return gszUdid; +#endif +} + +bool HostHelpers::IsExiting() +{ +#if 0 + return [g_appDelegate isExiting]; +#else + Log("HostHelpers::IsExitig not implemented yet"); + return false; +#endif +} + +void HostHelpers::InitiateAsk(const char *title, int max, const char *def, + int keyboard, bool secure) +{ +#if 0 + BOOL s = secure ? YES : NO; + return [g_appDelegate initiateAsk:title max:max default:def + keyboard:keyboard secure:s]; +#else + Log("HostHelpers::InitiateAsk not implemented yet"); +#endif +} + +void HostHelpers::GetAskString(char *psz, int cb) +{ +#if 0 + return [g_appDelegate getAskString:psz size:cb]; +#else + Log("HostHelpers::GetAskString not implemented yet"); +#endif +} + +IChatController *HostHelpers::GetChatController() +{ +#if 0 + return [g_appDelegate getChatController]; +#else + Log("HostHelpers::GetChatController not implemented yet"); + return NULL; +#endif +} + +void HostHelpers::InitiateWebView(const char *title, const char *url) { +#if 0 + [g_appDelegate initiateWebView:title withUrl:url]; +#else + Log("HostHelpers::InitiateWebView not implemented yet"); +#endif +} + +void HostHelpers::GameThreadStart(void *pv) { + Log("Starting game..."); + wi::GameMain((char *)""); +} + +int HostHelpers::main(int argc, char **argv) +{ + // TODO(darrinm): should SDL call this? +#if 0 + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + return UIApplicationMain(argc, argv, nil, @"IPhoneAppDelegate"); +#else + Log("HostHelpers::main not implemented yet"); +#endif + return 0; +} + +} // namespace wi diff --git a/game/sdl/mac/icon.png b/game/sdl/mac/icon.png new file mode 100644 index 0000000..e8c8828 Binary files /dev/null and b/game/sdl/mac/icon.png differ diff --git a/game/sdl/mac/machttprequest.h b/game/sdl/mac/machttprequest.h new file mode 100644 index 0000000..ee44759 --- /dev/null +++ b/game/sdl/mac/machttprequest.h @@ -0,0 +1,50 @@ +#ifndef __MACHTTPREQUEST_H__ +#define __MACHTTPREQUEST_H__ + +#import +#import + +#include "game/httpservice.h" +#include "game/httprequest.h" +#include "base/thread.h" +#include "base/bytebuffer.h" + +namespace wi { +class MacHttpRequest; +} + +@interface ConnectionDelegate : NSObject { + NSURLConnection *conn_; + wi::MacHttpRequest *req_; +} +- (void)submit; +- (void)cancel; +- (void)connection:(NSURLConnection *)conn + didFailWithError:(NSError *)error; +- (void)connection:(NSURLConnection *)conn didReceiveData:(NSData *)data; +- (void)connectionDidFinishLoading:(NSURLConnection *)conn; +@end + +namespace wi { + +class MacHttpRequest : public HttpRequest, base::MessageHandler { +public: + MacHttpRequest(HttpResponseHandler *phandler); + ~MacHttpRequest(); + + void Submit(); + void Release(); + NSURLRequest *CreateNSURLRequest(); + void OnReceivedResponse(NSHTTPURLResponse *resp); + void OnReceivedData(NSData *data); + void OnFinishedLoading(); + void OnError(NSError *error); + +private: + HttpResponseHandler *handler_; + ConnectionDelegate *delegate_; +}; + +} // namespace wi + +#endif // __MACHTTPREQUEST_H__ diff --git a/game/sdl/mac/machttprequest.mm b/game/sdl/mac/machttprequest.mm new file mode 100644 index 0000000..2cc8dce --- /dev/null +++ b/game/sdl/mac/machttprequest.mm @@ -0,0 +1,156 @@ +#include "game/sdl/mac/machttprequest.h" +#include "base/thread.h" + +@implementation ConnectionDelegate + +- (id)initWithRequest:(wi::MacHttpRequest *)req { + self = [super init]; + if (self != nil) { + req_ = req; + conn_ = nil; + } + return self; +} + +- (void)dealloc { + [conn_ release]; + [super dealloc]; +} + +- (void)submit { + NSURLRequest *req = req_->CreateNSURLRequest(); + conn_ = [NSURLConnection + connectionWithRequest:req + delegate:self]; + [conn_ retain]; + [req release]; +} + +- (void)cancel { + [conn_ cancel]; + [conn_ release]; + conn_ = nil; +} + +- (void)connection:(NSURLConnection *)conn + didReceiveResponse:(NSURLResponse *)resp { + req_->OnReceivedResponse((NSHTTPURLResponse *)resp); +} + +- (void)connection:(NSURLConnection *)conn didReceiveData:(NSData *)data { + req_->OnReceivedData(data); +} + +- (void)connectionDidFinishLoading:(NSURLConnection *)conn { + req_->OnFinishedLoading(); +} + +- (void)connection:(NSURLConnection *)conn + didFailWithError:(NSError *)error { + NSLog(@"error: %@", error); + req_->OnError(error); +} +@end + +// C++ implementation of HttpRequest interface for Mac + +namespace wi { + +MacHttpRequest::MacHttpRequest(HttpResponseHandler *handler) : + handler_(handler), delegate_(nil) { +} + +MacHttpRequest::~MacHttpRequest() { +} + +void MacHttpRequest::Submit() { + delegate_ = [[ConnectionDelegate alloc] initWithRequest:this]; + [delegate_ submit]; +} + +void MacHttpRequest::Release() { + [delegate_ cancel]; + [delegate_ release]; + delegate_ = nil; + Dispose(); +} + +NSURLRequest *MacHttpRequest::CreateNSURLRequest() { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + NSMutableURLRequest *req = [[NSMutableURLRequest alloc] init]; + + // Set the url + NSString *s = [NSString stringWithCString:url_.c_str() + encoding:[NSString defaultCStringEncoding]]; + [req setURL:[NSURL URLWithString:s]]; + + // Set the method + s = [NSString stringWithCString:method_.c_str() + encoding:[NSString defaultCStringEncoding]]; + [req setHTTPMethod:s]; + + // Set the body + if (pbb_ != NULL) { + int cb; + void *pv = pbb_->Strip(&cb); + NSData *data = [NSData dataWithBytesNoCopy:(void *)pv length:cb]; + [req setHTTPBody:data]; + } + + // Set timeout + [req setTimeoutInterval:timeout_]; + + // Set cache policy + [req setCachePolicy:NSURLRequestReloadIgnoringCacheData]; + + // Set headers + Enum enm; + char szKey[128]; + while (headers_.EnumKeys(&enm, szKey, sizeof(szKey))) { + char szValue[256]; + if (headers_.GetValue(szKey, szValue, sizeof(szValue))) { + NSString *key = [NSString stringWithCString:szKey + encoding:[NSString defaultCStringEncoding]]; + NSString *value = [NSString stringWithCString:szValue + encoding:[NSString defaultCStringEncoding]]; + [req setValue:value forHTTPHeaderField:key]; + } + } + + // Done + [pool release]; + return req; +} + +void MacHttpRequest::OnReceivedResponse(NSHTTPURLResponse *resp) { + Map headers; + NSDictionary *dict = [resp allHeaderFields]; + for (NSString *k in dict) { + NSString *v = [dict objectForKey:k]; + headers.SetValue( + [k cStringUsingEncoding:[NSString defaultCStringEncoding]], + [v cStringUsingEncoding:[NSString defaultCStringEncoding]]); + } + int code = [resp statusCode]; + handler_->OnReceivedResponse(this, code, &headers); +} + +void MacHttpRequest::OnReceivedData(NSData *data) { + base::ByteBuffer bb; + bb.WriteBytes((const byte *)[data bytes], [data length]); + handler_->OnReceivedData(this, &bb); +} + +void MacHttpRequest::OnFinishedLoading() { + handler_->OnFinishedLoading(this); +} + +void MacHttpRequest::OnError(NSError *error) { + const char *psz = [[error localizedDescription] cStringUsingEncoding: + [NSString defaultCStringEncoding]]; + char szError[80]; + strncpyz(szError, psz, sizeof(szError)); + handler_->OnError(this, szError); +} + +} // namespace wi diff --git a/game/sdl/mac/machttpservice.h b/game/sdl/mac/machttpservice.h new file mode 100644 index 0000000..20e5287 --- /dev/null +++ b/game/sdl/mac/machttpservice.h @@ -0,0 +1,19 @@ +#ifndef __MACHTTPSERVICE_H__ +#define __MACHTTPSERVICE_H__ + +#include "inc/basictypes.h" +#include "game/httpservice.h" + +namespace wi { + +class MacHttpService : public HttpService { +public: + // HttpService methods + virtual HttpRequest *NewRequest(HttpResponseHandler *phandler); + virtual void SubmitRequest(HttpRequest *preq); + virtual void ReleaseRequest(HttpRequest *preq); +}; + +} // namespace wi + +#endif // __MACHTTPSERVICE_H__ diff --git a/game/sdl/mac/machttpservice.mm b/game/sdl/mac/machttpservice.mm new file mode 100644 index 0000000..bc56a9b --- /dev/null +++ b/game/sdl/mac/machttpservice.mm @@ -0,0 +1,20 @@ +#include "game/sdl/mac/machttpservice.h" +#include "game/sdl/mac/machttprequest.h" + +namespace wi { + +HttpRequest *MacHttpService::NewRequest(HttpResponseHandler *phandler) { + return new MacHttpRequest(phandler); +} + +void MacHttpService::SubmitRequest(HttpRequest *preq) { + MacHttpRequest *preqT = (MacHttpRequest *)preq; + preqT->Submit(); +} + +void MacHttpService::ReleaseRequest(HttpRequest *preq) { + MacHttpRequest *preqT = (MacHttpRequest *)preq; + preqT->Release(); +} + +} // namespace wi diff --git a/game/sdl/mac/wi.icns b/game/sdl/mac/wi.icns new file mode 100644 index 0000000..6d8a28a Binary files /dev/null and b/game/sdl/mac/wi.icns differ diff --git a/game/sdl/main.cpp b/game/sdl/main.cpp new file mode 100644 index 0000000..fa3450d --- /dev/null +++ b/game/sdl/main.cpp @@ -0,0 +1,33 @@ +// Platform-specific entrypoints (e.g. mac/SDLMain.m) do early platform-specific +// setup and establish a uniform 'SDL' environment. Then they invoke SDL_main +// (see below) which does platform-independent SDL setup and calls the main +// game entrypoint wi::GameMain. +// +// Further platform-specific setup is done as the game calls Host* interfaces +// as it initializes. + +#include "game/ht.h" +#include "game/sdl/sdleventserver.h" +#include "base/thread.h" + +#if 0 +/* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */ +static void quit(int rc) +{ + SDL_Quit(); + exit(rc); +} +#endif + +#ifdef __cplusplus +extern "C" +#endif +int SDL_main(int argc, char *argv[]) +{ + // Set up the main thread as the SDL event thread. + base::Thread::current().set_ss(new wi::SdlEventServer()); + + // TODO(darrinm): pass args through + wi::GameMain((char *)""); + return 0; +} diff --git a/game/sdl/nacl/common.mk b/game/sdl/nacl/common.mk new file mode 100644 index 0000000..daeb1c3 --- /dev/null +++ b/game/sdl/nacl/common.mk @@ -0,0 +1,102 @@ +# Copyright (c) 2011, The Native Client Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# Common makefile for the examples. This has some basic variables, such as +# CC (the compiler) and some suffix rules such as .c.o. +# +# The main purpose of this makefile component is to demonstrate building a +# Native Client module (.nexe) + +.SUFFIXES: .c .cc .cpp .o + +.PHONY: check_variables + +ifeq ($(origin OS), undefined) + ifeq ($(shell uname -s), Darwin) + OS=Darwin + else + OS=$(shell uname -o) + endif +endif + +ifeq ($(OS), $(filter $(OS), Windows_NT Cygwin)) + PLATFORM = win + TARGET = x86 +endif +ifeq ($(OS), $(filter $(OS), Darwin MACOS)) + PLATFORM = mac + TARGET = x86 +endif + +# Look for 'Linux' in the $(OS) string. $(OS) is assumed to be a Linux +# variant if the result of $(findstring) is not empty. +ifneq (, $(findstring Linux, $(OS))) + PLATFORM = linux + TARGET = x86 +endif + +PYTHON ?= /usr/bin/python +NACL_SDK_ROOT ?= /Users/darrin/src/native_client_sdk_0_3_841 + +NACL_TOOLCHAIN_DIR = toolchain/$(PLATFORM)_$(TARGET) + +CC = $(NACL_SDK_ROOT)/$(NACL_TOOLCHAIN_DIR)/bin/nacl-gcc +CPP = $(NACL_SDK_ROOT)/$(NACL_TOOLCHAIN_DIR)/bin/nacl-g++ +NACL_STRIP = $(NACL_SDK_ROOT)/$(NACL_TOOLCHAIN_DIR)/bin/nacl-strip + +CFLAGS = -Wall -Wno-long-long -pthread -Werror +OPT_FLAGS = -O2 +DEBUG_FLAGS = -g + +$(OUTDIR)/%_x86_32_dbg.o: %.c + $(CC) $(CFLAGS) -m32 $(INCLUDES) $(DEBUG_FLAGS) -c -o $@ $< + +$(OUTDIR)/%_x86_32_dbg.o: %.cc + $(CPP) $(CFLAGS) -m32 $(INCLUDES) $(DEBUG_FLAGS) -c -o $@ $< + +$(OUTDIR)/%_x86_64_dbg.o: %.c + $(CC) $(CFLAGS) -m64 $(INCLUDES) $(DEBUG_FLAGS) -c -o $@ $< + +$(OUTDIR)/%_x86_64_dbg.o: %.cc + $(CPP) $(CFLAGS) -m64 $(INCLUDES) $(DEBUG_FLAGS) -c -o $@ $< + +$(OUTDIR)/%_x86_32.o: %.c + $(CC) $(CFLAGS) -m32 $(INCLUDES) $(OPT_FLAGS) -c -o $@ $< + +$(OUTDIR)/%_x86_32.o: %.cc + $(CPP) $(CFLAGS) -m32 $(INCLUDES) $(OPT_FLAGS) -c -o $@ $< + +$(OUTDIR)/%_x86_32.o: %.cpp + $(CPP) $(CFLAGS) -m32 $(INCLUDES) $(OPT_FLAGS) -c -o $@ $< + +$(OUTDIR)/%_x86_64.o: %.c + $(CC) $(CFLAGS) -m64 $(INCLUDES) $(OPT_FLAGS) -c -o $@ $< + +$(OUTDIR)/%_x86_64.o: %.cc + $(CPP) $(CFLAGS) -m64 $(INCLUDES) $(OPT_FLAGS) -c -o $@ $< + +$(OUTDIR)/%_x86_64.o: %.cpp + $(CPP) $(CFLAGS) -m64 $(INCLUDES) $(OPT_FLAGS) -c -o $@ $< + +# Generate list of .o files based on the list of .c and .cc files +OBJECTS_X86_32 = $(CFILES:%.c=%_x86_32.o) $(CCFILES:%.cc=%_x86_32.o) +OBJECTS_X86_64 = $(CFILES:%.c=%_x86_64.o) $(CCFILES:%.cc=%_x86_64.o) +OBJECTS_X86_32_DBG = $(CFILES:%.c=%_x86_32_dbg.o) $(CCFILES:%.cc=%_x86_32_dbg.o) +OBJECTS_X86_64_DBG = $(CFILES:%.c=%_x86_64_dbg.o) $(CCFILES:%.cc=%_x86_64_dbg.o) + +# Make sure certain variables are defined. This rule is set as a dependency +# for all the .nexe builds in the examples. +check_variables: +ifeq ($(origin OS), undefined) + @echo "Error: OS is undefined" ; exit 42 +endif +ifeq ($(origin PLATFORM), undefined) + @echo "Error: PLATFORM is undefined (OS = $(OS))" ; exit 42 +endif +ifeq ($(origin TARGET), undefined) + @echo "Error: TARGET is undefined (OS = $(OS))" ; exit 42 +endif +ifeq ($(origin NACL_SDK_ROOT), undefined) + @echo "Error: NACL_SDK_ROOT is undefined" ; exit 42 +endif diff --git a/game/sdl/nacl/generate_nmf.py b/game/sdl/nacl/generate_nmf.py new file mode 100755 index 0000000..f7173cf --- /dev/null +++ b/game/sdl/nacl/generate_nmf.py @@ -0,0 +1,60 @@ +#!/usr/bin/python +# +# Copyright (c) 2011, The Native Client Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import sys +import optparse + +# This script generates a JSON .nmf file, which provides the mapping to indicate +# which .nexe file to load and execute for a particular architecture. +# The script must have -nmf as an option, which designates the name +# of the .nmf file to be generated. +# One or more nexes must be specified on the command line. Each +# nexe file is preceded by an argument that specifies the architecture +# that the nexe is associated with: --x86-64, --x86-32, --arm. +# +# For example: +# generate_nmf.py --nmf test.nmf --x86-64 hello_world_x86-64.nexe \ +# --x86-32 hello32.nexe +# will create test.nmf that contains 2 entries, while +# +# generate_nmf.py --nmf hello.nmf --arm arm.nexe +# +# will create hello.nmf with a single entry. + +# Note: argv has been passed in without the program name in argv[0] +def main(argv): + parser = optparse.OptionParser() + parser.add_option('--nmf', dest='nmf_file', help='nmf file to generate') + parser.add_option('--x86-64', dest='x86_64', help='x86_64 nexe') + parser.add_option('--x86-32', dest='x86_32', help='x86_32 nexe') + parser.add_option('--arm', dest='arm', help='arm nexe') + + (options, args) = parser.parse_args(argv) + + if options.nmf_file == None: + parser.error("nmf file not specified. Use --nmf") + # Make sure that not all nexes are None -- i.e. at least one was specified. + if options.x86_64 == None and options.x86_32 == None and options.arm == None: + parser.error("No nexe files were specified") + + nmf_file = open(options.nmf_file, 'w') + nmf_file.write('{\n') + nmf_file.write(' "nexes": {\n') + + # Output an entry in the manifest file for each specified architecture + if options.x86_64: + nmf_file.write(' "x86-64": "%s",\n' % options.x86_64) + if options.x86_32: + nmf_file.write(' "x86-32": "%s",\n' % options.x86_32) + if options.arm: + nmf_file.write(' "arm": "%s",\n' % options.arm) + + nmf_file.write(' }\n') + nmf_file.write('}\n') + nmf_file.close() + +if __name__ == '__main__': + main(sys.argv[1:]) diff --git a/game/sdl/nacl/wi.cpp b/game/sdl/nacl/wi.cpp new file mode 100644 index 0000000..422e332 --- /dev/null +++ b/game/sdl/nacl/wi.cpp @@ -0,0 +1,150 @@ +/// @file wi.cc +/// This example demonstrates loading, running and scripting a very simple NaCl +/// module. To load the NaCl module, the browser first looks for the +/// CreateModule() factory method (at the end of this file). It calls +/// CreateModule() once to load the module code from your .nexe. After the +/// .nexe code is loaded, CreateModule() is not called again. +/// +/// Once the .nexe code is loaded, the browser than calls the CreateInstance() +/// method on the object returned by CreateModule(). It calls CreateInstance() +/// each time it encounters an tag that references your NaCl module. +/// +/// When the browser encounters JavaScript that references your NaCl module, it +/// calls the GetInstanceObject() method on the object returned from +/// CreateInstance(). In this example, the returned object is a subclass of +/// ScriptableObject, which handles the scripting support. + +#include +#include +#include +#include +#include +#include + +/// These are the method names as JavaScript sees them. Add any methods for +/// your class here. +namespace { +// A method consists of a const char* for the method ID and the method's +// declaration and implementation. +// TODO(sdk_user): 1. Add the declarations of your method IDs. + +// TODO(sdk_user): 2. Implement the methods that correspond to your method IDs. +} // namespace + +// Note to the user: This glue code reflects the current state of affairs. It +// may change. In particular, interface elements marked as deprecated will +// disappear sometime in the near future and replaced with more elegant +// interfaces. As of the time of this writing, the new interfaces are not +// available so we have to provide this code as it is written below. + +/// This class exposes the scripting interface for this NaCl module. The +/// HasMethod method is called by the browser when executing a method call on +/// the object. The name of the JavaScript function (e.g. "fortyTwo") is +/// passed in the |method| paramter as a string pp::Var. If HasMethod() +/// returns |true|, then the browser will call the Call() method to actually +/// invoke the method. +class WiScriptableObject : public pp::deprecated::ScriptableObject { + public: + /// Called by the browser to decide whether @a method is provided by this + /// plugin's scriptable interface. + /// @param[in] method The name of the method + /// @param[out] exception A pointer to an exception. May be used to notify + /// the browser if an exception occurs. + /// @return true iff @a method is one of the exposed method names. + virtual bool HasMethod(const pp::Var& method, pp::Var* exception); + + /// Invoke the function associated with @a method. The argument list passed + /// in via JavaScript is marshalled into a vector of pp::Vars. None of the + /// functions in this example take arguments, so this vector is always empty. + /// @param[in] method The name of the method to be invoked. + /// @param[in] args The arguments to be passed to the method. + /// @param[out] exception A pointer to an exception. May be used to notify + /// the browser if an exception occurs. + /// @return true iff @a method was called successfully. + virtual pp::Var Call(const pp::Var& method, + const std::vector& args, + pp::Var* exception); +}; + +bool WiScriptableObject::HasMethod(const pp::Var& method, + pp::Var* exception) { + if (!method.is_string()) { + return false; + } + std::string method_name = method.AsString(); + // TODO(sdk_user): 3. Make this function return true iff method_name is equal + // to any of your method IDs. + bool has_method = false; + return has_method; +} + +pp::Var WiScriptableObject::Call(const pp::Var& method, + const std::vector& args, + pp::Var* exception) { + if (!method.is_string()) { + return pp::Var(); + } + std::string method_name = method.AsString(); + // TODO(sdk_user): 4. Make this function call whatever method has method_name + // as its method ID. + return pp::Var(); +} + +/// The Instance class. One of these exists for each instance of your NaCl +/// module on the web page. The browser will ask the Module object to create +/// a new Instance for each occurence of the tag that has these +/// attributes: +/// type="application/x-nacl" +/// nexes="ARM: wi_arm.nexe +/// ..." +/// The Instance can return a ScriptableObject representing itself. When the +/// browser encounters JavaScript that wants to access the Instance, it calls +/// the GetInstanceObject() method. All the scripting work is done though +/// the returned ScriptableObject. +class WiInstance : public pp::Instance { + public: + /// The constructor creates the plugin-side instance. + /// @param[in] instance the handle to the browser-side plugin instance. + explicit WiInstance(PP_Instance instance) : pp::Instance(instance) + {} + virtual ~WiInstance() {} + + /// The browser calls this function to get a handle, in form of a pp::Var, + /// to the plugin-side scriptable object. The pp::Var takes over ownership + /// of said scriptable, meaning the browser can call its destructor. The + /// WiScriptableObject is the plugin-side representation of that + /// scriptable object. + /// @return The browser's handle to the plugin side instance. + virtual pp::Var GetInstanceObject() { + WiScriptableObject* hw_object = + new WiScriptableObject(); + return pp::Var(this, hw_object); + } +}; + +/// The Module class. The browser calls the CreateInstance() method to create +/// an instance of your NaCl module on the web page. The browser creates a new +/// instance for each tag with type="application/x-nacl". +class WiModule : public pp::Module { + public: + WiModule() : pp::Module() {} + virtual ~WiModule() {} + + /// Create and return a WiInstance object. + /// @param[in] instance The browser-side instance. + /// @return the plugin-side instance. + virtual pp::Instance* CreateInstance(PP_Instance instance) { + return new WiInstance(instance); + } +}; + +namespace pp { +/// Factory function called by the browser when the module is first loaded. +/// The browser keeps a singleton of this module. It calls the +/// CreateInstance() method on the object you return to make instances. There +/// is one instance per tag on the page. This is the main binding +/// point for your NaCl module with the browser. +Module* CreateModule() { + return new WiModule(); +} +} // namespace pp diff --git a/game/sdl/nacl/wi.html b/game/sdl/nacl/wi.html new file mode 100644 index 0000000..35cffdf --- /dev/null +++ b/game/sdl/nacl/wi.html @@ -0,0 +1,72 @@ + + + + Wi! + + + + + +

Native Client Module Wi

+

+ + +

+ +

Status

+
NO-STATUS
+ + diff --git a/game/sdl/savegame.cpp b/game/sdl/savegame.cpp new file mode 100644 index 0000000..498005d --- /dev/null +++ b/game/sdl/savegame.cpp @@ -0,0 +1,330 @@ +#include "../ht.h" +#include +#include +#include +//#include +#include +#include +#include +#include +#include +#include "hosthelpers.h" + +namespace wi { + +class FileStream : public Stream +{ +public: + FileStream(); + ~FileStream(); + bool Init(char *pszMode, char *pszFile, char *pszDelete, char *pszFinal); + + virtual void Close(); + virtual dword Read(void *pv, dword cb); + virtual dword Write(void *pv, dword cb); + virtual bool IsSuccess(); + +private: + bool m_fSuccess; + FILE *m_pf; + char m_szFile[PATH_MAX]; + char m_szDelete[PATH_MAX]; + char m_szFinal[PATH_MAX]; +}; + +FileStream::FileStream() +{ + m_fSuccess = true; + m_pf = NULL; + m_szFile[0] = 0; + m_szDelete[0] = 0; + m_szFinal[0] = 0; +} + +FileStream::~FileStream() +{ + Assert(m_pf == NULL); +} + +bool FileStream::Init(char *pszMode, char *pszFile, char *pszDelete, char *pszFinal) +{ + m_pf = fopen(pszFile, pszMode); + if (m_pf == NULL) + return false; + strcpy(m_szFile, pszFile); + if (pszDelete != NULL) + strcpy(m_szDelete, pszDelete); + if (pszFinal != NULL) + strcpy(m_szFinal, pszFinal); + return true; +} + +void FileStream::Close() +{ + fclose(m_pf); + m_pf = NULL; + + // Delete and rename file if no error + + if (m_fSuccess) { + if (m_szDelete[0] != 0) { + unlink(m_szDelete); + } + if (m_szFinal[0] != 0) { + rename(m_szFile, m_szFinal); + } + } +} + +dword FileStream::Read(void *pv, dword cb) +{ + size_t cbT = fread(pv, 1, cb, m_pf); + if (cb != cbT) + m_fSuccess = false; + return cbT; +} + +dword FileStream::Write(void *pv, dword cb) +{ + size_t cbT = fwrite(pv, 1, cb, m_pf); + if (cb != cbT) + m_fSuccess = false; + return cbT; +} + +bool FileStream::IsSuccess() +{ + return m_fSuccess; +} + +void PrependSavesDirectory(char *pszIn, char *pszOut) +{ + strcpy(pszOut, HostHelpers::GetSaveGamesDir()); + strcat(pszOut, "/"); + strcat(pszOut, pszIn); +} + +bool FindSaveGame(int nGame, char *psz, int cb, int *pc = NULL) +{ + if (pc != NULL) + *pc = 0; + + // This is the prefix of the file being looked for + + char szCompare[PATH_MAX]; + sprintf(szCompare, "htsave%d_", nGame); + int cchCompare = strlen(szCompare); + + // This is the special save game that is only used + // when the game exits and reloads right away + + char szReinitializeSave[20]; + sprintf(szReinitializeSave, "htsave%d_", knGameReinitializeSave); + int cchReinitializeSave = strlen(szReinitializeSave); + + // Enum files in this directory + + char szFileSpec[PATH_MAX]; + PrependSavesDirectory("", szFileSpec); + DIR *pdir = opendir(szFileSpec); + if (pdir == NULL) { + return false; + } + + int c = 0; + dirent *pdent; + while ((pdent = readdir(pdir)) != NULL) { + // Only consider save games, because if the desired save game is + // not found, we need to count "slots". + + if (strncmp("htsave", pdent->d_name, 6) != 0) { + continue; + } + + // Save game found? + + if (strncmp(szCompare, pdent->d_name, cchCompare) == 0) { + if (psz != NULL) + strncpyz(psz, pdent->d_name, cb); + closedir(pdir); + return true; + } + + // Count save games but don't count the temporary "Reinitialize" + // saved game + + if (strncmp(szReinitializeSave, pdent->d_name, + cchReinitializeSave) == 0) { + continue; + } + + // Count this save game as a slot + + c++; + } + closedir(pdir); + + // Didn't find the saved game, but did count the number of occupied slots + + if (pc != NULL) + *pc = c; + return false; +} + +int HostGetSaveGameCount() +{ + int c; + FindSaveGame(-1, NULL, 0, &c); + return c; +} + +bool HostGetSaveGameName(int nGame, char *psz, int cb, Date *pdate, int *pnHours24, int *pnMinutes, int *pnSeconds) +{ + // Find the game + + char szT[PATH_MAX]; + if (!FindSaveGame(nGame, szT, sizeof(szT))) { + strncpyz(psz, "-- Empty --", cb); + return false; + } + + char szPath[PATH_MAX]; + PrependSavesDirectory(szT, szPath); + + // Get the creation time in hours24, minutes + + struct stat st; + if (stat(szPath, &st) > 0) { + return false; + } + struct tm *ptm = localtime(&st.st_mtime); + *pnHours24 = ptm->tm_hour; + *pnMinutes = ptm->tm_min; + *pnSeconds = ptm->tm_sec; + pdate->nDay = ptm->tm_mday; + pdate->nMonth = ptm->tm_mon; + pdate->nYear = ptm->tm_year + 1900; + + // Copy over filename, lose prefix + + char *pszName = strchr(szT, '_') + 1; + int cbName = strlen(pszName) - 4 + 1; + strncpyz(psz, pszName, _min(cb, cbName)); + + // restore '#' to ':' + + char *pchInvalid = psz; + do { + pchInvalid = strchr(pchInvalid, '#'); + if (pchInvalid != 0) + *pchInvalid = ':'; + } while (pchInvalid != 0); + + return true; +} + +Stream *HostNewSaveGameStream(int nGame, char *pszName) +{ + // Get the old file name - we'll delete this if successful + + char szOld[PATH_MAX]; + char szOldFull[PATH_MAX]; + if (!FindSaveGame(nGame, szOld, sizeof(szOld))) { + szOldFull[0] = 0; + } else { + PrependSavesDirectory(szOld, szOldFull); + } + + // New file name + + char szNew[PATH_MAX]; + sprintf(szNew, "htsave%d_%s.bin", nGame, pszName); + + // windows disallows ':' in a filename, so sub those out + + char *pchInvalid = szNew; + do { + pchInvalid = strchr(pchInvalid, ':'); + if (pchInvalid != 0) + *pchInvalid = '#'; + } while (pchInvalid != 0); + + char szNewFull[PATH_MAX]; + PrependSavesDirectory(szNew, szNewFull); + + // Get stream over temp file + + FileStream *pstm = new FileStream(); + if (pstm == NULL) + return NULL; + + // If save is successful, szOld will be deleted and httempsave.bin + // will be renamed to szNew + + char szTempSaveFull[PATH_MAX]; + PrependSavesDirectory("httempsave.bin", szTempSaveFull); + + if (!pstm->Init("wb", szTempSaveFull, szOldFull, szNewFull)) { + delete pstm; + return NULL; + } + + return (Stream *)pstm; +} + +Stream *HostOpenSaveGameStream(int nGame, bool fDelete) +{ + char szT[PATH_MAX]; + if (!FindSaveGame(nGame, szT, sizeof(szT))) + return NULL; + + // rename to a temporary file before opening + + char szTFull[PATH_MAX]; + PrependSavesDirectory(szT, szTFull); + char szTempNameFull[PATH_MAX]; + PrependSavesDirectory(kszTempName, szTempNameFull); + rename(szTFull, szTempNameFull); + + // Get stream over temp file + + FileStream *pstm = new FileStream(); + if (pstm == NULL) + return NULL; + + // If load is successful, and fDelete is True szTempName will be deleted + // if fDelete is false szTempName will be renamed to szSaveGame + + if (!pstm->Init("rb", szTempNameFull, fDelete ? szTempNameFull : NULL, fDelete ? NULL : szTFull)) { + delete pstm; + return NULL; + } + + return (Stream *)pstm; +} + +bool HostDeleteSaveGame(char *psz, int nGame) +{ + // if nGame is > 0, delete that, otherwise if psz is non-null delete that + // return true if we deleted something + + char szSaveGame[PATH_MAX]; + + if (psz == NULL) { + if (!FindSaveGame(nGame, szSaveGame, sizeof(szSaveGame))) + return false; + } else { + strncpyz(szSaveGame, psz, sizeof(szSaveGame)); + } + + char szSaveGameFull[PATH_MAX]; + PrependSavesDirectory(szSaveGame, szSaveGameFull); + + if (psz != NULL) { + unlink(szSaveGameFull); + return true; + } + return false; +} + +} // namespace wi diff --git a/game/sdl/sdleventserver.h b/game/sdl/sdleventserver.h new file mode 100644 index 0000000..d3d80b9 --- /dev/null +++ b/game/sdl/sdleventserver.h @@ -0,0 +1,69 @@ +#ifndef __SDLEVENTSERVER_H__ +#define __SDLEVENTSERVER_H__ + +#include "inc/basictypes.h" +#include "base/thread.h" +#include "base/selectserver.h" +#include "game/sdl/sysmessages.h" +#include + +namespace wi { + +class SdlEventServer : public base::SelectServer { +public: + virtual bool Wait(long64 ctWait, bool fProcessIO = true) { + // Map ctWait (100th's sec) to timeout (milliseconds) + int timeout = -1; + if (ctWait != base::kctForever) { + timeout = ctWait * 10; + } + + // SDL_WaitEventTimeout(), with the addition of socket i/o + Uint32 expiration = 0; + if (timeout > 0) { + expiration = SDL_GetTicks() + timeout; + } + + wait_ = true; + while (wait_) { + // This pumps native events + SDL_PumpEvents(); + + // Check for a single event. Peek here, let the caller Get. + SDL_Event event; + switch (SDL_PeepEvents(&event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, + SDL_LASTEVENT)) { + case -1: + // App is exiting, or there is an error + return false; + case 1: + // There is an event + base::Thread::current().Post(kidmSdlEvent, NULL); + return true; + case 0: + // No SDL event. poll for and dispatch network events, if any + SelectServer::Wait(0, fProcessIO); + + // Polling and no events? + if (timeout == 0) { + return true; + } + + // Timeout expired and no events? + if (timeout > 0 && ((int) (SDL_GetTicks() - expiration) >= 0)) { + return true; + } + + // This is how SDL_WaitEventTimeout() does waiting. + // 10ms is fine for this game. + SDL_Delay(10); + break; + } + } + } + virtual void WakeUp() { wait_ = false; } +}; + +} // namespace wi + +#endif // __SDLEVENTSERVER_H__ diff --git a/game/sdl/sdlhttprequest.cpp b/game/sdl/sdlhttprequest.cpp new file mode 100644 index 0000000..94132d2 --- /dev/null +++ b/game/sdl/sdlhttprequest.cpp @@ -0,0 +1,179 @@ +#include "game/sdl/sdlhttprequest.h" +#include "game/sdl/hosthelpers.h" +#include "base/thread.h" + +// C++ implementation of HttpRequest interface for SDL + +namespace wi { + +#if 0 // TODO(darrinm) +SdlHttpRequest::SdlHttpRequest(HttpResponseHandler *handler) : + handler_(handler), delegate_(nil) { +#else +SdlHttpRequest::SdlHttpRequest(HttpResponseHandler *handler) { + LOG() << "SdlHttpRequest constructor not implemented yet"; +#endif +} + +SdlHttpRequest::~SdlHttpRequest() { +} + +void SdlHttpRequest::Submit() { +#if 0 + delegate_ = [[ConnectionDelegate alloc] initWithRequest:this]; + [delegate_ performSelectorOnMainThread:@selector(submit) + withObject:nil waitUntilDone: NO]; +#else + LOG() << "SdlHttpRequest::Submit not implemented yet"; +#endif +} + +void SdlHttpRequest::Release() { +#if 0 + // This can cause a deadlock when exiting because of how the main thread + // is synchronizing with the game thread to exit before it does + + if (!Sdl::IsExiting()) { + [delegate_ performSelectorOnMainThread:@selector(cancel) + withObject:nil waitUntilDone: YES]; + [delegate_ release]; + } + delegate_ = nil; + thread_.Clear(this); + Dispose(); +#else + LOG() << "SdlHttpRequest::Release not implemented yet"; +#endif +} + +#if 0 // TODO(darrinm) +NSURLRequest *SdlHttpRequest::CreateNSURLRequest() { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + NSMutableURLRequest *req = [[NSMutableURLRequest alloc] init]; + + // Set the url + NSString *s = [NSString stringWithCString:url_.c_str() + encoding:[NSString defaultCStringEncoding]]; + [req setURL:[NSURL URLWithString:s]]; + + // Set the method + s = [NSString stringWithCString:method_.c_str() + encoding:[NSString defaultCStringEncoding]]; + [req setHTTPMethod:s]; + + // Set the body + if (pbb_ != NULL) { + int cb; + void *pv = pbb_->Strip(&cb); + NSData *data = [NSData dataWithBytesNoCopy:(void *)pv length:cb]; + [req setHTTPBody:data]; + } + + // Set timeout + [req setTimeoutInterval:timeout_]; + + // Set cache policy + [req setCachePolicy:NSURLRequestReloadIgnoringCacheData]; + + // Set headers + Enum enm; + char szKey[128]; + while (headers_.EnumKeys(&enm, szKey, sizeof(szKey))) { + char szValue[256]; + if (headers_.GetValue(szKey, szValue, sizeof(szValue))) { + NSString *key = [NSString stringWithCString:szKey + encoding:[NSString defaultCStringEncoding]]; + NSString *value = [NSString stringWithCString:szValue + encoding:[NSString defaultCStringEncoding]]; + [req setValue:value forHTTPHeaderField:key]; + } + } + + // Done + [pool release]; +} +#endif + +void SdlHttpRequest::OnMessage(base::Message *pmsg) { +#if 0 // TODO(darrinm) + switch (pmsg->id) { + case kidmReceivedResponse: + { + ReceivedResponseParams *pparams = + (ReceivedResponseParams *)pmsg->data; + handler_->OnReceivedResponse(this, pparams->code, + &pparams->headers); + delete pparams; + } + break; + + case kidmReceivedData: + { + ReceivedDataParams *pparams = + (ReceivedDataParams *)pmsg->data; + handler_->OnReceivedData(this, &pparams->bb); + delete pparams; + } + break; + + case kidmFinishedLoading: + handler_->OnFinishedLoading(this); + break; + + case kidmError: + { + ErrorParams *pparams = (ErrorParams *)pmsg->data; + handler_->OnError(this, pparams->szError); + delete pparams; + } + break; + } +#endif +} + +#if 0 // TODO(darrinm) +void SdlHttpRequest::OnReceivedResponse(NSHTTPURLResponse *resp) { + // Called on main thread. Populate ReceivedResponseParams + ReceivedResponseParams *pparams = new ReceivedResponseParams; + NSDictionary *dict = [resp allHeaderFields]; + for (NSString *k in dict) { + NSString *v = [dict objectForKey:k]; + pparams->headers.SetValue( + [k cStringUsingEncoding:[NSString defaultCStringEncoding]], + [v cStringUsingEncoding:[NSString defaultCStringEncoding]]); + } + pparams->code = [resp statusCode]; + + // Post this to the game thread + thread_.Post(kidmReceivedResponse, this, pparams); +} + +void SdlHttpRequest::OnReceivedData(NSData *data) { + // Called on main thread. Populate ReceivedDataParams + ReceivedDataParams *pparams = new ReceivedDataParams; + pparams->bb.WriteBytes((const byte *)[data bytes], [data length]); + + // Post this to the game thread + thread_.Post(kidmReceivedData, this, pparams); +} + +void SdlHttpRequest::OnFinishedLoading() { + // Called on main thread. Post this to game thread. + thread_.Post(kidmFinishedLoading, this); +} + +void SdlHttpRequest::OnError(NSError *error) { + // Called on main thread. Populate ErrorParams. Use + // localizedDescription. Note there is also localizedFailureReason; + // not sure which is better at the moment + const char *psz = [[error localizedDescription] cStringUsingEncoding: + [NSString defaultCStringEncoding]]; + ErrorParams *pparams = new ErrorParams; + strncpyz(pparams->szError, psz, sizeof(pparams->szError)); + + // Called on main thread. Post this to game thread. + thread_.Post(kidmError, this, pparams); +} +#endif + +} // namespace wi diff --git a/game/sdl/sdlhttprequest.h b/game/sdl/sdlhttprequest.h new file mode 100644 index 0000000..45bdede --- /dev/null +++ b/game/sdl/sdlhttprequest.h @@ -0,0 +1,49 @@ +#ifndef __SDLHTTPREQUEST_H__ +#define __SDLHTTPREQUEST_H__ + +#include "game/httpservice.h" +#include "game/httprequest.h" +#include "base/thread.h" +#include "base/bytebuffer.h" + +namespace wi { + +class SdlHttpRequest : public HttpRequest, base::MessageHandler { +public: + SdlHttpRequest(HttpResponseHandler *phandler); + ~SdlHttpRequest(); + + void Submit(); + void Release(); +// TODO(darrinm): +// NSURLRequest *CreateNSURLRequest(); +// void OnReceivedResponse(NSHTTPURLResponse *resp); +// void OnReceivedData(NSData *data); + void OnFinishedLoading(); +// TODO(darrinm): +// void OnError(NSError *error); + +private: + virtual void OnMessage(base::Message *pmsg); + + HttpResponseHandler *handler_; +// TODO(darrinm): +// ConnectionDelegate *delegate_; +}; + +struct ReceivedResponseParams : base::MessageData { + int code; + Map headers; +}; + +struct ReceivedDataParams : base::MessageData { + base::ByteBuffer bb; +}; + +struct ErrorParams : base::MessageData { + char szError[80]; +}; + +} // namespace wi + +#endif // __SDLHTTPREQUEST_H__ diff --git a/game/sdl/sdlhttpservice.cpp b/game/sdl/sdlhttpservice.cpp new file mode 100644 index 0000000..634a5d4 --- /dev/null +++ b/game/sdl/sdlhttpservice.cpp @@ -0,0 +1,24 @@ +#include "game/sdl/sdlhttpservice.h" +#include "game/sdl/sdlhttprequest.h" +//#include "game/iphone/input.h" + +// HttpService calls come in on the game thread. In order to use +// iPhone NS* Http apis, requests execute on the main thread. + +namespace wi { + +HttpRequest *SdlHttpService::NewRequest(HttpResponseHandler *phandler) { + return new SdlHttpRequest(phandler); +} + +void SdlHttpService::SubmitRequest(HttpRequest *preq) { + SdlHttpRequest *preqT = (SdlHttpRequest *)preq; + preqT->Submit(); +} + +void SdlHttpService::ReleaseRequest(HttpRequest *preq) { + SdlHttpRequest *preqT = (SdlHttpRequest *)preq; + preqT->Release(); +} + +} // namespace wi diff --git a/game/sdl/sdlhttpservice.h b/game/sdl/sdlhttpservice.h new file mode 100644 index 0000000..c701c5e --- /dev/null +++ b/game/sdl/sdlhttpservice.h @@ -0,0 +1,19 @@ +#ifndef __SDLHTTPSERVICE_H__ +#define __SDLHTTPSERVICE_H__ + +#include "inc/basictypes.h" +#include "game/httpservice.h" + +namespace wi { + +class SdlHttpService : public HttpService { +public: + // HttpService methods + virtual HttpRequest *NewRequest(HttpResponseHandler *phandler); + virtual void SubmitRequest(HttpRequest *preq); + virtual void ReleaseRequest(HttpRequest *preq); +}; + +} // namespace wi + +#endif // __SDLHTTPSERVICE_H__ diff --git a/game/sdl/sdlpackfile.cpp b/game/sdl/sdlpackfile.cpp new file mode 100644 index 0000000..1fd92bf --- /dev/null +++ b/game/sdl/sdlpackfile.cpp @@ -0,0 +1,52 @@ +#include "game/sdl/sdlpackfile.h" +#include "game/mempdbreader.h" +#include "inc/rip.h" +#include +#include + +namespace wi { + +PdbReader *SdlPackFileReader::OpenPdb(const char *pszDir, + const char *pszFn) { + // Load data from this directory + + char szT[PATH_MAX]; + if (pszDir != NULL) { + strcpy(szT, pszDir); + strcat(szT, "/"); + strcat(szT, pszFn); + } else { + strcpy(szT, pszFn); + } + + // PdbReader over memory + MemPdbReader *ppdbReader = new MemPdbReader; + if (ppdbReader == NULL) { + Trace("MemPdbReader null"); + return false; + } + if (!ppdbReader->Open(szT)) { + delete ppdbReader; + Trace("MemPdbReader::Open(%s) failed", pszFn); + return false; + } + + return ppdbReader; +} + +bool SdlPackFileReader::DeletePdb(const char *pszDir, const char *pszFn) { +// TODO(darrinm): do something cross-platform. Then replace iphonepackfile.* w/ the new version. +// - does SDL have anything? [I don't think so] +// - POSIX (mac, linux, iOS): unlink() +// - Windows: see win/winpackfile.cpp +// - Android? + + char szT[PATH_MAX]; + strcpy(szT, pszDir); + strcat(szT, "/"); + strcat(szT, pszFn); + unlink(szT); + return true; +} + +} // namespace wi diff --git a/game/sdl/sdlpackfile.h b/game/sdl/sdlpackfile.h new file mode 100644 index 0000000..ac3a7ed --- /dev/null +++ b/game/sdl/sdlpackfile.h @@ -0,0 +1,18 @@ +#ifndef __SDLPACKFILE_H__ +#define __SDLPACKFILE_H__ + +#include "inc/basictypes.h" +#include "mpshared/packfile.h" + +namespace wi { + +class SdlPackFileReader : public PackFileReader +{ +private: + virtual PdbReader *OpenPdb(const char *pszDir, const char *pszFn); + virtual bool DeletePdb(const char *pszDir, const char *pszFn); +}; + +} // namespace wi + +#endif // __SDLPACKFILE_H__ diff --git a/game/sdl/sdlselectionsprite.cpp b/game/sdl/sdlselectionsprite.cpp new file mode 100644 index 0000000..0cec0f9 --- /dev/null +++ b/game/sdl/sdlselectionsprite.cpp @@ -0,0 +1,76 @@ +#include "game/sdl/sdlselectionsprite.h" + +namespace wi { + +SdlSelectionSprite::SdlSelectionSprite(SpriteManager *psprm) { + fVisible_ = false; + psprm_ = psprm; + psprm_->Add(this); +} + +SdlSelectionSprite::~SdlSelectionSprite() { + psprm_->Remove(this); +} + +void SdlSelectionSprite::SetDragRect(const DragRect& drc) { + crit_.Enter(); + drc_ = drc; + crit_.Leave(); + psprm_->Update(this); +} + +const DragRect& SdlSelectionSprite::GetDragRect() { + return drc_; +} + +void SdlSelectionSprite::Show(bool fShow) { + if (fVisible_ == fShow) { + return; + } + fVisible_ = fShow; + psprm_->Update(this); +} + +void SdlSelectionSprite::Draw(void *pv, Size *psiz) { + if (!fVisible_) { + return; + } + + crit_.Enter(); + +#if 0 // TODO(darrinm) + DPoint apt[4]; + drc_.GetPoints(apt); + + // x/y are swapped, and y needs to be origin adjusted + + CGContextRef ctx = (CGContextRef)pv; + CGContextSetRGBStrokeColor(ctx, 1.0, 1.0, 1.0, 1.0); + CGContextSetLineWidth(ctx, 2.0); + CGContextBeginPath(ctx); + CGContextMoveToPoint(ctx, apt[0].y, psiz->cy - apt[0].x); + for (int i = 1; i < ARRAYSIZE(apt); i++) { + CGContextAddLineToPoint(ctx, apt[i].y, psiz->cy - apt[i].x); + } + CGContextAddLineToPoint(ctx, apt[0].y, psiz->cy - apt[0].x); + CGContextStrokePath(ctx); + + // Draw circles at rect corners for "grabbies". White stroked, + // black filled. + CGContextSetRGBFillColor(ctx, 0.0, 0.0, 0.0, 1.0); + +#define kcpRectHalf 4 + + for (int i = 0; i < ARRAYSIZE(apt); i++) { + CGRect rc = CGRectMake(apt[i].y - kcpRectHalf, + psiz->cy - (apt[i].x + kcpRectHalf), + kcpRectHalf * 2, kcpRectHalf * 2); + CGContextFillEllipseInRect(ctx, rc); + CGContextStrokeEllipseInRect(ctx, rc); + } +#endif + + crit_.Leave(); +} + +} // namespace wi diff --git a/game/sdl/sdlselectionsprite.h b/game/sdl/sdlselectionsprite.h new file mode 100644 index 0000000..6440af0 --- /dev/null +++ b/game/sdl/sdlselectionsprite.h @@ -0,0 +1,41 @@ +#ifndef __SDLSELECTIONSPRITE_H__ +#define __SDLSELECTIONSPRITE_H__ + +#include "base/criticalsection.h" +#include "inc/basictypes.h" +#include "game/ht.h" +#include "game/dragrect.h" +#include "game/sprite.h" + +namespace wi { + +class SdlSelectionSprite : public SelectionSprite { +public: + SdlSelectionSprite(SpriteManager *psprm); + ~SdlSelectionSprite(); + + // SelectionSprite + virtual void SetDragRect(const DragRect& drc); + virtual const DragRect& GetDragRect(); + + // Sprite + virtual SpriteManager *GetManager() { return psprm_; } + virtual void SetScale(float scale) {} + virtual void SetPosition(int x, int y) {} + virtual void Show(bool fShow); + virtual bool IsVisible() { return fVisible_; } + virtual void GetBounds(Rect *prc) {} + + // PlatformSprite + virtual void Draw(void *pv, Size *psiz); + +private: + base::CriticalSection crit_; + SpriteManager *psprm_; + DragRect drc_; + bool fVisible_; +}; + +} // namespace wi + +#endif // __SDLSELECTIONSPRITE_H__ diff --git a/game/sdl/sdlsounddev.cpp b/game/sdl/sdlsounddev.cpp new file mode 100644 index 0000000..76a3a76 --- /dev/null +++ b/game/sdl/sdlsounddev.cpp @@ -0,0 +1,226 @@ +#include "../ht.h" +#include + +#include "../sounddevice.h" +#include "../mixer.h" + +namespace wi { + +#define kcBuffers 4 +#define kcbBuffer 256 +#define kcChannels 4 + +extern long HostGetTickCount(); +void AudioCallback(void *pvUser, Uint8 *stream, int len); + +class SdlSoundDevice : public SoundDevice // sndd +{ +public: + SdlSoundDevice(); + virtual ~SdlSoundDevice(); + + bool Init(); + virtual void Enable(bool fEnable); + virtual bool IsEnabled(); + virtual void PlayAdpcm(int ichnl, byte *pb, word cb); + virtual int GetChannelCount(); + virtual bool IsChannelFree(int ichnl); + virtual void ServiceProc(); + virtual bool IsSilent(); + virtual void SetVolume(int nVolume); + virtual int GetVolume(); + +private: + void InitAudioBuffer(byte *pbBuffer); + + bool m_fEnable; + long m_tSilence; + byte *m_apbBuffers[kcBuffers]; + Channel m_achnl[kcChannels]; + SDL_AudioSpec m_spec; + + friend void AudioCallback(void *pvUser, Uint8 *stream, int len); +}; + +SoundDevice *CreateSdlSoundDevice() +{ + SdlSoundDevice *psndd = new SdlSoundDevice; + if (psndd == NULL) + return NULL; + if (!psndd->Init()) { + delete psndd; + return NULL; + } + return (SoundDevice *)psndd; +} + +SdlSoundDevice::SdlSoundDevice() +{ + for (int i = 0; i < ARRAYSIZE(m_apbBuffers); i++) + m_apbBuffers[i] = NULL; + + m_fEnable = false; + m_tSilence = 0; +} + +SdlSoundDevice::~SdlSoundDevice() +{ + SDL_CloseAudio(); + + // TODO(darrinm): and free buffers? +} + +bool SdlSoundDevice::Init() +{ + // Set up streaming + m_spec.freq = 8000; + m_spec.format = AUDIO_U8; + m_spec.channels = 1; + m_spec.samples = kcbBuffer; + m_spec.callback = AudioCallback; + m_spec.userdata = this; + + if (SDL_OpenAudio(&m_spec, NULL) != 0) + return false; + + for (int i = 0; i < kcBuffers; i++) { + m_apbBuffers[i] = (byte *)malloc(m_spec.size); + if (m_apbBuffers[i] == NULL) { + return false; + } + } + + return true; +} + +bool SdlSoundDevice::IsEnabled() +{ + return m_fEnable; +} + +void SdlSoundDevice::Enable(bool fEnable) +{ + SDL_LockAudio(); + + if (fEnable) { + if (!m_fEnable) { + memset(m_achnl, 0, sizeof(m_achnl)); + m_tSilence = 0; + m_fEnable = true; + #if 0 + for (int i = 0; i < kcBuffers; i++) { + InitAudioBuffer(m_apbBuffers[i]); + } + #endif + SDL_PauseAudio(0); + } + } else { + if (m_fEnable) { + m_fEnable = false; + memset(m_achnl, 0, sizeof(m_achnl)); + m_tSilence = 0; + SDL_PauseAudio(1); + } + } + + SDL_UnlockAudio(); +} + +int SdlSoundDevice::GetChannelCount() +{ + return kcChannels; +} + +bool SdlSoundDevice::IsChannelFree(int ichnl) +{ + if (ichnl < 0 || ichnl >= kcChannels) + return false; + Channel *pchnl = &m_achnl[ichnl]; + return (pchnl->pb >= pchnl->pbEnd); +} + +bool SdlSoundDevice::IsSilent() +{ + if (!m_fEnable) + return true; + if (m_tSilence == 0) + return true; + long tCurrent = HostGetTickCount(); + if (tCurrent < 0 && m_tSilence >= 0) + return true; + return tCurrent >= m_tSilence; +} + +void SdlSoundDevice::ServiceProc() +{ +} + +void SdlSoundDevice::PlayAdpcm(int ichnl, byte *pb, word cb) +{ + if (ichnl < 0 || ichnl >= kcChannels) + return; + if (!m_fEnable) + return; + + // Start it up + + SDL_LockAudio(); + + Channel *pchnl = &m_achnl[ichnl]; + pchnl->pb = pb; + pchnl->pbEnd = pb + cb; + pchnl->nStepIndex = 0; + pchnl->bSampleLast = 128; + m_tSilence = HostGetTickCount() + + (cb + kcBuffers * m_spec.size + 39) / 40 + 1; + + SDL_UnlockAudio(); +} + +void SdlSoundDevice::SetVolume(int nVolume) +{ + if (nVolume < 0) + nVolume = 0; + if (nVolume > 255) + nVolume = 255; +#if 0 // TODO(darrinm): SDL has no volume control? + AudioQueueSetParameter(m_haq, kAudioQueueParam_Volume, + (Float32)nVolume / (Float32)255.0); +#endif +} + +int SdlSoundDevice::GetVolume() +{ +#if 0 // TODO(darrinm): SDL has no volume control? + AudioQueueParameterValue value; + AudioQueueGetParameter(m_haq, kAudioQueueParam_Volume, + &value); + return (int)(value * 255.0); +#else + return 255; +#endif +} + +void AudioCallback(void *pvUser, Uint8* stream, int len) +{ + SdlSoundDevice *psndd = (SdlSoundDevice *)pvUser; + psndd->InitAudioBuffer(stream); +} + +void SdlSoundDevice::InitAudioBuffer(byte *pbBuffer) +{ + // Don't send buffers to the device if not enabled + + if (!m_fEnable) + return; + + SDL_LockAudio(); + + // Fill the buffer and queue it for playing + + MixChannels(m_achnl, ARRAYSIZE(m_achnl), pbBuffer, m_spec.size); + + SDL_UnlockAudio(); +} + +} // namespace wi diff --git a/game/sdl/sdlspritemgr.h b/game/sdl/sdlspritemgr.h new file mode 100644 index 0000000..c2acfce --- /dev/null +++ b/game/sdl/sdlspritemgr.h @@ -0,0 +1,40 @@ +#ifndef __SDLSPRITEMGR_H__ +#define __SDLSPRITEMGR_H__ + +#include "game/sprite.h" +#include "game/sdl/sdlselectionsprite.h" + +namespace wi { + +class SdlSpriteManager : public SpriteManager { +public: + SdlSpriteManager() {} + + virtual void SetClipRects(wi::Rect *prc1, wi::Rect *prc2) { + LOG() << "SdlSpriteManager::SetClipRects not implemented yet"; + return; + } + virtual wi::AnimSprite *CreateAnimSprite() { + LOG() << "SdlSpriteManager::CreateAnimSprite not implemented yet"; + return NULL; + } + virtual wi::SelectionSprite *CreateSelectionSprite() { + return new SdlSelectionSprite(this); + } + virtual void Add(wi::Sprite *pspr) { + LOG() << "SdlSpriteManager::Add not implemented yet"; + return; + } + virtual void Remove(wi::Sprite *pspr) { + LOG() << "SdlSpriteManager::Remove not implemented yet"; + return; + } + virtual void Update(wi::Sprite *pspr) { + LOG() << "SdlSpriteManager::Update not implemented yet"; + return; + } +}; + +} // namespace wi + +#endif // __SDLSPRITEMGR_H__ diff --git a/game/sdl/sysmessages.h b/game/sdl/sysmessages.h new file mode 100644 index 0000000..1b90483 --- /dev/null +++ b/game/sdl/sysmessages.h @@ -0,0 +1,22 @@ +#ifndef __SYSMESSAGES_H__ +#define __SYSMESSAGES_H__ + +namespace wi { + +enum { + kidmAppTerminate = 1, + kidmBreakEvent, + kidmNullEvent, + kidmTransportEvent, + kidmAnimationTimer, + kidmDestroyAfterAnimation, + kidmAppSetFocus, + kidmAppKillFocus, + kidmAskStringEvent, + kidmSdlEvent, + kidmUser = 0x400 +}; + +} // namespace wi + +#endif // __SYSMESSAGES_H__ diff --git a/game/sdl/transportmgr.cpp b/game/sdl/transportmgr.cpp new file mode 100644 index 0000000..763a28a --- /dev/null +++ b/game/sdl/transportmgr.cpp @@ -0,0 +1,16 @@ +#include "../ht.h" +#include "xtransport.h" + +namespace wi { + +TransportMgr gtram; + +//--------------------------------------------------------------------------- +// TransportMgr implementation + +int TransportMgr::GetTransportDescriptions(TransportDescription *atrad, int ctradMax) +{ + return XTransport::GetTransportDescriptions(atrad, ctradMax); +} + +} // namespace wi diff --git a/game/sdl/worklist.txt b/game/sdl/worklist.txt new file mode 100644 index 0000000..6c6a9c7 --- /dev/null +++ b/game/sdl/worklist.txt @@ -0,0 +1,41 @@ +- TODOs + +- sound + - why was audio initially muted? + - volume control + +- network + - SDL_net? + +- sdl/savegame.cpp is platform dependent? + +- mission pack downloading +- SpriteManager? +- web ctl +- Display::Init and other failure cases +- fullscreen mode + +- "Conversion from string literal to 'char *' is deprecated" +- "Implicit conversion shorts 64-bit value into a 32-bit value" +- "Implicit conversion loses integer precision: 'long64' aka ('long long') to 'long'" + +- fix other warnings (after committing SDL) +- replace SDL source tree w/ virgin package +- get makefile.sdl working again +- consolidate iPhone/SDL code (e.g. savegame.cpp) + +- file/dir names and locations + - Library/Completes, prefs.bin, MissionPackInfos, MissionPacks, SaveGames + + +- Android? +- iOS? + - dropped smarts for dealing with audio session interruption + +- Windows? + + +Polish +- >= 128px icons +- About +- efficient sleeping while waiting for system events diff --git a/game/selectmission.cpp b/game/selectmission.cpp new file mode 100644 index 0000000..29040ef --- /dev/null +++ b/game/selectmission.cpp @@ -0,0 +1,417 @@ +#include "game/ht.h" +#include "base/format.h" +#include "yajl/wrapper/jsontypes.h" +#include "game/httppackinfomanager.h" +#include "game/completemanager.h" + +namespace wi { + +#define kcUnlockAhead 1 +#define kfItemLocked 0x8000 + +// Should be moved into PackInfoManager, but in a better form, +// for getting specific properties without needing to know the key. + +const char *GetString(const json::JsonMap *map, const char *key) { + const json::JsonObject *obj = map->GetObject(key); + if (obj == NULL || obj->type() != json::JSONTYPE_STRING) { + return NULL; + } + const json::JsonString *s = (json::JsonString *)obj; + + bool fWhitespace = true; + char ch; + const char *psz = s->GetString(); + while ((ch = *psz++) != 0) { + if (!isspace(ch)) { + fWhitespace = false; + break; + } + } + if (fWhitespace) { + return NULL; + } + return s->GetString(); +} + +SelectMissionForm::SelectMissionForm(MissionList *pml, + const MissionIdentifier *pmiidFind) { + m_pml = pml; + m_pmiidFind = pmiidFind; + m_mt = kmtStory; + m_cMagic = 0; + m_fMagicUnlock = false; +#ifdef DEBUG + m_fMagicUnlock = true; +#endif + memset(m_aplstc, 0, sizeof(m_aplstc)); +} + +bool SelectMissionForm::Init(FormMgr *pfrmm, IniReader *pini, word idf) { + if (!ShellForm::Init(pfrmm, pini, idf)) + return false; + + // Format the lists. 3 lists are used as a simple cache. + + int aidcList[] = { kidcStoryList, kidcChallengeList, kidcAddOnList }; + for (int i = 0; i < ARRAYSIZE(aidcList); i++) { + ListControl *plstc = (ListControl *)GetControlPtr(aidcList[i]); + m_aplstc[i] = plstc; + Rect rc; + plstc->GetRect(&rc); + Font *pfnt = gapfnt[kifntShadow]; + int cxComplete = pfnt->GetTextExtent("Complete"); + int xTitle = rc.Width() / 2 - cxComplete * 3 / 2; + int xRightComplete = rc.Width() - 10; + int xLeftComplete = xRightComplete - cxComplete - cxComplete / 2; + plstc->SetTabStops(xTitle, xLeftComplete, xRightComplete); + plstc->SetTabFlags(kfLstTabEllipsis, kfLstTabCenter, 0); + plstc->SetFlags(plstc->GetFlags() | kfLstcKeepInteriorPositioning); + } + + // If asked to find a certain mission, find it first to see what + // type it is, and switch the radio button bar to that type. + + int iPack = -1; + int iMission = -1; + int cMissions = m_pml->GetCount(); + if (m_pmiidFind != NULL) { + for (int i = 0; i < cMissions; i++) { + MissionIdentifier miid; + m_pml->GetMissionIdentifier(i, &miid); + if (memcmp(&miid.packid, &m_pmiidFind->packid, + sizeof(miid.packid)) == 0) { + if (iPack == -1) { + iPack = i; + } + if (strcmp(miid.szLvlFilename, + m_pmiidFind->szLvlFilename) == 0) { + iMission = i; + break; + } + } + } + if (iMission == -1) { + iMission = iPack; + } + } + int iMissionSelect = iMission; + + // Init the lists + + MissionType mt = InitLists(iMissionSelect); + SwitchToMissionType(mt); + + // Hide this label - only show it if there are no addon packs + GetControlPtr(kidcAddOnMessage)->Show(false); + + return true; +} + +int SelectMissionForm::IndexFromMissionType(MissionType mt) { + switch (mt) { + case kmtStory: + return 0; + case kmtChallenge: + return 1; + case kmtAddOn: + return 2; + default: + return -1; + } +} + +MissionType SelectMissionForm::MissionTypeFromIndex(int i) { + switch (i) { + case 0: + return kmtStory; + case 1: + return kmtChallenge; + case 2: + return kmtAddOn; + default: + return kmtUnknown; + } +} + +void SelectMissionForm::SwitchToMissionType(MissionType mt) { + m_mt = mt; + RadioButtonBarControl *prbbc = + (RadioButtonBarControl *)GetControlPtr(kidcCategories); + prbbc->SetSelectionIndex(IndexFromMissionType(mt)); + for (int i = 0; i < ARRAYSIZE(m_aplstc); i++) { + bool fShow = false; + if (i == IndexFromMissionType(mt)) { + fShow = true; + } + m_aplstc[i]->Show(fShow); + } + UpdateDescription(); +} + +MissionType SelectMissionForm::InitLists(int iMissionSelect) { + // Fill in the lists, and along the way keep track of useful selection + // indexes. + + int ailiFirstIncomplete[kmtUnknown + 1]; + memset(ailiFirstIncomplete, 0xff, sizeof(ailiFirstIncomplete)); + int iliLastCompleteStory = -1; + int iliSelectSpecial = -1; + MissionType mtSelectSpecial = kmtUnknown; + + int cMissions = m_pml->GetCount(); + for (int i = 0; i < cMissions; i++) { + MissionDescription md; + if (!m_pml->GetMissionDescription(i, &md)) { + continue; + } + if (md.mt != kmtStory && md.mt != kmtChallenge && md.mt != kmtAddOn) { + continue; + } + + // The first locked mission is kcUnlockAhead missions ahead of the + // last complete story mission. + + ListControl *plstc = m_aplstc[IndexFromMissionType(md.mt)]; + bool fLocked = false; + if (md.mt == kmtStory) { + int iliMissionLocked = iliLastCompleteStory + 1 + kcUnlockAhead; + int iliNext = plstc->GetCount(); + if (iliNext >= iliMissionLocked) { + fLocked = true; + } + } + + // Get the status - LOCKED, Complete, or nothing + + MissionIdentifier miid; + m_pml->GetMissionIdentifier(i, &miid); + dword dw = (dword)i; + const char *pszStatus = ""; + if (fLocked) { + pszStatus = "LOCKED"; + dw |= kfItemLocked; + } else { + if (gpcptm->IsComplete(&miid)) { + pszStatus = "Complete"; + } + } + + // Add the item + + plstc->Add(base::Format::ToString("%s\t%s", md.szLvlTitle, + pszStatus), (void *)dw); + + // Track the first incomplete for each mission type. + + if (ailiFirstIncomplete[md.mt] == -1) { + if (!gpcptm->IsComplete(&miid)) { + ailiFirstIncomplete[md.mt] = plstc->GetCount() - 1; + } + } + + // Track the last complete for story missions, for mission unlocking + + if (md.mt == kmtStory) { + if (gpcptm->IsComplete(&miid)) { + iliLastCompleteStory = plstc->GetCount() - 1; + } + } + + // This is passed in when the form needs to select a certain + // mission when it first shows. + + if (i == iMissionSelect) { + iliSelectSpecial = plstc->GetCount() - 1; + mtSelectSpecial = md.mt; + } + } + + // The initially selected missions are the first incomplete missions + // for each mission type. + + MissionType mtSelect = kmtStory; + for (int i = 0; i < ARRAYSIZE(m_aplstc); i++) { + ListControl *plstc = m_aplstc[i]; + + // Is this the list that is awarded the special selection? + + if (i == IndexFromMissionType(mtSelectSpecial)) { + plstc->Select(iliSelectSpecial, true, true); + mtSelect = mtSelectSpecial; + continue; + } + + int iliSelect = ailiFirstIncomplete[MissionTypeFromIndex(i)]; + if (iliSelect < 0) { + iliSelect = 0; + } + plstc->Select(iliSelect, true, true); + } + return mtSelect; +} + +bool SelectMissionForm::OnPenEvent(Event *pevt) { + if (m_mt != kmtStory || pevt->eType != penUpEvent) { + return ShellForm::OnPenEvent(pevt); + } + + Control *pctlCaptureBefore = GetControlCapture(); + bool f = ShellForm::OnPenEvent(pevt); + Control *pctlCaptureAfter = GetControlCapture(); + +#define kcMagic 5 + + // Tap down on back, move your finger off so it unhighlights, then + // let up. Do this kcMagic times and get a play button. + if (pctlCaptureBefore != NULL && pctlCaptureAfter == NULL) { + if (pctlCaptureBefore->GetId() == kidcCancel) { + m_cMagic++; + if (m_cMagic >= kcMagic) { + m_cMagic = 0; + m_fMagicUnlock = true; +#ifdef BETA_TIMEOUT + GetControlPtr(kidcOk)->Show(true); +#endif + } + } + } + return f; +} + +void SelectMissionForm::OnControlSelected(word idc) { + switch (idc) { + case kidcOk: + case kidcCancel: + EndForm(idc); + break; + + case kidcSetupGame: + { + ShellForm *pfrm = (ShellForm *)gpmfrmm->LoadForm(gpiniForms, + kidfInGameOptions, new InGameOptionsForm()); + if (pfrm != NULL) { + pfrm->DoModal(); + delete pfrm; + } + } + break; + } +} + +void SelectMissionForm::OnControlNotify(word idc, int nNotify) { + if (idc == kidcCategories) { + RadioButtonBarControl *prbbc = + (RadioButtonBarControl *)GetControlPtr(kidcCategories); + int iButtonSelected = prbbc->GetSelectionIndex(); + if (iButtonSelected < 0) { + iButtonSelected = 0; + } + MissionType mtNew = MissionTypeFromIndex(iButtonSelected); + if (mtNew == m_mt) { + return; + } + SwitchToMissionType(mtNew); + + // If in Add-On category, and there is nothing there, show this + // label, otherwise hide it + + bool fShowLabel = false; + ListControl *plstc = m_aplstc[IndexFromMissionType(kmtAddOn)]; + if (m_mt == kmtAddOn) { + if (plstc->GetCount() == 0) { + fShowLabel = true; + } + } + + LabelControl *plbl = (LabelControl *)GetControlPtr(kidcAddOnMessage); + if (fShowLabel) { + plbl->Show(true); + if (m_mt == kmtAddOn) { + plstc->Show(false); + } + } else { + plbl->Show(false); + if (m_mt == kmtAddOn) { + plstc->Show(true); + } + } + } + + if (idc == kidcStoryList || idc == kidcChallengeList || + idc == kidcAddOnList) { + // Update the mission pack description + UpdateDescription(); + } + + // Handle button hiding + + bool fShow = true; + ListControl *plstc = m_aplstc[IndexFromMissionType(m_mt)]; + if (plstc->GetSelectedItemIndex() == -1) { + fShow = false; + } + if (m_mt == kmtStory) { + if (IsSelectedMissionLocked(plstc)) { + fShow = false; + } + } + if (m_fMagicUnlock) { + fShow = true; + } + GetControlPtr(kidcOk)->Show(fShow); +} + +int SelectMissionForm::GetSelectedMissionIndex(ListControl *plstc) { + dword dw = (dword)plstc->GetSelectedItemData(); + return (int)(dw & ~kfItemLocked); +} + +bool SelectMissionForm::IsSelectedMissionLocked(ListControl *plstc) { + dword dw = (dword)plstc->GetSelectedItemData(); + return (dw & kfItemLocked) != 0; +} + +void SelectMissionForm::UpdateDescription() { + LabelControl *plbl = (LabelControl *)GetControlPtr(kidcMissionPackInfo); + ListControl *plstc = m_aplstc[IndexFromMissionType(m_mt)]; + if (plstc->GetSelectedItemIndex() == -1) { + plbl->SetText(""); + return; + } + int i = GetSelectedMissionIndex(plstc); + MissionIdentifier miid; + if (!m_pml->GetMissionIdentifier(i, &miid)) { + plbl->SetText(""); + return; + } + json::JsonMap *map = gppim->GetInfoMap(&miid.packid); + if (map == NULL) { + MissionDescription md; + if (!m_pml->GetMissionDescription(i, &md)) { + plbl->SetText(""); + return; + } + const char *s; + if (miid.packid.id == PACKID_MAIN) { + s = base::Format::ToString("%s by Spiffcode, Inc.", md.szPackName); + } else { + s = md.szPackName; + } + plbl->SetText(s); + return; + } + const char *szAuthor = GetString(map, "a"); + const char *szTitle = GetString(map, "t"); + const char *s = base::Format::ToString("%s by %s", szTitle, szAuthor); + plbl->SetText(s); + delete map; +} + +bool SelectMissionForm::GetSelectedMission(MissionIdentifier *pmiid) { + ListControl *plstc = m_aplstc[IndexFromMissionType(m_mt)]; + int i = GetSelectedMissionIndex(plstc); + return m_pml->GetMissionIdentifier(i, pmiid); +} + +} // namespace wi diff --git a/game/serviceurls.cpp b/game/serviceurls.cpp new file mode 100644 index 0000000..dc8f025 --- /dev/null +++ b/game/serviceurls.cpp @@ -0,0 +1,31 @@ +#include "serviceurls.h" + +namespace wi { + +#ifdef DEBUG +const char *kszIndexUrl = "http://localhost/wi/index"; +const char *kszPackInfoUrl = "http://localhost/wi/info"; +const char *kszPackUrl = "http://localhost/wi/pack"; +const char *kszAuthUrl = "http://localhost:8080/api/auth"; +const char *kszRegisterUrl = "http://localhost:8080/accounts/createaccount"; +const char *kszUpdateAccountUrl = "http://localhost:8080/accounts/updateaccount"; +const char *kszLeaderboardUrl = "http://localhost:8080/stats/leaderboard"; +const char *kszServerInfoUrl = "http://localhost:8080/api/serverinfo"; +const char *kszSyncErrorUploadUrl = ""; +#else +// TODO: Hostile Takeover should have its own server hosting its Mission Packs. +// In the meantime grab them from the Warfare Incorporated server. +const char *kszIndexUrl = "http://content.warfareincorporated.com/wi/index"; +const char *kszPackInfoUrl = "http://content.warfareincorporated.com/wi/info"; +const char *kszPackUrl = "http://content.warfareincorporated.com/wi/pack"; + +// TODO: Hostile Takeover needs multiplayer accounts, leaderboard, and server status functionality. +const char *kszAuthUrl = "https://.appspot.com/api/auth"; +const char *kszRegisterUrl = "https://.appspot.com/accounts/createaccount"; +const char *kszUpdateAccountUrl = "https://.appspot.com/accounts/updateaccount"; +const char *kszLeaderboardUrl = "http://.appspot.com/stats/leaderboard"; +const char *kszServerInfoUrl = "http://.appspot.com/api/serverinfo"; +const char *kszSyncErrorUploadUrl = "http://.appspot.com/api/syncerror"; +#endif + +} // namespace wi diff --git a/game/serviceurls.h b/game/serviceurls.h new file mode 100644 index 0000000..798bc0b --- /dev/null +++ b/game/serviceurls.h @@ -0,0 +1,18 @@ +#ifndef __SERVICEURLS_H__ +#define __SERVICEURLS_H__ + +namespace wi { + +extern const char *kszIndexUrl; +extern const char *kszPackInfoUrl; +extern const char *kszPackUrl; +extern const char *kszAuthUrl; +extern const char *kszRegisterUrl; +extern const char *kszUpdateAccountUrl; +extern const char *kszLeaderboardUrl; +extern const char *kszServerInfoUrl; +extern const char *kszSyncErrorUploadUrl; + +}; + +#endif // __SERVICEURLS_H__ diff --git a/game/simplerequest.cpp b/game/simplerequest.cpp new file mode 100644 index 0000000..aedc542 --- /dev/null +++ b/game/simplerequest.cpp @@ -0,0 +1,115 @@ +#include "game/simplerequest.h" +#include "game/ht.h" + +namespace wi { + +SimpleRequest::SimpleRequest(HttpService *service) : service_(service), + req_(NULL), code_(0), error_(false), errorstr_(NULL), + errorstrsize_(0), done_(false), timeout_(-1) { + if (service_ == NULL) { + service_ = gphttp; + } +} + +SimpleRequest::~SimpleRequest() { + Reset(); +} + +void SimpleRequest::Reset() { + if (req_ != NULL && service_ != NULL) { + service_->ReleaseRequest(req_); + req_ = NULL; + } + bb_.Rewind(); + errorstr_ = NULL; + errorstrsize_ = 0; +} + +bool SimpleRequest::Get(const char *url, char *result, int resultsize, + char *errorstr, int errorstrsize) { + // Stop anything in progress (there shouldn't be anything) + Reset(); + errorstr_ = errorstr; + errorstrsize_ = errorstrsize; + if (errorstr_ != NULL && errorstrsize_ > 0) { + *errorstr_ = 0; + } + + // Make a new request and submit it + req_ = service_->NewRequest(this); + if (req_ == NULL) { + return false; + } + req_->SetURL(url); + if (timeout_ > 0) { + req_->SetTimeout(timeout_); + } + service_->SubmitRequest(req_); + + // Sit in a loop waiting for the response. Note the http service + // has a timeout, so no timeout required here. + done_ = false; + gtimm.Enable(false); + while (!done_ && !error_) { + Event evt; + if (gevm.GetEvent(&evt, 50, false)) { + if (gevm.IsAppStopping()) { + break; + } + ggame.FilterEvent(&evt); + } + } + gtimm.Enable(true); + + // Return result if there is one + if (!error_) { + if (result != NULL) { + int cb = _min(bb_.Length(), resultsize - 1); + memcpy(result, bb_.Data(), cb); + result[cb] = 0; + } + Reset(); + return true; + } + Reset(); + return false; +} + +void SimpleRequest::OnReceivedResponse(HttpRequest *preq, int code, + const Map *pheaders) { + code_ = code; + if (code >= 400) { + // Http error + error_ = true; + if (errorstr_ != NULL) { + strncpyz(errorstr_, base::Format::ToString( + "Server returned error %d", code), errorstrsize_); + } + } +} + +void SimpleRequest::OnReceivedData(HttpRequest *preq, + const base::ByteBuffer *pbb) { + if (error_) { + return; + } + if (code_ >= 200 && code_ < 300) { + bb_.WriteBytes(pbb->Data(), pbb->Length()); + } +} + +void SimpleRequest::OnFinishedLoading(HttpRequest *preq) { + if (error_) { + return; + } + done_ = true; +} + +void SimpleRequest::OnError(HttpRequest *preq, const char *pszError) { + if (errorstr_ != NULL) { + strncpyz(errorstr_, pszError, errorstrsize_); + } + error_ = true; +} + +} // namespace wi diff --git a/game/simplerequest.h b/game/simplerequest.h new file mode 100644 index 0000000..4f633e9 --- /dev/null +++ b/game/simplerequest.h @@ -0,0 +1,42 @@ +#ifndef __SIMPLEREQUEST_H__ +#define __SIMPLEREQUEST_H__ + +#include "game/httpservice.h" +#include "base/bytebuffer.h" + +namespace wi { + +class SimpleRequest : HttpResponseHandler { +public: + SimpleRequest(HttpService *service = NULL); + ~SimpleRequest(); + + const base::ByteBuffer *body() { return &bb_; } + + bool Get(const char *url, char *result = NULL, int resultsize = 0, + char *error = NULL, int errorsize = 0); + void SetTimeout(int seconds) { timeout_ = seconds; } + void Reset(); + +private: + // HttpResponseHandler methods + virtual void OnReceivedResponse(HttpRequest *preq, int code, + const Map *pheaders); + virtual void OnReceivedData(HttpRequest *preq, + const base::ByteBuffer *pbb); + virtual void OnFinishedLoading(HttpRequest *preq); + virtual void OnError(HttpRequest *preq, const char *pszError); + + HttpService *service_; + HttpRequest *req_; + int code_; + bool error_; + base::ByteBuffer bb_; + char *errorstr_; + int errorstrsize_; + bool done_; + int timeout_; +}; + +} +#endif // __SIMPLEREQUEST_H__ diff --git a/game/sizeops.h b/game/sizeops.h new file mode 100644 index 0000000..4789817 --- /dev/null +++ b/game/sizeops.h @@ -0,0 +1,668 @@ +// op handler sizes, total 8816 +// "" suffix, file palm\tbltcode.sym +// "" suffix, file palm\tbltcodeleft.sym +// "_LC" suffix, file palm\tbltcodeleft.sym +// "_RC" suffix, file palm\tbltcoderight.sym +// "" suffix, file palm\tbltcoderight.sym +// "_YC" suffix, file palm\tbltcodey.sym +#define kcbOpTotal 8816 +#define kcbEvenData1 0x10 // 0x0, 0 +#define kibEvenData1 0x8 // 0x0, 0 +#define kcbEvenData2 0x10 // 0x1, 1 +#define kibEvenData2 0x2c // 0x1, 1 +#define kcbEvenData3 0x12 // 0x2, 2 +#define kibEvenData3 0x50 // 0x2, 2 +#define kcbEvenData4 0x10 // 0x3, 3 +#define kibEvenData4 0x78 // 0x3, 3 +#define kcbEvenData5 0x10 // 0x4, 4 +#define kibEvenData5 0x9c // 0x4, 4 +#define kcbEvenData6 0x10 // 0x5, 5 +#define kibEvenData6 0xc0 // 0x5, 5 +#define kcbEvenData7 0x12 // 0x6, 6 +#define kibEvenData7 0xe4 // 0x6, 6 +#define kcbEvenData8 0x10 // 0x7, 7 +#define kibEvenData8 0x10c // 0x7, 7 +#define kcbEvenData9 0x14 // 0x8, 8 +#define kibEvenData9 0x130 // 0x8, 8 +#define kcbEvenData10 0x14 // 0x9, 9 +#define kibEvenData10 0x15c // 0x9, 9 +#define kcbEvenData11 0x14 // 0xa, 10 +#define kibEvenData11 0x188 // 0xa, 10 +#define kcbEvenData12 0x14 // 0xb, 11 +#define kibEvenData12 0x1b4 // 0xb, 11 +#define kcbEvenData13 0x14 // 0xc, 12 +#define kibEvenData13 0x1e0 // 0xc, 12 +#define kcbEvenData14 0x14 // 0xd, 13 +#define kibEvenData14 0x20c // 0xd, 13 +#define kcbEvenData15 0x16 // 0xe, 14 +#define kibEvenData15 0x238 // 0xe, 14 +#define kcbEvenData16 0x14 // 0xf, 15 +#define kibEvenData16 0x268 // 0xf, 15 +#define kcbEvenData17 0x16 // 0x10, 16 +#define kibEvenData17 0x294 // 0x10, 16 +#define kcbEvenData18 0x16 // 0x11, 17 +#define kibEvenData18 0x2c4 // 0x11, 17 +#define kcbEvenData19 0x16 // 0x12, 18 +#define kibEvenData19 0x2f4 // 0x12, 18 +#define kcbEvenData20 0x16 // 0x13, 19 +#define kibEvenData20 0x324 // 0x13, 19 +#define kcbEvenData21 0x16 // 0x14, 20 +#define kibEvenData21 0x354 // 0x14, 20 +#define kcbEvenData22 0x16 // 0x15, 21 +#define kibEvenData22 0x384 // 0x15, 21 +#define kcbEvenData23 0x18 // 0x16, 22 +#define kibEvenData23 0x3b4 // 0x16, 22 +#define kcbEvenData24 0x16 // 0x17, 23 +#define kibEvenData24 0x3e8 // 0x17, 23 +#define kcbEvenData25 0x18 // 0x18, 24 +#define kibEvenData25 0x418 // 0x18, 24 +#define kcbEvenData26 0x18 // 0x19, 25 +#define kibEvenData26 0x44c // 0x19, 25 +#define kcbEvenData27 0x18 // 0x1a, 26 +#define kibEvenData27 0x480 // 0x1a, 26 +#define kcbEvenData28 0x18 // 0x1b, 27 +#define kibEvenData28 0x4b4 // 0x1b, 27 +#define kcbEvenData29 0x18 // 0x1c, 28 +#define kibEvenData29 0x4e8 // 0x1c, 28 +#define kcbEvenData30 0x18 // 0x1d, 29 +#define kibEvenData30 0x51c // 0x1d, 29 +#define kcbEvenData31 0x1a // 0x1e, 30 +#define kibEvenData31 0x550 // 0x1e, 30 +#define kcbEvenData32 0x18 // 0x1f, 31 +#define kibEvenData32 0x588 // 0x1f, 31 +#define kcbEvenData33 0x1a // 0x20, 32 +#define kibEvenData33 0x5bc // 0x20, 32 +#define kcbEvenData34 0x1a // 0x21, 33 +#define kibEvenData34 0x5f4 // 0x21, 33 +#define kcbEvenData35 0x1a // 0x22, 34 +#define kibEvenData35 0x62c // 0x22, 34 +#define kcbEvenData36 0x1a // 0x23, 35 +#define kibEvenData36 0x664 // 0x23, 35 +#define kcbEvenData37 0x1a // 0x24, 36 +#define kibEvenData37 0x69c // 0x24, 36 +#define kcbEvenData38 0x1a // 0x25, 37 +#define kibEvenData38 0x6d4 // 0x25, 37 +#define kcbEvenData39 0x1c // 0x26, 38 +#define kibEvenData39 0x70c // 0x26, 38 +#define kcbEvenData40 0x1a // 0x27, 39 +#define kibEvenData40 0x748 // 0x27, 39 +#define kcbEvenData41 0x1c // 0x28, 40 +#define kibEvenData41 0x780 // 0x28, 40 +#define kcbEvenData42 0x1c // 0x29, 41 +#define kibEvenData42 0x7bc // 0x29, 41 +#define kcbEvenData43 0x1c // 0x2a, 42 +#define kibEvenData43 0x7f8 // 0x2a, 42 +#define kcbEvenData44 0x1c // 0x2b, 43 +#define kibEvenData44 0x834 // 0x2b, 43 +#define kcbEvenData45 0x1c // 0x2c, 44 +#define kibEvenData45 0x870 // 0x2c, 44 +#define kcbEvenData46 0x1c // 0x2d, 45 +#define kibEvenData46 0x8ac // 0x2d, 45 +#define kcbEvenData47 0x1e // 0x2e, 46 +#define kibEvenData47 0x8e8 // 0x2e, 46 +#define kcbEvenData48 0x1c // 0x2f, 47 +#define kibEvenData48 0x928 // 0x2f, 47 +#define kcbEvenData1Align 0x14 // 0x30, 48 +#define kibEvenData1Align 0x18 // 0x30, 48 +#define kcbEvenData2Align 0x14 // 0x31, 49 +#define kibEvenData2Align 0x3c // 0x31, 49 +#define kcbEvenData3Align 0x16 // 0x32, 50 +#define kibEvenData3Align 0x62 // 0x32, 50 +#define kcbEvenData4Align 0x14 // 0x33, 51 +#define kibEvenData4Align 0x88 // 0x33, 51 +#define kcbEvenData5Align 0x14 // 0x34, 52 +#define kibEvenData5Align 0xac // 0x34, 52 +#define kcbEvenData6Align 0x14 // 0x35, 53 +#define kibEvenData6Align 0xd0 // 0x35, 53 +#define kcbEvenData7Align 0x16 // 0x36, 54 +#define kibEvenData7Align 0xf6 // 0x36, 54 +#define kcbEvenData8Align 0x14 // 0x37, 55 +#define kibEvenData8Align 0x11c // 0x37, 55 +#define kcbEvenData9Align 0x18 // 0x38, 56 +#define kibEvenData9Align 0x144 // 0x38, 56 +#define kcbEvenData10Align 0x18 // 0x39, 57 +#define kibEvenData10Align 0x170 // 0x39, 57 +#define kcbEvenData11Align 0x18 // 0x3a, 58 +#define kibEvenData11Align 0x19c // 0x3a, 58 +#define kcbEvenData12Align 0x18 // 0x3b, 59 +#define kibEvenData12Align 0x1c8 // 0x3b, 59 +#define kcbEvenData13Align 0x18 // 0x3c, 60 +#define kibEvenData13Align 0x1f4 // 0x3c, 60 +#define kcbEvenData14Align 0x18 // 0x3d, 61 +#define kibEvenData14Align 0x220 // 0x3d, 61 +#define kcbEvenData15Align 0x1a // 0x3e, 62 +#define kibEvenData15Align 0x24e // 0x3e, 62 +#define kcbEvenData16Align 0x18 // 0x3f, 63 +#define kibEvenData16Align 0x27c // 0x3f, 63 +#define kcbEvenData17Align 0x1a // 0x40, 64 +#define kibEvenData17Align 0x2aa // 0x40, 64 +#define kcbEvenData18Align 0x1a // 0x41, 65 +#define kibEvenData18Align 0x2da // 0x41, 65 +#define kcbEvenData19Align 0x1a // 0x42, 66 +#define kibEvenData19Align 0x30a // 0x42, 66 +#define kcbEvenData20Align 0x1a // 0x43, 67 +#define kibEvenData20Align 0x33a // 0x43, 67 +#define kcbEvenData21Align 0x1a // 0x44, 68 +#define kibEvenData21Align 0x36a // 0x44, 68 +#define kcbEvenData22Align 0x1a // 0x45, 69 +#define kibEvenData22Align 0x39a // 0x45, 69 +#define kcbEvenData23Align 0x1c // 0x46, 70 +#define kibEvenData23Align 0x3cc // 0x46, 70 +#define kcbEvenData24Align 0x1a // 0x47, 71 +#define kibEvenData24Align 0x3fe // 0x47, 71 +#define kcbEvenData25Align 0x1c // 0x48, 72 +#define kibEvenData25Align 0x430 // 0x48, 72 +#define kcbEvenData26Align 0x1c // 0x49, 73 +#define kibEvenData26Align 0x464 // 0x49, 73 +#define kcbEvenData27Align 0x1c // 0x4a, 74 +#define kibEvenData27Align 0x498 // 0x4a, 74 +#define kcbEvenData28Align 0x1c // 0x4b, 75 +#define kibEvenData28Align 0x4cc // 0x4b, 75 +#define kcbEvenData29Align 0x1c // 0x4c, 76 +#define kibEvenData29Align 0x500 // 0x4c, 76 +#define kcbEvenData30Align 0x1c // 0x4d, 77 +#define kibEvenData30Align 0x534 // 0x4d, 77 +#define kcbEvenData31Align 0x1e // 0x4e, 78 +#define kibEvenData31Align 0x56a // 0x4e, 78 +#define kcbEvenData32Align 0x1c // 0x4f, 79 +#define kibEvenData32Align 0x5a0 // 0x4f, 79 +#define kcbEvenData33Align 0x1e // 0x50, 80 +#define kibEvenData33Align 0x5d6 // 0x50, 80 +#define kcbEvenData34Align 0x1e // 0x51, 81 +#define kibEvenData34Align 0x60e // 0x51, 81 +#define kcbEvenData35Align 0x1e // 0x52, 82 +#define kibEvenData35Align 0x646 // 0x52, 82 +#define kcbEvenData36Align 0x1e // 0x53, 83 +#define kibEvenData36Align 0x67e // 0x53, 83 +#define kcbEvenData37Align 0x1e // 0x54, 84 +#define kibEvenData37Align 0x6b6 // 0x54, 84 +#define kcbEvenData38Align 0x1e // 0x55, 85 +#define kibEvenData38Align 0x6ee // 0x55, 85 +#define kcbEvenData39Align 0x20 // 0x56, 86 +#define kibEvenData39Align 0x728 // 0x56, 86 +#define kcbEvenData40Align 0x1e // 0x57, 87 +#define kibEvenData40Align 0x762 // 0x57, 87 +#define kcbEvenData41Align 0x20 // 0x58, 88 +#define kibEvenData41Align 0x79c // 0x58, 88 +#define kcbEvenData42Align 0x20 // 0x59, 89 +#define kibEvenData42Align 0x7d8 // 0x59, 89 +#define kcbEvenData43Align 0x20 // 0x5a, 90 +#define kibEvenData43Align 0x814 // 0x5a, 90 +#define kcbEvenData44Align 0x20 // 0x5b, 91 +#define kibEvenData44Align 0x850 // 0x5b, 91 +#define kcbEvenData45Align 0x20 // 0x5c, 92 +#define kibEvenData45Align 0x88c // 0x5c, 92 +#define kcbEvenData46Align 0x20 // 0x5d, 93 +#define kibEvenData46Align 0x8c8 // 0x5d, 93 +#define kcbEvenData47Align 0x22 // 0x5e, 94 +#define kibEvenData47Align 0x906 // 0x5e, 94 +#define kcbEvenData48Align 0x20 // 0x5f, 95 +#define kibEvenData48Align 0x944 // 0x5f, 95 +#define kcbOddData1 0x10 // 0x60, 96 +#define kibOddData1 0x964 // 0x60, 96 +#define kcbOddData2 0x12 // 0x61, 97 +#define kibOddData2 0x988 // 0x61, 97 +#define kcbOddData3 0x12 // 0x62, 98 +#define kibOddData3 0x9b0 // 0x62, 98 +#define kcbOddData4 0x14 // 0x63, 99 +#define kibOddData4 0x9d8 // 0x63, 99 +#define kcbOddData5 0x12 // 0x64, 100 +#define kibOddData5 0xa04 // 0x64, 100 +#define kcbOddData6 0x12 // 0x65, 101 +#define kibOddData6 0xa2c // 0x65, 101 +#define kcbOddData7 0x12 // 0x66, 102 +#define kibOddData7 0xa54 // 0x66, 102 +#define kcbOddData8 0x14 // 0x67, 103 +#define kibOddData8 0xa7c // 0x67, 103 +#define kcbOddData9 0x14 // 0x68, 104 +#define kibOddData9 0xaa8 // 0x68, 104 +#define kcbOddData10 0x16 // 0x69, 105 +#define kibOddData10 0xad4 // 0x69, 105 +#define kcbOddData11 0x16 // 0x6a, 106 +#define kibOddData11 0xb04 // 0x6a, 106 +#define kcbOddData12 0x16 // 0x6b, 107 +#define kibOddData12 0xb34 // 0x6b, 107 +#define kcbOddData13 0x16 // 0x6c, 108 +#define kibOddData13 0xb64 // 0x6c, 108 +#define kcbOddData14 0x16 // 0x6d, 109 +#define kibOddData14 0xb94 // 0x6d, 109 +#define kcbOddData15 0x16 // 0x6e, 110 +#define kibOddData15 0xbc4 // 0x6e, 110 +#define kcbOddData16 0x18 // 0x6f, 111 +#define kibOddData16 0xbf4 // 0x6f, 111 +#define kcbOddData17 0x16 // 0x70, 112 +#define kibOddData17 0xc28 // 0x70, 112 +#define kcbOddData18 0x18 // 0x71, 113 +#define kibOddData18 0xc58 // 0x71, 113 +#define kcbOddData19 0x18 // 0x72, 114 +#define kibOddData19 0xc8c // 0x72, 114 +#define kcbOddData20 0x18 // 0x73, 115 +#define kibOddData20 0xcc0 // 0x73, 115 +#define kcbOddData21 0x18 // 0x74, 116 +#define kibOddData21 0xcf4 // 0x74, 116 +#define kcbOddData22 0x18 // 0x75, 117 +#define kibOddData22 0xd28 // 0x75, 117 +#define kcbOddData23 0x18 // 0x76, 118 +#define kibOddData23 0xd5c // 0x76, 118 +#define kcbOddData24 0x1a // 0x77, 119 +#define kibOddData24 0xd90 // 0x77, 119 +#define kcbOddData25 0x18 // 0x78, 120 +#define kibOddData25 0xdc8 // 0x78, 120 +#define kcbOddData26 0x1a // 0x79, 121 +#define kibOddData26 0xdfc // 0x79, 121 +#define kcbOddData27 0x1a // 0x7a, 122 +#define kibOddData27 0xe34 // 0x7a, 122 +#define kcbOddData28 0x1a // 0x7b, 123 +#define kibOddData28 0xe6c // 0x7b, 123 +#define kcbOddData29 0x1a // 0x7c, 124 +#define kibOddData29 0xea4 // 0x7c, 124 +#define kcbOddData30 0x1a // 0x7d, 125 +#define kibOddData30 0xedc // 0x7d, 125 +#define kcbOddData31 0x1a // 0x7e, 126 +#define kibOddData31 0xf14 // 0x7e, 126 +#define kcbOddData32 0x1c // 0x7f, 127 +#define kibOddData32 0xf4c // 0x7f, 127 +#define kcbOddData33 0x1a // 0x80, 128 +#define kibOddData33 0xf88 // 0x80, 128 +#define kcbOddData34 0x1c // 0x81, 129 +#define kibOddData34 0xfc0 // 0x81, 129 +#define kcbOddData35 0x1c // 0x82, 130 +#define kibOddData35 0xffc // 0x82, 130 +#define kcbOddData36 0x1c // 0x83, 131 +#define kibOddData36 0x1038 // 0x83, 131 +#define kcbOddData37 0x1c // 0x84, 132 +#define kibOddData37 0x1074 // 0x84, 132 +#define kcbOddData38 0x1c // 0x85, 133 +#define kibOddData38 0x10b0 // 0x85, 133 +#define kcbOddData39 0x1c // 0x86, 134 +#define kibOddData39 0x10ec // 0x86, 134 +#define kcbOddData40 0x1e // 0x87, 135 +#define kibOddData40 0x1128 // 0x87, 135 +#define kcbOddData41 0x1c // 0x88, 136 +#define kibOddData41 0x1168 // 0x88, 136 +#define kcbOddData42 0x1e // 0x89, 137 +#define kibOddData42 0x11a4 // 0x89, 137 +#define kcbOddData43 0x1e // 0x8a, 138 +#define kibOddData43 0x11e4 // 0x8a, 138 +#define kcbOddData44 0x1e // 0x8b, 139 +#define kibOddData44 0x1224 // 0x8b, 139 +#define kcbOddData45 0x1e // 0x8c, 140 +#define kibOddData45 0x1264 // 0x8c, 140 +#define kcbOddData46 0x1e // 0x8d, 141 +#define kibOddData46 0x12a4 // 0x8d, 141 +#define kcbOddData47 0x1e // 0x8e, 142 +#define kibOddData47 0x12e4 // 0x8e, 142 +#define kcbOddData48 0x20 // 0x8f, 143 +#define kibOddData48 0x1324 // 0x8f, 143 +#define kcbOddData1Align 0x14 // 0x90, 144 +#define kibOddData1Align 0x974 // 0x90, 144 +#define kcbOddData2Align 0x16 // 0x91, 145 +#define kibOddData2Align 0x99a // 0x91, 145 +#define kcbOddData3Align 0x16 // 0x92, 146 +#define kibOddData3Align 0x9c2 // 0x92, 146 +#define kcbOddData4Align 0x18 // 0x93, 147 +#define kibOddData4Align 0x9ec // 0x93, 147 +#define kcbOddData5Align 0x16 // 0x94, 148 +#define kibOddData5Align 0xa16 // 0x94, 148 +#define kcbOddData6Align 0x16 // 0x95, 149 +#define kibOddData6Align 0xa3e // 0x95, 149 +#define kcbOddData7Align 0x16 // 0x96, 150 +#define kibOddData7Align 0xa66 // 0x96, 150 +#define kcbOddData8Align 0x18 // 0x97, 151 +#define kibOddData8Align 0xa90 // 0x97, 151 +#define kcbOddData9Align 0x18 // 0x98, 152 +#define kibOddData9Align 0xabc // 0x98, 152 +#define kcbOddData10Align 0x1a // 0x99, 153 +#define kibOddData10Align 0xaea // 0x99, 153 +#define kcbOddData11Align 0x1a // 0x9a, 154 +#define kibOddData11Align 0xb1a // 0x9a, 154 +#define kcbOddData12Align 0x1a // 0x9b, 155 +#define kibOddData12Align 0xb4a // 0x9b, 155 +#define kcbOddData13Align 0x1a // 0x9c, 156 +#define kibOddData13Align 0xb7a // 0x9c, 156 +#define kcbOddData14Align 0x1a // 0x9d, 157 +#define kibOddData14Align 0xbaa // 0x9d, 157 +#define kcbOddData15Align 0x1a // 0x9e, 158 +#define kibOddData15Align 0xbda // 0x9e, 158 +#define kcbOddData16Align 0x1c // 0x9f, 159 +#define kibOddData16Align 0xc0c // 0x9f, 159 +#define kcbOddData17Align 0x1a // 0xa0, 160 +#define kibOddData17Align 0xc3e // 0xa0, 160 +#define kcbOddData18Align 0x1c // 0xa1, 161 +#define kibOddData18Align 0xc70 // 0xa1, 161 +#define kcbOddData19Align 0x1c // 0xa2, 162 +#define kibOddData19Align 0xca4 // 0xa2, 162 +#define kcbOddData20Align 0x1c // 0xa3, 163 +#define kibOddData20Align 0xcd8 // 0xa3, 163 +#define kcbOddData21Align 0x1c // 0xa4, 164 +#define kibOddData21Align 0xd0c // 0xa4, 164 +#define kcbOddData22Align 0x1c // 0xa5, 165 +#define kibOddData22Align 0xd40 // 0xa5, 165 +#define kcbOddData23Align 0x1c // 0xa6, 166 +#define kibOddData23Align 0xd74 // 0xa6, 166 +#define kcbOddData24Align 0x1e // 0xa7, 167 +#define kibOddData24Align 0xdaa // 0xa7, 167 +#define kcbOddData25Align 0x1c // 0xa8, 168 +#define kibOddData25Align 0xde0 // 0xa8, 168 +#define kcbOddData26Align 0x1e // 0xa9, 169 +#define kibOddData26Align 0xe16 // 0xa9, 169 +#define kcbOddData27Align 0x1e // 0xaa, 170 +#define kibOddData27Align 0xe4e // 0xaa, 170 +#define kcbOddData28Align 0x1e // 0xab, 171 +#define kibOddData28Align 0xe86 // 0xab, 171 +#define kcbOddData29Align 0x1e // 0xac, 172 +#define kibOddData29Align 0xebe // 0xac, 172 +#define kcbOddData30Align 0x1e // 0xad, 173 +#define kibOddData30Align 0xef6 // 0xad, 173 +#define kcbOddData31Align 0x1e // 0xae, 174 +#define kibOddData31Align 0xf2e // 0xae, 174 +#define kcbOddData32Align 0x20 // 0xaf, 175 +#define kibOddData32Align 0xf68 // 0xaf, 175 +#define kcbOddData33Align 0x1e // 0xb0, 176 +#define kibOddData33Align 0xfa2 // 0xb0, 176 +#define kcbOddData34Align 0x20 // 0xb1, 177 +#define kibOddData34Align 0xfdc // 0xb1, 177 +#define kcbOddData35Align 0x20 // 0xb2, 178 +#define kibOddData35Align 0x1018 // 0xb2, 178 +#define kcbOddData36Align 0x20 // 0xb3, 179 +#define kibOddData36Align 0x1054 // 0xb3, 179 +#define kcbOddData37Align 0x20 // 0xb4, 180 +#define kibOddData37Align 0x1090 // 0xb4, 180 +#define kcbOddData38Align 0x20 // 0xb5, 181 +#define kibOddData38Align 0x10cc // 0xb5, 181 +#define kcbOddData39Align 0x20 // 0xb6, 182 +#define kibOddData39Align 0x1108 // 0xb6, 182 +#define kcbOddData40Align 0x22 // 0xb7, 183 +#define kibOddData40Align 0x1146 // 0xb7, 183 +#define kcbOddData41Align 0x20 // 0xb8, 184 +#define kibOddData41Align 0x1184 // 0xb8, 184 +#define kcbOddData42Align 0x22 // 0xb9, 185 +#define kibOddData42Align 0x11c2 // 0xb9, 185 +#define kcbOddData43Align 0x22 // 0xba, 186 +#define kibOddData43Align 0x1202 // 0xba, 186 +#define kcbOddData44Align 0x22 // 0xbb, 187 +#define kibOddData44Align 0x1242 // 0xbb, 187 +#define kcbOddData45Align 0x22 // 0xbc, 188 +#define kibOddData45Align 0x1282 // 0xbc, 188 +#define kcbOddData46Align 0x22 // 0xbd, 189 +#define kibOddData46Align 0x12c2 // 0xbd, 189 +#define kcbOddData47Align 0x22 // 0xbe, 190 +#define kibOddData47Align 0x1302 // 0xbe, 190 +#define kcbOddData48Align 0x24 // 0xbf, 191 +#define kibOddData48Align 0x1344 // 0xbf, 191 +#define kcbEvenSide1 0x12 // 0xc0, 192 +#define kibEvenSide1 0x1368 // 0xc0, 192 +#define kcbEvenSide2 0x12 // 0xc1, 193 +#define kibEvenSide2 0x137a // 0xc1, 193 +#define kcbEvenSide3 0x16 // 0xc2, 194 +#define kibEvenSide3 0x138c // 0xc2, 194 +#define kcbEvenSide4 0x12 // 0xc3, 195 +#define kibEvenSide4 0x13a2 // 0xc3, 195 +#define kcbEvenSide5 0x16 // 0xc4, 196 +#define kibEvenSide5 0x13b4 // 0xc4, 196 +#define kcbEvenSide6 0x16 // 0xc5, 197 +#define kibEvenSide6 0x13ca // 0xc5, 197 +#define kcbEvenSide7 0x1a // 0xc6, 198 +#define kibEvenSide7 0x13e0 // 0xc6, 198 +#define kcbEvenSide8 0x16 // 0xc7, 199 +#define kibEvenSide8 0x13fa // 0xc7, 199 +#define kcbEvenSide9 0x1c // 0xc8, 200 +#define kibEvenSide9 0x1410 // 0xc8, 200 +#define kcbEvenSide10 0x1c // 0xc9, 201 +#define kibEvenSide10 0x142c // 0xc9, 201 +#define kcbEvenSide11 0x20 // 0xca, 202 +#define kibEvenSide11 0x1448 // 0xca, 202 +#define kcbEvenSide12 0x1c // 0xcb, 203 +#define kibEvenSide12 0x1468 // 0xcb, 203 +#define kcbEvenSide13 0x20 // 0xcc, 204 +#define kibEvenSide13 0x1484 // 0xcc, 204 +#define kcbEvenSide14 0x20 // 0xcd, 205 +#define kibEvenSide14 0x14a4 // 0xcd, 205 +#define kcbEvenSide15 0x24 // 0xce, 206 +#define kibEvenSide15 0x14c4 // 0xce, 206 +#define kcbEvenSide16 0x20 // 0xcf, 207 +#define kibEvenSide16 0x14e8 // 0xcf, 207 +#define kcbOddSide1 0x12 // 0xd0, 208 +#define kibOddSide1 0x1508 // 0xd0, 208 +#define kcbOddSide2 0x16 // 0xd1, 209 +#define kibOddSide2 0x151a // 0xd1, 209 +#define kcbOddSide3 0x16 // 0xd2, 210 +#define kibOddSide3 0x1530 // 0xd2, 210 +#define kcbOddSide4 0x1a // 0xd3, 211 +#define kibOddSide4 0x1546 // 0xd3, 211 +#define kcbOddSide5 0x16 // 0xd4, 212 +#define kibOddSide5 0x1560 // 0xd4, 212 +#define kcbOddSide6 0x1a // 0xd5, 213 +#define kibOddSide6 0x1576 // 0xd5, 213 +#define kcbOddSide7 0x1a // 0xd6, 214 +#define kibOddSide7 0x1590 // 0xd6, 214 +#define kcbOddSide8 0x1e // 0xd7, 215 +#define kibOddSide8 0x15aa // 0xd7, 215 +#define kcbOddSide9 0x1c // 0xd8, 216 +#define kibOddSide9 0x15c8 // 0xd8, 216 +#define kcbOddSide10 0x20 // 0xd9, 217 +#define kibOddSide10 0x15e4 // 0xd9, 217 +#define kcbOddSide11 0x20 // 0xda, 218 +#define kibOddSide11 0x1604 // 0xda, 218 +#define kcbOddSide12 0x24 // 0xdb, 219 +#define kibOddSide12 0x1624 // 0xdb, 219 +#define kcbOddSide13 0x20 // 0xdc, 220 +#define kibOddSide13 0x1648 // 0xdc, 220 +#define kcbOddSide14 0x24 // 0xdd, 221 +#define kibOddSide14 0x1668 // 0xdd, 221 +#define kcbOddSide15 0x24 // 0xde, 222 +#define kibOddSide15 0x168c // 0xde, 222 +#define kcbOddSide16 0x28 // 0xdf, 223 +#define kibOddSide16 0x16b0 // 0xdf, 223 +#define kcbShadow1 0x16 // 0xe0, 224 +#define kibShadow1 0x1bc4 // 0xe0, 224 +#define kcbShadow2 0x1c // 0xe1, 225 +#define kibShadow2 0x1bda // 0xe1, 225 +#define kcbShadow3 0x20 // 0xe2, 226 +#define kibShadow3 0x1bf6 // 0xe2, 226 +#define kcbShadow4 0x24 // 0xe3, 227 +#define kibShadow4 0x1c16 // 0xe3, 227 +#define kcbShadow5 0x28 // 0xe4, 228 +#define kibShadow5 0x1c3a // 0xe4, 228 +#define kcbShadow6 0x2c // 0xe5, 229 +#define kibShadow6 0x1c62 // 0xe5, 229 +#define kcbShadow7 0x30 // 0xe6, 230 +#define kibShadow7 0x1c8e // 0xe6, 230 +#define kcbShadow8 0x34 // 0xe7, 231 +#define kibShadow8 0x1cbe // 0xe7, 231 +#define kcbShadow9 0x3a // 0xe8, 232 +#define kibShadow9 0x1cf2 // 0xe8, 232 +#define kcbShadow10 0x3e // 0xe9, 233 +#define kibShadow10 0x1d2c // 0xe9, 233 +#define kcbShadow11 0x42 // 0xea, 234 +#define kibShadow11 0x1d6a // 0xea, 234 +#define kcbShadow12 0x46 // 0xeb, 235 +#define kibShadow12 0x1dac // 0xeb, 235 +#define kcbShadow13 0x4a // 0xec, 236 +#define kibShadow13 0x1df2 // 0xec, 236 +#define kcbShadow14 0x4e // 0xed, 237 +#define kibShadow14 0x1e3c // 0xed, 237 +#define kcbShadow15 0x52 // 0xee, 238 +#define kibShadow15 0x1e8a // 0xee, 238 +#define kcbShadow16 0x56 // 0xef, 239 +#define kibShadow16 0x1edc // 0xef, 239 +#define kcbShadow17 0x5a // 0xf0, 240 +#define kibShadow17 0x1f32 // 0xf0, 240 +#define kcbShadow18 0x5e // 0xf1, 241 +#define kibShadow18 0x1f8c // 0xf1, 241 +#define kcbShadow19 0x62 // 0xf2, 242 +#define kibShadow19 0x1fea // 0xf2, 242 +#define kcbShadow20 0x66 // 0xf3, 243 +#define kibShadow20 0x204c // 0xf3, 243 +#define kcbShadow21 0x6a // 0xf4, 244 +#define kibShadow21 0x20b2 // 0xf4, 244 +#define kcbShadow22 0x6e // 0xf5, 245 +#define kibShadow22 0x211c // 0xf5, 245 +#define kcbShadow23 0x72 // 0xf6, 246 +#define kibShadow23 0x218a // 0xf6, 246 +#define kcbShadow24 0x76 // 0xf7, 247 +#define kibShadow24 0x21fc // 0xf7, 247 +#define kcbTransparent1 0x12 // 0xf8, 248 +#define kibTransparent1 0x16d8 // 0xf8, 248 +#define kcbTransparent2 0x12 // 0xf9, 249 +#define kibTransparent2 0x16ea // 0xf9, 249 +#define kcbTransparent3 0x12 // 0xfa, 250 +#define kibTransparent3 0x16fc // 0xfa, 250 +#define kcbTransparent4 0x12 // 0xfb, 251 +#define kibTransparent4 0x170e // 0xfb, 251 +#define kcbTransparent5 0x12 // 0xfc, 252 +#define kibTransparent5 0x1720 // 0xfc, 252 +#define kcbTransparent6 0x12 // 0xfd, 253 +#define kibTransparent6 0x1732 // 0xfd, 253 +#define kcbTransparent7 0x12 // 0xfe, 254 +#define kibTransparent7 0x1744 // 0xfe, 254 +#define kcbTransparent8 0x12 // 0xff, 255 +#define kibTransparent8 0x1756 // 0xff, 255 +#define kcbTransparent9 0x16 // 0x100, 256 +#define kibTransparent9 0x1768 // 0x100, 256 +#define kcbTransparent10 0x16 // 0x101, 257 +#define kibTransparent10 0x177e // 0x101, 257 +#define kcbTransparent11 0x16 // 0x102, 258 +#define kibTransparent11 0x1794 // 0x102, 258 +#define kcbTransparent12 0x16 // 0x103, 259 +#define kibTransparent12 0x17aa // 0x103, 259 +#define kcbTransparent13 0x16 // 0x104, 260 +#define kibTransparent13 0x17c0 // 0x104, 260 +#define kcbTransparent14 0x16 // 0x105, 261 +#define kibTransparent14 0x17d6 // 0x105, 261 +#define kcbTransparent15 0x16 // 0x106, 262 +#define kibTransparent15 0x17ec // 0x106, 262 +#define kcbTransparent16 0x16 // 0x107, 263 +#define kibTransparent16 0x1802 // 0x107, 263 +#define kcbTransparent17 0x16 // 0x108, 264 +#define kibTransparent17 0x1818 // 0x108, 264 +#define kcbTransparent18 0x16 // 0x109, 265 +#define kibTransparent18 0x182e // 0x109, 265 +#define kcbTransparent19 0x16 // 0x10a, 266 +#define kibTransparent19 0x1844 // 0x10a, 266 +#define kcbTransparent20 0x16 // 0x10b, 267 +#define kibTransparent20 0x185a // 0x10b, 267 +#define kcbTransparent21 0x16 // 0x10c, 268 +#define kibTransparent21 0x1870 // 0x10c, 268 +#define kcbTransparent22 0x16 // 0x10d, 269 +#define kibTransparent22 0x1886 // 0x10d, 269 +#define kcbTransparent23 0x16 // 0x10e, 270 +#define kibTransparent23 0x189c // 0x10e, 270 +#define kcbTransparent24 0x16 // 0x10f, 271 +#define kibTransparent24 0x18b2 // 0x10f, 271 +#define kcbTransparent25 0x16 // 0x110, 272 +#define kibTransparent25 0x18c8 // 0x110, 272 +#define kcbTransparent26 0x16 // 0x111, 273 +#define kibTransparent26 0x18de // 0x111, 273 +#define kcbTransparent27 0x16 // 0x112, 274 +#define kibTransparent27 0x18f4 // 0x112, 274 +#define kcbTransparent28 0x16 // 0x113, 275 +#define kibTransparent28 0x190a // 0x113, 275 +#define kcbTransparent29 0x16 // 0x114, 276 +#define kibTransparent29 0x1920 // 0x114, 276 +#define kcbTransparent30 0x16 // 0x115, 277 +#define kibTransparent30 0x1936 // 0x115, 277 +#define kcbTransparent31 0x16 // 0x116, 278 +#define kibTransparent31 0x194c // 0x116, 278 +#define kcbTransparent32 0x16 // 0x117, 279 +#define kibTransparent32 0x1962 // 0x117, 279 +#define kcbNextScan0 0xc // 0x118, 280 +#define kibNextScan0 0x1978 // 0x118, 280 +#define kcbNextScan1 0xc // 0x119, 281 +#define kibNextScan1 0x1984 // 0x119, 281 +#define kcbNextScan2 0xc // 0x11a, 282 +#define kibNextScan2 0x1990 // 0x11a, 282 +#define kcbNextScan3 0xc // 0x11b, 283 +#define kibNextScan3 0x199c // 0x11b, 283 +#define kcbNextScan4 0xc // 0x11c, 284 +#define kibNextScan4 0x19a8 // 0x11c, 284 +#define kcbNextScan5 0xc // 0x11d, 285 +#define kibNextScan5 0x19b4 // 0x11d, 285 +#define kcbNextScan6 0xc // 0x11e, 286 +#define kibNextScan6 0x19c0 // 0x11e, 286 +#define kcbNextScan7 0xc // 0x11f, 287 +#define kibNextScan7 0x19cc // 0x11f, 287 +#define kcbNextScan8 0xc // 0x120, 288 +#define kibNextScan8 0x19d8 // 0x120, 288 +#define kcbNextScan9 0xc // 0x121, 289 +#define kibNextScan9 0x19e4 // 0x121, 289 +#define kcbNextScan10 0xc // 0x122, 290 +#define kibNextScan10 0x19f0 // 0x122, 290 +#define kcbNextScan11 0xc // 0x123, 291 +#define kibNextScan11 0x19fc // 0x123, 291 +#define kcbNextScan12 0xc // 0x124, 292 +#define kibNextScan12 0x1a08 // 0x124, 292 +#define kcbNextScan13 0xc // 0x125, 293 +#define kibNextScan13 0x1a14 // 0x125, 293 +#define kcbNextScan14 0xc // 0x126, 294 +#define kibNextScan14 0x1a20 // 0x126, 294 +#define kcbNextScan15 0xc // 0x127, 295 +#define kibNextScan15 0x1a2c // 0x127, 295 +#define kcbNextScan16 0xc // 0x128, 296 +#define kibNextScan16 0x1a38 // 0x128, 296 +#define kcbNextScan17 0xc // 0x129, 297 +#define kibNextScan17 0x1a44 // 0x129, 297 +#define kcbNextScan18 0xc // 0x12a, 298 +#define kibNextScan18 0x1a50 // 0x12a, 298 +#define kcbNextScan19 0xc // 0x12b, 299 +#define kibNextScan19 0x1a5c // 0x12b, 299 +#define kcbNextScan20 0xc // 0x12c, 300 +#define kibNextScan20 0x1a68 // 0x12c, 300 +#define kcbNextScan21 0xc // 0x12d, 301 +#define kibNextScan21 0x1a74 // 0x12d, 301 +#define kcbNextScan22 0xc // 0x12e, 302 +#define kibNextScan22 0x1a80 // 0x12e, 302 +#define kcbNextScan23 0xc // 0x12f, 303 +#define kibNextScan23 0x1a8c // 0x12f, 303 +#define kcbNextScan24 0xc // 0x130, 304 +#define kibNextScan24 0x1a98 // 0x130, 304 +#define kcbNextScan25 0xc // 0x131, 305 +#define kibNextScan25 0x1aa4 // 0x131, 305 +#define kcbNextScan26 0xc // 0x132, 306 +#define kibNextScan26 0x1ab0 // 0x132, 306 +#define kcbNextScan27 0xc // 0x133, 307 +#define kibNextScan27 0x1abc // 0x133, 307 +#define kcbNextScan28 0xc // 0x134, 308 +#define kibNextScan28 0x1ac8 // 0x134, 308 +#define kcbNextScan29 0xc // 0x135, 309 +#define kibNextScan29 0x1ad4 // 0x135, 309 +#define kcbNextScan30 0xc // 0x136, 310 +#define kibNextScan30 0x1ae0 // 0x136, 310 +#define kcbNextScan31 0xc // 0x137, 311 +#define kibNextScan31 0x1aec // 0x137, 311 +#define kcbNextScan32 0xc // 0x138, 312 +#define kibNextScan32 0x1af8 // 0x138, 312 +#define kcbNextScan33 0xc // 0x139, 313 +#define kibNextScan33 0x1b04 // 0x139, 313 +#define kcbNextScan34 0xc // 0x13a, 314 +#define kibNextScan34 0x1b10 // 0x13a, 314 +#define kcbNextScan35 0xc // 0x13b, 315 +#define kibNextScan35 0x1b1c // 0x13b, 315 +#define kcbNextScan36 0xc // 0x13c, 316 +#define kibNextScan36 0x1b28 // 0x13c, 316 +#define kcbNextScan37 0xc // 0x13d, 317 +#define kibNextScan37 0x1b34 // 0x13d, 317 +#define kcbNextScan38 0xc // 0x13e, 318 +#define kibNextScan38 0x1b40 // 0x13e, 318 +#define kcbNextScan39 0xc // 0x13f, 319 +#define kibNextScan39 0x1b4c // 0x13f, 319 +#define kcbNextScan40 0xc // 0x140, 320 +#define kibNextScan40 0x1b58 // 0x140, 320 +#define kcbNextScan41 0xc // 0x141, 321 +#define kibNextScan41 0x1b64 // 0x141, 321 +#define kcbNextScan42 0xc // 0x142, 322 +#define kibNextScan42 0x1b70 // 0x142, 322 +#define kcbNextScan43 0xc // 0x143, 323 +#define kibNextScan43 0x1b7c // 0x143, 323 +#define kcbNextScan44 0xc // 0x144, 324 +#define kibNextScan44 0x1b88 // 0x144, 324 +#define kcbNextScan45 0xc // 0x145, 325 +#define kibNextScan45 0x1b94 // 0x145, 325 +#define kcbNextScan46 0xc // 0x146, 326 +#define kibNextScan46 0x1ba0 // 0x146, 326 +#define kcbNextScan47 0xc // 0x147, 327 +#define kibNextScan47 0x1bac // 0x147, 327 +#define kcbNextScan48 0xc // 0x148, 328 +#define kibNextScan48 0x1bb8 // 0x148, 328 +#define kcbEnd 0x6 // 0x149, 329 +#define kibEnd 0x2 // 0x149, 329 diff --git a/game/sizeops.makefile b/game/sizeops.makefile new file mode 100644 index 0000000..a8594ed --- /dev/null +++ b/game/sizeops.makefile @@ -0,0 +1,10 @@ +all: palm/sizeops.inc sizeops.h + +palm/sizeops.inc: palm/tbltcode.s palm/tbltcodeleft.s palm/tbltcoderight.s palm/tbltcodey.s + m68k-palmos-as -as --defsym SIZEOPS=0 palm/tbltcode.s > palm/tbltcode.sym + m68k-palmos-as -as --defsym SIZEOPS=0 palm/tbltcodeleft.s > palm/tbltcodeleft.sym + m68k-palmos-as -as --defsym SIZEOPS=0 palm/tbltcoderight.s > palm/tbltcoderight.sym + m68k-palmos-as -as --defsym SIZEOPS=0 palm/tbltcodey.s > palm/tbltcodey.sym + ../bin/sizeops -inc palm/tbltcode.sym palm/tbltcodeleft.sym palm/tbltcoderight.sym palm/tbltcodey.sym > palm/sizeops.inc + ../bin/sizeops -h palm/tbltcode.sym palm/tbltcodeleft.sym palm/tbltcoderight.sym palm/tbltcodey.sym > sizeops.h + cp palm/sizeops.inc ../data/sizeops.inc diff --git a/game/sounddevice.h b/game/sounddevice.h new file mode 100644 index 0000000..3faf519 --- /dev/null +++ b/game/sounddevice.h @@ -0,0 +1,25 @@ +#ifndef __SOUNDDEVICE_H__ +#define __SOUNDDEVICE_H__ + +#include "basictypes.h" + +namespace wi { + +class SoundDevice // sndd +{ +public: + virtual ~SoundDevice() = 0; + virtual void Enable(bool fEnable) = 0; + virtual bool IsEnabled() = 0; + virtual void PlayAdpcm(int ichnl, byte *pb, word cb) = 0; + virtual int GetChannelCount() = 0; + virtual bool IsChannelFree(int ichnl) = 0; + virtual void ServiceProc() = 0; + virtual bool IsSilent() = 0; + virtual void SetVolume(int nVolume) = 0; + virtual int GetVolume() = 0; +}; + +} // namespace wi + +#endif // __SOUNDDEVICE_H__ diff --git a/game/soundeffects.h b/game/soundeffects.h new file mode 100644 index 0000000..612fa85 --- /dev/null +++ b/game/soundeffects.h @@ -0,0 +1,221 @@ +// This file is read by schemer. This file can be editted as desired. Edits will require rebuilds of data. +// Change disabled to enabled when sound effect is in use in the game. +// Note: name deprecation is supported. Replace "none" with old names for loading old files + +enum Sfx { // sfx + ksfxNothing, // disabled; none + ksfxGalaxiteProcessorAbortRepair, // enabled; none + ksfxGalaxiteProcessorDamaged, // enabled; none + ksfxGalaxiteProcessorDestroyed, // enabled; none + ksfxGalaxiteProcessorDoorOpening, // enabled; none + ksfxGalaxiteProcessorDoorClosing, // enabled; none + ksfxGalaxiteProcessorRepair, // enabled; none + ksfxGalaxiteProcessorSelect, // enabled; none + ksfxGalaxiteWarehouseAbortRepair, // enabled; none + ksfxGalaxiteWarehouseDamaged, // enabled; none + ksfxGalaxiteWarehouseDestroyed, // enabled; none + ksfxGalaxiteWarehouseRepair, // enabled; none + ksfxGalaxiteWarehouseSelect, // enabled; none + ksfxGalaxiteWarehouseTooFull, // enabled; none + ksfxGalaxMinerMine, // enabled; none + ksfxGalaxMinerUnderAttack, // enabled; none + ksfxGalaxMinerDeliver, // enabled; none + ksfxGameBaseUnderAttack, // enabled; none + ksfxGameCreditsDecreasing, // enabled; none + ksfxGameCreditsIncreasing, // enabled; none + ksfxGameLevelResults, // disabled; none + ksfxGameLoseLevel, // enabled; none + ksfxGameNewVehicleOptions, // enabled; none + ksfxGameNewRecruitOptions, // enabled; none + ksfxGameNewStructureOptions, // enabled; none + ksfxGameWinLevel, // enabled; none + ksfxGuiBuildMenuHide, // disabled; none + ksfxGuiBuildMenuSelectItem, // disabled; none + ksfxGuiBuildMenuShow, // disabled; none + ksfxGuiButtonTap, // enabled; none + ksfxGuiCheckBoxTap, // enabled; none + ksfxGuiEditBoxCharacterEntered, // disabled; none + ksfxGuiFormHide, // enabled; none + ksfxGuiFormShow, // enabled; none + ksfxGuiMissionTextCharOutput, // disabled; none + ksfxGuiScrollingListSelectItem, // enabled; none + ksfxHeadquartersAbortConstruction, // enabled; none + ksfxHeadquartersAbortRepair, // enabled; none + ksfxHeadquartersConstruct, // enabled; none + ksfxHeadquartersDamaged, // enabled; none + ksfxHeadquartersDestroyed, // enabled; none + ksfxHeadquartersRepair, // enabled; none + ksfxHeadquartersSelect, // enabled; none + ksfxHeadquartersStructureReady, // enabled; none + ksfxHumanResourceCenterAbortRecruiting, // enabled; none + ksfxHumanResourceCenterAbortRepair, // enabled; none + ksfxHumanResourceCenterDamaged, // enabled; none + ksfxHumanResourceCenterDestroyed, // enabled; none + ksfxHumanResourceCenterRecruit, // enabled; none + ksfxHumanResourceCenterRepair, // enabled; none + ksfxHumanResourceCenterSelect, // enabled; none + ksfxHumanResourceCenterUnitReady, // enabled; none + ksfxLightTankFire, // enabled; none + ksfxLightTankImpact, // enabled; none + ksfxMachineGunInfantryFire, // enabled; none + ksfxMachineGunTowerAbortRepair, // enabled; none + ksfxMachineGunTowerAttack, // disabled; none + ksfxMachineGunTowerDamaged, // enabled; none + ksfxMachineGunTowerDestroyed, // enabled; none + ksfxMachineGunTowerFire, // enabled; none + ksfxMachineGunTowerRepair, // enabled; none + ksfxMachineGunTowerSelect, // enabled; none + ksfxMachineGunVehicleFire, // enabled; none + ksfxMediumTankFire, // enabled; none + ksfxMediumTankImpact, // enabled; none + ksfxMobileHeadquartersDeploy, // enabled; none + ksfxRadarAbortRepair, // enabled; none + ksfxRadarDamaged, // enabled; none + ksfxRadarDestroyed, // enabled; none + ksfxRadarRepair, // enabled; none + ksfxRadarSelect, // enabled; none + ksfxReactorAbortRepair, // enabled; none + ksfxReactorDamaged, // enabled; none + ksfxReactorDestroyed, // enabled; none + ksfxReactorPowerTooLow, // enabled; none + ksfxReactorRepair, // enabled; none + ksfxReactorSelect, // enabled; none + ksfxResearchCenterAbortRepair, // enabled; none + ksfxResearchCenterDamaged, // enabled; none + ksfxResearchCenterDestroyed, // enabled; none + ksfxResearchCenterRepair, // enabled; none + ksfxResearchCenterSelect, // enabled; none + ksfxRocketInfantryFire, // enabled; none + ksfxRocketInfantryImpact, // enabled; none + ksfxRocketTowerAbortRepair, // enabled; none + ksfxRocketTowerAttack, // disabled; none + ksfxRocketTowerDamaged, // enabled; none + ksfxRocketTowerDestroyed, // enabled; none + ksfxRocketTowerFire, // enabled; none + ksfxRocketTowerImpact, // enabled; none + ksfxRocketTowerRepair, // enabled; none + ksfxRocketTowerSelect, // enabled; none + ksfxRocketVehicleFire, // enabled; none + ksfxRocketVehicleImpact, // enabled; none + ksfxTakeoverSpecialistStructureCaptured, // enabled; none + ksfxVehicleTransportStationAbortManufacture, // enabled; none + ksfxVehicleTransportStationAbortRepair, // enabled; none + ksfxVehicleTransportStationDamaged, // enabled; none + ksfxVehicleTransportStationDestroyed, // enabled; none + ksfxVehicleTransportStationManufacture, // enabled; none + ksfxVehicleTransportStationRepair, // enabled; none + ksfxVehicleTransportStationSelect, // enabled; none + ksfxVehicleTransportStationVehicleReady, // enabled; none + ksfxVehicleDestroyed, // enabled; none + ksfxInfantryDestroyed0, // enabled; none + ksfxInfantryDestroyed1, // enabled; none + ksfxInfantryDestroyed2, // enabled; none + ksfxInfantryDestroyed3, // enabled; none + ksfxInfantryDestroyed4, // enabled; none + ksfxMale01Select0, // enabled; none + ksfxMale01Select1, // enabled; none + ksfxMale01Select2, // enabled; none + ksfxMale01Select3, // enabled; none + ksfxMale01Move0, // enabled; none + ksfxMale01Move1, // enabled; none + ksfxMale01Move2, // enabled; none + ksfxMale01Move3, // enabled; none + ksfxMale01Attack0, // enabled; none + ksfxMale01Attack1, // enabled; none + ksfxMale01Attack2, // enabled; none + ksfxMale01Attack3, // enabled; none + ksfxMale03Select0, // enabled; none + ksfxMale03Select1, // enabled; none + ksfxMale03Select2, // enabled; none + ksfxMale03Select3, // enabled; none + ksfxMale03Move0, // enabled; none + ksfxMale03Move1, // enabled; none + ksfxMale03Move2, // enabled; none + ksfxMale03Move3, // enabled; none + ksfxMale03Attack0, // enabled; none + ksfxMale03Attack1, // enabled; none + ksfxMale03Attack2, // enabled; none + ksfxMale03Attack3, // enabled; none + ksfxMale06Select0, // enabled; none + ksfxMale06Select1, // enabled; none + ksfxMale06Select2, // enabled; none + ksfxMale06Select3, // enabled; none + ksfxMale06Move0, // enabled; none + ksfxMale06Move1, // enabled; none + ksfxMale06Move2, // enabled; none + ksfxMale06Move3, // enabled; none + ksfxMale06Attack0, // enabled; none + ksfxMale06Attack1, // enabled; none + ksfxMale06Attack2, // enabled; none + ksfxMale06Attack3, // enabled; none + ksfxMajor01Select0, // enabled; none + ksfxMajor01Select1, // enabled; none + ksfxMajor01Select2, // enabled; none + ksfxMajor01Select3, // enabled; none + ksfxMajor01Move0, // enabled; none + ksfxMajor01Move1, // enabled; none + ksfxMajor01Move2, // enabled; none + ksfxMajor01Move3, // enabled; none + ksfxMajor01Attack0, // enabled; none + ksfxMajor01Attack1, // enabled; none + ksfxMajor01Attack2, // enabled; none + ksfxMajor01Attack3, // enabled; none + ksfxMajor02Select0, // enabled; none + ksfxMajor02Select1, // enabled; none + ksfxMajor02Select2, // enabled; none + ksfxMajor02Select3, // enabled; none + ksfxMajor02Move0, // enabled; none + ksfxMajor02Move1, // enabled; none + ksfxMajor02Move2, // enabled; none + ksfxMajor02Move3, // enabled; none + ksfxMajor02Attack0, // enabled; none + ksfxMajor02Attack1, // enabled; none + ksfxMajor02Attack2, // enabled; none + ksfxMajor02Attack3, // enabled; none + ksfxAndySelect, // enabled; none + ksfxAndyMove, // enabled; none + ksfxAndyAttack, // enabled; none + ksfxAndyFire, //enabled; none + ksfxAndyDestroyed, // enabled; none + ksfxReplicatorOn, // enabled; none + ksfxReplicatorOff, // enabled; none + ksfxReplicatorBuild, // enabled; none + ksfxActivatorOn, // enabled; none + ksfxActivatorOff, // enabled; none + ksfxArtilleryFire, // enabled; none + ksfxArtilleryImpact, // enabled; none + ksfxHappyEnding, //enabled; none + ksfxReplicatorDestroyed, //enabled; none + ksfxFoxDestroyed, //endabled; none +}; +// **sfx for triggers - keep grouped after HappyEnding, also add in res.h +// stop +// needed sfx +// ksfxResearchCenterUpgrade +// ksfxResearchCenterAbortUpgrade + +enum SfxCategory { + ksfxcNothing = -1, + ksfxcInfantryDestroyed, + ksfxcVehicleDestroyed, + ksfxcMale01Select, + ksfxcMale01Move, + ksfxcMale01Attack, + ksfxcMale03Select, + ksfxcMale03Move, + ksfxcMale03Attack, + ksfxcMale06Select, + ksfxcMale06Move, + ksfxcMale06Attack, + ksfxcMajor01Select, + ksfxcMajor01Move, + ksfxcMajor01Attack, + ksfxcMajor02Select, + ksfxcMajor02Move, + ksfxcMajor02Attack, + ksfxcAndySelect, + ksfxcAndyMove, + ksfxcAndyAttack, + ksfxcAndyDestroyed, + ksfxcFoxDestroyed, +}; diff --git a/game/soundmgr.cpp b/game/soundmgr.cpp new file mode 100644 index 0000000..9b15bc7 --- /dev/null +++ b/game/soundmgr.cpp @@ -0,0 +1,259 @@ +#include "ht.h" + +namespace wi { + +SoundMgr gsndm; + +SoundMgr::SoundMgr() +{ + m_apcmh = NULL; + m_cpcmh = 0; + m_asfxe = NULL; + m_csfxe = 0; + m_afmap = NULL; + m_psndd = NULL; + m_cChannels = 0; + memset(m_anPriorityChannel, 0, sizeof(m_anPriorityChannel)); + m_fStateSaved = false; +} + +SoundMgr::~SoundMgr() +{ + Assert(m_psndd == NULL); +} + +bool SoundMgr::Init() +{ + // Open the sound device + + m_psndd = HostOpenSoundDevice(); + if (m_psndd == NULL) + return false; + + FileMap fmap; + byte *pbFiles = (byte *)gpakr.MapFile("soundfiles", &fmap); + if (pbFiles == NULL) + return false; + + // Load sounds - get count and load arrays + + bool fSuccess = true; + m_cpcmh = BigWord(*(word *)pbFiles); + pbFiles += 2; + m_apcmh = new PcmHeader[m_cpcmh]; + if (m_apcmh == NULL) { + gpakr.UnmapFile(&fmap); + return false; + } + memset(m_apcmh, 0, sizeof(PcmHeader) * m_cpcmh); + m_afmap = new FileMap[m_cpcmh]; + if (m_afmap == NULL) { + gpakr.UnmapFile(&fmap); + return false; + } + memset(m_afmap, 0, sizeof(FileMap) * m_cpcmh); + + // Map the sound files + + char *psz = (char *)pbFiles; + for (int i = 0; i < m_cpcmh; i++) { + dword cb; + m_apcmh[i].pb = (byte *)gpakr.MapFile(psz, &m_afmap[i], &cb); + if (m_apcmh[i].pb == NULL) { + fSuccess = false; + break; + } + m_apcmh[i].cb = (word)cb; + if (i < m_cpcmh - 1) + psz += strlen(psz) + 1; + } + gpakr.UnmapFile(&fmap); + if (!fSuccess) + return false; + + // Map SfxEntries + + dword cb; + m_asfxe = (SfxEntry *)gpakr.MapFile("SfxEntries", &m_fmapSfxEntries, &cb); + if (m_asfxe == NULL) + return false; + m_csfxe = cb / kcbSfxEntry; + + // Turn on sound device + + m_cChannels = m_psndd->GetChannelCount(); + + return true; +} + +void SoundMgr::Exit() +{ + delete m_psndd; + m_psndd = NULL; + + for (int n = 0; n < m_cpcmh; n++) { + if (m_apcmh[n].pb != NULL) { + gpakr.UnmapFile(&m_afmap[n]); + m_apcmh[n].pb = NULL; + } + } + delete m_apcmh; + m_apcmh = NULL; + delete m_afmap; + m_afmap = NULL; + m_cpcmh = 0; + + if (m_asfxe != NULL) { + gpakr.UnmapFile(&m_fmapSfxEntries); + m_asfxe = NULL; + } +} + +void SoundMgr::Enable(bool fEnable) +{ + if (m_psndd != NULL) + m_psndd->Enable(fEnable); +} + +bool SoundMgr::IsEnabled() +{ + if (m_psndd != NULL) + return m_psndd->IsEnabled(); + return false; +} + +// Our game conflicts with OS audio on some devices like Clie. We try to detect these cases and turn audio +// completely off + +void SoundMgr::RestoreState() +{ + if (m_fStateSaved) { + m_fStateSaved = false; + if (m_psndd == NULL) + m_psndd = HostOpenSoundDevice(); + Enable(m_fEnabledSav); + SetVolume(m_nVolumeSav); + } +} + +bool SoundMgr::SaveStateAndClear() +{ + if (m_fStateSaved) + return false; + m_fStateSaved = true; + m_fEnabledSav = IsEnabled(); + m_nVolumeSav = GetVolume(); + delete m_psndd; + m_psndd = NULL; + return true; +} + +void SoundMgr::PlaySfx(Sfx sfx) +{ + // Get the sfx entry and channel + + if (m_psndd == NULL) + return; + if (!m_psndd->IsEnabled()) + return; + int nSfx = (int)sfx; + if (nSfx < 0 || nSfx > m_csfxe) + return; + SfxEntry *psfxe = (SfxEntry *)((byte *)m_asfxe + sfx * kcbSfxEntry); + if (psfxe->nSound == 0xff) + return; + + // Find a free channel, a channel playing the same priority sound effect, + // or channel playing a lower priority sound effect + + // First look to see if this category effect is playing + + int ichnlUse = -1; + byte nPriorityLowest = 0; + +#if 0 +// Problem: This'll cause one equal priority sound effect to replace another +// of the same priority even if there are free channels to use. + + for (int ichnl = 0; ichnl < m_cChannels; ichnl++) { + if (m_anPriorityChannel[ichnl] == psfxe->nPriority) { + nPriorityLowest = 255; + ichnlUse = ichnl; + break; + } + } +#endif + + // If not playing, see if there is an empty channel. Keep track of lowest + // priority channel + + if (ichnlUse == -1) { + for (int ichnl = 0; ichnl < m_cChannels; ichnl++) { + if (m_psndd->IsChannelFree(ichnl)) { + nPriorityLowest = 255; + ichnlUse = ichnl; + break; + } + if (m_anPriorityChannel[ichnl] >= nPriorityLowest) { + nPriorityLowest = m_anPriorityChannel[ichnl]; + ichnlUse = ichnl; + } + } + } + + // We need to replace a lower priority sound effect + // (higher numbers are lower priority) + + Assert(ichnlUse != -1); + if (psfxe->nPriority > nPriorityLowest) + return; + m_anPriorityChannel[ichnlUse] = psfxe->nPriority; + + // Play the sound + + PcmHeader *pcmh = &m_apcmh[psfxe->nSound]; + m_psndd->PlayAdpcm(ichnlUse, pcmh->pb, pcmh->cb); +} + +void SoundMgr::WaitSilence() +{ + if (m_psndd == NULL) + return; + while (!m_psndd->IsSilent()) { + HostSleep(1); + HostSoundServiceProc(); + } +} + +long SoundMgr::FilterSleepTicks(long ct) +{ + // HACK: + // On PocketPC sound is processed on an OS callback so we don't need to + // limit sleeping Further, if we DO limit sleeping we will cause problems + // for the background Bluetooth communications threads. + +#if !defined(CE) && !defined(IPHONE) + // If we have sound effects to play, don't sleep + + if (m_psndd != NULL) { + if (!m_psndd->IsSilent()) + return 0; + } +#endif + return ct; +} + +void SoundMgr::SetVolume(int nVolume) +{ + if (m_psndd != NULL) + m_psndd->SetVolume(nVolume); +} + +int SoundMgr::GetVolume() +{ + if (m_psndd != NULL) + return m_psndd->GetVolume(); + return -1; +} + +} // namespace wi diff --git a/game/sprite.h b/game/sprite.h new file mode 100644 index 0000000..06ad43e --- /dev/null +++ b/game/sprite.h @@ -0,0 +1,54 @@ +#ifndef __SPRITE_H__ +#define __SPRITE_H__ + +#include "game/ht.h" + +namespace wi { + +class Palette; +class Animation; +class AnimSprite; +class SelectionSprite; +class Sprite; + +class SpriteManager { +public: + virtual void SetClipRects(Rect *prc1, Rect *prc2) = 0; + virtual AnimSprite *CreateAnimSprite() = 0; + virtual SelectionSprite *CreateSelectionSprite() = 0; + virtual void Add(Sprite *pspr) = 0; + virtual void Remove(Sprite *pspr) = 0; + virtual void Update(Sprite *pspr) = 0; +}; + +class Sprite : public PlatformSprite { +public: + virtual ~Sprite() {} + + virtual SpriteManager *GetManager() = 0; + virtual void SetScale(float scale) = 0; + virtual void SetPosition(int x, int y) = 0; + virtual void Show(bool fShow) = 0; + virtual bool IsVisible() = 0; + virtual void GetBounds(Rect *prc) = 0; +}; + +class UnitGob; +class AnimSprite : public Sprite { +public: + virtual void SetPalette(Palette *ppal) = 0; + virtual void CaptureFrame(UnitGob *pgob) = 0; + virtual void SetScaleAnimation(float nScaleStart, float nScaleEnd, + dword cms, dword cmsRate, bool fAutoDestroy) = 0; +}; + +class DragRect; +class SelectionSprite : public Sprite { +public: + virtual void SetDragRect(const DragRect& drc) = 0; + virtual const DragRect& GetDragRect() = 0; +}; + +} // namespace wi + +#endif // __SPRITE_H__ diff --git a/game/stateframe.cpp b/game/stateframe.cpp new file mode 100644 index 0000000..903faf0 --- /dev/null +++ b/game/stateframe.cpp @@ -0,0 +1,115 @@ +#include "game/stateframe.h" +#include "game/statetracker.h" +#include +#include + +#define kcEntriesInit 2500 +#define kcEntriesGrow 500 + +namespace wi { + +StateFrame::StateFrame(StateTracker *tracker) : + tracker_(tracker), cUpdates_(0), entries_(NULL), + count_(0), index_(0), block_(false) { +} + +StateFrame::~StateFrame() { + free(entries_); +} + +bool StateFrame::Init(long cUpdates, bool block) { + if (entries_ == NULL) { + entries_ = (Entry *)malloc(sizeof(Entry) * kcEntriesInit); + if (entries_ == NULL) { + return false; + } + count_ = kcEntriesInit; + } + index_ = 0; + cUpdates_ = cUpdates; + block_ = block; + return true; +} + +bool StateFrame::Grow() { + Entry *entriesnew = (Entry *)malloc(sizeof(Entry) * + (kcEntriesGrow + count_)); + if (entriesnew == NULL) { + return false; + } + memcpy(entriesnew, entries_, sizeof(Entry) * count_); + free(entries_); + entries_ = entriesnew; + count_ += kcEntriesGrow; + return true; +} + +dword StateFrame::GetHash() { + dword h = 0; + Entry *entryMax = &entries_[index_]; + Entry *entry = entries_; + for (Entry *entry = entries_; entry < entryMax; entry++) { + h += entry->quad; + h ^= entry->value; + } + return h; +} + +const char *StateFrame::QuadToString(dword quad) { + static char s_achQuad[5]; + char *ach = (char *)&quad; + s_achQuad[0] = ach[3]; + s_achQuad[1] = ach[2] != ' ' ? ach[2] : 0; + s_achQuad[2] = ach[1] != ' ' ? ach[1] : 0; + s_achQuad[3] = ach[0] != ' ' ? ach[0] : 0; + s_achQuad[4] = 0; + return s_achQuad; +} + +base::ByteBuffer *StateFrame::ToJson() { + base::ByteBuffer *bb = new base::ByteBuffer; + if (bb == NULL) { + return NULL; + } + bb->WriteString("{", false); + bb->WriteString(base::Format::ToString("\"updates\":\"%d\"", cUpdates_), + false); + bb->WriteString(base::Format::ToString(",\"hash\":\"%08x\"", GetHash()), + false); + bb->WriteString(",\"dict\":{", false); + + bool first = true; + for (int i = 0; i < index_;) { + Entry *parent = &entries_[i]; + int count = parent->value; + + if (!first) { + bb->WriteString(",", false); + } + first = false; + + bb->WriteString(base::Format::ToString("\"%s\":{", + QuadToString(parent->quad)), false); + + i = i + 1; + int start = i; + + bool firstchild = true; + for (; i < start + count && i < index_; i++) { + if (!firstchild) { + bb->WriteString(",", false); + } + firstchild = false; + Entry *entry = &entries_[i]; + bb->WriteString(base::Format::ToString("\"%s\":\"%x\"", + QuadToString(entry->quad), entry->value), false); + } + + bb->WriteString("}", false); + } + bb->WriteString("}}", false); + bb->WriteString(""); + return bb; +} + +} // namespace wi diff --git a/game/stateframe.h b/game/stateframe.h new file mode 100644 index 0000000..8f57032 --- /dev/null +++ b/game/stateframe.h @@ -0,0 +1,67 @@ +#ifndef __STATEFRAME_H__ +#define __STATEFRAME_H__ + +#include "game/statetracker.h" +#include "base/bytebuffer.h" + +namespace wi { + +class StateFrame { // stf +private: + const char *QuadToString(dword quad); + + struct Entry { + dword quad; + dword value; + }; + Entry *entries_; + int count_; + int index_; + StateTracker *tracker_; + long cUpdates_; + bool block_; + +public: + StateFrame(StateTracker *tracker); + ~StateFrame(); + + bool Init(long cUpdates, bool block); + bool Grow(); + bool block() { return block_; } + + void AddValue(dword quad, dword value, int iCounter) { + if (index_ >= count_) { + if (!Grow()) { + return; + } + } + Entry *entry = &entries_[index_]; + entry->quad = quad; + entry->value = value; + index_++; + if (iCounter >= 0 && iCounter < index_) { + entries_[iCounter].value++; + } + } + + int AddCountedValue(dword quad) { + if (index_ >= count_) { + if (!Grow()) { + return -1; + } + } + Entry *entry = &entries_[index_]; + entry->quad = quad; + entry->value = 0; + index_++; + return index_ - 1; + } + + dword GetHash(); + base::ByteBuffer *ToJson(); + long updates() { return cUpdates_; } +}; + +} // namespace wi + +#endif // __STATEFRAME_H__ diff --git a/game/statetracker.cpp b/game/statetracker.cpp new file mode 100644 index 0000000..3576575 --- /dev/null +++ b/game/statetracker.cpp @@ -0,0 +1,145 @@ +#include "game/statetracker.h" +#include "game/stateframe.h" +#include "mpshared/misc.h" +#include "inc/rip.h" +#include "game/ht.h" +#include +#include + +#define kcFramesMax 200 + +namespace wi { + +StateTracker::StateTracker(dword gameid) : gameid_(gameid) { + frames_ = (StateFrame **)malloc(kcFramesMax * sizeof(StateFrame *)); + cFrames_ = 0; + framesFree_ = (StateFrame **)malloc(kcFramesMax * sizeof(StateFrame *)); + cFramesFree_ = 0; + cFramesAlloc_ = 200; + nextblock_ = true; +} + +StateTracker::~StateTracker() { + MoveToFreeList(cFrames_); + free(frames_); + if (framesFree_ != NULL) { + for (int i = 0; i < cFramesFree_; i++) { + StateFrame *frame = framesFree_[i]; + delete frame; + } + free(framesFree_); + } +} + +StateFrame *StateTracker::AddFrame(long cUpdates) { + // Move one frame to the free list if needed + + if (cFrames_ >= cFramesAlloc_) { + MoveToFreeList(1); + } + + // Alloc from the free list, otherwise alloc. + + StateFrame *frame = NULL; + if (cFramesFree_ != 0) { + frame = framesFree_[cFramesFree_ - 1]; + cFramesFree_--; + } else { + frame = new StateFrame(this); + } + if (frame == NULL) { + return NULL; + } + frame->Init(cUpdates, nextblock_); + nextblock_ = false; + + //Trace("add frame: %d block: %s", frame->updates(), + // frame->block() ? "yes" : "no"); + + // Add to the alloced list + + frames_[cFrames_] = frame; + cFrames_++; + return frame; +} + +void StateTracker::ExpireFrames(long cUpdates) { + //Trace("ExpireFrames: update %d", cUpdates); + + int i; + for (i = 0; i < cFrames_; i++) { + // Don't expire cUpdates even though all clients have hit it, + // because the client may not be at the next update yet, and it'll + // need that frame to calculate the hash for the update result. + StateFrame *frame = frames_[i]; + if (frame->updates() >= cUpdates) { + break; + } + //Trace("expiring frame: %d, block: %s", frame->updates(), + // frame->block() ? "yes" : "no"); + } + MoveToFreeList(i); +} + +void StateTracker::MoveToFreeList(int count) { + if (count == 0) { + return; + } + + for (int i = 0; i < count; i++) { + StateFrame *frame = frames_[i]; + cFrames_--; + framesFree_[cFramesFree_] = frame; + cFramesFree_++; + } + + memmove(&frames_[0], &frames_[count], ELEMENTSIZE(frames_) * cFrames_); +} + +base::ByteBuffer *StateTracker::ToJson() { + base::ByteBuffer *bb = new base::ByteBuffer(4096); + if (bb == NULL) { + return NULL; + } + + bb->WriteString("{", false); + bb->WriteString(base::Format::ToString("\"gameid\":\"%x\",", gameid_), + false); + + for (int i = 0; i < cFrames_; i++) { + StateFrame *frame = frames_[i]; + if (i != 0) { + bb->WriteString(",", false); + } + bb->WriteString(base::Format::ToString("\"frame%d\":", i), false); + base::ByteBuffer *bbFrame = frame->ToJson(); + if (bbFrame != NULL && bbFrame->Length() > 0) { + bb->WriteBytes(bbFrame->Data(), bbFrame->Length() - 1); + } + delete bbFrame; + } + bb->WriteString("}", false); + + // Zero terminate + bb->WriteString(""); + return bb; +} + +dword StateTracker::GetHash() { + // Calc the hash from the last "block" frame forward, since hashes + // between block updates are what is compared. + + dword h = 0; + for (int i = cFrames_ - 1; i >= 0; i--) { + StateFrame *frame = frames_[i]; + dword t = frame->GetHash(); + h = (h << (h & 3)) ^ t; + if (frame->block()) { + //Trace("GetHash: start update %d", frame->updates()); + break; + } + } + return h; +} + +} // namespace wi diff --git a/game/statetracker.h b/game/statetracker.h new file mode 100644 index 0000000..9f3f955 --- /dev/null +++ b/game/statetracker.h @@ -0,0 +1,36 @@ +#ifndef __STATETRACKER_H__ +#define __STATETRACKER_H__ + +#include "inc/basictypes.h" +#include "mpshared/mpht.h" +#include "base/bytebuffer.h" + +namespace wi { + +class StateFrame; +class StateTracker { +public: + StateTracker(dword gameid); + ~StateTracker(); + + StateFrame *AddFrame(long cUpdates); + void ExpireFrames(long cUpdates); + base::ByteBuffer *ToJson(); + dword GetHash(); + void SetNextBlock() { nextblock_ = true; } + +private: + void MoveToFreeList(int count); + + StateFrame **frames_; + int cFrames_; + StateFrame **framesFree_; + int cFramesFree_; + int cFramesAlloc_; + dword gameid_; + bool nextblock_; +}; + +} // namespace wi + +#endif // __STATETRACKER_H__ diff --git a/game/strings.h b/game/strings.h new file mode 100644 index 0000000..ecb12d1 --- /dev/null +++ b/game/strings.h @@ -0,0 +1,52 @@ +#ifndef __STRINGS_H__ +#define __STRINGS_H__ + +#ifndef __OBJC__ // SDL.h ends up including this when it is trying to include the CRT strings.h + +namespace wi { + +enum Strings { + kidsMinerUnderAttack, // Miner Under Attack! + kidsMinerNeedsGalaxite, // Bullpup can't find Galaxite! + kidsLowPower, // Low Power! + kidsWarehouseTooFull, // Need More Storage! + kidsBaseUnderAttack, // Base Under Attack! + kidsNewVehicleOptions, // New Vehicle Options! + kidsNewRecruitOptions, // New Recruit Options! + kidsNewStructureOptions, // New Structure Options! + kidsUnitLimitReached, // Unit Limit Reached! + kidsBuildingLimitReached, // Building Limit Reached! + kidsRankChallenger, // Challenger + kidsRank0, // Mining Operations Trainee + kidsRank1, // Mining Operations Specialist + kidsRank2, // Certified Basic Miner + kidsRank3, // Mining & Security Specialist + kidsRank4, // Certified Secure Miner + kidsRank5, // Senior Secure Miner + kidsRank6, // Lead Secure Miner + kidsRank7, // Secure Mining Site Supervisor + kidsRank8, // Secure Mining Supervisor + kidsRank9, // Secure Mining Base Commander + kidsRank10, // Jr. Director of Secure Mining + kidsRank11, // ACME Up-and-Comer + kidsRank12, // Operations Fast-Tracker + kidsRank13, // Operations Management Trainee + kidsRank14, // Operations Manager + kidsRank15, // Operations Middle-Manager + kidsRank16, // Operations Bureaucrat + kidsRank17, // Being Groomed For Executive + kidsRank18, // Operations Executive + kidsRank19, // Director of Icarus Operations + kidsRank20, // Director of ACME Ops Resources + kidsRank21, // Vice President of Operations + kidsRank22, // Executive VP of Operations + kidsRank23, // Senior Exec. VP of Operations + kidsRank24, // ACME Chief Operations Officer + kidsExitHost, // You are hosting the multiplayer game in progress. Are you sure you want to stop? + kidsExitClient, // You cannot return to a multiplayer game in progress. Are you sure you want to leave? +}; + +} // namespace wi + +#endif // ndef __OBJC__ +#endif // ndef __STRINGS_H__ diff --git a/game/stringtable.cpp b/game/stringtable.cpp new file mode 100644 index 0000000..bc5a312 --- /dev/null +++ b/game/stringtable.cpp @@ -0,0 +1,56 @@ +// stringtable.cpp +#include "ht.h" + +namespace wi { + +StringTable *gpstrtbl; + +StringTable::StringTable() +{ + m_pfil = NULL; +} + +StringTable::~StringTable() +{ + if (m_pfil != NULL) + gpakr.fclose(m_pfil); +} + +bool StringTable::Init(char *pszFilename) +{ + m_pfil = gpakr.fopen(pszFilename, "rb"); + return m_pfil != NULL; +} + +bool StringTable::GetString(int id, char *psz, int cb) +{ + Assert(m_pfil != NULL); + + // get index of string + + if (gpakr.fseek(m_pfil, 2 * id, SEEK_SET) != 0) + return false; + word off; + if (gpakr.fread(&off, sizeof(word), 1, m_pfil) != 1) + return false; + off = BigWord(off); + + // get length + if (gpakr.fseek(m_pfil, off, SEEK_SET) != 0) + return false; + byte cbT = 0; + if (gpakr.fread(&cbT, sizeof(byte), 1, m_pfil) != 1) + return false; + + if (cb < cbT) + cbT = cb - 1; + + // read in bytes and zero terminate string + if (gpakr.fread(psz, cbT, 1, m_pfil) != 1) + return false; + + psz[cbT] = 0; + return true; +} + +} // namespace wi \ No newline at end of file diff --git a/game/stylushandler.cpp b/game/stylushandler.cpp new file mode 100644 index 0000000..c9aded8 --- /dev/null +++ b/game/stylushandler.cpp @@ -0,0 +1,312 @@ +#include "game/ht.h" + +namespace wi { + +SimUIForm::StylusHandler::StylusHandler(SimUIForm *psui) +{ + m_psui = psui; + m_fDragging = false; +} + +bool SimUIForm::StylusHandler::OnPenEvent(Event *pevt, bool fScrollOnly) +{ + // Can't scroll the playfield from this PenHandler + + if (fScrollOnly) { + return true; + } + + // Dragging mode captures all pen events except pen-hold for the duration of + // the drag to perform drag-scrolling, etc (shift-drag or graffiti-scroll) + + if (m_fDragging && pevt->eType != penHoldEvent) { + OnPenDrag(pevt); + + // Force draw to keep dragging as interactive as possible + // Raises redraw priority above next update + + gevm.SetRedrawFlags(kfRedrawDirty | kfRedrawBeforeTimer); + return true; + } + + switch (pevt->eType) { + case penHoldEvent: + // Popup Gob menu if pen held on one of them + + OnPenHold(pevt); + break; + + case penDownEvent: + // Handle the beginning of a drag selection operation or a tap on a + // unit/structure + + OnPenDown(pevt); + break; + + case penUpEvent: + // Handle the canceling of any in progress modes + + OnPenUp(pevt); + break; + } + + return true; +} + +void SimUIForm::StylusHandler::OnPenDown(Event *pevt) +{ + // Enter dragging mode; this'll cause OnPenDrag to be called until + // that mode exits. + + m_fDragging = true; + + // Calc a hit target in world coords + + WCoord wxPen = WcFromPc(pevt->x); + WCoord wyPen = WcFromPc(pevt->y); + WCoord wxTarget, wyTarget; + gsim.GetViewPos(&wxTarget, &wyTarget); + wxTarget += wxPen; + wyTarget += wyPen; + + // s_wptSelect1 & 2 are used to determine the transition from a + // tentative selection to a real one and must contain the beginning + // and current pen positions, respectively. + + s_wptSelect1.wx = wxTarget; + s_wptSelect1.wy = wyTarget; + s_wptSelect2 = s_wptSelect1; + + if (gfLassoSelection) { + s_awptSelection[0] = s_wptSelect1; + s_cwptSelection = 1; + } else { + gwrcSelection.Set(s_wptSelect1, s_wptSelect2); + gsim.SetSelection(&gwrcSelection); + } + + // Only Select. Leave moving and attacking to the pen up handler. + + m_psui->MoveOrAttackOrSelect(pevt->x, pevt->y, kfMasSelect); +} + +void SimUIForm::StylusHandler::OnPenUp(Event *pevt) +{ + CancelModes(); +} + +// TUNE: +#define kwcMinSelection WcFromTile16ths(8) + +// The WI playfield finger UI: +// +// 1. One finger for selecting and rubber banding around troops +// 2. Two fingers for scrolling the map. +// 3. Two finger flick for momentum scrolls. +// +// Notes: +// - During a select operation, the second finger may come down. This cancels +// the selection and enters scroll mode. Units that were selected as part of +// entering the mode are unselected when entering scroll mode. Units that +// were selected before select mode was entered are left selected. +// +// - Once entered, scroll mode remains even if one of the fingers goes up +// +// - At the beginning of scroll mode, the first down is the reference +// point. +// +// - If in scroll mode, and one of the fingers goes up while the other remains +// down, the aggregate is still down, and the coordinate is based on the +// reference point so there is no map jumping. + +void SimUIForm::StylusHandler::OnPenDrag(Event *pevt) +{ + WCoord wxPen = WcFromPc(pevt->x); + WCoord wyPen = WcFromPc(pevt->y); + WCoord wxView, wyView; + gsim.GetViewPos(&wxView, &wyView); + + switch (pevt->eType) { + case penUpEvent: + // If this is a forced up event, cancel our modes + + if (pevt->dw == 1) { + CancelModes(); + break; + } + + // Real pen event. Leaving drag mode. + + m_fDragging = false; + + // If we're drag selecting complete the process. Otherwise handle this + // event as a tap on an enemy (attack it) or location (move to it) + + if (gfDragSelecting) { + // Invalidate this area first so we get it to redraw + + m_psui->InvalidateDragSelection(); + + // Turn off selection + + gfDragSelecting = false; + + if (gfLassoSelection && s_cwptSelection > 1) { + bool fGobsSelected = false; + + Gob *pgobT; + for (pgobT = ggobm.GetFirstGob(); pgobT != NULL; + pgobT = ggobm.GetNextGob(pgobT)) { + dword ff = pgobT->GetFlags(); + + // If were in select mode then only Gobs inside the + // selection rectangle get to be selected. + + bool fSelect = false; + if ((gfGodMode || pgobT->GetOwner() == gpplrLocal) && + ((ff & (kfGobActive | kfGobUnit)) == + (kfGobActive | kfGobUnit))) { + WPoint wptGobCenter; + pgobT->GetCenter(&wptGobCenter); + if (PtInPolygon(s_awptSelection, s_cwptSelection, + wptGobCenter.wx, wptGobCenter.wy)) { + fSelect = true; + fGobsSelected = true; + } + } + if (ff & kfGobUnit) { + ((UnitGob *)pgobT)->Select(fSelect); + } + } + + s_cwptSelection = 0; + } + } else { + m_psui->MoveOrAttackOrSelect(pevt->x, pevt->y, kfMasMove | kfMasAttack | kfMasShowMenu); + } + break; + + case penMoveEvent: + { + if (gfDragSelecting) { + // Inval old pos of selection rect + + m_psui->InvalidateDragSelection(); + } + + // Update() code uses s_wptSelect2 to determine if edge scrolling + // is necessary + + s_wptSelect2.wx = wxView + WcFromPc(pevt->x); + s_wptSelect2.wy = wyView + WcFromPc(pevt->y); + gwrcSelection.Set(s_wptSelect1, s_wptSelect2); + + if (!gfDragSelecting) { + if (gwrcSelection.Width() >= kwcMinSelection || + gwrcSelection.Height() >= kwcMinSelection) { + gfDragSelecting = true; + } + } + + if (gfDragSelecting) { + // Inval new size of selection rect + + m_psui->InvalidateDragSelection(); + + if (gfLassoSelection) { + AddPointToLassoSelection(s_wptSelect2); + } + + gsim.SetSelection(&gwrcSelection); + } + } + break; + } +} + +void SimUIForm::StylusHandler::OnPenHold(Event *pevt) +{ + WCoord wxTarget, wyTarget; + gsim.GetViewPos(&wxTarget, &wyTarget); + wxTarget += WcFromPc(pevt->x); + wyTarget += WcFromPc(pevt->y); + + Gob *pgobHit = NULL; + Enum enm; + if (!gsim.HitTest(&enm, wxTarget, wyTarget, kfGobActive | kfGobUnit, + &pgobHit)) { + return; + } + + // Don't popup menu if the player doesn't own the Gob (unless in God mode) + // God mode is limited for multiplayer games. + + if ((!gfGodMode || ggame.IsMultiplayer()) && + pgobHit->GetSide() != gpplrLocal->GetSide()) { + return; + } + + m_fDragging = false; + pgobHit->PopupMenu(); +} + +void SimUIForm::StylusHandler::CancelModes() +{ + m_fDragging = false; + + if (gfDragSelecting) { + // Invalidate this area first so we get it to redraw + + m_psui->InvalidateDragSelection(); + gfDragSelecting = false; + } +} + +void SimUIForm::StylusHandler::CheckScroll() +{ + // If the player is rectangle selecting and at the screen edge scroll the + // screen. + + if (!gfDragSelecting) { + return; + } + + Size sizPlayfield; + ggame.GetPlayfieldSize(&sizPlayfield); + WCoord wcxPlayfield = WcFromUpc(sizPlayfield.cx); + WCoord wcyPlayfield = WcFromUpc(sizPlayfield.cy); + + WCoord wxView, wyView; + gsim.GetViewPos(&wxView, &wyView); + + WCoord wxViewNew = wxView; + WCoord wyViewNew = wyView; + + if (s_wptSelect2.wx < wxView + kwcScrollBorderSize) { + wxViewNew -= kwcScrollStepSize; + } else if (s_wptSelect2.wx > wxView + wcxPlayfield - kwcScrollBorderSize) { + wxViewNew += kwcScrollStepSize; + } + + if (s_wptSelect2.wy < wyView + kwcScrollBorderSize) { + wyViewNew -= kwcScrollStepSize; + } else if (s_wptSelect2.wy > wyView + wcyPlayfield - kwcScrollBorderSize) { + wyViewNew += kwcScrollStepSize; + } + + if (wxViewNew != wxView || wyViewNew != wyView) { + gsim.SetViewPos(wxViewNew, wyViewNew); + WCoord wxViewActual, wyViewActual; + gsim.GetViewPos(&wxViewActual, &wyViewActual); + s_wptSelect2.wx += wxViewActual - wxView; + s_wptSelect2.wy += wyViewActual - wyView; + gwrcSelection.Set(s_wptSelect1, s_wptSelect2); + + if (gfLassoSelection) + AddPointToLassoSelection(s_wptSelect2); + + gsim.SetSelection(&gwrcSelection); + } +} + +} // namespace wi diff --git a/game/tbitmap.cpp b/game/tbitmap.cpp new file mode 100644 index 0000000..f6cca81 --- /dev/null +++ b/game/tbitmap.cpp @@ -0,0 +1,1586 @@ +#include "ht.h" + +namespace wi { + +dword *TBitmap::s_ampscaiclrSide[kcColoredSides]; + +int gcyClipBuffer; +TBitmap *LoadTBitmap(char *pszFn) +{ + // Init clip buffer size for quick query + + if (gpbmClip != NULL) { + Size sizClip; + gpbmClip->GetSize(&sizClip); + gcyClipBuffer = sizClip.cy; + } + + TBitmap *ptbm = new TBitmap(); + if (ptbm == NULL) + return NULL; + if (!ptbm->Init(pszFn)) { + delete ptbm; + return NULL; + } + + return ptbm; +} + +// Some things want to pool shared TBitmaps + +struct SharedRecord +{ + TBitmap *ptbm; + char sz[1]; +}; + +int gcshr; +int gcshrAlloc; +SharedRecord **gapshr; + +void FreeSharedTBitmaps() +{ + for (int n = 0; n < gcshr; n++) { + SharedRecord *pshr = gapshr[n]; + TBitmap *ptbm = pshr->ptbm; + ptbm->ClearShared(); + delete pshr->ptbm; + gmmgr.FreePtr(pshr); + } + delete gapshr; + gapshr = NULL; + gcshr = 0; + gcshrAlloc = 0; +} + +void FindSharedTBitmapFilename(TBitmap *ptbm, char *psz, int cb) +{ + *psz = 0; + for (int n = 0; n < gcshr; n++) { + SharedRecord *pshr = gapshr[n]; + if (pshr->ptbm == ptbm) { + strncpyz(psz, pshr->sz, cb); + return; + } + } + + Assert(); +} + +#define kcshrGrow 32 + +TBitmap *GetSharedTBitmap(char *pszFn) +{ + // First try to find it + // Note could be faster with a binary search assuming the names are sorted + + SharedRecord **ppshrMax = &gapshr[gcshr]; + for (SharedRecord **ppshr = gapshr; ppshr < ppshrMax; ppshr++) { + SharedRecord *pshr = *ppshr; + if (strcmp(pszFn, pshr->sz) == 0) { + return pshr->ptbm; + } + } + + // Couldn't find it, add it to the list + // Grow list? + + if (gcshr == gcshrAlloc) { + SharedRecord **ppshr = new SharedRecord *[gcshrAlloc + kcshrGrow]; + if (ppshr == NULL) + return NULL; + if (gapshr != NULL) { + memcpy(ppshr, gapshr, sizeof(SharedRecord *) * gcshr); + delete gapshr; + } + gapshr = ppshr; + gcshrAlloc += kcshrGrow; + } + Assert(gcshrAlloc > gcshr); + + // Load the TBitmap + + TBitmap *ptbm = LoadTBitmap(pszFn); + if (ptbm == NULL) + return NULL; + ptbm->SetShared(); + + // Alloc record + + SharedRecord *pshr = (SharedRecord *)gmmgr.AllocPtr(sizeof(SharedRecord) - 1 + strlen(pszFn) + 1); + if (pshr == NULL) { + delete ptbm; + return NULL; + } + + // Add it to the list and fill structure + + gapshr[gcshr] = pshr; + gcshr++; + gmmgr.WritePtr(pshr, OFFSETOF(SharedRecord, ptbm), &ptbm, sizeof(ptbm)); + gmmgr.WritePtr(pshr, OFFSETOF(SharedRecord, sz), pszFn, strlen(pszFn) + 1); + + // Done + + return ptbm; +} + +bool TBitmap::InitClass() +{ + static int aiclr[kcColoredSides][2] = { + { kiclrNeutralSideFirst, kiclrNeutralSideLast }, + { kiclrBlueSideFirst, kiclrBlueSideLast }, + { kiclrRedSideFirst, kiclrRedSideLast }, + { kiclrYellowSideFirst, kiclrYellowSideLast }, + { kiclrCyanSideFirst, kiclrCyanSideLast }, + { kiclrWhite, kiclrWhite } + }; + + // Create the side code lookup tables + +#define kcSideColors 5 +#define kcSideCodeEntries (kcSideColors * kcSideColors * kcSideColors * kcSideColors) + + dword *mpscaiclrSideT = new dword[kcSideCodeEntries]; + word cbAlloc = kcSideCodeEntries * sizeof(dword); + + for (int i = 0; i < kcColoredSides; i++) { + Color aclr[5]; + int iclrFirst = aiclr[i][0]; + int iclrLast = aiclr[i][1]; + if (iclrFirst == iclrLast) { + for (int i = 0; i < 5; i++) + aclr[i] = GetColor(iclrFirst); + } else { + Assert(iclrLast - iclrFirst + 1 == 5); + for (int iclr = iclrFirst; iclr <= iclrLast; iclr++) + aclr[iclr - iclrFirst] = GetColor(iclr); + } + + MakeSideCodeMapping(aclr, mpscaiclrSideT); + + // We need more side code entries in order to flash a hires Replicator white + + if (iclrFirst == iclrLast && gcxTile >= 24) + s_ampscaiclrSide[i] = (dword *)gmmgr.AllocPtr(cbAlloc * 2); + else + s_ampscaiclrSide[i] = (dword *)gmmgr.AllocPtr(cbAlloc); + if (s_ampscaiclrSide[i] == NULL) { + delete mpscaiclrSideT; + return false; + } + gmmgr.WritePtr(s_ampscaiclrSide[i], 0, mpscaiclrSideT, cbAlloc); + if (iclrFirst == iclrLast && gcxTile >= 24) + gmmgr.WritePtr(s_ampscaiclrSide[i], cbAlloc, mpscaiclrSideT, cbAlloc); + } + delete mpscaiclrSideT; + + // Done + + return true; +} + +void TBitmap::MakeSideCodeMapping(Color *aclr, dword *mpscaiclrSide) +{ + int sc = 0; + for (int i = 0; i < 5; i++) { + for (int j = 0; j < 5; j++) { + for (int k = 0; k < 5; k++) { + for (int m = 0; m < 5; m++) { + byte ab[4]; + ab[0] = (byte)aclr[i]; + ab[1] = (byte)aclr[j]; + ab[2] = (byte)aclr[k]; + ab[3] = (byte)aclr[m]; + mpscaiclrSide[sc++] = *((dword *)ab); + } + } + } + } +} + +void TBitmap::ExitClass() +{ + for (int i = 0; i < kcColoredSides; i++) { + gmmgr.FreePtr(s_ampscaiclrSide[i]); + s_ampscaiclrSide[i] = NULL; + } +} + +TBitmap::TBitmap() +{ + m_pfil = NULL; + m_ibtbh = 0; + m_wf = 0; + m_ctbm = 0; + m_atbe = NULL; + m_ahc = NULL; +} + +TBitmap::~TBitmap() +{ + Assert(!(m_wf & kfTbShared)); + + delete m_ahc; + m_ahc = NULL; + if (m_atbe != NULL) { + gmmgr.FreePtr(m_atbe); + m_atbe = NULL; + } + if (m_pfil != NULL && (m_wf & kfTbCloseFile) != 0) { + gpakr.fclose(m_pfil); + m_pfil = NULL; + } +} + +bool TBitmap::Init(char *pszFn) +{ + File *pfil = gpakr.fopen(pszFn, "rb"); + if (pfil == NULL) { + return false; + } + if (!Init(pfil, 0)) { + gpakr.fclose(pfil); + return false; + } + m_wf |= kfTbCloseFile; + return true; +} + +bool TBitmap::Init(File *pfil, word ib) +{ + // How many? + + if (gpakr.fseek(pfil, (long)ib, SEEK_SET) != 0) + return false; + word ctbmT; + if (gpakr.fread(&ctbmT, sizeof(ctbmT), 1, pfil) == 0) + return false; + m_ctbm = BigWord(ctbmT); + + // Alloc array of cache handles. These get stored in ram + + m_ahc = new CacheHandle[m_ctbm * 2]; + if (m_ahc == NULL) + return false; + memset(m_ahc, 0, sizeof(CacheHandle) * m_ctbm * 2); + + // Allocate space for entry headers, read them in, store + // in db ram. + + TBitmapEntry *ptbe = new TBitmapEntry[m_ctbm]; + if (ptbe == NULL) + return false; + for (int itbe = 0; itbe < m_ctbm; itbe++) { + TBitmapEntry *ptbeT = &ptbe[itbe]; + if ((int)gpakr.fread(ptbeT, kcbTBitmapEntry, 1, pfil) != 1) { + return false; + } +#ifndef __CPU_68K + // Swap bytes (non-Palm only) + + ptbeT->cx = BigWord(ptbeT->cx); + ptbeT->cy = BigWord(ptbeT->cy); + ptbeT->yBaseline = BigWord(ptbeT->yBaseline); + ptbeT->ibsd = BigWord(ptbeT->ibsd); + ptbeT->cbsd = BigWord(ptbeT->cbsd); +#endif + } + + word cbAlloc = sizeof(TBitmapEntry) * m_ctbm; + TBitmapEntry *ptbeT = (TBitmapEntry *)gmmgr.AllocPtr(cbAlloc); + if (ptbeT == NULL) { + delete ptbe; + return false; + } + gmmgr.WritePtr(ptbeT, 0, ptbe, cbAlloc); + m_atbe = ptbeT; + delete ptbe; + + // Remember file and offset + + m_pfil = pfil; + m_ibtbh = ib; + return true; +} + +void TBitmap::GetSize(Size *psiz) +{ + GetSize(0, psiz); +} + +void TBitmap::GetSize(int itbm, Size *psiz) +{ + Assert(itbm >= 0 && itbm < m_ctbm); + psiz->cx = (int)m_atbe[itbm].cx; + psiz->cy = (int)m_atbe[itbm].cy; +} + +extern "C" { +void CopyBits128Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) secCode8; +void CopyBits124Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) secCode8; +void CopyBits120Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) secCode8; +void CopyBits116Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) secCode8; +void CopyBits112Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) secCode8; +void CopyBits108Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) secCode8; +void CopyBits104Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) secCode8; +void CopyBits100Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) secCode8; +void CopyBits96Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) secCode8; +void CopyBits92Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) secCode8; +void CopyBits88Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) secCode8; +void CopyBits84Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) secCode8; +void CopyBits80Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) secCode8; +void CopyBits76Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) secCode8; +void CopyBits72Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) secCode8; +void CopyBits68Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) secCode8; +void CopyBits64Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) secCode8; +void CopyBits60Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) secCode8; +void CopyBits56Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) secCode8; +void CopyBits52Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) secCode8; +void CopyBits48Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) secCode8; +void CopyBits44Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) secCode8; +void CopyBits40Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) secCode8; +void CopyBits36Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) secCode8; +void CopyBits32Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) secCode8; +void CopyBits28Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) secCode8; +void CopyBits24Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) secCode8; +void CopyBits20Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) secCode8; +void CopyBits16Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) secCode8; +void CopyBits12Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) secCode8; +void CopyBits8Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) secCode8; +void CopyBits4Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) secCode8; +}; + +typedef void (*CopyBy4Proc)(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy); +CopyBy4Proc gapfnCopyBy4[kcCopyBy4Procs] = { + CopyBits4Wide, CopyBits8Wide, CopyBits12Wide, CopyBits16Wide, CopyBits20Wide, CopyBits24Wide, + CopyBits28Wide, CopyBits32Wide, CopyBits36Wide, CopyBits40Wide, CopyBits44Wide, CopyBits48Wide, + CopyBits52Wide, CopyBits56Wide, CopyBits60Wide, CopyBits64Wide, CopyBits68Wide, CopyBits72Wide, + CopyBits76Wide, CopyBits80Wide, CopyBits84Wide, CopyBits88Wide, CopyBits92Wide, CopyBits96Wide, + CopyBits100Wide, CopyBits104Wide, CopyBits108Wide, CopyBits112Wide, CopyBits116Wide, CopyBits120Wide, + CopyBits124Wide, CopyBits128Wide +}; + +void TBitmap::BltTo(class DibBitmap *pbmDst, int xDst, int yDst, Side side, Rect *prcSrc) +{ + BltTo(0, pbmDst, xDst, yDst, side, prcSrc); +} + +void TBitmap::BltTo(class DibBitmap *pbmDst, int xDst, int yDst, Rect *prcSrc) +{ + BltTo(0, pbmDst, xDst, yDst, ksideNeutral, prcSrc); +} + +void TBitmap::BltTo(int itbm, class DibBitmap *pbmDst, int xDst, int yDst, Side side, Rect *prcSrc) +{ + // Get tbm dimensions + + Assert(itbm >= 0 && itbm < m_ctbm); + int cx = (int)m_atbe[itbm].cx; + int cy = (int)m_atbe[itbm].cy; + + // Source rect to blt from + + Rect rcSrc; + if (prcSrc != NULL) { + rcSrc = *prcSrc; + } else { + rcSrc.left = 0; + rcSrc.top = 0; + rcSrc.right = cx; + rcSrc.bottom = cy; + } + + // Get dib dimensions + + Size sizT; + pbmDst->GetSize(&sizT); + + // Right and bottom edge clipping + + if (sizT.cx - xDst < rcSrc.Width()) + rcSrc.right = rcSrc.left + sizT.cx - xDst; + if (sizT.cy - yDst < rcSrc.Height()) + rcSrc.bottom = rcSrc.top + sizT.cy - yDst; + + // Left and top edge clipping + + int xDstT = xDst; + if (xDstT < 0) { + rcSrc.left -= xDstT; + xDstT = 0; + } + int yDstT = yDst; + if (yDstT < 0) { + rcSrc.top -= yDstT; + yDstT = 0; + } + + // Anything to blt? + + if (rcSrc.IsEmpty()) + return; + +#ifdef STATS_DISPLAY + extern int gcBitmapsDrawn; + gcBitmapsDrawn++; +#endif + +#if 1 + byte *pbBits = pbmDst->GetBits(); +#else + BitmapType *pbmpScreen = WinGetBitmap(WinGetDisplayWindow()); + byte *pbBits = (byte *)BmpGetBits(pbmpScreen); + Fill(pbBits, 160, 160, 160, 1); +#endif + + // Look in the cache to see if this tbitmap has been compiled + + byte *pbDraw = GetCompiledBits(itbm, (xDst & 1) != 0); + if (pbDraw == NULL) + return; + + // If it is not clipped, draw directly to the dib, otherwise clip. + + if (rcSrc.Width() == cx && rcSrc.Height() == cy) { + // Draw + + int cbRowDst = (sizT.cx + 1) & ~1; + byte *pbDst = pbBits + (long)yDstT * cbRowDst + xDstT; + + if ((short)side < 0) { + DrawDispatchThunk(pbDraw, (byte *)s_ampscaiclrSide[5], pbDst, cbRowDst - cx, s_ampscaiclrSide[5], gmpiclriclrShadow); + } else { + DrawDispatchThunk(pbDraw, NULL, pbDst, cbRowDst - cx, s_ampscaiclrSide[side], gmpiclriclrShadow); + } + } else { + // If there is no fast copy proc for this size, then blt this slower + // way, which can clip any size. + + int cxMax = kcCopyBy4Procs * 4; + if (cx > cxMax || cy > cxMax) { + BltToScan(pbDraw, cx, cy, pbmDst, xDstT, yDstT, side, &rcSrc); + return; + } + + if (gpbmClip != NULL && gcyClipBuffer >= cy) { + Rect rc; + rc.left = xDstT & ~3; + rc.top = yDstT; + rc.right = rc.left + (((xDstT & 3) + rcSrc.Width() + 3) & ~3); + rc.bottom = rc.top + rcSrc.Height(); + int xDstClip = (((xDst & 3) + rcSrc.left) & ~3); + + gpbmClip->Blt(pbmDst, &rc, xDstClip, rcSrc.top); + byte *pbDrawAt = gpbmClip->GetBits() + (xDst & 3); + Size sizClip; + gpbmClip->GetSize(&sizClip); + int cbDrawReturn = sizClip.cx - cx; + + if ((short)side < 0) { + DrawDispatchThunk(pbDraw, (byte *)s_ampscaiclrSide[5], pbDrawAt, cbDrawReturn, s_ampscaiclrSide[5], gmpiclriclrShadow); + } else { + DrawDispatchThunk(pbDraw, NULL, pbDrawAt, cbDrawReturn, s_ampscaiclrSide[side], gmpiclriclrShadow); + } + + rc.Offset(xDstClip - rc.left, rcSrc.top - rc.top); + pbmDst->Blt(gpbmClip, &rc, xDstT & ~3, yDstT); + } else { + // We want to use gpbScratch to hold the screen bits, so if our cache alloc failed, don't + // do the operation + + if (pbDraw == gpbScratch) + return; + + // Copy from screen, draw, copy back to screen + + int xCopy = xDstT & ~3; + byte *pbDib = pbBits + (long)yDstT * sizT.cx + xCopy; + + int cbRunCopy = ((xDst & 3) + cx + 3) & ~3; + byte *pbCopy = gpbScratch + rcSrc.top * cbRunCopy + (((xDst & 3) + rcSrc.left) & ~3); + + int cxCopy = ((xDstT & 3) + rcSrc.Width() + 3) & ~3; + Assert((cxCopy >> 2) - 1 < kcCopyBy4Procs); + if (cxCopy > kcCopyBy4Procs * 4) + return; + CopyBy4Proc pfnCopy = gapfnCopyBy4[(cxCopy >> 2) - 1]; + pfnCopy(pbDib, sizT.cx - cxCopy, pbCopy, cbRunCopy - cxCopy, rcSrc.Height()); + + // Draw into buffer + + byte *pbDrawAt = gpbScratch + (xDst & 3); + int cbDrawReturn = cbRunCopy - cx; + + if ((short)side < 0) { + DrawDispatchThunk(pbDraw, (byte *)s_ampscaiclrSide[5], pbDrawAt, cbDrawReturn, s_ampscaiclrSide[5], gmpiclriclrShadow); + } else { + DrawDispatchThunk(pbDraw, NULL, pbDrawAt, cbDrawReturn, s_ampscaiclrSide[side], gmpiclriclrShadow); + } + + // Copy back to screen + + pfnCopy(pbCopy, cbRunCopy - cxCopy, pbDib, sizT.cx - cxCopy, rcSrc.Height()); + } + } +} + +void TBitmap::BltToScan(byte *pbDraw, int cx, int cy, DibBitmap *pbmDst, + int xDst, int yDst, Side side, Rect *prcSrc) +{ + // Clipping has already been performed + +#ifdef STATS_DISPLAY + extern int gcBitmapsDrawn; + gcBitmapsDrawn++; +#endif + + byte *pbBits = pbmDst->GetBits(); + Size sizT; + pbmDst->GetSize(&sizT); + + int cbRowDst = (sizT.cx + 1) & ~1; + byte *pbDst = pbBits + (long)yDst * cbRowDst + xDst; + + word *pw = (word *)pbDraw; + word *psc = (word *)(pbDraw + pw[0]); + byte *pbSrc = pbDraw + pw[1]; + byte *pop = pbDraw + sizeof(word) + sizeof(word); + + // Scan past any piece of the source vertically clipped + // at the top + + int y = 0; + int xoffset = 0; + if (prcSrc->top > 0) { + xoffset = YClipToScan(prcSrc->top, cx, pop, pbSrc, psc); + y = prcSrc->top; + } + + if (prcSrc->Width() == cx) { + // Not horz clipped; draw each scan directly to dest + pbDst += xoffset; + for (; y < prcSrc->bottom; y++) { + xoffset = DrawScan(pbDst, cx, pbSrc, pop, psc, + s_ampscaiclrSide[side], gmpiclriclrShadow); + pbDst += xoffset + (cbRowDst - cx); + } + } else { + // Horz clipped; copy from dst to temp scan, draw onto scan, + // copy from scan back to dst + + byte *pbScan = gpbmClip->GetBits(); + byte *pbCopyTo = pbScan + prcSrc->left; + + // Draw the scans + for (; y < prcSrc->bottom; y++) { + memcpy(pbCopyTo, pbDst, prcSrc->Width()); + xoffset += DrawScan(&pbScan[xoffset], cx, pbSrc, pop, psc, + s_ampscaiclrSide[side], gmpiclriclrShadow) - cx; + memcpy(pbDst, pbCopyTo, prcSrc->Width()); + pbDst += cbRowDst; + } + } +} + +byte *TBitmap::GetCompiledBits(int itbm, bool fOdd) +{ + CacheHandle *phc = &m_ahc[itbm * 2 + (fOdd ? 1 : 0)]; + byte *pbDraw = (byte *)gcam.GetPtr(*phc); + if (pbDraw == NULL) { + // Need to get the ScanData first. This may fail. + // Note: load ScanData into end gpbScratch, compile to + // beginning. _Should_ work. + + TBitmapEntry *ptbe = &m_atbe[itbm]; + if (gpakr.fseek(m_pfil, m_ibtbh + ptbe->ibsd, SEEK_SET) != 0) + return NULL; + ScanData *psd = (ScanData *)(gpbScratch + gcbScratch - ((ptbe->cbsd + 1) & ~1)); + if (gpakr.fread(psd, ptbe->cbsd, 1, m_pfil) != 1) + return NULL; + word cb = Compile8Thunk(gpbScratch, psd, (fOdd ? 1 : 0)); + *phc = gcam.NewObject(gpbScratch, cb); + pbDraw = (byte *)gcam.GetPtr(*phc); + if (pbDraw == NULL) + pbDraw = gpbScratch; + } + return pbDraw; +} + +void TBitmap::FillTo(int itbm, class DibBitmap *pbmDst, int xDst, int yDst, int cxDst, int cyDst, int xOrigin, int yOrigin) +{ + // Clip to the dib + + int cxTbm = m_atbe[itbm].cx; + int cyTbm = m_atbe[itbm].cy; + + if (xDst < 0) { + xOrigin = -(xDst - xOrigin) % cxTbm; + cxDst += xDst; + xDst = 0; + } + if (yDst < 0) { + yOrigin = -(yDst - yOrigin) % cyTbm; + cyDst += yDst; + yDst = 0; + } + Size sizDib; + pbmDst->GetSize(&sizDib); + if (xDst + cxDst > sizDib.cx) + cxDst = sizDib.cx - xDst; + if (yDst + cyDst > sizDib.cy) + cyDst = sizDib.cy - yDst; + + // Determine what the non-clipped "inside" is + + int xLeftInside; + if (xOrigin == 0) { + xLeftInside = xDst; + } else { + xLeftInside = xDst + cxTbm - xOrigin; + } + + int yTopInside; + if (yOrigin == 0) { + yTopInside = yDst; + } else { + yTopInside = yDst + cyTbm - yOrigin; + } + + int xRightInside; + if (xOrigin == 0 && cxDst == cxTbm) { + xRightInside = xDst + cxDst; + } else { + xRightInside = xDst + cxDst - ((xDst + cxDst) - (xDst - xOrigin)) % cxTbm; + } + + int yBottomInside; + if (yOrigin == 0 && cyDst == cyTbm) { + yBottomInside = yDst + cyDst; + } else { + yBottomInside = yDst + cyDst - ((yDst + cyDst) - (yDst - yOrigin)) % cyTbm; + } + + // Draw inside if there is anything to do + // OPT: Could write this part in assembly + + if (yTopInside < yBottomInside && xLeftInside < xRightInside) { + int cbRowDst = pbmDst->GetRowBytes(); + byte *pbRow = pbmDst->GetBits() + (long)yTopInside * sizDib.cx + xLeftInside; + word cbNextRow = cyTbm * cbRowDst; + byte *pbDraw = GetCompiledBits(itbm, (xLeftInside & 1) != 0); + if (pbDraw == NULL) + return; + for (int y = yTopInside; y < yBottomInside; y += cyTbm) { + byte *pbDst = pbRow; + for (int x = xLeftInside; x < xRightInside; x += cxTbm) { + DrawDispatchThunk(pbDraw, NULL, pbDst, cbRowDst - cxTbm, s_ampscaiclrSide[kside1], gmpiclriclrShadow); + pbDst += cxTbm; + } + pbRow += cbNextRow; + } + } + + // Draw edges + + if (xLeftInside <= xRightInside) { + if (xLeftInside != xDst) { + Rect rcT; + rcT.Set(-xOrigin, -yOrigin, cxTbm - xOrigin, cyTbm - yOrigin); + for (int y = yDst; y < yDst + cyDst; y += cyTbm) { + BltTo(itbm, pbmDst, xLeftInside - cxTbm, y, kside1, /*&rcT*/ NULL); + rcT.top = 0; + rcT.bottom = (yDst + cyDst) - y; + if (rcT.bottom > cyTbm) + rcT.bottom = cyTbm; + } + } + if (xRightInside != xDst + cxDst) { + Rect rcT; + rcT.Set(0, -yOrigin, xDst + cxDst - xRightInside, cyTbm - yOrigin); + for (int y = yDst; y < yDst + cyDst; y += cyTbm) { + BltTo(itbm, pbmDst, xRightInside, y, kside1, /*&rcT*/ NULL); + rcT.top = 0; + rcT.bottom = (yDst + cyDst) - y; + if (rcT.bottom > cyTbm) + rcT.bottom = cyTbm; + } + } + } + if (yTopInside <= yBottomInside) { + if (yTopInside != yDst) { + Rect rcT; + rcT.Set(0, -yOrigin, cxTbm, cyTbm - yOrigin); + for (int x = xLeftInside; x < xRightInside; x += cxTbm) { + rcT.right = xRightInside - x; + if (rcT.right > cxTbm) + rcT.right = cxTbm; + BltTo(itbm, pbmDst, x, yTopInside, kside1, &rcT); + } + } + if (yBottomInside != yDst + cyDst) { + Rect rcT; + rcT.Set(0, -yOrigin, cxTbm, cyTbm - yOrigin); + for (int x = xLeftInside; x < xRightInside; x += cxTbm) { + rcT.right = xRightInside - x; + if (rcT.right > cxTbm) + rcT.right = cxTbm; + BltTo(itbm, pbmDst, x, yBottomInside, kside1, &rcT); + } + } + } +} + +// C code for compiling / drawing + +#ifndef __CPU_68K +void CopyBits128Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) +{ + dword *pdwSrc = (dword *)pbSrc; + dword *pdwDst = (dword *)pbDst; + while (cy-- != 0) { + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + pdwSrc = (dword *)((byte *)pdwSrc + cbSrcReturn); + pdwDst = (dword *)((byte *)pdwDst + cbDstReturn); + } +} + +void CopyBits124Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) +{ + dword *pdwSrc = (dword *)pbSrc; + dword *pdwDst = (dword *)pbDst; + while (cy-- != 0) { + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + pdwSrc = (dword *)((byte *)pdwSrc + cbSrcReturn); + pdwDst = (dword *)((byte *)pdwDst + cbDstReturn); + } +} + +void CopyBits120Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) +{ + dword *pdwSrc = (dword *)pbSrc; + dword *pdwDst = (dword *)pbDst; + while (cy-- != 0) { + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + pdwSrc = (dword *)((byte *)pdwSrc + cbSrcReturn); + pdwDst = (dword *)((byte *)pdwDst + cbDstReturn); + } +} + +void CopyBits116Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) +{ + dword *pdwSrc = (dword *)pbSrc; + dword *pdwDst = (dword *)pbDst; + while (cy-- != 0) { + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + pdwSrc = (dword *)((byte *)pdwSrc + cbSrcReturn); + pdwDst = (dword *)((byte *)pdwDst + cbDstReturn); + } +} + +void CopyBits112Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) +{ + dword *pdwSrc = (dword *)pbSrc; + dword *pdwDst = (dword *)pbDst; + while (cy-- != 0) { + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + pdwSrc = (dword *)((byte *)pdwSrc + cbSrcReturn); + pdwDst = (dword *)((byte *)pdwDst + cbDstReturn); + } +} + +void CopyBits108Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) +{ + dword *pdwSrc = (dword *)pbSrc; + dword *pdwDst = (dword *)pbDst; + while (cy-- != 0) { + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + pdwSrc = (dword *)((byte *)pdwSrc + cbSrcReturn); + pdwDst = (dword *)((byte *)pdwDst + cbDstReturn); + } +} + +void CopyBits104Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) +{ + dword *pdwSrc = (dword *)pbSrc; + dword *pdwDst = (dword *)pbDst; + while (cy-- != 0) { + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + pdwSrc = (dword *)((byte *)pdwSrc + cbSrcReturn); + pdwDst = (dword *)((byte *)pdwDst + cbDstReturn); + } +} + +void CopyBits100Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) +{ + dword *pdwSrc = (dword *)pbSrc; + dword *pdwDst = (dword *)pbDst; + while (cy-- != 0) { + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + pdwSrc = (dword *)((byte *)pdwSrc + cbSrcReturn); + pdwDst = (dword *)((byte *)pdwDst + cbDstReturn); + } +} + +void CopyBits96Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) +{ + dword *pdwSrc = (dword *)pbSrc; + dword *pdwDst = (dword *)pbDst; + while (cy-- != 0) { + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + pdwSrc = (dword *)((byte *)pdwSrc + cbSrcReturn); + pdwDst = (dword *)((byte *)pdwDst + cbDstReturn); + } +} + +void CopyBits92Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) +{ + dword *pdwSrc = (dword *)pbSrc; + dword *pdwDst = (dword *)pbDst; + while (cy-- != 0) { + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + pdwSrc = (dword *)((byte *)pdwSrc + cbSrcReturn); + pdwDst = (dword *)((byte *)pdwDst + cbDstReturn); + } +} + +void CopyBits88Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) +{ + dword *pdwSrc = (dword *)pbSrc; + dword *pdwDst = (dword *)pbDst; + while (cy-- != 0) { + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + pdwSrc = (dword *)((byte *)pdwSrc + cbSrcReturn); + pdwDst = (dword *)((byte *)pdwDst + cbDstReturn); + } +} + +void CopyBits84Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) +{ + dword *pdwSrc = (dword *)pbSrc; + dword *pdwDst = (dword *)pbDst; + while (cy-- != 0) { + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + pdwSrc = (dword *)((byte *)pdwSrc + cbSrcReturn); + pdwDst = (dword *)((byte *)pdwDst + cbDstReturn); + } +} + +void CopyBits80Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) +{ + dword *pdwSrc = (dword *)pbSrc; + dword *pdwDst = (dword *)pbDst; + while (cy-- != 0) { + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + pdwSrc = (dword *)((byte *)pdwSrc + cbSrcReturn); + pdwDst = (dword *)((byte *)pdwDst + cbDstReturn); + } +} + +void CopyBits76Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) +{ + dword *pdwSrc = (dword *)pbSrc; + dword *pdwDst = (dword *)pbDst; + while (cy-- != 0) { + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + pdwSrc = (dword *)((byte *)pdwSrc + cbSrcReturn); + pdwDst = (dword *)((byte *)pdwDst + cbDstReturn); + } +} + +void CopyBits72Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) +{ + dword *pdwSrc = (dword *)pbSrc; + dword *pdwDst = (dword *)pbDst; + while (cy-- != 0) { + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + pdwSrc = (dword *)((byte *)pdwSrc + cbSrcReturn); + pdwDst = (dword *)((byte *)pdwDst + cbDstReturn); + } +} + +void CopyBits68Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) +{ + dword *pdwSrc = (dword *)pbSrc; + dword *pdwDst = (dword *)pbDst; + while (cy-- != 0) { + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + pdwSrc = (dword *)((byte *)pdwSrc + cbSrcReturn); + pdwDst = (dword *)((byte *)pdwDst + cbDstReturn); + } +} + +void CopyBits64Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) +{ + dword *pdwSrc = (dword *)pbSrc; + dword *pdwDst = (dword *)pbDst; + while (cy-- != 0) { + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + pdwSrc = (dword *)((byte *)pdwSrc + cbSrcReturn); + pdwDst = (dword *)((byte *)pdwDst + cbDstReturn); + } +} + +void CopyBits60Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) +{ + dword *pdwSrc = (dword *)pbSrc; + dword *pdwDst = (dword *)pbDst; + while (cy-- != 0) { + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + pdwSrc = (dword *)((byte *)pdwSrc + cbSrcReturn); + pdwDst = (dword *)((byte *)pdwDst + cbDstReturn); + } +} + +void CopyBits56Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) +{ + dword *pdwSrc = (dword *)pbSrc; + dword *pdwDst = (dword *)pbDst; + while (cy-- != 0) { + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + pdwSrc = (dword *)((byte *)pdwSrc + cbSrcReturn); + pdwDst = (dword *)((byte *)pdwDst + cbDstReturn); + } +} + +void CopyBits52Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) +{ + dword *pdwSrc = (dword *)pbSrc; + dword *pdwDst = (dword *)pbDst; + while (cy-- != 0) { + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + pdwSrc = (dword *)((byte *)pdwSrc + cbSrcReturn); + pdwDst = (dword *)((byte *)pdwDst + cbDstReturn); + } +} + +void CopyBits48Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) +{ + dword *pdwSrc = (dword *)pbSrc; + dword *pdwDst = (dword *)pbDst; + while (cy-- != 0) { + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + pdwSrc = (dword *)((byte *)pdwSrc + cbSrcReturn); + pdwDst = (dword *)((byte *)pdwDst + cbDstReturn); + } +} + +void CopyBits44Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) +{ + dword *pdwSrc = (dword *)pbSrc; + dword *pdwDst = (dword *)pbDst; + while (cy-- != 0) { + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + pdwSrc = (dword *)((byte *)pdwSrc + cbSrcReturn); + pdwDst = (dword *)((byte *)pdwDst + cbDstReturn); + } +} + +void CopyBits40Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) +{ + dword *pdwSrc = (dword *)pbSrc; + dword *pdwDst = (dword *)pbDst; + while (cy-- != 0) { + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + pdwSrc = (dword *)((byte *)pdwSrc + cbSrcReturn); + pdwDst = (dword *)((byte *)pdwDst + cbDstReturn); + } +} + +void CopyBits36Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) +{ + dword *pdwSrc = (dword *)pbSrc; + dword *pdwDst = (dword *)pbDst; + while (cy-- != 0) { + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + pdwSrc = (dword *)((byte *)pdwSrc + cbSrcReturn); + pdwDst = (dword *)((byte *)pdwDst + cbDstReturn); + } +} + +void CopyBits32Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) +{ + dword *pdwSrc = (dword *)pbSrc; + dword *pdwDst = (dword *)pbDst; + while (cy-- != 0) { + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + pdwSrc = (dword *)((byte *)pdwSrc + cbSrcReturn); + pdwDst = (dword *)((byte *)pdwDst + cbDstReturn); + } +} + +void CopyBits28Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) +{ + dword *pdwSrc = (dword *)pbSrc; + dword *pdwDst = (dword *)pbDst; + while (cy-- != 0) { + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + pdwSrc = (dword *)((byte *)pdwSrc + cbSrcReturn); + pdwDst = (dword *)((byte *)pdwDst + cbDstReturn); + } +} + +void CopyBits24Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) +{ + dword *pdwSrc = (dword *)pbSrc; + dword *pdwDst = (dword *)pbDst; + while (cy-- != 0) { + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + pdwSrc = (dword *)((byte *)pdwSrc + cbSrcReturn); + pdwDst = (dword *)((byte *)pdwDst + cbDstReturn); + } +} + +void CopyBits20Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) +{ + dword *pdwSrc = (dword *)pbSrc; + dword *pdwDst = (dword *)pbDst; + while (cy-- != 0) { + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + pdwSrc = (dword *)((byte *)pdwSrc + cbSrcReturn); + pdwDst = (dword *)((byte *)pdwDst + cbDstReturn); + } +} + +void CopyBits16Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) +{ + dword *pdwSrc = (dword *)pbSrc; + dword *pdwDst = (dword *)pbDst; + while (cy-- != 0) { + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + pdwSrc = (dword *)((byte *)pdwSrc + cbSrcReturn); + pdwDst = (dword *)((byte *)pdwDst + cbDstReturn); + } +} + +void CopyBits12Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) +{ + dword *pdwSrc = (dword *)pbSrc; + dword *pdwDst = (dword *)pbDst; + while (cy-- != 0) { + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + pdwSrc = (dword *)((byte *)pdwSrc + cbSrcReturn); + pdwDst = (dword *)((byte *)pdwDst + cbDstReturn); + } +} + +void CopyBits8Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) +{ + dword *pdwSrc = (dword *)pbSrc; + dword *pdwDst = (dword *)pbDst; + while (cy-- != 0) { + *pdwDst++ = *pdwSrc++; + *pdwDst++ = *pdwSrc++; + pdwSrc = (dword *)((byte *)pdwSrc + cbSrcReturn); + pdwDst = (dword *)((byte *)pdwDst + cbDstReturn); + } +} + +void CopyBits4Wide(byte *pbSrc, int cbSrcReturn, byte *pbDst, int cbDstReturn, int cy) +{ + dword *pdwSrc = (dword *)pbSrc; + dword *pdwDst = (dword *)pbDst; + while (cy-- != 0) { + *pdwDst++ = *pdwSrc++; + pdwSrc = (dword *)((byte *)pdwSrc + cbSrcReturn); + pdwDst = (dword *)((byte *)pdwDst + cbDstReturn); + } +} +#endif + +} // namespace wi diff --git a/game/tbitmapsr.cpp b/game/tbitmapsr.cpp new file mode 100644 index 0000000..e9f42b3 --- /dev/null +++ b/game/tbitmapsr.cpp @@ -0,0 +1,771 @@ +#if 0 + +#if 0 +struct Frame { + word ibsrEven; + word ibsrOdd; +}; + +struct TBitmapSRHeader { + word cx; + word cy; + word cra; + word ibra; + word cFrames; + Frame aframe[1]; +}; + +class TBitmapSR : public TBitmap +{ +public: + TBitmapSR() secTBitmap; + virtual ~TBitmapSR() secTBitmap; + virtual bool Init(void *pv, FileMap *pfmap) secTBitmap; + virtual void BltTo(class DibBitmap *pbmDst, int xDst, int yDst, byte *aclrMap = NULL, Rect *prcSrc = NULL) secTBitmap; + virtual void GetSize(Size *psiz) secTBitmap; + +private: + FileMap m_fmap; + TBitmapSRHeader *m_pbmh; + dword *m_ppfn; +}; +#endif + +#include "ht.h" + +struct ScanRecord { + word officlr; + word iraTimes4; +}; + +struct RunArgs { + byte op; + byte cpSrc; + byte cpArgs; + byte cpDst; +}; + +#ifdef PIL +extern "C" { +extern dword gapfnRunOps[]; +extern dword *gmpopapfnLeftClip[]; +extern dword *gmpopapfnRightClip[]; +void TBltSR8NoClip(ScanRecord *psr, int cy, byte *pbDst, int cbDst, byte *aiclrSide, byte *aiclrShadow, dword *ppfn) secCode4; +void TBltSR8LeftClip(ScanRecord *psr, int xLeft, int cy, byte *pbDst, int cbDst, byte *aiclrSide, byte *aiclrShadow, dword *apfnList, RunArgs *praList, dword **mpopapfnLeftClip) secCode4; +void TBltSR8RightClip(ScanRecord *psr, int xLeft, int cy, byte *pbDst, int cbDst, byte *aiclrSide, byte *aiclrShadow, dword *apfnList, RunArgs *praList, dword **mpopapfnRightClip) secCode4; +void FillEvenEven8(byte *pbRow, int cx, int cy, int cxDib, byte bFill); +}; +#endif + +TBitmapSR::TBitmapSR() +{ + m_pbmh = NULL; + m_ppfn = NULL; +} + +TBitmapSR::~TBitmapSR() +{ + gmmgr.DbMemFree(m_ppfn); + if (m_pbmh != NULL) { + UnmapFile(&m_fmap); + m_pbmh = NULL; + } +} + +bool TBitmapSR::Init(void *pv, FileMap *pfmap) +{ + // Init run op function pointers + + TBitmapSRHeader *pbmh = (TBitmapSRHeader *)pv; + +#ifdef PIL + int cra = BigWord(pbmh->cra); + +#ifdef INCL_MEMTRACE + Trace("tbitmapsr: %d cra's", cra); +#endif + dword *ppfn = new dword[cra]; + if (ppfn == NULL) + return false; + RunArgs *praT = (RunArgs *)((byte *)pbmh + BigWord(pbmh->ibra)); + dword *ppfnT = ppfn; + for (int ira = 0; ira < cra; ira++) { + *ppfnT++ = gapfnRunOps[praT->op]; + praT++; + } + m_ppfn = (dword *)gmmgr.DbMemAlloc(cra * sizeof(dword)); + if (m_ppfn == NULL) { + delete ppfn; + return false; + } + gmmgr.DbWriteMem(m_ppfn, 0, ppfn, cra * sizeof(dword)); + delete ppfn; +#endif + + // Done + + m_pbmh = pbmh; + m_fmap = *pfmap; + return true; +} + +#ifdef WIN +enum Op { + EvenData1, // 0 + EvenData1_Inc, // 1 + EvenData2, // 2 + EvenData2_Inc, // 3 + EvenData3, // 4 + EvenData3_Inc, // 5 + EvenData4, // 6 + EvenData4_Inc, // 7 + EvenData5, // 8 + EvenData5_Inc, // 9 + EvenData6, // 10 + EvenData6_Inc, // 11 + EvenData7, // 12 + EvenData7_Inc, // 13 + EvenData8, // 14 + EvenData8_Inc, // 15 + EvenData9, // 16 + EvenData9_Inc, // 17 + EvenData10, // 18 + EvenData10_Inc, // 19 + EvenData11, // 20 + EvenData11_Inc, // 21 + EvenData12, // 22 + EvenData12_Inc, // 23 + EvenData13, // 24 + EvenData13_Inc, // 25 + EvenData14, // 26 + EvenData14_Inc, // 27 + EvenData15, // 28 + EvenData15_Inc, // 29 + EvenData16, // 30 + EvenData16_Inc, // 31 + EvenData17, // 32 + EvenData17_Inc, // 33 + EvenData18, // 34 + EvenData18_Inc, // 35 + EvenData19, // 36 + EvenData19_Inc, // 37 + EvenData20, // 38 + EvenData20_Inc, // 39 + EvenData21, // 40 + EvenData21_Inc, // 41 + EvenData22, // 42 + EvenData22_Inc, // 43 + EvenData23, // 44 + EvenData23_Inc, // 45 + EvenData24, // 46 + EvenData24_Inc, // 47 + EvenData25, // 48 + EvenData25_Inc, // 49 + EvenData26, // 50 + EvenData26_Inc, // 51 + EvenData27, // 52 + EvenData27_Inc, // 53 + EvenData28, // 54 + EvenData28_Inc, // 55 + EvenData29, // 56 + EvenData29_Inc, // 57 + EvenData30, // 58 + EvenData30_Inc, // 59 + EvenData31, // 60 + EvenData31_Inc, // 61 + EvenData32, // 62 + EvenData32_Inc, // 63 + OddData1, // 64 + OddData1_Inc, // 65 + OddData2, // 66 + OddData2_Inc, // 67 + OddData3, // 68 + OddData3_Inc, // 69 + OddData4, // 70 + OddData4_Inc, // 71 + OddData5, // 72 + OddData5_Inc, // 73 + OddData6, // 74 + OddData6_Inc, // 75 + OddData7, // 76 + OddData7_Inc, // 77 + OddData8, // 78 + OddData8_Inc, // 79 + OddData9, // 80 + OddData9_Inc, // 81 + OddData10, // 82 + OddData10_Inc, // 83 + OddData11, // 84 + OddData11_Inc, // 85 + OddData12, // 86 + OddData12_Inc, // 87 + OddData13, // 88 + OddData13_Inc, // 89 + OddData14, // 90 + OddData14_Inc, // 91 + OddData15, // 92 + OddData15_Inc, // 93 + OddData16, // 94 + OddData16_Inc, // 95 + OddData17, // 96 + OddData17_Inc, // 97 + OddData18, // 98 + OddData18_Inc, // 99 + OddData19, // 100 + OddData19_Inc, // 101 + OddData20, // 102 + OddData20_Inc, // 103 + OddData21, // 104 + OddData21_Inc, // 105 + OddData22, // 106 + OddData22_Inc, // 107 + OddData23, // 108 + OddData23_Inc, // 109 + OddData24, // 110 + OddData24_Inc, // 111 + OddData25, // 112 + OddData25_Inc, // 113 + OddData26, // 114 + OddData26_Inc, // 115 + OddData27, // 116 + OddData27_Inc, // 117 + OddData28, // 118 + OddData28_Inc, // 119 + OddData29, // 120 + OddData29_Inc, // 121 + OddData30, // 122 + OddData30_Inc, // 123 + OddData31, // 124 + OddData31_Inc, // 125 + OddData32, // 126 + OddData32_Inc, // 127 + Side1, // 128 + Side2, // 129 + Side3, // 130 + Side4, // 131 + Side5, // 132 + Side6, // 133 + Side7, // 134 + Side8, // 135 + Side9, // 136 + Side10, // 137 + Side11, // 138 + Side12, // 139 + Side13, // 140 + Side14, // 141 + Side15, // 142 + Side16, // 143 + Side17, // 144 + Side18, // 145 + Side19, // 146 + Side20, // 147 + Side21, // 148 + Side22, // 149 + Side23, // 150 + Side24, // 151 + Side25, // 152 + Side26, // 153 + Side27, // 154 + Side28, // 155 + Side29, // 156 + Side30, // 157 + Side31, // 158 + Side32, // 159 + Shadow1, // 160 + Shadow2, // 161 + Shadow3, // 162 + Shadow4, // 163 + Shadow5, // 164 + Shadow6, // 165 + Shadow7, // 166 + Shadow8, // 167 + Shadow9, // 168 + Shadow10, // 169 + Shadow11, // 170 + Shadow12, // 171 + Shadow13, // 172 + Shadow14, // 173 + Shadow15, // 174 + Shadow16, // 175 + Shadow17, // 176 + Shadow18, // 177 + Shadow19, // 178 + Shadow20, // 179 + Shadow21, // 180 + Shadow22, // 181 + Shadow23, // 182 + Shadow24, // 183 + Shadow25, // 184 + Shadow26, // 185 + Shadow27, // 186 + Shadow28, // 187 + Shadow29, // 188 + Shadow30, // 189 + Shadow31, // 190 + Shadow32, // 191 + DataN, // 192 + DataN_Inc, // 193 + SideN, // 194 + ShadowN, // 195 + TransparentN, // 196 + EndScan, // 197 + Error, // 198 + EvenDataStart = 0, + EvenDataEnd = 63, + OddDataStart = 64, + OddDataEnd = 127, + SideStart = 128, + SideEnd = 159, + ShadowStart = 160, + ShadowEnd = 191 +}; + +void TBltSR8NoClip(ScanRecord *psr, int cy, byte *pbDst, int cbDst, byte *aiclrSide, byte *aiclrShadow, TBitmapSRHeader *pbmh, DibBitmap *pbmDst) +{ + byte *pbDstSav = pbDst; + for (; cy != 0; cy--, psr++) { + byte *pbSrc = (byte *)psr + BigWord(psr->officlr); + RunArgs *pra = (RunArgs *)((byte *)pbmh + BigWord(pbmh->ibra) + BigWord(psr->iraTimes4)); + + for (; true; pra++) { + //temp + //gdisp.CopyToScreen(pbmDst, 0, 0); + + Op op = (Op)pra->op; + if (op == EndScan) { + pbDstSav += cbDst; + pbDst = pbDstSav; + break; + } + if (op <= OddDataEnd) { + if (op & 1) + pbSrc++; + int cp = pra->cpDst; + while (cp-- != 0) + *pbDst++ = *pbSrc++; + continue; + } + if (op <= SideEnd) { + int cp = pra->cpDst; + while (cp-- != 0) + *pbDst++ = aiclrSide[*pbSrc++]; + continue; + } + if (op <= ShadowEnd) { + int cp = pra->cpDst; + while (cp-- != 0) { + *pbDst = aiclrShadow[*pbDst]; + pbDst++; + } + continue; + } + if (op <= DataN_Inc) { + if (op & 1) + pbSrc++; + int cp = (*pbSrc++) + 1; + while (cp-- != 0) + *pbDst++ = *pbSrc++; + continue; + } + if (op == SideN) { + int cp = (*pbSrc++) + 1; + while (cp-- != 0) + *pbDst++ = aiclrSide[*pbSrc++]; + continue; + } + if (op == ShadowN) { + int cp = (*pbSrc++) + 1; + while (cp-- != 0) { + *pbDst = aiclrShadow[*pbDst]; + pbDst++; + } + continue; + } + if (op == TransparentN) { + pbDst += *pbSrc++; + continue; + } + Assert(false); + } + } +} + +void TBltSR8LeftClip(ScanRecord *psr, int xLeft, int cy, byte *pbDst, int cbDst, byte *aiclrSide, byte *aiclrShadow, TBitmapSRHeader *pbmh, DibBitmap *pbmDst) +{ + byte *pbDstSav = pbDst; + for (; cy != 0; cy--, psr++) { + byte *pbSrc = (byte *)psr + BigWord(psr->officlr); + RunArgs *pra = (RunArgs *)((byte *)pbmh + BigWord(pbmh->ibra) + BigWord(psr->iraTimes4)); + int x = 0; + bool fEndScan = false; + while (x + pra->cpDst <= xLeft) { + if (pra->op == EndScan) { + pbDstSav += cbDst; + pbDst = pbDstSav; + fEndScan = true; + break; + } + x += pra->cpDst; + pbSrc += pra->cpSrc; + pra++; + } + if (fEndScan) + continue; + + // Is this run partially clipped? + if (x != xLeft) { + Op op = (Op)pra->op; + int cp = x + pra->cpDst - xLeft; + if (op <= OddDataEnd) { + // cpSrc = N + // cpDst = N + // cpSrc = N + 1 + // cpDst = N + pbSrc += xLeft - x; + if (op & 1) + pbSrc++; + while (cp-- != 0) + *pbDst++ = *pbSrc++; + } else if (op <= SideEnd) { + // cpSrc = N + // cpDst = N + pbSrc += xLeft - x; + while (cp-- != 0) + *pbDst++ = aiclrSide[*pbSrc++]; + } else if (op <= ShadowEnd) { + // cpSrc = 0 + // cpDst = N + while (cp-- != 0) { + *pbDst = aiclrShadow[*pbDst]; + pbDst++; + } + } else if (op <= DataN_Inc) { + // cpSrc = 1 + N + // cpDst = N + // cpSrc = 1 + N + 1 + // cpDst = N + pbSrc += xLeft - x + 1; + if (op & 1) + pbSrc++; + while (cp-- != 0) + *pbDst++ = *pbSrc++; + } else if (op == SideN) { + // cpSrc = 1 + N + // cpDst = N + pbSrc += xLeft - x + 1; + while (cp-- != 0) + *pbDst++ = aiclrSide[*pbSrc++]; + } else if (op == ShadowN) { + // cpSrc = 1 + // cpDst = N + pbSrc += 1; + while (cp-- != 0) { + *pbDst = aiclrShadow[*pbDst]; + pbDst++; + } + } else if (op == TransparentN) { + // cpSrc == 1 + // cpDst == N + pbSrc += 1; + pbDst += cp; + } + } + + // The rest of the full runs + pra++; + for (; true; pra++) { + //temp + //gdisp.CopyToScreen(pbmDst, 0, 0); + + Op op = (Op)pra->op; + if (op == EndScan) { + pbDstSav += cbDst; + pbDst = pbDstSav; + break; + } + if (op <= OddDataEnd) { + if (op & 1) + pbSrc++; + int cp = pra->cpDst; + while (cp-- != 0) + *pbDst++ = *pbSrc++; + continue; + } + if (op <= SideEnd) { + int cp = pra->cpDst; + while (cp-- != 0) + *pbDst++ = aiclrSide[*pbSrc++]; + continue; + } + if (op <= ShadowEnd) { + int cp = pra->cpDst; + while (cp-- != 0) { + *pbDst = aiclrShadow[*pbDst]; + pbDst++; + } + continue; + } + if (op <= DataN_Inc) { + pbSrc++; + if (op & 1) + pbSrc++; + int cp = pra->cpDst; + while (cp-- != 0) + *pbDst++ = *pbSrc++; + continue; + } + if (op == SideN) { + pbSrc++; + int cp = pra->cpDst; + while (cp-- != 0) + *pbDst++ = aiclrSide[*pbSrc++]; + continue; + } + if (op == ShadowN) { + pbSrc++; + int cp = pra->cpDst; + while (cp-- != 0) { + *pbDst = aiclrShadow[*pbDst]; + pbDst++; + } + continue; + } + if (op == TransparentN) { + pbDst += *pbSrc++; + continue; + } + Assert(false); + } + } +} + +void TBltSR8RightClip(ScanRecord *psr, int cx, int cy, byte *pbDst, int cbDst, byte *aiclrSide, byte *aiclrShadow, TBitmapSRHeader *pbmh, DibBitmap *pbmDst) +{ + byte *pbDstSav = pbDst; + for (; cy != 0; cy--, psr++) { + byte *pbSrc = (byte *)psr + BigWord(psr->officlr); + RunArgs *pra = (RunArgs *)((byte *)pbmh + BigWord(pbmh->ibra) + BigWord(psr->iraTimes4)); + + // Find the intersecting run + RunArgs *praT = pra; + int x = 0; + while (praT->op != EndScan) { + if (x + praT->cpDst > cx) + break; + x += praT->cpDst; + praT++; + } + + // Draw full runs + for (; pra < praT; pra++) { + //temp + //gdisp.CopyToScreen(pbmDst, 0, 0); + + Op op = (Op)pra->op; + Assert(op != EndScan); + if (op <= OddDataEnd) { + if (op & 1) + pbSrc++; + int cp = pra->cpDst; + while (cp-- != 0) + *pbDst++ = *pbSrc++; + continue; + } + if (op <= SideEnd) { + int cp = pra->cpDst; + while (cp-- != 0) + *pbDst++ = aiclrSide[*pbSrc++]; + continue; + } + if (op <= ShadowEnd) { + int cp = pra->cpDst; + while (cp-- != 0) { + *pbDst = aiclrShadow[*pbDst]; + pbDst++; + } + continue; + } + if (op <= DataN_Inc) { + pbSrc++; + if (op & 1) + pbSrc++; + int cp = pra->cpDst; + while (cp-- != 0) + *pbDst++ = *pbSrc++; + continue; + } + if (op == SideN) { + pbSrc++; + int cp = pra->cpDst; + while (cp-- != 0) + *pbDst++ = aiclrSide[*pbSrc++]; + continue; + } + if (op == ShadowN) { + pbSrc++; + int cp = pra->cpDst; + while (cp-- != 0) { + *pbDst = aiclrShadow[*pbDst]; + pbDst++; + } + continue; + } + if (op == TransparentN) { + pbDst += *pbSrc++; + continue; + } + Assert(false); + } + + //temp + //gdisp.CopyToScreen(pbmDst, 0, 0); + + // Draw clipped run + if (x < cx) { + Op op = (Op)pra->op; + if (op != EndScan && op != TransparentN) { + int cp = cx - x; + if (op <= OddDataEnd) { + if (op & 1) + pbSrc++; + while (cp-- != 0) + *pbDst++ = *pbSrc++; + } else if (op <= SideEnd) { + while (cp-- != 0) + *pbDst++ = aiclrSide[*pbSrc++]; + } else if (op <= ShadowEnd) { + while (cp-- != 0) { + *pbDst = aiclrShadow[*pbDst]; + pbDst++; + } + } else if (op <= DataN_Inc) { + pbSrc++; + if (op & 1) + pbSrc++; + while (cp-- != 0) + *pbDst++ = *pbSrc++; + } else if (op == SideN) { + pbSrc++; + while (cp-- != 0) + *pbDst++ = aiclrSide[*pbSrc++]; + } else if (op == ShadowN) { + pbSrc++; + while (cp-- != 0) { + *pbDst = aiclrShadow[*pbDst]; + pbDst++; + } + } + } + } + pbDstSav += cbDst; + pbDst = pbDstSav; + + //temp + //gdisp.CopyToScreen(pbmDst, 0, 0); + } +} +#endif + +void TBitmapSR::BltTo(DibBitmap *pbmDst, int xDst, int yDst, byte *aclrMap, Rect *prcSrc) +{ + // Get dib dimensions + + Size sizT; + pbmDst->GetSize(&sizT); + + // Source rect to blt from + + Rect rcSrc; + if (prcSrc != NULL) { + rcSrc = *prcSrc; + } else { + rcSrc.Set(0, 0, BigWord(m_pbmh->cx), BigWord(m_pbmh->cy)); + } + + // Right and bottom edge clipping + + if (sizT.cx - xDst < rcSrc.Width()) + rcSrc.right = rcSrc.left + sizT.cx - xDst; + if (sizT.cy - yDst < rcSrc.Height()) + rcSrc.bottom = rcSrc.top + sizT.cy - yDst; + + // Before we much with xDst, get the alignment right + + Frame *pframe = &m_pbmh->aframe[0]; + word ibsr = (xDst & 1) ? BigWord(pframe->ibsrOdd) : BigWord(pframe->ibsrEven); + + // Left and top edge clipping + + if (xDst < 0) { + rcSrc.left -= xDst; + xDst = 0; + } + if (yDst < 0) { + rcSrc.top -= yDst; + yDst = 0; + } + + // Anything to blt? + + if (rcSrc.IsEmpty()) + return; + +#ifdef STATS_DISPLAY + extern int gcBitmapsDrawn; + gcBitmapsDrawn++; +#endif + + ScanRecord *psr = (ScanRecord *)((byte *)m_pbmh + ibsr + rcSrc.top * sizeof(ScanRecord)); + int cbRowDst = (sizT.cx + 1) & ~1; + byte *pbBits = pbmDst->GetBits(); + +//temp +// BitmapType *pbmpScreen = WinGetBitmap(WinGetDisplayWindow()); +// pbBits = (byte *)BmpGetBits(pbmpScreen); +// FillEvenEven8(pbBits, 160, 160, 160, 0); +//end temp + + byte *pbDst = pbBits + yDst * cbRowDst + xDst; + + if (aclrMap == NULL) + aclrMap = gaclrIdentity; + + if (rcSrc.left == 0) { + if (rcSrc.Width() != BigWord(m_pbmh->cx)) { + // Right clip + +#ifdef PIL +// TBltSR8RightClip(psr, rcSrc.Width(), rcSrc.Height(), pbDst, cbRowDst, aclrMap, gmpiclriclrShadow, m_ppfn, (RunArgs *)((byte *)m_pbmh + BigWord(m_pbmh->ibra)), gmpopapfnRightClip); +#endif +#ifdef WIN + TBltSR8RightClip(psr, rcSrc.Width(), rcSrc.Height(), pbDst, cbRowDst, aclrMap, gmpiclriclrShadow, m_pbmh, pbmDst); +#endif + } else { + // No clip +#ifdef PIL + TBltSR8NoClip(psr, rcSrc.Height(), pbDst, cbRowDst, aclrMap, gmpiclriclrShadow, m_ppfn); +#endif +#ifdef WIN + TBltSR8NoClip(psr, rcSrc.Height(), pbDst, cbRowDst, aclrMap, gmpiclriclrShadow, m_pbmh, pbmDst); +#endif + } + } else { + // left clip + +#ifdef PIL +// TBltSR8LeftClip(psr, rcSrc.left, rcSrc.Height(), pbDst, cbRowDst, aclrMap, gmpiclriclrShadow, m_ppfn, (RunArgs *)((byte *)m_pbmh + BigWord(m_pbmh->ibra)), gmpopapfnLeftClip); +#endif +#ifdef WIN + TBltSR8LeftClip(psr, rcSrc.left, rcSrc.Height(), pbDst, cbRowDst, aclrMap, gmpiclriclrShadow, m_pbmh, pbmDst); +#endif + } + +} + +void TBitmapSR::GetSize(Size *psiz) +{ + psiz->cx = BigWord(m_pbmh->cx); + psiz->cy = BigWord(m_pbmh->cy); +} + +#endif \ No newline at end of file diff --git a/game/terrainmap.cpp b/game/terrainmap.cpp new file mode 100644 index 0000000..84c9401 --- /dev/null +++ b/game/terrainmap.cpp @@ -0,0 +1,1212 @@ +#include "ht.h" + +namespace wi { + +/* +for (j = 0; j < 64; j += 8) { + var str = ""; + for (i = 0; i < 8; i++) + str += (j + i) * (j + i) + ", "; + WScript.Echo(str); +} +*/ + +int ganSquared[64] = { + 0, 1, 4, 9, 16, 25, 36, 49, + 64, 81, 100, 121, 144, 169, 196, 225, + 256, 289, 324, 361, 400, 441, 484, 529, + 576, 625, 676, 729, 784, 841, 900, 961, + 1024, 1089, 1156, 1225, 1296, 1369, 1444, 1521, + 1600, 1681, 1764, 1849, 1936, 2025, 2116, 2209, + 2304, 2401, 2500, 2601, 2704, 2809, 2916, 3025, + 3136, 3249, 3364, 3481, 3600, 3721, 3844, 3969, +}; + +byte gmpdirdirOpposite[8] = { 4, 5, 6, 7, 0, 1, 2, 3 }; + +// Road +// Open +// Wall +// Blocked + +#define knScoreBlocked 10000 +#define knScoreStructure 9000 + +word TerrainMap::s_anScoreTerrain[] = { 0, 0, knScoreBlocked, knScoreBlocked }; + +// Cost of moving by one tile in a certain direction + +word TerrainMap::s_anScoreVector[] = { 5, 7, 5, 7, 5, 7, 5, 7 }; + +// Max nodes futility cutoff. Traverse the map three times, and * 3 because of how +// nodes are added to the list + +#define kcNodesMax (ktcMax * 3 * 3) + +TerrainMap::TerrainMap() +{ + m_ptrmaph = NULL; + m_abBuffer = NULL; + m_ppathhList = NULL; + m_ppathhFreeList = NULL; + m_cNodes = 0; +} + +TerrainMap::~TerrainMap() +{ + if (m_ptrmaph != NULL) + gpakr.UnmapFile(&m_fmap); + delete m_abBuffer; + while (m_ppathhList != NULL) + RemovePathHead(m_ppathhList); + for (PathHead *ppathh = m_ppathhFreeList; ppathh != NULL; ) { + PathHead *ppathhT = ppathh; + ppathh = ppathh->ppathhNext; + delete ppathhT; + } +} + +bool TerrainMap::Init(char *pszFn) +{ + // Load the terrain map + + m_ptrmaph = (TerrainMapHeader *)gpakr.MapFile(pszFn, &m_fmap); + if (m_ptrmaph == NULL) + return false; + + // Alloc the path buffer + + m_ctx = BigWord(m_ptrmaph->ctx); + m_cty = BigWord(m_ptrmaph->cty); + m_cbBuffer = m_ctx * m_cty; + m_abBuffer = new byte[m_cbBuffer]; + Assert(m_abBuffer != NULL, "out of memory!"); + if (m_abBuffer == NULL) + return false; + memset(m_abBuffer, 0, m_cbBuffer); + + // Init + + for (int n = 0; n < 8; n++) + m_mpDirDelta[n] = g_mpDirToDy[n] * m_ctx + g_mpDirToDx[n]; + + return true; +} + +#ifdef MP_DEBUG +dword TerrainMap::GetChecksum() +{ + dword n = 0; + for (int i = 0; i < m_cbBuffer; i++) + n += m_abBuffer[i]; + return n; +} +#endif + +#define knVerTerrainMapState 1 +bool TerrainMap::LoadState(Stream *pstm) +{ + byte nVer = pstm->ReadByte(); + if (nVer != knVerTerrainMapState) + return false; + pstm->ReadBytesRLE(m_abBuffer, m_cbBuffer); + return pstm->IsSuccess(); +} + +bool TerrainMap::SaveState(Stream *pstm) +{ + pstm->WriteByte(knVerTerrainMapState); + for (int n = 0; n < m_cbBuffer; n++) + m_abBuffer[n] &= kbfMobileUnit | kbfStructure | kbfReserved; + pstm->WriteBytesRLE(m_abBuffer, m_cbBuffer); + return pstm->IsSuccess(); +} + +void TerrainMap::SetFlags(int tx, int ty, int ctx, int cty, byte bf) +{ + Assert(tx >= 0 && ty >= 0); + Assert(tx + ctx <= m_ctx && ty + cty <= m_cty); + + for (int txT = tx; txT < tx + ctx; txT++) { + for (int tyT = ty; tyT < ty + cty; tyT++) { + m_abBuffer[tyT * m_ctx + txT] |= bf; + } + } + +#ifdef MP_DEBUG +// MpTrace("Set Terrain Flags:%d,%d,%d,%d set 0x%02x", tx, ty, ctx, cty, bf); +#endif +} + +bool TerrainMap::TestFlags(int tx, int ty, int ctx, int cty, byte bf) +{ + // We consider all tiles off the edge of the map to be fully occupied + + if (tx < 0 || ty < 0 || tx + ctx > m_ctx || ty + cty > m_cty) + return true; + + for (int txT = tx; txT < tx + ctx; txT++) { + for (int tyT = ty; tyT < ty + cty; tyT++) { + int off = tyT * m_ctx + txT; + if (m_abBuffer[off] & bf) + return true; + } + } + return false; +} + +bool TerrainMap::IsOccupied(int tx, int ty, int ctx, int cty, byte bf) +{ + // We consider all tiles off the edge of the map to be fully occupied + + if (tx < 0 || ty < 0 || tx + ctx > m_ctx || ty + cty > m_cty) + return true; + + for (int txT = tx; txT < tx + ctx; txT++) { + for (int tyT = ty; tyT < ty + cty; tyT++) { + int off = tyT * m_ctx + txT; + if (m_abBuffer[off] & bf) + return true; + if (((byte *)(m_ptrmaph + 1))[off] > kttOpen) + return true; + } + } + return false; +} + +void TerrainMap::ClearFlags(int tx, int ty, int ctx, int cty, byte bf) +{ + Assert(tx >= 0 && ty >= 0); + Assert(tx + ctx <= m_ctx && ty + cty <= m_cty); + + for (int txT = tx; txT < tx + ctx; txT++) { + for (int tyT = ty; tyT < ty + cty; tyT++) { + m_abBuffer[tyT * m_ctx + txT] &= ~bf; + } + } + +#ifdef MP_DEBUG +// MpTrace("Clear Terrain Flags:%d,%d,%d,%d clear 0x%02x", tx, ty, ctx, cty, bf); +#endif +} + +bool TerrainMap::IsLineOccupied(TCoord txFrom, TCoord tyFrom, TCoord txTo, TCoord tyTo, byte bfTerrainAvoid) +{ + // There already? + + if (txFrom == txTo && tyFrom == tyTo) + return false; + + // Number of x's and y's to travel + + int dx = abs(txTo - txFrom); + int dy = abs(tyTo - tyFrom); + + // Direction on each axis + + int sx, sy; + if (txFrom < txTo) { + sx = 1; + } else { + sx = -1; + } + if (tyFrom < tyTo) { + sy = 1; + } else { + sy = -1; + } + + TCoord txT = txFrom; + TCoord tyT = tyFrom; + int nStep = 0; + int dc = 0; + while (true) { + if (dx > dy) { + txT += sx; + dc += dy; + if (dc >= dx) { + dc -= dx; + tyT += sy; + } + } else { + tyT += sy; + dc += dx; + if (dc >= dy) { + dc -= dy; + txT += sx; + } + } + + // If this is blocking, bail + + if (IsBlocked(txT, tyT, bfTerrainAvoid)) + return true; + + // If we're there, break + + if (txT == txTo && tyT == tyTo) + break; + } + + return false; +} + +bool TerrainMap::FindFirstUnoccupied(TCoord txFrom, TCoord tyFrom, TCoord txTo, TCoord tyTo, TCoord *ptxFree, TCoord *ptyFree) +{ + // Number of x's and y's to travel + + int dx = abs(txTo - txFrom); + int dy = abs(tyTo - tyFrom); + + // Direction on each axis + + int sx, sy; + if (txFrom < txTo) { + sx = 1; + } else { + sx = -1; + } + if (tyFrom < tyTo) { + sy = 1; + } else { + sy = -1; + } + + // If this is blocking, bail + + TCoord txT = txFrom; + TCoord tyT = tyFrom; + if (!IsBlocked(txT, tyT, 0)) { + *ptxFree = txT; + *ptyFree = tyT; + return true; + } + if (txFrom == txTo && tyFrom == tyTo) + return false; + + int nStep = 0; + int dc = 0; + while (true) { + if (dx > dy) { + txT += sx; + dc += dy; + if (dc >= dx) { + dc -= dx; + tyT += sy; + } + } else { + tyT += sy; + dc += dx; + if (dc >= dy) { + dc -= dy; + txT += sx; + } + } + + // If this is blocking, bail + + if (!IsBlocked(txT, tyT, 0)) { + *ptxFree = txT; + *ptyFree = tyT; + return true; + } + + // If we're there, break + + if (txT == txTo && tyT == tyTo) + break; + } + + return false; +} + +bool TerrainMap::FindLastUnoccupied(TCoord txFrom, TCoord tyFrom, TCoord txTo, TCoord tyTo, TCoord *ptxFree, TCoord *ptyFree) +{ + // Number of x's and y's to travel + + int dx = abs(txTo - txFrom); + int dy = abs(tyTo - tyFrom); + + // Direction on each axis + + int sx, sy; + if (txFrom < txTo) { + sx = 1; + } else { + sx = -1; + } + if (tyFrom < tyTo) { + sy = 1; + } else { + sy = -1; + } + + // If this is not blocking, remember + + TCoord txT = txFrom; + TCoord tyT = tyFrom; + if (IsBlocked(txT, tyT, 0)) + return false; + *ptxFree = txT; + *ptyFree = tyT; + if (txFrom == txTo && tyFrom == tyTo) + return true; + + int nStep = 0; + int dc = 0; + while (true) { + if (dx > dy) { + txT += sx; + dc += dy; + if (dc >= dx) { + dc -= dx; + tyT += sy; + } + } else { + tyT += sy; + dc += dx; + if (dc >= dy) { + dc -= dy; + txT += sx; + } + } + + // If this is not blocking, remember it + + if (IsBlocked(txT, tyT, 0)) + return true; + *ptxFree = txT; + *ptyFree = tyT; + + // If we're there, break + + if (txT == txTo && tyT == tyTo) + break; + } + + return true; +} + +Path *TerrainMap::FindLinePath(TCoord txFrom, TCoord tyFrom, TCoord txTo, TCoord tyTo, byte bfTerrainAvoid) +{ + if (txFrom == txTo && tyFrom == tyTo) + return NULL; + + TPoint atpt[ktcMax + ktcMax / 2]; + + // Number of x's and y's to travel + + int dx = abs(txTo - txFrom); + int dy = abs(tyTo - tyFrom); + + // Direction on each axis + + int sx, sy; + if (txFrom < txTo) { + sx = 1; + } else { + sx = -1; + } + if (tyFrom < tyTo) { + sy = 1; + } else { + sy = -1; + } + + TCoord txT = txFrom; + TCoord tyT = tyFrom; + int nStep = 0; + int dc = 0; + while (true) { + if (dx > dy) { + txT += sx; + dc += dy; + if (dc >= dx) { + dc -= dx; + tyT += sy; + } + } else { + tyT += sy; + dc += dx; + if (dc >= dy) { + dc -= dy; + txT += sx; + } + } + + // If this is blocking, bail + + if (IsBlocked(txT, tyT, bfTerrainAvoid)) + return NULL; + + // Location looks good, save it + + atpt[nStep].tx = (byte)txT; + atpt[nStep].ty = (byte)tyT; + nStep++; + Assert(nStep < ARRAYSIZE(atpt)); + + // If we're there, break + + if (txT == txTo && tyT == tyTo) + break; + } + + // Create a path from this + + return CreatePath(this, txFrom, tyFrom, atpt, nStep); +} + +Path *TerrainMap::FindPath(int txFrom, int tyFrom, int txTo, int tyTo, byte bfTerrainAvoid) +{ + // Arbitrary pathing + + if (txFrom == txTo && tyFrom == tyTo) + return NULL; + + // Quick out. Miners do this when they want to get to the dump point but it's set because + // the processor is occupied. + + if (abs(txFrom - txTo) <= 1 && abs(tyFrom - tyTo) <= 1) { + TPoint tptT; + tptT.tx = txTo; + tptT.ty = tyTo; + return CreatePath(this, txFrom, tyFrom, &tptT, 1); + } + + // Initialize. + +#if 0 + for (int n = 0; n < m_cbBuffer; n++) + m_abBuffer[n] &= kbfMobileUnit | kbfStructure | kbfReserved; +#else +#define kbClear (kbfMobileUnit | kbfStructure | kbfReserved) +#define kdwClear MakeDword(kbClear, kbClear, kbClear, kbClear) + + // Try to speed up initial buffer clearning + // If not dword aligned, adjust + + byte *pbT = m_abBuffer; + if (*((byte *)&pbT) & 2) { + *pbT++ &= kbClear; + *pbT++ &= kbClear; + } + + // 32 byte aligned count of dwords + + dword *pdwT = (dword *)pbT; + int cdw = (((dword *)&m_abBuffer[m_cbBuffer]) - pdwT) & ~7; + dword *pdwMax = &((dword *)m_abBuffer)[cdw]; + dword dwClear = kdwClear; + while (pdwT < pdwMax) { + *pdwT++ &= dwClear; + *pdwT++ &= dwClear; + *pdwT++ &= dwClear; + *pdwT++ &= dwClear; + *pdwT++ &= dwClear; + *pdwT++ &= dwClear; + *pdwT++ &= dwClear; + *pdwT++ &= dwClear; + } + + // Clear dwords (up to 7 dwords) + + pdwT = pdwMax; + int cdwT = ((dword *)&m_abBuffer[m_cbBuffer]) - pdwT; + pdwMax = &pdwT[cdwT]; + while (pdwT < pdwMax) + *pdwT++ &= dwClear; + + // Clear remaining bytes (up to 3 bytes) + + switch (&m_abBuffer[m_cbBuffer] - (byte *)pdwMax) { + case 0: + break; + case 1: + ((byte *)pdwMax)[0] &= kbClear; + break; + + case 2: + ((byte *)pdwMax)[0] &= kbClear; + ((byte *)pdwMax)[1] &= kbClear; + break; + + case 3: + ((byte *)pdwMax)[0] &= kbClear; + ((byte *)pdwMax)[1] &= kbClear; + ((byte *)pdwMax)[2] &= kbClear; + break; + + default: + Assert(); + break; + } +#endif + + while (m_ppathhList != NULL) + RemovePathHead(m_ppathhList); + Assert(m_ppathhList == NULL); + + byte *pbTo = &m_abBuffer[tyTo * m_ctx + txTo]; + + PathHead pathh; + memset(&pathh, 0, sizeof(pathh)); + pathh.offHead = tyFrom * m_ctx + txFrom; + pathh.txLast = txFrom; + pathh.tyLast = tyFrom; + PathHead *ppathhLast = &pathh; + + m_abBuffer[pathh.offHead] |= kbfLinked; + + word offClosest; + word nDistClosest = (word)-1; + while (ppathhLast != NULL) { + for (int n = 0; n < 8; n++) { + // Get next pos + + int txNew = ppathhLast->txLast + g_mpDirToDx[n]; + int tyNew = ppathhLast->tyLast + g_mpDirToDy[n]; + + // Check out of bounds. If we use 64*64 maps, we can use an AND mask + // of 0x2040 to determine out of bounds conditions. + + if (txNew < 0 || txNew >= m_ctx || tyNew < 0 || tyNew >= m_cty) + continue; + + // If this tile is linked already, abandon this path since + // it is longer + + byte *pbNew = &m_abBuffer[ppathhLast->offHead] + m_mpDirDelta[n]; + if (*pbNew & kbfLinked) + continue; + + // The tile it came from is no longer a head + + if (m_abBuffer[ppathhLast->offHead] & kbfHead) { + m_abBuffer[ppathhLast->offHead] &= ~kbfHead; + if (ppathhLast != &pathh) { + pathh = *ppathhLast; + RemovePathHead(ppathhLast); + ppathhLast = &pathh; + } + } + + // Calc score of terrain + + word offHead = pbNew - m_abBuffer; + word wScoreTerrain; + if (*pbNew & bfTerrainAvoid) { + wScoreTerrain = knScoreStructure; + } else { + wScoreTerrain = s_anScoreTerrain[((byte *)(m_ptrmaph + 1))[offHead]]; + } +#ifdef STATS_DISPLAY + gcPathScoresCalced++; +#endif + + // Link in this square, add it to the list. It is a path head. + + *pbNew = (*pbNew & (kbfMobileUnit | kbfStructure | kbfReserved)) | kbfHead | kbfLinked | n; + word wScoreKnown = ppathhLast->wScoreKnown + s_anScoreVector[n] + wScoreTerrain; + word nDist = ganSquared[abs(txNew - txTo)] + ganSquared[abs(tyNew - tyTo)]; + word wScore = wScoreKnown + nDist; + PathHead *ppathhNew = AddPathHead(wScoreKnown, wScore, ppathhLast->cSteps + 1, offHead, txNew, tyNew); + + // Remember "closest" point + + if (nDist < nDistClosest) { + nDistClosest = nDist; + offClosest = pbNew - m_abBuffer; + } + + // Either out of memory or max # nodes reached. Return best path so far + + if (ppathhNew == NULL) + return MakePath(txFrom, tyFrom, offClosest); + + // Are we at the destination? Then we're done! + + if (pbNew == pbTo) + return MakePath(txFrom, tyFrom, ppathhNew->offHead); + } + + // Perhaps this was a dead end? + + if (ppathhLast == m_ppathhList) + RemovePathHead(ppathhLast); + + // Grab the best path and do again + + ppathhLast = m_ppathhList; + } + + return NULL; +} + +PathHead *TerrainMap::AddPathHead(word wScoreKnown, word wScore, int cSteps, word offHead, int txNew, int tyNew) +{ + // Alloc + + PathHead *ppathh; + if (m_ppathhFreeList != NULL) { + ppathh = m_ppathhFreeList; + m_ppathhFreeList = ppathh->ppathhNext; + } else { + if (m_cNodes >= kcNodesMax) + return NULL; + ppathh = new PathHead; + if (ppathh == NULL) + return NULL; + m_cNodes++; + } + + // Initialize + + ppathh->txLast = txNew; + ppathh->tyLast = tyNew; + ppathh->cSteps = cSteps; + ppathh->offHead = offHead; + ppathh->wScoreKnown = wScoreKnown; + ppathh->wScore = wScore; + + // Sorted insertion + // OPT: The fast approach here will be to use a binary tree of some variety + // (AVL / Right Handed / Splay etc, TBD). For the time being, using + // a simple linear list w/scanning for simplicity to get searching + // working. + + for (PathHead **pppathhT = &m_ppathhList; true; pppathhT = &(*pppathhT)->ppathhNext) { + if (*pppathhT == NULL) { + *pppathhT = ppathh; + ppathh->ppathhNext = NULL; + break; + } + if (ppathh->wScore <= (*pppathhT)->wScore) { + ppathh->ppathhNext = *pppathhT; + *pppathhT = ppathh; + break; + } + } + + return ppathh; +} + +void TerrainMap::RemovePathHead(PathHead *ppathh) +{ + for (PathHead **pppathhT = &m_ppathhList; (*pppathhT) != NULL; pppathhT = &(*pppathhT)->ppathhNext) { + if (*pppathhT == ppathh) { + *pppathhT = ppathh->ppathhNext; + ppathh->ppathhNext = m_ppathhFreeList; + m_ppathhFreeList = ppathh; + return; + } + } + Assert(); +} + +bool TerrainMap::IsBlocked(TCoord tx, TCoord ty, byte bf) +{ + word offMap = ty * m_ctx + tx; + + // We path through structures and blocked terrain (at very high cost) so that structures or + // terrain that are "blocking" can at least be approached (albeit at high pathing cost) + // as opposed to be totally blocked and never considered. Then at path following we determine + // if there is blockage with this method. + + if (m_abBuffer[offMap] & bf) + return true; + if (((byte *)(m_ptrmaph + 1))[offMap] > kttOpen) + return true; + + return false; +} + +int TerrainMap::GetTerrainType(TCoord tx, TCoord ty) +{ + Assert(tx >= 0 && tx < m_ctx && ty >= 0 && ty < m_cty); + word offMap = ty * m_ctx + tx; + return ((byte *)(m_ptrmaph + 1))[offMap]; +} + +bool TerrainMap::GetFlags(int tx, int ty, byte *pbf) +{ + Assert(tx >= 0 && ty >= 0); + Assert(tx < m_ctx && ty < m_cty); + + word offMap = ty * m_ctx + tx; + if (((byte *)(m_ptrmaph + 1))[offMap] > kttOpen) + return false; + *pbf = m_abBuffer[offMap]; + return true; +} + +Path *TerrainMap::MakePath(TCoord txStart, TCoord tyStart, word off) +{ + Direction adir[kcNodesMax]; + + TCoord ty = off / m_ctx; + TCoord tx = off % m_ctx; + byte *pbPathWalk = &m_abBuffer[off]; + Direction *pdirT = &adir[ARRAYSIZE(adir)]; + while (tx != txStart || ty != tyStart) { + Assert(*pbPathWalk & kbfLinked); + Direction dir = (*pbPathWalk) & kbfDirMask; + pdirT--; + *pdirT = dir; + Direction dirOpposite = gmpdirdirOpposite[dir]; + pbPathWalk += m_mpDirDelta[dirOpposite]; + tx += g_mpDirToDx[dirOpposite]; + ty += g_mpDirToDy[dirOpposite]; + } + + return CreatePath(this, txStart, tyStart, pdirT, &adir[ARRAYSIZE(adir)] - pdirT); +} + +// +// Path class +// + +Path *CreatePath(TerrainMap *ptrmap, TCoord txStart, TCoord tyStart, TPoint *atpt, int ctpt) +{ + Direction adir[kcNodesMax]; + + TCoord txLast = txStart; + TCoord tyLast = tyStart; + for (int idir = 0; idir < ctpt; idir++) { + adir[idir] = DirectionFromLocations(txLast, tyLast, atpt[idir].tx, atpt[idir].ty); + txLast = atpt[idir].tx; + tyLast = atpt[idir].ty; + } + + return CreatePath(ptrmap, txStart, tyStart, adir, ctpt); +} + +Path *CreatePath(TerrainMap *ptrmap, TCoord txStart, TCoord tyStart, Direction *adir, int cdir) +{ + Path *ppath = new Path; + if (ppath == NULL) + return NULL; + if (!ppath->Init(ptrmap, txStart, tyStart, adir, cdir)) { + delete ppath; + return NULL; + } + return ppath; +} + +Path::Path() +{ + m_cdirs = 0; + m_adir = NULL; +} + +Path::~Path() +{ + delete m_adir; +} + +bool Path::Init(TerrainMap *ptrmap, TCoord txStart, TCoord tyStart, Direction *adir, int cdir) +{ + m_adir = new Direction[cdir]; + if (m_adir == NULL) + return false; + memcpy(m_adir, adir, cdir * sizeof(Direction)); + + m_cdirs = cdir; + m_txStart = txStart; + m_tyStart = tyStart; + + // Init cache + + m_idirCache = -1; + m_txCache = txStart; + m_tyCache = tyStart; + m_ptrmap = ptrmap; + + return true; +} + +int Path::GetCount() +{ + return m_cdirs; +} + +bool Path::GetPoint(int itpt, TPoint *ptpt, byte bf) +{ + if (itpt < 0 || itpt >= m_cdirs) + return false; + + TCoord txT, tyT; + CalcTo(itpt, &txT, &tyT); + + if (m_ptrmap->IsBlocked(txT, tyT, bf)) + return false; + + ptpt->tx = txT; + ptpt->ty = tyT; + return true; +} + +bool Path::GetPointRaw(int itpt, TPoint *ptpt) +{ + if (itpt < 0 || itpt >= m_cdirs) + return false; + TCoord txT, tyT; + CalcTo(itpt, &txT, &tyT); + ptpt->tx = txT; + ptpt->ty = tyT; + return true; +} + +void Path::SetCacheIndex(int idir) +{ + if (idir < 0 || idir >= m_cdirs) + return; + + TCoord txT, tyT; + CalcTo(idir, &txT, &tyT); + m_idirCache = idir; + m_txCache = txT; + m_tyCache = tyT; +} + +void Path::CalcTo(int idir, TCoord *ptx, TCoord *pty) +{ + if (idir < m_idirCache) { + m_idirCache = -1; + m_txCache = m_txStart; + m_tyCache = m_tyStart; + Assert(false); + } + + int idirT = m_idirCache; + TCoord txT = m_txCache; + TCoord tyT = m_tyCache; + while (true) { + idirT++; + if (idirT > idir) + break; + txT += g_mpDirToDx[m_adir[idirT]]; + tyT += g_mpDirToDy[m_adir[idirT]]; + } + + *ptx = txT; + *pty = tyT; +} + +int Path::FindClosestPoint(TCoord txFrom, TCoord tyFrom, int itptStart, int ctptFurtherStop, TPoint *ptptClosest) +{ + int ctptRemaining = m_cdirs - itptStart; + + word n2Last = (word)-1; + word nSmallest = (word)-1; + int jSmallest = -1; + int ctptFurtherStopT = ctptFurtherStop; + for (int j = 0; j < ctptRemaining; j++) { + // Get location. False would mean it's blocked + + TPoint tpt; + if (!GetPointRaw(j + itptStart, &tpt)) + break; + + // If we're on the path, return that + + if (tpt.tx == txFrom && tpt.ty == tyFrom) { + *ptptClosest = tpt; + return j + itptStart; + } + + // Calc smallest distance, always advance if we get smaller + + word n2 = ganSquared[abs(tpt.tx - txFrom)] + ganSquared[abs(tpt.ty - tyFrom)]; + if (n2 <= nSmallest) { + nSmallest = n2; + jSmallest = j; + *ptptClosest = tpt; + } + + // If we've increased distance the last cFurtherAwayStop times, stop + + if (n2 > n2Last) { + ctptFurtherStopT--; + if (ctptFurtherStopT == 0) + break; + } else { + ctptFurtherStopT = ctptFurtherStop; + } + n2Last = n2; + } + + if (jSmallest == -1) + return -1; + return jSmallest + itptStart; +} + +bool Path::Append(Path *ppath) +{ + Direction *adirNew = new Direction[m_cdirs + ppath->m_cdirs]; + if (adirNew == NULL) + return false; + memcpy(adirNew, m_adir, m_cdirs); + memcpy(&adirNew[m_cdirs], ppath->m_adir, ppath->m_cdirs); + delete m_adir; + m_adir = adirNew; + m_cdirs += ppath->m_cdirs; + return true; +} + +bool Path::TrimEnd(int itptStart) +{ + if (itptStart >= m_cdirs) + return false; + int cdirsNew = itptStart; + Direction *adirNew = new Direction[cdirsNew]; + if (adirNew == NULL) + return false; + memcpy(adirNew, m_adir, cdirsNew); + delete m_adir; + m_adir = adirNew; + m_cdirs = cdirsNew; + return true; +} + +Path *Path::Clone() +{ + Path *ppathNew = new Path(); + if (ppathNew == NULL) + return NULL; + + ppathNew->m_ptrmap = m_ptrmap; + ppathNew->m_cdirs = m_cdirs; + ppathNew->m_txStart = m_txStart; + ppathNew->m_tyStart = m_tyStart; + ppathNew->m_idirCache = -1; + ppathNew->m_txCache = m_txStart; + ppathNew->m_tyCache = m_tyStart; + + ppathNew->m_adir = new Direction[m_cdirs]; + if (ppathNew->m_adir == NULL) { + delete ppathNew; + return NULL; + } + memcpy(ppathNew->m_adir, m_adir, m_cdirs); + return ppathNew; +} + +void Path::GetStartPoint(TPoint *ptpt) +{ + ptpt->tx = m_txStart; + ptpt->ty = m_tyStart; +} + +#define knVerPathState 4 +bool Path::LoadState(TerrainMap *ptrmap, Stream *pstm) +{ + byte nVer = pstm->ReadByte(); + if (nVer != knVerPathState) + return false; + + m_ptrmap = ptrmap; + m_cdirs = pstm->ReadWord(); + m_txStart = pstm->ReadWord(); + m_tyStart = pstm->ReadWord(); + m_adir = new Direction[m_cdirs]; + if (m_adir == NULL) + return false; + m_idirCache = (int)(short)pstm->ReadWord(); + m_txCache = pstm->ReadWord(); + m_tyCache = pstm->ReadWord(); + + byte bT; + for (int idir = 0; idir < m_cdirs; idir++) { + Direction dir; + if ((idir & 1) == 0) { + bT = pstm->ReadByte(); + dir = (bT & 0xf0) >> 4; + } else { + dir = bT & 0x0f; + } + m_adir[idir] = dir; + } + + // Done + + return pstm->IsSuccess(); +} + +bool Path::SaveState(Stream *pstm) +{ + pstm->WriteByte(knVerPathState); + pstm->WriteWord(m_cdirs); + pstm->WriteWord(m_txStart); + pstm->WriteWord(m_tyStart); + pstm->WriteWord(m_idirCache); + pstm->WriteWord(m_txCache); + pstm->WriteWord(m_tyCache); + + // 2 directions per byte, smaller encoding + + byte bT = 0; + for (int idir = 0; idir < m_cdirs; idir++) { + Direction dir = m_adir[idir]; + if ((idir & 1) == 0) { + bT = dir << 4; + if (idir == m_cdirs - 1) + pstm->WriteByte(bT); + } else { + bT |= dir; + pstm->WriteByte(bT); + } + } + + // Done + + return pstm->IsSuccess(); +} + +#ifdef DRAW_PATHS +void Path::Draw(DibBitmap *pbm, int xView, int yView, Side side) +{ + TCoord txOld = m_txStart; + TCoord tyOld = m_tyStart; + + for (int idir = 0; idir < m_cdirs; idir++) { + Direction dir = m_adir[idir]; + TCoord txNew = txOld + g_mpDirToDx[dir]; + TCoord tyNew = tyOld + g_mpDirToDy[dir]; + DrawArrow(pbm, PcFromTc(txOld) - xView, PcFromTc(tyOld) - yView, dir, side); + txOld = txNew; + tyOld = tyNew; + } + + // Draw 'X' at destination + + DrawArrow(pbm, PcFromTc(txOld) - xView, PcFromTc(tyOld) - yView, 8, side); +} +#endif + +Direction DirectionFromLocations(TCoord txOld, TCoord tyOld, TCoord txNew, TCoord tyNew) +{ + Assert(abs(txOld - txNew) <= 1 && abs(tyOld - tyNew) <= 1); + + Direction dir; + if (txNew < txOld) { + if (tyNew < tyOld) { + dir = kdirNW; + } else if (tyNew > tyOld) { + dir = kdirSW; + } else { + dir = kdirW; + } + } else if (txNew > txOld) { + if (tyNew < tyOld) { + dir = kdirNE; + } else if (tyNew > tyOld) { + dir = kdirSE; + } else { + dir = kdirE; + } + } else { + if (tyNew < tyOld) { + dir = kdirN; + } else if (tyNew > tyOld) { + dir = kdirS; + } else { + Assert(false); + dir = kdirInvalid; + } + } + + return dir; +} + +// +// TrackPoint class +// + +bool TrackPoint::Init(Path *ppath, TCoord txFrom, TCoord tyFrom, int itptStart, int ctptFurtherStop) +{ + // Create a tracking point from txFrom, tyFrom to the path + + m_itptClosest = ppath->FindClosestPoint(txFrom, tyFrom, itptStart, ctptFurtherStop, &m_tptClosest); + if (m_itptClosest == -1) + return false; + + // Get the previous and next locations + + if (!ppath->GetPoint(m_itptClosest - 1, &m_tptBefore, 0)) + m_tptBefore = m_tptClosest; + if (!ppath->GetPoint(m_itptClosest + 1, &m_tptAfter, 0)) + m_tptAfter = m_tptClosest; + + // Measure current distances to these locations. We'll measure + // progress against these + + m_n2Before = ganSquared[abs(txFrom - m_tptBefore.tx)] + ganSquared[abs(tyFrom - m_tptBefore.ty)]; + m_n2After = ganSquared[abs(txFrom - m_tptAfter.tx)] + ganSquared[abs(tyFrom - m_tptAfter.ty)]; + m_tptInitial.tx = txFrom; + m_tptInitial.ty = tyFrom; + return true; +} + +void TrackPoint::InitFrom(TrackPoint *ptrkp) +{ + m_tptInitial = ptrkp->m_tptInitial; + m_tptClosest = ptrkp->m_tptClosest; + m_tptBefore = ptrkp->m_tptBefore; + m_tptAfter = ptrkp->m_tptAfter; + m_itptClosest = ptrkp->m_itptClosest; + m_n2Before = ptrkp->m_n2Before; + m_n2After = ptrkp->m_n2After; +} + +bool TrackPoint::IsProgress(TrackPoint *ptrkpA) +{ + // Returns true if this is closer than ptrkp + // First compare closest path point. Longer is better "progress" + + TrackPoint *ptrkpB = this; + if (ptrkpA->m_itptClosest < ptrkpB->m_itptClosest) + return false; + if (ptrkpA->m_itptClosest > ptrkpB->m_itptClosest) + return true; + + // Either same distance to before and closer to after, or further from before and same or greater to + // after or closer to both before and after are recognized as "progress". + + if ((ptrkpA->m_n2Before == ptrkpB->m_n2Before && ptrkpA->m_n2After < ptrkpB->m_n2After) || + (ptrkpA->m_n2Before > ptrkpB->m_n2Before && ptrkpA->m_n2After <= ptrkpB->m_n2After) || + (ptrkpA->m_n2Before < ptrkpB->m_n2Before && ptrkpA->m_n2After < ptrkpB->m_n2After)) { + return true; + } + return false; +} + +bool TrackPoint::IsCloser(TrackPoint *ptrkpA) +{ + // Is ptrkpA closer to the unit path than ptrkpB? If so, it is closer. If same distance, + // measure progress. + + TrackPoint *ptrkpB = this; + if (ptrkpA->m_n2After < ptrkpB->m_n2After) + return true; + if (ptrkpA->m_n2After == ptrkpB->m_n2After) + return IsProgress(ptrkpA); + return false; +} + +bool TrackPoint::IsBetterSort(TrackPoint *ptrkpA) +{ + return IsProgress(ptrkpA); +#if 0 + TrackPoint *ptrkpB = this; + if (ptrkpA->m_n2After < ptrkpB->m_n2After) + return true; + if (ptrkpA->m_n2After == ptrkpB->m_n2After) { + if (ptrkpA->m_n2Before > ptrkpB->m_n2Before) + return true; + } + return false; +#endif +} + +} // namespace wi \ No newline at end of file diff --git a/game/tests.cpp b/game/tests.cpp new file mode 100644 index 0000000..698adae --- /dev/null +++ b/game/tests.cpp @@ -0,0 +1,161 @@ +#include "ht.h" + +namespace wi { + +#if 1 +void TestTBitmap(DibBitmap *pbm) secCode8; + +void TestTBitmap(DibBitmap *pbm) +{ +} +#else +extern "C" void BltTest68K(byte *pbSrc, byte *pbDst); +extern "C" void TBitmapTest(byte *pbDst, byte *aiclrSide, byte *aiclrShadow) secCode4; +extern "C" void FillEvenEven8(byte *pbRow, int cx, int cy, int cxDib, byte bFill); +extern "C" void CopyBits48Wide(byte *pbSrc, int cbSrcRow, byte *pbDst, int cbDstRow, int cy) secCode8; + +void DoTimeTest(DibBitmap *pbm) +{ + long c = 5000; + long lStart; + long lEnd; + long n; + + TBitmap *ptbmCode = (TBitmap *)LoadTBitmap("srtest.tbm"); + //ptbmCode->CompileTest(c); + //ptbmCode->DrawTest(pbm, c); + delete ptbmCode; + + byte *pbDst = new byte[48 * 32]; + byte *pbSrc = pbm->GetBits(); + lStart = HostGetTickCount(); + for (n = 0; n < c; n++) + CopyBits48Wide(pbSrc, 160 - 48, pbDst, 0, 32); + lEnd = HostGetTickCount(); + HostMessageBox("%ld count %ld ticks", c, lEnd - lStart); + +#if 0 + ptbm = ptbmCode; + //ptbm = LoadTBitmap("srtest.tbm"); + ptbm->BltTo(pbm, -24, 10); + lStart = HostGetTickCount(); + for (n = 0; n < c; n++) + ptbm->BltTo(pbm, -24, 10); + lEnd = HostGetTickCount(); + HostMessageBox("%ld count %ld ticks", c, lEnd - lStart); + delete ptbm; + + ptbm = LoadTBitmap("srtest2.tbm"); + lStart = HostGetTickCount(); + for (n = 0; n < c; n++) + ptbm->BltTo(pbm, 10, 10); + lEnd = HostGetTickCount(); + HostMessageBox("%ld count %ld ticks", c, lEnd - lStart); + delete ptbm; + + ptbm = LoadTBitmap("sri_jog_0_0.tbm"); + lStart = HostGetTickCount(); + for (n = 0; n < c; n++) + ptbm->BltTo(pbm, 10, 10); + lEnd = HostGetTickCount(); + HostMessageBox("%ld count %ld ticks", c, lEnd - lStart); + delete ptbm; +#endif + +#if 0 + byte *pbBits = pbm->GetBits(); + lStart = HostGetTickCount(); + for (n = 0; n < c; n++) + TBitmapTest(pbBits, gaclrSideRed, gmpiclriclrShadow); + lEnd = HostGetTickCount(); + HostMessageBox("%ld blts %ld ticks", c, lEnd - lStart); +#endif + +#if 0 + FileMap fmap; + BitmapHeader *pbmh = (BitmapHeader *)MapFile("sri_fire_5_1.tbm", &fmap); + byte *pbDst = pbm->GetBits() + 160 * 15; + lStart = HostGetTickCount(); + for (n = 0; n < c; n++) + BltTest68K((byte *)(pbmh + 1), pbDst); + lEnd = HostGetTickCount(); + HostMessageBox("%ld blts %ld ticks", c, lEnd - lStart); + UnmapFile(&fmap); +#endif +} + +TBitmap *ptbmTest; +int gxTest; +int gyTest; +int dxTest; +int gcxTest; +int gcDelay; +void TestTBitmap(DibBitmap *pbm) +{ + static bool sfTested; + +#if 0 + if (sfTested) + return; + sfTested = true; + DoTimeTest(pbm); + return; +#endif + +#if 1 + if (ptbmTest == NULL) { + ptbmTest = LoadTBitmap("sri_stand_4_0.tbm"); + if (ptbmTest == NULL) + return; + gxTest = 11; + gyTest= 10; + dxTest = 1; + Size siz; + ptbmTest->GetSize(&siz); + gcxTest = siz.cx; + } + Trace("BltTo %d,%d", gxTest, gyTest); + ptbmTest->BltTo(pbm, gxTest, gyTest, 1); +#if 1 + gcDelay++; + if (gcDelay < 1) // 20) + return; + gcDelay = 0; + gxTest += dxTest; + if (dxTest == 1 && gxTest == 0) { + gyTest--; + if (gyTest == -14) + gyTest = 0; + dxTest = -1; + } + if (dxTest == -1 && gxTest == -7) { + gyTest--; + if (gyTest == -14) + gyTest = 0; + dxTest = 1; + } +#endif +#endif +} + +#ifdef INCL_TESTS + +#if 0 +//Obviously doesn't compile at the moment + +//temp + if (HostGetCurrentKeyState(keyBitPageDown)) { + long c = 5000; + pfogm->RevealAll(); + long lStart = HostGetTickCount(); + for (long n = 0; n < c; n++) + ptmap->Draw(pbm, xView, yView, pfogm->GetMapPtr()); + long lEnd = HostGetTickCount(); + HostMessageBox("%ld mapdraws %ld ticks", c, lEnd - lStart); + } + +#endif +#endif +#endif + +} // namespace wi \ No newline at end of file diff --git a/game/thunks.cpp b/game/thunks.cpp new file mode 100644 index 0000000..5ba7d72 --- /dev/null +++ b/game/thunks.cpp @@ -0,0 +1,112 @@ +#include "ht.h" + +#ifndef __CPU_68K +#include "game/ops.h" +#endif + +namespace wi { + +#ifndef __CPU_68K +#include "miscgraphics.cpp" +#endif + +void FillShadowThunk(byte *pbDst, int cbRowDst, int cx, int cy, byte *aclrMap) +{ +#ifndef __CPU_68K + Shadow(pbDst, cbRowDst, cx, cy, aclrMap); +#else + if (gfArmPresent) { + FillShadowArm(pbDst, cbRowDst, cx, cy, aclrMap); + } else { + FillShadow68K(pbDst, cbRowDst, cx, cy, aclrMap); + } +#endif +} + +void FillThunk(byte *pbDst, int cx, int cy, int cbStride, byte bFill) +{ +#ifndef __CPU_68K + Fill(pbDst, cx, cy, cbStride, bFill); +#else + if (gfArmPresent) { + FillArm(pbDst, cx, cy, cbStride, bFill); + } else { + Fill68K(pbDst, cx, cy, cbStride, bFill); + } +#endif +} + +void LeftToRightBltThunk(byte *pbSrc, int cxSrcStride, byte *pbDst, int cxDstStride, int cx, int cy) +{ +#ifndef __CPU_68K + LeftToRightBlt(pbSrc, cxSrcStride, pbDst, cxDstStride, cx, cy); +#else + if (gfArmPresent) { + LeftToRightBltArm(pbSrc, cxSrcStride, pbDst, cxDstStride, cx, cy); + } else { + LeftToRightBlt68K(pbSrc, cxSrcStride, pbDst, cxDstStride, cx, cy); + } +#endif +} + +void RightToLeftBltThunk(byte *pbSrc, int cxSrcStride, byte *pbDst, int cxDstStride, int cx, int cy) +{ +#ifndef __CPU_68K + RightToLeftBlt(pbSrc, cxSrcStride, pbDst, cxDstStride, cx, cy); +#else + if (gfArmPresent) { + RightToLeftBltArm(pbSrc, cxSrcStride, pbDst, cxDstStride, cx, cy); + } else { + RightToLeftBlt68K(pbSrc, cxSrcStride, pbDst, cxDstStride, cx, cy); + } +#endif +} + +void DrawTileMapThunk(byte **ppbMap, int ctx, int cty, byte *pbDst, int cbDstStride, int cxLeftTile, int cyTopTile, int cxRightTile, int cyBottomTile, int ctxInside, int ctyInside, int cxTile, int cyTile) +{ +#ifndef __CPU_68K + DrawTileMap(ppbMap, ctx, cty, pbDst, cbDstStride, cxLeftTile, cyTopTile, cxRightTile, cyBottomTile, ctxInside, ctyInside, cxTile, cyTile); +#else + if (gfArmPresent) { + DrawTileMapArm(ppbMap, ctx, cty, pbDst, cbDstStride, cxLeftTile, cyTopTile, cxRightTile, cyBottomTile, ctxInside, ctyInside, cxTile, cyTile); + } else { + switch (cxTile) { + case 16: + DrawTileMap816(ppbMap, ctx, cty, pbDst, cbDstStride, cxLeftTile, cyTopTile, cxRightTile, cyBottomTile, ctxInside, ctyInside); + return; + + case 24: + DrawTileMap824(ppbMap, ctx, cty, pbDst, cbDstStride, cxLeftTile, cyTopTile, cxRightTile, cyBottomTile, ctxInside, ctyInside); + return; + } + } +#endif +} + +word Compile8Thunk(byte *pb, ScanData *psd, bool fOdd) +{ +#ifndef __CPU_68K + return Compile8(pb, psd, fOdd); +#else + if (gfArmPresent) { + return Compile8Arm(pb, psd, fOdd); + } else { + return Compile868K(pb, psd, fOdd); + } +#endif +} + +void DrawDispatchThunk(byte *pb, byte *pbSrc, byte *pbDst, int cbReturn, dword *mpscaiclrSide, byte *mpiclriclrShadow) +{ +#ifndef __CPU_68K + DrawDispatch(pb, pbSrc, pbDst, cbReturn, mpscaiclrSide, mpiclriclrShadow); +#else + if (gfArmPresent) { + DrawDispatchArm(pb, pbSrc, pbDst, cbReturn, mpscaiclrSide, mpiclriclrShadow); + } else { + DrawDispatch68K(pb, pbSrc, pbDst, cbReturn, mpscaiclrSide, mpiclriclrShadow); + } +#endif +} + +} // namespace wi diff --git a/game/tilemap.cpp b/game/tilemap.cpp new file mode 100644 index 0000000..b73ba9e --- /dev/null +++ b/game/tilemap.cpp @@ -0,0 +1,238 @@ +#include "ht.h" + +namespace wi { + +TileMap *LoadTileMap(char *pszFn, Size *psizPlayfield) +{ + TileMap *ptmap = new TileMap; + if (!ptmap->Load(pszFn, psizPlayfield)) { + delete ptmap; + return NULL; + } + return ptmap; +} + +TileMap::TileMap() +{ + memset(m_aptseth, 0, sizeof(m_aptseth)); + m_apbTileData = NULL; + m_cTileSets = 0; + m_cTiles = 0; + m_wf = 0; + m_apbDrawMap = NULL; + m_pmtseth = NULL; +} + +TileMap::~TileMap() +{ + if (m_wf & kfTmapMapped) { + gpakr.UnmapFile(&m_fmapTmap); + for (int nTset = 0; nTset < m_cTileSets; nTset++) + gpakr.UnmapFile(&m_afmapTset[nTset]); + } + if (m_wf & kfMiniTsetMapped) + gpakr.UnmapFile(&m_fmapMiniTset); + if (m_apbTileData != NULL) + gmmgr.FreePtr(m_apbTileData); + delete m_apbDrawMap; +} + +bool TileMap::Load(char *psz, Size *psizPlayfield) +{ + // First try to alloc local map + + m_ctx = (psizPlayfield->cx + (gcxTile - 1)) / gcxTile + 1; + m_cty = (psizPlayfield->cy + (gcyTile - 1)) / gcyTile + 1; + m_apbDrawMap = new byte *[m_ctx * m_cty]; + if (m_apbDrawMap == NULL) + return false; + + // Load the tile data + + m_ptmaph = (TileMapHeader *)gpakr.MapFile(psz, &m_fmapTmap); + if (m_ptmaph == NULL) + return false; + m_wf |= kfTmapMapped; + m_pwMapData = (word *)(m_ptmaph + 1); + + // Start loading tsets. + + m_cTileSets = 0; + m_cTiles = 0; + while (true) { + char sz[kcchFnTset]; + strcpy(sz, m_ptmaph->szFnTset); + if (m_cTileSets != 0) { + char szT[8]; + szT[0] = '.'; + itoa(m_cTileSets, &szT[1], 10); + strcat(sz, szT); + } + TileSetHeader *ptseth = (TileSetHeader *)gpakr.MapFile(sz, &m_afmapTset[m_cTileSets]); + if (ptseth == NULL) + break; + m_aptseth[m_cTileSets] = ptseth; + m_cxTile = BigWord(ptseth->cxTile); + m_cyTile = BigWord(ptseth->cyTile); + m_cTileSets++; + m_cTiles += BigWord(ptseth->cTiles); + } + if (m_cTiles == 0) + return false; + + // Alloc enough tile pointers to point to the individual tiles + + byte **apbTileData = new byte *[m_cTiles]; + if (apbTileData == NULL) + return false; + + // Fill in the pointers to tile data + + byte **ppb = apbTileData; + int cbTile = m_cxTile * m_cyTile; + for (int nTset = 0; nTset < m_cTileSets; nTset++) { + TileSetHeader *ptseth = m_aptseth[nTset]; + byte *pbTileData = (byte *)ptseth + kcbTileSetHeader; + for (int nTile = 0; nTile < BigWord(ptseth->cTiles); nTile++) { + *ppb = pbTileData; + ppb++; + pbTileData += cbTile; + } + } + + // Save away + + int cbT = sizeof(byte *) * m_cTiles; + m_apbTileData = (byte **)gmmgr.AllocPtr(cbT); + if (m_apbTileData == NULL) { + delete apbTileData; + return false; + } + gmmgr.WritePtr(m_apbTileData, 0, apbTileData, cbT); + delete apbTileData; + + // Load mini tset + + char sz[50]; + strcpy(sz, m_ptmaph->szFnTset); + strcat(sz, "mini"); + m_pmtseth = (MiniTileSetHeader *)gpakr.MapFile(sz, &m_fmapMiniTset); + if (m_pmtseth == NULL) + return false; + m_wf |= kfMiniTsetMapped; + + // We're done + + return true; +} + +MiniTileSetHeader *TileMap::GetMiniTileSetHeader(int nScale) +{ + MiniTileSetHeader *pmtseth = m_pmtseth; + while (true) { + if (BigWord(pmtseth->cxTile) == nScale) + return pmtseth; + if (pmtseth->offNext == 0) + return NULL; + pmtseth = (MiniTileSetHeader *)((byte *)pmtseth + BigWord(pmtseth->offNext)); + } +} + +void TileMap::GetMapSize(Size *psiz) +{ + psiz->cx = BigWord(m_ptmaph->ctx) * m_cxTile; + psiz->cy = BigWord(m_ptmaph->cty) * m_cyTile; +} + +void TileMap::GetTileSize(Size *psiz) +{ + psiz->cx = m_cxTile; + psiz->cy = m_cyTile; +} + +void TileMap::Draw(DibBitmap *pbm, int x, int y, int cx, int cy, int xMap, int yMap, byte *pbFogMap, UpdateMap *pupd) +{ + Assert(!((xMap | yMap) & 1)); + + //For debugging: + //BitmapType *pbmpScreen = WinGetBitmap(WinGetDisplayWindow()); + //pbBits = (byte *)BmpGetBits(pbmpScreen); + + // Get the tile coordinates into the map from which drawing is to start + + int tx = TcFromPc(xMap); + int ty = TcFromPc(yMap); + + // Figure map info + + MapInfo *pmnfo = pupd->GetMapInfo(); + int ctx = TcFromPc(cx - pmnfo->cxLeftTile + gcxTile - 1) + TcFromPc(pmnfo->cxLeftTile + gcxTile - 1); + int cty = TcFromPc(cy - pmnfo->cyTopTile + gcyTile - 1) + TcFromPc(pmnfo->cyTopTile + gcyTile - 1); + int ctxMap = BigWord(m_ptmaph->ctx); + +#ifdef DEBUG + Assert(ctx <= m_ctx); + Assert(cty <= m_cty); + Assert(tx + ctx <= BigWord(m_ptmaph->ctx)); + Assert(ty + cty <= BigWord(m_ptmaph->cty)); + Size sizT; + pupd->GetMapSize(&sizT); + Assert(sizT.cx == m_ctx && sizT.cy == m_cty); +#endif + + // Get running pointers into various maps + // Note: Fog will be accounted for in updatemap in the near future + + word iCell = ty * ctxMap + tx; + word *pwMapT = &m_pwMapData[iCell]; + byte *pbFogT = &pbFogMap[iCell]; + bool *pfInvalid = pupd->GetInvalidMap(); + byte **ppbDrawMap = m_apbDrawMap; + + int cReturnDrawMap = m_ctx - ctx; + int cReturnTileMap = ctxMap - ctx; + for (int tyT = 0; tyT < cty; tyT++) { + Assert(ty + tyT < BigWord(m_ptmaph->cty)); + for (int txT = 0; txT < ctx; txT++) { + Assert(tx + txT < BigWord(m_ptmaph->ctx)); + + *ppbDrawMap = NULL; + if (!IsFogOpaque(*pbFogT)) { + if (*pfInvalid) { + word offset = BigWord(*pwMapT); + Assert(offset < 0xff00); + *ppbDrawMap = *(byte **)((long)m_apbTileData + offset); + } + } + + ppbDrawMap++; + pbFogT++; + pfInvalid++; + pwMapT++; + } + + ppbDrawMap += cReturnDrawMap; + pfInvalid += cReturnDrawMap; + pwMapT += cReturnTileMap; + pbFogT += cReturnTileMap; + } + + // Now draw this map +#if 0 + BitmapType *pbmpScreen = WinGetBitmap(WinGetDisplayWindow()); + byte *pbDib = (byte *)BmpGetBits(pbmpScreen); + Size sizFill; + pbm->GetSize(&sizFill); + Fill(pbDib, sizFill.cx, sizFill.cy, sizFill.cx, 1); +#else + byte *pbDib = pbm->GetBits(); +#endif + + Size sizDib; + pbm->GetSize(&sizDib); + pbDib += y * sizDib.cx + x; + + DrawTileMapThunk(m_apbDrawMap, m_ctx, m_cty, pbDib, sizDib.cx, pmnfo->cxLeftTile, pmnfo->cyTopTile, pmnfo->cxRightTile, pmnfo->cyBottomTile, pmnfo->ctxInside, pmnfo->ctyInside, gcxTile, gcyTile); +} + +} // namespace wi \ No newline at end of file diff --git a/game/timer.cpp b/game/timer.cpp new file mode 100644 index 0000000..529719c --- /dev/null +++ b/game/timer.cpp @@ -0,0 +1,245 @@ +#include "ht.h" +#include "base/messagequeue.h" + +namespace wi { + +//#define TIME_STEPPER + +#ifdef TIME_STEPPER +// This makes time "stop" when in the debugger +class TimeStepper : public base::MessageHandler { +public: + TimeStepper() : m_ct(0), m_fInitialized(false) {} + + long GetTickCount() { + if (!m_fInitialized) { + OneShot(); + m_fInitialized = true; + } + return m_ct; + } + +private: + void OneShot() { + base::Message msg; + msg.handler = this; + thread_.PostDelayed(&msg, 1); + } + + virtual void OnMessage(base::Message *pmsg) { + m_ct++; + OneShot(); + } + + long m_ct; + bool m_fInitialized; +}; +#endif + +TimerMgr::TimerMgr() +{ + m_fEnabled = true; + m_ptmrFirst = NULL; + m_ptmrNotifying = NULL; + m_ptmrTriggerNext = NULL; + m_fForceScan = true; +} + +TimerMgr::~TimerMgr() +{ + Assert(m_ptmrFirst == NULL); +} + +long TimerMgr::GetTickCount() +{ +#ifdef TIME_STEPPER + // Makes time stop while in the debugger, for easier debugging + + static TimeStepper stepper; + return stepper.GetTickCount(); +#else + return HostGetTickCount(); +#endif +} + +long TimerMgr::ScanDispatch(long tCurrent) +{ + if (!m_fEnabled) + return -1; + + // If currently scanning and being re-entered, disallow it. + + Assert(m_ptmrNotifying == NULL); + if (m_ptmrNotifying != NULL) + return -1; + + // If the timer state hasn't changed since the last scan, we can exit if not + // enough time has elapsed for a timer to go off. + + if (!m_fForceScan) { + if (m_ptmrTriggerNext != NULL) { + long ctT = m_ptmrTriggerNext->m_tTrigger - tCurrent; + if (ctT > 0) + return ctT; + } + } + m_fForceScan = false; + + // Scan timers to look for next timer to go off. + // Unfortunately these can't be sorted since the firing order is unique for any + // point in time. + + Timer *ptmrT; + for (ptmrT = m_ptmrFirst; ptmrT != NULL; ) { + + // If the timer has triggered, set its next trigger time to its current + // trigger time plus its rate. This is an attempt at keeping timing + // consistent and predictable. However, if this new time is less than + // or equal to the current real time we set it to the real time so we + // don't get stuck in a loop only processing timers. + + bool fRemoved = false; + Timer *ptmrNext = ptmrT->m_ptmrNext; + + if (ptmrT->m_tTrigger <= tCurrent) { + + // Remember that this timer is notifying. + + m_ptmrNotifying = ptmrT; + ptmrT->OnTimer(tCurrent); + m_ptmrNotifying = NULL; + + fRemoved = ptmrT->m_tTrigger == -1; + + if (!fRemoved) { + // Reset the rate after notification in case the rate was + // changed during notification. + + ptmrT->m_tTrigger += ptmrT->m_ctRate; + + // If the timer is falling behind real time catch it up + + tCurrent = GetTickCount(); + if (ptmrT->m_tTrigger < tCurrent) + ptmrT->m_tTrigger = tCurrent; + } + } + + ptmrT = ptmrNext; + } + + // Determine the next timer to trigger. This is done outside the + // above loop because timers, including the m_ptmrTriggerNext, may + // be removed during the OnTimer callback. + + m_ptmrTriggerNext = m_ptmrFirst; + for (ptmrT = m_ptmrFirst; ptmrT != NULL; ptmrT = ptmrT->m_ptmrNext) { + if (ptmrT->m_tTrigger < m_ptmrTriggerNext->m_tTrigger) + m_ptmrTriggerNext = ptmrT; + } + + // Return time till next timer + + if (m_ptmrTriggerNext == NULL) + return -1; + long ct = tCurrent - m_ptmrTriggerNext->m_tTrigger; + if (ct < 0) + return 0; + return ct; +} + +void TimerMgr::AddTimer(Timer *ptmr, long ct) +{ + for (Timer *ptmrT = m_ptmrFirst; ptmrT != NULL; ptmrT = ptmrT->m_ptmrNext) { + Assert(ptmrT != ptmr, "Timer already added!"); + if (ptmrT == ptmr) + return; + } + + // Link it into the list + + if (m_ptmrFirst != NULL) + m_ptmrFirst->m_ptmrPrev = ptmr; + ptmr->m_ptmrPrev = NULL; + + ptmr->m_ptmrNext = m_ptmrFirst; + ptmr->m_ptimm = this; + m_ptmrFirst = ptmr; + + SetTimerRate(ptmr, ct); +} + +void TimerMgr::RemoveTimer(Timer *ptmr) +{ + bool fFound = false; + for (Timer *ptmrT = m_ptmrFirst; ptmrT != NULL; ptmrT = ptmrT->m_ptmrNext) { + if (ptmrT == ptmr) { + fFound = true; + break; + } + } + Assert(fFound, "Timer not on list! (already removed?)"); + if (!fFound) + return; + + ptmr->m_tTrigger = -1; + ptmr->m_ptimm = NULL; + + if (ptmr->m_ptmrPrev != NULL) { + ptmr->m_ptmrPrev->m_ptmrNext = ptmr->m_ptmrNext; + } else { + m_ptmrFirst = ptmr->m_ptmrNext; + } + + if (ptmr->m_ptmrNext != NULL) + ptmr->m_ptmrNext->m_ptmrPrev = ptmr->m_ptmrPrev; + + if (m_ptmrNotifying != ptmr && m_ptmrTriggerNext == ptmr) { + m_ptmrTriggerNext = NULL; + m_fForceScan = true; + } +} + +void TimerMgr::SetTimerRate(Timer *ptmr, long ct) +{ + // If the timer rate is decreasing, this may change the time till the next + // timer goes off, so force a scan next time. + + ptmr->m_ctRate = ct; + ptmr->m_tTrigger = GetTickCount() + ct; + m_fForceScan = true; +} + +void TimerMgr::BoostTimer(Timer *ptmr, long ct) { + ptmr->m_tTrigger += ct; + m_fForceScan = true; +} + +// Force this timer to be dispatched at the next call to TimerMgr::ScanDispatch +// (presumably by EventMgr::GetEvent) + +void TimerMgr::TriggerTimer(Timer *ptmr) +{ + ptmr->m_tTrigger = GetTickCount(); + m_fForceScan = true; +} + +bool TimerMgr::IsAdded(Timer *ptmr) +{ + Timer *ptmrT = m_ptmrFirst; + while (ptmrT != NULL) { + if (ptmrT == ptmr) { + break; + } + ptmrT = ptmrT->m_ptmrNext; + } + return ptmrT == ptmr; +} + +Timer::~Timer() { + if (m_ptimm != NULL && m_ptimm->IsAdded(this)) { + Assert("Timer being destroyed, but still in list!"); + } +} + +} // namespace wi diff --git a/game/timerwrap.h b/game/timerwrap.h new file mode 100644 index 0000000..2a5640f --- /dev/null +++ b/game/timerwrap.h @@ -0,0 +1,34 @@ +#ifndef__TIMERWRAP_H__ +#define __TIMERWRAP_H__ + +#include "game/ht.h" + +namespace wi { + +// This wraps the timer service, allowing an object to start and process +// multiple timers, by adding an id. This should of been in the original +// timer design. + +class ITimerWrap { + void OnTimerWrap(long tCurrent, dword id) = 0; +}; + +class TimerWrap : Timer { +public: + TimerWrap(ITimerWrap *pfn, dword id = 0) : pfn_(pfn), id_(id) { } + void Start(long ct) { gtimm.AddTimer(this, ct); } + void Stop() { gtimm.RemoveTimer(this); } + void started() { return gtimm.IsAdded(this); } + +private: + void OnTimer(long tCurrent) { + pfn_->OnTimerWrap(tCurrent, id_); + } + + ITimerWrap *pfn_; + dword id_; +}; + +} // namespace wi + +#endif // __TIMERWRAP_H__ diff --git a/game/triggermgr.cpp b/game/triggermgr.cpp new file mode 100644 index 0000000..81875c3 --- /dev/null +++ b/game/triggermgr.cpp @@ -0,0 +1,716 @@ +#include "ht.h" + +namespace wi { + +// Implementation Notes: +// - Trigger manager: +// * loads & manages triggers +// * has framework for trigger eval +// * maintains per-side evaluation order. +// - Trigger +// * holder for conditions and actions +// * understands 'blocking' actions +// +// Triggers get evaluated in per-side order +// Individual triggers can have actions that are in mid-execution +// +// Specific conditions and actions are each typed out. + +// +// TriggerMgr +// + +TriggerMgr::TriggerMgr() +{ + m_ctgr = 0; + m_atgr = NULL; + memset(m_mpSide2nTrigger, 0xff, sizeof(m_mpSide2nTrigger)); + memset(m_asidmCondition, 0, sizeof(m_asidmCondition)); + memset(m_abSwitch, 0, sizeof(m_abSwitch)); + m_cTimers = 0; + m_fEnabled = true; +} + +TriggerMgr::~TriggerMgr() +{ + delete[] m_atgr; + m_ctgr = 0; +} + +bool TriggerMgr::Init(IniReader *pini) +{ + // Get trigger count + + int cArgs = pini->GetPropertyValue("Triggers", "Count", "%d", &m_ctgr); + if (cArgs != 1) + return false; + + // Allocate trigger storage, one chunk + + Assert(m_atgr == NULL); + m_atgr = new Trigger[m_ctgr]; + Assert(m_atgr != NULL, "out of memory!"); + if (m_atgr == NULL) + return false; + + // Skip the Count property + + char szProp[128]; + FindProp find; + if (!pini->FindNextProperty(&find, "Triggers", szProp, sizeof(szProp))) + return false; +#ifdef _DEBUG + if (strcmp(szProp, "Count") != 0) + return false; +#endif + + // Initialize the triggers + + for (int ntgr = 0; ntgr < m_ctgr; ntgr++) { + // Next property name should be "T" + + if (!pini->FindNextProperty(&find, "Triggers", szProp, sizeof(szProp))) + return false; + if (strcmp(szProp, "T") != 0) + return false; + + // Remember per-side assignments and indexes + + int cArgs = pini->GetPropertyValue(&find, "%s", &szProp); + if (cArgs != 1) + return false; + if (!AssignTriggerSides(ntgr, szProp)) + return false; + + // Initialize this trigger + + if (!m_atgr[ntgr].Init(pini, &find)) + return false; + } + + m_tLastUpdate = gsim.GetTickCount(); + +#ifdef DEBUG_HELPERS + FindProp findSwitch; + int isw = 0; + while (pini->FindNextProperty(&findSwitch, "Switches", szProp, sizeof(szProp))) { + strncpyz(m_aszSwitchNames[isw], szProp, sizeof(m_aszSwitchNames[0])); + isw++; + Assert(isw <= kcSwitchMax); + } + + extern void UpdateTriggerViewer(); + UpdateTriggerViewer(); +#endif + return true; +} + +#define knVerTriggerMgrState 2 +bool TriggerMgr::LoadState(Stream *pstm) +{ + byte nVer = pstm->ReadByte(); + if (nVer != knVerTriggerMgrState) + return false; + + // Read Triggers + + Trigger *ptgr = m_atgr; + int i; // thank you WinCE compiler for this + for (i = 0; i < this->m_ctgr; i++, ptgr++) + ptgr->LoadState(pstm); + + // Read Switch state + + pstm->ReadBytesRLE(m_abSwitch, sizeof(m_abSwitch)); + + // Read one-update conditions + + pstm->ReadBytesRLE((byte *)m_asidmCondition, sizeof(m_asidmCondition)); + + // Read timer countdown values + + for (i = 0; i < m_cTimers; i++) + m_actCountdown[i] = pstm->ReadDword(); + m_tLastUpdate = pstm->ReadDword(); + + // load the game countdown timer + + m_cdt.LoadState(pstm); + + return pstm->IsSuccess(); +} + +bool TriggerMgr::SaveState(Stream *pstm) +{ + pstm->WriteByte(knVerTriggerMgrState); + + // Write Triggers + + Trigger *ptgr = m_atgr; + int i; // Thank you WinCE compiler for this + for (i = 0; i < this->m_ctgr; i++, ptgr++) + ptgr->SaveState(pstm); + + // Write Switch state + + pstm->WriteBytesRLE(m_abSwitch, sizeof(m_abSwitch)); + + // Write one-update conditions + + pstm->WriteBytesRLE((byte *)m_asidmCondition, sizeof(m_asidmCondition)); + + // Write timer countdown values + + for (i = 0; i < m_cTimers; i++) + pstm->WriteDword(m_actCountdown[i]); + pstm->WriteDword(m_tLastUpdate); + + m_cdt.SaveState(pstm); + + return pstm->IsSuccess(); +} + +bool TriggerMgr::AssignTriggerSides(int ntgr, char *psz) +{ + Assert(ntgr >= 0 && ntgr < m_ctgr); + + while (true) { + int side; + int nIndex; + int nch = 0; + int cArgs = IniScanf(psz, "%d:%d,%+", &side, &nIndex, &nch); + if (cArgs == 0) + break; + if (cArgs != 2 && cArgs != 3) + return false; + psz += nch; + Assert(side >= ksideNeutral && side < kcSides); + Assert(nIndex >= 0 && nIndex < kcTriggersPerSide); + if (nIndex < kcTriggersPerSide) + m_mpSide2nTrigger[side][nIndex] = (byte)ntgr; + if (cArgs == 2) + break; + } + + return true; +} + +void TriggerMgr::Update() +{ + if (!m_fEnabled) + return; + + m_cdt.Update(); + + // Update all the trigger timers + + long t = gsim.GetTickCount(); + long dt = t - m_tLastUpdate; + m_tLastUpdate = t; + + long *pct = m_actCountdown; + int i; // thank you WinCE compiler for this + for (i = 0; i < m_cTimers; i++) + if (*pct != kctTimerNotStarted) + *pct++ -= dt; + + // For each side... + + for (Side side = ksideNeutral; side < kcSides; side++) { + // Execute per-side triggers in the per-side specified order + + byte *pntgr = &m_mpSide2nTrigger[side][0]; + for (; *pntgr != 0xff; pntgr++) { + Assert(*pntgr < m_ctgr); + + // Subsequent triggers may have been disabled by the prior trigger + // (e.g., End Mission action) + + if (m_fEnabled) + m_atgr[*pntgr].Execute(side); + } + } + + // Clear one-Update conditions + + memset(m_asidmCondition, 0, sizeof(m_asidmCondition)); + + // Reset any underflowed timers + + pct = m_actCountdown; + for (i = 0; i < m_cTimers; i++) { + if (*pct <= 0 && *pct != kctTimerNotStarted) + *pct += m_actPeriod[i]; + pct++; + } + +#ifdef DEBUG_HELPERS + extern void UpdateTriggerViewer(); + UpdateTriggerViewer(); +#endif +} + +void TriggerMgr::SetConditionTrue(int nCondition, SideMask sidm) +{ + m_asidmCondition[nCondition] |= sidm; +} + +bool TriggerMgr::IsConditionTrue(int nCondition, SideMask sidm) +{ + return (m_asidmCondition[nCondition] & sidm) != 0; +} + +// +// Trigger +// + +Trigger::Trigger() +{ + m_pcdn = NULL; + m_pactn = NULL; + memset(m_apactnLast, 0, sizeof(m_apactnLast)); + memset(m_afArmed, 1, sizeof(m_afArmed)); +} + +Trigger::~Trigger() +{ + // Delete Conditions + + Condition *pcdn = m_pcdn; + while (pcdn != NULL) { + Condition *pcdnNext = pcdn->m_pcdnNext; + delete pcdn; + pcdn = pcdnNext; + } + + // Delete Actions + + TriggerAction *pactn = m_pactn; + while (pactn != NULL) { + TriggerAction *pactnNext = pactn->m_pactnNext; + delete pactn; + pactn = pactnNext; + } +} + +#define knVerTriggerState 1 +bool Trigger::LoadState(Stream *pstm) +{ + byte nVer = pstm->ReadByte(); + if (nVer != knVerTriggerState) + return false; + + // Read the last (in-progress) action (if any) for each side + + for (int i = 0; i < kcSides; i++) { + char nAction = pstm->ReadByte(); + if (nAction != -1) { + TriggerAction *pactn = m_pactn; + for (int j = 0; j < nAction; j++, pactn = pactn->m_pactnNext); + m_apactnLast[i] = pactn; + } + } + + // Read the per-side armed flags + + pstm->ReadBytesRLE((byte *)m_afArmed, sizeof(m_afArmed)); + + // Give each Action a chance to read its state + + for (TriggerAction *pactn = m_pactn; pactn != NULL; pactn = pactn->m_pactnNext) + pactn->LoadState(pstm); + + return pstm->IsSuccess(); +} + +bool Trigger::SaveState(Stream *pstm) +{ + pstm->WriteByte(knVerTriggerState); + + // Map m_apactnLast into an index or -1 for no last action + + for (int i = 0; i < kcSides; i++) { + if (m_apactnLast[i] == NULL) { + pstm->WriteByte((byte)-1); + } else { + char nAction = 0; + for (TriggerAction *pactn = m_pactn; pactn != NULL; pactn = pactn->m_pactnNext, nAction++) + if (pactn == m_apactnLast[i]) + break; + pstm->WriteByte(nAction); + } + } + + // Write the per-side armed flags + + pstm->WriteBytesRLE((byte *)m_afArmed, sizeof(m_afArmed)); + + // Give each Action a chance to write its state + + for (TriggerAction *pactn = m_pactn; pactn != NULL; pactn = pactn->m_pactnNext) + pactn->SaveState(pstm); + + return pstm->IsSuccess(); +} + +bool Trigger::Init(IniReader *pini, FindProp *pfind) +{ + // Property names + // 'T' means start of new trigger + // 'C' means condition + // 'A' means action + + char szProp[16]; + FindProp findLast; + findLast.Assign(pfind); + while (pini->FindNextProperty(pfind, "Triggers", szProp, sizeof(szProp))) { + switch (szProp[0]) { + case 'T': + // Return the last PropFind since TriggerMgr wants to find a 'T' next + + pfind->Assign(&findLast); + return true; + + case 'C': + if (!LoadCondition(pini, pfind)) + return false; + break; + + case 'A': + if (!LoadAction(pini, pfind)) + return false; + break; + + default: + Assert(false); + break; + } + + findLast.Assign(pfind); + } + + return true; +} + +// Execute will either resume at a blocking action or check conditions for true-ness and then +// perform actions. An action just needs to return false to "block" and be resumed later. + +void Trigger::Execute(Side side, bool fForce) +{ + // Is this trigger still executing an action for this side? + + TriggerAction *pactnNext = m_apactnLast[side]; + if (pactnNext == NULL) { + // If not armed, then this trigger no longer executes for this side + + if (!fForce) { + if (!m_afArmed[side]) + return; + + // It's armed so we check conditions. Return if any condition is not true. + + for (Condition *pcdn = m_pcdn; pcdn != NULL; pcdn = pcdn->m_pcdnNext) { + if (!pcdn->IsTrue(side)) + return; + } + } + + // The conditions are met so we're going to perform the actions. Disarm the + // trigger so its actions only happen once, unless a PreserveTriggerAction + // is used to rearm it. + + m_afArmed[side] = false; + } + + // Start executing actions. If pactnNext != NULL, start with that action + + if (pactnNext == NULL) + pactnNext = m_pactn; + + while (pactnNext != NULL) { + // If this action doesn't complete, remember that and return + + m_apactnLast[side] = pactnNext; + +#ifdef DEBUG_HELPERS + extern void UpdateTriggerViewer(); + UpdateTriggerViewer(); +#endif + if (!pactnNext->Perform(this, side)) + return; + + // Action complete, continue to next action + + m_apactnLast[side] = NULL; // clear out 'in-progress' action + pactnNext = pactnNext->m_pactnNext; + } +} + +void Trigger::SetCurrentActionComplete(Side side) +{ + m_apactnLast[side] = NULL; // clear out 'in-progress' action +} + +void Trigger::Arm(Side side) +{ + m_afArmed[side] = true; +} + +// +// Condition/Action parameter types +// + +bool QualifiedNumber::Parse(char **ppsz) +{ + int nch = 0; + int cArgs = IniScanf(*ppsz, "%d,%ld,%+", &m_nQualifier, &m_nNumber, &nch); + *ppsz += nch; + return cArgs >= 2; +} + +bool QualifiedNumber::Compare(long nNumber) +{ + switch (m_nQualifier) { + case knQualifierAtLeast: + return nNumber >= m_nNumber; + + case knQualifierAtMost: + return nNumber <= m_nNumber; + + case knQualifierExactly: + return nNumber == m_nNumber; + } + + return false; +} + +// countdown timer class + +CountdownTimer::CountdownTimer() +{ + m_szFormat[0] = 0; + m_secs = 0; + m_tLast = 0; + m_wf = 0; +} + +#define knVerCountdownTimerState 1 +bool CountdownTimer::LoadState(Stream *pstm) +{ + byte nVer = pstm->ReadByte(); + if (nVer != knVerCountdownTimerState) + return false; + + m_secs = (short)pstm->ReadWord(); + m_wf = pstm->ReadWord(); + m_tLast = pstm->ReadDword(); + pstm->ReadString(m_szFormat, sizeof(m_szFormat)); + + return pstm->IsSuccess(); +} + +bool CountdownTimer::SaveState(Stream *pstm) +{ + pstm->WriteByte(knVerCountdownTimerState); + pstm->WriteWord(m_secs); + + word wf = ggame.GetSimUIForm()->GetControlPtr(kidcCountdown)->GetFlags(); + m_wf = (wf & kfCtlVisible) ? (m_wf | kfCtVisibleAtStart) : (m_wf & ~kfCtVisibleAtStart); + + pstm->WriteWord(m_wf); + pstm->WriteDword(m_tLast); + pstm->WriteString(m_szFormat); + + + return pstm->IsSuccess(); +} + +void CountdownTimer::SetTimer(int csecs, char *pszFormatString) +{ + m_secs = csecs; + strncpyz(m_szFormat, pszFormatString, sizeof(m_szFormat)); +} + +void CountdownTimer::StartTimer(bool fStart) +{ + m_wf = (m_wf & ~kfCtRunning); + if (fStart) { + m_wf |= kfCtRunning; + m_tLast = gsim.GetTickCount(); + UpdateString(); + } +} + +void CountdownTimer::ShowTimer(bool fShow) +{ + ggame.GetSimUIForm()->GetControlPtr(kidcCountdown)->Show(fShow); +} + +bool CountdownTimer::GetTimer(int *psecs) +{ + *psecs = m_secs; + return (m_wf & kfCtRunning) != 0; +} + +void CountdownTimer::Update() +{ + if (m_wf & kfCtRunning && (m_secs > 0)) + { + long t = gsim.GetTickCount(); + long dt = t - m_tLast; + if (dt > 100) // greater than 1 second + { + m_secs -= 1; + m_tLast += 100; + + UpdateString(); + } + } +} + +void CountdownTimer::UpdateString() +{ + char szT[50]; + char szTemp[15]; + + // bummer! Palm does not support this format string + //sprintf(szTemp, "%.2d:%.2d", m_secsCountdown / 60, m_secsCountdown % 60); + + int cMins = m_secs / 60; + int cSecs = m_secs % 60; + int cBytes = sprintf(szTemp, "%s%d:%s%d", cMins > 9 ? "" : "0", cMins, cSecs > 9 ? "" : "0", cSecs); + + sprintf(szT, m_szFormat, szTemp); + LabelControl *plbl = (LabelControl *)ggame.GetSimUIForm()->GetControlPtr(kidcCountdown); + plbl->SetText(szT); +} + +// +// Parsers, helpers & interesting datatypes +// UNDONE: a scanf-like general parser to make condition/action parsers super easy to write +// E.g. for AreaContainsUnitsCondition: +// CaParse("dqud", &m_nArea, &m_qnum, &m_um, &m_nCaSideMask) +// + +SideMask GetSideMaskFromCaSideMask(Side sideCur, word wfCaSideMask) +{ + Assert(knCaSideSide1 == kside1 && knCaSideSide2 == kside2 && knCaSideSide3 == kside3 && knCaSideSide4 == kside4); + + // TODO: this approach is expensive at run-time + + SideMask sidm = wfCaSideMask & ksidmAll; + if (wfCaSideMask & (1 << knCaSideAllSides)) { + sidm |= ksidmAll; + } else { + if (wfCaSideMask & (1 << knCaSideAllies)) { + Player *pplr = gplrm.GetPlayer(sideCur); + sidm |= pplr->GetAllies(); + } + if (wfCaSideMask & (1 << knCaSideEnemies)) { + Player *pplr = gplrm.GetPlayer(sideCur); + sidm |= ksidmAll & ~pplr->GetAllies(); + } + if (wfCaSideMask & (1 << knCaSideCurrentSide)) + sidm |= GetSideMask(sideCur); + } + + return sidm; +} + +int GetPlayersListFromCaSideMask(Side sideCur, word wfMask, Player **applr) +{ + SideMask sidm = GetSideMaskFromCaSideMask(sideCur, wfMask); + + Player *pplr = NULL; + int i = 0; + while (true) { + pplr = gplrm.GetNextPlayer(pplr); + if (pplr == NULL) + break; + if (sidm & GetSideMask(pplr->GetSide())) + applr[i++] = pplr; + } + + return i; +} + +bool ParseNumber(char **ppsz, int *pn) +{ + int nch = 0; + int cArgs = IniScanf(*ppsz, "%d,%+", pn, &nch); + + // If this is the last of a comma separated list of items set the + // string pointer to point to the zero-terminator so subsequent + // calls will fail. + + if (cArgs == 1) { + *ppsz += strlen(*ppsz); + } else { + *ppsz += nch; + } + return cArgs >= 1; +} + +bool ParseLong(char **ppsz, long *pn) +{ + int nch = 0; + int cArgs = IniScanf(*ppsz, "%ld,%+", pn, &nch); + + // If this is the last of a comma separated list of items set the + // string pointer to point to the zero-terminator so subsequent + // calls will fail. + + if (cArgs == 1) { + *ppsz += strlen(*ppsz); + } else { + *ppsz += nch; + } + return cArgs >= 1; +} + +bool ParseArea(char **ppsz, int *pn) +{ + if (!ParseNumber(ppsz, pn)) + return false; + + Assert(*pn >= knAreaLastDiscovery, "Detected reference to invalid (deleted?) Area"); + return true; +} + +bool ParseUnitMask(char **ppsz, UnitMask *pum) +{ + *pum = 0; + int nch = 0; + int cArgs = IniScanf(*ppsz, "%ld,%+", pum, &nch); + *ppsz += nch; + return cArgs >= 1; +} + +bool ParseUpgradeMask(char **ppsz, UpgradeMask *pupgm) +{ + int upgm = 0; + int nch = 0; + int cArgs = IniScanf(*ppsz, "%d,%+", &upgm, &nch); + *ppsz += nch; + *pupgm = (word)upgm; + return cArgs >= 1; +} + +bool ParseString(char **ppsz, char *psz) +{ + int nch = 0; + int cArgs = IniScanf(*ppsz, "%s,%+", psz, &nch); + + // If this is the last of a comma separated list of items set the + // string pointer to point to the zero-terminator so subsequent + // calls will fail. + + if (cArgs == 1) { + *ppsz += strlen(*ppsz); + } else { + *ppsz += nch; + } + return cArgs >= 1; +} + +} // namespace wi diff --git a/game/updatemap.cpp b/game/updatemap.cpp new file mode 100644 index 0000000..b71f2d4 --- /dev/null +++ b/game/updatemap.cpp @@ -0,0 +1,658 @@ +#include "ht.h" + +namespace wi { + +UpdateMap::UpdateMap() +{ + m_afInvalid = NULL; + m_afInvalidDamage = NULL; + m_ctx = 0; + m_cty = 0; + m_fInvalid = false; + m_fInvalidDamage = false; + m_fMergeDamage = false; + m_txOrigin = 0; + m_tyOrigin = 0; + m_xOriginView = 0; + m_yOriginView = 0; +} + +UpdateMap::~UpdateMap() +{ + delete m_afInvalid; + delete m_afInvalidDamage; +} + +bool UpdateMap::Init(Size *psiz) +{ + m_rcDib.Set(0, 0, psiz->cx, psiz->cy); + m_ctx = (psiz->cx + (gcxTile - 1)) / gcxTile + 1; + m_cty = (psiz->cy + (gcyTile - 1)) / gcyTile + 1; + int cb = m_ctx * m_cty; + delete m_afInvalid; + delete m_afInvalidDamage; + m_afInvalid = new bool[cb]; + if (m_afInvalid == NULL) + return false; + memset(m_afInvalid, 0, cb); + m_afInvalidDamage = new bool[cb]; + if (m_afInvalidDamage == NULL) + return false; + SetMapOffset(0, 0, false); + return true; +} + +void UpdateMap::Reset() +{ + SetMapOffset(0, 0, true); + m_txOrigin = 0; + m_tyOrigin = 0; +} + +bool UpdateMap::IsInvalid() +{ + return m_fInvalid; +} + +bool UpdateMap::EnumUpdateRects(bool fFirst, Rect *prcBounds, Rect *prc) +{ + static Rect s_rcEnumLast; + static bool s_fDone; + + // Enum row rects two at a time. If the 2nd one can be coalesced + // vertically with the last one, do it, otherwise return them as is + + if (fFirst) { + if (!EnumRowRects(fFirst, prcBounds, &s_rcEnumLast)) + return false; + fFirst = false; + s_fDone = false; + } else { + // Not first; done from last time? + + if (s_fDone) + return false; + } + + Rect rcT; + while (true) { + if (!EnumRowRects(fFirst, prcBounds, &rcT)) { + s_fDone = true; + *prc = s_rcEnumLast; + return true; + } + + // If this is a clean union, then do it to join rows + + if (rcT.left == s_rcEnumLast.left && rcT.right == s_rcEnumLast.right && rcT.top == s_rcEnumLast.bottom) { + s_rcEnumLast.bottom = rcT.bottom; + continue; + } + + // Not a clean union - return the last one and save the current one + + *prc = s_rcEnumLast; + s_rcEnumLast = rcT; + return true; + } +} + +bool UpdateMap::EnumRowRects(bool fFirst, Rect *prcBounds, Rect *prc) { + // If first, reset + + static int s_tyEnumNext = 0; + static int s_txEnumNext = 0; + static int s_txLeftBounds = 0; + static int s_tyTopBounds = 0; + static int s_txRightBounds = 0; + static int s_tyBottomBounds = 0; + static int s_cbInvalidReturn = 0; + + if (fFirst) { + if (prcBounds == NULL) { + s_txLeftBounds = 0; + s_tyTopBounds = 0; + s_txRightBounds = m_ctx; + s_tyBottomBounds = m_cty; + s_cbInvalidReturn = 0; + } else { + s_txLeftBounds = TcFromPc(prcBounds->left + m_xMapOffset); + if (s_txLeftBounds < 0) + s_txLeftBounds = 0; + s_tyTopBounds = TcFromPc(prcBounds->top + m_yMapOffset); + if (s_tyTopBounds < 0) + s_tyTopBounds = 0; + s_txRightBounds = TcFromPc(prcBounds->right + m_xMapOffset + gcxTile - 1); + if (s_txRightBounds > m_ctx) + s_txRightBounds = m_ctx; + s_tyBottomBounds = TcFromPc(prcBounds->bottom + m_yMapOffset + gcyTile - 1); + if (s_tyBottomBounds > m_cty) + s_tyBottomBounds = m_cty; + s_cbInvalidReturn = m_ctx - (s_txRightBounds - s_txLeftBounds); + } + s_txEnumNext = s_txLeftBounds; + s_tyEnumNext = s_tyTopBounds; + } + int txT = s_txEnumNext; + int tyT = s_tyEnumNext; + bool *pfInvalid = &m_afInvalid[tyT * m_ctx + txT]; + + // Try to coalesce into larger rectangles + + for (; tyT < s_tyBottomBounds; tyT++) { + int txStart = -1; + int txEnd = -1; + for (; txT < s_txRightBounds; txT++) { + if (*pfInvalid) { + // Invalid tile. Start or extend an invalid rect? + + if (txStart == -1) + txStart = txT; + txEnd = txT + 1; + } else { + // Valid tile. Return accumulated invalid rect if it exists + + if (txStart != -1) { + s_txEnumNext = txT + 1; + s_tyEnumNext = tyT; + goto ReturnRect; + } + } + pfInvalid++; + } + s_txEnumNext = s_txLeftBounds; + s_tyEnumNext = tyT + 1; + pfInvalid += s_cbInvalidReturn; + txT = s_txLeftBounds; + + // End of the row. Return accumulated rect if it exists + + if (txStart != -1) { +ReturnRect: + prc->left = txStart * gcxTile; + prc->top = tyT * gcyTile; + prc->right = prc->left + (txEnd - txStart) * gcxTile; + prc->bottom = prc->top + gcyTile; + prc->Offset(-m_xMapOffset, -m_yMapOffset); + if (prcBounds == NULL) { + prc->Intersect(prc, &m_rcDib); + } else { + prc->Intersect(prc, prcBounds); + } + return true; + } + } + + // All done + + return false; +} + +// OPT: Assembly candidate + +void UpdateMap::InvalidateTileRect(TRectSmall *ptrc) +{ + Assert(ptrc->txLeft >= 0 && ptrc->tyTop >= 0); + Assert(ptrc->txRight <= m_ctx && ptrc->tyBottom <= m_cty); + + bool *pfInvalid = &m_afInvalid[ptrc->tyTop * m_ctx + ptrc->txLeft]; + int cbNext = m_ctx - (ptrc->txRight - ptrc->txLeft); + for (int tyT = ptrc->tyTop; tyT < ptrc->tyBottom; tyT++) { + for (int txT = ptrc->txLeft; txT < ptrc->txRight; txT++) { + if (*pfInvalid == false) { + m_fInvalid = true; + (*pfInvalid) = true; + } + pfInvalid++; + } + pfInvalid += cbNext; + } +} + +bool UpdateMap::IsTileRectInvalid(TRectSmall *ptrc) +{ + Assert(ptrc->txLeft >= 0 && ptrc->tyTop >= 0); + Assert(ptrc->txRight <= m_ctx && ptrc->tyBottom <= m_cty); + + bool *pfInvalid = &m_afInvalid[ptrc->tyTop * m_ctx + ptrc->txLeft]; + int cbNext = m_ctx - (ptrc->txRight - ptrc->txLeft); + for (int tyT = ptrc->tyTop; tyT < ptrc->tyBottom; tyT++) { + for (int txT = ptrc->txLeft; txT < ptrc->txRight; txT++) { + if (*pfInvalid) + return true; + pfInvalid++; + } + pfInvalid += cbNext; + } + return false; +} + +bool UpdateMap::IsTileRectInvalidAndTrackDamage(TRectSmall *ptrc, bool *pfNewInvalid) +{ + Assert(ptrc->txLeft >= 0 && ptrc->tyTop >= 0); + Assert(ptrc->txRight <= m_ctx && ptrc->tyBottom <= m_cty); + + bool *pfInvalid = &m_afInvalid[ptrc->tyTop * m_ctx + ptrc->txLeft]; + int cbNext = m_ctx - (ptrc->txRight - ptrc->txLeft); + + // See if any part of trc is touching invalid area + + bool fAnyInvalid = false; + bool *pfInvalidT = pfInvalid; + int tyT; + for (tyT = ptrc->tyTop; tyT < ptrc->tyBottom; tyT++) { + for (int txT = ptrc->txLeft; txT < ptrc->txRight; txT++) { + if (*pfInvalidT) { + fAnyInvalid = true; + break; + } + pfInvalidT++; + } + if (fAnyInvalid) + break; + pfInvalidT += cbNext; + } + + // If not, don't invalidate the whole thing + + if (!fAnyInvalid) + return false; + + // If merging damage, don't add to the damage map + + if (m_fMergeDamage) { + // Some part is touching invalid area, invalidate the whole thing + + bool fNewInvalid = false; + pfInvalidT = pfInvalid; + for (tyT = ptrc->tyTop; tyT < ptrc->tyBottom; tyT++) { + for (int txT = ptrc->txLeft; txT < ptrc->txRight; txT++) { + if (*pfInvalidT == false) { + (*pfInvalidT) = true; + fNewInvalid = true; + } + pfInvalidT++; + } + pfInvalidT += cbNext; + } + + // Accumulate whether there was "new" invalidation + + *pfNewInvalid |= fNewInvalid; + } else { + // Something is invalid, mark the damage map for the portions that will be + // damaged + + + bool *pfInvalidDamage = &m_afInvalidDamage[pfInvalid - m_afInvalid]; + for (tyT = ptrc->tyTop; tyT < ptrc->tyBottom; tyT++) { + for (int txT = ptrc->txLeft; txT < ptrc->txRight; txT++) { + *pfInvalidDamage = true; + m_fInvalidDamage = true; + pfInvalidDamage++; + } + pfInvalidDamage += cbNext; + } + } + + // Yes this ptrc was touching invalid area + + return true; +} + +bool UpdateMap::IsMapTileRectInvalidAndTrackDamage(TRectSmall *ptrc, bool *pfNewInvalid) +{ + // Convert from map tile coords to updatemap coords + + TRectSmall trc; + trc.txLeft = ptrc->txLeft - m_txOrigin; + trc.tyTop = ptrc->tyTop - m_tyOrigin; + trc.txRight = ptrc->txRight - m_txOrigin; + trc.tyBottom = ptrc->tyBottom - m_tyOrigin; + + // Clip to the update map + + if (trc.txLeft < 0) + trc.txLeft = 0; + if (trc.tyTop < 0) + trc.tyTop = 0; + if (trc.txRight > m_ctx) + trc.txRight = m_ctx; + if (trc.tyBottom > m_cty) + trc.tyBottom = m_cty; + + return IsTileRectInvalidAndTrackDamage(&trc, pfNewInvalid); +} + +void UpdateMap::Validate() +{ + memset(m_afInvalid, 0, m_ctx * m_cty); + m_fInvalid = false; +} + +void UpdateMap::CalcTileRect(Rect *prc, TRectSmall *ptrc) +{ +#if WIN + Assert(m_xOriginView == 0 || (m_xOriginView % gcxTile) == m_xMapOffset); + Assert(m_yOriginView == 0 || (m_yOriginView % gcyTile) == m_yMapOffset); +#endif + + int x = prc->left + m_xMapOffset; + if (x >= kpcMax) + x = kpcMax - 1; + if (x < 0) + x = 0; + ptrc->txLeft = (char)TcFromPc(x); + int y = prc->top + m_yMapOffset; + if (y >= kpcMax) + y = kpcMax - 1; + if (y < 0) + y = 0; + ptrc->tyTop = (char)TcFromPc(y); + + // Preclip right/bottom to the coordinate range supported + // by TcFromPc. + + int xT = x + (prc->right - prc->left) + (gcxTile - 1); + if (xT >= kpcMax) + xT = kpcMax - 1; + int yT = y + (prc->bottom - prc->top) + (gcyTile - 1); + if (yT >= kpcMax) + yT = kpcMax - 1; + + ptrc->txRight = (char)TcFromPc(xT); + if (ptrc->txRight > m_ctx) + ptrc->txRight = m_ctx; + + ptrc->tyBottom = (char)TcFromPc(yT); + if (ptrc->tyBottom > m_cty) + ptrc->tyBottom = m_cty; +} + +void UpdateMap::InvalidateRect(Rect *prc) +{ + Rect rcT; + if (prc == NULL) { + rcT.Set(0, 0, m_ctx * gcxTile, m_cty * gcyTile); + prc = &rcT; + } + + TRectSmall trc; + CalcTileRect(prc, &trc); + InvalidateTileRect(&trc); +} + +void UpdateMap::InvalidateMapTileRect(TRectSmall *ptrc) +{ + // Convert from map tile coords to updatemap coords + + TRectSmall trc; + trc.txLeft = ptrc->txLeft - m_txOrigin; + trc.tyTop = ptrc->tyTop - m_tyOrigin; + trc.txRight = ptrc->txRight - m_txOrigin; + trc.tyBottom = ptrc->tyBottom - m_tyOrigin; + + // Clip to the update map + + if (trc.txLeft < 0) { + if (trc.txRight <= 0) + return; + trc.txLeft = 0; + } + if (trc.tyTop < 0) { + if (trc.tyBottom <= 0) + return; + trc.tyTop = 0; + } + if (trc.txRight > m_ctx) { + if (trc.txLeft >= m_ctx) + return; + trc.txRight = m_ctx; + } + if (trc.tyBottom > m_cty) { + if (trc.tyTop >= m_cty) + return; + trc.tyBottom = m_cty; + } + + // Invalidate update map + + InvalidateTileRect(&trc); +} + +void UpdateMap::InvalidateTile(TCoord txMap, TCoord tyMap) +{ + // Convert from map tile coords to updatemap coords + + int tx = txMap - m_txOrigin; + int ty = tyMap - m_tyOrigin; + if (tx < 0 || tx >= m_ctx) + return; + if (ty < 0 || ty >= m_cty) + return; + + bool *pfInvalid = &m_afInvalid[ty * m_ctx + tx]; + if (*pfInvalid) + return; + + // Invalidate + + *pfInvalid = true; + m_fInvalid = true; +} + +void UpdateMap::StartMergeDamagedInvalid() +{ + byte *pbDst = (byte *)m_afInvalid; + byte *pbSrc = (byte *)m_afInvalidDamage; + int cb = m_ctx * m_cty; + while (cb-- != 0) + *pbDst++ |= *pbSrc++; + m_fMergeDamage = true; +} + +void UpdateMap::EndMergeDamagedInvalid() +{ + memset(m_afInvalidDamage, 0, m_ctx * m_cty); + m_fInvalidDamage = false; + m_fMergeDamage = false; +} + +void UpdateMap::SetViewOrigin(int xOrigin, int yOrigin) +{ + m_xOriginView = xOrigin; + m_yOriginView = yOrigin; +} + +void UpdateMap::SetMapOffset(int xMapOffset, int yMapOffset, bool fInvalidate) +{ + m_xMapOffset = xMapOffset; + m_yMapOffset = yMapOffset; + int cxDib = m_rcDib.Width(); + int cyDib = m_rcDib.Height(); + m_mnfo.cxLeftTile = PcFracFromUpc(gcxTile - m_xMapOffset); + if (cyDib < gcyTile - m_yMapOffset) { + m_mnfo.cyTopTile = cyDib; + } else { + m_mnfo.cyTopTile = PcFracFromUpc(gcyTile - m_yMapOffset); + } + m_mnfo.cxRightTile = PcFracFromUpc(cxDib - m_mnfo.cxLeftTile); + m_mnfo.cyBottomTile = PcFracFromUpc(cyDib - m_mnfo.cyTopTile); + m_mnfo.ctxInside = TcFromPc(cxDib - m_mnfo.cxLeftTile - m_mnfo.cxRightTile); + m_mnfo.ctyInside = TcFromPc(cyDib - m_mnfo.cyTopTile - m_mnfo.cyBottomTile); + if (fInvalidate) + InvalidateRect(); +} + +MapInfo *UpdateMap::GetMapInfo() +{ + return &m_mnfo; +} + +bool UpdateMap::Scroll(int dx, int dy) +{ + // If no scroll, nothing to do + + if (dx == 0 && dy == 0) + return false; + + // Calc new sub-tile map offsets + + int xMapOffsetNew, yMapOffsetNew; + if (dx <= 0) { + xMapOffsetNew = PcFracFromUpc(m_xMapOffset - dx); + } else { + xMapOffsetNew = PcFracFromUpc(gcxTile - PcFracFromUpc(gcxTile - m_xMapOffset + dx)); + } + if (dy <= 0) { + yMapOffsetNew = PcFracFromUpc(m_yMapOffset - dy); + } else { + yMapOffsetNew = PcFracFromUpc(gcyTile - PcFracFromUpc(gcyTile - m_yMapOffset + dy)); + } + + // Figure out the number of whole tiles to scroll + + int dtx; + if (dx <= 0) { + dtx = -TcFromPc(m_xMapOffset - dx); + } else { + dtx = TcFromPc((gcxTile - 1) - m_xMapOffset + dx); + } + + int dty; + if (dy <= 0) { + dty = -TcFromPc(m_yMapOffset - dy); + } else { + dty = TcFromPc((gcyTile - 1) - m_yMapOffset + dy); + } + + // Adjust tile origin + + m_txOrigin -= dtx; + m_tyOrigin -= dty; + + // Scroll the map if needed. If we don't actually need to scroll the map, + // we still need to cause invalidation along the edges + + if (dtx != 0 || dty != 0) { + // Need to scroll the map. Figure out the scrolling rect + + Rect rcSrc; + rcSrc.Set(0, 0, m_ctx, m_cty); + rcSrc.Offset(-dtx, -dty); + + // Clip + + if (rcSrc.left < 0) + rcSrc.left = 0; + if (rcSrc.top < 0) + rcSrc.top = 0; + if (rcSrc.right > m_ctx) + rcSrc.right = m_ctx; + if (rcSrc.bottom > m_cty) + rcSrc.bottom = m_cty; + + // Figure out dst + + int txDst = dtx; + if (txDst < 0) + txDst = 0; + int tyDst = dty; + if (tyDst < 0) + tyDst = 0; + + // Don't need to worry about copy direction since we're using a temp buffer + + int cb = m_ctx * m_cty; + bool *pfT = new bool[cb]; + if (pfT != NULL) { + // Scroll the invalid map + + memcpy(pfT, m_afInvalid, cb); + memset(m_afInvalid, 1, cb); + if (txDst < m_ctx && tyDst < m_cty) { + bool *pfSrc = &pfT[rcSrc.top * m_ctx + rcSrc.left]; + bool *pfDst = &m_afInvalid[tyDst * m_ctx + txDst]; + int cbNext = m_ctx - rcSrc.Width(); + int ctx = rcSrc.Width(); + int cty = rcSrc.Height(); + for (int ty = 0; ty < cty; ty++) { + for (int tx = 0; tx < ctx; tx++) { + *pfDst++ = *pfSrc++; + } + pfSrc += cbNext; + pfDst += cbNext; + } + } + + // Scroll the damage map + + memcpy(pfT, m_afInvalidDamage, cb); + memset(m_afInvalidDamage, 0, cb); + if (txDst < m_ctx && tyDst < m_cty) { + bool *pfSrc = &pfT[rcSrc.top * m_ctx + rcSrc.left]; + bool *pfDst = &m_afInvalidDamage[tyDst * m_ctx + txDst]; + int cbNext = m_ctx - rcSrc.Width(); + int ctx = rcSrc.Width(); + int cty = rcSrc.Height(); + for (int ty = 0; ty < cty; ty++) { + for (int tx = 0; tx < ctx; tx++) { + *pfDst++ = *pfSrc++; + } + pfSrc += cbNext; + pfDst += cbNext; + } + } + + // Now scrolled. Cleanup + + delete pfT; + } else { + // Couldn't alloc temp buffer, so invalidate everything + + memset(m_afInvalid, 1, cb); + memset(m_afInvalidDamage, 0, cb); + } + } + + // Update the new map offsets before edge invalidation + + SetMapOffset(xMapOffsetNew, yMapOffsetNew, false); + + // Invalidate edges + + Rect rcNew; + if (dx != 0) { + rcNew = m_rcDib; + rcNew.Offset(dx, 0); + rcNew.Intersect(&rcNew, &m_rcDib); + if (rcNew.Subtract(&m_rcDib, &rcNew)) + InvalidateRect(&rcNew); + } + if (dy != 0) { + rcNew = m_rcDib; + rcNew.Offset(0, dy); + rcNew.Intersect(&rcNew, &m_rcDib); + if (rcNew.Subtract(&m_rcDib, &rcNew)) + InvalidateRect(&rcNew); + } + +// Trace("xMapOffset: %d, yMapOffset: %d", m_xMapOffset, m_yMapOffset); + + // Return true to cause bits to be scrolled + + return true; +} + +bool *UpdateMap::GetInvalidMap() +{ + return m_afInvalid; +} + +void UpdateMap::GetMapSize(Size *psiz) +{ + psiz->cx = m_ctx; + psiz->cy = m_cty; +} + +} // namespace wi diff --git a/game/uploader.cpp b/game/uploader.cpp new file mode 100644 index 0000000..5d52e23 --- /dev/null +++ b/game/uploader.cpp @@ -0,0 +1,129 @@ +#include "game/ht.h" +#include "game/uploader.h" +#include "base/thread.h" + +namespace wi { + +bool UploadByteBuffer(HttpService *service, base::ByteBuffer *bb, + const char *url) { + Uploader *uploader = new Uploader(service); + if (uploader == NULL) { + delete bb; + return false; + } + if (!uploader->Upload(bb, url)) { + delete uploader; + return false; + } + delete uploader; + return true; +} + +Uploader::Uploader(HttpService *service) : service_(service), req_(NULL), + done_(false), error_(false), ui_(NULL), cbLength_(0), cbTotal_(0) { +} + +Uploader::~Uploader() { + if (req_ != NULL) { + service_->ReleaseRequest(req_); + req_ = NULL; + } + delete ui_; +} + +bool Uploader::Upload(base::ByteBuffer *bb, const char *url) { + req_ = service_->NewRequest(this); + if (req_ == NULL) { + delete bb; + return false; + } + req_->SetURL(url); + req_->SetBody(bb); + req_->SetMethod("POST"); + service_->SubmitRequest(req_); + + SetMessage("Uploading game state..."); + + // Sit in a modal loop until the upload either succeeds or fails + + while (!done_ && !error_) { + Event evt; + if (!gevm.GetEvent(&evt)) { + continue; + } + if (ggame.FilterEvent(&evt)) { + continue; + } + gevm.DispatchEvent(&evt); + } + + service_->ReleaseRequest(req_); + req_ = NULL; + + if (errorstr_.size() != 0) { + HtMessageBox(kfMbWhiteBorder, "Upload Error", errorstr_.c_str()); + } + + return !error_; +} + +void Uploader::SetMessage(const char *message) { + delete ui_; + message_ = message; + ui_ = new TransportWaitingUI((char *)message_.c_str()); +} + +void Uploader::Wakeup() { + // Wakes up the modal loop + + base::Thread::current().Post(base::kidmNullEvent, NULL); +} + +void Uploader::OnReceivedResponse(HttpRequest *preq, int code, + const Map *pheaders) { + if (code >= 400) { + errorstr_ = base::Format::ToString("error code %d", code); + error_ = true; + Wakeup(); + return; + } + if (code >= 200 && code < 300) { + // Success! Get Content-Length and call back + + cbLength_ = -1; + char szLength[32]; + if (pheaders->GetValue("Content-Length", szLength, + sizeof(szLength))) { + base::Format::ToInteger(szLength, 10, &cbLength_); + } + cbTotal_ = 0; + SetMessage(base::Format::ToString("Uploaded %d of %d bytes...", + cbTotal_, cbLength_)); + return; + } + // Ignore other status codes. If it's a redirect, OnReceivedResponse + // will get called again. + return; +} + +void Uploader::OnReceivedData(HttpRequest *preq, const base::ByteBuffer *pbb) { + if (error_) { + return; + } +} + +void Uploader::OnFinishedLoading(HttpRequest *preq) { + if (error_) { + return; + } + done_ = true; + Wakeup(); +} + +void Uploader::OnError(HttpRequest *preq, const char *pszError) { + errorstr_ = pszError; + error_ = true; + Wakeup(); +} + +} diff --git a/game/uploader.h b/game/uploader.h new file mode 100644 index 0000000..5d18d18 --- /dev/null +++ b/game/uploader.h @@ -0,0 +1,50 @@ +#ifndef __UPLOADER_H__ +#define __UPLOADER_H__ + +#include "inc/basictypes.h" +#include "game/httpservice.h" +#include "game/httprequest.h" +#include "game/map.h" +#include "base/bytebuffer.h" +#include + +namespace wi { + +bool UploadByteBuffer(HttpService *service, base::ByteBuffer *bb, + const char *url); + +class TransportWaitingUI; + +class Uploader : HttpResponseHandler { +public: + Uploader(HttpService *service); + ~Uploader(); + + bool Upload(base::ByteBuffer *bb, const char *url); + +private: + void SetMessage(const char *message); + void Wakeup(); + + // HttpResponseHandler + virtual void OnReceivedResponse(HttpRequest *preq, int code, + const Map *pheaders); + virtual void OnReceivedData(HttpRequest *preq, + const base::ByteBuffer *pbb); + virtual void OnFinishedLoading(HttpRequest *preq); + virtual void OnError(HttpRequest *preq, const char *pszError); + + TransportWaitingUI *ui_; + std::string message_; + std::string errorstr_; + HttpService *service_; + HttpRequest *req_; + int cbLength_; + int cbTotal_; + bool done_; + bool error_; +}; + +} + +#endif // __UPLOADER_H__ diff --git a/game/vec2d.h b/game/vec2d.h new file mode 100644 index 0000000..b9219a9 --- /dev/null +++ b/game/vec2d.h @@ -0,0 +1,81 @@ +#ifndef __VEC2D_H__ +#define __VEC2D_H__ + +#include + +namespace wi { + +struct DPoint { + double x; + double y; +}; + +class Vec2d { +public: + Vec2d() { + dx = 0; + dy = 0; + } + + Vec2d(const DPoint& pt0, const DPoint& pt1) { + dx = pt1.x - pt0.x; + dy = pt1.y - pt0.y; + } + + Vec2d(double dx, double dy) { + this->dx = dx; + this->dy = dy; + } + + Vec2d(const Vec2d& v) { + dx = v.dx; + dy = v.dy; + } + + Vec2d unit() const { + double magT = mag(); + return Vec2d(dx / magT, dy / magT); + } + + double mag() const { + return sqrt(dx * dx + dy * dy); + } + + Vec2d scale(double amount) const { + return Vec2d(dx * amount, dy * amount); + } + + DPoint add(const DPoint& pt) const { + DPoint ptT; + ptT.x = pt.x + dx; + ptT.y = pt.y + dy; + return ptT; + } + + double dot(const Vec2d& v) const { + return dx * v.dx + dy * v.dy; + } + + double project(const Vec2d& v) const { + return unit().dot(v); + } + + Vec2d flip90() const { + return Vec2d(-dy, dx); + } + + Vec2d flip180() const { + return Vec2d(-dx, -dy); + } + + Vec2d flip270() const { + return Vec2d(dy, -dx); + } + + double dx; + double dy; +}; + +} // namespace wi + +#endif // __VEC2D_H__ diff --git a/game/wi.xcodeproj/project.pbxproj b/game/wi.xcodeproj/project.pbxproj new file mode 100755 index 0000000..1b542ca --- /dev/null +++ b/game/wi.xcodeproj/project.pbxproj @@ -0,0 +1,1488 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 1D3623EC0D0F72F000981E51 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D3623EB0D0F72F000981E51 /* CoreGraphics.framework */; }; + 1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D30AB110D05D00D00671497 /* Foundation.framework */; }; + 1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */; }; + 5500324318B9936800466FDF /* xtransport.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5500324118B9936800466FDF /* xtransport.cpp */; }; + C10777AD0E1D9FE5003DA7D6 /* messages.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C10777AB0E1D9FE5003DA7D6 /* messages.cpp */; }; + C1077A1C0E31C4B5003DA7D6 /* decompress.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1077A130E31C4B5003DA7D6 /* decompress.cpp */; }; + C1077A1D0E31C4B5003DA7D6 /* ini.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1077A160E31C4B5003DA7D6 /* ini.cpp */; }; + C1077A1E0E31C4B5003DA7D6 /* packfile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1077A180E31C4B5003DA7D6 /* packfile.cpp */; }; + C1077A210E31C57F003DA7D6 /* iphonepackfile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1077A1F0E31C57F003DA7D6 /* iphonepackfile.cpp */; }; + C1077A340E31CAA3003DA7D6 /* mempdbreader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1077A320E31CAA3003DA7D6 /* mempdbreader.cpp */; }; + C1077A510E330836003DA7D6 /* md5c.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1077A500E330836003DA7D6 /* md5c.cpp */; }; + C1077AA40E36A74C003DA7D6 /* tick.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1077AA20E36A74C003DA7D6 /* tick.cpp */; }; + C1077CAB0E3E6F5F003DA7D6 /* deletetracker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1077CA90E3E6F5F003DA7D6 /* deletetracker.cpp */; }; + C11FDC691033682E0053CD83 /* eventer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C11FDC651033682E0053CD83 /* eventer.cpp */; }; + C11FDC6A1033682E0053CD83 /* selectserver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C11FDC671033682E0053CD83 /* selectserver.cpp */; }; + C12429A00E553D9500DDDFD1 /* stylushandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C124299F0E553D9500DDDFD1 /* stylushandler.cpp */; }; + C12429F10E5727BF00DDDFD1 /* fingerhandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C12429F00E5727BF00DDDFD1 /* fingerhandler.cpp */; }; + C12D9D2E0ECA3F94006B5FD9 /* completemanager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C12D9D2C0ECA3F94006B5FD9 /* completemanager.cpp */; }; + C12DA1FB0ECE7A9E006B5FD9 /* xmsglog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C12DA1F90ECE7A9E006B5FD9 /* xmsglog.cpp */; }; + C13709550E6D9E3E00F9F923 /* httprequest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C13709510E6D9E3E00F9F923 /* httprequest.cpp */; }; + C137095A0E6D9E6700F9F923 /* iphonehttprequest.mm in Sources */ = {isa = PBXBuildFile; fileRef = C13709570E6D9E6700F9F923 /* iphonehttprequest.mm */; }; + C137095B0E6D9E6700F9F923 /* iphonehttpservice.mm in Sources */ = {isa = PBXBuildFile; fileRef = C13709590E6D9E6700F9F923 /* iphonehttpservice.mm */; }; + C1370A0A0E707C1E00F9F923 /* jsonbuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1370A060E707C1E00F9F923 /* jsonbuilder.cpp */; }; + C1370A0B0E707C1E00F9F923 /* jsontypes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1370A080E707C1E00F9F923 /* jsontypes.cpp */; }; + C1370A150E707C5700F9F923 /* yajl.c in Sources */ = {isa = PBXBuildFile; fileRef = C1370A0C0E707C5700F9F923 /* yajl.c */; }; + C1370A160E707C5700F9F923 /* yajl_buf.c in Sources */ = {isa = PBXBuildFile; fileRef = C1370A0D0E707C5700F9F923 /* yajl_buf.c */; }; + C1370A170E707C5700F9F923 /* yajl_encode.c in Sources */ = {isa = PBXBuildFile; fileRef = C1370A0F0E707C5700F9F923 /* yajl_encode.c */; }; + C1370A180E707C5700F9F923 /* yajl_lex.c in Sources */ = {isa = PBXBuildFile; fileRef = C1370A110E707C5700F9F923 /* yajl_lex.c */; }; + C1370A190E707C5700F9F923 /* yajl_parser.c in Sources */ = {isa = PBXBuildFile; fileRef = C1370A130E707C5700F9F923 /* yajl_parser.c */; }; + C1370A300E70831E00F9F923 /* map.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1370A2E0E70831E00F9F923 /* map.cpp */; }; + C1370AA50E7099A200F9F923 /* format.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1370AA40E7099A200F9F923 /* format.cpp */; }; + C1370AE70E719A1400F9F923 /* dlmissionpack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1370AE60E719A1400F9F923 /* dlmissionpack.cpp */; }; + C1370C1B0E71CC7600F9F923 /* serviceurls.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1370C190E71CC7600F9F923 /* serviceurls.cpp */; }; + C139CFA40E8B352A005F4A4B /* drawscan.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C139CFA30E8B352A005F4A4B /* drawscan.cpp */; }; + C13D85BC0F07580200E63C5D /* stateframe.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C13D85B80F07580200E63C5D /* stateframe.cpp */; }; + C13D85BD0F07580200E63C5D /* statetracker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C13D85BA0F07580200E63C5D /* statetracker.cpp */; }; + C13D87C90F0AF5A300E63C5D /* uploader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C13D87C70F0AF5A300E63C5D /* uploader.cpp */; }; + C13F7C5C0FF5CAAD00F77047 /* webviewcontroller.mm in Sources */ = {isa = PBXBuildFile; fileRef = C13F7C5A0FF5CAAD00F77047 /* webviewcontroller.mm */; }; + C13F80A40FFE811E00F77047 /* misc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C13F80A30FFE811E00F77047 /* misc.cpp */; }; + C1456694101A9EF0006DCF81 /* inputcontroller.mm in Sources */ = {isa = PBXBuildFile; fileRef = C1456692101A9EF0006DCF81 /* inputcontroller.mm */; }; + C14753000EB6903A0059D367 /* selectionsprite.mm in Sources */ = {isa = PBXBuildFile; fileRef = C14752FE0EB6903A0059D367 /* selectionsprite.mm */; }; + C14753040EB6908B0059D367 /* dragrect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C14753010EB6908B0059D367 /* dragrect.cpp */; }; + C1603E060F6984CB00B19D51 /* wiviewcontroller.mm in Sources */ = {isa = PBXBuildFile; fileRef = C1603E050F6984CB00B19D51 /* wiviewcontroller.mm */; }; + C1603E200F69AAB200B19D51 /* chatviewcontroller.mm in Sources */ = {isa = PBXBuildFile; fileRef = C1603E1E0F69AAB200B19D51 /* chatviewcontroller.mm */; }; + C166A8CA0DE5F13B000B45A0 /* oglview2.mm in Sources */ = {isa = PBXBuildFile; fileRef = C166A8C90DE5F13B000B45A0 /* oglview2.mm */; }; + C166A8CD0DE62EF4000B45A0 /* ogldib.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C166A8CC0DE62EF4000B45A0 /* ogldib.cpp */; }; + C166A8CF0DE62F20000B45A0 /* cgdib.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C166A8CE0DE62F20000B45A0 /* cgdib.cpp */; }; + C175F5C60DC0465000985493 /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C175F5C50DC0465000985493 /* OpenGLES.framework */; }; + C176856A0E0C35820058E49D /* messagequeue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17685610E0C35820058E49D /* messagequeue.cpp */; }; + C176856B0E0C35820058E49D /* socket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17685630E0C35820058E49D /* socket.cpp */; }; + C176856C0E0C35820058E49D /* socketaddress.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17685650E0C35820058E49D /* socketaddress.cpp */; }; + C176856D0E0C35820058E49D /* socketserver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17685670E0C35820058E49D /* socketserver.cpp */; }; + C176856F0E0C35A60058E49D /* bytebuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C176856E0E0C35A60058E49D /* bytebuffer.cpp */; }; + C17685770E0C36100058E49D /* misc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17685700E0C36100058E49D /* misc.cpp */; }; + C17685780E0C36100058E49D /* netmessage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17685730E0C36100058E49D /* netmessage.cpp */; }; + C17685B80E15CB180058E49D /* xpump.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17685B60E15CB180058E49D /* xpump.cpp */; }; + C178D3F90EABD5AF0015E32D /* icon.png in Resources */ = {isa = PBXBuildFile; fileRef = C178D3F80EABD5AF0015E32D /* icon.png */; }; + C17D66090D9E15A300CB9F01 /* alertcontrol.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66080D9E15A300CB9F01 /* alertcontrol.cpp */; }; + C17D665E0D9E15BE00CB9F01 /* Andy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D660A0D9E15BE00CB9F01 /* Andy.cpp */; }; + C17D665F0D9E15BE00CB9F01 /* Animation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D660B0D9E15BE00CB9F01 /* Animation.cpp */; }; + C17D66600D9E15BE00CB9F01 /* Artillery.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D660C0D9E15BE00CB9F01 /* Artillery.cpp */; }; + C17D66610D9E15BE00CB9F01 /* bitmap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D660E0D9E15BE00CB9F01 /* bitmap.cpp */; }; + C17D66620D9E15BE00CB9F01 /* Builder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D660F0D9E15BE00CB9F01 /* Builder.cpp */; }; + C17D66630D9E15BE00CB9F01 /* BuildMgr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66100D9E15BE00CB9F01 /* BuildMgr.cpp */; }; + C17D66640D9E15BE00CB9F01 /* cachemgr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66110D9E15BE00CB9F01 /* cachemgr.cpp */; }; + C17D66660D9E15BE00CB9F01 /* compression.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66130D9E15BE00CB9F01 /* compression.cpp */; }; + C17D66670D9E15BE00CB9F01 /* CutScene.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66140D9E15BE00CB9F01 /* CutScene.cpp */; }; + C17D66680D9E15BE00CB9F01 /* drm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66150D9E15BE00CB9F01 /* drm.cpp */; }; + C17D66690D9E15BE00CB9F01 /* Ecom.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66160D9E15BE00CB9F01 /* Ecom.cpp */; }; + C17D666A0D9E15BE00CB9F01 /* event.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66170D9E15BE00CB9F01 /* event.cpp */; }; + C17D666B0D9E15BE00CB9F01 /* fogmap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66180D9E15BE00CB9F01 /* fogmap.cpp */; }; + C17D666C0D9E15BE00CB9F01 /* font.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66190D9E15BE00CB9F01 /* font.cpp */; }; + C17D666D0D9E15BE00CB9F01 /* form.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D661A0D9E15BE00CB9F01 /* form.cpp */; }; + C17D666E0D9E15BE00CB9F01 /* formmgr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D661B0D9E15BE00CB9F01 /* formmgr.cpp */; }; + C17D666F0D9E15BE00CB9F01 /* game.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D661C0D9E15BE00CB9F01 /* game.cpp */; }; + C17D66700D9E15BE00CB9F01 /* GameObjects.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D661D0D9E15BE00CB9F01 /* GameObjects.cpp */; }; + C17D66710D9E15BE00CB9F01 /* GameOptions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D661E0D9E15BE00CB9F01 /* GameOptions.cpp */; }; + C17D66720D9E15BE00CB9F01 /* Headquarters.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D661F0D9E15BE00CB9F01 /* Headquarters.cpp */; }; + C17D66730D9E15BE00CB9F01 /* Help.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66200D9E15BE00CB9F01 /* Help.cpp */; }; + C17D66740D9E15BE00CB9F01 /* HRC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66210D9E15BE00CB9F01 /* HRC.cpp */; }; + C17D66760D9E15BE00CB9F01 /* InputUI.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66240D9E15BE00CB9F01 /* InputUI.cpp */; }; + C17D66770D9E15BE00CB9F01 /* Level.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66250D9E15BE00CB9F01 /* Level.cpp */; }; + C17D66780D9E15BE00CB9F01 /* loadsave.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66260D9E15BE00CB9F01 /* loadsave.cpp */; }; + C17D66790D9E15BE00CB9F01 /* LRInfantry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66270D9E15BE00CB9F01 /* LRInfantry.cpp */; }; + C17D667A0D9E15BE00CB9F01 /* memmgr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66280D9E15BE00CB9F01 /* memmgr.cpp */; }; + C17D667B0D9E15BE00CB9F01 /* Miner.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66290D9E15BE00CB9F01 /* Miner.cpp */; }; + C17D667C0D9E15BE00CB9F01 /* misc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D662A0D9E15BE00CB9F01 /* misc.cpp */; }; + C17D667D0D9E15BE00CB9F01 /* misccontrols.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D662B0D9E15BE00CB9F01 /* misccontrols.cpp */; }; + C17D667F0D9E15BE00CB9F01 /* mixer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D662D0D9E15BE00CB9F01 /* mixer.cpp */; }; + C17D66800D9E15BE00CB9F01 /* MobileBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D662F0D9E15BE00CB9F01 /* MobileBuilder.cpp */; }; + C17D66810D9E15BE00CB9F01 /* MobileHQ.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66300D9E15BE00CB9F01 /* MobileHQ.cpp */; }; + C17D66820D9E15BE00CB9F01 /* MobileUnit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66310D9E15BE00CB9F01 /* MobileUnit.cpp */; }; + C17D66840D9E15BE00CB9F01 /* Overmind.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66340D9E15BE00CB9F01 /* Overmind.cpp */; }; + C17D66860D9E15BE00CB9F01 /* Player.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66370D9E15BE00CB9F01 /* Player.cpp */; }; + C17D66870D9E15BE00CB9F01 /* Processor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66380D9E15BE00CB9F01 /* Processor.cpp */; }; + C17D66880D9E15BE00CB9F01 /* Radar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66390D9E15BE00CB9F01 /* Radar.cpp */; }; + C17D66890D9E15BE00CB9F01 /* RawBitmap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D663A0D9E15BE00CB9F01 /* RawBitmap.cpp */; }; + C17D668A0D9E15BE00CB9F01 /* Reactor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D663B0D9E15BE00CB9F01 /* Reactor.cpp */; }; + C17D668B0D9E15BE00CB9F01 /* Replicator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D663C0D9E15BE00CB9F01 /* Replicator.cpp */; }; + C17D668C0D9E15BE00CB9F01 /* Research.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D663E0D9E15BE00CB9F01 /* Research.cpp */; }; + C17D668D0D9E15BE00CB9F01 /* rip.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D663F0D9E15BE00CB9F01 /* rip.cpp */; }; + C17D668E0D9E15BE00CB9F01 /* Shell.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66400D9E15BE00CB9F01 /* Shell.cpp */; }; + C17D668F0D9E15BE00CB9F01 /* SimUI.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66410D9E15BE00CB9F01 /* SimUI.cpp */; }; + C17D66900D9E15BE00CB9F01 /* Simulation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66420D9E15BE00CB9F01 /* Simulation.cpp */; }; + C17D66920D9E15BE00CB9F01 /* soundmgr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66470D9E15BE00CB9F01 /* soundmgr.cpp */; }; + C17D66930D9E15BE00CB9F01 /* SpInfantry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66480D9E15BE00CB9F01 /* SpInfantry.cpp */; }; + C17D66940D9E15BE00CB9F01 /* SRInfantry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66490D9E15BE00CB9F01 /* SRInfantry.cpp */; }; + C17D66950D9E15BE00CB9F01 /* StateMachine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D664A0D9E15BE00CB9F01 /* StateMachine.cpp */; }; + C17D66960D9E15BE00CB9F01 /* stringtable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D664C0D9E15BE00CB9F01 /* stringtable.cpp */; }; + C17D66970D9E15BE00CB9F01 /* Struct.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D664D0D9E15BE00CB9F01 /* Struct.cpp */; }; + C17D66980D9E15BE00CB9F01 /* Tank.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D664E0D9E15BE00CB9F01 /* Tank.cpp */; }; + C17D66990D9E15BE00CB9F01 /* tbitmap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D664F0D9E15BE00CB9F01 /* tbitmap.cpp */; }; + C17D669A0D9E15BE00CB9F01 /* terrainmap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66500D9E15BE00CB9F01 /* terrainmap.cpp */; }; + C17D669B0D9E15BE00CB9F01 /* tests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66510D9E15BE00CB9F01 /* tests.cpp */; }; + C17D669C0D9E15BE00CB9F01 /* thunks.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66520D9E15BE00CB9F01 /* thunks.cpp */; }; + C17D669D0D9E15BE00CB9F01 /* tilemap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66530D9E15BE00CB9F01 /* tilemap.cpp */; }; + C17D669E0D9E15BE00CB9F01 /* timer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66540D9E15BE00CB9F01 /* timer.cpp */; }; + C17D669F0D9E15BE00CB9F01 /* Tower.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66550D9E15BE00CB9F01 /* Tower.cpp */; }; + C17D66A00D9E15BE00CB9F01 /* TriggerActions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66560D9E15BE00CB9F01 /* TriggerActions.cpp */; }; + C17D66A10D9E15BE00CB9F01 /* TriggerConditions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66570D9E15BE00CB9F01 /* TriggerConditions.cpp */; }; + C17D66A20D9E15BE00CB9F01 /* triggermgr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66580D9E15BE00CB9F01 /* triggermgr.cpp */; }; + C17D66A30D9E15BE00CB9F01 /* Unit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66590D9E15BE00CB9F01 /* Unit.cpp */; }; + C17D66A40D9E15BE00CB9F01 /* UnitGroupMgr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D665A0D9E15BE00CB9F01 /* UnitGroupMgr.cpp */; }; + C17D66A50D9E15BE00CB9F01 /* updatemap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D665B0D9E15BE00CB9F01 /* updatemap.cpp */; }; + C17D66A60D9E15BE00CB9F01 /* VTS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D665C0D9E15BE00CB9F01 /* VTS.cpp */; }; + C17D66A70D9E15BE00CB9F01 /* Warehouse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D665D0D9E15BE00CB9F01 /* Warehouse.cpp */; }; + C17D66C00D9E160100CB9F01 /* display.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66AE0D9E160100CB9F01 /* display.cpp */; }; + C17D66C20D9E160100CB9F01 /* host.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66B10D9E160100CB9F01 /* host.cpp */; }; + C17D66C30D9E160100CB9F01 /* iphone.mm in Sources */ = {isa = PBXBuildFile; fileRef = C17D66B50D9E160100CB9F01 /* iphone.mm */; }; + C17D66C40D9E160100CB9F01 /* iphonesounddev.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66B60D9E160100CB9F01 /* iphonesounddev.cpp */; }; + C17D66C60D9E160100CB9F01 /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66B90D9E160100CB9F01 /* main.cpp */; }; + C17D66C80D9E160100CB9F01 /* savegame.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66BC0D9E160100CB9F01 /* savegame.cpp */; }; + C17D66CA0D9E160100CB9F01 /* transportmgr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66BF0D9E160100CB9F01 /* transportmgr.cpp */; }; + C17D66D00D9E162500CB9F01 /* htsfx.pdb in Resources */ = {isa = PBXBuildFile; fileRef = C17D66CC0D9E162500CB9F01 /* htsfx.pdb */; }; + C17D66E50D9E163900CB9F01 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C17D66E40D9E163900CB9F01 /* AudioToolbox.framework */; }; + C1949CF80E7874C200A654A1 /* downloadbox.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1949CF70E7874C200A654A1 /* downloadbox.cpp */; }; + C194A1050E7F8CF000A654A1 /* httpindexloader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C194A0FF0E7F8CF000A654A1 /* httpindexloader.cpp */; }; + C194A1060E7F8CF000A654A1 /* httppackinfomanager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C194A1010E7F8CF000A654A1 /* httppackinfomanager.cpp */; }; + C194A1070E7F8CF000A654A1 /* httppackmanager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C194A1030E7F8CF000A654A1 /* httppackmanager.cpp */; }; + C194A1C40E80300C00A654A1 /* indexloader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C194A1C00E80300C00A654A1 /* indexloader.cpp */; }; + C194A1C50E80300C00A654A1 /* packinfomanager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C194A1C20E80300C00A654A1 /* packinfomanager.cpp */; }; + C194A1C60E80300C00A654A1 /* packmanager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C194A1C30E80300C00A654A1 /* packmanager.cpp */; }; + C1994E2E0EA149D200A48088 /* iphoneanimsprite.mm in Sources */ = {isa = PBXBuildFile; fileRef = C1994E2B0EA149D200A48088 /* iphoneanimsprite.mm */; }; + C19D45100F745A9600283E95 /* chatcell.mm in Sources */ = {isa = PBXBuildFile; fileRef = C19D450F0F745A9600283E95 /* chatcell.mm */; }; + C1A111F60E416739007913B4 /* messagehandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1A111F40E416739007913B4 /* messagehandler.cpp */; }; + C1A1C2080FE9B2BD00042055 /* thread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1A1C2060FE9B2BD00042055 /* thread.cpp */; }; + C1ABDCFB0DC7C19E000A5B75 /* flickscroller.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1ABDCFA0DC7C19E000A5B75 /* flickscroller.cpp */; }; + C1B3F5360E65D6C100D36F66 /* missionlist.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1B3F5340E65D6C100D36F66 /* missionlist.cpp */; }; + C1B3F5EA0E671C0600D36F66 /* selectmission.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1B3F5E80E671C0600D36F66 /* selectmission.cpp */; }; + C1C0F2EE0F3D028F00DDA646 /* Multiplayer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C17D66320D9E15BE00CB9F01 /* Multiplayer.cpp */; }; + C1C7067C0DBC03130062B73B /* cgview.mm in Sources */ = {isa = PBXBuildFile; fileRef = C17D66B80D9E160100CB9F01 /* cgview.mm */; }; + C1C709740DBDAC470062B73B /* oglview.mm in Sources */ = {isa = PBXBuildFile; fileRef = C18E0E030DBAC70700096237 /* oglview.mm */; }; + C1C709770DBDAF290062B73B /* wiview.mm in Sources */ = {isa = PBXBuildFile; fileRef = C1C709760DBDAF290062B73B /* wiview.mm */; }; + C1D143980E45881F0025FA74 /* dist.plist in Resources */ = {isa = PBXBuildFile; fileRef = C1D143970E45881F0025FA74 /* dist.plist */; }; + C1D4E03F0F8FCBEA00D8C118 /* creategameform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D4E0370F8FCBEA00D8C118 /* creategameform.cpp */; }; + C1D4E0410F8FCBEA00D8C118 /* picktransportform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D4E03B0F8FCBEA00D8C118 /* picktransportform.cpp */; }; + C1D4E0420F8FCBEA00D8C118 /* roomform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D4E03D0F8FCBEA00D8C118 /* roomform.cpp */; }; + C1D4E05B0F98F01D00D8C118 /* lobbyform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D4E0550F98F01D00D8C118 /* lobbyform.cpp */; }; + C1D4E05C0F98F01D00D8C118 /* loginform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D4E0570F98F01D00D8C118 /* loginform.cpp */; }; + C1D4E05D0F98F01D00D8C118 /* loginhandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D4E0590F98F01D00D8C118 /* loginhandler.cpp */; }; + C1D4E07F0F98F4DC00D8C118 /* simplerequest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D4E07D0F98F4DC00D8C118 /* simplerequest.cpp */; }; + C1D4E0C80F9D220C00D8C118 /* base64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D4E0C60F9D220C00D8C118 /* base64.cpp */; }; + C1D4E0CD0F9F8A6600D8C118 /* lobby.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D4E0CB0F9F8A6600D8C118 /* lobby.cpp */; }; + C1D4E4AE0FA7B9AA00D8C118 /* createroomform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D4E4AC0FA7B9AA00D8C118 /* createroomform.cpp */; }; + C1D4E6860FABB4A500D8C118 /* gameform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D4E6850FABB4A500D8C118 /* gameform.cpp */; }; + C1D4E7DD0FAF90E500D8C118 /* comm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D4E7DC0FAF90E500D8C118 /* comm.cpp */; }; + C1DD17A60EC279A500D941E8 /* layerdib.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1DD179F0EC279A500D941E8 /* layerdib.cpp */; }; + C1DD17A80EC279A500D941E8 /* layerview.mm in Sources */ = {isa = PBXBuildFile; fileRef = C1DD17A50EC279A500D941E8 /* layerview.mm */; }; + C1DD18070EC3756A00D941E8 /* layermap.mm in Sources */ = {isa = PBXBuildFile; fileRef = C1DD18060EC3756A00D941E8 /* layermap.mm */; }; + C1DE48291029254C00DB7394 /* chooseserverform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1DE48271029254C00DB7394 /* chooseserverform.cpp */; }; + C1DF859F10026655007A26BA /* NavBackSmall.png in Resources */ = {isa = PBXBuildFile; fileRef = C1DF859D10026655007A26BA /* NavBackSmall.png */; }; + C1DF85A010026655007A26BA /* NavForwardSmall.png in Resources */ = {isa = PBXBuildFile; fileRef = C1DF859E10026655007A26BA /* NavForwardSmall.png */; }; + C1E3BC660FD998B100E258F3 /* chatter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1E3BC640FD998B100E258F3 /* chatter.cpp */; }; + C1E56FD40DBFE589005DE321 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C1E56FD30DBFE589005DE321 /* CoreAudio.framework */; }; + C1E570AC0DBFEAA7005DE321 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C1E570AB0DBFEAA7005DE321 /* QuartzCore.framework */; }; + C1EC87C30DDE4A7E00B595FD /* htdata832.pdb in Resources */ = {isa = PBXBuildFile; fileRef = C1EC87C20DDE4A7E00B595FD /* htdata832.pdb */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 1D30AB110D05D00D00671497 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 1D3623EB0D0F72F000981E51 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + 1D6058910D05DD3D006BFB54 /* wi.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = wi.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + 5500324118B9936800466FDF /* xtransport.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = xtransport.cpp; sourceTree = SOURCE_ROOT; }; + 5500324218B9936800466FDF /* xtransport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xtransport.h; sourceTree = SOURCE_ROOT; }; + 636035590F19AA7C00ABF8EB /* multiplayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = multiplayer.h; sourceTree = SOURCE_ROOT; }; + C10777AB0E1D9FE5003DA7D6 /* messages.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = messages.cpp; path = ../mpshared/messages.cpp; sourceTree = SOURCE_ROOT; }; + C10777AC0E1D9FE5003DA7D6 /* messages.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = messages.h; path = ../mpshared/messages.h; sourceTree = SOURCE_ROOT; }; + C1077A130E31C4B5003DA7D6 /* decompress.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = decompress.cpp; path = ../mpshared/decompress.cpp; sourceTree = SOURCE_ROOT; }; + C1077A140E31C4B5003DA7D6 /* decompress.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = decompress.h; path = ../mpshared/decompress.h; sourceTree = SOURCE_ROOT; }; + C1077A150E31C4B5003DA7D6 /* enum.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = enum.h; path = ../mpshared/enum.h; sourceTree = SOURCE_ROOT; }; + C1077A160E31C4B5003DA7D6 /* ini.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ini.cpp; path = ../mpshared/ini.cpp; sourceTree = SOURCE_ROOT; }; + C1077A170E31C4B5003DA7D6 /* ini.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ini.h; path = ../mpshared/ini.h; sourceTree = SOURCE_ROOT; }; + C1077A180E31C4B5003DA7D6 /* packfile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = packfile.cpp; path = ../mpshared/packfile.cpp; sourceTree = SOURCE_ROOT; }; + C1077A190E31C4B5003DA7D6 /* packfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = packfile.h; path = ../mpshared/packfile.h; sourceTree = SOURCE_ROOT; }; + C1077A1A0E31C4B5003DA7D6 /* pdbreader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = pdbreader.h; path = ../mpshared/pdbreader.h; sourceTree = SOURCE_ROOT; }; + C1077A1B0E31C4B5003DA7D6 /* side.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = side.h; path = ../mpshared/side.h; sourceTree = SOURCE_ROOT; }; + C1077A1F0E31C57F003DA7D6 /* iphonepackfile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = iphonepackfile.cpp; path = iphone/iphonepackfile.cpp; sourceTree = ""; }; + C1077A200E31C57F003DA7D6 /* iphonepackfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = iphonepackfile.h; path = iphone/iphonepackfile.h; sourceTree = ""; }; + C1077A220E31C5A8003DA7D6 /* misc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = misc.h; path = ../base/misc.h; sourceTree = SOURCE_ROOT; }; + C1077A320E31CAA3003DA7D6 /* mempdbreader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mempdbreader.cpp; sourceTree = SOURCE_ROOT; }; + C1077A330E31CAA3003DA7D6 /* mempdbreader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mempdbreader.h; sourceTree = SOURCE_ROOT; }; + C1077A4F0E330836003DA7D6 /* md5.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = md5.h; path = ../base/md5.h; sourceTree = SOURCE_ROOT; }; + C1077A500E330836003DA7D6 /* md5c.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = md5c.cpp; path = ../base/md5c.cpp; sourceTree = SOURCE_ROOT; }; + C1077AA20E36A74C003DA7D6 /* tick.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = tick.cpp; path = ../base/tick.cpp; sourceTree = SOURCE_ROOT; }; + C1077AA30E36A74C003DA7D6 /* tick.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = tick.h; path = ../base/tick.h; sourceTree = SOURCE_ROOT; }; + C1077CA90E3E6F5F003DA7D6 /* deletetracker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = deletetracker.cpp; path = ../base/deletetracker.cpp; sourceTree = SOURCE_ROOT; }; + C1077CAA0E3E6F5F003DA7D6 /* deletetracker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = deletetracker.h; path = ../base/deletetracker.h; sourceTree = SOURCE_ROOT; }; + C11FDC651033682E0053CD83 /* eventer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = eventer.cpp; path = ../base/eventer.cpp; sourceTree = SOURCE_ROOT; }; + C11FDC661033682E0053CD83 /* eventer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = eventer.h; path = ../base/eventer.h; sourceTree = SOURCE_ROOT; }; + C11FDC671033682E0053CD83 /* selectserver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = selectserver.cpp; path = ../base/selectserver.cpp; sourceTree = SOURCE_ROOT; }; + C11FDC681033682E0053CD83 /* selectserver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = selectserver.h; path = ../base/selectserver.h; sourceTree = SOURCE_ROOT; }; + C124299F0E553D9500DDDFD1 /* stylushandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = stylushandler.cpp; sourceTree = SOURCE_ROOT; }; + C12429F00E5727BF00DDDFD1 /* fingerhandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = fingerhandler.cpp; sourceTree = SOURCE_ROOT; }; + C12D9D2C0ECA3F94006B5FD9 /* completemanager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = completemanager.cpp; sourceTree = SOURCE_ROOT; }; + C12D9D2D0ECA3F94006B5FD9 /* completemanager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = completemanager.h; sourceTree = SOURCE_ROOT; }; + C12DA1F90ECE7A9E006B5FD9 /* xmsglog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = xmsglog.cpp; path = ../mpshared/xmsglog.cpp; sourceTree = SOURCE_ROOT; }; + C12DA1FA0ECE7A9E006B5FD9 /* xmsglog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = xmsglog.h; path = ../mpshared/xmsglog.h; sourceTree = SOURCE_ROOT; }; + C13709510E6D9E3E00F9F923 /* httprequest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = httprequest.cpp; sourceTree = SOURCE_ROOT; }; + C13709520E6D9E3E00F9F923 /* httprequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = httprequest.h; sourceTree = SOURCE_ROOT; }; + C13709530E6D9E3E00F9F923 /* httpservice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = httpservice.h; sourceTree = SOURCE_ROOT; }; + C13709560E6D9E6700F9F923 /* iphonehttprequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = iphonehttprequest.h; path = iphone/iphonehttprequest.h; sourceTree = ""; }; + C13709570E6D9E6700F9F923 /* iphonehttprequest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = iphonehttprequest.mm; path = iphone/iphonehttprequest.mm; sourceTree = ""; }; + C13709580E6D9E6700F9F923 /* iphonehttpservice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = iphonehttpservice.h; path = iphone/iphonehttpservice.h; sourceTree = ""; }; + C13709590E6D9E6700F9F923 /* iphonehttpservice.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = iphonehttpservice.mm; path = iphone/iphonehttpservice.mm; sourceTree = ""; }; + C1370A060E707C1E00F9F923 /* jsonbuilder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = jsonbuilder.cpp; path = ../yajl/wrapper/jsonbuilder.cpp; sourceTree = SOURCE_ROOT; }; + C1370A070E707C1E00F9F923 /* jsonbuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = jsonbuilder.h; path = ../yajl/wrapper/jsonbuilder.h; sourceTree = SOURCE_ROOT; }; + C1370A080E707C1E00F9F923 /* jsontypes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = jsontypes.cpp; path = ../yajl/wrapper/jsontypes.cpp; sourceTree = SOURCE_ROOT; }; + C1370A090E707C1E00F9F923 /* jsontypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = jsontypes.h; path = ../yajl/wrapper/jsontypes.h; sourceTree = SOURCE_ROOT; }; + C1370A0C0E707C5700F9F923 /* yajl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = yajl.c; path = ../yajl/src/yajl.c; sourceTree = SOURCE_ROOT; }; + C1370A0D0E707C5700F9F923 /* yajl_buf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = yajl_buf.c; path = ../yajl/src/yajl_buf.c; sourceTree = SOURCE_ROOT; }; + C1370A0E0E707C5700F9F923 /* yajl_buf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = yajl_buf.h; path = ../yajl/src/yajl_buf.h; sourceTree = SOURCE_ROOT; }; + C1370A0F0E707C5700F9F923 /* yajl_encode.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = yajl_encode.c; path = ../yajl/src/yajl_encode.c; sourceTree = SOURCE_ROOT; }; + C1370A100E707C5700F9F923 /* yajl_encode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = yajl_encode.h; path = ../yajl/src/yajl_encode.h; sourceTree = SOURCE_ROOT; }; + C1370A110E707C5700F9F923 /* yajl_lex.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = yajl_lex.c; path = ../yajl/src/yajl_lex.c; sourceTree = SOURCE_ROOT; }; + C1370A120E707C5700F9F923 /* yajl_lex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = yajl_lex.h; path = ../yajl/src/yajl_lex.h; sourceTree = SOURCE_ROOT; }; + C1370A130E707C5700F9F923 /* yajl_parser.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = yajl_parser.c; path = ../yajl/src/yajl_parser.c; sourceTree = SOURCE_ROOT; }; + C1370A140E707C5700F9F923 /* yajl_parser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = yajl_parser.h; path = ../yajl/src/yajl_parser.h; sourceTree = SOURCE_ROOT; }; + C1370A1A0E707C7F00F9F923 /* yajl_common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = yajl_common.h; path = ../yajl/api/yajl_common.h; sourceTree = SOURCE_ROOT; }; + C1370A1B0E707C7F00F9F923 /* yajl_gen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = yajl_gen.h; path = ../yajl/api/yajl_gen.h; sourceTree = SOURCE_ROOT; }; + C1370A1C0E707C7F00F9F923 /* yajl_parse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = yajl_parse.h; path = ../yajl/api/yajl_parse.h; sourceTree = SOURCE_ROOT; }; + C1370A2E0E70831E00F9F923 /* map.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = map.cpp; sourceTree = SOURCE_ROOT; }; + C1370A2F0E70831E00F9F923 /* map.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = map.h; sourceTree = SOURCE_ROOT; }; + C1370AA40E7099A200F9F923 /* format.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = format.cpp; path = ../base/format.cpp; sourceTree = SOURCE_ROOT; }; + C1370AE60E719A1400F9F923 /* dlmissionpack.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dlmissionpack.cpp; sourceTree = SOURCE_ROOT; }; + C1370B140E719C4200F9F923 /* endpoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = endpoint.cpp; path = ../server/endpoint.cpp; sourceTree = SOURCE_ROOT; }; + C1370B150E719C4200F9F923 /* endpoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = endpoint.h; path = ../server/endpoint.h; sourceTree = SOURCE_ROOT; }; + C1370B160E719C4200F9F923 /* game.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = game.cpp; path = ../server/game.cpp; sourceTree = SOURCE_ROOT; }; + C1370B170E719C4200F9F923 /* game.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = game.h; path = ../server/game.h; sourceTree = SOURCE_ROOT; }; + C1370B180E719C4200F9F923 /* ids.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ids.h; path = ../server/ids.h; sourceTree = SOURCE_ROOT; }; + C1370B190E719C4200F9F923 /* levelinfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = levelinfo.cpp; path = ../server/levelinfo.cpp; sourceTree = SOURCE_ROOT; }; + C1370B1A0E719C4200F9F923 /* levelinfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = levelinfo.h; path = ../server/levelinfo.h; sourceTree = SOURCE_ROOT; }; + C1370B1B0E719C4200F9F923 /* levelinfocache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = levelinfocache.cpp; path = ../server/levelinfocache.cpp; sourceTree = SOURCE_ROOT; }; + C1370B1C0E719C4200F9F923 /* levelinfocache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = levelinfocache.h; path = ../server/levelinfocache.h; sourceTree = SOURCE_ROOT; }; + C1370B1D0E719C4200F9F923 /* lobby.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = lobby.cpp; path = ../server/lobby.cpp; sourceTree = SOURCE_ROOT; }; + C1370B1E0E719C4200F9F923 /* lobby.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = lobby.h; path = ../server/lobby.h; sourceTree = SOURCE_ROOT; }; + C1370B1F0E719C4200F9F923 /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = main.cpp; path = ../server/main.cpp; sourceTree = SOURCE_ROOT; }; + C1370B200E719C4200F9F923 /* ncpackfile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ncpackfile.cpp; path = ../server/ncpackfile.cpp; sourceTree = SOURCE_ROOT; }; + C1370B210E719C4200F9F923 /* ncpackfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ncpackfile.h; path = ../server/ncpackfile.h; sourceTree = SOURCE_ROOT; }; + C1370B220E719C4200F9F923 /* ncpdbreader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ncpdbreader.cpp; path = ../server/ncpdbreader.cpp; sourceTree = SOURCE_ROOT; }; + C1370B230E719C4200F9F923 /* ncpdbreader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ncpdbreader.h; path = ../server/ncpdbreader.h; sourceTree = SOURCE_ROOT; }; + C1370B240E719C4200F9F923 /* player.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = player.cpp; path = ../server/player.cpp; sourceTree = SOURCE_ROOT; }; + C1370B250E719C4200F9F923 /* player.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = player.h; path = ../server/player.h; sourceTree = SOURCE_ROOT; }; + C1370B260E719C4200F9F923 /* playermgr.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = playermgr.cpp; path = ../server/playermgr.cpp; sourceTree = SOURCE_ROOT; }; + C1370B270E719C4200F9F923 /* playermgr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = playermgr.h; path = ../server/playermgr.h; sourceTree = SOURCE_ROOT; }; + C1370B280E719C4200F9F923 /* server.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = server.cpp; path = ../server/server.cpp; sourceTree = SOURCE_ROOT; }; + C1370B290E719C4200F9F923 /* server.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = server.h; path = ../server/server.h; sourceTree = SOURCE_ROOT; }; + C1370C190E71CC7600F9F923 /* serviceurls.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = serviceurls.cpp; sourceTree = SOURCE_ROOT; }; + C1370C1A0E71CC7600F9F923 /* serviceurls.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = serviceurls.h; sourceTree = SOURCE_ROOT; }; + C139CFA30E8B352A005F4A4B /* drawscan.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = drawscan.cpp; sourceTree = SOURCE_ROOT; }; + C13D85B70F07580200E63C5D /* refmap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = refmap.h; sourceTree = SOURCE_ROOT; }; + C13D85B80F07580200E63C5D /* stateframe.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = stateframe.cpp; sourceTree = SOURCE_ROOT; }; + C13D85B90F07580200E63C5D /* stateframe.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stateframe.h; sourceTree = SOURCE_ROOT; }; + C13D85BA0F07580200E63C5D /* statetracker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = statetracker.cpp; sourceTree = SOURCE_ROOT; }; + C13D85BB0F07580200E63C5D /* statetracker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = statetracker.h; sourceTree = SOURCE_ROOT; }; + C13D87C70F0AF5A300E63C5D /* uploader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = uploader.cpp; sourceTree = SOURCE_ROOT; }; + C13D87C80F0AF5A300E63C5D /* uploader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = uploader.h; sourceTree = SOURCE_ROOT; }; + C13F7C5A0FF5CAAD00F77047 /* webviewcontroller.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = webviewcontroller.mm; path = iphone/webviewcontroller.mm; sourceTree = ""; }; + C13F7C5B0FF5CAAD00F77047 /* webviewcontroller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = webviewcontroller.h; path = iphone/webviewcontroller.h; sourceTree = ""; }; + C13F80A30FFE811E00F77047 /* misc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = misc.cpp; path = ../base/misc.cpp; sourceTree = SOURCE_ROOT; }; + C1456692101A9EF0006DCF81 /* inputcontroller.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = inputcontroller.mm; path = iphone/inputcontroller.mm; sourceTree = ""; }; + C1456693101A9EF0006DCF81 /* inputcontroller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = inputcontroller.h; path = iphone/inputcontroller.h; sourceTree = ""; }; + C14752FE0EB6903A0059D367 /* selectionsprite.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = selectionsprite.mm; path = iphone/selectionsprite.mm; sourceTree = ""; }; + C14752FF0EB6903A0059D367 /* selectionsprite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = selectionsprite.h; path = iphone/selectionsprite.h; sourceTree = ""; }; + C14753010EB6908B0059D367 /* dragrect.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dragrect.cpp; sourceTree = SOURCE_ROOT; }; + C14753020EB6908B0059D367 /* dragrect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dragrect.h; sourceTree = SOURCE_ROOT; }; + C14753030EB6908B0059D367 /* vec2d.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vec2d.h; sourceTree = SOURCE_ROOT; }; + C158A66A0EA3E6F3007035AF /* spritemgradapter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = spritemgradapter.h; path = iphone/spritemgradapter.h; sourceTree = ""; }; + C1603E040F6984CB00B19D51 /* wiviewcontroller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = wiviewcontroller.h; path = iphone/wiviewcontroller.h; sourceTree = ""; }; + C1603E050F6984CB00B19D51 /* wiviewcontroller.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = wiviewcontroller.mm; path = iphone/wiviewcontroller.mm; sourceTree = ""; }; + C1603E1D0F69AAB200B19D51 /* chatviewcontroller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = chatviewcontroller.h; path = iphone/chatviewcontroller.h; sourceTree = ""; }; + C1603E1E0F69AAB200B19D51 /* chatviewcontroller.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = chatviewcontroller.mm; path = iphone/chatviewcontroller.mm; sourceTree = ""; }; + C166A8C80DE5F13B000B45A0 /* oglview2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = oglview2.h; path = iphone/oglview2.h; sourceTree = ""; }; + C166A8C90DE5F13B000B45A0 /* oglview2.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = oglview2.mm; path = iphone/oglview2.mm; sourceTree = ""; }; + C166A8CC0DE62EF4000B45A0 /* ogldib.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ogldib.cpp; path = iphone/ogldib.cpp; sourceTree = ""; }; + C166A8CE0DE62F20000B45A0 /* cgdib.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = cgdib.cpp; path = iphone/cgdib.cpp; sourceTree = ""; }; + C175F5C50DC0465000985493 /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = System/Library/Frameworks/OpenGLES.framework; sourceTree = SDKROOT; }; + C176855C0E0C35820058E49D /* bytebuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = bytebuffer.h; path = ../base/bytebuffer.h; sourceTree = SOURCE_ROOT; }; + C176855D0E0C35820058E49D /* criticalsection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = criticalsection.h; path = ../base/criticalsection.h; sourceTree = SOURCE_ROOT; }; + C176855E0E0C35820058E49D /* dispatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dispatcher.h; path = ../base/dispatcher.h; sourceTree = SOURCE_ROOT; }; + C17685610E0C35820058E49D /* messagequeue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = messagequeue.cpp; path = ../base/messagequeue.cpp; sourceTree = SOURCE_ROOT; }; + C17685620E0C35820058E49D /* messagequeue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = messagequeue.h; path = ../base/messagequeue.h; sourceTree = SOURCE_ROOT; }; + C17685630E0C35820058E49D /* socket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = socket.cpp; path = ../base/socket.cpp; sourceTree = SOURCE_ROOT; }; + C17685640E0C35820058E49D /* socket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = socket.h; path = ../base/socket.h; sourceTree = SOURCE_ROOT; }; + C17685650E0C35820058E49D /* socketaddress.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = socketaddress.cpp; path = ../base/socketaddress.cpp; sourceTree = SOURCE_ROOT; }; + C17685660E0C35820058E49D /* socketaddress.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = socketaddress.h; path = ../base/socketaddress.h; sourceTree = SOURCE_ROOT; }; + C17685670E0C35820058E49D /* socketserver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = socketserver.cpp; path = ../base/socketserver.cpp; sourceTree = SOURCE_ROOT; }; + C17685680E0C35820058E49D /* socketserver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = socketserver.h; path = ../base/socketserver.h; sourceTree = SOURCE_ROOT; }; + C176856E0E0C35A60058E49D /* bytebuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = bytebuffer.cpp; path = ../base/bytebuffer.cpp; sourceTree = SOURCE_ROOT; }; + C17685700E0C36100058E49D /* misc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = misc.cpp; path = ../mpshared/misc.cpp; sourceTree = SOURCE_ROOT; }; + C17685710E0C36100058E49D /* misc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = misc.h; path = ../mpshared/misc.h; sourceTree = SOURCE_ROOT; }; + C17685720E0C36100058E49D /* mpht.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mpht.h; path = ../mpshared/mpht.h; sourceTree = SOURCE_ROOT; }; + C17685730E0C36100058E49D /* netmessage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = netmessage.cpp; path = ../mpshared/netmessage.cpp; sourceTree = SOURCE_ROOT; }; + C17685740E0C36100058E49D /* netmessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = netmessage.h; path = ../mpshared/netmessage.h; sourceTree = SOURCE_ROOT; }; + C17685760E0C36100058E49D /* xmsg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = xmsg.h; path = ../mpshared/xmsg.h; sourceTree = SOURCE_ROOT; }; + C17685B60E15CB180058E49D /* xpump.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = xpump.cpp; path = ../mpshared/xpump.cpp; sourceTree = SOURCE_ROOT; }; + C17685B70E15CB180058E49D /* xpump.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = xpump.h; path = ../mpshared/xpump.h; sourceTree = SOURCE_ROOT; }; + C178D3F80EABD5AF0015E32D /* icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = icon.png; path = iphone/icon.png; sourceTree = ""; }; + C17D66080D9E15A300CB9F01 /* alertcontrol.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = alertcontrol.cpp; sourceTree = SOURCE_ROOT; }; + C17D660A0D9E15BE00CB9F01 /* Andy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Andy.cpp; sourceTree = SOURCE_ROOT; }; + C17D660B0D9E15BE00CB9F01 /* Animation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Animation.cpp; sourceTree = SOURCE_ROOT; }; + C17D660C0D9E15BE00CB9F01 /* Artillery.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Artillery.cpp; sourceTree = SOURCE_ROOT; }; + C17D660D0D9E15BE00CB9F01 /* basictypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = basictypes.h; path = ../inc/basictypes.h; sourceTree = SOURCE_ROOT; }; + C17D660E0D9E15BE00CB9F01 /* bitmap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = bitmap.cpp; sourceTree = SOURCE_ROOT; }; + C17D660F0D9E15BE00CB9F01 /* Builder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Builder.cpp; sourceTree = SOURCE_ROOT; }; + C17D66100D9E15BE00CB9F01 /* BuildMgr.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BuildMgr.cpp; sourceTree = SOURCE_ROOT; }; + C17D66110D9E15BE00CB9F01 /* cachemgr.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cachemgr.cpp; sourceTree = SOURCE_ROOT; }; + C17D66130D9E15BE00CB9F01 /* compression.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = compression.cpp; sourceTree = SOURCE_ROOT; }; + C17D66140D9E15BE00CB9F01 /* CutScene.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CutScene.cpp; sourceTree = SOURCE_ROOT; }; + C17D66150D9E15BE00CB9F01 /* drm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = drm.cpp; sourceTree = SOURCE_ROOT; }; + C17D66160D9E15BE00CB9F01 /* Ecom.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Ecom.cpp; sourceTree = SOURCE_ROOT; }; + C17D66170D9E15BE00CB9F01 /* event.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = event.cpp; sourceTree = SOURCE_ROOT; }; + C17D66180D9E15BE00CB9F01 /* fogmap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = fogmap.cpp; sourceTree = SOURCE_ROOT; }; + C17D66190D9E15BE00CB9F01 /* font.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = font.cpp; sourceTree = SOURCE_ROOT; }; + C17D661A0D9E15BE00CB9F01 /* form.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = form.cpp; sourceTree = SOURCE_ROOT; }; + C17D661B0D9E15BE00CB9F01 /* formmgr.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = formmgr.cpp; sourceTree = SOURCE_ROOT; }; + C17D661C0D9E15BE00CB9F01 /* game.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = game.cpp; sourceTree = SOURCE_ROOT; }; + C17D661D0D9E15BE00CB9F01 /* GameObjects.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GameObjects.cpp; sourceTree = SOURCE_ROOT; }; + C17D661E0D9E15BE00CB9F01 /* GameOptions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GameOptions.cpp; sourceTree = SOURCE_ROOT; }; + C17D661F0D9E15BE00CB9F01 /* Headquarters.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Headquarters.cpp; sourceTree = SOURCE_ROOT; }; + C17D66200D9E15BE00CB9F01 /* Help.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Help.cpp; sourceTree = SOURCE_ROOT; }; + C17D66210D9E15BE00CB9F01 /* HRC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HRC.cpp; sourceTree = SOURCE_ROOT; }; + C17D66220D9E15BE00CB9F01 /* ht.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ht.h; sourceTree = SOURCE_ROOT; }; + C17D66240D9E15BE00CB9F01 /* InputUI.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InputUI.cpp; sourceTree = SOURCE_ROOT; }; + C17D66250D9E15BE00CB9F01 /* Level.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Level.cpp; sourceTree = SOURCE_ROOT; }; + C17D66260D9E15BE00CB9F01 /* loadsave.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = loadsave.cpp; sourceTree = SOURCE_ROOT; }; + C17D66270D9E15BE00CB9F01 /* LRInfantry.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LRInfantry.cpp; sourceTree = SOURCE_ROOT; }; + C17D66280D9E15BE00CB9F01 /* memmgr.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = memmgr.cpp; sourceTree = SOURCE_ROOT; }; + C17D66290D9E15BE00CB9F01 /* Miner.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Miner.cpp; sourceTree = SOURCE_ROOT; }; + C17D662A0D9E15BE00CB9F01 /* misc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = misc.cpp; sourceTree = SOURCE_ROOT; }; + C17D662B0D9E15BE00CB9F01 /* misccontrols.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = misccontrols.cpp; sourceTree = SOURCE_ROOT; }; + C17D662C0D9E15BE00CB9F01 /* miscgraphics.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = miscgraphics.cpp; sourceTree = SOURCE_ROOT; }; + C17D662D0D9E15BE00CB9F01 /* mixer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mixer.cpp; sourceTree = SOURCE_ROOT; }; + C17D662E0D9E15BE00CB9F01 /* mixer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mixer.h; sourceTree = SOURCE_ROOT; }; + C17D662F0D9E15BE00CB9F01 /* MobileBuilder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MobileBuilder.cpp; sourceTree = SOURCE_ROOT; }; + C17D66300D9E15BE00CB9F01 /* MobileHQ.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MobileHQ.cpp; sourceTree = SOURCE_ROOT; }; + C17D66310D9E15BE00CB9F01 /* MobileUnit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MobileUnit.cpp; sourceTree = SOURCE_ROOT; }; + C17D66320D9E15BE00CB9F01 /* Multiplayer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Multiplayer.cpp; sourceTree = SOURCE_ROOT; }; + C17D66340D9E15BE00CB9F01 /* Overmind.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Overmind.cpp; sourceTree = SOURCE_ROOT; }; + C17D66370D9E15BE00CB9F01 /* Player.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Player.cpp; sourceTree = SOURCE_ROOT; }; + C17D66380D9E15BE00CB9F01 /* Processor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Processor.cpp; sourceTree = SOURCE_ROOT; }; + C17D66390D9E15BE00CB9F01 /* Radar.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Radar.cpp; sourceTree = SOURCE_ROOT; }; + C17D663A0D9E15BE00CB9F01 /* RawBitmap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RawBitmap.cpp; sourceTree = SOURCE_ROOT; }; + C17D663B0D9E15BE00CB9F01 /* Reactor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Reactor.cpp; sourceTree = SOURCE_ROOT; }; + C17D663C0D9E15BE00CB9F01 /* Replicator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Replicator.cpp; sourceTree = SOURCE_ROOT; }; + C17D663D0D9E15BE00CB9F01 /* res.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = res.h; sourceTree = SOURCE_ROOT; }; + C17D663E0D9E15BE00CB9F01 /* Research.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Research.cpp; sourceTree = SOURCE_ROOT; }; + C17D663F0D9E15BE00CB9F01 /* rip.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = rip.cpp; path = ../inc/rip.cpp; sourceTree = SOURCE_ROOT; }; + C17D66400D9E15BE00CB9F01 /* Shell.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Shell.cpp; sourceTree = SOURCE_ROOT; }; + C17D66410D9E15BE00CB9F01 /* SimUI.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SimUI.cpp; sourceTree = SOURCE_ROOT; }; + C17D66420D9E15BE00CB9F01 /* Simulation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Simulation.cpp; sourceTree = SOURCE_ROOT; }; + C17D66450D9E15BE00CB9F01 /* sounddevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sounddevice.h; sourceTree = SOURCE_ROOT; }; + C17D66460D9E15BE00CB9F01 /* soundeffects.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = soundeffects.h; sourceTree = SOURCE_ROOT; }; + C17D66470D9E15BE00CB9F01 /* soundmgr.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = soundmgr.cpp; sourceTree = SOURCE_ROOT; }; + C17D66480D9E15BE00CB9F01 /* SpInfantry.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SpInfantry.cpp; sourceTree = SOURCE_ROOT; }; + C17D66490D9E15BE00CB9F01 /* SRInfantry.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SRInfantry.cpp; sourceTree = SOURCE_ROOT; }; + C17D664A0D9E15BE00CB9F01 /* StateMachine.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StateMachine.cpp; sourceTree = SOURCE_ROOT; }; + C17D664B0D9E15BE00CB9F01 /* strings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = strings.h; sourceTree = SOURCE_ROOT; }; + C17D664C0D9E15BE00CB9F01 /* stringtable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = stringtable.cpp; sourceTree = SOURCE_ROOT; }; + C17D664D0D9E15BE00CB9F01 /* Struct.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Struct.cpp; sourceTree = SOURCE_ROOT; }; + C17D664E0D9E15BE00CB9F01 /* Tank.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Tank.cpp; sourceTree = SOURCE_ROOT; }; + C17D664F0D9E15BE00CB9F01 /* tbitmap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tbitmap.cpp; sourceTree = SOURCE_ROOT; }; + C17D66500D9E15BE00CB9F01 /* terrainmap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = terrainmap.cpp; sourceTree = SOURCE_ROOT; }; + C17D66510D9E15BE00CB9F01 /* tests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tests.cpp; sourceTree = SOURCE_ROOT; }; + C17D66520D9E15BE00CB9F01 /* thunks.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = thunks.cpp; sourceTree = SOURCE_ROOT; }; + C17D66530D9E15BE00CB9F01 /* tilemap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tilemap.cpp; sourceTree = SOURCE_ROOT; }; + C17D66540D9E15BE00CB9F01 /* timer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = timer.cpp; sourceTree = SOURCE_ROOT; }; + C17D66550D9E15BE00CB9F01 /* Tower.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Tower.cpp; sourceTree = SOURCE_ROOT; }; + C17D66560D9E15BE00CB9F01 /* TriggerActions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TriggerActions.cpp; sourceTree = SOURCE_ROOT; }; + C17D66570D9E15BE00CB9F01 /* TriggerConditions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TriggerConditions.cpp; sourceTree = SOURCE_ROOT; }; + C17D66580D9E15BE00CB9F01 /* triggermgr.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = triggermgr.cpp; sourceTree = SOURCE_ROOT; }; + C17D66590D9E15BE00CB9F01 /* Unit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Unit.cpp; sourceTree = SOURCE_ROOT; }; + C17D665A0D9E15BE00CB9F01 /* UnitGroupMgr.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UnitGroupMgr.cpp; sourceTree = SOURCE_ROOT; }; + C17D665B0D9E15BE00CB9F01 /* updatemap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = updatemap.cpp; sourceTree = SOURCE_ROOT; }; + C17D665C0D9E15BE00CB9F01 /* VTS.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VTS.cpp; sourceTree = SOURCE_ROOT; }; + C17D665D0D9E15BE00CB9F01 /* Warehouse.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Warehouse.cpp; sourceTree = SOURCE_ROOT; }; + C17D66AE0D9E160100CB9F01 /* display.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = display.cpp; path = iphone/display.cpp; sourceTree = ""; }; + C17D66B10D9E160100CB9F01 /* host.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = host.cpp; path = iphone/host.cpp; sourceTree = ""; }; + C17D66B20D9E160100CB9F01 /* htplatform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = htplatform.h; path = iphone/htplatform.h; sourceTree = ""; }; + C17D66B30D9E160100CB9F01 /* input.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = input.h; path = iphone/input.h; sourceTree = ""; }; + C17D66B40D9E160100CB9F01 /* iphone.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = iphone.h; path = iphone/iphone.h; sourceTree = ""; }; + C17D66B50D9E160100CB9F01 /* iphone.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = iphone.mm; path = iphone/iphone.mm; sourceTree = ""; }; + C17D66B60D9E160100CB9F01 /* iphonesounddev.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = iphonesounddev.cpp; path = iphone/iphonesounddev.cpp; sourceTree = ""; }; + C17D66B70D9E160100CB9F01 /* cgview.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = cgview.h; path = iphone/cgview.h; sourceTree = ""; }; + C17D66B80D9E160100CB9F01 /* cgview.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = cgview.mm; path = iphone/cgview.mm; sourceTree = ""; }; + C17D66B90D9E160100CB9F01 /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = main.cpp; path = iphone/main.cpp; sourceTree = ""; }; + C17D66BC0D9E160100CB9F01 /* savegame.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = savegame.cpp; path = iphone/savegame.cpp; sourceTree = ""; }; + C17D66BF0D9E160100CB9F01 /* transportmgr.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = transportmgr.cpp; path = iphone/transportmgr.cpp; sourceTree = ""; }; + C17D66CC0D9E162500CB9F01 /* htsfx.pdb */ = {isa = PBXFileReference; lastKnownFileType = text; path = htsfx.pdb; sourceTree = ""; }; + C17D66E40D9E163900CB9F01 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = /System/Library/Frameworks/AudioToolbox.framework; sourceTree = ""; }; + C184248611EE4EFA007BDB3D /* Entitlements.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Entitlements.plist; sourceTree = ""; }; + C18E0E020DBAC70700096237 /* oglview.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = oglview.h; path = iphone/oglview.h; sourceTree = ""; }; + C18E0E030DBAC70700096237 /* oglview.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = oglview.mm; path = iphone/oglview.mm; sourceTree = ""; }; + C1949CF70E7874C200A654A1 /* downloadbox.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = downloadbox.cpp; sourceTree = SOURCE_ROOT; }; + C194A0FF0E7F8CF000A654A1 /* httpindexloader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = httpindexloader.cpp; sourceTree = SOURCE_ROOT; }; + C194A1000E7F8CF000A654A1 /* httpindexloader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = httpindexloader.h; sourceTree = SOURCE_ROOT; }; + C194A1010E7F8CF000A654A1 /* httppackinfomanager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = httppackinfomanager.cpp; sourceTree = SOURCE_ROOT; }; + C194A1020E7F8CF000A654A1 /* httppackinfomanager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = httppackinfomanager.h; sourceTree = SOURCE_ROOT; }; + C194A1030E7F8CF000A654A1 /* httppackmanager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = httppackmanager.cpp; sourceTree = SOURCE_ROOT; }; + C194A1040E7F8CF000A654A1 /* httppackmanager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = httppackmanager.h; sourceTree = SOURCE_ROOT; }; + C194A1BF0E80300C00A654A1 /* constants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = constants.h; path = ../mpshared/constants.h; sourceTree = SOURCE_ROOT; }; + C194A1C00E80300C00A654A1 /* indexloader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = indexloader.cpp; path = ../mpshared/indexloader.cpp; sourceTree = SOURCE_ROOT; }; + C194A1C10E80300C00A654A1 /* indexloader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = indexloader.h; path = ../mpshared/indexloader.h; sourceTree = SOURCE_ROOT; }; + C194A1C20E80300C00A654A1 /* packinfomanager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = packinfomanager.cpp; path = ../mpshared/packinfomanager.cpp; sourceTree = SOURCE_ROOT; }; + C194A1C30E80300C00A654A1 /* packmanager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = packmanager.cpp; path = ../mpshared/packmanager.cpp; sourceTree = SOURCE_ROOT; }; + C1994E2A0EA149D200A48088 /* iphoneanimsprite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = iphoneanimsprite.h; path = iphone/iphoneanimsprite.h; sourceTree = ""; }; + C1994E2B0EA149D200A48088 /* iphoneanimsprite.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = iphoneanimsprite.mm; path = iphone/iphoneanimsprite.mm; sourceTree = ""; }; + C1994E300EA14A2800A48088 /* progresscallback.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = progresscallback.h; sourceTree = SOURCE_ROOT; }; + C1994E310EA14A2800A48088 /* sprite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sprite.h; sourceTree = SOURCE_ROOT; }; + C19D450E0F745A9600283E95 /* chatcell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = chatcell.h; path = iphone/chatcell.h; sourceTree = ""; }; + C19D450F0F745A9600283E95 /* chatcell.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = chatcell.mm; path = iphone/chatcell.mm; sourceTree = ""; }; + C1A111F40E416739007913B4 /* messagehandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = messagehandler.cpp; path = ../base/messagehandler.cpp; sourceTree = SOURCE_ROOT; }; + C1A111F50E416739007913B4 /* messagehandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = messagehandler.h; path = ../base/messagehandler.h; sourceTree = SOURCE_ROOT; }; + C1A1C2040FE9B2BD00042055 /* format.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = format.h; path = ../base/format.h; sourceTree = SOURCE_ROOT; }; + C1A1C2050FE9B2BD00042055 /* log.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = log.h; path = ../base/log.h; sourceTree = SOURCE_ROOT; }; + C1A1C2060FE9B2BD00042055 /* thread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = thread.cpp; path = ../base/thread.cpp; sourceTree = SOURCE_ROOT; }; + C1A1C2070FE9B2BD00042055 /* thread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = thread.h; path = ../base/thread.h; sourceTree = SOURCE_ROOT; }; + C1ABDCFA0DC7C19E000A5B75 /* flickscroller.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = flickscroller.cpp; sourceTree = SOURCE_ROOT; }; + C1B3F5340E65D6C100D36F66 /* missionlist.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = missionlist.cpp; sourceTree = SOURCE_ROOT; }; + C1B3F5350E65D6C100D36F66 /* missionlist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = missionlist.h; sourceTree = SOURCE_ROOT; }; + C1B3F5E80E671C0600D36F66 /* selectmission.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = selectmission.cpp; sourceTree = SOURCE_ROOT; }; + C1C706950DBD0F090062B73B /* cgdib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = cgdib.h; path = iphone/cgdib.h; sourceTree = ""; }; + C1C706970DBD0F090062B73B /* ogldib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ogldib.h; path = iphone/ogldib.h; sourceTree = ""; }; + C1C708DE0DBD6C060062B73B /* rip.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = rip.h; path = ../inc/rip.h; sourceTree = SOURCE_ROOT; }; + C1C709750DBDAF290062B73B /* wiview.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = wiview.h; path = iphone/wiview.h; sourceTree = ""; }; + C1C709760DBDAF290062B73B /* wiview.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = wiview.mm; path = iphone/wiview.mm; sourceTree = ""; }; + C1D143970E45881F0025FA74 /* dist.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = dist.plist; sourceTree = ""; }; + C1D4E0370F8FCBEA00D8C118 /* creategameform.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = creategameform.cpp; sourceTree = SOURCE_ROOT; }; + C1D4E0380F8FCBEA00D8C118 /* creategameform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = creategameform.h; sourceTree = SOURCE_ROOT; }; + C1D4E03B0F8FCBEA00D8C118 /* picktransportform.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = picktransportform.cpp; sourceTree = SOURCE_ROOT; }; + C1D4E03C0F8FCBEA00D8C118 /* picktransportform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = picktransportform.h; sourceTree = SOURCE_ROOT; }; + C1D4E03D0F8FCBEA00D8C118 /* roomform.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = roomform.cpp; sourceTree = SOURCE_ROOT; }; + C1D4E03E0F8FCBEA00D8C118 /* roomform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = roomform.h; sourceTree = SOURCE_ROOT; }; + C1D4E0550F98F01D00D8C118 /* lobbyform.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = lobbyform.cpp; sourceTree = SOURCE_ROOT; }; + C1D4E0560F98F01D00D8C118 /* lobbyform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lobbyform.h; sourceTree = SOURCE_ROOT; }; + C1D4E0570F98F01D00D8C118 /* loginform.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = loginform.cpp; sourceTree = SOURCE_ROOT; }; + C1D4E0580F98F01D00D8C118 /* loginform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = loginform.h; sourceTree = SOURCE_ROOT; }; + C1D4E0590F98F01D00D8C118 /* loginhandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = loginhandler.cpp; sourceTree = SOURCE_ROOT; }; + C1D4E05A0F98F01D00D8C118 /* loginhandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = loginhandler.h; sourceTree = SOURCE_ROOT; }; + C1D4E07D0F98F4DC00D8C118 /* simplerequest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = simplerequest.cpp; sourceTree = SOURCE_ROOT; }; + C1D4E07E0F98F4DC00D8C118 /* simplerequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = simplerequest.h; sourceTree = SOURCE_ROOT; }; + C1D4E0C60F9D220C00D8C118 /* base64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = base64.cpp; path = ../base/base64.cpp; sourceTree = SOURCE_ROOT; }; + C1D4E0C70F9D220C00D8C118 /* base64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = base64.h; path = ../base/base64.h; sourceTree = SOURCE_ROOT; }; + C1D4E0CB0F9F8A6600D8C118 /* lobby.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = lobby.cpp; sourceTree = SOURCE_ROOT; }; + C1D4E0CC0F9F8A6600D8C118 /* lobby.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lobby.h; sourceTree = SOURCE_ROOT; }; + C1D4E4AC0FA7B9AA00D8C118 /* createroomform.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = createroomform.cpp; sourceTree = SOURCE_ROOT; }; + C1D4E4AD0FA7B9AA00D8C118 /* createroomform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = createroomform.h; sourceTree = SOURCE_ROOT; }; + C1D4E6840FABB4A500D8C118 /* gameform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gameform.h; sourceTree = SOURCE_ROOT; }; + C1D4E6850FABB4A500D8C118 /* gameform.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = gameform.cpp; sourceTree = SOURCE_ROOT; }; + C1D4E79A0FAF72BA00D8C118 /* tokenauth.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = tokenauth.h; path = ../server/tokenauth.h; sourceTree = SOURCE_ROOT; }; + C1D4E79B0FAF72BA00D8C118 /* tokenauth.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = tokenauth.cpp; path = ../server/tokenauth.cpp; sourceTree = SOURCE_ROOT; }; + C1D4E79C0FAF72BA00D8C118 /* room.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = room.h; path = ../server/room.h; sourceTree = SOURCE_ROOT; }; + C1D4E79D0FAF72BA00D8C118 /* room.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = room.cpp; path = ../server/room.cpp; sourceTree = SOURCE_ROOT; }; + C1D4E7DC0FAF90E500D8C118 /* comm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = comm.cpp; sourceTree = SOURCE_ROOT; }; + C1D4E90C0FB359E000D8C118 /* chatcontroller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = chatcontroller.h; sourceTree = SOURCE_ROOT; }; + C1DD179F0EC279A500D941E8 /* layerdib.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = layerdib.cpp; path = iphone/layerdib.cpp; sourceTree = ""; }; + C1DD17A00EC279A500D941E8 /* layerdib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = layerdib.h; path = iphone/layerdib.h; sourceTree = ""; }; + C1DD17A20EC279A500D941E8 /* layermap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = layermap.h; path = iphone/layermap.h; sourceTree = ""; }; + C1DD17A30EC279A500D941E8 /* layertile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = layertile.h; path = iphone/layertile.h; sourceTree = ""; }; + C1DD17A40EC279A500D941E8 /* layerview.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = layerview.h; path = iphone/layerview.h; sourceTree = ""; }; + C1DD17A50EC279A500D941E8 /* layerview.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = layerview.mm; path = iphone/layerview.mm; sourceTree = ""; }; + C1DD18060EC3756A00D941E8 /* layermap.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = layermap.mm; path = iphone/layermap.mm; sourceTree = ""; }; + C1DE48271029254C00DB7394 /* chooseserverform.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = chooseserverform.cpp; sourceTree = SOURCE_ROOT; }; + C1DE48281029254C00DB7394 /* chooseserverform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = chooseserverform.h; sourceTree = SOURCE_ROOT; }; + C1DF859D10026655007A26BA /* NavBackSmall.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = NavBackSmall.png; path = iphone/NavBackSmall.png; sourceTree = ""; }; + C1DF859E10026655007A26BA /* NavForwardSmall.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = NavForwardSmall.png; path = iphone/NavForwardSmall.png; sourceTree = ""; }; + C1E3BC640FD998B100E258F3 /* chatter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = chatter.cpp; sourceTree = SOURCE_ROOT; }; + C1E3BC650FD998B100E258F3 /* chatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = chatter.h; sourceTree = SOURCE_ROOT; }; + C1E56FD30DBFE589005DE321 /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = ../../../../iPhoneOS.platform/Developer/SDKs/iPhoneOS2.0.sdk/System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; }; + C1E570AB0DBFEAA7005DE321 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; + C1EC87C20DDE4A7E00B595FD /* htdata832.pdb */ = {isa = PBXFileReference; lastKnownFileType = text; path = htdata832.pdb; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 1D60588F0D05DD3D006BFB54 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */, + 1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */, + 1D3623EC0D0F72F000981E51 /* CoreGraphics.framework in Frameworks */, + C17D66E50D9E163900CB9F01 /* AudioToolbox.framework in Frameworks */, + C1E56FD40DBFE589005DE321 /* CoreAudio.framework in Frameworks */, + C1E570AC0DBFEAA7005DE321 /* QuartzCore.framework in Frameworks */, + C175F5C60DC0465000985493 /* OpenGLES.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 080E96DDFE201D6D7F000001 /* game */ = { + isa = PBXGroup; + children = ( + 5500324118B9936800466FDF /* xtransport.cpp */, + 5500324218B9936800466FDF /* xtransport.h */, + C1DE48271029254C00DB7394 /* chooseserverform.cpp */, + C1DE48281029254C00DB7394 /* chooseserverform.h */, + C1D4E0590F98F01D00D8C118 /* loginhandler.cpp */, + C1D4E05A0F98F01D00D8C118 /* loginhandler.h */, + C1D4E0570F98F01D00D8C118 /* loginform.cpp */, + C1D4E0580F98F01D00D8C118 /* loginform.h */, + C1D4E0CB0F9F8A6600D8C118 /* lobby.cpp */, + C1D4E0CC0F9F8A6600D8C118 /* lobby.h */, + C1D4E0550F98F01D00D8C118 /* lobbyform.cpp */, + C1D4E0560F98F01D00D8C118 /* lobbyform.h */, + C1D4E4AC0FA7B9AA00D8C118 /* createroomform.cpp */, + C1D4E4AD0FA7B9AA00D8C118 /* createroomform.h */, + C1D4E03D0F8FCBEA00D8C118 /* roomform.cpp */, + C1D4E03E0F8FCBEA00D8C118 /* roomform.h */, + C1D4E0370F8FCBEA00D8C118 /* creategameform.cpp */, + C1D4E0380F8FCBEA00D8C118 /* creategameform.h */, + C1D4E6850FABB4A500D8C118 /* gameform.cpp */, + C1D4E6840FABB4A500D8C118 /* gameform.h */, + C1E3BC640FD998B100E258F3 /* chatter.cpp */, + C1E3BC650FD998B100E258F3 /* chatter.h */, + C1D4E90C0FB359E000D8C118 /* chatcontroller.h */, + C1D4E07D0F98F4DC00D8C118 /* simplerequest.cpp */, + C1D4E07E0F98F4DC00D8C118 /* simplerequest.h */, + C1D4E7DC0FAF90E500D8C118 /* comm.cpp */, + C1D4E03B0F8FCBEA00D8C118 /* picktransportform.cpp */, + C1D4E03C0F8FCBEA00D8C118 /* picktransportform.h */, + C17D66320D9E15BE00CB9F01 /* Multiplayer.cpp */, + 636035590F19AA7C00ABF8EB /* multiplayer.h */, + C13D87C70F0AF5A300E63C5D /* uploader.cpp */, + C13D87C80F0AF5A300E63C5D /* uploader.h */, + C13D85B70F07580200E63C5D /* refmap.h */, + C13D85B80F07580200E63C5D /* stateframe.cpp */, + C13D85B90F07580200E63C5D /* stateframe.h */, + C13D85BA0F07580200E63C5D /* statetracker.cpp */, + C13D85BB0F07580200E63C5D /* statetracker.h */, + C12D9D2C0ECA3F94006B5FD9 /* completemanager.cpp */, + C12D9D2D0ECA3F94006B5FD9 /* completemanager.h */, + C14753010EB6908B0059D367 /* dragrect.cpp */, + C14753020EB6908B0059D367 /* dragrect.h */, + C14753030EB6908B0059D367 /* vec2d.h */, + C1994E300EA14A2800A48088 /* progresscallback.h */, + C1994E310EA14A2800A48088 /* sprite.h */, + C139CFA30E8B352A005F4A4B /* drawscan.cpp */, + C194A0FF0E7F8CF000A654A1 /* httpindexloader.cpp */, + C194A1000E7F8CF000A654A1 /* httpindexloader.h */, + C194A1010E7F8CF000A654A1 /* httppackinfomanager.cpp */, + C194A1020E7F8CF000A654A1 /* httppackinfomanager.h */, + C194A1030E7F8CF000A654A1 /* httppackmanager.cpp */, + C194A1040E7F8CF000A654A1 /* httppackmanager.h */, + C1949CF70E7874C200A654A1 /* downloadbox.cpp */, + C1370C190E71CC7600F9F923 /* serviceurls.cpp */, + C1370C1A0E71CC7600F9F923 /* serviceurls.h */, + C1370AE60E719A1400F9F923 /* dlmissionpack.cpp */, + C1370A2E0E70831E00F9F923 /* map.cpp */, + C1370A2F0E70831E00F9F923 /* map.h */, + C13709510E6D9E3E00F9F923 /* httprequest.cpp */, + C13709520E6D9E3E00F9F923 /* httprequest.h */, + C13709530E6D9E3E00F9F923 /* httpservice.h */, + C1B3F5E80E671C0600D36F66 /* selectmission.cpp */, + C1B3F5340E65D6C100D36F66 /* missionlist.cpp */, + C1B3F5350E65D6C100D36F66 /* missionlist.h */, + C1077A320E31CAA3003DA7D6 /* mempdbreader.cpp */, + C1077A330E31CAA3003DA7D6 /* mempdbreader.h */, + C17D660A0D9E15BE00CB9F01 /* Andy.cpp */, + C17D660B0D9E15BE00CB9F01 /* Animation.cpp */, + C17D660C0D9E15BE00CB9F01 /* Artillery.cpp */, + C17D660D0D9E15BE00CB9F01 /* basictypes.h */, + C17D660E0D9E15BE00CB9F01 /* bitmap.cpp */, + C17D660F0D9E15BE00CB9F01 /* Builder.cpp */, + C17D66100D9E15BE00CB9F01 /* BuildMgr.cpp */, + C17D66110D9E15BE00CB9F01 /* cachemgr.cpp */, + C17D66130D9E15BE00CB9F01 /* compression.cpp */, + C17D66140D9E15BE00CB9F01 /* CutScene.cpp */, + C17D66150D9E15BE00CB9F01 /* drm.cpp */, + C17D66160D9E15BE00CB9F01 /* Ecom.cpp */, + C17D66170D9E15BE00CB9F01 /* event.cpp */, + C17D66180D9E15BE00CB9F01 /* fogmap.cpp */, + C1ABDCFA0DC7C19E000A5B75 /* flickscroller.cpp */, + C17D66190D9E15BE00CB9F01 /* font.cpp */, + C17D661A0D9E15BE00CB9F01 /* form.cpp */, + C17D661B0D9E15BE00CB9F01 /* formmgr.cpp */, + C17D661C0D9E15BE00CB9F01 /* game.cpp */, + C17D661D0D9E15BE00CB9F01 /* GameObjects.cpp */, + C17D661E0D9E15BE00CB9F01 /* GameOptions.cpp */, + C17D661F0D9E15BE00CB9F01 /* Headquarters.cpp */, + C17D66200D9E15BE00CB9F01 /* Help.cpp */, + C17D66210D9E15BE00CB9F01 /* HRC.cpp */, + C17D66220D9E15BE00CB9F01 /* ht.h */, + C17D66240D9E15BE00CB9F01 /* InputUI.cpp */, + C17D66250D9E15BE00CB9F01 /* Level.cpp */, + C17D66260D9E15BE00CB9F01 /* loadsave.cpp */, + C17D66270D9E15BE00CB9F01 /* LRInfantry.cpp */, + C17D66280D9E15BE00CB9F01 /* memmgr.cpp */, + C17D66290D9E15BE00CB9F01 /* Miner.cpp */, + C17D662A0D9E15BE00CB9F01 /* misc.cpp */, + C17D662B0D9E15BE00CB9F01 /* misccontrols.cpp */, + C17D662C0D9E15BE00CB9F01 /* miscgraphics.cpp */, + C17D662D0D9E15BE00CB9F01 /* mixer.cpp */, + C17D662E0D9E15BE00CB9F01 /* mixer.h */, + C17D662F0D9E15BE00CB9F01 /* MobileBuilder.cpp */, + C17D66300D9E15BE00CB9F01 /* MobileHQ.cpp */, + C17D66310D9E15BE00CB9F01 /* MobileUnit.cpp */, + C17D66340D9E15BE00CB9F01 /* Overmind.cpp */, + C17D66370D9E15BE00CB9F01 /* Player.cpp */, + C17D66380D9E15BE00CB9F01 /* Processor.cpp */, + C17D66390D9E15BE00CB9F01 /* Radar.cpp */, + C17D663A0D9E15BE00CB9F01 /* RawBitmap.cpp */, + C17D663B0D9E15BE00CB9F01 /* Reactor.cpp */, + C17D663C0D9E15BE00CB9F01 /* Replicator.cpp */, + C17D663D0D9E15BE00CB9F01 /* res.h */, + C17D663E0D9E15BE00CB9F01 /* Research.cpp */, + C17D663F0D9E15BE00CB9F01 /* rip.cpp */, + C1C708DE0DBD6C060062B73B /* rip.h */, + C17D66400D9E15BE00CB9F01 /* Shell.cpp */, + C17D66410D9E15BE00CB9F01 /* SimUI.cpp */, + C124299F0E553D9500DDDFD1 /* stylushandler.cpp */, + C12429F00E5727BF00DDDFD1 /* fingerhandler.cpp */, + C17D66420D9E15BE00CB9F01 /* Simulation.cpp */, + C17D66450D9E15BE00CB9F01 /* sounddevice.h */, + C17D66460D9E15BE00CB9F01 /* soundeffects.h */, + C17D66470D9E15BE00CB9F01 /* soundmgr.cpp */, + C17D66480D9E15BE00CB9F01 /* SpInfantry.cpp */, + C17D66490D9E15BE00CB9F01 /* SRInfantry.cpp */, + C17D664A0D9E15BE00CB9F01 /* StateMachine.cpp */, + C17D664B0D9E15BE00CB9F01 /* strings.h */, + C17D664C0D9E15BE00CB9F01 /* stringtable.cpp */, + C17D664D0D9E15BE00CB9F01 /* Struct.cpp */, + C17D664E0D9E15BE00CB9F01 /* Tank.cpp */, + C17D664F0D9E15BE00CB9F01 /* tbitmap.cpp */, + C17D66500D9E15BE00CB9F01 /* terrainmap.cpp */, + C17D66510D9E15BE00CB9F01 /* tests.cpp */, + C17D66520D9E15BE00CB9F01 /* thunks.cpp */, + C17D66530D9E15BE00CB9F01 /* tilemap.cpp */, + C17D66540D9E15BE00CB9F01 /* timer.cpp */, + C17D66550D9E15BE00CB9F01 /* Tower.cpp */, + C17D66560D9E15BE00CB9F01 /* TriggerActions.cpp */, + C17D66570D9E15BE00CB9F01 /* TriggerConditions.cpp */, + C17D66580D9E15BE00CB9F01 /* triggermgr.cpp */, + C17D66590D9E15BE00CB9F01 /* Unit.cpp */, + C17D665A0D9E15BE00CB9F01 /* UnitGroupMgr.cpp */, + C17D665B0D9E15BE00CB9F01 /* updatemap.cpp */, + C17D665C0D9E15BE00CB9F01 /* VTS.cpp */, + C17D665D0D9E15BE00CB9F01 /* Warehouse.cpp */, + C17D66080D9E15A300CB9F01 /* alertcontrol.cpp */, + ); + name = game; + path = Classes; + sourceTree = ""; + }; + 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = { + isa = PBXGroup; + children = ( + C175F5C50DC0465000985493 /* OpenGLES.framework */, + C1E570AB0DBFEAA7005DE321 /* QuartzCore.framework */, + C1E56FD30DBFE589005DE321 /* CoreAudio.framework */, + 1D3623EB0D0F72F000981E51 /* CoreGraphics.framework */, + 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */, + 1D30AB110D05D00D00671497 /* Foundation.framework */, + ); + name = "Linked Frameworks"; + sourceTree = ""; + }; + 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = "Other Frameworks"; + sourceTree = ""; + }; + 19C28FACFE9D520D11CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 1D6058910D05DD3D006BFB54 /* wi.app */, + ); + name = Products; + sourceTree = ""; + }; + 29B97314FDCFA39411CA2CEA /* CustomTemplate */ = { + isa = PBXGroup; + children = ( + C184248611EE4EFA007BDB3D /* Entitlements.plist */, + 080E96DDFE201D6D7F000001 /* game */, + C17D66A80D9E15CA00CB9F01 /* iphone */, + C176855B0E0C35560058E49D /* base */, + C176855A0E0C354A0058E49D /* mpshared */, + C1370A020E707BAB00F9F923 /* yajl */, + C1370B130E719C0400F9F923 /* server */, + 29B97317FDCFA39411CA2CEA /* Resources */, + 29B97323FDCFA39411CA2CEA /* Frameworks */, + 19C28FACFE9D520D11CA2CBB /* Products */, + C1D143970E45881F0025FA74 /* dist.plist */, + ); + name = CustomTemplate; + sourceTree = ""; + }; + 29B97317FDCFA39411CA2CEA /* Resources */ = { + isa = PBXGroup; + children = ( + C178D3F80EABD5AF0015E32D /* icon.png */, + C1EC87C20DDE4A7E00B595FD /* htdata832.pdb */, + C17D66CC0D9E162500CB9F01 /* htsfx.pdb */, + ); + name = Resources; + sourceTree = ""; + }; + 29B97323FDCFA39411CA2CEA /* Frameworks */ = { + isa = PBXGroup; + children = ( + C17D66E40D9E163900CB9F01 /* AudioToolbox.framework */, + 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */, + 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */, + ); + name = Frameworks; + sourceTree = ""; + }; + C1370A020E707BAB00F9F923 /* yajl */ = { + isa = PBXGroup; + children = ( + C1370A050E707BED00F9F923 /* wrapper */, + C1370A040E707BE300F9F923 /* src */, + C1370A030E707BDB00F9F923 /* api */, + ); + name = yajl; + sourceTree = ""; + }; + C1370A030E707BDB00F9F923 /* api */ = { + isa = PBXGroup; + children = ( + C1370A1A0E707C7F00F9F923 /* yajl_common.h */, + C1370A1B0E707C7F00F9F923 /* yajl_gen.h */, + C1370A1C0E707C7F00F9F923 /* yajl_parse.h */, + ); + name = api; + sourceTree = ""; + }; + C1370A040E707BE300F9F923 /* src */ = { + isa = PBXGroup; + children = ( + C1370A0C0E707C5700F9F923 /* yajl.c */, + C1370A0D0E707C5700F9F923 /* yajl_buf.c */, + C1370A0E0E707C5700F9F923 /* yajl_buf.h */, + C1370A0F0E707C5700F9F923 /* yajl_encode.c */, + C1370A100E707C5700F9F923 /* yajl_encode.h */, + C1370A110E707C5700F9F923 /* yajl_lex.c */, + C1370A120E707C5700F9F923 /* yajl_lex.h */, + C1370A130E707C5700F9F923 /* yajl_parser.c */, + C1370A140E707C5700F9F923 /* yajl_parser.h */, + ); + name = src; + sourceTree = ""; + }; + C1370A050E707BED00F9F923 /* wrapper */ = { + isa = PBXGroup; + children = ( + C1370A060E707C1E00F9F923 /* jsonbuilder.cpp */, + C1370A070E707C1E00F9F923 /* jsonbuilder.h */, + C1370A080E707C1E00F9F923 /* jsontypes.cpp */, + C1370A090E707C1E00F9F923 /* jsontypes.h */, + ); + name = wrapper; + sourceTree = ""; + }; + C1370B130E719C0400F9F923 /* server */ = { + isa = PBXGroup; + children = ( + C1D4E79A0FAF72BA00D8C118 /* tokenauth.h */, + C1D4E79B0FAF72BA00D8C118 /* tokenauth.cpp */, + C1D4E79C0FAF72BA00D8C118 /* room.h */, + C1D4E79D0FAF72BA00D8C118 /* room.cpp */, + C1370B140E719C4200F9F923 /* endpoint.cpp */, + C1370B150E719C4200F9F923 /* endpoint.h */, + C1370B160E719C4200F9F923 /* game.cpp */, + C1370B170E719C4200F9F923 /* game.h */, + C1370B180E719C4200F9F923 /* ids.h */, + C1370B190E719C4200F9F923 /* levelinfo.cpp */, + C1370B1A0E719C4200F9F923 /* levelinfo.h */, + C1370B1B0E719C4200F9F923 /* levelinfocache.cpp */, + C1370B1C0E719C4200F9F923 /* levelinfocache.h */, + C1370B1D0E719C4200F9F923 /* lobby.cpp */, + C1370B1E0E719C4200F9F923 /* lobby.h */, + C1370B1F0E719C4200F9F923 /* main.cpp */, + C1370B200E719C4200F9F923 /* ncpackfile.cpp */, + C1370B210E719C4200F9F923 /* ncpackfile.h */, + C1370B220E719C4200F9F923 /* ncpdbreader.cpp */, + C1370B230E719C4200F9F923 /* ncpdbreader.h */, + C1370B240E719C4200F9F923 /* player.cpp */, + C1370B250E719C4200F9F923 /* player.h */, + C1370B260E719C4200F9F923 /* playermgr.cpp */, + C1370B270E719C4200F9F923 /* playermgr.h */, + C1370B280E719C4200F9F923 /* server.cpp */, + C1370B290E719C4200F9F923 /* server.h */, + ); + name = server; + sourceTree = ""; + }; + C176855A0E0C354A0058E49D /* mpshared */ = { + isa = PBXGroup; + children = ( + C12DA1F90ECE7A9E006B5FD9 /* xmsglog.cpp */, + C12DA1FA0ECE7A9E006B5FD9 /* xmsglog.h */, + C194A1BF0E80300C00A654A1 /* constants.h */, + C194A1C00E80300C00A654A1 /* indexloader.cpp */, + C194A1C10E80300C00A654A1 /* indexloader.h */, + C194A1C20E80300C00A654A1 /* packinfomanager.cpp */, + C194A1C30E80300C00A654A1 /* packmanager.cpp */, + C1077A130E31C4B5003DA7D6 /* decompress.cpp */, + C1077A140E31C4B5003DA7D6 /* decompress.h */, + C1077A150E31C4B5003DA7D6 /* enum.h */, + C1077A160E31C4B5003DA7D6 /* ini.cpp */, + C1077A170E31C4B5003DA7D6 /* ini.h */, + C1077A180E31C4B5003DA7D6 /* packfile.cpp */, + C1077A190E31C4B5003DA7D6 /* packfile.h */, + C1077A1A0E31C4B5003DA7D6 /* pdbreader.h */, + C1077A1B0E31C4B5003DA7D6 /* side.h */, + C10777AB0E1D9FE5003DA7D6 /* messages.cpp */, + C10777AC0E1D9FE5003DA7D6 /* messages.h */, + C17685700E0C36100058E49D /* misc.cpp */, + C17685710E0C36100058E49D /* misc.h */, + C17685720E0C36100058E49D /* mpht.h */, + C17685730E0C36100058E49D /* netmessage.cpp */, + C17685740E0C36100058E49D /* netmessage.h */, + C17685760E0C36100058E49D /* xmsg.h */, + C17685B60E15CB180058E49D /* xpump.cpp */, + C17685B70E15CB180058E49D /* xpump.h */, + ); + name = mpshared; + sourceTree = ""; + }; + C176855B0E0C35560058E49D /* base */ = { + isa = PBXGroup; + children = ( + C11FDC651033682E0053CD83 /* eventer.cpp */, + C11FDC661033682E0053CD83 /* eventer.h */, + C11FDC671033682E0053CD83 /* selectserver.cpp */, + C11FDC681033682E0053CD83 /* selectserver.h */, + C1A1C2050FE9B2BD00042055 /* log.h */, + C1A1C2060FE9B2BD00042055 /* thread.cpp */, + C1A1C2070FE9B2BD00042055 /* thread.h */, + C1D4E0C60F9D220C00D8C118 /* base64.cpp */, + C1D4E0C70F9D220C00D8C118 /* base64.h */, + C1370AA40E7099A200F9F923 /* format.cpp */, + C1A1C2040FE9B2BD00042055 /* format.h */, + C1A111F40E416739007913B4 /* messagehandler.cpp */, + C1A111F50E416739007913B4 /* messagehandler.h */, + C1077CA90E3E6F5F003DA7D6 /* deletetracker.cpp */, + C1077CAA0E3E6F5F003DA7D6 /* deletetracker.h */, + C1077AA20E36A74C003DA7D6 /* tick.cpp */, + C1077AA30E36A74C003DA7D6 /* tick.h */, + C1077A4F0E330836003DA7D6 /* md5.h */, + C1077A500E330836003DA7D6 /* md5c.cpp */, + C1077A220E31C5A8003DA7D6 /* misc.h */, + C13F80A30FFE811E00F77047 /* misc.cpp */, + C176856E0E0C35A60058E49D /* bytebuffer.cpp */, + C176855C0E0C35820058E49D /* bytebuffer.h */, + C176855D0E0C35820058E49D /* criticalsection.h */, + C176855E0E0C35820058E49D /* dispatcher.h */, + C17685610E0C35820058E49D /* messagequeue.cpp */, + C17685620E0C35820058E49D /* messagequeue.h */, + C17685630E0C35820058E49D /* socket.cpp */, + C17685640E0C35820058E49D /* socket.h */, + C17685650E0C35820058E49D /* socketaddress.cpp */, + C17685660E0C35820058E49D /* socketaddress.h */, + C17685670E0C35820058E49D /* socketserver.cpp */, + C17685680E0C35820058E49D /* socketserver.h */, + ); + name = base; + sourceTree = ""; + }; + C17D66A80D9E15CA00CB9F01 /* iphone */ = { + isa = PBXGroup; + children = ( + C1456692101A9EF0006DCF81 /* inputcontroller.mm */, + C1456693101A9EF0006DCF81 /* inputcontroller.h */, + C1DF859D10026655007A26BA /* NavBackSmall.png */, + C1DF859E10026655007A26BA /* NavForwardSmall.png */, + C13F7C5A0FF5CAAD00F77047 /* webviewcontroller.mm */, + C13F7C5B0FF5CAAD00F77047 /* webviewcontroller.h */, + C19D450E0F745A9600283E95 /* chatcell.h */, + C19D450F0F745A9600283E95 /* chatcell.mm */, + C1603E1D0F69AAB200B19D51 /* chatviewcontroller.h */, + C1603E1E0F69AAB200B19D51 /* chatviewcontroller.mm */, + C1603E040F6984CB00B19D51 /* wiviewcontroller.h */, + C1603E050F6984CB00B19D51 /* wiviewcontroller.mm */, + C14752FE0EB6903A0059D367 /* selectionsprite.mm */, + C14752FF0EB6903A0059D367 /* selectionsprite.h */, + C158A66A0EA3E6F3007035AF /* spritemgradapter.h */, + C1994E2A0EA149D200A48088 /* iphoneanimsprite.h */, + C1994E2B0EA149D200A48088 /* iphoneanimsprite.mm */, + C13709560E6D9E6700F9F923 /* iphonehttprequest.h */, + C13709570E6D9E6700F9F923 /* iphonehttprequest.mm */, + C13709580E6D9E6700F9F923 /* iphonehttpservice.h */, + C13709590E6D9E6700F9F923 /* iphonehttpservice.mm */, + C1077A1F0E31C57F003DA7D6 /* iphonepackfile.cpp */, + C1077A200E31C57F003DA7D6 /* iphonepackfile.h */, + C17D66AE0D9E160100CB9F01 /* display.cpp */, + C17D66B10D9E160100CB9F01 /* host.cpp */, + C17D66B20D9E160100CB9F01 /* htplatform.h */, + C17D66B30D9E160100CB9F01 /* input.h */, + C17D66B40D9E160100CB9F01 /* iphone.h */, + C17D66B50D9E160100CB9F01 /* iphone.mm */, + C17D66B60D9E160100CB9F01 /* iphonesounddev.cpp */, + C17D66B90D9E160100CB9F01 /* main.cpp */, + C17D66BC0D9E160100CB9F01 /* savegame.cpp */, + C17D66BF0D9E160100CB9F01 /* transportmgr.cpp */, + C1DD17A50EC279A500D941E8 /* layerview.mm */, + C1DD17A40EC279A500D941E8 /* layerview.h */, + C1DD179F0EC279A500D941E8 /* layerdib.cpp */, + C1DD17A00EC279A500D941E8 /* layerdib.h */, + C1DD18060EC3756A00D941E8 /* layermap.mm */, + C1DD17A20EC279A500D941E8 /* layermap.h */, + C1DD17A30EC279A500D941E8 /* layertile.h */, + C17D66B80D9E160100CB9F01 /* cgview.mm */, + C17D66B70D9E160100CB9F01 /* cgview.h */, + C166A8CE0DE62F20000B45A0 /* cgdib.cpp */, + C1C706950DBD0F090062B73B /* cgdib.h */, + C18E0E030DBAC70700096237 /* oglview.mm */, + C18E0E020DBAC70700096237 /* oglview.h */, + C166A8C90DE5F13B000B45A0 /* oglview2.mm */, + C166A8C80DE5F13B000B45A0 /* oglview2.h */, + C166A8CC0DE62EF4000B45A0 /* ogldib.cpp */, + C1C706970DBD0F090062B73B /* ogldib.h */, + C1C709760DBDAF290062B73B /* wiview.mm */, + C1C709750DBDAF290062B73B /* wiview.h */, + ); + name = iphone; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 1D6058900D05DD3D006BFB54 /* wi */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "wi" */; + buildPhases = ( + 1D60588D0D05DD3D006BFB54 /* Resources */, + 1D60588E0D05DD3D006BFB54 /* Sources */, + 1D60588F0D05DD3D006BFB54 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = wi; + productName = wi; + productReference = 1D6058910D05DD3D006BFB54 /* wi.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 29B97313FDCFA39411CA2CEA /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0510; + TargetAttributes = { + 1D6058900D05DD3D006BFB54 = { + DevelopmentTeam = BTM58P9QZU; + }; + }; + }; + buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "wi" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 1; + knownRegions = ( + en, + ); + mainGroup = 29B97314FDCFA39411CA2CEA /* CustomTemplate */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 1D6058900D05DD3D006BFB54 /* wi */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 1D60588D0D05DD3D006BFB54 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C17D66D00D9E162500CB9F01 /* htsfx.pdb in Resources */, + C1EC87C30DDE4A7E00B595FD /* htdata832.pdb in Resources */, + C1D143980E45881F0025FA74 /* dist.plist in Resources */, + C178D3F90EABD5AF0015E32D /* icon.png in Resources */, + C1DF859F10026655007A26BA /* NavBackSmall.png in Resources */, + C1DF85A010026655007A26BA /* NavForwardSmall.png in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 1D60588E0D05DD3D006BFB54 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C17D66090D9E15A300CB9F01 /* alertcontrol.cpp in Sources */, + C17D665E0D9E15BE00CB9F01 /* Andy.cpp in Sources */, + C17D665F0D9E15BE00CB9F01 /* Animation.cpp in Sources */, + C17D66600D9E15BE00CB9F01 /* Artillery.cpp in Sources */, + C17D66610D9E15BE00CB9F01 /* bitmap.cpp in Sources */, + C17D66620D9E15BE00CB9F01 /* Builder.cpp in Sources */, + C17D66630D9E15BE00CB9F01 /* BuildMgr.cpp in Sources */, + C17D66640D9E15BE00CB9F01 /* cachemgr.cpp in Sources */, + C17D66660D9E15BE00CB9F01 /* compression.cpp in Sources */, + C17D66670D9E15BE00CB9F01 /* CutScene.cpp in Sources */, + C17D66680D9E15BE00CB9F01 /* drm.cpp in Sources */, + C17D66690D9E15BE00CB9F01 /* Ecom.cpp in Sources */, + C17D666A0D9E15BE00CB9F01 /* event.cpp in Sources */, + C17D666B0D9E15BE00CB9F01 /* fogmap.cpp in Sources */, + C17D666C0D9E15BE00CB9F01 /* font.cpp in Sources */, + C17D666D0D9E15BE00CB9F01 /* form.cpp in Sources */, + C17D666E0D9E15BE00CB9F01 /* formmgr.cpp in Sources */, + C17D666F0D9E15BE00CB9F01 /* game.cpp in Sources */, + C17D66700D9E15BE00CB9F01 /* GameObjects.cpp in Sources */, + C17D66710D9E15BE00CB9F01 /* GameOptions.cpp in Sources */, + C17D66720D9E15BE00CB9F01 /* Headquarters.cpp in Sources */, + C17D66730D9E15BE00CB9F01 /* Help.cpp in Sources */, + C17D66740D9E15BE00CB9F01 /* HRC.cpp in Sources */, + C17D66760D9E15BE00CB9F01 /* InputUI.cpp in Sources */, + C17D66770D9E15BE00CB9F01 /* Level.cpp in Sources */, + C17D66780D9E15BE00CB9F01 /* loadsave.cpp in Sources */, + C17D66790D9E15BE00CB9F01 /* LRInfantry.cpp in Sources */, + C17D667A0D9E15BE00CB9F01 /* memmgr.cpp in Sources */, + C17D667B0D9E15BE00CB9F01 /* Miner.cpp in Sources */, + C17D667C0D9E15BE00CB9F01 /* misc.cpp in Sources */, + C17D667D0D9E15BE00CB9F01 /* misccontrols.cpp in Sources */, + C17D667F0D9E15BE00CB9F01 /* mixer.cpp in Sources */, + C17D66800D9E15BE00CB9F01 /* MobileBuilder.cpp in Sources */, + C17D66810D9E15BE00CB9F01 /* MobileHQ.cpp in Sources */, + C17D66820D9E15BE00CB9F01 /* MobileUnit.cpp in Sources */, + C17D66840D9E15BE00CB9F01 /* Overmind.cpp in Sources */, + C17D66860D9E15BE00CB9F01 /* Player.cpp in Sources */, + C17D66870D9E15BE00CB9F01 /* Processor.cpp in Sources */, + C17D66880D9E15BE00CB9F01 /* Radar.cpp in Sources */, + C17D66890D9E15BE00CB9F01 /* RawBitmap.cpp in Sources */, + C17D668A0D9E15BE00CB9F01 /* Reactor.cpp in Sources */, + C17D668B0D9E15BE00CB9F01 /* Replicator.cpp in Sources */, + C17D668C0D9E15BE00CB9F01 /* Research.cpp in Sources */, + C17D668D0D9E15BE00CB9F01 /* rip.cpp in Sources */, + C17D668E0D9E15BE00CB9F01 /* Shell.cpp in Sources */, + C17D668F0D9E15BE00CB9F01 /* SimUI.cpp in Sources */, + C17D66900D9E15BE00CB9F01 /* Simulation.cpp in Sources */, + C17D66920D9E15BE00CB9F01 /* soundmgr.cpp in Sources */, + C17D66930D9E15BE00CB9F01 /* SpInfantry.cpp in Sources */, + C17D66940D9E15BE00CB9F01 /* SRInfantry.cpp in Sources */, + C17D66950D9E15BE00CB9F01 /* StateMachine.cpp in Sources */, + C17D66960D9E15BE00CB9F01 /* stringtable.cpp in Sources */, + C17D66970D9E15BE00CB9F01 /* Struct.cpp in Sources */, + C17D66980D9E15BE00CB9F01 /* Tank.cpp in Sources */, + C17D66990D9E15BE00CB9F01 /* tbitmap.cpp in Sources */, + C17D669A0D9E15BE00CB9F01 /* terrainmap.cpp in Sources */, + C17D669B0D9E15BE00CB9F01 /* tests.cpp in Sources */, + C17D669C0D9E15BE00CB9F01 /* thunks.cpp in Sources */, + C17D669D0D9E15BE00CB9F01 /* tilemap.cpp in Sources */, + C17D669E0D9E15BE00CB9F01 /* timer.cpp in Sources */, + C17D669F0D9E15BE00CB9F01 /* Tower.cpp in Sources */, + C17D66A00D9E15BE00CB9F01 /* TriggerActions.cpp in Sources */, + C17D66A10D9E15BE00CB9F01 /* TriggerConditions.cpp in Sources */, + C17D66A20D9E15BE00CB9F01 /* triggermgr.cpp in Sources */, + C17D66A30D9E15BE00CB9F01 /* Unit.cpp in Sources */, + C17D66A40D9E15BE00CB9F01 /* UnitGroupMgr.cpp in Sources */, + C17D66A50D9E15BE00CB9F01 /* updatemap.cpp in Sources */, + C17D66A60D9E15BE00CB9F01 /* VTS.cpp in Sources */, + C17D66A70D9E15BE00CB9F01 /* Warehouse.cpp in Sources */, + C17D66C00D9E160100CB9F01 /* display.cpp in Sources */, + C17D66C20D9E160100CB9F01 /* host.cpp in Sources */, + C17D66C30D9E160100CB9F01 /* iphone.mm in Sources */, + C17D66C40D9E160100CB9F01 /* iphonesounddev.cpp in Sources */, + C17D66C60D9E160100CB9F01 /* main.cpp in Sources */, + C17D66C80D9E160100CB9F01 /* savegame.cpp in Sources */, + C17D66CA0D9E160100CB9F01 /* transportmgr.cpp in Sources */, + C1C7067C0DBC03130062B73B /* cgview.mm in Sources */, + C1C709740DBDAC470062B73B /* oglview.mm in Sources */, + C1C709770DBDAF290062B73B /* wiview.mm in Sources */, + C1ABDCFB0DC7C19E000A5B75 /* flickscroller.cpp in Sources */, + C166A8CA0DE5F13B000B45A0 /* oglview2.mm in Sources */, + C166A8CD0DE62EF4000B45A0 /* ogldib.cpp in Sources */, + C166A8CF0DE62F20000B45A0 /* cgdib.cpp in Sources */, + C176856A0E0C35820058E49D /* messagequeue.cpp in Sources */, + C176856B0E0C35820058E49D /* socket.cpp in Sources */, + C176856C0E0C35820058E49D /* socketaddress.cpp in Sources */, + C176856D0E0C35820058E49D /* socketserver.cpp in Sources */, + C176856F0E0C35A60058E49D /* bytebuffer.cpp in Sources */, + C17685770E0C36100058E49D /* misc.cpp in Sources */, + C17685780E0C36100058E49D /* netmessage.cpp in Sources */, + C17685B80E15CB180058E49D /* xpump.cpp in Sources */, + C10777AD0E1D9FE5003DA7D6 /* messages.cpp in Sources */, + C1077A1C0E31C4B5003DA7D6 /* decompress.cpp in Sources */, + C1077A1D0E31C4B5003DA7D6 /* ini.cpp in Sources */, + C1077A1E0E31C4B5003DA7D6 /* packfile.cpp in Sources */, + C1077A210E31C57F003DA7D6 /* iphonepackfile.cpp in Sources */, + C1077A340E31CAA3003DA7D6 /* mempdbreader.cpp in Sources */, + C1077A510E330836003DA7D6 /* md5c.cpp in Sources */, + C1077AA40E36A74C003DA7D6 /* tick.cpp in Sources */, + C1077CAB0E3E6F5F003DA7D6 /* deletetracker.cpp in Sources */, + C1A111F60E416739007913B4 /* messagehandler.cpp in Sources */, + C12429A00E553D9500DDDFD1 /* stylushandler.cpp in Sources */, + C12429F10E5727BF00DDDFD1 /* fingerhandler.cpp in Sources */, + C1B3F5360E65D6C100D36F66 /* missionlist.cpp in Sources */, + C1B3F5EA0E671C0600D36F66 /* selectmission.cpp in Sources */, + C13709550E6D9E3E00F9F923 /* httprequest.cpp in Sources */, + C137095A0E6D9E6700F9F923 /* iphonehttprequest.mm in Sources */, + C137095B0E6D9E6700F9F923 /* iphonehttpservice.mm in Sources */, + C1370A0A0E707C1E00F9F923 /* jsonbuilder.cpp in Sources */, + C1370A0B0E707C1E00F9F923 /* jsontypes.cpp in Sources */, + C1370A150E707C5700F9F923 /* yajl.c in Sources */, + C1370A160E707C5700F9F923 /* yajl_buf.c in Sources */, + C1370A170E707C5700F9F923 /* yajl_encode.c in Sources */, + C1370A180E707C5700F9F923 /* yajl_lex.c in Sources */, + C1370A190E707C5700F9F923 /* yajl_parser.c in Sources */, + C1370A300E70831E00F9F923 /* map.cpp in Sources */, + C1370AA50E7099A200F9F923 /* format.cpp in Sources */, + C1370AE70E719A1400F9F923 /* dlmissionpack.cpp in Sources */, + C1370C1B0E71CC7600F9F923 /* serviceurls.cpp in Sources */, + C1949CF80E7874C200A654A1 /* downloadbox.cpp in Sources */, + C194A1050E7F8CF000A654A1 /* httpindexloader.cpp in Sources */, + C194A1060E7F8CF000A654A1 /* httppackinfomanager.cpp in Sources */, + C194A1070E7F8CF000A654A1 /* httppackmanager.cpp in Sources */, + C194A1C40E80300C00A654A1 /* indexloader.cpp in Sources */, + C194A1C50E80300C00A654A1 /* packinfomanager.cpp in Sources */, + C194A1C60E80300C00A654A1 /* packmanager.cpp in Sources */, + C139CFA40E8B352A005F4A4B /* drawscan.cpp in Sources */, + C1994E2E0EA149D200A48088 /* iphoneanimsprite.mm in Sources */, + C14753000EB6903A0059D367 /* selectionsprite.mm in Sources */, + C14753040EB6908B0059D367 /* dragrect.cpp in Sources */, + C1DD17A60EC279A500D941E8 /* layerdib.cpp in Sources */, + C1DD17A80EC279A500D941E8 /* layerview.mm in Sources */, + C1DD18070EC3756A00D941E8 /* layermap.mm in Sources */, + C12D9D2E0ECA3F94006B5FD9 /* completemanager.cpp in Sources */, + C12DA1FB0ECE7A9E006B5FD9 /* xmsglog.cpp in Sources */, + C13D85BC0F07580200E63C5D /* stateframe.cpp in Sources */, + C13D85BD0F07580200E63C5D /* statetracker.cpp in Sources */, + C13D87C90F0AF5A300E63C5D /* uploader.cpp in Sources */, + C1C0F2EE0F3D028F00DDA646 /* Multiplayer.cpp in Sources */, + C1603E060F6984CB00B19D51 /* wiviewcontroller.mm in Sources */, + C1603E200F69AAB200B19D51 /* chatviewcontroller.mm in Sources */, + C19D45100F745A9600283E95 /* chatcell.mm in Sources */, + C1D4E03F0F8FCBEA00D8C118 /* creategameform.cpp in Sources */, + C1D4E0410F8FCBEA00D8C118 /* picktransportform.cpp in Sources */, + C1D4E0420F8FCBEA00D8C118 /* roomform.cpp in Sources */, + C1D4E05B0F98F01D00D8C118 /* lobbyform.cpp in Sources */, + C1D4E05C0F98F01D00D8C118 /* loginform.cpp in Sources */, + C1D4E05D0F98F01D00D8C118 /* loginhandler.cpp in Sources */, + C1D4E07F0F98F4DC00D8C118 /* simplerequest.cpp in Sources */, + C1D4E0C80F9D220C00D8C118 /* base64.cpp in Sources */, + C1D4E0CD0F9F8A6600D8C118 /* lobby.cpp in Sources */, + C1D4E4AE0FA7B9AA00D8C118 /* createroomform.cpp in Sources */, + C1D4E6860FABB4A500D8C118 /* gameform.cpp in Sources */, + C1D4E7DD0FAF90E500D8C118 /* comm.cpp in Sources */, + C1E3BC660FD998B100E258F3 /* chatter.cpp in Sources */, + C1A1C2080FE9B2BD00042055 /* thread.cpp in Sources */, + C13F7C5C0FF5CAAD00F77047 /* webviewcontroller.mm in Sources */, + C13F80A40FFE811E00F77047 /* misc.cpp in Sources */, + C1456694101A9EF0006DCF81 /* inputcontroller.mm in Sources */, + C1DE48291029254C00DB7394 /* chooseserverform.cpp in Sources */, + C11FDC691033682E0053CD83 /* eventer.cpp in Sources */, + C11FDC6A1033682E0053CD83 /* selectserver.cpp in Sources */, + 5500324318B9936800466FDF /* xtransport.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 1D6058940D05DD3E006BFB54 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_LINK_OBJC_RUNTIME = NO; + CODE_SIGN_ENTITLEMENTS = Entitlements.plist; + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + FRAMEWORK_SEARCH_PATHS = ""; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = NO; + GCC_PREFIX_HEADER = ""; + GCC_VERSION = ""; + INFOPLIST_FILE = iphone/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 4.3; + ONLY_ACTIVE_ARCH = NO; + OTHER_CFLAGS = "-Wno-write-strings"; + OTHER_CPLUSPLUSFLAGS = ( + "$(OTHER_CFLAGS)", + "-DIPHONE", + "-DDEBUG", + "-DDEBUG_LOGGING", + "-DDEV_BUILD", + "-DMULTIPLAYER", + "-DTRACKSTATE", + ); + PREBINDING = NO; + PRODUCT_NAME = wi; + PROVISIONING_PROFILE = "E522801F-D4E0-499E-9568-7FF0F4B07D52"; + "PROVISIONING_PROFILE[sdk=iphoneos*]" = "E522801F-D4E0-499E-9568-7FF0F4B07D52"; + SDKROOT = iphoneos; + VALID_ARCHS = armv7; + }; + name = Debug; + }; + 1D6058950D05DD3E006BFB54 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_LINK_OBJC_RUNTIME = NO; + CODE_SIGN_ENTITLEMENTS = Entitlements.plist; + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + FRAMEWORK_SEARCH_PATHS = ""; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_PRECOMPILE_PREFIX_HEADER = NO; + GCC_PREFIX_HEADER = ""; + GCC_WARN_UNUSED_VARIABLE = NO; + INFOPLIST_FILE = iphone/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 4.3; + ONLY_ACTIVE_ARCH = NO; + OTHER_CFLAGS = "-Wno-write-strings"; + OTHER_CPLUSPLUSFLAGS = ( + "$(OTHER_CFLAGS)", + "-DIPHONE", + "-DMULTIPLAYER", + "-DBETA_TIMEOUT", + "-DTRACKSTATE", + ); + PREBINDING = NO; + PRODUCT_NAME = wi; + PROVISIONING_PROFILE = "E522801F-D4E0-499E-9568-7FF0F4B07D52"; + "PROVISIONING_PROFILE[sdk=iphoneos*]" = "E522801F-D4E0-499E-9568-7FF0F4B07D52"; + SDKROOT = iphoneos; + VALID_ARCHS = armv7; + }; + name = Release; + }; + C01FCF4F08A954540054247B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = YES; + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_ENABLE_SYMBOL_SEPARATION = NO; + GCC_VERSION = ""; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = NO; + INFOPLIST_EXPAND_BUILD_SETTINGS = YES; + INFOPLIST_FILE = ""; + ONLY_ACTIVE_ARCH = YES; + PREBINDING = NO; + PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = NO; + SDKROOT = iphoneos; + USER_HEADER_SEARCH_PATHS = ".. ../inc iphone"; + }; + name = Debug; + }; + C01FCF5008A954540054247B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_ENABLE_SYMBOL_SEPARATION = NO; + GCC_VERSION = ""; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = NO; + INFOPLIST_EXPAND_BUILD_SETTINGS = YES; + INFOPLIST_FILE = ""; + PREBINDING = NO; + SDKROOT = iphoneos; + USER_HEADER_SEARCH_PATHS = ".. ../inc iphone"; + }; + name = Release; + }; + C13CB9050E456DB300820077 /* Distribution */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = YES; + CODE_SIGN_ENTITLEMENTS = dist.plist; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_ENABLE_SYMBOL_SEPARATION = NO; + GCC_OPTIMIZATION_LEVEL = s; + GCC_THUMB_SUPPORT = NO; + GCC_VERSION = ""; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = NO; + INFOPLIST_EXPAND_BUILD_SETTINGS = YES; + INFOPLIST_FILE = ""; + PREBINDING = NO; + SDKROOT = iphoneos; + USER_HEADER_SEARCH_PATHS = ".. ../inc iphone"; + }; + name = Distribution; + }; + C13CB9060E456DB300820077 /* Distribution */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_LINK_OBJC_RUNTIME = NO; + CODE_SIGN_ENTITLEMENTS = Entitlements.plist; + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + FRAMEWORK_SEARCH_PATHS = ""; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_PRECOMPILE_PREFIX_HEADER = NO; + GCC_PREFIX_HEADER = ""; + GCC_THUMB_SUPPORT = YES; + GCC_VERSION = ""; + GCC_WARN_UNUSED_VARIABLE = NO; + INFOPLIST_FILE = iphone/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 4.3; + ONLY_ACTIVE_ARCH = NO; + OTHER_CFLAGS = "-Wno-write-strings"; + OTHER_CPLUSPLUSFLAGS = ( + "$(OTHER_CFLAGS)", + "-DIPHONE", + "-DMULTIPLAYER", + "-DTRACKSTATE", + ); + PREBINDING = NO; + PRODUCT_NAME = wi; + PROVISIONING_PROFILE = "E522801F-D4E0-499E-9568-7FF0F4B07D52"; + "PROVISIONING_PROFILE[sdk=iphoneos*]" = "E522801F-D4E0-499E-9568-7FF0F4B07D52"; + SDKROOT = iphoneos; + VALID_ARCHS = armv7; + }; + name = Distribution; + }; + C19C0D540EE0724B00963860 /* Distribution-store */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = YES; + CODE_SIGN_ENTITLEMENTS = dist.plist; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_ENABLE_SYMBOL_SEPARATION = NO; + GCC_VERSION = ""; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = NO; + INFOPLIST_EXPAND_BUILD_SETTINGS = YES; + INFOPLIST_FILE = ""; + PREBINDING = NO; + SDKROOT = iphoneos; + USER_HEADER_SEARCH_PATHS = ".. ../inc iphone"; + }; + name = "Distribution-store"; + }; + C19C0D550EE0724B00963860 /* Distribution-store */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_LINK_OBJC_RUNTIME = NO; + CODE_SIGN_ENTITLEMENTS = Entitlements.plist; + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + FRAMEWORK_SEARCH_PATHS = ""; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_PRECOMPILE_PREFIX_HEADER = NO; + GCC_PREFIX_HEADER = ""; + GCC_VERSION = ""; + GCC_WARN_UNUSED_VARIABLE = NO; + INFOPLIST_FILE = iphone/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 4.3; + ONLY_ACTIVE_ARCH = NO; + OTHER_CFLAGS = "-Wno-write-strings"; + OTHER_CPLUSPLUSFLAGS = ( + "$(OTHER_CFLAGS)", + "-DIPHONE", + "-DMULTIPLAYER", + "-DTRACKSTATE", + ); + PREBINDING = NO; + PRODUCT_NAME = wi; + PROVISIONING_PROFILE = "E522801F-D4E0-499E-9568-7FF0F4B07D52"; + SDKROOT = iphoneos; + VALID_ARCHS = armv7; + }; + name = "Distribution-store"; + }; + C1C108120E4554AF00D8DCA0 /* AdHoc */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_ENABLE_SYMBOL_SEPARATION = NO; + GCC_VERSION = ""; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = NO; + INFOPLIST_EXPAND_BUILD_SETTINGS = YES; + INFOPLIST_FILE = ""; + PREBINDING = NO; + SDKROOT = iphoneos; + USER_HEADER_SEARCH_PATHS = ".. ../inc iphone"; + }; + name = AdHoc; + }; + C1C108130E4554AF00D8DCA0 /* AdHoc */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_LINK_OBJC_RUNTIME = NO; + CODE_SIGN_ENTITLEMENTS = Entitlements.plist; + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + FRAMEWORK_SEARCH_PATHS = ""; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_PRECOMPILE_PREFIX_HEADER = NO; + GCC_PREFIX_HEADER = ""; + GCC_WARN_UNUSED_VARIABLE = NO; + INFOPLIST_FILE = iphone/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 4.3; + ONLY_ACTIVE_ARCH = NO; + OTHER_CPLUSPLUSFLAGS = ( + "$(OTHER_CFLAGS)", + "-DIPHONE", + "-DDEV_BUILD", + ); + PREBINDING = NO; + PRODUCT_NAME = wi; + PROVISIONING_PROFILE = "E522801F-D4E0-499E-9568-7FF0F4B07D52"; + "PROVISIONING_PROFILE[sdk=iphoneos*]" = "E522801F-D4E0-499E-9568-7FF0F4B07D52"; + SDKROOT = iphoneos; + VALID_ARCHS = armv7; + }; + name = AdHoc; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "wi" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1D6058940D05DD3E006BFB54 /* Debug */, + 1D6058950D05DD3E006BFB54 /* Release */, + C13CB9060E456DB300820077 /* Distribution */, + C19C0D550EE0724B00963860 /* Distribution-store */, + C1C108130E4554AF00D8DCA0 /* AdHoc */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C01FCF4E08A954540054247B /* Build configuration list for PBXProject "wi" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C01FCF4F08A954540054247B /* Debug */, + C01FCF5008A954540054247B /* Release */, + C13CB9050E456DB300820077 /* Distribution */, + C19C0D540EE0724B00963860 /* Distribution-store */, + C1C108120E4554AF00D8DCA0 /* AdHoc */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; +} diff --git a/game/wi.xcodeproj/scottlu.pbxuser b/game/wi.xcodeproj/scottlu.pbxuser new file mode 100644 index 0000000..d96a1d0 --- /dev/null +++ b/game/wi.xcodeproj/scottlu.pbxuser @@ -0,0 +1,2577 @@ +// !$*UTF8*$! +{ + 1D6058900D05DD3D006BFB54 /* wi */ = { + activeExec = 0; + executables = ( + C1F1F5EC0D9E137800FF3191 /* wi */, + ); + }; + 29B97313FDCFA39411CA2CEA /* Project object */ = { + activeBuildConfigurationName = Distribution; + activeExecutable = C1F1F5EC0D9E137800FF3191 /* wi */; + activeSDKPreference = iphonesimulator4.0; + activeTarget = 1D6058900D05DD3D006BFB54 /* wi */; + addToTargets = ( + 1D6058900D05DD3D006BFB54 /* wi */, + ); + breakpoints = ( + ); + codeSenseManager = C1F1F5F70D9E137E00FF3191 /* Code sense */; + executables = ( + C1F1F5EC0D9E137800FF3191 /* wi */, + ); + expressions = ( + iMissionSelect, + ); + perUserDictionary = { + "PBXConfiguration.PBXBreakpointsDataSource.v1:1CA1AED706398EBD00589147" = { + PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; + PBXFileTableDataSourceColumnSortingKey = PBXBreakpointsDataSource_BreakpointID; + PBXFileTableDataSourceColumnWidthsKey = ( + 20, + 20, + 198, + 20, + 99, + 99, + 29, + 20, + ); + PBXFileTableDataSourceColumnsKey = ( + PBXBreakpointsDataSource_ActionID, + PBXBreakpointsDataSource_TypeID, + PBXBreakpointsDataSource_BreakpointID, + PBXBreakpointsDataSource_UseID, + PBXBreakpointsDataSource_LocationID, + PBXBreakpointsDataSource_ConditionID, + PBXBreakpointsDataSource_IgnoreCountID, + PBXBreakpointsDataSource_ContinueID, + ); + }; + "PBXConfiguration.PBXBreakpointsDataSource.v1:1CA23EDF0692099D00951B8B" = { + PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; + PBXFileTableDataSourceColumnSortingKey = PBXBreakpointsDataSource_BreakpointID; + PBXFileTableDataSourceColumnWidthsKey = ( + 20, + 20, + 289, + 20, + 188, + 188, + 148, + 20, + ); + PBXFileTableDataSourceColumnsKey = ( + PBXBreakpointsDataSource_ActionID, + PBXBreakpointsDataSource_TypeID, + PBXBreakpointsDataSource_BreakpointID, + PBXBreakpointsDataSource_UseID, + PBXBreakpointsDataSource_LocationID, + PBXBreakpointsDataSource_ConditionID, + PBXBreakpointsDataSource_IgnoreCountID, + PBXBreakpointsDataSource_ContinueID, + ); + }; + PBXConfiguration.PBXFileTableDataSource3.PBXErrorsWarningsDataSource = { + PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; + PBXFileTableDataSourceColumnSortingKey = PBXErrorsWarningsDataSource_LocationID; + PBXFileTableDataSourceColumnWidthsKey = ( + 20, + 300, + 525, + ); + PBXFileTableDataSourceColumnsKey = ( + PBXErrorsWarningsDataSource_TypeID, + PBXErrorsWarningsDataSource_MessageID, + PBXErrorsWarningsDataSource_LocationID, + ); + }; + PBXConfiguration.PBXFileTableDataSource3.PBXExecutablesDataSource = { + PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; + PBXFileTableDataSourceColumnSortingKey = PBXExecutablesDataSource_NameID; + PBXFileTableDataSourceColumnWidthsKey = ( + 22, + 300, + 621, + ); + PBXFileTableDataSourceColumnsKey = ( + PBXExecutablesDataSource_ActiveFlagID, + PBXExecutablesDataSource_NameID, + PBXExecutablesDataSource_CommentsID, + ); + }; + PBXConfiguration.PBXFileTableDataSource3.PBXFileTableDataSource = { + PBXFileTableDataSourceColumnSortingDirectionKey = 1; + PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID; + PBXFileTableDataSourceColumnWidthsKey = ( + 20, + 733, + 20, + 48, + 43, + 43, + 20, + ); + PBXFileTableDataSourceColumnsKey = ( + PBXFileDataSource_FiletypeID, + PBXFileDataSource_Filename_ColumnID, + PBXFileDataSource_Built_ColumnID, + PBXFileDataSource_ObjectSize_ColumnID, + PBXFileDataSource_Errors_ColumnID, + PBXFileDataSource_Warnings_ColumnID, + PBXFileDataSource_Target_ColumnID, + ); + }; + PBXConfiguration.PBXTargetDataSource.PBXTargetDataSource = { + PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; + PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID; + PBXFileTableDataSourceColumnWidthsKey = ( + 20, + 693, + 60, + 20, + 48, + 43, + 43, + ); + PBXFileTableDataSourceColumnsKey = ( + PBXFileDataSource_FiletypeID, + PBXFileDataSource_Filename_ColumnID, + PBXTargetDataSource_PrimaryAttribute, + PBXFileDataSource_Built_ColumnID, + PBXFileDataSource_ObjectSize_ColumnID, + PBXFileDataSource_Errors_ColumnID, + PBXFileDataSource_Warnings_ColumnID, + ); + }; + PBXPerProjectTemplateStateSaveDate = 316241724; + PBXWorkspaceStateSaveDate = 316241724; + }; + perUserProjectItems = { + C10390C6107269AD00F4E6BD /* PBXTextBookmark */ = C10390C6107269AD00F4E6BD /* PBXTextBookmark */; + C10390DC10726A8800F4E6BD /* PBXTextBookmark */ = C10390DC10726A8800F4E6BD /* PBXTextBookmark */; + C104AA241095025E003D2E59 /* PBXTextBookmark */ = C104AA241095025E003D2E59 /* PBXTextBookmark */; + C1197B2812F4A4620060149F /* PlistBookmark */ = C1197B2812F4A4620060149F /* PlistBookmark */; + C1197B2E1304EBA40060149F /* PlistBookmark */ = C1197B2E1304EBA40060149F /* PlistBookmark */; + C11FDC8E10367ACB0053CD83 /* PBXTextBookmark */ = C11FDC8E10367ACB0053CD83 /* PBXTextBookmark */; + C12684A810659FAE00E3CBFE /* PBXTextBookmark */ = C12684A810659FAE00E3CBFE /* PBXTextBookmark */; + C12684CA1067F39500E3CBFE /* PBXTextBookmark */ = C12684CA1067F39500E3CBFE /* PBXTextBookmark */; + C12684E61067F7EC00E3CBFE /* PBXTextBookmark */ = C12684E61067F7EC00E3CBFE /* PBXTextBookmark */; + C12684E71067F7EC00E3CBFE /* PBXTextBookmark */ = C12684E71067F7EC00E3CBFE /* PBXTextBookmark */; + C126853010693E1100E3CBFE /* PBXTextBookmark */ = C126853010693E1100E3CBFE /* PBXTextBookmark */; + C126857A1069831000E3CBFE /* PBXTextBookmark */ = C126857A1069831000E3CBFE /* PBXTextBookmark */; + C12685BE106AAB9800E3CBFE /* PBXTextBookmark */ = C12685BE106AAB9800E3CBFE /* PBXTextBookmark */; + C12685DA106ACA7500E3CBFE /* PBXTextBookmark */ = C12685DA106ACA7500E3CBFE /* PBXTextBookmark */; + C1450481105C543F006C5B5F /* PBXTextBookmark */ = C1450481105C543F006C5B5F /* PBXTextBookmark */; + C1450482105C543F006C5B5F /* PBXTextBookmark */ = C1450482105C543F006C5B5F /* PBXTextBookmark */; + C14505A7106047AE006C5B5F /* PBXTextBookmark */ = C14505A7106047AE006C5B5F /* PBXTextBookmark */; + C145063510640A36006C5B5F /* PBXTextBookmark */ = C145063510640A36006C5B5F /* PBXTextBookmark */; + C145064410641680006C5B5F /* PBXTextBookmark */ = C145064410641680006C5B5F /* PBXTextBookmark */; + C145066F10644251006C5B5F /* PBXTextBookmark */ = C145066F10644251006C5B5F /* PBXTextBookmark */; + C145067010644251006C5B5F /* PBXTextBookmark */ = C145067010644251006C5B5F /* PBXTextBookmark */; + C145069D10646E7D006C5B5F /* PBXTextBookmark */ = C145069D10646E7D006C5B5F /* PBXTextBookmark */; + C14506AF1064715E006C5B5F /* PBXTextBookmark */ = C14506AF1064715E006C5B5F /* PBXTextBookmark */; + C157869F10446365006E0AC2 /* PBXTextBookmark */ = C157869F10446365006E0AC2 /* PBXTextBookmark */; + C15786B110446A4F006E0AC2 /* PBXTextBookmark */ = C15786B110446A4F006E0AC2 /* PBXTextBookmark */; + C15786D110449E05006E0AC2 /* PBXTextBookmark */ = C15786D110449E05006E0AC2 /* PBXTextBookmark */; + C16054881039F7CD00FBFC51 /* PBXTextBookmark */ = C16054881039F7CD00FBFC51 /* PBXTextBookmark */; + C16054A41039FBBC00FBFC51 /* PBXTextBookmark */ = C16054A41039FBBC00FBFC51 /* PBXTextBookmark */; + C16054CB103B2F6C00FBFC51 /* PBXTextBookmark */ = C16054CB103B2F6C00FBFC51 /* PBXTextBookmark */; + C16054CC103B2F6C00FBFC51 /* PBXTextBookmark */ = C16054CC103B2F6C00FBFC51 /* PBXTextBookmark */; + C16054DA103B2F7D00FBFC51 /* PBXTextBookmark */ = C16054DA103B2F7D00FBFC51 /* PBXTextBookmark */; + C16054E4103B317500FBFC51 /* PBXTextBookmark */ = C16054E4103B317500FBFC51 /* PBXTextBookmark */; + C16054FA103B3F4A00FBFC51 /* PBXTextBookmark */ = C16054FA103B3F4A00FBFC51 /* PBXTextBookmark */; + C1605501103B3F7900FBFC51 /* PBXTextBookmark */ = C1605501103B3F7900FBFC51 /* PBXTextBookmark */; + C1605587103CA6D900FBFC51 /* PBXTextBookmark */ = C1605587103CA6D900FBFC51 /* PBXTextBookmark */; + C160558C103CA70300FBFC51 /* PBXTextBookmark */ = C160558C103CA70300FBFC51 /* PBXTextBookmark */; + C16055A5103CB03F00FBFC51 /* PBXTextBookmark */ = C16055A5103CB03F00FBFC51 /* PBXTextBookmark */; + C16055C8103DEDD900FBFC51 /* PBXTextBookmark */ = C16055C8103DEDD900FBFC51 /* PBXTextBookmark */; + C1713BD311AC78B600EDF4E7 /* PBXTextBookmark */ = C1713BD311AC78B600EDF4E7 /* PBXTextBookmark */; + C18423F111ECE9B8007BDB3D /* PBXTextBookmark */ = C18423F111ECE9B8007BDB3D /* PBXTextBookmark */; + C184242B11EE30B5007BDB3D /* PBXTextBookmark */ = C184242B11EE30B5007BDB3D /* PBXTextBookmark */; + C184249311EE4F4C007BDB3D /* PBXTextBookmark */ = C184249311EE4F4C007BDB3D /* PBXTextBookmark */; + C1D9668E108511E20088A942 /* PBXTextBookmark */ = C1D9668E108511E20088A942 /* PBXTextBookmark */; + C1D966A81086D2080088A942 /* PBXTextBookmark */ = C1D966A81086D2080088A942 /* PBXTextBookmark */; + C1D966B91086D6C70088A942 /* PBXTextBookmark */ = C1D966B91086D6C70088A942 /* PBXTextBookmark */; + C1D966BA1086D6C70088A942 /* PBXTextBookmark */ = C1D966BA1086D6C70088A942 /* PBXTextBookmark */; + C1D966BB1086D6C70088A942 /* PBXTextBookmark */ = C1D966BB1086D6C70088A942 /* PBXTextBookmark */; + C1D966BE1086D6C70088A942 /* PBXTextBookmark */ = C1D966BE1086D6C70088A942 /* PBXTextBookmark */; + C1D966CC1086DBCE0088A942 /* PBXTextBookmark */ = C1D966CC1086DBCE0088A942 /* PBXTextBookmark */; + C1D966CF1086DC3F0088A942 /* PBXTextBookmark */ = C1D966CF1086DC3F0088A942 /* PBXTextBookmark */; + C1D966E01086DFF00088A942 /* PBXTextBookmark */ = C1D966E01086DFF00088A942 /* PBXTextBookmark */; + C1D966E11086DFF00088A942 /* PBXTextBookmark */ = C1D966E11086DFF00088A942 /* PBXTextBookmark */; + }; + sourceControlManager = C1F1F5F60D9E137E00FF3191 /* Source Control */; + userBuildSettings = { + }; + }; + 636035590F19AA7C00ABF8EB /* multiplayer.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {810, 260}}"; + sepNavSelRange = "{344, 0}"; + sepNavVisRange = "{0, 344}"; + }; + }; + C10390C6107269AD00F4E6BD /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C1D4E0570F98F01D00D8C118 /* loginform.cpp */; + name = "loginform.cpp: 281"; + rLen = 0; + rLoc = 7796; + rType = 0; + vrLen = 1247; + vrLoc = 5043; + }; + C10390DC10726A8800F4E6BD /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C13F7C5A0FF5CAAD00F77047 /* webviewcontroller.mm */; + name = "webviewcontroller.mm: 176"; + rLen = 0; + rLoc = 6251; + rType = 0; + vrLen = 2370; + vrLoc = 1488; + }; + C104AA241095025E003D2E59 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C1DE48281029254C00DB7394 /* chooseserverform.h */; + name = "chooseserverform.h: 50"; + rLen = 43; + rLoc = 1231; + rType = 0; + vrLen = 910; + vrLoc = 0; + }; + C10777AB0E1D9FE5003DA7D6 /* messages.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {911, 8358}}"; + sepNavSelRange = "{16802, 0}"; + sepNavVisRange = "{0, 1295}"; + }; + }; + C10777AC0E1D9FE5003DA7D6 /* messages.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {810, 4704}}"; + sepNavSelRange = "{5782, 47}"; + sepNavVisRange = "{4919, 1364}"; + }; + }; + C1077A130E31C4B5003DA7D6 /* decompress.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {810, 1512}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRange = "{1724, 650}"; + }; + }; + C1077A150E31C4B5003DA7D6 /* enum.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {897, 496}}"; + sepNavSelRange = "{175, 2}"; + sepNavVisRange = "{0, 439}"; + }; + }; + C1077A160E31C4B5003DA7D6 /* ini.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1115, 5656}}"; + sepNavSelRange = "{5892, 0}"; + sepNavVisRange = "{5710, 383}"; + }; + }; + C1077A170E31C4B5003DA7D6 /* ini.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {881, 1134}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRange = "{0, 412}"; + }; + }; + C1077A180E31C4B5003DA7D6 /* packfile.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1115, 8330}}"; + sepNavSelRange = "{6710, 0}"; + sepNavVisRange = "{7745, 304}"; + sepNavWindowFrame = "{{55, 379}, {1057, 794}}"; + }; + }; + C1077A1F0E31C57F003DA7D6 /* iphonepackfile.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1135, 658}}"; + sepNavSelRange = "{302, 0}"; + sepNavVisRange = "{531, 435}"; + }; + }; + C1077A220E31C5A8003DA7D6 /* misc.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1115, 700}}"; + sepNavSelRange = "{571, 55}"; + sepNavVisRange = "{263, 691}"; + }; + }; + C1077A320E31CAA3003DA7D6 /* mempdbreader.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1115, 3528}}"; + sepNavSelRange = "{3910, 0}"; + sepNavVisRange = "{4770, 436}"; + }; + }; + C1077A4F0E330836003DA7D6 /* md5.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {881, 644}}"; + sepNavSelRange = "{789, 34}"; + sepNavVisRange = "{310, 923}"; + }; + }; + C1077A500E330836003DA7D6 /* md5c.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1115, 3808}}"; + sepNavSelRange = "{1711, 0}"; + sepNavVisRange = "{1482, 373}"; + }; + }; + C1077AA20E36A74C003DA7D6 /* tick.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {897, 496}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRange = "{0, 424}"; + }; + }; + C1077AA30E36A74C003DA7D6 /* tick.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {881, 470}}"; + sepNavSelRange = "{230, 0}"; + sepNavVisRange = "{0, 230}"; + }; + }; + C1077CA90E3E6F5F003DA7D6 /* deletetracker.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {897, 686}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRange = "{0, 665}"; + }; + }; + C1197B2812F4A4620060149F /* PlistBookmark */ = { + isa = PlistBookmark; + fRef = C184248611EE4EFA007BDB3D /* Entitlements.plist */; + fallbackIsa = PBXBookmark; + isK = 0; + kPath = ( + ); + name = /Users/scottlu/wi/game/Entitlements.plist; + rLen = 0; + rLoc = 9223372036854775807; + }; + C1197B2E1304EBA40060149F /* PlistBookmark */ = { + isa = PlistBookmark; + fRef = C184248611EE4EFA007BDB3D /* Entitlements.plist */; + fallbackIsa = PBXBookmark; + isK = 0; + kPath = ( + ); + name = /Users/scottlu/wi/game/Entitlements.plist; + rLen = 0; + rLoc = 9223372036854775807; + }; + C11FDC671033682E0053CD83 /* selectserver.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1115, 3416}}"; + sepNavSelRange = "{3121, 0}"; + sepNavVisRange = "{2925, 483}"; + }; + }; + C11FDC8E10367ACB0053CD83 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C17D66B10D9E160100CB9F01 /* host.cpp */; + name = "host.cpp: 417"; + rLen = 0; + rLoc = 8797; + rType = 0; + vrLen = 801; + vrLoc = 7467; + }; + C124299F0E553D9500DDDFD1 /* stylushandler.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1104, 7140}}"; + sepNavSelRange = "{2253, 0}"; + sepNavVisRange = "{1969, 401}"; + sepNavWindowFrame = "{{78, 358}, {1057, 794}}"; + }; + }; + C12429F00E5727BF00DDDFD1 /* fingerhandler.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1115, 10220}}"; + sepNavSelRange = "{19069, 0}"; + sepNavVisRange = "{17441, 586}"; + sepNavWindowFrame = "{{55, 379}, {1057, 794}}"; + }; + }; + C12684A810659FAE00E3CBFE /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C13D87C70F0AF5A300E63C5D /* uploader.cpp */; + name = "uploader.cpp: 108"; + rLen = 0; + rLoc = 2667; + rType = 0; + vrLen = 1026; + vrLoc = 2023; + }; + C12684CA1067F39500E3CBFE /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C17D66220D9E15BE00CB9F01 /* ht.h */; + name = "ht.h: 5604"; + rLen = 0; + rLoc = 138857; + rType = 0; + vrLen = 567; + vrLoc = 138463; + }; + C12684E61067F7EC00E3CBFE /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C17D66370D9E15BE00CB9F01 /* Player.cpp */; + name = "Player.cpp: 107"; + rLen = 0; + rLoc = 2099; + rType = 0; + vrLen = 1072; + vrLoc = 1642; + }; + C12684E71067F7EC00E3CBFE /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C17D661C0D9E15BE00CB9F01 /* game.cpp */; + name = "game.cpp: 2126"; + rLen = 0; + rLoc = 52386; + rType = 0; + vrLen = 862; + vrLoc = 51835; + }; + C126853010693E1100E3CBFE /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C17D66410D9E15BE00CB9F01 /* SimUI.cpp */; + name = "SimUI.cpp: 2770"; + rLen = 0; + rLoc = 74972; + rType = 0; + vrLen = 1373; + vrLoc = 21539; + }; + C126857A1069831000E3CBFE /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C1D4E6850FABB4A500D8C118 /* gameform.cpp */; + name = "gameform.cpp: 608"; + rLen = 0; + rLoc = 17783; + rType = 0; + vrLen = 1357; + vrLoc = 5283; + }; + C12685BE106AAB9800E3CBFE /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C17D66560D9E15BE00CB9F01 /* TriggerActions.cpp */; + name = "TriggerActions.cpp: 1380"; + rLen = 0; + rLoc = 29702; + rType = 0; + vrLen = 1509; + vrLoc = 7545; + }; + C12685DA106ACA7500E3CBFE /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C1D4E0590F98F01D00D8C118 /* loginhandler.cpp */; + name = "loginhandler.cpp: 233"; + rLen = 0; + rLoc = 6261; + rType = 0; + vrLen = 1174; + vrLoc = 1312; + }; + C12D9D2C0ECA3F94006B5FD9 /* completemanager.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1115, 1764}}"; + sepNavSelRange = "{3947, 0}"; + sepNavVisRange = "{3098, 849}"; + }; + }; + C12D9D2D0ECA3F94006B5FD9 /* completemanager.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {881, 546}}"; + sepNavSelRange = "{936, 0}"; + sepNavVisRange = "{0, 885}"; + }; + }; + C12DA1F90ECE7A9E006B5FD9 /* xmsglog.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {810, 2044}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRange = "{0, 691}"; + }; + }; + C13709510E6D9E3E00F9F923 /* httprequest.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1103, 798}}"; + sepNavSelRange = "{254, 1}"; + sepNavVisRange = "{0, 674}"; + }; + }; + C13709520E6D9E3E00F9F923 /* httprequest.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1103, 532}}"; + sepNavSelRange = "{624, 0}"; + sepNavVisRange = "{0, 773}"; + }; + }; + C13709560E6D9E6700F9F923 /* iphonehttprequest.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {901, 952}}"; + sepNavSelRange = "{1567, 0}"; + sepNavVisRange = "{410, 1008}"; + }; + }; + C13709570E6D9E6700F9F923 /* iphonehttprequest.mm */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {881, 3290}}"; + sepNavSelRange = "{6271, 0}"; + sepNavVisRange = "{1281, 1067}"; + }; + }; + C13709580E6D9E6700F9F923 /* iphonehttpservice.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {897, 496}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRange = "{0, 450}"; + }; + }; + C13709590E6D9E6700F9F923 /* iphonehttpservice.mm */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1104, 420}}"; + sepNavSelRange = "{449, 0}"; + sepNavVisRange = "{245, 553}"; + }; + }; + C1370A060E707C1E00F9F923 /* jsonbuilder.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1115, 3206}}"; + sepNavSelRange = "{5393, 0}"; + sepNavVisRange = "{4723, 670}"; + }; + }; + C1370A070E707C1E00F9F923 /* jsonbuilder.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {881, 602}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRange = "{0, 688}"; + }; + }; + C1370A080E707C1E00F9F923 /* jsontypes.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1115, 2142}}"; + sepNavSelRange = "{993, 0}"; + sepNavVisRange = "{774, 499}"; + }; + }; + C1370A090E707C1E00F9F923 /* jsontypes.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {881, 1246}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRange = "{0, 595}"; + }; + }; + C1370A130E707C5700F9F923 /* yajl_parser.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {897, 6804}}"; + sepNavSelRange = "{780, 1}"; + sepNavVisRange = "{0, 1643}"; + }; + }; + C1370A2E0E70831E00F9F923 /* map.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1145, 644}}"; + sepNavSelRange = "{140, 0}"; + sepNavVisRange = "{0, 626}"; + }; + }; + C1370AA40E7099A200F9F923 /* format.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1337, 1260}}"; + sepNavSelRange = "{237, 0}"; + sepNavVisRange = "{167, 594}"; + }; + }; + C1370AE60E719A1400F9F923 /* dlmissionpack.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1115, 9856}}"; + sepNavSelRange = "{20417, 0}"; + sepNavVisRange = "{13433, 812}"; + sepNavWindowFrame = "{{55, 379}, {1057, 794}}"; + }; + }; + C1370B140E719C4200F9F923 /* endpoint.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {810, 7616}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRange = "{10290, 1009}"; + }; + }; + C1370B160E719C4200F9F923 /* game.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {810, 8414}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRange = "{0, 1180}"; + }; + }; + C1370B190E719C4200F9F923 /* levelinfo.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {897, 1064}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRange = "{1095, 963}"; + }; + }; + C1370B1B0E719C4200F9F923 /* levelinfocache.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {897, 1820}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRange = "{2568, 845}"; + }; + }; + C1370B1D0E719C4200F9F923 /* lobby.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {897, 1582}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRange = "{2331, 891}"; + }; + }; + C1370B1F0E719C4200F9F923 /* main.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {897, 1050}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRange = "{963, 837}"; + }; + }; + C1370B200E719C4200F9F923 /* ncpackfile.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {897, 630}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRange = "{31, 827}"; + }; + }; + C1370B220E719C4200F9F923 /* ncpdbreader.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {897, 2828}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRange = "{3626, 709}"; + }; + }; + C1370B240E719C4200F9F923 /* player.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {897, 1694}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRange = "{2137, 638}"; + }; + }; + C1370B260E719C4200F9F923 /* playermgr.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {897, 3864}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRange = "{6346, 634}"; + }; + }; + C1370B280E719C4200F9F923 /* server.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {897, 1162}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRange = "{1077, 953}"; + }; + }; + C1370C190E71CC7600F9F923 /* serviceurls.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1115, 546}}"; + sepNavSelRange = "{1593, 0}"; + sepNavVisRange = "{290, 1156}"; + }; + }; + C1370C1A0E71CC7600F9F923 /* serviceurls.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {810, 500}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRange = "{0, 307}"; + }; + }; + C139CFA30E8B352A005F4A4B /* drawscan.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1021, 2436}}"; + sepNavSelRange = "{3890, 0}"; + sepNavVisRange = "{21, 780}"; + }; + }; + C13D85B70F07580200E63C5D /* refmap.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {810, 616}}"; + sepNavSelRange = "{672, 0}"; + sepNavVisRange = "{42, 590}"; + }; + }; + C13D85B80F07580200E63C5D /* stateframe.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {810, 1624}}"; + sepNavSelRange = "{376, 51}"; + sepNavVisRange = "{2036, 922}"; + }; + }; + C13D85B90F07580200E63C5D /* stateframe.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {810, 952}}"; + sepNavSelRange = "{1249, 0}"; + sepNavVisRange = "{391, 850}"; + }; + }; + C13D85BA0F07580200E63C5D /* statetracker.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1115, 2016}}"; + sepNavSelRange = "{3811, 0}"; + sepNavVisRange = "{1425, 550}"; + }; + }; + C13D85BB0F07580200E63C5D /* statetracker.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1103, 616}}"; + sepNavSelRange = "{914, 0}"; + sepNavVisRange = "{384, 530}"; + }; + }; + C13D87C70F0AF5A300E63C5D /* uploader.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {911, 1848}}"; + sepNavSelRange = "{2667, 0}"; + sepNavVisRange = "{2023, 1026}"; + }; + }; + C13F7C5A0FF5CAAD00F77047 /* webviewcontroller.mm */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1145, 2226}}"; + sepNavSelRange = "{6251, 0}"; + sepNavVisRange = "{1824, 1536}"; + }; + }; + C13F7C5B0FF5CAAD00F77047 /* webviewcontroller.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1115, 266}}"; + sepNavSelRange = "{502, 0}"; + sepNavVisRange = "{32, 470}"; + }; + }; + C13F80A30FFE811E00F77047 /* misc.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {881, 854}}"; + sepNavSelRange = "{1689, 0}"; + sepNavVisRange = "{0, 984}"; + }; + }; + C1450481105C543F006C5B5F /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C17D661D0D9E15BE00CB9F01 /* GameObjects.cpp */; + name = "GameObjects.cpp: 1130"; + rLen = 0; + rLoc = 27671; + rType = 0; + vrLen = 1177; + vrLoc = 27035; + }; + C1450482105C543F006C5B5F /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C1463EB50DF0A47E0066B6AB /* xtransport.cpp */; + name = "xtransport.cpp: 211"; + rLen = 0; + rLoc = 6030; + rType = 0; + vrLen = 881; + vrLoc = 4082; + }; + C14505A7106047AE006C5B5F /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C1D4E6840FABB4A500D8C118 /* gameform.h */; + name = "gameform.h: 76"; + rLen = 0; + rLoc = 2148; + rType = 0; + vrLen = 1155; + vrLoc = 993; + }; + C145063510640A36006C5B5F /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C17D66540D9E15BE00CB9F01 /* timer.cpp */; + name = "timer.cpp: 246"; + rLen = 0; + rLoc = 5517; + rType = 0; + vrLen = 926; + vrLoc = 4591; + }; + C145064410641680006C5B5F /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C10777AB0E1D9FE5003DA7D6 /* messages.cpp */; + name = "messages.cpp: 596"; + rLen = 0; + rLoc = 16802; + rType = 0; + vrLen = 1295; + vrLoc = 0; + }; + C145066F10644251006C5B5F /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C1D4E0550F98F01D00D8C118 /* lobbyform.cpp */; + name = "lobbyform.cpp: 384"; + rLen = 0; + rLoc = 10633; + rType = 0; + vrLen = 1099; + vrLoc = 0; + }; + C145067010644251006C5B5F /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C1D4E0560F98F01D00D8C118 /* lobbyform.h */; + name = "lobbyform.h: 41"; + rLen = 70; + rLoc = 830; + rType = 0; + vrLen = 918; + vrLoc = 0; + }; + C145069D10646E7D006C5B5F /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C17D66320D9E15BE00CB9F01 /* Multiplayer.cpp */; + name = "Multiplayer.cpp: 69"; + rLen = 0; + rLoc = 1314; + rType = 0; + vrLen = 527; + vrLoc = 1054; + }; + C14506AF1064715E006C5B5F /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C1370A2E0E70831E00F9F923 /* map.cpp */; + name = "map.cpp: 9"; + rLen = 0; + rLoc = 140; + rType = 0; + vrLen = 626; + vrLoc = 0; + }; + C1456692101A9EF0006DCF81 /* inputcontroller.mm */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {881, 2114}}"; + sepNavSelRange = "{671, 21}"; + sepNavVisRange = "{194, 829}"; + }; + }; + C1463EB50DF0A47E0066B6AB /* xtransport.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1115, 13076}}"; + sepNavSelRange = "{6030, 0}"; + sepNavVisRange = "{4172, 716}"; + sepNavWindowFrame = "{{78, 58}, {1057, 794}}"; + }; + }; + C1463EB60DF0A47E0066B6AB /* xtransport.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {881, 2436}}"; + sepNavSelRange = "{6688, 0}"; + sepNavVisRange = "{3989, 1272}"; + }; + }; + C14752FE0EB6903A0059D367 /* selectionsprite.mm */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1454, 994}}"; + sepNavSelRange = "{1862, 0}"; + sepNavVisRange = "{1132, 683}"; + }; + }; + C14752FF0EB6903A0059D367 /* selectionsprite.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {901, 588}}"; + sepNavSelRange = "{1007, 0}"; + sepNavVisRange = "{0, 950}"; + }; + }; + C14753010EB6908B0059D367 /* dragrect.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {810, 4592}}"; + sepNavSelRange = "{6057, 1}"; + sepNavVisRange = "{7981, 712}"; + }; + }; + C14753020EB6908B0059D367 /* dragrect.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {901, 588}}"; + sepNavSelRange = "{312, 65}"; + sepNavVisRange = "{0, 1157}"; + }; + }; + C14753030EB6908B0059D367 /* vec2d.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {810, 1148}}"; + sepNavSelRange = "{1326, 0}"; + sepNavVisRange = "{742, 584}"; + }; + }; + C157869F10446365006E0AC2 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C1370AE60E719A1400F9F923 /* dlmissionpack.cpp */; + name = "dlmissionpack.cpp: 706"; + rLen = 0; + rLoc = 20417; + rType = 0; + vrLen = 1010; + vrLoc = 13253; + }; + C15786B110446A4F006E0AC2 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C1D4E03D0F8FCBEA00D8C118 /* roomform.cpp */; + name = "roomform.cpp: 647"; + rLen = 0; + rLoc = 18765; + rType = 0; + vrLen = 994; + vrLoc = 16328; + }; + C15786D110449E05006E0AC2 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C17D66380D9E15BE00CB9F01 /* Processor.cpp */; + name = "Processor.cpp: 374"; + rLen = 0; + rLoc = 10332; + rType = 0; + vrLen = 1038; + vrLoc = 6338; + }; + C158A66A0EA3E6F3007035AF /* spritemgradapter.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1021, 560}}"; + sepNavSelRange = "{937, 0}"; + sepNavVisRange = "{134, 803}"; + }; + }; + C1603E040F6984CB00B19D51 /* wiviewcontroller.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {881, 470}}"; + sepNavSelRange = "{1019, 0}"; + sepNavVisRange = "{0, 1019}"; + }; + }; + C1603E050F6984CB00B19D51 /* wiviewcontroller.mm */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {911, 1989}}"; + sepNavSelRange = "{4453, 0}"; + sepNavVisRange = "{1753, 1240}"; + sepNavWindowFrame = "{{78, 358}, {1057, 794}}"; + }; + }; + C1603E1D0F69AAB200B19D51 /* chatviewcontroller.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1115, 1218}}"; + sepNavSelRange = "{1412, 0}"; + sepNavVisRange = "{1182, 680}"; + }; + }; + C1603E1E0F69AAB200B19D51 /* chatviewcontroller.mm */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1145, 9412}}"; + sepNavSelRange = "{22030, 0}"; + sepNavVisRange = "{20571, 847}"; + }; + }; + C16054881039F7CD00FBFC51 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C1D4E07D0F98F4DC00D8C118 /* simplerequest.cpp */; + name = "simplerequest.cpp: 31"; + rLen = 0; + rLoc = 758; + rType = 0; + vrLen = 845; + vrLoc = 323; + }; + C16054A41039FBBC00FBFC51 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C1603E1D0F69AAB200B19D51 /* chatviewcontroller.h */; + name = "chatviewcontroller.h: 60"; + rLen = 0; + rLoc = 1412; + rType = 0; + vrLen = 881; + vrLoc = 981; + }; + C16054CB103B2F6C00FBFC51 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C17D66170D9E15BE00CB9F01 /* event.cpp */; + name = "event.cpp: 150"; + rLen = 0; + rLoc = 3596; + rType = 0; + vrLen = 1023; + vrLoc = 3246; + }; + C16054CC103B2F6C00FBFC51 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C17D661A0D9E15BE00CB9F01 /* form.cpp */; + name = "form.cpp: 602"; + rLen = 0; + rLoc = 13081; + rType = 0; + vrLen = 628; + vrLoc = 12821; + }; + C16054DA103B2F7D00FBFC51 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C17D662F0D9E15BE00CB9F01 /* MobileBuilder.cpp */; + name = "MobileBuilder.cpp: 51"; + rLen = 8; + rLoc = 1467; + rType = 0; + vrLen = 965; + vrLoc = 962; + }; + C16054E4103B317500FBFC51 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C17D662B0D9E15BE00CB9F01 /* misccontrols.cpp */; + name = "misccontrols.cpp: 1476"; + rLen = 0; + rLoc = 35551; + rType = 0; + vrLen = 586; + vrLoc = 35263; + }; + C16054FA103B3F4A00FBFC51 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C17D660F0D9E15BE00CB9F01 /* Builder.cpp */; + name = "Builder.cpp: 539"; + rLen = 0; + rLoc = 15048; + rType = 0; + vrLen = 877; + vrLoc = 14639; + }; + C1605501103B3F7900FBFC51 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C17D66590D9E15BE00CB9F01 /* Unit.cpp */; + name = "Unit.cpp: 670"; + rLen = 9; + rLoc = 19187; + rType = 0; + vrLen = 770; + vrLoc = 18689; + }; + C1605587103CA6D900FBFC51 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C12D9D2D0ECA3F94006B5FD9 /* completemanager.h */; + name = "completemanager.h: 39"; + rLen = 0; + rLoc = 936; + rType = 0; + vrLen = 885; + vrLoc = 0; + }; + C160558C103CA70300FBFC51 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C17685700E0C36100058E49D /* misc.cpp */; + name = "misc.cpp: 55"; + rLen = 0; + rLoc = 950; + rType = 0; + vrLen = 614; + vrLoc = 336; + }; + C16055A5103CB03F00FBFC51 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C12D9D2C0ECA3F94006B5FD9 /* completemanager.cpp */; + name = "completemanager.cpp: 149"; + rLen = 0; + rLoc = 4355; + rType = 0; + vrLen = 1208; + vrLoc = 2403; + }; + C16055C8103DEDD900FBFC51 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C11FDC671033682E0053CD83 /* selectserver.cpp */; + name = "selectserver.cpp: 123"; + rLen = 0; + rLoc = 3121; + rType = 0; + vrLen = 1003; + vrLoc = 2623; + }; + C166A8C80DE5F13B000B45A0 /* oglview2.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {942, 765}}"; + sepNavSelRange = "{549, 27}"; + sepNavVisRange = "{0, 1361}"; + }; + }; + C166A8C90DE5F13B000B45A0 /* oglview2.mm */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1145, 3146}}"; + sepNavSelRange = "{8612, 0}"; + sepNavVisRange = "{2224, 1384}"; + }; + }; + C166A8CC0DE62EF4000B45A0 /* ogldib.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {875, 1918}}"; + sepNavSelRange = "{4035, 0}"; + sepNavVisRange = "{0, 939}"; + }; + }; + C166A8CE0DE62F20000B45A0 /* cgdib.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1115, 4662}}"; + sepNavSelRange = "{152, 0}"; + sepNavVisRange = "{0, 580}"; + sepNavWindowFrame = "{{57, 419}, {1057, 578}}"; + }; + }; + C1713BD311AC78B600EDF4E7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C1DE48271029254C00DB7394 /* chooseserverform.cpp */; + name = "chooseserverform.cpp: 269"; + rLen = 0; + rLoc = 8215; + rType = 0; + vrLen = 954; + vrLoc = 2235; + }; + C176855C0E0C35820058E49D /* bytebuffer.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1357, 826}}"; + sepNavSelRange = "{304, 0}"; + sepNavVisRange = "{0, 575}"; + }; + }; + C176855D0E0C35820058E49D /* criticalsection.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1044, 672}}"; + sepNavSelRange = "{739, 0}"; + sepNavVisRange = "{546, 400}"; + }; + }; + C176855E0E0C35820058E49D /* dispatcher.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {897, 496}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRange = "{0, 584}"; + }; + }; + C17685610E0C35820058E49D /* messagequeue.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1115, 4102}}"; + sepNavSelRange = "{461, 0}"; + sepNavVisRange = "{1363, 547}"; + }; + }; + C17685620E0C35820058E49D /* messagequeue.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {897, 1246}}"; + sepNavSelRange = "{1010, 0}"; + sepNavVisRange = "{903, 1059}"; + }; + }; + C17685630E0C35820058E49D /* socket.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1409, 4802}}"; + sepNavSelRange = "{8310, 0}"; + sepNavVisRange = "{2453, 600}"; + }; + }; + C17685640E0C35820058E49D /* socket.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {897, 1106}}"; + sepNavSelRange = "{1881, 0}"; + sepNavVisRange = "{1351, 814}"; + }; + }; + C17685650E0C35820058E49D /* socketaddress.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {911, 4134}}"; + sepNavSelRange = "{5467, 63}"; + sepNavVisRange = "{5050, 1139}"; + }; + }; + C17685670E0C35820058E49D /* socketserver.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {810, 3584}}"; + sepNavSelRange = "{3037, 0}"; + sepNavVisRange = "{2578, 843}"; + sepNavWindowFrame = "{{124, 316}, {1057, 794}}"; + }; + }; + C176856E0E0C35A60058E49D /* bytebuffer.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1103, 2576}}"; + sepNavSelRange = "{3751, 0}"; + sepNavVisRange = "{0, 649}"; + }; + }; + C17685700E0C36100058E49D /* misc.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {881, 770}}"; + sepNavSelRange = "{950, 0}"; + sepNavVisRange = "{336, 614}"; + }; + }; + C17685710E0C36100058E49D /* misc.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {901, 526}}"; + sepNavSelRange = "{331, 64}"; + sepNavVisRange = "{0, 492}"; + }; + }; + C17685720E0C36100058E49D /* mpht.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {881, 4410}}"; + sepNavSelRange = "{1537, 38}"; + sepNavVisRange = "{1199, 1037}"; + }; + }; + C17685730E0C36100058E49D /* netmessage.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1115, 6188}}"; + sepNavSelRange = "{5563, 1}"; + sepNavVisRange = "{5240, 986}"; + }; + }; + C17685740E0C36100058E49D /* netmessage.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {881, 8036}}"; + sepNavSelRange = "{4523, 34}"; + sepNavVisRange = "{4108, 872}"; + }; + }; + C17685760E0C36100058E49D /* xmsg.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {810, 6300}}"; + sepNavSelRange = "{9829, 28}"; + sepNavVisRange = "{9459, 782}"; + }; + }; + C17685B60E15CB180058E49D /* xpump.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1044, 9968}}"; + sepNavSelRange = "{1791, 12}"; + sepNavVisRange = "{1776, 646}"; + }; + }; + C17685B70E15CB180058E49D /* xpump.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1044, 1526}}"; + sepNavSelRange = "{223, 0}"; + sepNavVisRange = "{40, 845}"; + }; + }; + C17D66080D9E15A300CB9F01 /* alertcontrol.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {881, 1216}}"; + sepNavSelRange = "{1173, 35}"; + sepNavVisRange = "{781, 510}"; + }; + }; + C17D660A0D9E15BE00CB9F01 /* Andy.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1115, 3906}}"; + sepNavSelRange = "{5544, 41}"; + sepNavVisRange = "{5217, 567}"; + }; + }; + C17D660B0D9E15BE00CB9F01 /* Animation.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1135, 4844}}"; + sepNavSelRange = "{3883, 0}"; + sepNavVisRange = "{3654, 698}"; + }; + }; + C17D660C0D9E15BE00CB9F01 /* Artillery.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {897, 1918}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRange = "{0, 870}"; + }; + }; + C17D660D0D9E15BE00CB9F01 /* basictypes.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {800, 440}}"; + sepNavSelRange = "{296, 0}"; + sepNavVisRange = "{0, 328}"; + }; + }; + C17D660E0D9E15BE00CB9F01 /* bitmap.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1021, 7868}}"; + sepNavSelRange = "{3450, 0}"; + sepNavVisRange = "{2775, 934}"; + }; + }; + C17D660F0D9E15BE00CB9F01 /* Builder.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1115, 16814}}"; + sepNavSelRange = "{33506, 0}"; + sepNavVisRange = "{8468, 698}"; + }; + }; + C17D66100D9E15BE00CB9F01 /* BuildMgr.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {960, 2926}}"; + sepNavSelRange = "{5133, 0}"; + sepNavVisRange = "{3796, 1486}"; + }; + }; + C17D66110D9E15BE00CB9F01 /* cachemgr.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {897, 3150}}"; + sepNavSelRange = "{4053, 1}"; + sepNavVisRange = "{3668, 801}"; + }; + }; + C17D66130D9E15BE00CB9F01 /* compression.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {800, 1540}}"; + sepNavSelRange = "{2647, 0}"; + sepNavVisRange = "{1783, 864}"; + }; + }; + C17D66140D9E15BE00CB9F01 /* CutScene.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1116, 4340}}"; + sepNavSelRange = "{755, 0}"; + sepNavVisRange = "{568, 448}"; + }; + }; + C17D66150D9E15BE00CB9F01 /* drm.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {881, 8596}}"; + sepNavSelRange = "{11993, 0}"; + sepNavVisRange = "{10339, 614}"; + }; + }; + C17D66160D9E15BE00CB9F01 /* Ecom.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1135, 7924}}"; + sepNavSelRange = "{14048, 0}"; + sepNavVisRange = "{13121, 421}"; + }; + }; + C17D66170D9E15BE00CB9F01 /* event.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1115, 6818}}"; + sepNavSelRange = "{3596, 0}"; + sepNavVisRange = "{3246, 1023}"; + sepNavWindowFrame = "{{55, 424}, {1057, 578}}"; + }; + }; + C17D66180D9E15BE00CB9F01 /* fogmap.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {901, 11144}}"; + sepNavSelRange = "{19982, 0}"; + sepNavVisRange = "{2873, 1041}"; + }; + }; + C17D66190D9E15BE00CB9F01 /* font.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1044, 5642}}"; + sepNavSelRange = "{8280, 0}"; + sepNavVisRange = "{6042, 228}"; + }; + }; + C17D661A0D9E15BE00CB9F01 /* form.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1115, 16268}}"; + sepNavSelRange = "{13081, 0}"; + sepNavVisRange = "{12821, 628}"; + sepNavWindowFrame = "{{55, 419}, {1057, 578}}"; + }; + }; + C17D661B0D9E15BE00CB9F01 /* formmgr.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1044, 15274}}"; + sepNavSelRange = "{975, 0}"; + sepNavVisRange = "{741, 567}"; + }; + }; + C17D661C0D9E15BE00CB9F01 /* game.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1145, 37884}}"; + sepNavSelRange = "{52386, 0}"; + sepNavVisRange = "{52075, 515}"; + sepNavWindowFrame = "{{78, 574}, {1057, 578}}"; + }; + }; + C17D661D0D9E15BE00CB9F01 /* GameObjects.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1044, 39578}}"; + sepNavSelRange = "{27671, 0}"; + sepNavVisRange = "{27035, 1177}"; + sepNavWindowFrame = "{{78, 358}, {1057, 794}}"; + }; + }; + C17D661E0D9E15BE00CB9F01 /* GameOptions.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1145, 22165}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRange = "{0, 432}"; + }; + }; + C17D661F0D9E15BE00CB9F01 /* Headquarters.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1145, 14417}}"; + sepNavSelRange = "{30339, 0}"; + sepNavVisRange = "{20245, 1205}"; + sepNavWindowFrame = "{{55, 379}, {1057, 794}}"; + }; + }; + C17D66200D9E15BE00CB9F01 /* Help.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1103, 15750}}"; + sepNavSelRange = "{28725, 0}"; + sepNavVisRange = "{25711, 1007}"; + }; + }; + C17D66210D9E15BE00CB9F01 /* HRC.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {624, 952}}"; + sepNavSelRange = "{726, 1}"; + sepNavVisRange = "{233, 1127}"; + }; + }; + C17D66220D9E15BE00CB9F01 /* ht.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1145, 127358}}"; + sepNavSelRange = "{138857, 0}"; + sepNavVisRange = "{138629, 400}"; + sepNavWindowFrame = "{{55, 595}, {1057, 578}}"; + }; + }; + C17D66240D9E15BE00CB9F01 /* InputUI.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1103, 10234}}"; + sepNavSelRange = "{9836, 26}"; + sepNavVisRange = "{9406, 619}"; + }; + }; + C17D66250D9E15BE00CB9F01 /* Level.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1135, 7686}}"; + sepNavSelRange = "{802, 0}"; + sepNavVisRange = "{594, 373}"; + }; + }; + C17D66260D9E15BE00CB9F01 /* loadsave.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {800, 2884}}"; + sepNavSelRange = "{4586, 0}"; + sepNavVisRange = "{3948, 638}"; + }; + }; + C17D66270D9E15BE00CB9F01 /* LRInfantry.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {897, 5250}}"; + sepNavSelRange = "{1640, 11}"; + sepNavVisRange = "{1158, 947}"; + }; + }; + C17D66280D9E15BE00CB9F01 /* memmgr.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {870, 17178}}"; + sepNavSelRange = "{20263, 0}"; + sepNavVisRange = "{19733, 1061}"; + }; + }; + C17D66290D9E15BE00CB9F01 /* Miner.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1337, 12698}}"; + sepNavSelRange = "{7573, 0}"; + sepNavVisRange = "{7246, 856}"; + sepNavWindowFrame = "{{57, 379}, {1057, 794}}"; + }; + }; + C17D662A0D9E15BE00CB9F01 /* misc.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1115, 25536}}"; + sepNavSelRange = "{15782, 0}"; + sepNavVisRange = "{15529, 585}"; + sepNavWindowFrame = "{{55, 379}, {1057, 794}}"; + }; + }; + C17D662B0D9E15BE00CB9F01 /* misccontrols.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1115, 30702}}"; + sepNavSelRange = "{35551, 0}"; + sepNavVisRange = "{35263, 586}"; + }; + }; + C17D662C0D9E15BE00CB9F01 /* miscgraphics.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {870, 6832}}"; + sepNavSelRange = "{6320, 21}"; + sepNavVisRange = "{5837, 991}"; + }; + }; + C17D662D0D9E15BE00CB9F01 /* mixer.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {800, 3318}}"; + sepNavSelRange = "{13239, 0}"; + sepNavVisRange = "{12395, 844}"; + }; + }; + C17D662E0D9E15BE00CB9F01 /* mixer.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1075, 294}}"; + sepNavSelRange = "{271, 0}"; + sepNavVisRange = "{20, 274}"; + }; + }; + C17D662F0D9E15BE00CB9F01 /* MobileBuilder.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1115, 3150}}"; + sepNavSelRange = "{1467, 8}"; + sepNavVisRange = "{1013, 766}"; + }; + }; + C17D66300D9E15BE00CB9F01 /* MobileHQ.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {804, 2814}}"; + sepNavSelRange = "{4552, 0}"; + sepNavVisRange = "{3743, 809}"; + }; + }; + C17D66310D9E15BE00CB9F01 /* MobileUnit.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1103, 43470}}"; + sepNavSelRange = "{58828, 26}"; + sepNavVisRange = "{58113, 864}"; + }; + }; + C17D66320D9E15BE00CB9F01 /* Multiplayer.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1145, 1162}}"; + sepNavSelRange = "{1314, 0}"; + sepNavVisRange = "{1054, 527}"; + }; + }; + C17D66340D9E15BE00CB9F01 /* Overmind.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {800, 3612}}"; + sepNavSelRange = "{7885, 0}"; + sepNavVisRange = "{7109, 776}"; + }; + }; + C17D66370D9E15BE00CB9F01 /* Player.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1145, 17346}}"; + sepNavSelRange = "{1645, 0}"; + sepNavVisRange = "{1382, 654}"; + sepNavWindowFrame = "{{78, 58}, {1057, 794}}"; + }; + }; + C17D66380D9E15BE00CB9F01 /* Processor.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {881, 8792}}"; + sepNavSelRange = "{10332, 0}"; + sepNavVisRange = "{6338, 1038}"; + }; + }; + C17D66390D9E15BE00CB9F01 /* Radar.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1103, 1666}}"; + sepNavSelRange = "{2327, 0}"; + sepNavVisRange = "{1383, 944}"; + }; + }; + C17D663A0D9E15BE00CB9F01 /* RawBitmap.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1135, 3612}}"; + sepNavSelRange = "{158, 0}"; + sepNavVisRange = "{370, 309}"; + }; + }; + C17D663B0D9E15BE00CB9F01 /* Reactor.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {800, 728}}"; + sepNavSelRange = "{794, 0}"; + sepNavVisRange = "{238, 556}"; + }; + }; + C17D663C0D9E15BE00CB9F01 /* Replicator.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1337, 7784}}"; + sepNavSelRange = "{10937, 5}"; + sepNavVisRange = "{10355, 772}"; + }; + }; + C17D663D0D9E15BE00CB9F01 /* res.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {800, 5362}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRange = "{17503, 866}"; + }; + }; + C17D663E0D9E15BE00CB9F01 /* Research.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1077, 8414}}"; + sepNavSelRange = "{15334, 16}"; + sepNavVisRange = "{14643, 707}"; + }; + }; + C17D663F0D9E15BE00CB9F01 /* rip.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1115, 6328}}"; + sepNavSelRange = "{6207, 0}"; + sepNavVisRange = "{6116, 300}"; + }; + }; + C17D66400D9E15BE00CB9F01 /* Shell.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {911, 13338}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRange = "{0, 1248}"; + sepNavWindowFrame = "{{55, 300}, {1057, 578}}"; + }; + }; + C17D66410D9E15BE00CB9F01 /* SimUI.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1145, 38878}}"; + sepNavSelRange = "{74972, 0}"; + sepNavVisRange = "{21866, 796}"; + sepNavWindowFrame = "{{78, 574}, {1057, 578}}"; + }; + }; + C17D66420D9E15BE00CB9F01 /* Simulation.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {881, 18284}}"; + sepNavSelRange = "{34343, 0}"; + sepNavVisRange = "{7383, 1480}"; + }; + }; + C17D66450D9E15BE00CB9F01 /* sounddevice.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1103, 521}}"; + sepNavSelRange = "{556, 0}"; + sepNavVisRange = "{0, 585}"; + }; + }; + C17D66460D9E15BE00CB9F01 /* soundeffects.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {800, 2086}}"; + sepNavSelRange = "{270, 0}"; + sepNavVisRange = "{0, 1510}"; + }; + }; + C17D66470D9E15BE00CB9F01 /* soundmgr.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1115, 3668}}"; + sepNavSelRange = "{529, 0}"; + sepNavVisRange = "{276, 336}"; + }; + }; + C17D66480D9E15BE00CB9F01 /* SpInfantry.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {800, 2184}}"; + sepNavSelRange = "{3752, 0}"; + sepNavVisRange = "{3084, 668}"; + }; + }; + C17D66490D9E15BE00CB9F01 /* SRInfantry.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {834, 3080}}"; + sepNavSelRange = "{4863, 0}"; + sepNavVisRange = "{4446, 417}"; + }; + }; + C17D664A0D9E15BE00CB9F01 /* StateMachine.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1103, 7826}}"; + sepNavSelRange = "{3050, 0}"; + sepNavVisRange = "{2851, 491}"; + }; + }; + C17D664B0D9E15BE00CB9F01 /* strings.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {800, 616}}"; + sepNavSelRange = "{1806, 0}"; + sepNavVisRange = "{0, 1271}"; + }; + }; + C17D664C0D9E15BE00CB9F01 /* stringtable.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {800, 784}}"; + sepNavSelRange = "{943, 0}"; + sepNavVisRange = "{316, 627}"; + }; + }; + C17D664D0D9E15BE00CB9F01 /* Struct.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1135, 16296}}"; + sepNavSelRange = "{14090, 0}"; + sepNavVisRange = "{13969, 612}"; + }; + }; + C17D664E0D9E15BE00CB9F01 /* Tank.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {901, 11984}}"; + sepNavSelRange = "{5623, 51}"; + sepNavVisRange = "{5116, 912}"; + }; + }; + C17D664F0D9E15BE00CB9F01 /* tbitmap.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1337, 22344}}"; + sepNavSelRange = "{10919, 0}"; + sepNavVisRange = "{10735, 373}"; + }; + }; + C17D66500D9E15BE00CB9F01 /* terrainmap.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1337, 16702}}"; + sepNavSelRange = "{3476, 15}"; + sepNavVisRange = "{2971, 777}"; + }; + }; + C17D66510D9E15BE00CB9F01 /* tests.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {800, 2254}}"; + sepNavSelRange = "{3499, 0}"; + sepNavVisRange = "{2955, 544}"; + }; + }; + C17D66520D9E15BE00CB9F01 /* thunks.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1337, 1596}}"; + sepNavSelRange = "{2983, 0}"; + sepNavVisRange = "{2384, 620}"; + }; + }; + C17D66530D9E15BE00CB9F01 /* tilemap.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1206, 3262}}"; + sepNavSelRange = "{4842, 0}"; + sepNavVisRange = "{4307, 689}"; + }; + }; + C17D66540D9E15BE00CB9F01 /* timer.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1145, 3542}}"; + sepNavSelRange = "{5517, 0}"; + sepNavVisRange = "{4855, 521}"; + }; + }; + C17D66550D9E15BE00CB9F01 /* Tower.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1135, 8624}}"; + sepNavSelRange = "{16562, 0}"; + sepNavVisRange = "{10875, 556}"; + }; + }; + C17D66560D9E15BE00CB9F01 /* TriggerActions.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1145, 18620}}"; + sepNavSelRange = "{29702, 0}"; + sepNavVisRange = "{7944, 1072}"; + }; + }; + C17D66570D9E15BE00CB9F01 /* TriggerConditions.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1103, 8246}}"; + sepNavSelRange = "{9638, 0}"; + sepNavVisRange = "{9227, 1119}"; + }; + }; + C17D66580D9E15BE00CB9F01 /* triggermgr.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {881, 9856}}"; + sepNavSelRange = "{9592, 0}"; + sepNavVisRange = "{9228, 689}"; + }; + }; + C17D66590D9E15BE00CB9F01 /* Unit.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1115, 19698}}"; + sepNavSelRange = "{19224, 0}"; + sepNavVisRange = "{18845, 516}"; + sepNavWindowFrame = "{{55, 295}, {1057, 578}}"; + }; + }; + C17D665A0D9E15BE00CB9F01 /* UnitGroupMgr.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1337, 20454}}"; + sepNavSelRange = "{1241, 0}"; + sepNavVisRange = "{11104, 514}"; + }; + }; + C17D665B0D9E15BE00CB9F01 /* updatemap.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1337, 9254}}"; + sepNavSelRange = "{978, 0}"; + sepNavVisRange = "{742, 497}"; + }; + }; + C17D665C0D9E15BE00CB9F01 /* VTS.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {800, 924}}"; + sepNavSelRange = "{1555, 0}"; + sepNavVisRange = "{654, 901}"; + }; + }; + C17D665D0D9E15BE00CB9F01 /* Warehouse.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1358, 2114}}"; + sepNavSelRange = "{970, 0}"; + sepNavVisRange = "{818, 393}"; + }; + }; + C17D66AE0D9E160100CB9F01 /* display.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1103, 2870}}"; + sepNavSelRange = "{3801, 0}"; + sepNavVisRange = "{605, 431}"; + }; + }; + C17D66B10D9E160100CB9F01 /* host.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1115, 5922}}"; + sepNavSelRange = "{8797, 0}"; + sepNavVisRange = "{7579, 641}"; + sepNavWindowFrame = "{{55, 295}, {1057, 578}}"; + }; + }; + C17D66B20D9E160100CB9F01 /* htplatform.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1103, 2240}}"; + sepNavSelRange = "{3845, 0}"; + sepNavVisRange = "{3457, 418}"; + }; + }; + C17D66B30D9E160100CB9F01 /* input.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {827, 526}}"; + sepNavSelRange = "{67, 0}"; + sepNavVisRange = "{0, 322}"; + }; + }; + C17D66B40D9E160100CB9F01 /* iphone.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {810, 966}}"; + sepNavSelRange = "{1737, 0}"; + sepNavVisRange = "{0, 607}"; + }; + }; + C17D66B50D9E160100CB9F01 /* iphone.mm */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1145, 6006}}"; + sepNavSelRange = "{4624, 0}"; + sepNavVisRange = "{4119, 1370}"; + sepNavWindowFrame = "{{55, 424}, {1057, 578}}"; + }; + }; + C17D66B60D9E160100CB9F01 /* iphonesounddev.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {911, 3393}}"; + sepNavSelRange = "{6694, 0}"; + sepNavVisRange = "{2346, 1122}"; + }; + }; + C17D66B70D9E160100CB9F01 /* cgview.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {881, 532}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRange = "{0, 954}"; + }; + }; + C17D66B80D9E160100CB9F01 /* cgview.mm */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1145, 3120}}"; + sepNavSelRange = "{6168, 0}"; + sepNavVisRange = "{1229, 1199}"; + sepNavWindowFrame = "{{587, 424}, {1057, 578}}"; + }; + }; + C17D66B90D9E160100CB9F01 /* main.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {908, 630}}"; + sepNavSelRange = "{579, 0}"; + sepNavVisRange = "{149, 327}"; + }; + }; + C17D66BC0D9E160100CB9F01 /* savegame.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {897, 4620}}"; + sepNavSelRange = "{6978, 0}"; + sepNavVisRange = "{6206, 772}"; + }; + }; + C17D66BF0D9E160100CB9F01 /* transportmgr.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {897, 496}}"; + sepNavSelRange = "{84, 1}"; + sepNavVisRange = "{0, 366}"; + }; + }; + C18423F111ECE9B8007BDB3D /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C17D66B80D9E160100CB9F01 /* cgview.mm */; + name = "cgview.mm: 229"; + rLen = 0; + rLoc = 6168; + rType = 0; + vrLen = 806; + vrLoc = 1369; + }; + C184242B11EE30B5007BDB3D /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C17D66B50D9E160100CB9F01 /* iphone.mm */; + name = "iphone.mm: 157"; + rLen = 0; + rLoc = 4624; + rType = 0; + vrLen = 896; + vrLoc = 4309; + }; + C184249311EE4F4C007BDB3D /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C17D661F0D9E15BE00CB9F01 /* Headquarters.cpp */; + name = "Headquarters.cpp: 1114"; + rLen = 0; + rLoc = 30339; + rType = 0; + vrLen = 609; + vrLoc = 20706; + }; + C18E0E020DBAC70700096237 /* oglview.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1392, 826}}"; + sepNavSelRange = "{549, 0}"; + sepNavVisRange = "{0, 1611}"; + }; + }; + C18E0E030DBAC70700096237 /* oglview.mm */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {897, 2912}}"; + sepNavSelRange = "{4467, 9}"; + sepNavVisRange = "{0, 567}"; + sepNavWindowFrame = "{{78, 574}, {1057, 578}}"; + }; + }; + C1949CF70E7874C200A654A1 /* downloadbox.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1103, 3626}}"; + sepNavSelRange = "{6824, 40}"; + sepNavVisRange = "{6316, 1073}"; + }; + }; + C194A0FF0E7F8CF000A654A1 /* httpindexloader.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1077, 4662}}"; + sepNavSelRange = "{1438, 0}"; + sepNavVisRange = "{1498, 828}"; + }; + }; + C194A1000E7F8CF000A654A1 /* httpindexloader.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {810, 1092}}"; + sepNavSelRange = "{1872, 0}"; + sepNavVisRange = "{1169, 1013}"; + }; + }; + C194A1010E7F8CF000A654A1 /* httppackinfomanager.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {870, 2940}}"; + sepNavSelRange = "{1787, 1}"; + sepNavVisRange = "{1442, 1057}"; + }; + }; + C194A1020E7F8CF000A654A1 /* httppackinfomanager.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {810, 742}}"; + sepNavSelRange = "{1286, 0}"; + sepNavVisRange = "{0, 1073}"; + }; + }; + C194A1030E7F8CF000A654A1 /* httppackmanager.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1077, 4282}}"; + sepNavSelRange = "{8177, 0}"; + sepNavVisRange = "{2810, 984}"; + }; + }; + C194A1040E7F8CF000A654A1 /* httppackmanager.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1021, 686}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRange = "{0, 947}"; + }; + }; + C194A1BF0E80300C00A654A1 /* constants.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {810, 1344}}"; + sepNavSelRange = "{2913, 21}"; + sepNavVisRange = "{1222, 752}"; + }; + }; + C194A1C00E80300C00A654A1 /* indexloader.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {810, 2072}}"; + sepNavSelRange = "{3288, 0}"; + sepNavVisRange = "{2683, 605}"; + }; + }; + C194A1C20E80300C00A654A1 /* packinfomanager.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {810, 1120}}"; + sepNavSelRange = "{2111, 0}"; + sepNavVisRange = "{1294, 817}"; + }; + }; + C194A1C30E80300C00A654A1 /* packmanager.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1135, 2366}}"; + sepNavSelRange = "{2284, 0}"; + sepNavVisRange = "{2007, 441}"; + }; + }; + C1994E2A0EA149D200A48088 /* iphoneanimsprite.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {901, 798}}"; + sepNavSelRange = "{1277, 0}"; + sepNavVisRange = "{112, 905}"; + }; + }; + C1994E2B0EA149D200A48088 /* iphoneanimsprite.mm */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1115, 5138}}"; + sepNavSelRange = "{4529, 0}"; + sepNavVisRange = "{4246, 693}"; + }; + }; + C1994E300EA14A2800A48088 /* progresscallback.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {810, 500}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRange = "{0, 401}"; + }; + }; + C1994E310EA14A2800A48088 /* sprite.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {901, 560}}"; + sepNavSelRange = "{777, 0}"; + sepNavVisRange = "{0, 754}"; + }; + }; + C19D450E0F745A9600283E95 /* chatcell.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {911, 489}}"; + sepNavSelRange = "{222, 15}"; + sepNavVisRange = "{0, 395}"; + }; + }; + C19D450F0F745A9600283E95 /* chatcell.mm */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {911, 715}}"; + sepNavSelRange = "{157, 13}"; + sepNavVisRange = "{0, 1198}"; + sepNavWindowFrame = "{{149, 295}, {1057, 794}}"; + }; + }; + C1A111F40E416739007913B4 /* messagehandler.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1021, 449}}"; + sepNavSelRange = "{323, 70}"; + sepNavVisRange = "{0, 549}"; + }; + }; + C1A111F50E416739007913B4 /* messagehandler.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {897, 496}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRange = "{0, 461}"; + }; + }; + C1A1C2040FE9B2BD00042055 /* format.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {881, 470}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRange = "{0, 517}"; + }; + }; + C1A1C2060FE9B2BD00042055 /* thread.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1115, 1078}}"; + sepNavSelRange = "{732, 0}"; + sepNavVisRange = "{446, 576}"; + }; + }; + C1ABDCFA0DC7C19E000A5B75 /* flickscroller.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {881, 1484}}"; + sepNavSelRange = "{2966, 0}"; + sepNavVisRange = "{1741, 1023}"; + }; + }; + C1B3F5340E65D6C100D36F66 /* missionlist.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1115, 6286}}"; + sepNavSelRange = "{3480, 0}"; + sepNavVisRange = "{3234, 410}"; + }; + }; + C1B3F5350E65D6C100D36F66 /* missionlist.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {897, 854}}"; + sepNavSelRange = "{1108, 40}"; + sepNavVisRange = "{295, 904}"; + }; + }; + C1B3F5E80E671C0600D36F66 /* selectmission.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1103, 6146}}"; + sepNavSelRange = "{5598, 0}"; + sepNavVisRange = "{4656, 1183}"; + }; + }; + C1C706950DBD0F090062B73B /* cgdib.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {827, 765}}"; + sepNavSelRange = "{971, 0}"; + sepNavVisRange = "{0, 1097}"; + }; + }; + C1C706970DBD0F090062B73B /* ogldib.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {827, 765}}"; + sepNavSelRange = "{220, 122}"; + sepNavVisRange = "{0, 468}"; + }; + }; + C1C708DE0DBD6C060062B73B /* rip.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1103, 1008}}"; + sepNavSelRange = "{1745, 0}"; + sepNavVisRange = "{479, 1496}"; + }; + }; + C1C709750DBDAF290062B73B /* wiview.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {881, 896}}"; + sepNavSelRange = "{1364, 0}"; + sepNavVisRange = "{3, 703}"; + }; + }; + C1C709760DBDAF290062B73B /* wiview.mm */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1145, 4472}}"; + sepNavSelRange = "{9321, 0}"; + sepNavVisRange = "{2828, 977}"; + }; + }; + C1D4E0370F8FCBEA00D8C118 /* creategameform.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1115, 5964}}"; + sepNavSelRange = "{6028, 0}"; + sepNavVisRange = "{5824, 682}"; + }; + }; + C1D4E0380F8FCBEA00D8C118 /* creategameform.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {810, 532}}"; + sepNavSelRange = "{1044, 0}"; + sepNavVisRange = "{0, 870}"; + }; + }; + C1D4E03B0F8FCBEA00D8C118 /* picktransportform.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1044, 1764}}"; + sepNavSelRange = "{3210, 0}"; + sepNavVisRange = "{2067, 620}"; + }; + }; + C1D4E03D0F8FCBEA00D8C118 /* roomform.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1115, 9100}}"; + sepNavSelRange = "{18765, 0}"; + sepNavVisRange = "{16417, 739}"; + }; + }; + C1D4E03E0F8FCBEA00D8C118 /* roomform.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {810, 1652}}"; + sepNavSelRange = "{3112, 0}"; + sepNavVisRange = "{2142, 877}"; + }; + }; + C1D4E0550F98F01D00D8C118 /* lobbyform.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {911, 5334}}"; + sepNavSelRange = "{10633, 0}"; + sepNavVisRange = "{0, 1069}"; + }; + }; + C1D4E0560F98F01D00D8C118 /* lobbyform.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {911, 1344}}"; + sepNavSelRange = "{830, 70}"; + sepNavVisRange = "{0, 918}"; + }; + }; + C1D4E0570F98F01D00D8C118 /* loginform.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1145, 3948}}"; + sepNavSelRange = "{7796, 0}"; + sepNavVisRange = "{5150, 870}"; + }; + }; + C1D4E0590F98F01D00D8C118 /* loginhandler.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {911, 3402}}"; + sepNavSelRange = "{6261, 0}"; + sepNavVisRange = "{1312, 1174}"; + }; + }; + C1D4E05A0F98F01D00D8C118 /* loginhandler.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1044, 532}}"; + sepNavSelRange = "{476, 0}"; + sepNavVisRange = "{183, 565}"; + }; + }; + C1D4E07D0F98F4DC00D8C118 /* simplerequest.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {881, 1624}}"; + sepNavSelRange = "{758, 0}"; + sepNavVisRange = "{323, 845}"; + }; + }; + C1D4E07E0F98F4DC00D8C118 /* simplerequest.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {810, 574}}"; + sepNavSelRange = "{995, 0}"; + sepNavVisRange = "{28, 936}"; + }; + }; + C1D4E0CB0F9F8A6600D8C118 /* lobby.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {881, 2982}}"; + sepNavSelRange = "{1004, 1}"; + sepNavVisRange = "{579, 788}"; + }; + }; + C1D4E0CC0F9F8A6600D8C118 /* lobby.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {810, 826}}"; + sepNavSelRange = "{1306, 0}"; + sepNavVisRange = "{543, 723}"; + }; + }; + C1D4E4AC0FA7B9AA00D8C118 /* createroomform.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {810, 1512}}"; + sepNavSelRange = "{1573, 1}"; + sepNavVisRange = "{1225, 823}"; + }; + }; + C1D4E4AD0FA7B9AA00D8C118 /* createroomform.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {810, 504}}"; + sepNavSelRange = "{845, 0}"; + sepNavVisRange = "{0, 845}"; + }; + }; + C1D4E6840FABB4A500D8C118 /* gameform.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1145, 1064}}"; + sepNavSelRange = "{2148, 0}"; + sepNavVisRange = "{1457, 691}"; + }; + }; + C1D4E6850FABB4A500D8C118 /* gameform.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {911, 8708}}"; + sepNavSelRange = "{17783, 0}"; + sepNavVisRange = "{5283, 1357}"; + }; + }; + C1D4E79B0FAF72BA00D8C118 /* tokenauth.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {810, 1680}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRange = "{0, 807}"; + }; + }; + C1D4E79D0FAF72BA00D8C118 /* room.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {810, 2786}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRange = "{5459, 743}"; + }; + }; + C1D4E7DC0FAF90E500D8C118 /* comm.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {810, 420}}"; + sepNavSelRange = "{138, 0}"; + sepNavVisRange = "{0, 443}"; + }; + }; + C1D4E90C0FB359E000D8C118 /* chatcontroller.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {810, 420}}"; + sepNavSelRange = "{520, 0}"; + sepNavVisRange = "{0, 520}"; + }; + }; + C1D9668E108511E20088A942 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C1C709760DBDAF290062B73B /* wiview.mm */; + name = "wiview.mm: 347"; + rLen = 0; + rLoc = 9321; + rType = 0; + vrLen = 276; + vrLoc = 3114; + }; + C1D966A81086D2080088A942 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C17D661E0D9E15BE00CB9F01 /* GameOptions.cpp */; + name = "GameOptions.cpp: 1"; + rLen = 0; + rLoc = 0; + rType = 0; + vrLen = 134; + vrLoc = 0; + }; + C1D966B91086D6C70088A942 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C17D66400D9E15BE00CB9F01 /* Shell.cpp */; + name = "Shell.cpp: 1"; + rLen = 0; + rLoc = 0; + rType = 0; + vrLen = 1248; + vrLoc = 0; + }; + C1D966BA1086D6C70088A942 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C17D66B60D9E160100CB9F01 /* iphonesounddev.cpp */; + name = "iphonesounddev.cpp: 270"; + rLen = 0; + rLoc = 6694; + rType = 0; + vrLen = 1122; + vrLoc = 2346; + }; + C1D966BB1086D6C70088A942 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C17685650E0C35820058E49D /* socketaddress.cpp */; + name = "socketaddress.cpp: 250"; + rLen = 63; + rLoc = 5467; + rType = 0; + vrLen = 1139; + vrLoc = 5050; + }; + C1D966BE1086D6C70088A942 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C19D450E0F745A9600283E95 /* chatcell.h */; + name = "chatcell.h: 10"; + rLen = 15; + rLoc = 222; + rType = 0; + vrLen = 395; + vrLoc = 0; + }; + C1D966CC1086DBCE0088A942 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C166A8C90DE5F13B000B45A0 /* oglview2.mm */; + name = "oglview2.mm: 261"; + rLen = 0; + rLoc = 8579; + rType = 0; + vrLen = 1703; + vrLoc = 2114; + }; + C1D966CF1086DC3F0088A942 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C1603E050F6984CB00B19D51 /* wiviewcontroller.mm */; + name = "wiviewcontroller.mm: 149"; + rLen = 0; + rLoc = 4453; + rType = 0; + vrLen = 1240; + vrLoc = 1753; + }; + C1D966E01086DFF00088A942 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C1603E1E0F69AAB200B19D51 /* chatviewcontroller.mm */; + name = "chatviewcontroller.mm: 712"; + rLen = 0; + rLoc = 22030; + rType = 0; + vrLen = 1209; + vrLoc = 20306; + }; + C1D966E11086DFF00088A942 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = C19D450F0F745A9600283E95 /* chatcell.mm */; + name = "chatcell.mm: 5"; + rLen = 13; + rLoc = 157; + rType = 0; + vrLen = 1198; + vrLoc = 0; + }; + C1DD179F0EC279A500D941E8 /* layerdib.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1021, 1414}}"; + sepNavSelRange = "{1155, 0}"; + sepNavVisRange = "{802, 724}"; + }; + }; + C1DD17A00EC279A500D941E8 /* layerdib.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1021, 518}}"; + sepNavSelRange = "{843, 0}"; + sepNavVisRange = "{0, 798}"; + }; + }; + C1DD17A20EC279A500D941E8 /* layermap.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1255, 504}}"; + sepNavSelRange = "{415, 0}"; + sepNavVisRange = "{181, 618}"; + }; + }; + C1DD17A30EC279A500D941E8 /* layertile.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1255, 1400}}"; + sepNavSelRange = "{957, 0}"; + sepNavVisRange = "{757, 412}"; + }; + }; + C1DD17A40EC279A500D941E8 /* layerview.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1021, 504}}"; + sepNavSelRange = "{983, 0}"; + sepNavVisRange = "{91, 907}"; + }; + }; + C1DD17A50EC279A500D941E8 /* layerview.mm */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1255, 2926}}"; + sepNavSelRange = "{5574, 0}"; + sepNavVisRange = "{3025, 402}"; + }; + }; + C1DD18060EC3756A00D941E8 /* layermap.mm */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1255, 5096}}"; + sepNavSelRange = "{9520, 0}"; + sepNavVisRange = "{5434, 296}"; + }; + }; + C1DE48271029254C00DB7394 /* chooseserverform.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1145, 5057}}"; + sepNavSelRange = "{8215, 0}"; + sepNavVisRange = "{2192, 1336}"; + }; + }; + C1DE48281029254C00DB7394 /* chooseserverform.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {911, 884}}"; + sepNavSelRange = "{1231, 43}"; + sepNavVisRange = "{0, 910}"; + }; + }; + C1E3BC640FD998B100E258F3 /* chatter.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {881, 1288}}"; + sepNavSelRange = "{132, 1}"; + sepNavVisRange = "{0, 869}"; + }; + }; + C1E3BC650FD998B100E258F3 /* chatter.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {881, 672}}"; + sepNavSelRange = "{1026, 0}"; + sepNavVisRange = "{0, 764}"; + }; + }; + C1F1F5EC0D9E137800FF3191 /* wi */ = { + isa = PBXExecutable; + activeArgIndices = ( + ); + argumentStrings = ( + ); + autoAttachOnCrash = 1; + breakpointsEnabled = 1; + configStateDict = { + }; + customDataFormattersEnabled = 1; + dataTipCustomDataFormattersEnabled = 1; + dataTipShowTypeColumn = 1; + dataTipSortType = 0; + debuggerPlugin = GDBDebugging; + disassemblyDisplayState = 0; + dylibVariantSuffix = ""; + enableDebugStr = 1; + environmentEntries = ( + ); + executableSystemSymbolLevel = 0; + executableUserSymbolLevel = 0; + libgmallocEnabled = 0; + name = wi; + savedGlobals = { + }; + showTypeColumn = 0; + sourceDirectories = ( + ); + variableFormatDictionary = { + "0-byte-wi::PackInfoManager::GetInfoFilename" = 1; + "0-byte-wi::PackInfoManager::SetServiceUrl" = 1; + "1-byte-wi::PackInfoManager::GetInfoFilename" = 1; + "1-byte-wi::PackInfoManager::SetServiceUrl" = 1; + "2-byte-wi::PackInfoManager::GetInfoFilename" = 1; + "2-byte-wi::PackInfoManager::SetServiceUrl" = 1; + "3-byte-wi::PackInfoManager::GetInfoFilename" = 1; + "3-byte-wi::PackInfoManager::SetServiceUrl" = 1; + "4-byte-wi::PackInfoManager::GetInfoFilename" = 1; + "4-byte-wi::PackInfoManager::SetServiceUrl" = 1; + "5-byte-wi::PackInfoManager::GetInfoFilename" = 1; + "5-byte-wi::PackInfoManager::SetServiceUrl" = 1; + "6-byte-wi::PackInfoManager::GetInfoFilename" = 1; + "6-byte-wi::PackInfoManager::SetServiceUrl" = 1; + "7-byte-wi::PackInfoManager::GetInfoFilename" = 1; + "7-byte-wi::PackInfoManager::SetServiceUrl" = 1; + "8-byte-wi::PackInfoManager::GetInfoFilename" = 1; + "8-byte-wi::PackInfoManager::SetServiceUrl" = 1; + "9-byte-wi::PackInfoManager::GetInfoFilename" = 1; + "9-byte-wi::PackInfoManager::SetServiceUrl" = 1; + "bottom-int-wi::PlaceStructureForm::GetSubRects" = 1; + "hash-byte [16]-wi::PackInfoManager::SetServiceUrl" = 1; + "id-dword-wi::CompleteManager::GetKey" = 1; + "id-dword-wi::PackInfoManager::SetServiceUrl" = 1; + "key-dword-wi::CompleteManager::IsComplete" = 1; + "key-dword-wi::CompleteManager::MarkComplete" = 1; + "left-int-wi::PlaceStructureForm::GetSubRects" = 1; + "m_ff-dword-wi::SimUIForm::FingerHandler::OnPenMove2" = 1; + "m_wf-word-wi::LabelControl::OnHitTest" = 1; + "right-int-wi::PlaceStructureForm::GetSubRects" = 1; + "temppackid_-wi::PackId-wi::PackInfoManager::SetServiceUrl" = 1; + "top-int-wi::PlaceStructureForm::GetSubRects" = 1; + "wrcInside-wi::WRect-wi::PlaceStructureForm::GetSubRects" = 1; + }; + }; + C1F1F5F60D9E137E00FF3191 /* Source Control */ = { + isa = PBXSourceControlManager; + fallbackIsa = XCSourceControlManager; + isSCMEnabled = 0; + scmConfiguration = { + repositoryNamesForRoots = { + "" = ""; + }; + }; + }; + C1F1F5F70D9E137E00FF3191 /* Code sense */ = { + isa = PBXCodeSenseManager; + indexTemplatePath = ""; + }; +} diff --git a/game/wi.xcodeproj/scottlu.perspectivev3 b/game/wi.xcodeproj/scottlu.perspectivev3 new file mode 100644 index 0000000..09bcd5d --- /dev/null +++ b/game/wi.xcodeproj/scottlu.perspectivev3 @@ -0,0 +1,1603 @@ + + + + + ActivePerspectiveName + Debug + AllowedModules + + + BundleLoadPath + + MaxInstances + n + Module + PBXSmartGroupTreeModule + Name + Groups and Files Outline View + + + BundleLoadPath + + MaxInstances + n + Module + PBXNavigatorGroup + Name + Editor + + + BundleLoadPath + + MaxInstances + n + Module + XCTaskListModule + Name + Task List + + + BundleLoadPath + + MaxInstances + n + Module + XCDetailModule + Name + File and Smart Group Detail Viewer + + + BundleLoadPath + + MaxInstances + 1 + Module + PBXBuildResultsModule + Name + Detailed Build Results Viewer + + + BundleLoadPath + + MaxInstances + 1 + Module + PBXProjectFindModule + Name + Project Batch Find Tool + + + BundleLoadPath + + MaxInstances + n + Module + XCProjectFormatConflictsModule + Name + Project Format Conflicts List + + + BundleLoadPath + + MaxInstances + n + Module + PBXBookmarksModule + Name + Bookmarks Tool + + + BundleLoadPath + + MaxInstances + n + Module + PBXClassBrowserModule + Name + Class Browser + + + BundleLoadPath + + MaxInstances + n + Module + PBXCVSModule + Name + Source Code Control Tool + + + BundleLoadPath + + MaxInstances + n + Module + PBXDebugBreakpointsModule + Name + Debug Breakpoints Tool + + + BundleLoadPath + + MaxInstances + n + Module + XCDockableInspector + Name + Inspector + + + BundleLoadPath + + MaxInstances + n + Module + PBXOpenQuicklyModule + Name + Open Quickly Tool + + + BundleLoadPath + + MaxInstances + 1 + Module + PBXDebugSessionModule + Name + Debugger + + + BundleLoadPath + + MaxInstances + 1 + Module + PBXDebugCLIModule + Name + Debug Console + + + BundleLoadPath + + MaxInstances + n + Module + XCSnapshotModule + Name + Snapshots Tool + + + BundlePath + /Developer/Library/PrivateFrameworks/DevToolsInterface.framework/Resources + Description + AIODescriptionKey + DockingSystemVisible + + Extension + perspectivev3 + FavBarConfig + + PBXProjectModuleGUID + C1F1F5F50D9E137E00FF3191 + XCBarModuleItemNames + + XCBarModuleItems + + + FirstTimeWindowDisplayed + + Identifier + com.apple.perspectives.project.defaultV3 + MajorVersion + 34 + MinorVersion + 0 + Name + All-In-One + Notifications + + + XCObserverAutoDisconnectKey + + XCObserverDefintionKey + + PBXStatusErrorsKey + 0 + + XCObserverFactoryKey + XCPerspectivesSpecificationIdentifier + XCObserverGUIDKey + XCObserverProjectIdentifier + XCObserverNotificationKey + PBXStatusBuildStateMessageNotification + XCObserverTargetKey + XCMainBuildResultsModuleGUID + XCObserverTriggerKey + awakenModuleWithObserver: + XCObserverValidationKey + + PBXStatusErrorsKey + 2 + + + + XCObserverAutoDisconnectKey + + XCObserverDefintionKey + + PBXStatusWarningsKey + 0 + + XCObserverFactoryKey + XCPerspectivesSpecificationIdentifier + XCObserverGUIDKey + XCObserverProjectIdentifier + XCObserverNotificationKey + PBXStatusBuildStateMessageNotification + XCObserverTargetKey + XCMainBuildResultsModuleGUID + XCObserverTriggerKey + awakenModuleWithObserver: + XCObserverValidationKey + + PBXStatusWarningsKey + 2 + + + + XCObserverAutoDisconnectKey + + XCObserverDefintionKey + + PBXStatusAnalyzerResultsKey + 0 + + XCObserverFactoryKey + XCPerspectivesSpecificationIdentifier + XCObserverGUIDKey + XCObserverProjectIdentifier + XCObserverNotificationKey + PBXStatusBuildStateMessageNotification + XCObserverTargetKey + XCMainBuildResultsModuleGUID + XCObserverTriggerKey + awakenModuleWithObserver: + XCObserverValidationKey + + PBXStatusAnalyzerResultsKey + 2 + + + + OpenEditors + + PerspectiveWidths + + 1206 + 1206 + + Perspectives + + + ChosenToolbarItems + + XCToolbarPerspectiveControl + NSToolbarSeparatorItem + active-combo-popup + action + NSToolbarFlexibleSpaceItem + debugger-enable-breakpoints + build-and-go + com.apple.ide.PBXToolbarStopButton + get-info + NSToolbarFlexibleSpaceItem + com.apple.pbx.toolbar.searchfield + + ControllerClassBaseName + + IconName + WindowOfProject + Identifier + perspective.project + IsVertical + + Layout + + + ContentConfiguration + + PBXBottomSmartGroupGIDs + + 1C37FBAC04509CD000000102 + 1C37FAAC04509CD000000102 + 1C37FABC05509CD000000102 + 1C37FABC05539CD112110102 + E2644B35053B69B200211256 + 1C37FABC04509CD000100104 + 1CC0EA4004350EF90044410B + 1CC0EA4004350EF90041110B + 1C77FABC04509CD000000102 + + PBXProjectModuleGUID + 1CA23ED40692098700951B8B + PBXProjectModuleLabel + Files + PBXProjectStructureProvided + yes + PBXSmartGroupTreeModuleColumnData + + PBXSmartGroupTreeModuleColumnWidthsKey + + 212 + + PBXSmartGroupTreeModuleColumnsKey_v4 + + MainColumn + + + PBXSmartGroupTreeModuleOutlineStateKey_v7 + + PBXSmartGroupTreeModuleOutlineStateExpansionKey + + 29B97314FDCFA39411CA2CEA + 080E96DDFE201D6D7F000001 + C17D66A80D9E15CA00CB9F01 + C176855A0E0C354A0058E49D + 1C37FBAC04509CD000000102 + 1C37FAAC04509CD000000102 + 1C77FABC04509CD000000102 + 1C3E0DCA080725EA00A55177 + + PBXSmartGroupTreeModuleOutlineStateSelectionKey + + + 0 + + + PBXSmartGroupTreeModuleOutlineStateVisibleRectKey + {{0, 0}, {212, 895}} + + PBXTopSmartGroupGIDs + + XCIncludePerspectivesSwitch + + + GeometryConfiguration + + Frame + {{0, 0}, {229, 913}} + GroupTreeTableConfiguration + + MainColumn + 212 + + + Module + PBXSmartGroupTreeModule + Proportion + 229pt + + + Dock + + + ContentConfiguration + + PBXProjectModuleGUID + C1F1F5F00D9E137E00FF3191 + PBXProjectModuleLabel + Entitlements.plist + PBXSplitModuleInNavigatorKey + + Split0 + + PBXProjectModuleGUID + C1F1F5F10D9E137E00FF3191 + PBXProjectModuleLabel + Entitlements.plist + _historyCapacity + 0 + bookmark + C1197B2E1304EBA40060149F + history + + C11FDC8E10367ACB0053CD83 + C16054881039F7CD00FBFC51 + C16054A41039FBBC00FBFC51 + C16054CB103B2F6C00FBFC51 + C16054CC103B2F6C00FBFC51 + C16054DA103B2F7D00FBFC51 + C16054E4103B317500FBFC51 + C16054FA103B3F4A00FBFC51 + C1605501103B3F7900FBFC51 + C1605587103CA6D900FBFC51 + C160558C103CA70300FBFC51 + C16055A5103CB03F00FBFC51 + C16055C8103DEDD900FBFC51 + C157869F10446365006E0AC2 + C15786B110446A4F006E0AC2 + C15786D110449E05006E0AC2 + C1450481105C543F006C5B5F + C1450482105C543F006C5B5F + C14505A7106047AE006C5B5F + C145063510640A36006C5B5F + C145064410641680006C5B5F + C145066F10644251006C5B5F + C145067010644251006C5B5F + C145069D10646E7D006C5B5F + C14506AF1064715E006C5B5F + C12684A810659FAE00E3CBFE + C12684CA1067F39500E3CBFE + C12684E61067F7EC00E3CBFE + C12684E71067F7EC00E3CBFE + C126853010693E1100E3CBFE + C126857A1069831000E3CBFE + C12685BE106AAB9800E3CBFE + C12685DA106ACA7500E3CBFE + C10390C6107269AD00F4E6BD + C10390DC10726A8800F4E6BD + C1D9668E108511E20088A942 + C1D966A81086D2080088A942 + C1D966B91086D6C70088A942 + C1D966BA1086D6C70088A942 + C1D966BB1086D6C70088A942 + C1D966BE1086D6C70088A942 + C1D966CC1086DBCE0088A942 + C1D966CF1086DC3F0088A942 + C1D966E01086DFF00088A942 + C1D966E11086DFF00088A942 + C104AA241095025E003D2E59 + C1713BD311AC78B600EDF4E7 + C18423F111ECE9B8007BDB3D + C184242B11EE30B5007BDB3D + C184249311EE4F4C007BDB3D + C1197B2812F4A4620060149F + + + SplitCount + 1 + + StatusBarVisibility + + XCSharingToken + com.apple.Xcode.CommonNavigatorGroupSharingToken + + GeometryConfiguration + + Frame + {{0, 0}, {972, 193}} + + Module + PBXNavigatorGroup + Proportion + 193pt + + + Proportion + 715pt + Tabs + + + ContentConfiguration + + PBXProjectModuleGUID + 1CA23EDF0692099D00951B8B + PBXProjectModuleLabel + Detail + + GeometryConfiguration + + Frame + {{10, 27}, {972, 688}} + + Module + XCDetailModule + + + ContentConfiguration + + PBXProjectModuleGUID + 1CA23EE00692099D00951B8B + PBXProjectModuleLabel + Project Find + + GeometryConfiguration + + Frame + {{10, 27}, {888, 187}} + + Module + PBXProjectFindModule + + + ContentConfiguration + + PBXCVSModuleFilterTypeKey + 1032 + PBXProjectModuleGUID + 1CA23EE10692099D00951B8B + PBXProjectModuleLabel + SCM Results + + GeometryConfiguration + + Frame + {{10, 27}, {888, 187}} + + Module + PBXCVSModule + + + ContentConfiguration + + PBXProjectModuleGUID + XCMainBuildResultsModuleGUID + PBXProjectModuleLabel + Build Results + XCBuildResultsTrigger_Collapse + 1023 + XCBuildResultsTrigger_Open + 1013 + + GeometryConfiguration + + Frame + {{10, 27}, {972, 530}} + + Module + PBXBuildResultsModule + + + + + Proportion + 972pt + + + Name + Project + ServiceClasses + + XCModuleDock + PBXSmartGroupTreeModule + XCModuleDock + PBXNavigatorGroup + XCDockableTabModule + XCDetailModule + PBXProjectFindModule + PBXCVSModule + PBXBuildResultsModule + + TableOfContents + + C1197B0B12D9774B0060149F + 1CA23ED40692098700951B8B + C1197B0C12D9774B0060149F + C1F1F5F00D9E137E00FF3191 + C1197B0D12D9774B0060149F + 1CA23EDF0692099D00951B8B + 1CA23EE00692099D00951B8B + 1CA23EE10692099D00951B8B + XCMainBuildResultsModuleGUID + + ToolbarConfigUserDefaultsMinorVersion + 2 + ToolbarConfiguration + xcode.toolbar.config.defaultV3 + + + ChosenToolbarItems + + XCToolbarPerspectiveControl + NSToolbarSeparatorItem + build-and-go + go + NSToolbarFlexibleSpaceItem + debugger-fix-and-continue + debugger-restart-executable + debugger-pause + debugger-step-over + debugger-step-into + debugger-step-out + debugger-step-instruction + NSToolbarFlexibleSpaceItem + + ControllerClassBaseName + PBXDebugSessionModule + IconName + DebugTabIcon + Identifier + perspective.debug + IsVertical + + Layout + + + BecomeActive + + ContentConfiguration + + PBXProjectModuleGUID + 1CCC7628064C1048000F2A68 + PBXProjectModuleLabel + Debugger Console + + GeometryConfiguration + + Frame + {{0, 0}, {1206, 195}} + RubberWindowFrame + 163 145 1206 954 0 0 1920 1178 + + Module + PBXDebugCLIModule + Proportion + 195pt + + + ContentConfiguration + + Debugger + + HorizontalSplitView + + _collapsingFrameDimension + 0.0 + _indexOfCollapsedView + 0 + _percentageOfCollapsedView + 0.0 + isCollapsed + yes + sizes + + {{0, 0}, {576, 233}} + {{576, 0}, {630, 233}} + + + VerticalSplitView + + _collapsingFrameDimension + 0.0 + _indexOfCollapsedView + 0 + _percentageOfCollapsedView + 0.0 + isCollapsed + yes + sizes + + {{0, 0}, {1206, 233}} + {{0, 233}, {1206, 480}} + + + + LauncherConfigVersion + 8 + PBXProjectModuleGUID + 1CCC7629064C1048000F2A68 + PBXProjectModuleLabel + Debug + + GeometryConfiguration + + DebugConsoleVisible + None + DebugConsoleWindowFrame + {{200, 200}, {500, 300}} + DebugSTDIOWindowFrame + {{200, 200}, {500, 300}} + Frame + {{0, 200}, {1206, 713}} + PBXDebugSessionStackFrameViewKey + + DebugVariablesTableConfiguration + + Name + 210 + Value + 85 + Summary + 310 + + Frame + {{576, 0}, {630, 233}} + RubberWindowFrame + 163 145 1206 954 0 0 1920 1178 + + RubberWindowFrame + 163 145 1206 954 0 0 1920 1178 + + Module + PBXDebugSessionModule + Proportion + 713pt + + + Name + Debug + ServiceClasses + + XCModuleDock + PBXDebugCLIModule + PBXDebugSessionModule + PBXDebugProcessAndThreadModule + PBXDebugProcessViewModule + PBXDebugThreadViewModule + PBXDebugStackFrameViewModule + PBXNavigatorGroup + + TableOfContents + + C1197B0E12D9774B0060149F + 1CCC7628064C1048000F2A68 + 1CCC7629064C1048000F2A68 + C1197B0F12D9774B0060149F + C1197B1012D9774B0060149F + C1197B1112D9774B0060149F + C1197B1212D9774B0060149F + C1F1F5F00D9E137E00FF3191 + + ToolbarConfigUserDefaultsMinorVersion + 2 + ToolbarConfiguration + xcode.toolbar.config.debugV3 + + + PerspectivesBarVisible + + ShelfIsVisible + + SourceDescription + file at '/Developer/Library/PrivateFrameworks/DevToolsInterface.framework/Resources/XCPerspectivesSpecification.xcperspec' + StatusbarIsVisible + + TimeStamp + 319089572.71041697 + ToolbarDisplayMode + 2 + ToolbarIsVisible + + ToolbarSizeMode + 2 + Type + Perspectives + UpdateMessage + + WindowJustification + 5 + WindowOrderList + + C1197B1B12EA94450060149F + C1197B1412D9774B0060149F + /Users/scottlu/wi/game/wi.xcodeproj + + WindowString + 163 145 1206 954 0 0 1920 1178 + WindowToolsV3 + + + Identifier + windowTool.debugger + Layout + + + Dock + + + ContentConfiguration + + Debugger + + HorizontalSplitView + + _collapsingFrameDimension + 0.0 + _indexOfCollapsedView + 0 + _percentageOfCollapsedView + 0.0 + isCollapsed + yes + sizes + + {{0, 0}, {317, 164}} + {{317, 0}, {377, 164}} + + + VerticalSplitView + + _collapsingFrameDimension + 0.0 + _indexOfCollapsedView + 0 + _percentageOfCollapsedView + 0.0 + isCollapsed + yes + sizes + + {{0, 0}, {694, 164}} + {{0, 164}, {694, 216}} + + + + LauncherConfigVersion + 8 + PBXProjectModuleGUID + 1C162984064C10D400B95A72 + PBXProjectModuleLabel + Debug - GLUTExamples (Underwater) + + GeometryConfiguration + + DebugConsoleDrawerSize + {100, 120} + DebugConsoleVisible + None + DebugConsoleWindowFrame + {{200, 200}, {500, 300}} + DebugSTDIOWindowFrame + {{200, 200}, {500, 300}} + Frame + {{0, 0}, {694, 380}} + RubberWindowFrame + 321 238 694 422 0 0 1440 878 + + Module + PBXDebugSessionModule + Proportion + 100% + + + Proportion + 100% + + + Name + Debugger + ServiceClasses + + PBXDebugSessionModule + + StatusbarIsVisible + 1 + TableOfContents + + 1CD10A99069EF8BA00B06720 + 1C0AD2AB069F1E9B00FABCE6 + 1C162984064C10D400B95A72 + 1C0AD2AC069F1E9B00FABCE6 + + ToolbarConfiguration + xcode.toolbar.config.debugV3 + WindowString + 321 238 694 422 0 0 1440 878 + WindowToolGUID + 1CD10A99069EF8BA00B06720 + WindowToolIsVisible + 0 + + + Identifier + windowTool.build + Layout + + + Dock + + + ContentConfiguration + + PBXProjectModuleGUID + 1CD0528F0623707200166675 + PBXProjectModuleLabel + <No Editor> + PBXSplitModuleInNavigatorKey + + Split0 + + PBXProjectModuleGUID + 1CD052900623707200166675 + + SplitCount + 1 + + StatusBarVisibility + 1 + + GeometryConfiguration + + Frame + {{0, 0}, {500, 215}} + RubberWindowFrame + 192 257 500 500 0 0 1280 1002 + + Module + PBXNavigatorGroup + Proportion + 218pt + + + BecomeActive + 1 + ContentConfiguration + + PBXProjectModuleGUID + XCMainBuildResultsModuleGUID + PBXProjectModuleLabel + Build + + GeometryConfiguration + + Frame + {{0, 222}, {500, 236}} + RubberWindowFrame + 192 257 500 500 0 0 1280 1002 + + Module + PBXBuildResultsModule + Proportion + 236pt + + + Proportion + 458pt + + + Name + Build Results + ServiceClasses + + PBXBuildResultsModule + + StatusbarIsVisible + 1 + TableOfContents + + 1C78EAA5065D492600B07095 + 1C78EAA6065D492600B07095 + 1CD0528F0623707200166675 + XCMainBuildResultsModuleGUID + + ToolbarConfiguration + xcode.toolbar.config.buildV3 + WindowString + 192 257 500 500 0 0 1280 1002 + + + Identifier + windowTool.find + Layout + + + Dock + + + Dock + + + ContentConfiguration + + PBXProjectModuleGUID + 1CDD528C0622207200134675 + PBXProjectModuleLabel + <No Editor> + PBXSplitModuleInNavigatorKey + + Split0 + + PBXProjectModuleGUID + 1CD0528D0623707200166675 + + SplitCount + 1 + + StatusBarVisibility + 1 + + GeometryConfiguration + + Frame + {{0, 0}, {781, 167}} + RubberWindowFrame + 62 385 781 470 0 0 1440 878 + + Module + PBXNavigatorGroup + Proportion + 781pt + + + Proportion + 50% + + + BecomeActive + 1 + ContentConfiguration + + PBXProjectModuleGUID + 1CD0528E0623707200166675 + PBXProjectModuleLabel + Project Find + + GeometryConfiguration + + Frame + {{8, 0}, {773, 254}} + RubberWindowFrame + 62 385 781 470 0 0 1440 878 + + Module + PBXProjectFindModule + Proportion + 50% + + + Proportion + 428pt + + + Name + Project Find + ServiceClasses + + PBXProjectFindModule + + StatusbarIsVisible + 1 + TableOfContents + + 1C530D57069F1CE1000CFCEE + 1C530D58069F1CE1000CFCEE + 1C530D59069F1CE1000CFCEE + 1CDD528C0622207200134675 + 1C530D5A069F1CE1000CFCEE + 1CE0B1FE06471DED0097A5F4 + 1CD0528E0623707200166675 + + WindowString + 62 385 781 470 0 0 1440 878 + WindowToolGUID + 1C530D57069F1CE1000CFCEE + WindowToolIsVisible + 0 + + + Identifier + windowTool.snapshots + Layout + + + Dock + + + Module + XCSnapshotModule + Proportion + 100% + + + Proportion + 100% + + + Name + Snapshots + ServiceClasses + + XCSnapshotModule + + StatusbarIsVisible + Yes + ToolbarConfiguration + xcode.toolbar.config.snapshots + WindowString + 315 824 300 550 0 0 1440 878 + WindowToolIsVisible + Yes + + + Identifier + windowTool.debuggerConsole + Layout + + + Dock + + + BecomeActive + 1 + ContentConfiguration + + PBXProjectModuleGUID + 1C78EAAC065D492600B07095 + PBXProjectModuleLabel + Debugger Console + + GeometryConfiguration + + Frame + {{0, 0}, {700, 358}} + RubberWindowFrame + 149 87 700 400 0 0 1440 878 + + Module + PBXDebugCLIModule + Proportion + 358pt + + + Proportion + 358pt + + + Name + Debugger Console + ServiceClasses + + PBXDebugCLIModule + + StatusbarIsVisible + 1 + TableOfContents + + 1C530D5B069F1CE1000CFCEE + 1C530D5C069F1CE1000CFCEE + 1C78EAAC065D492600B07095 + + ToolbarConfiguration + xcode.toolbar.config.consoleV3 + WindowString + 149 87 440 400 0 0 1440 878 + WindowToolGUID + 1C530D5B069F1CE1000CFCEE + WindowToolIsVisible + 0 + + + Identifier + windowTool.scm + Layout + + + Dock + + + ContentConfiguration + + PBXProjectModuleGUID + 1C78EAB2065D492600B07095 + PBXProjectModuleLabel + <No Editor> + PBXSplitModuleInNavigatorKey + + Split0 + + PBXProjectModuleGUID + 1C78EAB3065D492600B07095 + + SplitCount + 1 + + StatusBarVisibility + 1 + + GeometryConfiguration + + Frame + {{0, 0}, {452, 0}} + RubberWindowFrame + 743 379 452 308 0 0 1280 1002 + + Module + PBXNavigatorGroup + Proportion + 0pt + + + BecomeActive + 1 + ContentConfiguration + + PBXProjectModuleGUID + 1CD052920623707200166675 + PBXProjectModuleLabel + SCM + + GeometryConfiguration + + ConsoleFrame + {{0, 259}, {452, 0}} + Frame + {{0, 7}, {452, 259}} + RubberWindowFrame + 743 379 452 308 0 0 1280 1002 + TableConfiguration + + Status + 30 + FileName + 199 + Path + 197.09500122070312 + + TableFrame + {{0, 0}, {452, 250}} + + Module + PBXCVSModule + Proportion + 262pt + + + Proportion + 266pt + + + Name + SCM + ServiceClasses + + PBXCVSModule + + StatusbarIsVisible + 1 + TableOfContents + + 1C78EAB4065D492600B07095 + 1C78EAB5065D492600B07095 + 1C78EAB2065D492600B07095 + 1CD052920623707200166675 + + ToolbarConfiguration + xcode.toolbar.config.scmV3 + WindowString + 743 379 452 308 0 0 1280 1002 + + + FirstTimeWindowDisplayed + + Identifier + windowTool.breakpoints + IsVertical + + Layout + + + Dock + + + ContentConfiguration + + PBXBottomSmartGroupGIDs + + 1C77FABC04509CD000000102 + + PBXProjectModuleGUID + 1CE0B1FE06471DED0097A5F4 + PBXProjectModuleLabel + Files + PBXProjectStructureProvided + no + PBXSmartGroupTreeModuleColumnData + + PBXSmartGroupTreeModuleColumnWidthsKey + + 168 + + PBXSmartGroupTreeModuleColumnsKey_v4 + + MainColumn + + + PBXSmartGroupTreeModuleOutlineStateKey_v7 + + PBXSmartGroupTreeModuleOutlineStateExpansionKey + + 1C77FABC04509CD000000102 + + PBXSmartGroupTreeModuleOutlineStateSelectionKey + + + 0 + + + PBXSmartGroupTreeModuleOutlineStateVisibleRectKey + {{0, 0}, {168, 350}} + + PBXTopSmartGroupGIDs + + XCIncludePerspectivesSwitch + + + GeometryConfiguration + + Frame + {{0, 0}, {185, 368}} + GroupTreeTableConfiguration + + MainColumn + 168 + + RubberWindowFrame + 620 586 744 409 0 0 1920 1178 + + Module + PBXSmartGroupTreeModule + Proportion + 185pt + + + BecomeActive + + ContentConfiguration + + PBXProjectModuleGUID + 1CA1AED706398EBD00589147 + PBXProjectModuleLabel + Detail + + GeometryConfiguration + + Frame + {{190, 0}, {554, 368}} + RubberWindowFrame + 620 586 744 409 0 0 1920 1178 + + Module + XCDetailModule + Proportion + 554pt + + + Proportion + 368pt + + + MajorVersion + 3 + MinorVersion + 0 + Name + Breakpoints + ServiceClasses + + PBXSmartGroupTreeModule + XCDetailModule + + StatusbarIsVisible + + TableOfContents + + C1B1B2C50E81865500A4A001 + C1B1B2C60E81865500A4A001 + 1CE0B1FE06471DED0097A5F4 + 1CA1AED706398EBD00589147 + + ToolbarConfiguration + xcode.toolbar.config.breakpointsV3 + WindowString + 620 586 744 409 0 0 1920 1178 + WindowToolGUID + C1B1B2C50E81865500A4A001 + WindowToolIsVisible + + + + Identifier + windowTool.debugAnimator + Layout + + + Dock + + + Module + PBXNavigatorGroup + Proportion + 100% + + + Proportion + 100% + + + Name + Debug Visualizer + ServiceClasses + + PBXNavigatorGroup + + StatusbarIsVisible + 1 + ToolbarConfiguration + xcode.toolbar.config.debugAnimatorV3 + WindowString + 100 100 700 500 0 0 1280 1002 + + + Identifier + windowTool.bookmarks + Layout + + + Dock + + + Module + PBXBookmarksModule + Proportion + 166pt + + + Proportion + 166pt + + + Name + Bookmarks + ServiceClasses + + PBXBookmarksModule + + StatusbarIsVisible + 0 + WindowString + 538 42 401 187 0 0 1280 1002 + + + Identifier + windowTool.projectFormatConflicts + Layout + + + Dock + + + Module + XCProjectFormatConflictsModule + Proportion + 100% + + + Proportion + 100% + + + Name + Project Format Conflicts + ServiceClasses + + XCProjectFormatConflictsModule + + StatusbarIsVisible + 0 + WindowContentMinSize + 450 300 + WindowString + 50 850 472 307 0 0 1440 877 + + + Identifier + windowTool.classBrowser + Layout + + + Dock + + + BecomeActive + 1 + ContentConfiguration + + OptionsSetName + Hierarchy, all classes + PBXProjectModuleGUID + 1CA6456E063B45B4001379D8 + PBXProjectModuleLabel + Class Browser - NSObject + + GeometryConfiguration + + ClassesFrame + {{0, 0}, {369, 96}} + ClassesTreeTableConfiguration + + PBXClassNameColumnIdentifier + 208 + PBXClassBookColumnIdentifier + 22 + + Frame + {{0, 0}, {616, 353}} + MembersFrame + {{0, 105}, {369, 395}} + MembersTreeTableConfiguration + + PBXMemberTypeIconColumnIdentifier + 22 + PBXMemberNameColumnIdentifier + 216 + PBXMemberTypeColumnIdentifier + 94 + PBXMemberBookColumnIdentifier + 22 + + PBXModuleWindowStatusBarHidden2 + 1 + RubberWindowFrame + 597 125 616 374 0 0 1280 1002 + + Module + PBXClassBrowserModule + Proportion + 354pt + + + Proportion + 354pt + + + Name + Class Browser + ServiceClasses + + PBXClassBrowserModule + + StatusbarIsVisible + 0 + TableOfContents + + 1C78EABA065D492600B07095 + 1C78EABB065D492600B07095 + 1CA6456E063B45B4001379D8 + + ToolbarConfiguration + xcode.toolbar.config.classbrowser + WindowString + 597 125 616 374 0 0 1280 1002 + + + Identifier + windowTool.refactoring + IncludeInToolsMenu + 0 + Layout + + + Dock + + + BecomeActive + 1 + GeometryConfiguration + + Frame + {0, 0}, {500, 335} + RubberWindowFrame + {0, 0}, {500, 335} + + Module + XCRefactoringModule + Proportion + 100% + + + Proportion + 100% + + + Name + Refactoring + ServiceClasses + + XCRefactoringModule + + WindowString + 200 200 500 356 0 0 1920 1200 + + + + diff --git a/game/win/BtSerTransport.cpp b/game/win/BtSerTransport.cpp new file mode 100644 index 0000000..50b6c2f --- /dev/null +++ b/game/win/BtSerTransport.cpp @@ -0,0 +1,615 @@ +#include "..\ht.h" +#include "..\Multiplayer.h" +#include +#include +#ifndef CE +#include +#include +#include +#endif + +// The 'inbound' port must be used to 'Advertise' availability of this device +// for others to connect to it. The 'outbound' port, when used, will invoke +// "Bluetooth Browser" UI for connecting to another device. + +int BtSerTransport::GetTransportDescriptions(TransportDescription *atrad, int ctradMax) +{ + // Make sure the BT com port is there and available + + word wInboundPort = 0xffff, wOutboundPort = 0xffff; + +#ifdef CE + HKEY hkey; + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("Drivers\\BuiltIn"), 0, KEY_ALL_ACCESS, &hkey) != ERROR_SUCCESS) + return 0; + + int i = 0; + while (true) { + TCHAR tszT[100]; + FILETIME ftT; + DWORD cbtszT = sizeof(tszT); + if (RegEnumKeyEx(hkey, i++, (TCHAR *)&tszT, &cbtszT, NULL, NULL, NULL, &ftT) != ERROR_SUCCESS) + break; + + HKEY hkeyDevice; + if (RegOpenKeyEx(hkey, tszT, 0, KEY_ALL_ACCESS, &hkeyDevice) != ERROR_SUCCESS) + break; + + // Look for devices with "COM" prefixes + + TCHAR tszPrefix[50]; + DWORD cbT = sizeof(tszPrefix); + if (RegQueryValueEx(hkeyDevice, TEXT("Prefix"), 0, NULL, (byte *)tszPrefix, &cbT) != ERROR_SUCCESS) { + RegCloseKey(hkeyDevice); + continue; + } + if (_tcscmp(tszPrefix, TEXT("COM")) != 0) { + RegCloseKey(hkeyDevice); + continue; + } + + // Get this COM device's index + + DWORD dwIndex; + cbT = sizeof(DWORD); + if (RegQueryValueEx(hkeyDevice, TEXT("Index"), 0, NULL, (byte *)&dwIndex, &cbT) != ERROR_SUCCESS) { + RegCloseKey(hkeyDevice); + continue; + } + +#if 0 // don't do this because it can make UI ("Bluetooth Browser") pop up + // See if we can open this port + + TCHAR tszComPort[20]; + _stprintf(tszComPort, TEXT("COM%d:"), dwIndex); + HANDLE hf = CreateFile(tszComPort, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + if (hf == INVALID_HANDLE_VALUE) { + RegCloseKey(hkeyDevice); + continue; + } + CloseHandle(hf); +#endif + + // Get the device's friendly name + + TCHAR tszFriendly[100]; + cbT = sizeof(tszFriendly); + if (RegQueryValueEx(hkeyDevice, TEXT("FriendlyName"), 0, NULL, (byte *)tszFriendly, &cbT) != ERROR_SUCCESS) { +// sprintf(ptrad->szName, "COM%d", dwIndex); + RegCloseKey(hkeyDevice); + continue; + + } + + RegCloseKey(hkeyDevice); + + // Is this a Bluetooth port + // UNDONE: friendly names are localized? + + _tcsupr(tszFriendly); + if (_tcsstr(tszFriendly, TEXT("BLUETOOTH")) != NULL) { + + // Assign first Bluetooth port as the inbound port, second as the outbound port + + if (wInboundPort == 0xffff) + wInboundPort = (word)dwIndex; + else if (wOutboundPort == 0xffff) + wOutboundPort = (word)dwIndex; + } + } + + RegCloseKey(hkey); + +#else + HDEVINFO hdevi = INVALID_HANDLE_VALUE; + SP_DEVICE_INTERFACE_DETAIL_DATA *pDetData = NULL; + + // Let's take a look at com ports + + hdevi = SetupDiGetClassDevs(&GUID_CLASS_COMPORT, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + + if (hdevi == INVALID_HANDLE_VALUE) { + Assert("SetupDiGetClassDevs failed. (err=%lx)", GetLastError()); + return 0; + } + + // Enumerate the serial ports + + SP_DEVICE_INTERFACE_DATA difd; + DWORD dwDetDataSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) + 256; + pDetData = (SP_DEVICE_INTERFACE_DETAIL_DATA*) new char[dwDetDataSize]; + + // This is required, according to the documentation. Yes, it's weird. + + difd.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + pDetData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + for (int i = 0; true; i++) { + if (!SetupDiEnumDeviceInterfaces(hdevi, NULL, &GUID_CLASS_COMPORT, i, &difd)) { + DWORD err = GetLastError(); + if (err != ERROR_NO_MORE_ITEMS) + Assert("SetupDiEnumDeviceInterfaces failed. (err=%lx)", err); + break; + } + + SP_DEVINFO_DATA devdata = { sizeof(SP_DEVINFO_DATA) }; + if (!SetupDiGetDeviceInterfaceDetail(hdevi, &difd, pDetData, dwDetDataSize, NULL, &devdata)) { + Assert("SetupDiGetDeviceInterfaceDetail failed. (err=%lx)", GetLastError()); + break; + } + + // Got a path to the device. Try to get some more info. + +// TCHAR szFriendly[256]; +// BOOL fSuccess = SetupDiGetDeviceRegistryProperty(hdevi, &devdata, SPDRP_FRIENDLYNAME, NULL, (PBYTE)szFriendly, sizeof(szFriendly), NULL); + TCHAR szDesc[256]; + BOOL fSuccess = SetupDiGetDeviceRegistryProperty(hdevi, &devdata, SPDRP_DEVICEDESC, NULL, (PBYTE)szDesc, sizeof(szDesc), NULL); + if (!fSuccess) + continue; + + // Is this a Bluetooth port? + + strupr(szDesc); + if ((strstr(szDesc, "BLUETOOTH")) != NULL) { + + // Get its PortName + + HKEY hkey = SetupDiOpenDevRegKey(hdevi, &devdata, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_ALL_ACCESS); + if (hkey == NULL) + continue; + + TCHAR szPortName[256]; + DWORD cbT = sizeof(szPortName); + if (RegQueryValueEx(hkey, "PortName", 0, NULL, (byte *)szPortName, &cbT) != ERROR_SUCCESS) { + RegCloseKey(hkey); + continue; + } + + RegCloseKey(hkey); + + // Assign first Bluetooth port as the inbound port, second as the outbound port + + if (wInboundPort == 0xffff) + wInboundPort = (word)(szPortName[3] - '0'); + else if (wOutboundPort == 0xffff) + wOutboundPort = (word)(szPortName[3] - '0'); + strupr(szPortName); + } + } + + delete pDetData; + SetupDiDestroyDeviceInfoList(hdevi); +#endif + + if (wInboundPort == 0xffff || wOutboundPort == 0xffff) + return 0; + atrad[0].dwTransportSpecific = ((DWORD)wInboundPort << 16) | wOutboundPort; + atrad[0].trat = ktratBluetoothSer; + strcpy(atrad[0].szName, "BLUETOOTH SERIAL"); + atrad[0].pfnOpen = BtSerTransport::Open; + + return 1; +} + +Transport *BtSerTransport::Open(TransportDescription *ptrad) +{ + Transport *ptra = new BtSerTransport(ptrad->dwTransportSpecific); + if (ptra == NULL) + return NULL; + + if (!ptra->Open()) + return NULL; + return ptra; +} + +BtSerTransport::BtSerTransport(dword dwPorts) +{ + m_wInboundPort = (word)(dwPorts >> 16); + m_wOutboundPort = (word)(dwPorts & 0xffff); + m_hfInbound = NULL; + m_hfOutbound = NULL; + m_cInboundPortRef = m_cOutboundPortRef = 0; + m_wf = 0; +} + +bool BtSerTransport::Open() +{ + // Nothing to do + + return Transport::Open(); +} + +void BtSerTransport::Close() +{ + Transport::Close(); // Closes any open Connections +} + +HANDLE BtSerTransport::OpenInboundPort() +{ + if (m_hfInbound != NULL) { + m_cInboundPortRef++; + return m_hfInbound; + } + + TCHAR tszPort[10]; + _stprintf(tszPort, TEXT("COM%d:"), m_wInboundPort); + m_hfInbound = CreateFile(tszPort, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + if (m_hfInbound == INVALID_HANDLE_VALUE) { + HtMessageBox(kfMbWhiteBorder, "Communication Error", "Failed to open COM%d (%d).", m_wInboundPort, GetLastError()); + return INVALID_HANDLE_VALUE; + } + + m_cInboundPortRef = 1; + return m_hfInbound; +} + +void BtSerTransport::CloseInboundPort() +{ + Assert(m_cInboundPortRef > 0); + m_cInboundPortRef--; + if (m_cInboundPortRef == 0) { + CloseHandle(m_hfInbound); + m_hfInbound = NULL; + } +} + +HANDLE BtSerTransport::OpenOutboundPort() +{ + if (m_hfOutbound != NULL) { + m_cOutboundPortRef++; + return m_hfOutbound; + } + + TCHAR tszPort[10]; + _stprintf(tszPort, TEXT("COM%d:"), m_wOutboundPort); + m_hfOutbound = CreateFile(tszPort, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + if (m_hfOutbound == INVALID_HANDLE_VALUE) { + HtMessageBox(kfMbWhiteBorder, "Communication Error", "Failed to open COM%d (%d).", m_wOutboundPort, GetLastError()); + return INVALID_HANDLE_VALUE; + } + + m_cOutboundPortRef = 1; + return m_hfOutbound; +} + +void BtSerTransport::CloseOutboundPort() +{ + Assert(m_cOutboundPortRef > 0); + m_cOutboundPortRef--; + if (m_cOutboundPortRef == 0) { + CloseHandle(m_hfOutbound); + m_hfOutbound = NULL; + } +} + +Connection *BtSerTransport::NewConnection() +{ + return new BtSerConnection(); +} + +// BeginGameSearch is called when the JoinOrHostMultiplayer form enters its +// modal loop. EndGameSearch is called when the form is destructed. + +// Serial GameSearch +// - create a non-blocking datagram socket to listen for SERVERINFO broadcasts +// - poll the socket to check for SERVERINFO broadcasts every 1/10th of a second +// - for each SERVERINFO broadcast received call the Transport's registered +// ITransportCallback::OnReceive method + +bool BtSerTransport::BeginGameSearch() +{ + // Already searching? + + if ((m_wf & kfBstSearchingForGames) != 0) + return false; + + if (OpenOutboundPort() == INVALID_HANDLE_VALUE) + return false; + + // UNDONE: set a timeout on the port so future ReadFile's won't block forever? + + // Start timer + + gtimm.AddTimer(this, 10); // Every 100ms, i.e., 1/10 second + + m_wf |= kfBstSearchingForGames; + return true; +} + +void BtSerTransport::EndGameSearch() +{ + if ((m_wf & kfBstSearchingForGames) == 0) + return; + + // Stop timer + + gtimm.RemoveTimer(this); + + CloseOutboundPort(); + m_wf &= ~kfBstSearchingForGames; +} + +bool BtSerTransport::AdvertiseGame(const char *pszGameName) +{ + // Remember the game name we'll be broadcasting + + strncpyz(m_szGameName, (char *)pszGameName, sizeof(m_szGameName)); + + // Create a socket to broadcast through + + if (OpenInboundPort() == INVALID_HANDLE_VALUE) + return false; + + // Start timer + + gtimm.AddTimer(this, 50); // Every 500ms, i.e., 1/2 second + + // Broadcast game availability immediately + + OnTimer(0); + m_wf |= kfBstAdvertisingGame; + return true; +} + +void BtSerTransport::UnadvertiseGame(bool fRetainConnections) +{ + if ((m_wf & kfBstAdvertisingGame) == 0) + return; + + // Stop timer + + gtimm.RemoveTimer(this); + + // Close broadcast port + + CloseInboundPort(); + m_wf &= ~kfBstAdvertisingGame; +} + +void BtSerTransport::OnTimer(long tCurrent) +{ + // If we're Advertising send a SERVERINFO message + + if ((m_wf & kfBstAdvertisingGame) != 0) { + Assert(m_hfInbound != NULL); + + // Broadcast a notification that this device is hosting a game + + ServerInfoNetMessage sinm(m_szGameName); + MpTrace("> %s", PszFromNetMessage(&sinm)); + + dword cb; + if (!WriteFile(m_hfInbound, &sinm, sizeof(sinm), &cb, NULL)) { +#ifdef DEBUG + HostMessageBox(TEXT("WriteFile err: %d"), GetLastError()); +#endif + } + Assert(cb == sizeof(sinm)); + } + + // If we're searching for hosts check for receipt of a SERVERINFO message + + if ((m_wf & kfBstSearchingForGames) != 0) { + Assert(m_hfOutbound != NULL); + + // Anything coming in on the serial port? + + COMSTAT coms; + DWORD dwErrors; + if (!ClearCommError(m_hfOutbound, &dwErrors, &coms)) { +#ifdef DEBUG + HostMessageBox(TEXT("ClearCommError returned %ld"), dwErrors); +#endif + return; + } + + if (coms.cbInQue >= sizeof(ServerInfoNetMessage)) { + ServerInfoNetMessage sinm; + dword cb; + if (!ReadFile(m_hfOutbound, &sinm, sizeof(ServerInfoNetMessage), &cb, NULL)) { +#ifdef DEBUG + HostMessageBox(TEXT("Failed ReadFile ServerInfoNetMessage (%d)"), GetLastError()); +#endif + return; + } + MpTrace("< GAMEHOSTFOUND"); + + if (m_ptcb != NULL) { + NetAddress nad; + memset(&nad, 0, sizeof(nad)); + m_ptcb->OnGameHostFound(&nad); + } + } + } +} + +//--------------------------------------------------------------------------- +// BtSerConnection implementation + +BtSerConnection::BtSerConnection() +{ + m_hf = NULL; + m_fListening = false; + m_fInbound = false; + gptra->AddConnection(this); +} + +BtSerConnection::BtSerConnection(HANDLE hf, bool fInbound) +{ + m_hf = hf; + m_fListening = false; + m_fInbound = fInbound; + gptra->AddConnection(this); +} + +BtSerConnection::~BtSerConnection() +{ + Disconnect(); + gptra->RemoveConnection(this); +} + +bool BtSerConnection::Poll() +{ + if (m_hf == NULL) + return false; + + if (m_fListening) { + + // Accept a client connection and produce a new file handle for communicating with that client + + HANDLE hfInbound = ((BtSerTransport *)gptra)->OpenInboundPort(); + if (hfInbound != INVALID_HANDLE_VALUE) { + Connection *pcon = new BtSerConnection(hfInbound, true); + if (pcon == NULL) + return false; + + if (m_pccb != NULL) + m_pccb->OnConnect(pcon); + } + } + + { + // Test if any incoming data is pending + + COMSTAT coms; + DWORD dwErrors; + if (!ClearCommError(m_hf, &dwErrors, &coms)) { +#ifdef DEBUG + HostMessageBox(TEXT("ClearCommError returned %ld"), dwErrors); +#endif + return false; + } + + if (coms.cbInQue >= sizeof(NetMessage)) { + NetMessage nm; + dword cb; + if (!ReadFile(m_hf, &nm, sizeof(NetMessage), &cb, NULL)) { +#ifdef DEBUG + HostMessageBox(TEXT("Failed ReadFile NetMessage header (%d)"), GetLastError()); +#endif + } + if (cb != sizeof(NetMessage)) { + HandleRecvError(); + return false; + } + + // UNDONE: issue for polymorphic NetMessages? + int cbT = BigWord(nm.cb); + NetMessage *pnm = (NetMessage *)new byte[cbT]; + if (pnm == NULL) { + // Data is still pending but the assumption here is that the + // caller is going to give up (out of memory) and close the socket + // UNDONE: is this a good assumption? + return false; + } + memcpy(pnm, &nm, sizeof(NetMessage)); + dword cbRemaining = cbT - sizeof(NetMessage); + if (cbRemaining != 0) { + dword cbActual; + if (!ReadFile(m_hf, (pnm + 1), cbRemaining, &cbActual, NULL)) { +#ifdef DEBUG + HostMessageBox(TEXT("Failed ReadFile full NetMessage (%d)"), GetLastError()); +#endif + } + + if (cbActual != cbRemaining) { + delete pnm; + HandleRecvError(); + return false; + } + } + + if (m_pccb != NULL) { + // Before calling OnReceive, order in native byte order + + NetMessageByteOrderSwap(BigWord(pnm->nmid), pnm, false); + + MpTrace("< %s", PszFromNetMessage(pnm)); + + m_pccb->OnReceive(this, pnm); + } + delete pnm; + } + } + + return true; +} + +void BtSerConnection::HandleRecvError() +{ + Disconnect(); +} + +void BtSerConnection::HandleSendError() +{ + Disconnect(); +} + +bool BtSerConnection::AsyncListen() +{ + m_hf = ((BtSerTransport *)gptra)->OpenInboundPort(); + if (m_hf == INVALID_HANDLE_VALUE) { + HtMessageBox(kfMbWhiteBorder, "Communication Error", "Failed to open inbound port."); + return false; + } + + m_fListening = true; + return true; +} + +// UNDONE: this implementation is synchronous + +bool BtSerConnection::AsyncConnect(NetAddress *pnad) +{ + m_hf = ((BtSerTransport *)gptra)->OpenOutboundPort(); + if (m_hf == INVALID_HANDLE_VALUE) { + HtMessageBox(kfMbWhiteBorder, "Communication Error", "Failed to connect."); + return false; + } + + // UNDONE: OnConnectComplete + + return true; +} + +bool BtSerConnection::AsyncSend(NetMessage *pnm) +{ + if (m_hf == NULL) + return false; + + MpTrace("> %s", PszFromNetMessage(pnm)); + + // Before sending, order in network byte order + + int cb = pnm->cb; // nab this before byte-swapping it! + NetMessageByteOrderSwap(pnm->nmid, pnm, true); + + // UNDONE: this is synchronous + + dword cbActual; + if (!WriteFile(m_hf, pnm, cb, &cbActual, NULL)) { +#ifdef DEBUG + HostMessageBox(TEXT("WriteFile err: %d"), GetLastError()); +#endif + } + if (cbActual != cb) { + HandleSendError(); + return false; + } + + return true; +} + +void BtSerConnection::Disconnect() +{ + if (m_hf == NULL) + return; + + if (m_fInbound) + ((BtSerTransport *)gptra)->CloseInboundPort(); + else + ((BtSerTransport *)gptra)->CloseOutboundPort(); + m_hf = NULL; + + if (m_pccb != NULL) + m_pccb->OnDisconnect(this); +} diff --git a/game/win/BtSerTransport.h b/game/win/BtSerTransport.h new file mode 100644 index 0000000..231f135 --- /dev/null +++ b/game/win/BtSerTransport.h @@ -0,0 +1,76 @@ +class BtSerConnection; + +class BtSerTransport: public Transport, Timer // tra +{ + friend BtSerConnection; + +public: + static int GetTransportDescriptions(TransportDescription *atrad, int ctradMax); + static Transport *Open(TransportDescription *ptrad); + + BtSerTransport(dword dwPorts); + virtual bool Open(); + virtual void Close(); + virtual Connection *NewConnection(); + + virtual bool BeginGameSearch(); + virtual void EndGameSearch(); + virtual bool AdvertiseGame(const char *pszGameName); + virtual void UnadvertiseGame(bool fRetainConnections); + + // Timer method + + virtual void OnTimer(long tCurrent); + +private: +#if 0 + word GetInboundPort() { + return m_wInboundPort; + } + + word GetOutboundPort() { + return m_wOutboundPort; + } +#endif + + HANDLE OpenInboundPort(); + void CloseInboundPort(); + HANDLE OpenOutboundPort(); + void CloseOutboundPort(); + +private: + char m_szGameName[kcbGameName]; + word m_wInboundPort; + word m_wOutboundPort; + HANDLE m_hfOutbound; + HANDLE m_hfInbound; + int m_cInboundPortRef; + int m_cOutboundPortRef; + word m_wf; +}; + +const word kfBstAdvertisingGame = 0x0001; +const word kfBstSearchingForGames = 0x0002; + +class BtSerConnection : public Connection +{ +public: + BtSerConnection(); + BtSerConnection(HANDLE hf, bool fInbound = false); + ~BtSerConnection(); + void HandleRecvError(); + void HandleSendError(); + + // Connection overrides + + virtual bool Poll(); + virtual bool AsyncListen(); + virtual bool AsyncConnect(NetAddress *pnad); + virtual bool AsyncSend(NetMessage *pnm); + virtual void Disconnect(); + +private: + HANDLE m_hf; + bool m_fListening; + bool m_fInbound; +}; diff --git a/game/win/TransportMgr.cpp b/game/win/TransportMgr.cpp new file mode 100644 index 0000000..4052eb3 --- /dev/null +++ b/game/win/TransportMgr.cpp @@ -0,0 +1,52 @@ +#include "..\ht.h" +#include + +#ifdef CE +// Can't make this work on Windows without spending another $1,400 for the BTW for Windows SDK + +static TransportHost s_trah; +static HMODULE s_hmodTransport = NULL; +#endif + +TransportMgr gtram; + +//--------------------------------------------------------------------------- +// TransportMgr implementation + +int TransportMgr::GetTransportDescriptions(TransportDescription *atrad, int ctradMax) +{ + int ctrad = 0; +#ifdef CE + // First try our favorite PocketPC 2003, BTW-CE 1.4 compatible transport + + if (s_hmodTransport == NULL) + s_hmodTransport = LoadLibrary(TEXT("Wc14Transport.dll")); + + // No? Then try the BTW-CE 1.3 compatible transport + + if (s_hmodTransport == NULL) + s_hmodTransport = LoadLibrary(TEXT("Wc13Transport.dll")); + + // Process cleanup will unload the DLL loaded above (whichever it is) + + if (s_hmodTransport != NULL) { + int (*pfnGetTransportDescriptions)(TransportHost *ptrah, TransportDescription *atrad, int ctradMax); + pfnGetTransportDescriptions = (int (*)(TransportHost *ptrah, TransportDescription *atrad, int ctradMax)) + GetProcAddress(s_hmodTransport, TEXT("GetTransportDescriptions")); + if (pfnGetTransportDescriptions != NULL) { + s_trah.New = (void *(*)(int))::operator new; + s_trah.Delete = ::operator delete; + s_trah.strncpyz = strncpyz; + s_trah.HtMessageBox = HtMessageBox; + s_trah.Status = Status; + s_trah.ptimm = >imm; + + ctrad += pfnGetTransportDescriptions(&s_trah, atrad + ctrad, ctradMax - ctrad); + } + } +#endif + + ctrad += WsTransport::GetTransportDescriptions(atrad + ctrad, ctradMax - ctrad); + + return ctrad; +} diff --git a/game/win/WsTransport.cpp b/game/win/WsTransport.cpp new file mode 100644 index 0000000..8452ba8 --- /dev/null +++ b/game/win/WsTransport.cpp @@ -0,0 +1,201 @@ +#include "..\ht.h" +#include "..\Multiplayer.h" +#include + +#ifdef DEBUG +const TCHAR *PszFromSocError(); +const TCHAR *PszFromSocError(int nError); +#endif + +//--------------------------------------------------------------------------- +// WsTransport implementation + +int WsTransport::GetTransportDescriptions(TransportDescription *atrad, int ctradMax) +{ + char szHostName[100]; + gethostname(szHostName, sizeof(szHostName)); + + HOSTENT *phste = gethostbyname(szHostName); + + TransportDescription *ptrad = atrad; + for (int i = 0; phste->h_addr_list[i] != NULL && i < ctradMax; i++, ptrad++) { + in_addr *pina = (in_addr *)phste->h_addr_list[i]; + ptrad->trat = ktratIP; + sprintf(ptrad->szName, "IP %s", inet_ntoa(*pina)); + ptrad->pfnOpen = WsTransport::Open; + ptrad->dwTransportSpecific = ntohl(pina->S_un.S_addr); + } + return i; + +// This is a good start for finding the Bluetooth transport on a device that has +// Microsoft's Bluetooth stack. Haven't encountered one yet. + +#if 0 + WSAQUERYSET qs; + memset(&qs, 0, sizeof(qs)); + qs.dwSize = sizeof(qs); + // UNDONE: namespace? + + HANDLE hls = NULL; + int err = WSALookupServiceBegin(&qs, + LUP_FLUSHCACHE | LUP_NEAREST | LUP_RETURN_NAME | LUP_RETURN_TYPE | LUP_RETURN_COMMENT | LUP_RETURN_ADDR, + &hls); + if (err != 0) { +#ifdef DEBUG + HostMessageBox(TEXT("WSALookupServiceBegin err: %s"), PszFromSocError()); +#else + HostMessageBox(TEXT("Unable to enumerate transports")); +#endif + return 0; + } + + TransportDescription *ptrad = atrad; + while (true) { + DWORD dwSize = sizeof(qs); + + int err = WSALookupServiceNext(hls, + LUP_FLUSHCACHE | LUP_NEAREST | LUP_RETURN_NAME | LUP_RETURN_TYPE | LUP_RETURN_COMMENT | LUP_RETURN_ADDR, + &dwSize, &qs); + if (err == WSA_E_NO_MORE || err == WSAENOMORE) + break; + + if (err != 0) { +#ifdef DEBUG + HostMessageBox(TEXT("WSALookupServiceNet err: %s"), PszFromSocError(err)); +#else + HostMessageBox(TEXT("Unable to enumerate transports")); +#endif + return 0; + } + + // UNDONE: translate qs.lpServiceClassId into a TransportType + strncpyz(ptrad->szName, qs.lpszServiceInstanceName, sizeof(ptrad->szName)); + + ctrad++; + ptrad++; + } + + return ctrad; +#endif +} + +Transport *WsTransport::Open(TransportDescription *ptrad) +{ + Transport *ptra = new WsTransport(ptrad->dwTransportSpecific); + if (ptra == NULL) + return NULL; + + if (!ptra->Open()) + return NULL; + return ptra; +} + +bool WsTransport::Open() +{ + WSADATA wsad; + int err = WSAStartup(MAKEWORD(1, 1), &wsad); + if (err != 0) { + + // unrecoverable low-level problem (e.g., socket limit reached, network + // subsystem not ready, requested version not supported) + +#ifdef DEBUG + HostMessageBox(TEXT("WSAStartup err: %s"), PszFromSocError(err)); +#else + HostMessageBox(TEXT("Unable to initialize network")); +#endif + return false; + } + + return SocTransport::Open(); +} + +void WsTransport::Close() +{ + SocTransport::Close(); // Closes any open Connections + + WSACleanup(); +} + +WsTransport::WsTransport(dword dwIpAddress) : SocTransport(dwIpAddress) +{ +} + +// Error strings for WSA errors + +#ifdef DEBUG +struct { + int nError; + TCHAR *pszError; +} aSocErrors[] = { + { WSAEINTR, TEXT("EINTR") }, + { WSAEBADF, TEXT("EBADF") }, + { WSAEACCES, TEXT("EACCES") }, + { WSAEFAULT, TEXT("EFAULT") }, + { WSAEINVAL, TEXT("EINVAL") }, + { WSAEMFILE, TEXT("EMFILE") }, + { WSAEWOULDBLOCK, TEXT("EWOULDBLOCK") }, + { WSAEINPROGRESS, TEXT("EINPROGRESS") }, + { WSAEALREADY, TEXT("EALREADY") }, + { WSAENOTSOCK, TEXT("ENOTSOCK") }, + { WSAEDESTADDRREQ, TEXT("EDESTADDRREQ") }, + { WSAEMSGSIZE, TEXT("EMSGSIZE") }, + { WSAEPROTOTYPE, TEXT("EPROTOTYPE") }, + { WSAENOPROTOOPT, TEXT("ENOPROTOOPT") }, + { WSAEPROTONOSUPPORT, TEXT("EPROTONOSUPPORT") }, + { WSAESOCKTNOSUPPORT, TEXT("ESOCKTNOSUPPORT") }, + { WSAEOPNOTSUPP, TEXT("EOPNOTSUPP") }, + { WSAEPFNOSUPPORT, TEXT("EPFNOSUPPORT") }, + { WSAEAFNOSUPPORT, TEXT("EAFNOSUPPORT") }, + { WSAEADDRINUSE, TEXT("EADDRINUSE") }, + { WSAEADDRNOTAVAIL, TEXT("EADDRNOTAVAIL") }, + { WSAENETDOWN, TEXT("ENETDOWN") }, + { WSAENETUNREACH, TEXT("ENETUNREACH") }, + { WSAENETRESET, TEXT("ENETRESET") }, + { WSAECONNABORTED, TEXT("ECONNABORTED") }, + { WSAECONNRESET, TEXT("ECONNRESET") }, + { WSAENOBUFS, TEXT("ENOBUFS") }, + { WSAEISCONN, TEXT("EISCONN") }, + { WSAENOTCONN, TEXT("ENOTCONN") }, + { WSAESHUTDOWN, TEXT("ESHUTDOWN") }, + { WSAETOOMANYREFS, TEXT("ETOOMANYREFS") }, + { WSAETIMEDOUT, TEXT("ETIMEDOUT") }, + { WSAECONNREFUSED, TEXT("ECONNREFUSED") }, + { WSAELOOP, TEXT("ELOOP") }, + { WSAENAMETOOLONG, TEXT("ENAMETOOLONG") }, + { WSAEHOSTDOWN, TEXT("EHOSTDOWN") }, + { WSAEHOSTUNREACH, TEXT("EHOSTUNREACH") }, + { WSAENOTEMPTY, TEXT("ENOTEMPTY") }, + { WSAEPROCLIM, TEXT("EPROCLIM") }, + { WSAEUSERS, TEXT("EUSERS") }, + { WSAEDQUOT, TEXT("EDQUOT") }, + { WSAESTALE, TEXT("ESTALE") }, + { WSAEREMOTE, TEXT("EREMOTE") }, + { WSAEDISCON, TEXT("EDISCON") }, + { WSASYSNOTREADY, TEXT("SYSNOTREADY") }, + { WSAVERNOTSUPPORTED, TEXT("VERNOTSUPPORTED") }, + { WSANOTINITIALISED, TEXT("NOTINITIALISED") }, + { WSAHOST_NOT_FOUND, TEXT("HOST_NOT_FOUND") }, + { WSATRY_AGAIN, TEXT("TRY_AGAIN") }, + { WSANO_RECOVERY, TEXT("NO_RECOVERY") }, + { WSANO_DATA, TEXT("NO_DATA") }, +}; + +const TCHAR *PszFromSocError(int nError) +{ + static TCHAR szError[80]; + TCHAR *pszError = TEXT("Unknown Socket error!"); + + for (int i = 0; i < sizeof(aSocErrors) / (sizeof(int) + sizeof(char *)); + i++) { + if (nError == aSocErrors[i].nError) { + pszError = aSocErrors[i].pszError; + break; + } + } + + wsprintf(szError, TEXT("%s (%d)"), pszError, nError); + return szError; +} + +#endif diff --git a/game/win/WsTransport.h b/game/win/WsTransport.h new file mode 100644 index 0000000..ee4188c --- /dev/null +++ b/game/win/WsTransport.h @@ -0,0 +1,15 @@ +#include +#include "..\SocTransport.h" + +class WsTransport : public SocTransport // tra +{ +public: + static int GetTransportDescriptions(TransportDescription *atrad, int ctradMax); + static Transport *Open(TransportDescription *ptrad); + WsTransport(dword dwIpAddress); + + // Transport implementation + + virtual bool Open(); + virtual void Close(); +}; diff --git a/game/win/app.ico b/game/win/app.ico new file mode 100644 index 0000000..132d5f9 Binary files /dev/null and b/game/win/app.ico differ diff --git a/game/win/aviwrite.cpp b/game/win/aviwrite.cpp new file mode 100644 index 0000000..5f4ed81 --- /dev/null +++ b/game/win/aviwrite.cpp @@ -0,0 +1,248 @@ +#include "..\ht.h" +#include +#include +#include +#include + +AviRecorder *gpavir; + +AviRecorder::AviRecorder() +{ + m_pavif = NULL; + m_pstmVideo = NULL; + m_pstmAudio = NULL; + m_pbmFlip = NULL; + m_ptbmPointer = NULL; + m_nSample = 0; + m_fAudioReady = false; +} + +AviRecorder::~AviRecorder() +{ + Stop(); +} + +bool AviRecorder::Start(int cx, int cy, char *pszFn) +{ +#ifndef DEV_BUILD + // Don't let mortals record avis + + return false; +#endif + +#if 1 + // Modify cx & cy to be 4 by 3 for NTSC video, + // which is what Microsoft Movie Maker makes. + + if (cx > cy) { + int cyT = cx * 3 / 4; + if (cyT < cy) { + cx = cy * 4 / 3; + } else { + cy = cyT; + } + } else { + int cxT = cy * 4 / 3; + if (cxT < cx) { + cy = cx * 3 / 4; + } else { + cx = cxT; + } + } +#endif + + // Now dword align the dst, for Windows sake + + cx = (cx + 3) & ~3; + + // Create a temp buffer that we'll use for flipping the dib since it needs to + // be upside down for windows. + + m_pbmFlip = CreateDibBitmap(NULL, cx, cy); + if (m_pbmFlip == NULL) + return false; + + // Init + + m_ptbmPointer = LoadTBitmap("arrow5.tbm"); + AVIFileInit(); + + // Create filename + + if (pszFn == NULL) { + char szFn[MAX_PATH]; + strcpy(szFn, "c:\\ht.avi"); + pszFn = szFn; + } + + // Open AVI file + + HRESULT hr = AVIFileOpen(&m_pavif, pszFn, OF_CREATE | OF_WRITE, NULL); + if (hr != AVIERR_OK) + return false; + + // Create the video streams if they don't exist + + AVISTREAMINFO stmInfo; + memset(&stmInfo, 0, sizeof(stmInfo)); + stmInfo.fccType = streamtypeVIDEO; + stmInfo.fccHandler = 0; // mmioFOURCC('M','S','V','C'); + stmInfo.dwScale = 2; + stmInfo.dwRate = 25; + stmInfo.dwSuggestedBufferSize = cx * cy; + stmInfo.dwQuality = 0; // 10000; + SetRect(&stmInfo.rcFrame, 0, 0, cx, cy); + + // Create video stream + + if (m_pstmVideo == NULL) { + hr = AVIFileCreateStream(m_pavif, &m_pstmVideo, &stmInfo); + if (hr != AVIERR_OK) + return false; + } + + // Set the stream format + + struct Header { + BITMAPINFOHEADER bih; + RGBQUAD argbqPal[256]; + }; + + Header hdr; + memset(&hdr, 0, sizeof(hdr)); + hdr.bih.biSize = sizeof(hdr.bih); + hdr.bih.biWidth = cx; + hdr.bih.biHeight = cy; + hdr.bih.biPlanes = 1; + hdr.bih.biBitCount = 8; + hdr.bih.biCompression = BI_RGB; + hdr.bih.biClrUsed = 256; + + // Get the palette + + gpdisp->GetWindowsPalette(hdr.argbqPal); + + // Set the format + + hr = AVIStreamSetFormat(m_pstmVideo, 0, &hdr, sizeof(hdr)); + if (hr != AVIERR_OK) + return false; + + // When recording started, useful for keeping video in sync + + m_tStart = HostGetTickCount(); + + // Now create the audio stream + + memset(&stmInfo, 0, sizeof(stmInfo)); + stmInfo.fccType = streamtypeAUDIO; + stmInfo.fccHandler = 0; + stmInfo.dwScale = 1; + stmInfo.dwRate = 8000; + + // Create audio stream + + if (m_pstmAudio == NULL) { + hr = AVIFileCreateStream(m_pavif, &m_pstmAudio, &stmInfo); + if (hr != AVIERR_OK) + return false; + } + + // Set the audio format + + WAVEFORMATEX fmt; + fmt.wFormatTag = WAVE_FORMAT_PCM; + fmt.nChannels = 1; + fmt.nSamplesPerSec = 8000; + fmt.nAvgBytesPerSec = 8000; + fmt.nBlockAlign = 1; + fmt.wBitsPerSample = 8; + fmt.cbSize = 0; + + // Set the format + + hr = AVIStreamSetFormat(m_pstmAudio, 0, &fmt, sizeof(fmt)); + if (hr != AVIERR_OK) + return false; + + // Now we can allow the audio thread to call + + m_fAudioReady = true; + + return true; +} + +void AviRecorder::Stop() +{ + m_fAudioReady = false; + + if (m_pstmVideo != NULL) { + AVIStreamClose(m_pstmVideo); + m_pstmVideo = NULL; + } + + if (m_pstmAudio != NULL) { + AVIStreamClose(m_pstmAudio); + m_pstmAudio = NULL; + } + + if (m_pavif != NULL) { + AVIFileClose(m_pavif); + m_pavif = NULL; + } + + AVIFileExit(); + + m_nSample = 0; + delete m_pbmFlip; + m_pbmFlip = NULL; + delete m_ptbmPointer; + m_ptbmPointer = NULL; +} + +void AviRecorder::AddFrame(DibBitmap *pbm) +{ + // Find the center of the flip dib + + Size sizSrc; + pbm->GetSize(&sizSrc); + Size sizDst; + m_pbmFlip->GetSize(&sizDst); + int xDst = (sizDst.cx - sizSrc.cx) / 2; + int yDst = (sizDst.cy - sizSrc.cy) / 2; + int cbRowDst = m_pbmFlip->GetRowBytes(); + int cbRowSrc = pbm->GetRowBytes(); + + // Flip the dib for Windows' sake into this spot + + m_pbmFlip->Clear(GetColor(kiclrBlack)); + byte *pbSrc = pbm->GetBits(); + byte *pbDst = m_pbmFlip->GetBits(); + for (int y = 0; y < sizSrc.cy; y++) + memcpy(&pbDst[(yDst + y) * cbRowDst + xDst], &pbSrc[(sizSrc.cy - y - 1) * cbRowSrc], cbRowSrc); + + // Take out for screenshot purposes + // Point a pointer in there to represent where the mouse / stylus is + + Size sizT; + m_ptbmPointer->GetSize(&sizT); + int xT = xDst + gxPenLast - 5; + int yT = yDst + sizSrc.cy - gyPenLast - (sizT.cy - 5); + m_ptbmPointer->BltTo(m_pbmFlip, xT, yT, GetKeyState(VK_LBUTTON) < 0 ? kside2 : ksideNeutral, NULL); + + // Write to the video stream + + int nFrame = (HostGetTickCount() - m_tStart) / 8; + AVIStreamWrite(m_pstmVideo, nFrame, 1, pbDst, sizDst.cx * sizDst.cy, 0, NULL, NULL); +} + +void AviRecorder::AddAudio(byte *pb8Unsigned, dword cb) +{ + if (!m_fAudioReady) + return; + + // Write to the audio stream + + AVIStreamWrite(m_pstmAudio, m_nSample, cb, pb8Unsigned, cb, 0, NULL, NULL); + m_nSample += cb; +} \ No newline at end of file diff --git a/game/win/display.cpp b/game/win/display.cpp new file mode 100644 index 0000000..b6e0a15 --- /dev/null +++ b/game/win/display.cpp @@ -0,0 +1,569 @@ +#include "..\ht.h" + +#define kidmScale1 100 +#define kidmScale2 101 +#define kidmScale3 102 +#define kidmScale4 103 +#define kidmScaleDefault 104 + +HWND Display::m_hwnd; +HMENU Display::m_hmenuPopup; +RECT g_rcOldWindowPos; + +RECT g_arcSilkscreen[] = { + { 27 , 206 , (27+18) , (206+14) }, // abc area + { 115 , 206 , (115+18) , (206+14) }, // 123 area + { 0 , 164 , (0+27) , (164+28) }, // launch button + + // WARNING: host.cpp code assumes the rectangle for the menu button + // is at index 3 (from 0) in this list + { 0 , 192 , (0+27) , (192+28) }, // menu button + + // WARNING: host.cpp code assumes the rectangle for the calc button + // is at index 4 (from 0) in this list + { 133 , 164 , (133+27) , (164+28) }, // calculator button + + // WARNING: host.cpp code assumes the rectangle for the find button + // is at index 5 (from 0) in this list + { 133 , 192 , (133+27) , (192+28) }, // find button + + // WARNING: HostGetSilkRect() assumes all these indexes + { 0, 0, 160 , 160 }, // screen + { 27 , 164 , (27+62) , (164+56) }, // alpha rect + { 89 , 164 , (89+44) , (164+56) }, // number rect +}; + +Display *HostCreateDisplay() +{ + // Make sure the Hostile Takeover window isn't underlaying the taskbar if + // it happens to be along the left side of the desktop. + + if (g_rcOldWindowPos.left == 0 && g_rcOldWindowPos.top == 0) { + POINT ptT = { 0, 0 }; + HMONITOR hmon = MonitorFromPoint(ptT, MONITOR_DEFAULTTOPRIMARY); + + MONITORINFOEX mi; + mi.cbSize = sizeof(mi); + GetMonitorInfo(hmon, &mi); + g_rcOldWindowPos.left = mi.rcWork.left; + g_rcOldWindowPos.top = mi.rcWork.top; + } + + // Create a display + + Display *pdisp = new Display(); + if (pdisp == NULL) + return NULL; + if (!pdisp->Init()) { + delete pdisp; + return NULL; + } + return pdisp; +} + +LRESULT CALLBACK MainWndProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp) +{ + switch (wm) { + case WM_CLOSE: + PostMessage(NULL, WM_GAMEEVENT, appStopEvent, knAppExit); + return 0; + + case WM_PAINT: + if (gpmfrmm != NULL) + gpmfrmm->InvalidateRect(NULL); + PostMessage(NULL, WM_GAMEEVENT, gamePaintEvent, 0); + break; + + case WM_RBUTTONDOWN: + { + POINT pt; + pt.x = LOWORD(lp); + pt.y = HIWORD(lp); + MapWindowPoints(Display::m_hwnd, NULL, &pt, 1); + int idm = TrackPopupMenu(Display::m_hmenuPopup, + TPM_CENTERALIGN | TPM_TOPALIGN | TPM_RETURNCMD | TPM_LEFTBUTTON, + pt.x, pt.y, 0, Display::m_hwnd, NULL); + switch (idm) { + case kidmScale1: + gpdisp->SetScale(1); + break; + + case kidmScale2: + gpdisp->SetScale(2); + break; + + case kidmScale3: + gpdisp->SetScale(3); + break; + + case kidmScale4: + gpdisp->SetScale(4); + break; + + case kidmScaleDefault: + gpdisp->SetScale(-1); + break; + } + } + break; + + case WM_ERASEBKGND: + if (gpdisp != NULL) { + ModeInfo mode; + gpdisp->GetMode(&mode); + if (mode.cyGraffiti != 0) { + DefWindowProc(hwnd, wm, wp, lp); + HDC hdc = (HDC)wp; + HBRUSH hbrT = (HBRUSH)GetStockObject(WHITE_BRUSH); + ModeInfo mode; + gpdisp->GetMode(&mode); + int nScale = gpdisp->GetScale() * mode.cx / 160; + RECT *prcT = g_arcSilkscreen; + for (int i = 0; i < sizeof(g_arcSilkscreen) / sizeof(RECT); i++, prcT++) { + RECT rcT = *prcT; + rcT.left = rcT.left * nScale; + rcT.top = rcT.top * nScale; + rcT.right = rcT.right * nScale; + rcT.bottom = rcT.bottom * nScale; + FrameRect(hdc, &rcT, hbrT); + } + return 1; + } + } + break; + } + return DefWindowProc(hwnd, wm, wp, lp); +} + +Display::Display() +{ + m_hwnd = NULL; + m_hbm = NULL; + m_hbmSav = NULL; + m_hdc = NULL; + m_hdcMem = NULL; + m_imode = -1; + m_cmodes = 0; + m_pbmClip = NULL; + m_pbmFront = NULL; + m_pbmBack = NULL; + m_nScale = 1; + m_hmenuPopup = NULL; +} + +Display::~Display() +{ + if (m_hdc != NULL) { + ReleaseDC(m_hwnd, m_hdc); + m_hdc = NULL; + } + if (m_hwnd != NULL) { + GetWindowRect(m_hwnd, &g_rcOldWindowPos); + DestroyWindow(m_hwnd); + m_hwnd = NULL; + } + if (m_hbm != NULL) { + if (m_hdcMem != NULL) + SelectObject(m_hdcMem, (HGDIOBJ)m_hbmSav); + DeleteObject(m_hbm); + m_hbm = NULL; + } + if (m_hdcMem != NULL) { + DeleteDC(m_hdcMem); + m_hdcMem = NULL; + } + if (m_hmenuPopup != NULL) { + DestroyMenu(m_hmenuPopup); + m_hmenuPopup = NULL; + } + delete m_pbmBack; + m_pbmBack = NULL; + delete m_pbmFront; + m_pbmFront = NULL; + delete m_pbmClip; + m_pbmClip = NULL; +} + +bool Display::Init() +{ + WNDCLASS wc; + memset(&wc, 0, sizeof(wc)); + wc.lpfnWndProc = MainWndProc; + wc.hInstance = ghInst; + wc.hIcon = LoadIcon(ghInst, MAKEINTRESOURCE(kidiMain)); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH); + wc.lpszClassName = kszWindowClass; + RegisterClass(&wc); + + // Adjust so the client area is the size we want + + dword dwStyle = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CLIPCHILDREN; + RECT rc; + SetRect(&rc, 0, 0, 320, 320 + (60 * 2 /* Graffiti area */)); + AdjustWindowRect(&rc, dwStyle, false); + + m_hwnd = CreateWindowEx(0, kszWindowClass, kszWindowTitle, + dwStyle, g_rcOldWindowPos.left, g_rcOldWindowPos.top, // CW_USEDEFAULT, CW_USEDEFAULT, + rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, ghInst, 0); + if (m_hwnd == NULL) + return false; + m_hdc = GetDC(m_hwnd); + if (m_hdc == NULL) + return false; + m_hdcMem = CreateCompatibleDC(m_hdc); + if (m_hdcMem == NULL) + return false; + + // Create popup menu + + m_hmenuPopup = CreatePopupMenu(); + AppendMenu(m_hmenuPopup, MF_STRING, kidmScale1, "1x scale"); + AppendMenu(m_hmenuPopup, MF_STRING, kidmScale2, "2x scale"); + AppendMenu(m_hmenuPopup, MF_STRING, kidmScale3, "3x scale"); + AppendMenu(m_hmenuPopup, MF_STRING, kidmScale4, "4x scale"); + AppendMenu(m_hmenuPopup, MF_SEPARATOR, 0, NULL); + AppendMenu(m_hmenuPopup, MF_STRING, kidmScaleDefault, "Default scale"); + + // Set up mode list + + ModeInfo *pmode = m_amodeInfo; + + pmode->cx = 160; + pmode->cy = 160; + pmode->nDepth = 4; + pmode->fNative = false; + pmode->cyGraffiti = 60; + pmode->nScale = 2; + pmode->nDegreeOrientation = 0; + pmode++; + + pmode->cx = 160; + pmode->cy = 240; + pmode->nDepth = 4; + pmode->fNative = false; + pmode->cyGraffiti = 0; + pmode->nScale = 2; + pmode->nDegreeOrientation = 0; + pmode++; + + pmode->cx = 160; + pmode->cy = 160; + pmode->nDepth = 8; + pmode->fNative = false; + pmode->cyGraffiti = 60; + pmode->nScale = 2; + pmode->nDegreeOrientation = 0; + pmode++; + + pmode->cx = 160; + pmode->cy = 240; + pmode->nDepth = 8; + pmode->fNative = false; + pmode->cyGraffiti = 0; + pmode->nScale = 2; + pmode->nDegreeOrientation = 0; + pmode++; + + pmode->cx = 240; + pmode->cy = 320; + pmode->nDepth = 8; + pmode->fNative = false; + pmode->cyGraffiti = 0; + pmode->nScale = 1; + pmode->nDegreeOrientation = 0; + pmode++; + + pmode->cx = 320; + pmode->cy = 320; + pmode->nDepth = 4; + pmode->fNative = false; + pmode->cyGraffiti = 120; + pmode->nScale = 1; + pmode->nDegreeOrientation = 0; + pmode++; + + pmode->cx = 320; + pmode->cy = 480; + pmode->nDepth = 4; + pmode->fNative = false; + pmode->cyGraffiti = 0; + pmode->nScale = 1; + pmode->nDegreeOrientation = 0; + pmode++; + + pmode->cx = 320; + pmode->cy = 320; + pmode->nDepth = 8; + pmode->fNative = false; + pmode->cyGraffiti = 120; + pmode->nScale = 1; + pmode->nDegreeOrientation = 0; + pmode++; + + pmode->cx = 320; + pmode->cy = 480; + pmode->nDepth = 8; + pmode->fNative = false; + pmode->cyGraffiti = 0; + pmode->nScale = 1; + pmode->nDegreeOrientation = 0; + pmode++; + + pmode->cx = 480; + pmode->cy = 320; + pmode->nDepth = 8; + pmode->fNative = false; + pmode->cyGraffiti = 0; + pmode->nScale = 1; + pmode->nDegreeOrientation = 0; + pmode++; + + pmode->cx = 320; + pmode->cy = 240; + pmode->nDepth = 8; + pmode->fNative = false; + pmode->cyGraffiti = 0; + pmode->nScale = 1; + pmode->nDegreeOrientation = 0; + pmode++; + + pmode->cx = 640; + pmode->cy = 480; + pmode->nDepth = 8; + pmode->fNative = false; + pmode->cyGraffiti = 0; + pmode->nScale = 1; + pmode->nDegreeOrientation = 0; + pmode++; + + m_cmodes = pmode - m_amodeInfo; + + return true; +} + +bool Display::SetMode(int imode, int nScale) +{ + ModeInfo *pmode = &m_amodeInfo[imode]; + + // Create dib bitmap + + DibBitmap *pbmBack = CreateDibBitmap(NULL, pmode->cx, pmode->cy); + if (pbmBack == NULL) + return false; + + // Create display surface with default palm palette + + struct BitmapInfo // bi + { + BITMAPINFOHEADER bmiHeader; + RGBQUAD bmiColors[256]; + }; + BitmapInfo bi; + memset(&bi, 0, sizeof(bi)); + bi.bmiHeader.biSize = sizeof(bi.bmiHeader); + bi.bmiHeader.biWidth = pmode->cx; + bi.bmiHeader.biHeight = -pmode->cy; + bi.bmiHeader.biPlanes = 1; + bi.bmiHeader.biBitCount = 8; + bi.bmiHeader.biCompression = BI_RGB; + switch (pmode->nDepth) { + case 4: + bi.bmiHeader.biClrUsed = 16; + break; + + case 8: + bi.bmiHeader.biClrUsed = 256; + break; + } + + byte *pbScreen; + HBITMAP hbm = CreateDIBSection(m_hdcMem, (BITMAPINFO *)&bi, DIB_RGB_COLORS, (void **)&pbScreen, NULL, 0); + if (hbm == NULL) { + delete pbmBack; + return false; + } + + // Success + + if (m_hbm != NULL) { + SelectObject(m_hdcMem, (HGDIOBJ)m_hbmSav); + DeleteObject(m_hbm); + m_hbm = NULL; + delete m_pbmBack; + m_pbmBack = NULL; + delete m_pbmFront; + m_pbmFront = NULL; + } + + DibBitmap *pbmFront = CreateDibBitmap(pbScreen, pmode->cx, pmode->cy); + if (pbmFront == NULL) { + delete pbmBack; + return false; + } + + m_hbm = hbm; + m_hbmSav = (HBITMAP)SelectObject(m_hdcMem, (HGDIOBJ)m_hbm); + m_imode = imode; + m_pbmBack = pbmBack; + m_pbmFront = pbmFront; + + // Scale the window + + if (nScale <= 0) + nScale = m_amodeInfo[imode].nScale; + SetScale(nScale); + return true; +} + +void Display::SetScale(int nScale) +{ + if (nScale <= 0) + nScale = m_amodeInfo[m_imode].nScale; + m_nScale = nScale; + ResizeWindow(nScale); +} + +void Display::ResizeWindow(int nScale) +{ + // Size window to the requested size + + ModeInfo *pmode = &m_amodeInfo[m_imode]; + int cx = pmode->cx * nScale; + int cy = (pmode->cy + pmode->cyGraffiti) * nScale; + dword dwStyle = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CLIPCHILDREN; + RECT rc; + SetRect(&rc, 0, 0, cx, cy); + AdjustWindowRect(&rc, dwStyle, false); + + // Hack: If we're running in multiplayer test mode, position this window according to side represented + + int x = g_rcOldWindowPos.left; + int y = g_rcOldWindowPos.top; +#ifdef MP_STRESS + if (gnMPPos != 0) + x += (rc.right - rc.left) * gnMPPos; +#endif + + SetWindowPos(m_hwnd, NULL, x, y, rc.right - rc.left, rc.bottom - rc.top, SWP_NOZORDER | SWP_SHOWWINDOW); + InvalidateRect(m_hwnd, NULL, TRUE); +} + +int Display::GetMode(ModeInfo *pmode) +{ + if (pmode != NULL) { + if (m_imode == -1) { + memset(pmode, 0, sizeof(*pmode)); + } else { + *pmode = m_amodeInfo[m_imode]; + } + } + return m_imode; +} + +int Display::GetModeCount() +{ + return m_cmodes; +} + +void Display::GetModeInfo(int imode, ModeInfo *pmode) +{ + memset(pmode, 0, sizeof(*pmode)); + if (imode >= 0 && imode < m_cmodes) + *pmode = m_amodeInfo[imode]; +} + +DibBitmap *Display::GetBackDib() +{ + return m_pbmBack; +} + +DibBitmap *Display::GetFrontDib() +{ + return m_pbmFront; +} + +DibBitmap *Display::GetClippingDib() +{ + if (m_pbmClip != NULL) + return m_pbmClip; + DibBitmap *pbm = CreateDibBitmap(NULL, kcCopyBy4Procs * 4, kcCopyBy4Procs * 4); + if (pbm == NULL) + return NULL; + m_pbmClip = pbm; + return pbm; +} + +void Display::FrameStart() +{ +} + +void Display::FrameComplete() +{ + // Update screen + + int cx = m_amodeInfo[m_imode].cx; + int cy = m_amodeInfo[m_imode].cy; + StretchBlt(m_hdc, 0, 0, cx * m_nScale, cy * m_nScale, m_hdcMem, 0, 0, cx, cy, SRCCOPY); + +#ifdef DEBUG_HELPERS + extern void paint(); + paint(); +#endif +} + +void Display::SetPalette(Palette *ppal) +{ + RGBQUAD *prgbq = new RGBQUAD[BigWord(ppal->cEntries)]; + for (int n = 0; n < BigWord(ppal->cEntries); n++) { + prgbq[n].rgbRed = ppal->argb[n][0]; + prgbq[n].rgbGreen = ppal->argb[n][1]; + prgbq[n].rgbBlue = ppal->argb[n][2]; + prgbq[n].rgbReserved = 0; + } + SetDIBColorTable(m_hdcMem, 0, BigWord(ppal->cEntries), prgbq); + delete prgbq; +} + +void Display::GetWindowsPalette(RGBQUAD *pargbq) +{ + GetDIBColorTable(m_hdcMem, 0, 256, pargbq); +} + +void Display::DrawText(const char *psz, int x, int y, word wf) +{ + if (y == -1) + y = m_amodeInfo[m_imode].cy * m_nScale - 16; + + RECT rc; + rc.left = 0; + rc.top = y; + if (wf & kfDtClearLine) { + rc.right = m_amodeInfo[m_imode].cx * m_nScale; + rc.bottom = rc.top + 16; + HBRUSH hbrT = CreateSolidBrush(RGB(0, 0, 0)); + FillRect(m_hdc, &rc, hbrT); + DeleteObject(hbrT); + } + SetTextColor(m_hdc, RGB(255, 255, 255)); + SetBkColor(m_hdc, RGB(0, 0, 0)); + TextOut(m_hdc, rc.left, rc.top, psz, strlen(psz)); + GdiFlush(); +} + +void Display::DrawFrameInclusive(Rect *prc) +{ + RECT rcT; + SetRect(&rcT, prc->left * m_nScale, prc->top * m_nScale, prc->right * m_nScale, prc->bottom * m_nScale); + HBRUSH hbr = CreateSolidBrush(RGB(255, 255, 0)); + FrameRect(m_hdc, &rcT, hbr); + DeleteObject((HGDIOBJ)hbr); + GdiFlush(); +} + +void Display::GetHslAdjustments(short *pnHueOffset, short *pnSatMultiplier, short *pnLumOffset) +{ + *pnHueOffset = 0; + *pnSatMultiplier = 0; + *pnLumOffset = 0; +} diff --git a/game/win/host.cpp b/game/win/host.cpp new file mode 100644 index 0000000..8ad5251 --- /dev/null +++ b/game/win/host.cpp @@ -0,0 +1,666 @@ +#include +#include "..\ht.h" + +extern RECT g_arcSilkscreen[]; +static MSG s_msgMouseLast; +static dword s_tLastMouseMsg; +int gxPenLast; +int gyPenLast; + +void GetSilkRect(RECT *prcIn, RECT *prcOut, bool fScale) +{ + *prcOut = *prcIn; + if (gpdisp != NULL) { + ModeInfo mode; + gpdisp->GetMode(&mode); + int nScale; + if (fScale) { + nScale = gpdisp->GetScale() * mode.cx / 160; + } else { + nScale = mode.cx / 160; + } + prcOut->left = prcIn->left * nScale; + prcOut->top = prcIn->top * nScale; + prcOut->right = prcIn->right * nScale; + prcOut->bottom = prcIn->bottom * nScale; + } +} + +bool WaitForInput(dword msStart, dword cmsWaitTotal) +{ + dword cmsWait = cmsWaitTotal; + if (cmsWaitTotal != INFINITE) { + dword cmsElapsed = GetTickCount() - msStart; + if (cmsElapsed > cmsWaitTotal) + return false; + cmsWait = cmsWaitTotal - cmsElapsed; + } + dword dw = MsgWaitForMultipleObjects(0, NULL, TRUE, cmsWait, QS_ALLEVENTS); + return dw != WAIT_TIMEOUT; +} + +void ProcessKeyEvent(MSG *pmsg, Event *pevt) +{ + // Palm mixes "chars" with "vkeys", however with values that don't + // overlap. The host will treat this the same. + + pevt->eType = keyDownEvent; + switch (pmsg->wParam) { + case VK_UP: + pevt->chr = chrPageUp; + return; + + case VK_DOWN: + pevt->chr = chrPageDown; + return; + + case VK_LEFT: + pevt->chr = chrLeft; + return; + + case VK_RIGHT: + pevt->chr = chrRight; + return; + + case VK_BACK: + pevt->chr = chrBackspace; + return; + + case VK_DELETE: + pevt->chr = chrDelete; + return; + + case VK_F7: + if (gpavir == NULL) { + gpavir = new AviRecorder(); + Size siz; + gpdisp->GetFrontDib()->GetSize(&siz); + if (!gpavir->Start(siz.cx, siz.cy)) { + delete gpavir; + gpavir = NULL; + } + } + break; + + case VK_F8: + if (gpavir != NULL) { + gpavir->Stop(); + delete gpavir; + gpavir = NULL; + } + break; + + case VK_SUBTRACT: // numpad + case VK_OEM_MINUS: // '-' key + { + for (int i = 0; i < ARRAYSIZE(gatGameSpeeds); i++) + if (gatGameSpeeds[i] == gtGameSpeed) + break; + i--; + if (i < 0) + i = 0; + ggame.SetGameSpeed(gatGameSpeeds[i]); + } + break; + + case VK_ADD: // numpad + case VK_OEM_PLUS: // '=' key + { + for (int i = 0; i < ARRAYSIZE(gatGameSpeeds); i++) + if (gatGameSpeeds[i] == gtGameSpeed) + break; + i++; + if (i >= ARRAYSIZE(gatGameSpeeds)) + i = ARRAYSIZE(gatGameSpeeds) - 1; + ggame.SetGameSpeed(gatGameSpeeds[i]); + } + break; + + default: +#ifdef DEBUG_HELPERS + extern void DebugHelperKeyHandler(word vk); + DebugHelperKeyHandler(pmsg->wParam); +#endif + pevt->chr = pmsg->wParam; + break; + } + if (!TranslateMessage(pmsg)) + return; + + // A WM_CHAR *may* have been posted to the queue - we don't know for + // sure until we take a peek. + + MSG msg; + if (PeekMessage(&msg, NULL, WM_CHAR, WM_CHAR, TRUE)) + pevt->chr = msg.wParam; +} + +bool ProcessMouseEvent(MSG *pmsg, Event *pevt, bool fHover = false) +{ + static bool s_fAppButtonDown; + POINT ptT; + +#ifdef DEBUG_HELPERS + // This allows the debug window to get events it needs + + extern HWND s_hwndDebug; + if (pmsg->hwnd != Display::m_hwnd) + if (pmsg->hwnd != s_hwndDebug || LOWORD(pmsg->lParam) >= 320) + return false; +#endif + + // Scale the position appropriately. + + int nScale = gpdisp->GetScale(); + gxPenLast = (int)((float)(short)LOWORD(pmsg->lParam) / (float)nScale); + gyPenLast = (int)((float)(short)HIWORD(pmsg->lParam) / (float)nScale); + + // Remember the last + + s_msgMouseLast = *pmsg; + s_tLastMouseMsg = GetTickCount(); + + // Setup + + RECT rcApp; + GetSilkRect(&g_arcSilkscreen[2], &rcApp, true); + bool fGraffitiArea = false; + if (gpdisp != NULL) { + ModeInfo mode; + gpdisp->GetMode(&mode); + fGraffitiArea = (mode.cyGraffiti != 0); + } + + switch (pmsg->message) { + case WM_LBUTTONDOWN: + pevt->eType = penDownEvent; + SetCapture(pmsg->hwnd); + + ptT.x = LOWORD(pmsg->lParam); + ptT.y = HIWORD(pmsg->lParam); + if (fGraffitiArea) { + if (PtInRect(&rcApp, ptT)) + s_fAppButtonDown = true; + } + break; + + case WM_LBUTTONUP: + pevt->eType = penUpEvent; + ReleaseCapture(); + + // Check for soft silk screen buttons. + + if (fGraffitiArea) { + ptT.x = LOWORD(pmsg->lParam); + ptT.y = HIWORD(pmsg->lParam); + if (s_fAppButtonDown && PtInRect(&rcApp, ptT)) + PostMessage(pmsg->hwnd, WM_GAMEEVENT, appStopEvent, 0); + s_fAppButtonDown = false; + } + break; + + case WM_MOUSEMOVE: + if (GetAsyncKeyState(VK_LBUTTON) < 0) { + pevt->eType = penMoveEvent; + break; + } + if (fHover) + break; + return false; + +#if 0 +#ifdef HOSTILE_TAKEOVER + case WM_RBUTTONDOWN: + pevt->chr = chrPageDown; + pevt->eType = keyDownEvent; + break; +#endif +#else + case WM_RBUTTONDOWN: + return false; +#endif + } + + pevt->x = gxPenLast; + pevt->y = gyPenLast; + pevt->dw = 0; + return true; +} + +bool ProcessEvent(MSG *pmsg, Event *pevt) +{ + // Is it something the host will not process? + + switch (pmsg->message) { +#ifdef HOSTILE_TAKEOVER + case WM_RBUTTONDOWN: +#endif + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_MOUSEMOVE: + return ProcessMouseEvent(pmsg, pevt); + + case WM_KEYDOWN: + ProcessKeyEvent(pmsg, pevt); + return true; + + case WM_GAMEEVENT: + pevt->eType = pmsg->wParam; + pevt->dw = pmsg->lParam; + return true; + } + + return false; +} + +#define kcmsMouseHover 1000 + +bool HostGetEvent(Event *pevt, long ctWait) +{ + GdiFlush(); + + // See if we have a hover event + + if (s_msgMouseLast.message == WM_MOUSEMOVE && (GetTickCount() - s_tLastMouseMsg) >= kcmsMouseHover) { + ProcessMouseEvent(&s_msgMouseLast, pevt, true); + pevt->eType = penHoverEvent; + s_msgMouseLast.message = 0; + return true; + } + + // Check for input from the queue first + + MSG msg; + while (PeekMessage(&msg, NULL, 0, 0, TRUE)) { + if (ProcessEvent(&msg, pevt)) + return true; + DispatchMessage(&msg); + } + + // No input; wait for it + + if (ctWait == 0) + return false; + + dword msStart = GetTickCount(); + dword cmsWaitTotal = INFINITE; + if (ctWait != -1) { + cmsWaitTotal = ctWait * 10; + } + + while (true) { + // false means a timeout occured + + if (!WaitForInput(msStart, cmsWaitTotal)) + return false; + + // We have input. See if it is returnable + + MSG msg; + while (PeekMessage(&msg, NULL, 0, 0, TRUE)) { + if (ProcessEvent(&msg, pevt)) + return true; + DispatchMessage(&msg); + } + } +} + +void HostOutputDebugString(char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + ReporterOutputDebugString(pszFormat, va); + va_end(va); +} + +long HostGetTickCount() +{ + return (long)(GetTickCount() / 10); +} + +long HostRunSpeedTests(DibBitmap *pbmSrc) +{ + return 0; +} + +dword HostGetCurrentKeyState(dword keyBit) +{ + if (GetForegroundWindow() != Display::m_hwnd) + return 0; + + struct KeyBitVKey { + dword keyBit; + short vk; + } s_akk[] = { +// { keyBitPower, 0 }, +#ifdef HOSTILE_TAKEOVER + { keyBitPageUp, VK_SHIFT }, + { keyBitPageUp, VK_UP }, + { keyBitPageDown, VK_DOWN }, +#else + { keyBitPageUp, VK_PRIOR }, +#endif + { keyBitPageDown, VK_NEXT }, + { keyBitHard1, VK_INSERT }, + { keyBitHard2, VK_HOME }, + { keyBitHard3, VK_DELETE }, + { keyBitHard4, VK_END }, +// { keyBitCradle, 0 }, +// { keyBitAntenna, 0 }, +// { keyBitContrast, 0 }, + { keyBitDpadLeft, VK_LEFT }, + { keyBitDpadRight, VK_RIGHT }, + { keyBitDpadButton, VK_RETURN }, + }; + + Assert(keyBit != 0); + + dword keyBitRet = 0; + for (int i = 0; i < sizeof(s_akk) / sizeof(KeyBitVKey); i++) { + if ((keyBit & s_akk[i].keyBit) != 0) { + if (GetAsyncKeyState(s_akk[i].vk) < 0) + keyBitRet |= s_akk[i].keyBit; + } + } + + return keyBitRet; +} + +bool HostIsPenDown() +{ + return GetAsyncKeyState(VK_LBUTTON) < 0; +} + +void HostMessageBox(TCHAR *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + TCHAR sz[512]; + vsprintf(sz, pszFormat, va); + MessageBox(NULL, sz, TEXT("Message"), MB_OK); + va_end(va); +} + +void HostGetUserName(char *pszBuff, int cb) +{ + // Can fail if WSAStartup hasn't been called yet + + if (gethostname(pszBuff, cb - 1) != 0) { + strncpyz(pszBuff, "bogus name", cb); + } +} + +bool HostGetOwnerName(char *pszBuff, int cb, bool fShowError) +{ + if (gethostname(pszBuff, cb - 1) != 0) { + if (fShowError) + HtMessageBox(kfMbClearDib, "Hostile Takeover", "This PC has an invalid computer name! Set the computer name and try again."); + return false; + } + return true; +} + +// Override packfile.h's overrides + +#undef FILE +#undef fopen +#undef fclose +#undef fread +#undef fwrite + +// UNDONE: prefix directory? + +FileHandle HostOpenFile(const char *pszFilename, word wf) +{ + char *pszMode; + if (wf == kfOfRead) + pszMode = "rb"; + else if (wf == kfOfWrite) + pszMode = "wb"; + else if (wf == (kfOfRead | kfOfWrite)) + pszMode = "rb+"; + + return (FileHandle)fopen((char *)pszFilename, pszMode); +} + +void HostCloseFile(FileHandle hf) +{ + fclose((FILE *)hf); +} + +dword HostWriteFile(FileHandle hf, void *pv, dword cb) +{ + dword cbWritten = fwrite(pv, 1, cb, (FILE *)hf); +#ifdef DEBUG + fflush((FILE *)hf); +#endif + return cbWritten; +} + +dword HostReadFile(FileHandle hf, void *pv, dword cb) +{ + return fread(pv, 1, cb, (FILE *)hf); +} + +void HostSleep(dword ct) +{ + Sleep(ct * 10); +} + +void HostGetSilkRect(int irc, Rect *prc) +{ + RECT rcT; + switch (irc) { + case kircSilkGraffiti: + rcT.left = g_arcSilkscreen[7].left; + rcT.top = g_arcSilkscreen[7].top; + rcT.right = g_arcSilkscreen[8].right; + rcT.bottom = g_arcSilkscreen[7].bottom; + break; + + case kircSilkApps: + rcT = g_arcSilkscreen[2]; + break; + + case kircSilkMenu: + rcT = g_arcSilkscreen[3]; + break; + + case kircSilkCalc: + rcT = g_arcSilkscreen[4]; + break; + + case kircSilkFind: + rcT = g_arcSilkscreen[5]; + break; + } + + GetSilkRect(&rcT, &rcT, false); + prc->left = rcT.left; + prc->top = rcT.top; + prc->right = rcT.right; + prc->bottom = rcT.bottom; +} + +// Figure out what kind of sound device exists, and return a SoundDevice for it + +SoundDevice *HostOpenSoundDevice() +{ + return CreateWin32SoundDevice(); +} + +SoundDevice::~SoundDevice() +{ +} + +// Used for sound buffer maintenance requirements + +SoundDevice *gpsnddService; +void SetSoundServiceDevice(SoundDevice *psndd) +{ + gpsnddService = psndd; +} + +bool HostSoundServiceProc() +{ + if (gpsnddService == NULL) + return false; + gpsnddService->ServiceProc(); + return true; +} + +void HostGetCurrentDate(Date *pdate) +{ + SYSTEMTIME time; + GetLocalTime(&time); + pdate->nYear = time.wYear; + pdate->nMonth = time.wMonth; + pdate->nDay = time.wDay; +} + +#define kszRegKey TEXT("Software\\Spiffcode\\Takeover") +#define kszRegValue TEXT("Preferences") + +bool HostSavePreferences(void *pv, int cb) +{ + // Open key / create if it doesn't exist + + HKEY hkey; + DWORD dwDisposition; + LRESULT lr = RegCreateKeyEx(HKEY_CURRENT_USER, kszRegKey, 0, "", 0, KEY_WRITE, NULL, &hkey, &dwDisposition); + if (lr != ERROR_SUCCESS) + return false; + + // Set value + + lr = RegSetValueEx(hkey, kszRegValue, 0, REG_BINARY, (BYTE *)pv, cb); + if (lr != ERROR_SUCCESS) { + RegCloseKey(hkey); + return false; + } + + // Done + + RegCloseKey(hkey); + return true; +} + +int HostLoadPreferences(void *pv, int cb) +{ + // Key exist? + + HKEY hkey; + LRESULT lr = RegOpenKeyEx(HKEY_CURRENT_USER, kszRegKey, 0, KEY_READ, &hkey); + if (lr != ERROR_SUCCESS) + return -1; + + // Read the data + + dword cbT = (dword)cb; + lr = RegQueryValueEx(hkey, kszRegValue, NULL, NULL, (BYTE *)pv, &cbT); + if (lr != ERROR_SUCCESS) { + byte *pb = new byte[cbT]; + lr = RegQueryValueEx(hkey, kszRegValue, NULL, NULL, (BYTE *)pb, &cbT); + if (lr == ERROR_SUCCESS) { + cbT = min(cb, (int)cbT); + memcpy(pv, pb, cbT); + delete pb; + } else { + RegCloseKey(hkey); + return -1; + } + } + + // Close and return size read + + RegCloseKey(hkey); + return (int)cbT; +} + +char *HostGetDataDirectory() +{ + // The data directory is the where the executable is executing from + // On Windows, use the current directory + // On CE, use the directory the executable is in (mostly because storage cards aren't + // the "current directory" when an app is executed from the CE launcher. + + char szWorkingDir[_MAX_PATH]; + GetCurrentDirectory(sizeof(szWorkingDir) - 1, szWorkingDir); + + // Return it + + static char s_szDataDir[_MAX_PATH]; + strcpy(s_szDataDir, szWorkingDir); + return s_szDataDir; +} + +void HostSuspendModalLoop(DibBitmap *pbm) +{ +} + +void HostNotEnoughMemory(bool fStorage, dword cbFree, dword cbNeed) +{ + HostMessageBox(TEXT("Need %ld bytes of memory but only %ld bytes are free!"), cbNeed, cbFree); +} + +bool HostEnumAddonFiles(Enum *penm, char *pszAddon, int cb) +{ + WIN32_FIND_DATA find; + + if (penm->m_wUser == (word)kEnmFirst) { + penm->m_wUser = 0; + + char szFileSpec[_MAX_PATH]; + PrependDataDirectory("*.pdb", szFileSpec); + + TCHAR szT[_MAX_PATH]; +#ifdef UNICODE + MultiByteToWideChar(CP_ACP, 0, szFileSpec, -1, szT, ARRAYSIZE(szT) - 1); +#else + strcpy(szT, szFileSpec); +#endif + + penm->m_pvNext = (void *)FindFirstFile(szT, &find); + if (penm->m_pvNext == NULL) + return false; + } else { + if (FindNextFile((HANDLE)penm->m_pvNext, &find) == 0) { + FindClose((HANDLE)penm->m_pvNext); + return false; + } + } + + while (true) { + // Get file + + char szFilename[_MAX_PATH]; +#ifdef UNICODE + WideCharToMultiByte(CP_ACP, 0, find.cFileName, -1, szFilename, ARRAYSIZE(szFilename) - 1, NULL, NULL); +#else + strcpy(szFilename, find.cFileName); +#endif + + char szPath[_MAX_PATH]; + PrependDataDirectory(szFilename, szPath); + + // See if it is an extension file + + FILE *pf = fopen(szPath, "rb"); + if (pf != NULL) { + char szType[5]; + szType[4] = 0; + fseek(pf, 0x3c, SEEK_SET); + fread(szType, 4, 1, pf); + fclose(pf); + if (strcmp(szType, kszTypeAddon) == 0) { + strncpyz(pszAddon, szFilename, cb); + return true; + } + } + + if (FindNextFile((HANDLE)penm->m_pvNext, &find) == 0) { + FindClose((HANDLE)penm->m_pvNext); + return false; + } + } +} \ No newline at end of file diff --git a/game/win/htplatform.h b/game/win/htplatform.h new file mode 100644 index 0000000..041d746 --- /dev/null +++ b/game/win/htplatform.h @@ -0,0 +1,224 @@ + +#ifdef CHECK_OLD_ALLOCS + +void my_delete(void *pv); +void *my_new(int cb); +void my_alloccheck(); +void my_freeall(); + +inline int _CrtCheckMemory(void) +{ + my_alloccheck(); + return 1; +} +inline int _CrtDumpMemoryLeaks(void) +{ + my_freeall(); + return 0; +} + +inline void __cdecl operator delete(void * _P) + { my_delete(_P); } + +inline void * __cdecl operator new(size_t s) + { return my_new(s); } +#else +#ifdef DEBUG +#define _CRTDBG_MAP_ALLOC +#include +#include +#else +#include +#endif +#endif + +// htplatform.h + +#include +#include +//#include +#include +#include +#include +#include + +#define MakeDword(a, b, c, d) ((a) | ((b) << 8) | ((c) << 16) | ((d) << 24)) + +// Can't just include because that hoses users of PackFile.h +extern "C" { +_CRTIMP int __cdecl sprintf(char *, const char *, ...); +_CRTIMP int __cdecl vsprintf(char *, const char *, va_list); +} + +#define kszWindowTitle "Hostile Takeover" +#define kszWindowClass "HostileTakeover" +#define kidiMain 1 // icon id + +#define BigWord(x) ((((x)&0xFF)<<8) | (((x)&0xFF00)>>8)) +#define BigDword(x) ((((x)&0xFF)<<24) | (((x)&0xFF00)<<8) | (((x)&0xFF0000)>>8) | (((x)&0xFF000000)>>24)) + +#define WM_GAMEEVENT (WM_USER+0) + +#define chrPageUp 0xb +#define chrPageDown 0xc +#define vchrMenu 0x105 +#define vchrFind 0x10a +#define vchrCalc 0x10b +#define vchrHard4 0x0207 // memo button +#define penDownEvent 1 +#define penUpEvent 2 +#define penMoveEvent 3 +#define keyDownEvent 4 +#define appStopEvent 22 +#define chrLeft 0x1000 +#define chrRight 0x1001 +#define chrBackspace 0x0008 +#define chrDelete 0x007f +#define chrUp chrPageUp +#define chrDown chrPageDown +#define chrDpad 0x0503 +#define keyBitPower 0x0001 // Power key +#define keyBitPageUp 0x0002 // Page-up, aka Tungsten d-pad up +#define keyBitPageDown 0x0004 // Page-down, aka Tungsten d-pad down +#define keyBitHard1 0x0008 // App #1 +#define keyBitHard2 0x0010 // App #2 +#define keyBitHard3 0x0020 // App #3 +#define keyBitHard4 0x0040 // App #4 +#define keyBitCradle 0x0080 // Button on cradle +#define keyBitAntenna 0x0100 // Antenna "key" +#define keyBitContrast 0x0200 // Contrast key + +// Palm SG's bits + +#define keyBitDpadLeft 0x01000000 // Tungsten d-pad left +#define keyBitDpadRight 0x02000000 // Tungsten d-pad right +#define keyBitDpadButton 0x04000000 // Tungsten d-pad center button + +// Official PalmSource bits + +#define keyBitRockerUp 0x00010000 // 5-way rocker +#define keyBitRockerDown 0x00020000 +#define keyBitRockerLeft 0x00040000 +#define keyBitRockerRight 0x00080000 +#define keyBitRockerCenter 0x00100000 + +#define keyBitsAll 0xFFFFFFFF // all keys + +// shutdown() is in winsock.h but these defines aren't + +#define SD_RECEIVE 0x00 +#define SD_SEND 0x01 +#define SD_BOTH 0x02 + +struct ModeInfo +{ + int cx; + int cy; + int nDepth; + bool fNative; + int cyGraffiti; + int nScale; + int nDegreeOrientation; +}; +#define kcmodesMax 16 + +struct Palette; +class Rect; +class DibBitmap; +class UpdateMap; +class Display // disp +{ +public: + Display(); + ~Display(); + + bool Init(); + void SetPalette(Palette *ppal); + int GetModeCount(); + void GetModeInfo(int imode, ModeInfo *pmode); + int GetMode(ModeInfo *pmode); + bool SetMode(int imode, int nScale); + void DrawText(const char *psz, int x, int y, word wf); + void DrawFrameInclusive(Rect *prc); + void GetHslAdjustments(short *pnHueOffset, short *pnSatMultiplier, short *pnLumOffset); + void GetWindowsPalette(RGBQUAD *pargbq); + DibBitmap *GetBackDib(); + DibBitmap *GetFrontDib(); + DibBitmap *GetClippingDib(); + void FrameStart(); + void FrameComplete(); + void SetScale(int nScale); + int GetScale() { + return m_nScale; + } + + static HMENU m_hmenuPopup; + static HWND m_hwnd; + +#ifndef DEBUG_HELPERS +private: +#endif + HDC m_hdcMem; + +private: + void ResizeWindow(int nScale); + + int m_nScale; + HBITMAP m_hbm; + HBITMAP m_hbmSav; + HDC m_hdc; + byte *m_pbScreen; + DibBitmap *m_pbmFront; + DibBitmap *m_pbmBack; + DibBitmap *m_pbmClip; + ModeInfo m_amodeInfo[kcmodesMax]; + int m_cmodes; + int m_imode; +}; +extern Display *gpdisp; + +#define kfDtClearLine 1 + +// Windows only, for creating avis + +#include +class TBitmap; +class AviRecorder // avir +{ +public: + AviRecorder(); + ~AviRecorder(); + + bool Start(int cx, int cy, char *pszFn = NULL); + void AddFrame(DibBitmap *pbm); + void AddAudio(byte *pb8Unsigned, dword cb); + void Stop(); + +private: + PAVIFILE m_pavif; + PAVISTREAM m_pstmVideo; + PAVISTREAM m_pstmAudio; + long m_tStart; + int m_nSample; + DibBitmap *m_pbmFlip; + TBitmap *m_ptbmPointer; + bool m_fAudioReady; +}; +extern AviRecorder *gpavir; + +void ReporterInit(); +void ReporterExit(); +void ReporterOutputDebugString(char *pszFormat, va_list va); + +#include "../mixer.h" + +class SoundDevice; +void SetSoundServiceDevice(SoundDevice *psndd); +SoundDevice *CreateWin32SoundDevice(); +void PrependDataDirectory(char *pszIn, char *pszOut); + +#define DebugBreak() _asm { int 3 } + +extern HINSTANCE ghInst; +extern int gxPenLast; +extern int gyPenLast; diff --git a/game/win/main.cpp b/game/win/main.cpp new file mode 100644 index 0000000..79e9fcd --- /dev/null +++ b/game/win/main.cpp @@ -0,0 +1,86 @@ +#include "..\ht.h" + +HINSTANCE ghInst; + +int __stdcall WinMain(HINSTANCE hInst, HINSTANCE hInstPrev, char *pszCmds, int nCmdShow) +{ + WSADATA wsad; + WSAStartup(MAKEWORD(1, 1), &wsad); + + ghInst = hInst; + ReporterInit(); + GameMain(pszCmds); + ReporterExit(); + + WSACleanup(); + return 1; +} + + +// Ring buffer of old allocs to check for corruption + +#ifdef CHECK_OLD_ALLOCS + +void *gapvRing[500]; +int gipv; + +void my_check(void *pv) +{ + dword *pcb = (dword *)((dword *)pv - 1); + byte *pb = (byte *)pv; + for (dword n = 0; n < *pcb; n++) { + if (pb[n] != 0xdd) + _asm { int 3 }; + } +} + +void my_free(void *pv) +{ + my_check(pv); + void *pvT = (void *)((dword *)pv - 1); + free(pvT); +} + +void my_freeall() +{ + for (int i = 0; i < ARRAYSIZE(gapvRing); i++) { + if (gapvRing[i] != NULL) + my_free(gapvRing[i]); + } +} + +void my_fill(void *pv) +{ + dword *pcb = (dword *)((dword *)pv - 1); + memset(pv, 0xdd, *pcb); +} + +void my_delete(void *pv) +{ + if (pv == NULL) + return; + if (gapvRing[gipv] != NULL) + my_free((dword *)gapvRing[gipv]); + gapvRing[gipv] = pv; + gipv++; + if (gipv >= ARRAYSIZE(gapvRing)) + gipv = 0; + my_fill(pv); +} + +void *my_new(int cb) +{ + int cbT = cb + 4; + void *pv = malloc(cbT); + *((dword *)pv) = cb; + return (void *)((dword *)pv + 1); +} + +void my_alloccheck() +{ + for (int i = 0; i < ARRAYSIZE(gapvRing); i++) { + if (gapvRing[i] != NULL) + my_check(gapvRing[i]); + } +} +#endif \ No newline at end of file diff --git a/game/win/reporter.cpp b/game/win/reporter.cpp new file mode 100644 index 0000000..e53d2e8 --- /dev/null +++ b/game/win/reporter.cpp @@ -0,0 +1,49 @@ +// Homegrown reporter talking code, as gleaned from POSE. + +#include "..\ht.h" + +void (__stdcall *tracer_init_outputport) (HINSTANCE, const char*, const char*); +void (__stdcall *tracer_close_outputport) (void); +void (__stdcall *tracer_output_VT) (WORD, const char*, const va_list*); +void (__stdcall *tracer_output_VTL) (WORD, const char*, const va_list*); +void (__stdcall *tracer_output_B) (WORD, const BYTE*, size_t); +void (__stdcall *tracer_get_caps) (char*,size_t*); +long (__stdcall *tracer_get_outputport_status) (void); +HMODULE ghTracerLib; + +void ReporterInit() +{ +#ifdef INCL_TRACE + ghTracerLib = LoadLibrary ("palmtrace.dll"); + if (ghTracerLib == NULL) + return; + tracer_init_outputport = (void (__stdcall *)(HINSTANCE, const char*,const char*)) GetProcAddress(ghTracerLib, "tracer_init_outputport"); + tracer_close_outputport = (void (__stdcall *)(void)) GetProcAddress(ghTracerLib, "tracer_close_outputport"); + tracer_output_VT = (void (__stdcall *)(unsigned short,const char *,const va_list * )) GetProcAddress(ghTracerLib, "tracer_output_VT"); + tracer_output_VTL = (void (__stdcall *)(unsigned short,const char *,const va_list * )) GetProcAddress(ghTracerLib, "tracer_output_VTL"); + tracer_output_B = (void (__stdcall *)(unsigned short,const unsigned char *,unsigned int)) GetProcAddress(ghTracerLib, "tracer_output_B"); + tracer_get_caps = (void (__stdcall *) (char *,size_t*)) GetProcAddress(ghTracerLib, "tracer_get_capabilities"); + tracer_get_outputport_status = (long (__stdcall *)(void)) GetProcAddress(ghTracerLib, "tracer_get_outputport_status"); + + (*tracer_init_outputport) (GetModuleHandle(0), "tcp", "localhost"); +#endif +} + +void ReporterExit() +{ +#ifdef INCL_TRACE + if (ghTracerLib == NULL) + return; + (*tracer_close_outputport)(); + FreeLibrary(ghTracerLib); +#endif +} + +void ReporterOutputDebugString(char *pszFormat, va_list va) +{ +#ifdef INCL_TRACE + if (ghTracerLib == NULL) + return; + (*tracer_output_VTL)(0, pszFormat, &va); +#endif +} diff --git a/game/win/savegame.cpp b/game/win/savegame.cpp new file mode 100644 index 0000000..f6e2cde --- /dev/null +++ b/game/win/savegame.cpp @@ -0,0 +1,379 @@ +// Turn this on to test the UNICODE path under XP (needed for PocketPC which is only +// unicode). Be sure to turn it off before checking; Win9x / ME doesn't have unicode. + +//#define UNICODE + +#include "..\ht.h" +#include +#ifdef DEBUG +#define _CRTDBG_MAP_ALLOC +#endif +#include +#if defined(DEBUG) && !defined(CE) +//#include +#endif +#include +#include + +class FileStream : public Stream +{ +public: + FileStream(); + ~FileStream(); + bool Init(char *pszMode, char *pszFile, char *pszDelete, char *pszFinal); + + virtual void Close(); + virtual dword Read(void *pv, dword cb); + virtual dword Write(void *pv, dword cb); + virtual bool IsSuccess(); + +private: + bool m_fSuccess; + FILE *m_pf; + char m_szFile[_MAX_PATH]; + char m_szDelete[_MAX_PATH]; + char m_szFinal[_MAX_PATH]; +}; + +FileStream::FileStream() +{ + m_fSuccess = true; + m_pf = NULL; + m_szFile[0] = 0; + m_szDelete[0] = 0; + m_szFinal[0] = 0; +} + +FileStream::~FileStream() +{ + Assert(m_pf == NULL); +} + +bool FileStream::Init(char *pszMode, char *pszFile, char *pszDelete, char *pszFinal) +{ + m_pf = fopen(pszFile, pszMode); + if (m_pf == NULL) + return false; + strcpy(m_szFile, pszFile); + if (pszDelete != NULL) + strcpy(m_szDelete, pszDelete); + if (pszFinal != NULL) + strcpy(m_szFinal, pszFinal); + return true; +} + +void FileStream::Close() +{ + fclose(m_pf); + m_pf = NULL; + + // Delete and rename file if no error + + if (m_fSuccess) { + if (m_szDelete[0] != 0) { + TCHAR szFilename[MAX_PATH]; +#ifdef UNICODE + MultiByteToWideChar(CP_ACP, 0, m_szDelete, -1, szFilename, ARRAYSIZE(szFilename) - 1); +#else + strcpy(szFilename, m_szDelete); +#endif + + DeleteFile(szFilename); + } + if (m_szFinal[0] != 0) { + TCHAR szFile[MAX_PATH]; + TCHAR szFinal[MAX_PATH]; +#ifdef UNICODE + MultiByteToWideChar(CP_ACP, 0, m_szFile, -1, szFile, ARRAYSIZE(szFile) - 1); + MultiByteToWideChar(CP_ACP, 0, m_szFinal, -1, szFinal, ARRAYSIZE(szFinal) - 1); +#else + strcpy(szFile, m_szFile); + strcpy(szFinal, m_szFinal); +#endif + MoveFile(szFile, szFinal); + } + } +} + +dword FileStream::Read(void *pv, dword cb) +{ + size_t cbT = fread(pv, 1, cb, m_pf); + if (cb != cbT) + m_fSuccess = false; + return cbT; +} + +dword FileStream::Write(void *pv, dword cb) +{ + size_t cbT = fwrite(pv, 1, cb, m_pf); + if (cb != cbT) + m_fSuccess = false; + return cbT; +} + +bool FileStream::IsSuccess() +{ + return m_fSuccess; +} + +void PrependDataDirectory(char *pszIn, char *pszOut) +{ + char *pszDataDir = HostGetDataDirectory(); + strcpy(pszOut, pszDataDir); + strcat(pszOut, "\\"); + strcat(pszOut, pszIn); +} + +bool FindSaveGame(int nGame, char *psz, int cb, int *pc = NULL) +{ + if (pc != NULL) + *pc = 0; + + char szFileSpec[_MAX_PATH]; + PrependDataDirectory("htsave*.bin", szFileSpec); + + TCHAR szT[_MAX_PATH]; +#ifdef UNICODE + MultiByteToWideChar(CP_ACP, 0, szFileSpec, -1, szT, ARRAYSIZE(szT) - 1); +#else + strcpy(szT, szFileSpec); +#endif + WIN32_FIND_DATA find; + HANDLE h = FindFirstFile(szT, &find); + + if (h == INVALID_HANDLE_VALUE) + return false; + + char szCompare[_MAX_PATH]; + sprintf(szCompare, "htsave%d_", nGame); + int cchCompare = strlen(szCompare); + + char szReinitializeSave[20]; + sprintf(szReinitializeSave, "htsave%d_", knGameReinitializeSave); + int cchReinitializeSave = strlen(szReinitializeSave); + + int c = 0; + do { + char szFilename[MAX_PATH]; +#ifdef UNICODE + WideCharToMultiByte(CP_ACP, 0, find.cFileName, -1, szFilename, ARRAYSIZE(szFilename) - 1, NULL, NULL); +#else + strcpy(szFilename, find.cFileName); +#endif + if (strncmp(szCompare, szFilename, cchCompare) == 0) { + FindClose(h); + if (psz != NULL) + strncpyz(psz, szFilename, cb); + return true; + } + + // Don't count the temporary "Reinitialize" saved game + + if (strncmp(szReinitializeSave, szFilename, cchReinitializeSave) == 0) + continue; + + c++; + } while (FindNextFile(h, &find) != 0); + + FindClose(h); + + if (pc != NULL) + *pc = c; + return false; +} + +int HostGetSaveGameCount() +{ + int c; + FindSaveGame(-1, NULL, 0, &c); + return c; +} + +bool HostGetSaveGameName(int nGame, char *psz, int cb, Date *pdate, int *pnHours24, int *pnMinutes, int *pnSeconds) +{ + // Find the game + + char szT[_MAX_PATH]; + if (!FindSaveGame(nGame, szT, sizeof(szT))) { + strncpyz(psz, "-- Empty --", cb); + return false; + } + + char szPath[_MAX_PATH]; + PrependDataDirectory(szT, szPath); + + // Get the creation time in hours24, minutes + // Note to Win32 guys: this is insane. + + TCHAR szFilename[MAX_PATH]; +#ifdef UNICODE + MultiByteToWideChar(CP_ACP, 0, szPath, -1, szFilename, ARRAYSIZE(szFilename) - 1); +#else + strcpy(szFilename, szPath); +#endif + + HANDLE h = CreateFile(szFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (h == INVALID_HANDLE_VALUE) + return false; + + FILETIME timeCreation; + FILETIME timeLastAccess; + FILETIME timeLastWrite; + if (!GetFileTime(h, &timeCreation, &timeLastAccess, &timeLastWrite)) { + CloseHandle(h); + return false; + } + FILETIME timeLastWriteLocal; + FileTimeToLocalFileTime(&timeLastWrite, &timeLastWriteLocal); + SYSTEMTIME timeLastWriteLocalAndReadable; + FileTimeToSystemTime(&timeLastWriteLocal, &timeLastWriteLocalAndReadable); + *pnHours24 = timeLastWriteLocalAndReadable.wHour; + *pnMinutes = timeLastWriteLocalAndReadable.wMinute; + *pnSeconds = timeLastWriteLocalAndReadable.wSecond; + pdate->nDay = timeLastWriteLocalAndReadable.wDay; + pdate->nMonth = timeLastWriteLocalAndReadable.wMonth; + pdate->nYear = timeLastWriteLocalAndReadable.wYear; + CloseHandle(h); + + // Copy over filename, lose prefix + + char *pszName = strchr(szT, '_') + 1; + int cbName = strlen(pszName) - 4 + 1; + strncpyz(psz, pszName, min(cb, cbName)); + + // restore '#' to ':' + + char *pchInvalid = psz; + do { + pchInvalid = strchr(pchInvalid, '#'); + if (pchInvalid != 0) + *pchInvalid = ':'; + } while (pchInvalid != 0); + + return true; +} + +Stream *HostNewSaveGameStream(int nGame, char *pszName) +{ + // Get the old file name - we'll delete this if successful + + char szOld[_MAX_PATH]; + char szOldFull[_MAX_PATH]; + if (!FindSaveGame(nGame, szOld, sizeof(szOld))) { + szOldFull[0] = 0; + } else { + PrependDataDirectory(szOld, szOldFull); + } + + // New file name + + char szNew[_MAX_PATH]; + sprintf(szNew, "htsave%d_%s.bin", nGame, pszName); + + // windows disallows ':' in a filename, so sub those out + + char *pchInvalid = szNew; + do { + pchInvalid = strchr(pchInvalid, ':'); + if (pchInvalid != 0) + *pchInvalid = '#'; + } while (pchInvalid != 0); + + char szNewFull[_MAX_PATH]; + PrependDataDirectory(szNew, szNewFull); + + // Get stream over temp file + + FileStream *pstm = new FileStream(); + if (pstm == NULL) + return NULL; + + // If save is successful, szOld will be deleted and httempsave.bin + // will be renamed to szNew + + char szTempSaveFull[_MAX_PATH]; + PrependDataDirectory("httempsave.bin", szTempSaveFull); + + if (!pstm->Init("wb", szTempSaveFull, szOldFull, szNewFull)) { + delete pstm; + return NULL; + } + + return (Stream *)pstm; +} + +Stream *HostOpenSaveGameStream(int nGame, bool fDelete) +{ + char szT[_MAX_PATH]; + if (!FindSaveGame(nGame, szT, sizeof(szT))) + return NULL; + + // rename to a temporary file before opening + + char szTFull[_MAX_PATH]; + PrependDataDirectory(szT, szTFull); + char szTempNameFull[_MAX_PATH]; + PrependDataDirectory(kszTempName, szTempNameFull); + + TCHAR szSaveGame[_MAX_PATH]; + TCHAR szTempName[_MAX_PATH]; + +#ifdef UNICODE + MultiByteToWideChar(CP_ACP, 0, szTFull, -1, szSaveGame, ARRAYSIZE(szSaveGame) - 1); + MultiByteToWideChar(CP_ACP, 0, szTempNameFull, -1, szTempName, ARRAYSIZE(szTempName) - 1); +#else + strcpy(szSaveGame, szTFull); + strcpy(szTempName, szTempNameFull); +#endif + MoveFile(szSaveGame, szTempName); + + // Get stream over temp file + + FileStream *pstm = new FileStream(); + if (pstm == NULL) + return NULL; + + // If load is successful, and fDelete is True szTempName will be deleted + // if fDelete is false szTempName will be renamed to szSaveGame + + if (!pstm->Init("rb", szTempNameFull, fDelete ? szTempNameFull : NULL, fDelete ? NULL : szTFull)) { + delete pstm; + return NULL; + } + + return (Stream *)pstm; +} + +bool HostDeleteSaveGame(char *psz, int nGame) +{ + // if nGame is > 0, delete that, otherwise if psz is non-null delete that + // return true if we deleted something + + char szSaveGame[_MAX_PATH]; + + if (psz == NULL) { + if (!FindSaveGame(nGame, szSaveGame, sizeof(szSaveGame))) + return false; + } else { + strncpyz(szSaveGame, psz, sizeof(szSaveGame)); + } + + char szSaveGameFull[_MAX_PATH]; + PrependDataDirectory(szSaveGame, szSaveGameFull); + + // the use of ARRAYSIZE says to me that we don't know whether the string will be UNICODE or not + // and this is a conservative way of getting the character count. But why don't we know? + + if (psz != NULL) { + TCHAR szFilename[_MAX_PATH]; +#ifdef UNICODE + MultiByteToWideChar(CP_ACP, 0, szSaveGameFull, -1, szFilename, ARRAYSIZE(szFilename) - 1); +#else + strcpy(szFilename, szSaveGameFull); +#endif + + DeleteFile(szFilename); + return true; + } + return false; +} \ No newline at end of file diff --git a/game/win/win.rc b/game/win/win.rc new file mode 100644 index 0000000..8ff8c9c --- /dev/null +++ b/game/win/win.rc @@ -0,0 +1,5 @@ +#include "htplatform.h" + +kidiMain ICON DISCARDABLE "app.ico" + + diff --git a/game/win/win32sounddev.cpp b/game/win/win32sounddev.cpp new file mode 100644 index 0000000..f5aa7d0 --- /dev/null +++ b/game/win/win32sounddev.cpp @@ -0,0 +1,339 @@ +#include "..\ht.h" + +#define kcChannels 4 + +class Win32SoundDevice : public SoundDevice // sndd +{ +public: + Win32SoundDevice(); + virtual ~Win32SoundDevice(); + + bool Init(); + virtual void Enable(bool fEnable); + virtual bool IsEnabled(); + virtual void PlayAdpcm(int ichnl, byte *pb, word cb); + virtual int GetChannelCount(); + virtual bool IsChannelFree(int ichnl); + virtual void ServiceProc(); + virtual bool IsSilent(); + virtual void SetVolume(int nVolume); + virtual int GetVolume(); + +private: + void InitNextBufferForPlayback(int nBuffer); + + int m_cBuffers; + int m_cbBuffer; + HWAVEOUT m_hwavo; + WAVEHDR *m_awavHeader; + byte *m_abWaveData; + Channel m_achnl[kcChannels]; + bool m_fEnable; + long m_tSilence; + int m_nVolumeLast; + bool m_fVolumeFixed; + CRITICAL_SECTION m_crit; + + friend void CALLBACK WaveOutProc(HWAVEOUT hwavo, UINT msg, dword dwInstance, dword wp, dword lp); +}; + +SoundDevice *CreateWin32SoundDevice() +{ + Win32SoundDevice *pwinsndd = new Win32SoundDevice; + if (pwinsndd == NULL) + return NULL; + if (!pwinsndd->Init()) { + delete pwinsndd; + return NULL; + } + + return (SoundDevice *)pwinsndd; +} + +Win32SoundDevice::Win32SoundDevice() +{ + m_tSilence = 0; + m_fEnable = false; + m_hwavo = NULL; + m_awavHeader = NULL; + m_abWaveData = NULL; + m_cBuffers = 0; + m_cbBuffer = 0; + m_fVolumeFixed = false; + InitializeCriticalSection(&m_crit); +} + +Win32SoundDevice::~Win32SoundDevice() +{ + EnterCriticalSection(&m_crit); + Enable(false); + if (m_hwavo != NULL) { + waveOutReset(m_hwavo); + for (int n = 0; n < m_cBuffers; n++) + waveOutUnprepareHeader(m_hwavo, &m_awavHeader[n], sizeof(WAVEHDR)); + if (m_hwavo != NULL) { + waveOutClose(m_hwavo); + m_hwavo = NULL; + } + } + LeaveCriticalSection(&m_crit); + DeleteCriticalSection(&m_crit); + + delete m_awavHeader; + m_awavHeader = NULL; + delete m_abWaveData; + m_abWaveData = NULL; +} + +bool Win32SoundDevice::Init() +{ +#ifdef CE + // Get version information. Buffer size will be based on this + + OSVERSIONINFO osver; + osver.dwOSVersionInfoSize = sizeof(osver); + GetVersionEx(&osver); + +#define kdwBuildPocketPC 9348 +#define kdwBuildPocketPC2002 11171 + +#define kcBuffersSlow 6 +#define kcbBufferSlow 512 + +#define kcBuffersFast 6 +#define kcbBufferFast 512 + + m_cBuffers = kcBuffersFast; + m_cbBuffer = kcbBufferFast; + + // Below PocketPC2002 latency performance sucks. + + if (osver.dwMajorVersion == 3) { + // Is PocketPC but less than 2002? + + if (osver.dwMinorVersion == 0 && osver.dwBuildNumber < kdwBuildPocketPC2002) { + m_cBuffers = kcBuffersSlow; + m_cbBuffer = kcbBufferSlow; + } + } + +#else + // Windows + + m_cBuffers = 3; + m_cbBuffer = 256; +#endif + + m_awavHeader = new WAVEHDR[m_cBuffers]; + if (m_awavHeader == NULL) + return false; + m_abWaveData = new byte[m_cBuffers * m_cbBuffer]; + if (m_abWaveData == NULL) + return false; + + EnterCriticalSection(&m_crit); + + // Open sound device + + WAVEFORMATEX fmt; + fmt.wFormatTag = WAVE_FORMAT_PCM; + fmt.nChannels = 1; + fmt.nSamplesPerSec = 8000; + fmt.nAvgBytesPerSec = 8000; + fmt.nBlockAlign = 1; + fmt.wBitsPerSample = 8; + fmt.cbSize = 0; + MMRESULT res = waveOutOpen(&m_hwavo, WAVE_MAPPER, &fmt, + (dword)WaveOutProc, (dword)this, CALLBACK_FUNCTION); + if (res != MMSYSERR_NOERROR) { + LeaveCriticalSection(&m_crit); + return false; + } + + // Remember what the initial volume is + + m_nVolumeLast = GetVolume(); + + // Init headers + + for (int n = 0; n < m_cBuffers; n++) { + memset(&m_awavHeader[n], 0, sizeof(WAVEHDR)); + m_awavHeader[n].lpData = (char *)&m_abWaveData[n * m_cbBuffer]; + m_awavHeader[n].dwBufferLength = m_cbBuffer; + waveOutPrepareHeader(m_hwavo, &m_awavHeader[n], sizeof(WAVEHDR)); + } + + // Some PocketPC devices disallow devices to change the volume. Detect if we're running on + // one of these types of devices. + + int nVolumeSave = GetVolume(); + if (nVolumeSave >= 0x80) { + SetVolume(nVolumeSave - 0x20); + } else { + SetVolume(nVolumeSave + 0x20); + } + int nVolumeNew = GetVolume(); + SetVolume(nVolumeSave); + if (nVolumeNew == nVolumeSave) + m_fVolumeFixed = true; + + LeaveCriticalSection(&m_crit); + return true; +} + +bool Win32SoundDevice::IsEnabled() +{ + return m_fEnable; +} + +void Win32SoundDevice::Enable(bool fEnable) +{ + EnterCriticalSection(&m_crit); + if (fEnable) { + if (!m_fEnable) { + memset(m_achnl, 0, sizeof(m_achnl)); + m_tSilence = 0; + m_fEnable = true; + for (int nBuffer = 0; nBuffer < m_cBuffers; nBuffer++) { + m_awavHeader[nBuffer].dwFlags |= WHDR_DONE; + InitNextBufferForPlayback(nBuffer); + } + SetSoundServiceDevice(this); + } + } else { + if (m_fEnable) { + memset(m_achnl, 0, sizeof(m_achnl)); + m_tSilence = 0; + m_fEnable = false; + SetSoundServiceDevice(NULL); + } + } + LeaveCriticalSection(&m_crit); +} + +int Win32SoundDevice::GetChannelCount() +{ + return kcChannels; +} + +bool Win32SoundDevice::IsChannelFree(int ichnl) +{ + if (ichnl < 0 || ichnl >= kcChannels) + return false; + Channel *pchnl = &m_achnl[ichnl]; + return (pchnl->pb >= pchnl->pbEnd); +} + +bool Win32SoundDevice::IsSilent() +{ + if (!m_fEnable) + return true; + if (m_tSilence == 0) + return true; + long tCurrent = HostGetTickCount(); + if (tCurrent < 0 && m_tSilence >= 0) + return true; + return tCurrent >= m_tSilence; +} + +void Win32SoundDevice::ServiceProc() +{ +#ifdef CHECK_OLD_ALLOCS + my_alloccheck(); +#endif +} + +void Win32SoundDevice::PlayAdpcm(int ichnl, byte *pb, word cb) +{ + if (ichnl < 0 || ichnl >= kcChannels) + return; + if (!m_fEnable) + return; + + // Some PocketPC's reset the volume to the "system setting" when the + // unit is turned off then turned back on. Reset the volume to our + // setting in this case + + int nVolume = GetVolume(); + if (nVolume != m_nVolumeLast) + SetVolume(m_nVolumeLast); + + // Start it up + + Channel *pchnl = &m_achnl[ichnl]; + pchnl->pb = pb; + pchnl->pbEnd = pb + cb; + pchnl->nStepIndex = 0; + pchnl->bSampleLast = 128; + m_tSilence = HostGetTickCount() + (cb + m_cBuffers * m_cbBuffer + 39) / 40 + 1; +} + +void Win32SoundDevice::SetVolume(int nVolume) +{ + if (m_fVolumeFixed) + return; + + if (nVolume < 0) + nVolume = 0; + if (nVolume > 255) + nVolume = 255; + dword dwVolume = nVolume | (nVolume << 8) | (nVolume << 16) | (nVolume << 24); + EnterCriticalSection(&m_crit); + waveOutSetVolume(m_hwavo, dwVolume); + m_nVolumeLast = GetVolume(); + LeaveCriticalSection(&m_crit); +} + +int Win32SoundDevice::GetVolume() +{ + if (m_fVolumeFixed) + return -1; + + dword dwVolume; + EnterCriticalSection(&m_crit); + waveOutGetVolume(m_hwavo, &dwVolume); + LeaveCriticalSection(&m_crit); + return (int)(byte)dwVolume; +} + +void CALLBACK WaveOutProc(HWAVEOUT hwavo, UINT msg, dword dwInstance, dword wp, dword lp) +{ + if (msg != WOM_DONE) + return; + Win32SoundDevice *pwinsndd = (Win32SoundDevice *)dwInstance; + WAVEHDR *pwavHeader = (WAVEHDR *)wp; + int nBuffer = pwavHeader - pwinsndd->m_awavHeader; + pwinsndd->InitNextBufferForPlayback(nBuffer); +} + +void Win32SoundDevice::InitNextBufferForPlayback(int nBuffer) +{ + // Don't send buffers to the device if not enabled + + if (!m_fEnable) + return; + + // Fill the chunk and queue it for playing + + Assert(nBuffer >= 0 && nBuffer < m_cBuffers); + if (nBuffer < 0 || nBuffer >= m_cBuffers) + return; + + // This is protected by critical sections! The second thread and the UI thread + // can both hang on waveOutWrite unless access is serialized. + + EnterCriticalSection(&m_crit); + Assert(m_awavHeader[nBuffer].dwFlags & WHDR_DONE); + m_awavHeader[nBuffer].dwFlags &= ~WHDR_DONE; + MixChannels(m_achnl, ARRAYSIZE(m_achnl), &m_abWaveData[nBuffer * m_cbBuffer], m_cbBuffer); + waveOutWrite(m_hwavo, &m_awavHeader[nBuffer], sizeof(WAVEHDR)); + + // For AVI recording + +#ifndef CE + if (gpavir != NULL) + gpavir->AddAudio(&m_abWaveData[nBuffer * m_cbBuffer], m_cbBuffer); +#endif + + LeaveCriticalSection(&m_crit); +} diff --git a/game/win/winpackfile.cpp b/game/win/winpackfile.cpp new file mode 100644 index 0000000..993acb3 --- /dev/null +++ b/game/win/winpackfile.cpp @@ -0,0 +1,41 @@ +#include "winpackfile.h" + +PdbReader *WinPackFileReader::OpenPdb(char *szDir, char *pszFn) { + // Load data from this directory + + char szT[_MAX_PATH]; + strcpy(szT, pszDir); + strcat(szT, "\\"); + strcat(szT, pszFn); + + // PdbReader over memory + + MemPdbReader *ppdbReader = new MemPdbReader; + if (ppdbReader == NULL) { + Trace("FilePdbReader null"); + return false; + } + if (!ppdbReader->Open(szT)) { + delete ppdbReader; + Trace("FilePdbReader::Open(%s, %s) failed", pszDir, pszFn); + return false; + } + + return ppdbReader; +} + +bool WinPackFileReader::DeletePdb(char *szDir, char *pszFn) { + char szT[_MAX_PATH]; + strcpy(szT, pszDir); + strcat(szT, "\\"); + strcat(szT, pszFn); + + TCHAR szFilename[_MAX_PATH]; +#ifdef UNICODE + MultiByteToWideChar(CP_ACP, 0, szT, -1, szFilename, ARRAYSIZE(szFilename) - 1); +#else + strcpy(szFilename, szT); +#endif + DeleteFile(szFilename); + return true; +} diff --git a/game/win/winpackfile.h b/game/win/winpackfile.h new file mode 100644 index 0000000..92757e7 --- /dev/null +++ b/game/win/winpackfile.h @@ -0,0 +1,17 @@ +#ifndef __WINPACKFILE_H__ +#define __WINPACKFILE_H__ + +#include "inc/basictypes.h" + +namespace wi { + +class WinPackFileReader : public PackFileReader +{ +private: + virtual PdbReader *OpenPdb(char *pszDir, char *pszFn); + virtual bool DeletePdb(char *pszDir, char *pszFn); +}; + +} // namespace wi + +#endif // __WINPACKFILE_H__ diff --git a/game/wisdl.xcodeproj/project.pbxproj b/game/wisdl.xcodeproj/project.pbxproj new file mode 100644 index 0000000..6a8ddda --- /dev/null +++ b/game/wisdl.xcodeproj/project.pbxproj @@ -0,0 +1,1278 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 5501E616139076E20048AAFE /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AC651390627C003B15C5 /* main.cpp */; }; + 5501E62E139079710048AAFE /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 5501E61C139079710048AAFE /* Credits.rtf */; }; + 5501E62F139079710048AAFE /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5501E61E139079710048AAFE /* InfoPlist.strings */; }; + 5501E630139079710048AAFE /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5501E620139079710048AAFE /* MainMenu.xib */; }; + 5501E632139079710048AAFE /* SDLMain.m in Sources */ = {isa = PBXBuildFile; fileRef = 5501E624139079710048AAFE /* SDLMain.m */; }; + 551C90DA1390A1860018ECBA /* alertcontrol.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AA6E1390627C003B15C5 /* alertcontrol.cpp */; }; + 551C90DB1390A1860018ECBA /* Andy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AA6F1390627C003B15C5 /* Andy.cpp */; }; + 551C90DC1390A1860018ECBA /* Animation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AA701390627C003B15C5 /* Animation.cpp */; }; + 551C90DD1390A1860018ECBA /* Artillery.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AA711390627C003B15C5 /* Artillery.cpp */; }; + 551C90DE1390A1860018ECBA /* bitmap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AA721390627C003B15C5 /* bitmap.cpp */; }; + 551C90DF1390A1860018ECBA /* Builder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AA741390627C003B15C5 /* Builder.cpp */; }; + 551C90E01390A1860018ECBA /* BuildMgr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AA751390627C003B15C5 /* BuildMgr.cpp */; }; + 551C90E11390A1860018ECBA /* cachemgr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AA761390627C003B15C5 /* cachemgr.cpp */; }; + 551C90E21390A1860018ECBA /* compression.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AA801390627C003B15C5 /* compression.cpp */; }; + 551C90E31390A1860018ECBA /* CutScene.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AA851390627C003B15C5 /* CutScene.cpp */; }; + 551C90E41390A1860018ECBA /* drm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AA8D1390627C003B15C5 /* drm.cpp */; }; + 551C90E51390A1860018ECBA /* Ecom.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AA8E1390627C003B15C5 /* Ecom.cpp */; }; + 551C90E61390A1860018ECBA /* event.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AA901390627C003B15C5 /* event.cpp */; }; + 551C90E71390A1860018ECBA /* fogmap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AA951390627C003B15C5 /* fogmap.cpp */; }; + 551C90E81390A1860018ECBA /* font.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AA961390627C003B15C5 /* font.cpp */; }; + 551C90E91390A1860018ECBA /* form.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AA971390627C003B15C5 /* form.cpp */; }; + 551C90EA1390A1860018ECBA /* formmgr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AA981390627C003B15C5 /* formmgr.cpp */; }; + 551C90EB1390A1860018ECBA /* game.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AA991390627C003B15C5 /* game.cpp */; }; + 551C90EC1390A1860018ECBA /* GameObjects.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AA9C1390627C003B15C5 /* GameObjects.cpp */; }; + 551C90ED1390A1860018ECBA /* GameOptions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AA9D1390627C003B15C5 /* GameOptions.cpp */; }; + 551C90EE1390A1860018ECBA /* Headquarters.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AA9F1390627C003B15C5 /* Headquarters.cpp */; }; + 551C90EF1390A1860018ECBA /* Help.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AAA01390627C003B15C5 /* Help.cpp */; }; + 551C90F01390A1860018ECBA /* HRC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AAA11390627C003B15C5 /* HRC.cpp */; }; + 551C90F11390A1860018ECBA /* InputUI.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AAB11390627C003B15C5 /* InputUI.cpp */; }; + 551C90F21390A1860018ECBA /* Level.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AAF31390627C003B15C5 /* Level.cpp */; }; + 551C90F31390A1860018ECBA /* loadsave.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AAF61390627C003B15C5 /* loadsave.cpp */; }; + 551C90F41390A1860018ECBA /* LRInfantry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AAFF1390627C003B15C5 /* LRInfantry.cpp */; }; + 551C90F51390A1860018ECBA /* memmgr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AC221390627C003B15C5 /* memmgr.cpp */; }; + 551C90F61390A1860018ECBA /* Miner.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AC251390627C003B15C5 /* Miner.cpp */; }; + 551C90F71390A1860018ECBA /* misc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AC261390627C003B15C5 /* misc.cpp */; }; + 551C90F81390A1860018ECBA /* misccontrols.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AC271390627C003B15C5 /* misccontrols.cpp */; }; + 551C90F91390A1860018ECBA /* mixer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AC2B1390627C003B15C5 /* mixer.cpp */; }; + 551C90FA1390A1860018ECBA /* MobileBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AC2E1390627C003B15C5 /* MobileBuilder.cpp */; }; + 551C90FB1390A1860018ECBA /* MobileHQ.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AC2F1390627C003B15C5 /* MobileHQ.cpp */; }; + 551C90FC1390A1860018ECBA /* MobileUnit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AC301390627C003B15C5 /* MobileUnit.cpp */; }; + 551C90FD1390A1860018ECBA /* Overmind.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AC351390627C003B15C5 /* Overmind.cpp */; }; + 551C90FE1390A1860018ECBA /* Player.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AC391390627C003B15C5 /* Player.cpp */; }; + 551C90FF1390A1860018ECBA /* Processor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AC401390627C003B15C5 /* Processor.cpp */; }; + 551C91001390A1860018ECBA /* Radar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AC421390627C003B15C5 /* Radar.cpp */; }; + 551C91011390A1860018ECBA /* RawBitmap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AC431390627C003B15C5 /* RawBitmap.cpp */; }; + 551C91021390A1860018ECBA /* Reactor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AC441390627C003B15C5 /* Reactor.cpp */; }; + 551C91031390A1860018ECBA /* Replicator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AC461390627C003B15C5 /* Replicator.cpp */; }; + 551C91041390A1860018ECBA /* Research.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AC481390627C003B15C5 /* Research.cpp */; }; + 551C91051390A1860018ECBA /* Shell.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5ACBD1390627C003B15C5 /* Shell.cpp */; }; + 551C91061390A1860018ECBA /* SimUI.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5ACC01390627C003B15C5 /* SimUI.cpp */; }; + 551C91071390A1860018ECBA /* Simulation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5ACC11390627C003B15C5 /* Simulation.cpp */; }; + 551C91081390A1860018ECBA /* soundmgr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5ACC91390627C003B15C5 /* soundmgr.cpp */; }; + 551C91091390A1860018ECBA /* SpInfantry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5ACCA1390627C003B15C5 /* SpInfantry.cpp */; }; + 551C910A1390A1860018ECBA /* SRInfantry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5ACCC1390627C003B15C5 /* SRInfantry.cpp */; }; + 551C910B1390A1860018ECBA /* StateMachine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5ACCF1390627C003B15C5 /* StateMachine.cpp */; }; + 551C910C1390A1860018ECBA /* Struct.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5ACD41390627C003B15C5 /* Struct.cpp */; }; + 551C910D1390A1860018ECBA /* Tank.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5ACD61390627C003B15C5 /* Tank.cpp */; }; + 551C910E1390A1860018ECBA /* tbitmap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5ACD71390627C003B15C5 /* tbitmap.cpp */; }; + 551C910F1390A1860018ECBA /* terrainmap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5ACD91390627C003B15C5 /* terrainmap.cpp */; }; + 551C91101390A1860018ECBA /* tilemap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5ACDC1390627C003B15C5 /* tilemap.cpp */; }; + 551C91111390A1860018ECBA /* timer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5ACDD1390627C003B15C5 /* timer.cpp */; }; + 551C91121390A1860018ECBA /* Tower.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5ACE01390627C003B15C5 /* Tower.cpp */; }; + 551C91131390A1860018ECBA /* TriggerActions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5ACE11390627C003B15C5 /* TriggerActions.cpp */; }; + 551C91141390A1860018ECBA /* TriggerConditions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5ACE21390627C003B15C5 /* TriggerConditions.cpp */; }; + 551C91151390A1860018ECBA /* triggermgr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5ACE31390627C003B15C5 /* triggermgr.cpp */; }; + 551C91161390A1860018ECBA /* Unit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5ACE41390627C003B15C5 /* Unit.cpp */; }; + 551C91171390A1860018ECBA /* UnitGroupMgr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5ACE51390627C003B15C5 /* UnitGroupMgr.cpp */; }; + 551C91181390A1860018ECBA /* updatemap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5ACE61390627C003B15C5 /* updatemap.cpp */; }; + 551C91191390A1860018ECBA /* VTS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5ACEA1390627C003B15C5 /* VTS.cpp */; }; + 551C911A1390A1860018ECBA /* Warehouse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5ACEB1390627C003B15C5 /* Warehouse.cpp */; }; + 551C911B1390A2F00018ECBA /* chatter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AA791390627C003B15C5 /* chatter.cpp */; }; + 551C911C1390A2F00018ECBA /* chooseserverform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AA7B1390627C003B15C5 /* chooseserverform.cpp */; }; + 551C911D1390A2F00018ECBA /* comm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AA7D1390627C003B15C5 /* comm.cpp */; }; + 551C911E1390A2F00018ECBA /* completemanager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AA7E1390627C003B15C5 /* completemanager.cpp */; }; + 551C911F1390A2F00018ECBA /* creategameform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AA811390627C003B15C5 /* creategameform.cpp */; }; + 551C91201390A2F00018ECBA /* createroomform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AA831390627C003B15C5 /* createroomform.cpp */; }; + 551C91211390A2F00018ECBA /* dlmissionpack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AA881390627C003B15C5 /* dlmissionpack.cpp */; }; + 551C91221390A2F00018ECBA /* downloadbox.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AA891390627C003B15C5 /* downloadbox.cpp */; }; + 551C91231390A2F00018ECBA /* dragrect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AA8A1390627C003B15C5 /* dragrect.cpp */; }; + 551C91241390A2F00018ECBA /* drawscan.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AA8C1390627C003B15C5 /* drawscan.cpp */; }; + 551C91251390A2F00018ECBA /* fingerhandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AA931390627C003B15C5 /* fingerhandler.cpp */; }; + 551C91261390A2F00018ECBA /* flickscroller.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AA941390627C003B15C5 /* flickscroller.cpp */; }; + 551C91271390A2F00018ECBA /* gameform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AA9A1390627C003B15C5 /* gameform.cpp */; }; + 551C91281390A2F00018ECBA /* httpindexloader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AAA71390627C003B15C5 /* httpindexloader.cpp */; }; + 551C91291390A2F00018ECBA /* httppackinfomanager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AAA91390627C003B15C5 /* httppackinfomanager.cpp */; }; + 551C912A1390A2F00018ECBA /* httppackmanager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AAAB1390627C003B15C5 /* httppackmanager.cpp */; }; + 551C912B1390A2F00018ECBA /* httprequest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AAAD1390627C003B15C5 /* httprequest.cpp */; }; + 551C912C1390A2F00018ECBA /* lobby.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AAF71390627C003B15C5 /* lobby.cpp */; }; + 551C912D1390A2F00018ECBA /* lobbyform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AAF91390627C003B15C5 /* lobbyform.cpp */; }; + 551C912E1390A2F00018ECBA /* loginform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AAFB1390627C003B15C5 /* loginform.cpp */; }; + 551C912F1390A2F00018ECBA /* loginhandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AAFD1390627C003B15C5 /* loginhandler.cpp */; }; + 551C91301390A2F00018ECBA /* map.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AC201390627C003B15C5 /* map.cpp */; }; + 551C91311390A2F10018ECBA /* mempdbreader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AC231390627C003B15C5 /* mempdbreader.cpp */; }; + 551C91321390A2F10018ECBA /* missionlist.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AC291390627C003B15C5 /* missionlist.cpp */; }; + 551C91331390A2F10018ECBA /* Multiplayer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AC321390627C003B15C5 /* Multiplayer.cpp */; }; + 551C91341390A2F10018ECBA /* picktransportform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AC371390627C003B15C5 /* picktransportform.cpp */; }; + 551C91351390A2F10018ECBA /* roomform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AC491390627C003B15C5 /* roomform.cpp */; }; + 551C91361390A2F10018ECBA /* display.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AC4D1390627C003B15C5 /* display.cpp */; }; + 551C91371390A2F10018ECBA /* host.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AC4E1390627C003B15C5 /* host.cpp */; }; + 551C91391390A2F10018ECBA /* serviceurls.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5ACBB1390627C003B15C5 /* serviceurls.cpp */; }; + 551C913A1390A2F10018ECBA /* simplerequest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5ACBE1390627C003B15C5 /* simplerequest.cpp */; }; + 551C913B1390A2F10018ECBA /* stateframe.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5ACCD1390627C003B15C5 /* stateframe.cpp */; }; + 551C913C1390A2F10018ECBA /* statetracker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5ACD01390627C003B15C5 /* statetracker.cpp */; }; + 551C913D1390A2F10018ECBA /* stylushandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5ACD51390627C003B15C5 /* stylushandler.cpp */; }; + 551C913E1390A2F10018ECBA /* uploader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5ACE71390627C003B15C5 /* uploader.cpp */; }; + 551C913F1390A3930018ECBA /* savegame.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AC791390627C003B15C5 /* savegame.cpp */; }; + 551C91421390A3940018ECBA /* sdlpackfile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5AC7E1390627C003B15C5 /* sdlpackfile.cpp */; }; + 551C91601390A40F0018ECBA /* yajl.c in Sources */ = {isa = PBXBuildFile; fileRef = 551C914A1390A40F0018ECBA /* yajl.c */; }; + 551C91611390A40F0018ECBA /* yajl_buf.c in Sources */ = {isa = PBXBuildFile; fileRef = 551C914B1390A40F0018ECBA /* yajl_buf.c */; }; + 551C91621390A40F0018ECBA /* yajl_encode.c in Sources */ = {isa = PBXBuildFile; fileRef = 551C914D1390A40F0018ECBA /* yajl_encode.c */; }; + 551C91641390A40F0018ECBA /* yajl_lex.c in Sources */ = {isa = PBXBuildFile; fileRef = 551C91501390A40F0018ECBA /* yajl_lex.c */; }; + 551C91651390A40F0018ECBA /* yajl_parser.c in Sources */ = {isa = PBXBuildFile; fileRef = 551C91521390A40F0018ECBA /* yajl_parser.c */; }; + 551C91681390A40F0018ECBA /* jsonbuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 551C91571390A40F0018ECBA /* jsonbuilder.cpp */; }; + 551C916A1390A40F0018ECBA /* jsontypes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 551C915A1390A40F0018ECBA /* jsontypes.cpp */; }; + 551C91951390A42C0018ECBA /* base64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 551C916F1390A42C0018ECBA /* base64.cpp */; }; + 551C91961390A42C0018ECBA /* bytebuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 551C91711390A42C0018ECBA /* bytebuffer.cpp */; }; + 551C91971390A42C0018ECBA /* deletetracker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 551C91741390A42C0018ECBA /* deletetracker.cpp */; }; + 551C91991390A42C0018ECBA /* eventer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 551C91791390A42C0018ECBA /* eventer.cpp */; }; + 551C919A1390A42C0018ECBA /* format.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 551C917B1390A42C0018ECBA /* format.cpp */; }; + 551C919B1390A42C0018ECBA /* md5c.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 551C917F1390A42C0018ECBA /* md5c.cpp */; }; + 551C919C1390A42C0018ECBA /* messagehandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 551C91801390A42C0018ECBA /* messagehandler.cpp */; }; + 551C919D1390A42C0018ECBA /* messagequeue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 551C91821390A42C0018ECBA /* messagequeue.cpp */; }; + 551C919E1390A42C0018ECBA /* misc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 551C91841390A42C0018ECBA /* misc.cpp */; }; + 551C919F1390A42D0018ECBA /* selectserver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 551C91861390A42C0018ECBA /* selectserver.cpp */; }; + 551C91A01390A42D0018ECBA /* socket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 551C91891390A42C0018ECBA /* socket.cpp */; }; + 551C91A11390A42D0018ECBA /* socketaddress.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 551C918B1390A42C0018ECBA /* socketaddress.cpp */; }; + 551C91A21390A42D0018ECBA /* socketserver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 551C918D1390A42C0018ECBA /* socketserver.cpp */; }; + 551C91A41390A42D0018ECBA /* thread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 551C91911390A42C0018ECBA /* thread.cpp */; }; + 551C91A51390A42D0018ECBA /* tick.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 551C91931390A42C0018ECBA /* tick.cpp */; }; + 551C91C41390A43E0018ECBA /* decompress.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 551C91A81390A43E0018ECBA /* decompress.cpp */; }; + 551C91C51390A43E0018ECBA /* indexloader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 551C91AB1390A43E0018ECBA /* indexloader.cpp */; }; + 551C91C61390A43E0018ECBA /* ini.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 551C91AD1390A43E0018ECBA /* ini.cpp */; }; + 551C91C71390A43E0018ECBA /* messages.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 551C91AF1390A43E0018ECBA /* messages.cpp */; }; + 551C91C81390A43F0018ECBA /* misc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 551C91B11390A43E0018ECBA /* misc.cpp */; }; + 551C91CA1390A43F0018ECBA /* netmessage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 551C91B51390A43E0018ECBA /* netmessage.cpp */; }; + 551C91CB1390A43F0018ECBA /* packfile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 551C91B71390A43E0018ECBA /* packfile.cpp */; }; + 551C91CC1390A43F0018ECBA /* packinfomanager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 551C91B91390A43E0018ECBA /* packinfomanager.cpp */; }; + 551C91CD1390A43F0018ECBA /* packmanager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 551C91BB1390A43E0018ECBA /* packmanager.cpp */; }; + 551C91CE1390A43F0018ECBA /* xmsglog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 551C91C01390A43E0018ECBA /* xmsglog.cpp */; }; + 551C91CF1390A43F0018ECBA /* xpump.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 551C91C21390A43E0018ECBA /* xpump.cpp */; }; + 551C91D11390A90D0018ECBA /* rip.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 551C91D01390A90D0018ECBA /* rip.cpp */; }; + 551C91D21390A9960018ECBA /* stringtable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5ACD31390627C003B15C5 /* stringtable.cpp */; }; + 551C91D31390AA050018ECBA /* selectmission.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5ACBA1390627C003B15C5 /* selectmission.cpp */; }; + 551C91D41390AA6F0018ECBA /* thunks.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55C5ACDB1390627C003B15C5 /* thunks.cpp */; }; + 551C91EB1390BC570018ECBA /* SDL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 551C91EA1390BC570018ECBA /* SDL.framework */; }; + 5595A9701392DCCE00988594 /* sdlselectionsprite.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5595A96E1392DCCD00988594 /* sdlselectionsprite.cpp */; }; + 55BAE1D8139074B40046D160 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 55BAE1D7139074B40046D160 /* Cocoa.framework */; }; + 55D1DC5A1393FAE50035F179 /* sdlsounddev.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55D1DC591393FAE50035F179 /* sdlsounddev.cpp */; }; + 55D1DC6513940F830035F179 /* icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 55D1DC6413940F830035F179 /* icon.png */; }; + 55D1DC67139414500035F179 /* wi.icns in Resources */ = {isa = PBXBuildFile; fileRef = 55D1DC66139414500035F179 /* wi.icns */; }; + 55D631D11390C105002061DF /* SDL.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 551C91EA1390BC570018ECBA /* SDL.framework */; }; + 55D631DA13918313002061DF /* htdata832.pdb in Resources */ = {isa = PBXBuildFile; fileRef = 55C5AAA41390627C003B15C5 /* htdata832.pdb */; }; + 55D631DB13918313002061DF /* htsfx.pdb in Resources */ = {isa = PBXBuildFile; fileRef = 55C5AAA61390627C003B15C5 /* htsfx.pdb */; }; + 55D631DF13919455002061DF /* hosthelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 55D631DE13919455002061DF /* hosthelpers.mm */; }; + C1647925139C7C22007473A8 /* xtransport.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1647924139C7C22007473A8 /* xtransport.cpp */; }; + C169F5AE139C972D00B4491D /* transportmgr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C169F5AD139C972D00B4491D /* transportmgr.cpp */; }; + C1C82A1F13A57D6C009071DC /* machttprequest.mm in Sources */ = {isa = PBXBuildFile; fileRef = C1C82A1C13A57D6C009071DC /* machttprequest.mm */; }; + C1C82A2013A57D6C009071DC /* machttpservice.mm in Sources */ = {isa = PBXBuildFile; fileRef = C1C82A1E13A57D6C009071DC /* machttpservice.mm */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 5501E6191390777C0048AAFE /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 55D631D11390C105002061DF /* SDL.framework in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 5501E61D139079710048AAFE /* en */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = en; path = en.lproj/Credits.rtf; sourceTree = ""; }; + 5501E61F139079710048AAFE /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + 5501E621139079710048AAFE /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/MainMenu.xib; sourceTree = ""; }; + 5501E622139079710048AAFE /* SDL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = SDL.framework; sourceTree = ""; }; + 5501E623139079710048AAFE /* SDLMain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDLMain.h; sourceTree = ""; }; + 5501E624139079710048AAFE /* SDLMain.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDLMain.m; sourceTree = ""; }; + 5501E625139079710048AAFE /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 5501E626139079710048AAFE /* Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Prefix.pch; sourceTree = ""; }; + 551C91451390A40F0018ECBA /* yajl_common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = yajl_common.h; sourceTree = ""; }; + 551C91461390A40F0018ECBA /* yajl_gen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = yajl_gen.h; sourceTree = ""; }; + 551C91471390A40F0018ECBA /* yajl_parse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = yajl_parse.h; sourceTree = ""; }; + 551C914A1390A40F0018ECBA /* yajl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = yajl.c; sourceTree = ""; }; + 551C914B1390A40F0018ECBA /* yajl_buf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = yajl_buf.c; sourceTree = ""; }; + 551C914C1390A40F0018ECBA /* yajl_buf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = yajl_buf.h; sourceTree = ""; }; + 551C914D1390A40F0018ECBA /* yajl_encode.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = yajl_encode.c; sourceTree = ""; }; + 551C914E1390A40F0018ECBA /* yajl_encode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = yajl_encode.h; sourceTree = ""; }; + 551C91501390A40F0018ECBA /* yajl_lex.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = yajl_lex.c; sourceTree = ""; }; + 551C91511390A40F0018ECBA /* yajl_lex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = yajl_lex.h; sourceTree = ""; }; + 551C91521390A40F0018ECBA /* yajl_parser.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = yajl_parser.c; sourceTree = ""; }; + 551C91531390A40F0018ECBA /* yajl_parser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = yajl_parser.h; sourceTree = ""; }; + 551C91571390A40F0018ECBA /* jsonbuilder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = jsonbuilder.cpp; sourceTree = ""; }; + 551C91581390A40F0018ECBA /* jsonbuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = jsonbuilder.h; sourceTree = ""; }; + 551C915A1390A40F0018ECBA /* jsontypes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = jsontypes.cpp; sourceTree = ""; }; + 551C915B1390A40F0018ECBA /* jsontypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = jsontypes.h; sourceTree = ""; }; + 551C916F1390A42C0018ECBA /* base64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = base64.cpp; sourceTree = ""; }; + 551C91701390A42C0018ECBA /* base64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = base64.h; sourceTree = ""; }; + 551C91711390A42C0018ECBA /* bytebuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = bytebuffer.cpp; sourceTree = ""; }; + 551C91721390A42C0018ECBA /* bytebuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bytebuffer.h; sourceTree = ""; }; + 551C91731390A42C0018ECBA /* criticalsection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = criticalsection.h; sourceTree = ""; }; + 551C91741390A42C0018ECBA /* deletetracker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = deletetracker.cpp; sourceTree = ""; }; + 551C91751390A42C0018ECBA /* deletetracker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = deletetracker.h; sourceTree = ""; }; + 551C91761390A42C0018ECBA /* dispatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dispatcher.h; sourceTree = ""; }; + 551C91791390A42C0018ECBA /* eventer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = eventer.cpp; sourceTree = ""; }; + 551C917A1390A42C0018ECBA /* eventer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = eventer.h; sourceTree = ""; }; + 551C917B1390A42C0018ECBA /* format.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = format.cpp; sourceTree = ""; }; + 551C917C1390A42C0018ECBA /* format.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = format.h; sourceTree = ""; }; + 551C917D1390A42C0018ECBA /* log.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = log.h; sourceTree = ""; }; + 551C917E1390A42C0018ECBA /* md5.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = md5.h; sourceTree = ""; }; + 551C917F1390A42C0018ECBA /* md5c.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = md5c.cpp; sourceTree = ""; }; + 551C91801390A42C0018ECBA /* messagehandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = messagehandler.cpp; sourceTree = ""; }; + 551C91811390A42C0018ECBA /* messagehandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = messagehandler.h; sourceTree = ""; }; + 551C91821390A42C0018ECBA /* messagequeue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = messagequeue.cpp; sourceTree = ""; }; + 551C91831390A42C0018ECBA /* messagequeue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = messagequeue.h; sourceTree = ""; }; + 551C91841390A42C0018ECBA /* misc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = misc.cpp; sourceTree = ""; }; + 551C91851390A42C0018ECBA /* misc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = misc.h; sourceTree = ""; }; + 551C91861390A42C0018ECBA /* selectserver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = selectserver.cpp; sourceTree = ""; }; + 551C91871390A42C0018ECBA /* selectserver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = selectserver.h; sourceTree = ""; }; + 551C91881390A42C0018ECBA /* sigslot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sigslot.h; sourceTree = ""; }; + 551C91891390A42C0018ECBA /* socket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = socket.cpp; sourceTree = ""; }; + 551C918A1390A42C0018ECBA /* socket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = socket.h; sourceTree = ""; }; + 551C918B1390A42C0018ECBA /* socketaddress.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = socketaddress.cpp; sourceTree = ""; }; + 551C918C1390A42C0018ECBA /* socketaddress.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = socketaddress.h; sourceTree = ""; }; + 551C918D1390A42C0018ECBA /* socketserver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = socketserver.cpp; sourceTree = ""; }; + 551C918E1390A42C0018ECBA /* socketserver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = socketserver.h; sourceTree = ""; }; + 551C91911390A42C0018ECBA /* thread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = thread.cpp; sourceTree = ""; }; + 551C91921390A42C0018ECBA /* thread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = thread.h; sourceTree = ""; }; + 551C91931390A42C0018ECBA /* tick.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tick.cpp; sourceTree = ""; }; + 551C91941390A42C0018ECBA /* tick.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tick.h; sourceTree = ""; }; + 551C91A71390A43E0018ECBA /* constants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = constants.h; sourceTree = ""; }; + 551C91A81390A43E0018ECBA /* decompress.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = decompress.cpp; sourceTree = ""; }; + 551C91A91390A43E0018ECBA /* decompress.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = decompress.h; sourceTree = ""; }; + 551C91AA1390A43E0018ECBA /* enum.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = enum.h; sourceTree = ""; }; + 551C91AB1390A43E0018ECBA /* indexloader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = indexloader.cpp; sourceTree = ""; }; + 551C91AC1390A43E0018ECBA /* indexloader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = indexloader.h; sourceTree = ""; }; + 551C91AD1390A43E0018ECBA /* ini.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ini.cpp; sourceTree = ""; }; + 551C91AE1390A43E0018ECBA /* ini.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ini.h; sourceTree = ""; }; + 551C91AF1390A43E0018ECBA /* messages.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = messages.cpp; sourceTree = ""; }; + 551C91B01390A43E0018ECBA /* messages.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = messages.h; sourceTree = ""; }; + 551C91B11390A43E0018ECBA /* misc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = misc.cpp; sourceTree = ""; }; + 551C91B21390A43E0018ECBA /* misc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = misc.h; sourceTree = ""; }; + 551C91B31390A43E0018ECBA /* mp.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = mp.txt; sourceTree = ""; }; + 551C91B41390A43E0018ECBA /* mpht.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mpht.h; sourceTree = ""; }; + 551C91B51390A43E0018ECBA /* netmessage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = netmessage.cpp; sourceTree = ""; }; + 551C91B61390A43E0018ECBA /* netmessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = netmessage.h; sourceTree = ""; }; + 551C91B71390A43E0018ECBA /* packfile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = packfile.cpp; sourceTree = ""; }; + 551C91B81390A43E0018ECBA /* packfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = packfile.h; sourceTree = ""; }; + 551C91B91390A43E0018ECBA /* packinfomanager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = packinfomanager.cpp; sourceTree = ""; }; + 551C91BA1390A43E0018ECBA /* packinfomanager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = packinfomanager.h; sourceTree = ""; }; + 551C91BB1390A43E0018ECBA /* packmanager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = packmanager.cpp; sourceTree = ""; }; + 551C91BC1390A43E0018ECBA /* packmanager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = packmanager.h; sourceTree = ""; }; + 551C91BD1390A43E0018ECBA /* pdbreader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pdbreader.h; sourceTree = ""; }; + 551C91BE1390A43E0018ECBA /* side.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = side.h; sourceTree = ""; }; + 551C91BF1390A43E0018ECBA /* xmsg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xmsg.h; sourceTree = ""; }; + 551C91C01390A43E0018ECBA /* xmsglog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = xmsglog.cpp; sourceTree = ""; }; + 551C91C11390A43E0018ECBA /* xmsglog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xmsglog.h; sourceTree = ""; }; + 551C91C21390A43E0018ECBA /* xpump.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = xpump.cpp; sourceTree = ""; }; + 551C91C31390A43E0018ECBA /* xpump.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xpump.h; sourceTree = ""; }; + 551C91D01390A90D0018ECBA /* rip.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = rip.cpp; path = ../inc/rip.cpp; sourceTree = ""; }; + 551C91EA1390BC570018ECBA /* SDL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDL.framework; path = sdl/mac/SDL.framework; sourceTree = ""; }; + 5595A96C1392DA6900988594 /* sdlspritemgr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sdlspritemgr.h; sourceTree = ""; }; + 5595A96E1392DCCD00988594 /* sdlselectionsprite.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sdlselectionsprite.cpp; sourceTree = ""; }; + 5595A96F1392DCCE00988594 /* sdlselectionsprite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sdlselectionsprite.h; sourceTree = ""; }; + 55BAE1D3139074B40046D160 /* Warfare Incorporated.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Warfare Incorporated.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 55BAE1D7139074B40046D160 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; + 55BAE1DA139074B40046D160 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; + 55BAE1DB139074B40046D160 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; + 55BAE1DC139074B40046D160 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 55C5AA6E1390627C003B15C5 /* alertcontrol.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = alertcontrol.cpp; sourceTree = ""; }; + 55C5AA6F1390627C003B15C5 /* Andy.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Andy.cpp; sourceTree = ""; }; + 55C5AA701390627C003B15C5 /* Animation.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Animation.cpp; sourceTree = ""; }; + 55C5AA711390627C003B15C5 /* Artillery.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Artillery.cpp; sourceTree = ""; }; + 55C5AA721390627C003B15C5 /* bitmap.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = bitmap.cpp; sourceTree = ""; }; + 55C5AA741390627C003B15C5 /* Builder.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Builder.cpp; sourceTree = ""; }; + 55C5AA751390627C003B15C5 /* BuildMgr.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = BuildMgr.cpp; sourceTree = ""; }; + 55C5AA761390627C003B15C5 /* cachemgr.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = cachemgr.cpp; sourceTree = ""; }; + 55C5AA781390627C003B15C5 /* chatcontroller.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = chatcontroller.h; sourceTree = ""; }; + 55C5AA791390627C003B15C5 /* chatter.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = chatter.cpp; sourceTree = ""; }; + 55C5AA7A1390627C003B15C5 /* chatter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = chatter.h; sourceTree = ""; }; + 55C5AA7B1390627C003B15C5 /* chooseserverform.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = chooseserverform.cpp; sourceTree = ""; }; + 55C5AA7C1390627C003B15C5 /* chooseserverform.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = chooseserverform.h; sourceTree = ""; }; + 55C5AA7D1390627C003B15C5 /* comm.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = comm.cpp; sourceTree = ""; }; + 55C5AA7E1390627C003B15C5 /* completemanager.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = completemanager.cpp; sourceTree = ""; }; + 55C5AA7F1390627C003B15C5 /* completemanager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = completemanager.h; sourceTree = ""; }; + 55C5AA801390627C003B15C5 /* compression.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = compression.cpp; sourceTree = ""; }; + 55C5AA811390627C003B15C5 /* creategameform.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = creategameform.cpp; sourceTree = ""; }; + 55C5AA821390627C003B15C5 /* creategameform.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = creategameform.h; sourceTree = ""; }; + 55C5AA831390627C003B15C5 /* createroomform.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = createroomform.cpp; sourceTree = ""; }; + 55C5AA841390627C003B15C5 /* createroomform.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = createroomform.h; sourceTree = ""; }; + 55C5AA851390627C003B15C5 /* CutScene.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CutScene.cpp; sourceTree = ""; }; + 55C5AA861390627C003B15C5 /* DebugHelpers.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = DebugHelpers.cpp; sourceTree = ""; }; + 55C5AA871390627C003B15C5 /* dist.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = dist.plist; sourceTree = ""; }; + 55C5AA881390627C003B15C5 /* dlmissionpack.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = dlmissionpack.cpp; sourceTree = ""; }; + 55C5AA891390627C003B15C5 /* downloadbox.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = downloadbox.cpp; sourceTree = ""; }; + 55C5AA8A1390627C003B15C5 /* dragrect.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = dragrect.cpp; sourceTree = ""; }; + 55C5AA8B1390627C003B15C5 /* dragrect.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = dragrect.h; sourceTree = ""; }; + 55C5AA8C1390627C003B15C5 /* drawscan.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = drawscan.cpp; sourceTree = ""; }; + 55C5AA8D1390627C003B15C5 /* drm.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = drm.cpp; sourceTree = ""; }; + 55C5AA8E1390627C003B15C5 /* Ecom.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Ecom.cpp; sourceTree = ""; }; + 55C5AA8F1390627C003B15C5 /* Entitlements.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Entitlements.plist; sourceTree = ""; }; + 55C5AA901390627C003B15C5 /* event.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = event.cpp; sourceTree = ""; }; + 55C5AA911390627C003B15C5 /* example.json */ = {isa = PBXFileReference; lastKnownFileType = text; path = example.json; sourceTree = ""; }; + 55C5AA921390627C003B15C5 /* filepdbreader.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = filepdbreader.cpp; sourceTree = ""; }; + 55C5AA931390627C003B15C5 /* fingerhandler.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = fingerhandler.cpp; sourceTree = ""; }; + 55C5AA941390627C003B15C5 /* flickscroller.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = flickscroller.cpp; sourceTree = ""; }; + 55C5AA951390627C003B15C5 /* fogmap.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = fogmap.cpp; sourceTree = ""; }; + 55C5AA961390627C003B15C5 /* font.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = font.cpp; sourceTree = ""; }; + 55C5AA971390627C003B15C5 /* form.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = form.cpp; sourceTree = ""; }; + 55C5AA981390627C003B15C5 /* formmgr.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = formmgr.cpp; sourceTree = ""; }; + 55C5AA991390627C003B15C5 /* game.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = game.cpp; sourceTree = ""; }; + 55C5AA9A1390627C003B15C5 /* gameform.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = gameform.cpp; sourceTree = ""; }; + 55C5AA9B1390627C003B15C5 /* gameform.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = gameform.h; sourceTree = ""; }; + 55C5AA9C1390627C003B15C5 /* GameObjects.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = GameObjects.cpp; sourceTree = ""; }; + 55C5AA9D1390627C003B15C5 /* GameOptions.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = GameOptions.cpp; sourceTree = ""; }; + 55C5AA9E1390627C003B15C5 /* hashtablecode.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = hashtablecode.cpp; sourceTree = ""; }; + 55C5AA9F1390627C003B15C5 /* Headquarters.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Headquarters.cpp; sourceTree = ""; }; + 55C5AAA01390627C003B15C5 /* Help.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Help.cpp; sourceTree = ""; }; + 55C5AAA11390627C003B15C5 /* HRC.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = HRC.cpp; sourceTree = ""; }; + 55C5AAA21390627C003B15C5 /* ht.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ht.h; sourceTree = ""; }; + 55C5AAA41390627C003B15C5 /* htdata832.pdb */ = {isa = PBXFileReference; lastKnownFileType = file; path = htdata832.pdb; sourceTree = ""; }; + 55C5AAA61390627C003B15C5 /* htsfx.pdb */ = {isa = PBXFileReference; lastKnownFileType = file; path = htsfx.pdb; sourceTree = ""; }; + 55C5AAA71390627C003B15C5 /* httpindexloader.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = httpindexloader.cpp; sourceTree = ""; }; + 55C5AAA81390627C003B15C5 /* httpindexloader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = httpindexloader.h; sourceTree = ""; }; + 55C5AAA91390627C003B15C5 /* httppackinfomanager.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = httppackinfomanager.cpp; sourceTree = ""; }; + 55C5AAAA1390627C003B15C5 /* httppackinfomanager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = httppackinfomanager.h; sourceTree = ""; }; + 55C5AAAB1390627C003B15C5 /* httppackmanager.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = httppackmanager.cpp; sourceTree = ""; }; + 55C5AAAC1390627C003B15C5 /* httppackmanager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = httppackmanager.h; sourceTree = ""; }; + 55C5AAAD1390627C003B15C5 /* httprequest.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = httprequest.cpp; sourceTree = ""; }; + 55C5AAAE1390627C003B15C5 /* httprequest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = httprequest.h; sourceTree = ""; }; + 55C5AAAF1390627C003B15C5 /* httpservice.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = httpservice.h; sourceTree = ""; }; + 55C5AAB01390627C003B15C5 /* index.json */ = {isa = PBXFileReference; lastKnownFileType = text; path = index.json; sourceTree = ""; }; + 55C5AAB11390627C003B15C5 /* InputUI.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = InputUI.cpp; sourceTree = ""; }; + 55C5AAF31390627C003B15C5 /* Level.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Level.cpp; sourceTree = ""; }; + 55C5AAF51390627C003B15C5 /* license.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = license.h; sourceTree = ""; }; + 55C5AAF61390627C003B15C5 /* loadsave.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = loadsave.cpp; sourceTree = ""; }; + 55C5AAF71390627C003B15C5 /* lobby.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = lobby.cpp; sourceTree = ""; }; + 55C5AAF81390627C003B15C5 /* lobby.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = lobby.h; sourceTree = ""; }; + 55C5AAF91390627C003B15C5 /* lobbyform.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = lobbyform.cpp; sourceTree = ""; }; + 55C5AAFA1390627C003B15C5 /* lobbyform.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = lobbyform.h; sourceTree = ""; }; + 55C5AAFB1390627C003B15C5 /* loginform.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = loginform.cpp; sourceTree = ""; }; + 55C5AAFC1390627C003B15C5 /* loginform.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = loginform.h; sourceTree = ""; }; + 55C5AAFD1390627C003B15C5 /* loginhandler.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = loginhandler.cpp; sourceTree = ""; }; + 55C5AAFE1390627C003B15C5 /* loginhandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = loginhandler.h; sourceTree = ""; }; + 55C5AAFF1390627C003B15C5 /* LRInfantry.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = LRInfantry.cpp; sourceTree = ""; }; + 55C5AC201390627C003B15C5 /* map.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = map.cpp; sourceTree = ""; }; + 55C5AC211390627C003B15C5 /* map.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = map.h; sourceTree = ""; }; + 55C5AC221390627C003B15C5 /* memmgr.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = memmgr.cpp; sourceTree = ""; }; + 55C5AC231390627C003B15C5 /* mempdbreader.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = mempdbreader.cpp; sourceTree = ""; }; + 55C5AC241390627C003B15C5 /* mempdbreader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = mempdbreader.h; sourceTree = ""; }; + 55C5AC251390627C003B15C5 /* Miner.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Miner.cpp; sourceTree = ""; }; + 55C5AC261390627C003B15C5 /* misc.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = misc.cpp; sourceTree = ""; }; + 55C5AC271390627C003B15C5 /* misccontrols.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = misccontrols.cpp; sourceTree = ""; }; + 55C5AC281390627C003B15C5 /* miscgraphics.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = miscgraphics.cpp; sourceTree = ""; }; + 55C5AC291390627C003B15C5 /* missionlist.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = missionlist.cpp; sourceTree = ""; }; + 55C5AC2A1390627C003B15C5 /* missionlist.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = missionlist.h; sourceTree = ""; }; + 55C5AC2B1390627C003B15C5 /* mixer.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = mixer.cpp; sourceTree = ""; }; + 55C5AC2C1390627C003B15C5 /* mixer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = mixer.h; sourceTree = ""; }; + 55C5AC2E1390627C003B15C5 /* MobileBuilder.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MobileBuilder.cpp; sourceTree = ""; }; + 55C5AC2F1390627C003B15C5 /* MobileHQ.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MobileHQ.cpp; sourceTree = ""; }; + 55C5AC301390627C003B15C5 /* MobileUnit.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MobileUnit.cpp; sourceTree = ""; }; + 55C5AC311390627C003B15C5 /* mp_test.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = mp_test.cpp; sourceTree = ""; }; + 55C5AC321390627C003B15C5 /* Multiplayer.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Multiplayer.cpp; sourceTree = ""; }; + 55C5AC331390627C003B15C5 /* multiplayer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = multiplayer.h; sourceTree = ""; }; + 55C5AC341390627C003B15C5 /* ops.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ops.h; sourceTree = ""; }; + 55C5AC351390627C003B15C5 /* Overmind.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Overmind.cpp; sourceTree = ""; }; + 55C5AC371390627C003B15C5 /* picktransportform.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = picktransportform.cpp; sourceTree = ""; }; + 55C5AC381390627C003B15C5 /* picktransportform.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = picktransportform.h; sourceTree = ""; }; + 55C5AC391390627C003B15C5 /* Player.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Player.cpp; sourceTree = ""; }; + 55C5AC401390627C003B15C5 /* Processor.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Processor.cpp; sourceTree = ""; }; + 55C5AC411390627C003B15C5 /* progresscallback.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = progresscallback.h; sourceTree = ""; }; + 55C5AC421390627C003B15C5 /* Radar.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Radar.cpp; sourceTree = ""; }; + 55C5AC431390627C003B15C5 /* RawBitmap.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = RawBitmap.cpp; sourceTree = ""; }; + 55C5AC441390627C003B15C5 /* Reactor.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Reactor.cpp; sourceTree = ""; }; + 55C5AC451390627C003B15C5 /* refmap.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = refmap.h; sourceTree = ""; }; + 55C5AC461390627C003B15C5 /* Replicator.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Replicator.cpp; sourceTree = ""; }; + 55C5AC471390627C003B15C5 /* res.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = res.h; sourceTree = ""; }; + 55C5AC481390627C003B15C5 /* Research.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Research.cpp; sourceTree = ""; }; + 55C5AC491390627C003B15C5 /* roomform.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = roomform.cpp; sourceTree = ""; }; + 55C5AC4A1390627C003B15C5 /* roomform.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = roomform.h; sourceTree = ""; }; + 55C5AC4D1390627C003B15C5 /* display.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = display.cpp; sourceTree = ""; }; + 55C5AC4E1390627C003B15C5 /* host.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = host.cpp; sourceTree = ""; }; + 55C5AC501390627C003B15C5 /* hosthelpers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = hosthelpers.h; sourceTree = ""; }; + 55C5AC511390627C003B15C5 /* htplatform.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = htplatform.h; sourceTree = ""; }; + 55C5AC651390627C003B15C5 /* main.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = ""; }; + 55C5AC691390627C003B15C5 /* common.mk */ = {isa = PBXFileReference; lastKnownFileType = text; path = common.mk; sourceTree = ""; }; + 55C5AC6A1390627C003B15C5 /* generate_nmf.py */ = {isa = PBXFileReference; lastKnownFileType = text.script.python; path = generate_nmf.py; sourceTree = ""; }; + 55C5AC6B1390627C003B15C5 /* Makefile */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = ""; }; + 55C5AC6C1390627C003B15C5 /* wi.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = wi.cc; sourceTree = ""; }; + 55C5AC6D1390627C003B15C5 /* wi.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = wi.html; sourceTree = ""; }; + 55C5AC6E1390627C003B15C5 /* wi.nmf */ = {isa = PBXFileReference; lastKnownFileType = text; path = wi.nmf; sourceTree = ""; }; + 55C5AC6F1390627C003B15C5 /* wi_dbg.nmf */ = {isa = PBXFileReference; lastKnownFileType = text; path = wi_dbg.nmf; sourceTree = ""; }; + 55C5AC701390627C003B15C5 /* wi_x86_32.nexe */ = {isa = PBXFileReference; lastKnownFileType = file; path = wi_x86_32.nexe; sourceTree = ""; }; + 55C5AC711390627C003B15C5 /* wi_x86_32.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = wi_x86_32.o; sourceTree = ""; }; + 55C5AC721390627C003B15C5 /* wi_x86_32_dbg.nexe */ = {isa = PBXFileReference; lastKnownFileType = file; path = wi_x86_32_dbg.nexe; sourceTree = ""; }; + 55C5AC731390627C003B15C5 /* wi_x86_32_dbg.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = wi_x86_32_dbg.o; sourceTree = ""; }; + 55C5AC741390627C003B15C5 /* wi_x86_64.nexe */ = {isa = PBXFileReference; lastKnownFileType = file; path = wi_x86_64.nexe; sourceTree = ""; }; + 55C5AC751390627C003B15C5 /* wi_x86_64.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = wi_x86_64.o; sourceTree = ""; }; + 55C5AC761390627C003B15C5 /* wi_x86_64_dbg.nexe */ = {isa = PBXFileReference; lastKnownFileType = file; path = wi_x86_64_dbg.nexe; sourceTree = ""; }; + 55C5AC771390627C003B15C5 /* wi_x86_64_dbg.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = wi_x86_64_dbg.o; sourceTree = ""; }; + 55C5AC791390627C003B15C5 /* savegame.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = savegame.cpp; sourceTree = ""; }; + 55C5AC7E1390627C003B15C5 /* sdlpackfile.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = sdlpackfile.cpp; sourceTree = ""; }; + 55C5AC7F1390627C003B15C5 /* sdlpackfile.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = sdlpackfile.h; sourceTree = ""; }; + 55C5ACBA1390627C003B15C5 /* selectmission.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = selectmission.cpp; sourceTree = ""; }; + 55C5ACBB1390627C003B15C5 /* serviceurls.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = serviceurls.cpp; sourceTree = ""; }; + 55C5ACBC1390627C003B15C5 /* serviceurls.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = serviceurls.h; sourceTree = ""; }; + 55C5ACBD1390627C003B15C5 /* Shell.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Shell.cpp; sourceTree = ""; }; + 55C5ACBE1390627C003B15C5 /* simplerequest.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = simplerequest.cpp; sourceTree = ""; }; + 55C5ACBF1390627C003B15C5 /* simplerequest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = simplerequest.h; sourceTree = ""; }; + 55C5ACC01390627C003B15C5 /* SimUI.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SimUI.cpp; sourceTree = ""; }; + 55C5ACC11390627C003B15C5 /* Simulation.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Simulation.cpp; sourceTree = ""; }; + 55C5ACC21390627C003B15C5 /* sizeops.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = sizeops.h; sourceTree = ""; }; + 55C5ACC41390627C003B15C5 /* smallest.json */ = {isa = PBXFileReference; lastKnownFileType = text; path = smallest.json; sourceTree = ""; }; + 55C5ACC51390627C003B15C5 /* SocTransport.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SocTransport.cpp; sourceTree = ""; }; + 55C5ACC61390627C003B15C5 /* SocTransport.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SocTransport.h; sourceTree = ""; }; + 55C5ACC71390627C003B15C5 /* sounddevice.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = sounddevice.h; sourceTree = ""; }; + 55C5ACC81390627C003B15C5 /* soundeffects.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = soundeffects.h; sourceTree = ""; }; + 55C5ACC91390627C003B15C5 /* soundmgr.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = soundmgr.cpp; sourceTree = ""; }; + 55C5ACCA1390627C003B15C5 /* SpInfantry.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SpInfantry.cpp; sourceTree = ""; }; + 55C5ACCB1390627C003B15C5 /* sprite.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = sprite.h; sourceTree = ""; }; + 55C5ACCC1390627C003B15C5 /* SRInfantry.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SRInfantry.cpp; sourceTree = ""; }; + 55C5ACCD1390627C003B15C5 /* stateframe.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = stateframe.cpp; sourceTree = ""; }; + 55C5ACCE1390627C003B15C5 /* stateframe.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = stateframe.h; sourceTree = ""; }; + 55C5ACCF1390627C003B15C5 /* StateMachine.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = StateMachine.cpp; sourceTree = ""; }; + 55C5ACD01390627C003B15C5 /* statetracker.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = statetracker.cpp; sourceTree = ""; }; + 55C5ACD11390627C003B15C5 /* statetracker.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = statetracker.h; sourceTree = ""; }; + 55C5ACD21390627C003B15C5 /* strings.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = strings.h; sourceTree = ""; }; + 55C5ACD31390627C003B15C5 /* stringtable.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = stringtable.cpp; sourceTree = ""; }; + 55C5ACD41390627C003B15C5 /* Struct.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Struct.cpp; sourceTree = ""; }; + 55C5ACD51390627C003B15C5 /* stylushandler.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = stylushandler.cpp; sourceTree = ""; }; + 55C5ACD61390627C003B15C5 /* Tank.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Tank.cpp; sourceTree = ""; }; + 55C5ACD71390627C003B15C5 /* tbitmap.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = tbitmap.cpp; sourceTree = ""; }; + 55C5ACD81390627C003B15C5 /* tbitmapsr.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = tbitmapsr.cpp; sourceTree = ""; }; + 55C5ACD91390627C003B15C5 /* terrainmap.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = terrainmap.cpp; sourceTree = ""; }; + 55C5ACDA1390627C003B15C5 /* tests.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = tests.cpp; sourceTree = ""; }; + 55C5ACDB1390627C003B15C5 /* thunks.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = thunks.cpp; sourceTree = ""; }; + 55C5ACDC1390627C003B15C5 /* tilemap.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = tilemap.cpp; sourceTree = ""; }; + 55C5ACDD1390627C003B15C5 /* timer.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = timer.cpp; sourceTree = ""; }; + 55C5ACDE1390627C003B15C5 /* timerwrap.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = timerwrap.h; sourceTree = ""; }; + 55C5ACDF1390627C003B15C5 /* todo.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = todo.txt; sourceTree = ""; }; + 55C5ACE01390627C003B15C5 /* Tower.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Tower.cpp; sourceTree = ""; }; + 55C5ACE11390627C003B15C5 /* TriggerActions.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = TriggerActions.cpp; sourceTree = ""; }; + 55C5ACE21390627C003B15C5 /* TriggerConditions.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = TriggerConditions.cpp; sourceTree = ""; }; + 55C5ACE31390627C003B15C5 /* triggermgr.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = triggermgr.cpp; sourceTree = ""; }; + 55C5ACE41390627C003B15C5 /* Unit.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Unit.cpp; sourceTree = ""; }; + 55C5ACE51390627C003B15C5 /* UnitGroupMgr.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = UnitGroupMgr.cpp; sourceTree = ""; }; + 55C5ACE61390627C003B15C5 /* updatemap.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = updatemap.cpp; sourceTree = ""; }; + 55C5ACE71390627C003B15C5 /* uploader.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = uploader.cpp; sourceTree = ""; }; + 55C5ACE81390627C003B15C5 /* uploader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = uploader.h; sourceTree = ""; }; + 55C5ACE91390627C003B15C5 /* vec2d.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = vec2d.h; sourceTree = ""; }; + 55C5ACEA1390627C003B15C5 /* VTS.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = VTS.cpp; sourceTree = ""; }; + 55C5ACEB1390627C003B15C5 /* Warehouse.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Warehouse.cpp; sourceTree = ""; }; + 55C5AD041390627C003B15C5 /* worklist.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = worklist.txt; sourceTree = ""; }; + 55D1DC591393FAE50035F179 /* sdlsounddev.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sdlsounddev.cpp; sourceTree = ""; }; + 55D1DC6413940F830035F179 /* icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon.png; sourceTree = ""; }; + 55D1DC66139414500035F179 /* wi.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = wi.icns; sourceTree = ""; }; + 55D631DE13919455002061DF /* hosthelpers.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = hosthelpers.mm; sourceTree = ""; }; + C11151EE13A5317B005A3273 /* sysmessages.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sysmessages.h; sourceTree = ""; }; + C1647924139C7C22007473A8 /* xtransport.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = xtransport.cpp; sourceTree = ""; }; + C1647926139C7CC0007473A8 /* xtransport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xtransport.h; sourceTree = ""; }; + C169F5AD139C972D00B4491D /* transportmgr.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = transportmgr.cpp; sourceTree = ""; }; + C169F5B013A43CD800B4491D /* sdleventserver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sdleventserver.h; sourceTree = ""; }; + C1C82A1B13A57D6C009071DC /* machttprequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = machttprequest.h; sourceTree = ""; }; + C1C82A1C13A57D6C009071DC /* machttprequest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = machttprequest.mm; sourceTree = ""; }; + C1C82A1D13A57D6C009071DC /* machttpservice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = machttpservice.h; sourceTree = ""; }; + C1C82A1E13A57D6C009071DC /* machttpservice.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = machttpservice.mm; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 55BAE1D0139074B40046D160 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 551C91EB1390BC570018ECBA /* SDL.framework in Frameworks */, + 55BAE1D8139074B40046D160 /* Cocoa.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 5501E61B139079710048AAFE /* mac */ = { + isa = PBXGroup; + children = ( + 5501E61C139079710048AAFE /* Credits.rtf */, + 55D1DC6413940F830035F179 /* icon.png */, + 5501E61E139079710048AAFE /* InfoPlist.strings */, + 55D631DE13919455002061DF /* hosthelpers.mm */, + C1C82A1C13A57D6C009071DC /* machttprequest.mm */, + C1C82A1B13A57D6C009071DC /* machttprequest.h */, + C1C82A1E13A57D6C009071DC /* machttpservice.mm */, + C1C82A1D13A57D6C009071DC /* machttpservice.h */, + 5501E620139079710048AAFE /* MainMenu.xib */, + 5501E622139079710048AAFE /* SDL.framework */, + 5501E623139079710048AAFE /* SDLMain.h */, + 5501E624139079710048AAFE /* SDLMain.m */, + 5501E625139079710048AAFE /* Info.plist */, + 55D1DC66139414500035F179 /* wi.icns */, + 5501E626139079710048AAFE /* Prefix.pch */, + ); + path = mac; + sourceTree = ""; + }; + 551C91431390A40F0018ECBA /* yajl */ = { + isa = PBXGroup; + children = ( + 551C91441390A40F0018ECBA /* api */, + 551C91491390A40F0018ECBA /* src */, + 551C91541390A40F0018ECBA /* wrapper */, + ); + name = yajl; + path = ../yajl; + sourceTree = ""; + }; + 551C91441390A40F0018ECBA /* api */ = { + isa = PBXGroup; + children = ( + 551C91451390A40F0018ECBA /* yajl_common.h */, + 551C91461390A40F0018ECBA /* yajl_gen.h */, + 551C91471390A40F0018ECBA /* yajl_parse.h */, + ); + path = api; + sourceTree = ""; + }; + 551C91491390A40F0018ECBA /* src */ = { + isa = PBXGroup; + children = ( + 551C914A1390A40F0018ECBA /* yajl.c */, + 551C914B1390A40F0018ECBA /* yajl_buf.c */, + 551C914C1390A40F0018ECBA /* yajl_buf.h */, + 551C914D1390A40F0018ECBA /* yajl_encode.c */, + 551C914E1390A40F0018ECBA /* yajl_encode.h */, + 551C91501390A40F0018ECBA /* yajl_lex.c */, + 551C91511390A40F0018ECBA /* yajl_lex.h */, + 551C91521390A40F0018ECBA /* yajl_parser.c */, + 551C91531390A40F0018ECBA /* yajl_parser.h */, + ); + path = src; + sourceTree = ""; + }; + 551C91541390A40F0018ECBA /* wrapper */ = { + isa = PBXGroup; + children = ( + 551C91571390A40F0018ECBA /* jsonbuilder.cpp */, + 551C91581390A40F0018ECBA /* jsonbuilder.h */, + 551C915A1390A40F0018ECBA /* jsontypes.cpp */, + 551C915B1390A40F0018ECBA /* jsontypes.h */, + ); + path = wrapper; + sourceTree = ""; + }; + 551C916E1390A42C0018ECBA /* base */ = { + isa = PBXGroup; + children = ( + 551C916F1390A42C0018ECBA /* base64.cpp */, + 551C91701390A42C0018ECBA /* base64.h */, + 551C91711390A42C0018ECBA /* bytebuffer.cpp */, + 551C91721390A42C0018ECBA /* bytebuffer.h */, + 551C91731390A42C0018ECBA /* criticalsection.h */, + 551C91741390A42C0018ECBA /* deletetracker.cpp */, + 551C91751390A42C0018ECBA /* deletetracker.h */, + 551C91761390A42C0018ECBA /* dispatcher.h */, + 551C91791390A42C0018ECBA /* eventer.cpp */, + 551C917A1390A42C0018ECBA /* eventer.h */, + 551C917B1390A42C0018ECBA /* format.cpp */, + 551C917C1390A42C0018ECBA /* format.h */, + 551C917D1390A42C0018ECBA /* log.h */, + 551C917E1390A42C0018ECBA /* md5.h */, + 551C917F1390A42C0018ECBA /* md5c.cpp */, + 551C91801390A42C0018ECBA /* messagehandler.cpp */, + 551C91811390A42C0018ECBA /* messagehandler.h */, + 551C91821390A42C0018ECBA /* messagequeue.cpp */, + 551C91831390A42C0018ECBA /* messagequeue.h */, + 551C91841390A42C0018ECBA /* misc.cpp */, + 551C91851390A42C0018ECBA /* misc.h */, + 551C91861390A42C0018ECBA /* selectserver.cpp */, + 551C91871390A42C0018ECBA /* selectserver.h */, + 551C91881390A42C0018ECBA /* sigslot.h */, + 551C91891390A42C0018ECBA /* socket.cpp */, + 551C918A1390A42C0018ECBA /* socket.h */, + 551C918B1390A42C0018ECBA /* socketaddress.cpp */, + 551C918C1390A42C0018ECBA /* socketaddress.h */, + 551C918D1390A42C0018ECBA /* socketserver.cpp */, + 551C918E1390A42C0018ECBA /* socketserver.h */, + 551C91911390A42C0018ECBA /* thread.cpp */, + 551C91921390A42C0018ECBA /* thread.h */, + 551C91931390A42C0018ECBA /* tick.cpp */, + 551C91941390A42C0018ECBA /* tick.h */, + ); + name = base; + path = ../base; + sourceTree = ""; + }; + 551C91A61390A43E0018ECBA /* mpshared */ = { + isa = PBXGroup; + children = ( + 551C91A71390A43E0018ECBA /* constants.h */, + 551C91A81390A43E0018ECBA /* decompress.cpp */, + 551C91A91390A43E0018ECBA /* decompress.h */, + 551C91AA1390A43E0018ECBA /* enum.h */, + 551C91AB1390A43E0018ECBA /* indexloader.cpp */, + 551C91AC1390A43E0018ECBA /* indexloader.h */, + 551C91AD1390A43E0018ECBA /* ini.cpp */, + 551C91AE1390A43E0018ECBA /* ini.h */, + 551C91AF1390A43E0018ECBA /* messages.cpp */, + 551C91B01390A43E0018ECBA /* messages.h */, + 551C91B11390A43E0018ECBA /* misc.cpp */, + 551C91B21390A43E0018ECBA /* misc.h */, + 551C91B31390A43E0018ECBA /* mp.txt */, + 551C91B41390A43E0018ECBA /* mpht.h */, + 551C91B51390A43E0018ECBA /* netmessage.cpp */, + 551C91B61390A43E0018ECBA /* netmessage.h */, + 551C91B71390A43E0018ECBA /* packfile.cpp */, + 551C91B81390A43E0018ECBA /* packfile.h */, + 551C91B91390A43E0018ECBA /* packinfomanager.cpp */, + 551C91BA1390A43E0018ECBA /* packinfomanager.h */, + 551C91BB1390A43E0018ECBA /* packmanager.cpp */, + 551C91BC1390A43E0018ECBA /* packmanager.h */, + 551C91BD1390A43E0018ECBA /* pdbreader.h */, + 551C91BE1390A43E0018ECBA /* side.h */, + 551C91BF1390A43E0018ECBA /* xmsg.h */, + 551C91C01390A43E0018ECBA /* xmsglog.cpp */, + 551C91C11390A43E0018ECBA /* xmsglog.h */, + 551C91C21390A43E0018ECBA /* xpump.cpp */, + 551C91C31390A43E0018ECBA /* xpump.h */, + ); + name = mpshared; + path = ../mpshared; + sourceTree = ""; + }; + 55BAE1D4139074B40046D160 /* Products */ = { + isa = PBXGroup; + children = ( + 55BAE1D3139074B40046D160 /* Warfare Incorporated.app */, + ); + name = Products; + sourceTree = ""; + }; + 55BAE1D6139074B40046D160 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 551C91EA1390BC570018ECBA /* SDL.framework */, + 55BAE1D7139074B40046D160 /* Cocoa.framework */, + 55BAE1D9139074B40046D160 /* Other Frameworks */, + ); + name = Frameworks; + sourceTree = ""; + }; + 55BAE1D9139074B40046D160 /* Other Frameworks */ = { + isa = PBXGroup; + children = ( + 55BAE1DA139074B40046D160 /* AppKit.framework */, + 55BAE1DB139074B40046D160 /* CoreData.framework */, + 55BAE1DC139074B40046D160 /* Foundation.framework */, + ); + name = "Other Frameworks"; + sourceTree = ""; + }; + 55C5AA4913906055003B15C5 = { + isa = PBXGroup; + children = ( + 551C91D01390A90D0018ECBA /* rip.cpp */, + 551C91A61390A43E0018ECBA /* mpshared */, + 551C916E1390A42C0018ECBA /* base */, + 551C91431390A40F0018ECBA /* yajl */, + 55C5AA6B1390627C003B15C5 /* game */, + 55BAE1D6139074B40046D160 /* Frameworks */, + 55BAE1D4139074B40046D160 /* Products */, + ); + sourceTree = ""; + }; + 55C5AA6B1390627C003B15C5 /* game */ = { + isa = PBXGroup; + children = ( + 55C5AA6E1390627C003B15C5 /* alertcontrol.cpp */, + 55C5AA6F1390627C003B15C5 /* Andy.cpp */, + 55C5AA701390627C003B15C5 /* Animation.cpp */, + 55C5AA711390627C003B15C5 /* Artillery.cpp */, + 55C5AA721390627C003B15C5 /* bitmap.cpp */, + 55C5AA741390627C003B15C5 /* Builder.cpp */, + 55C5AA751390627C003B15C5 /* BuildMgr.cpp */, + 55C5AA761390627C003B15C5 /* cachemgr.cpp */, + 55C5AA781390627C003B15C5 /* chatcontroller.h */, + 55C5AA791390627C003B15C5 /* chatter.cpp */, + 55C5AA7A1390627C003B15C5 /* chatter.h */, + 55C5AA7B1390627C003B15C5 /* chooseserverform.cpp */, + 55C5AA7C1390627C003B15C5 /* chooseserverform.h */, + 55C5AA7D1390627C003B15C5 /* comm.cpp */, + 55C5AA7E1390627C003B15C5 /* completemanager.cpp */, + 55C5AA7F1390627C003B15C5 /* completemanager.h */, + 55C5AA801390627C003B15C5 /* compression.cpp */, + 55C5AA811390627C003B15C5 /* creategameform.cpp */, + 55C5AA821390627C003B15C5 /* creategameform.h */, + 55C5AA831390627C003B15C5 /* createroomform.cpp */, + 55C5AA841390627C003B15C5 /* createroomform.h */, + 55C5AA851390627C003B15C5 /* CutScene.cpp */, + 55C5AA861390627C003B15C5 /* DebugHelpers.cpp */, + 55C5AA871390627C003B15C5 /* dist.plist */, + 55C5AA881390627C003B15C5 /* dlmissionpack.cpp */, + 55C5AA891390627C003B15C5 /* downloadbox.cpp */, + 55C5AA8A1390627C003B15C5 /* dragrect.cpp */, + 55C5AA8B1390627C003B15C5 /* dragrect.h */, + 55C5AA8C1390627C003B15C5 /* drawscan.cpp */, + 55C5AA8D1390627C003B15C5 /* drm.cpp */, + 55C5AA8E1390627C003B15C5 /* Ecom.cpp */, + 55C5AA8F1390627C003B15C5 /* Entitlements.plist */, + 55C5AA901390627C003B15C5 /* event.cpp */, + 55C5AA911390627C003B15C5 /* example.json */, + 55C5AA921390627C003B15C5 /* filepdbreader.cpp */, + 55C5AA931390627C003B15C5 /* fingerhandler.cpp */, + 55C5AA941390627C003B15C5 /* flickscroller.cpp */, + 55C5AA951390627C003B15C5 /* fogmap.cpp */, + 55C5AA961390627C003B15C5 /* font.cpp */, + 55C5AA971390627C003B15C5 /* form.cpp */, + 55C5AA981390627C003B15C5 /* formmgr.cpp */, + 55C5AA991390627C003B15C5 /* game.cpp */, + 55C5AA9A1390627C003B15C5 /* gameform.cpp */, + 55C5AA9B1390627C003B15C5 /* gameform.h */, + 55C5AA9C1390627C003B15C5 /* GameObjects.cpp */, + 55C5AA9D1390627C003B15C5 /* GameOptions.cpp */, + 55C5AA9E1390627C003B15C5 /* hashtablecode.cpp */, + 55C5AA9F1390627C003B15C5 /* Headquarters.cpp */, + 55C5AAA01390627C003B15C5 /* Help.cpp */, + 55C5AAA11390627C003B15C5 /* HRC.cpp */, + 55C5AAA21390627C003B15C5 /* ht.h */, + 55C5AAA41390627C003B15C5 /* htdata832.pdb */, + 55C5AAA61390627C003B15C5 /* htsfx.pdb */, + 55C5AAA71390627C003B15C5 /* httpindexloader.cpp */, + 55C5AAA81390627C003B15C5 /* httpindexloader.h */, + 55C5AAA91390627C003B15C5 /* httppackinfomanager.cpp */, + 55C5AAAA1390627C003B15C5 /* httppackinfomanager.h */, + 55C5AAAB1390627C003B15C5 /* httppackmanager.cpp */, + 55C5AAAC1390627C003B15C5 /* httppackmanager.h */, + 55C5AAAD1390627C003B15C5 /* httprequest.cpp */, + 55C5AAAE1390627C003B15C5 /* httprequest.h */, + 55C5AAAF1390627C003B15C5 /* httpservice.h */, + 55C5AAB01390627C003B15C5 /* index.json */, + 55C5AAB11390627C003B15C5 /* InputUI.cpp */, + 55C5AAF31390627C003B15C5 /* Level.cpp */, + 55C5AAF51390627C003B15C5 /* license.h */, + 55C5AAF61390627C003B15C5 /* loadsave.cpp */, + 55C5AAF71390627C003B15C5 /* lobby.cpp */, + 55C5AAF81390627C003B15C5 /* lobby.h */, + 55C5AAF91390627C003B15C5 /* lobbyform.cpp */, + 55C5AAFA1390627C003B15C5 /* lobbyform.h */, + 55C5AAFB1390627C003B15C5 /* loginform.cpp */, + 55C5AAFC1390627C003B15C5 /* loginform.h */, + 55C5AAFD1390627C003B15C5 /* loginhandler.cpp */, + 55C5AAFE1390627C003B15C5 /* loginhandler.h */, + 55C5AAFF1390627C003B15C5 /* LRInfantry.cpp */, + 55C5AC201390627C003B15C5 /* map.cpp */, + 55C5AC211390627C003B15C5 /* map.h */, + 55C5AC221390627C003B15C5 /* memmgr.cpp */, + 55C5AC231390627C003B15C5 /* mempdbreader.cpp */, + 55C5AC241390627C003B15C5 /* mempdbreader.h */, + 55C5AC251390627C003B15C5 /* Miner.cpp */, + 55C5AC261390627C003B15C5 /* misc.cpp */, + 55C5AC271390627C003B15C5 /* misccontrols.cpp */, + 55C5AC281390627C003B15C5 /* miscgraphics.cpp */, + 55C5AC291390627C003B15C5 /* missionlist.cpp */, + 55C5AC2A1390627C003B15C5 /* missionlist.h */, + 55C5AC2B1390627C003B15C5 /* mixer.cpp */, + 55C5AC2C1390627C003B15C5 /* mixer.h */, + 55C5AC2E1390627C003B15C5 /* MobileBuilder.cpp */, + 55C5AC2F1390627C003B15C5 /* MobileHQ.cpp */, + 55C5AC301390627C003B15C5 /* MobileUnit.cpp */, + 55C5AC311390627C003B15C5 /* mp_test.cpp */, + 55C5AC321390627C003B15C5 /* Multiplayer.cpp */, + 55C5AC331390627C003B15C5 /* multiplayer.h */, + 55C5AC341390627C003B15C5 /* ops.h */, + 55C5AC351390627C003B15C5 /* Overmind.cpp */, + 55C5AC371390627C003B15C5 /* picktransportform.cpp */, + 55C5AC381390627C003B15C5 /* picktransportform.h */, + 55C5AC391390627C003B15C5 /* Player.cpp */, + 55C5AC401390627C003B15C5 /* Processor.cpp */, + 55C5AC411390627C003B15C5 /* progresscallback.h */, + 55C5AC421390627C003B15C5 /* Radar.cpp */, + 55C5AC431390627C003B15C5 /* RawBitmap.cpp */, + 55C5AC441390627C003B15C5 /* Reactor.cpp */, + 55C5AC451390627C003B15C5 /* refmap.h */, + 55C5AC461390627C003B15C5 /* Replicator.cpp */, + 55C5AC471390627C003B15C5 /* res.h */, + 55C5AC481390627C003B15C5 /* Research.cpp */, + 55C5AC491390627C003B15C5 /* roomform.cpp */, + 55C5AC4A1390627C003B15C5 /* roomform.h */, + 55C5AC4B1390627C003B15C5 /* sdl */, + 55C5ACBA1390627C003B15C5 /* selectmission.cpp */, + 55C5ACBB1390627C003B15C5 /* serviceurls.cpp */, + 55C5ACBC1390627C003B15C5 /* serviceurls.h */, + 55C5ACBD1390627C003B15C5 /* Shell.cpp */, + 55C5ACBE1390627C003B15C5 /* simplerequest.cpp */, + 55C5ACBF1390627C003B15C5 /* simplerequest.h */, + 55C5ACC01390627C003B15C5 /* SimUI.cpp */, + 55C5ACC11390627C003B15C5 /* Simulation.cpp */, + 55C5ACC21390627C003B15C5 /* sizeops.h */, + 55C5ACC41390627C003B15C5 /* smallest.json */, + 55C5ACC51390627C003B15C5 /* SocTransport.cpp */, + 55C5ACC61390627C003B15C5 /* SocTransport.h */, + 55C5ACC71390627C003B15C5 /* sounddevice.h */, + 55C5ACC81390627C003B15C5 /* soundeffects.h */, + 55C5ACC91390627C003B15C5 /* soundmgr.cpp */, + 55C5ACCA1390627C003B15C5 /* SpInfantry.cpp */, + 55C5ACCB1390627C003B15C5 /* sprite.h */, + 55C5ACCC1390627C003B15C5 /* SRInfantry.cpp */, + 55C5ACCD1390627C003B15C5 /* stateframe.cpp */, + 55C5ACCE1390627C003B15C5 /* stateframe.h */, + 55C5ACCF1390627C003B15C5 /* StateMachine.cpp */, + 55C5ACD01390627C003B15C5 /* statetracker.cpp */, + 55C5ACD11390627C003B15C5 /* statetracker.h */, + 55C5ACD21390627C003B15C5 /* strings.h */, + 55C5ACD31390627C003B15C5 /* stringtable.cpp */, + 55C5ACD41390627C003B15C5 /* Struct.cpp */, + 55C5ACD51390627C003B15C5 /* stylushandler.cpp */, + 55C5ACD61390627C003B15C5 /* Tank.cpp */, + 55C5ACD71390627C003B15C5 /* tbitmap.cpp */, + 55C5ACD81390627C003B15C5 /* tbitmapsr.cpp */, + 55C5ACD91390627C003B15C5 /* terrainmap.cpp */, + 55C5ACDA1390627C003B15C5 /* tests.cpp */, + 55C5ACDB1390627C003B15C5 /* thunks.cpp */, + 55C5ACDC1390627C003B15C5 /* tilemap.cpp */, + 55C5ACDD1390627C003B15C5 /* timer.cpp */, + 55C5ACDE1390627C003B15C5 /* timerwrap.h */, + 55C5ACDF1390627C003B15C5 /* todo.txt */, + 55C5ACE01390627C003B15C5 /* Tower.cpp */, + 55C5ACE11390627C003B15C5 /* TriggerActions.cpp */, + 55C5ACE21390627C003B15C5 /* TriggerConditions.cpp */, + 55C5ACE31390627C003B15C5 /* triggermgr.cpp */, + 55C5ACE41390627C003B15C5 /* Unit.cpp */, + 55C5ACE51390627C003B15C5 /* UnitGroupMgr.cpp */, + 55C5ACE61390627C003B15C5 /* updatemap.cpp */, + 55C5ACE71390627C003B15C5 /* uploader.cpp */, + 55C5ACE81390627C003B15C5 /* uploader.h */, + 55C5ACE91390627C003B15C5 /* vec2d.h */, + 55C5ACEA1390627C003B15C5 /* VTS.cpp */, + 55C5ACEB1390627C003B15C5 /* Warehouse.cpp */, + 55C5AD041390627C003B15C5 /* worklist.txt */, + C1647924139C7C22007473A8 /* xtransport.cpp */, + C1647926139C7CC0007473A8 /* xtransport.h */, + ); + name = game; + sourceTree = ""; + }; + 55C5AC4B1390627C003B15C5 /* sdl */ = { + isa = PBXGroup; + children = ( + 5501E61B139079710048AAFE /* mac */, + 55C5AC4C1390627C003B15C5 /* android */, + 55C5AC681390627C003B15C5 /* nacl */, + 55C5AC801390627C003B15C5 /* win */, + 55C5AC4D1390627C003B15C5 /* display.cpp */, + 55C5AC4E1390627C003B15C5 /* host.cpp */, + 55C5AC501390627C003B15C5 /* hosthelpers.h */, + 55C5AC511390627C003B15C5 /* htplatform.h */, + 55C5AC651390627C003B15C5 /* main.cpp */, + 55C5AC791390627C003B15C5 /* savegame.cpp */, + C169F5B013A43CD800B4491D /* sdleventserver.h */, + 55C5AC7E1390627C003B15C5 /* sdlpackfile.cpp */, + 55C5AC7F1390627C003B15C5 /* sdlpackfile.h */, + 5595A96E1392DCCD00988594 /* sdlselectionsprite.cpp */, + 5595A96F1392DCCE00988594 /* sdlselectionsprite.h */, + 5595A96C1392DA6900988594 /* sdlspritemgr.h */, + 55D1DC591393FAE50035F179 /* sdlsounddev.cpp */, + C11151EE13A5317B005A3273 /* sysmessages.h */, + C169F5AD139C972D00B4491D /* transportmgr.cpp */, + ); + path = sdl; + sourceTree = ""; + }; + 55C5AC4C1390627C003B15C5 /* android */ = { + isa = PBXGroup; + children = ( + ); + path = android; + sourceTree = ""; + }; + 55C5AC681390627C003B15C5 /* nacl */ = { + isa = PBXGroup; + children = ( + 55C5AC691390627C003B15C5 /* common.mk */, + 55C5AC6A1390627C003B15C5 /* generate_nmf.py */, + 55C5AC6B1390627C003B15C5 /* Makefile */, + 55C5AC6C1390627C003B15C5 /* wi.cc */, + 55C5AC6D1390627C003B15C5 /* wi.html */, + 55C5AC6E1390627C003B15C5 /* wi.nmf */, + 55C5AC6F1390627C003B15C5 /* wi_dbg.nmf */, + 55C5AC701390627C003B15C5 /* wi_x86_32.nexe */, + 55C5AC711390627C003B15C5 /* wi_x86_32.o */, + 55C5AC721390627C003B15C5 /* wi_x86_32_dbg.nexe */, + 55C5AC731390627C003B15C5 /* wi_x86_32_dbg.o */, + 55C5AC741390627C003B15C5 /* wi_x86_64.nexe */, + 55C5AC751390627C003B15C5 /* wi_x86_64.o */, + 55C5AC761390627C003B15C5 /* wi_x86_64_dbg.nexe */, + 55C5AC771390627C003B15C5 /* wi_x86_64_dbg.o */, + ); + path = nacl; + sourceTree = ""; + }; + 55C5AC801390627C003B15C5 /* win */ = { + isa = PBXGroup; + children = ( + ); + path = win; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 55BAE1D2139074B40046D160 /* Warfare Incorporated */ = { + isa = PBXNativeTarget; + buildConfigurationList = 55BAE1F1139074B40046D160 /* Build configuration list for PBXNativeTarget "Warfare Incorporated" */; + buildPhases = ( + 55BAE1CF139074B40046D160 /* Sources */, + 55BAE1D0139074B40046D160 /* Frameworks */, + 55BAE1D1139074B40046D160 /* Resources */, + 5501E6191390777C0048AAFE /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Warfare Incorporated"; + productName = "Warfare Incorporated"; + productReference = 55BAE1D3139074B40046D160 /* Warfare Incorporated.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 55C5AA4B13906055003B15C5 /* Project object */ = { + isa = PBXProject; + buildConfigurationList = 55C5AA4E13906055003B15C5 /* Build configuration list for PBXProject "wisdl" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 55C5AA4913906055003B15C5; + productRefGroup = 55BAE1D4139074B40046D160 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 55BAE1D2139074B40046D160 /* Warfare Incorporated */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 55BAE1D1139074B40046D160 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 55D631DA13918313002061DF /* htdata832.pdb in Resources */, + 55D631DB13918313002061DF /* htsfx.pdb in Resources */, + 5501E62E139079710048AAFE /* Credits.rtf in Resources */, + 5501E62F139079710048AAFE /* InfoPlist.strings in Resources */, + 5501E630139079710048AAFE /* MainMenu.xib in Resources */, + 55D1DC6513940F830035F179 /* icon.png in Resources */, + 55D1DC67139414500035F179 /* wi.icns in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 55BAE1CF139074B40046D160 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 551C91D41390AA6F0018ECBA /* thunks.cpp in Sources */, + 551C91D31390AA050018ECBA /* selectmission.cpp in Sources */, + 551C91D21390A9960018ECBA /* stringtable.cpp in Sources */, + 551C91D11390A90D0018ECBA /* rip.cpp in Sources */, + 551C913F1390A3930018ECBA /* savegame.cpp in Sources */, + 551C91421390A3940018ECBA /* sdlpackfile.cpp in Sources */, + 551C911B1390A2F00018ECBA /* chatter.cpp in Sources */, + 551C911C1390A2F00018ECBA /* chooseserverform.cpp in Sources */, + 551C911D1390A2F00018ECBA /* comm.cpp in Sources */, + 551C911E1390A2F00018ECBA /* completemanager.cpp in Sources */, + 551C911F1390A2F00018ECBA /* creategameform.cpp in Sources */, + 551C91201390A2F00018ECBA /* createroomform.cpp in Sources */, + 551C91211390A2F00018ECBA /* dlmissionpack.cpp in Sources */, + 551C91221390A2F00018ECBA /* downloadbox.cpp in Sources */, + 551C91231390A2F00018ECBA /* dragrect.cpp in Sources */, + 551C91241390A2F00018ECBA /* drawscan.cpp in Sources */, + 551C91251390A2F00018ECBA /* fingerhandler.cpp in Sources */, + 551C91261390A2F00018ECBA /* flickscroller.cpp in Sources */, + 551C91271390A2F00018ECBA /* gameform.cpp in Sources */, + 551C91281390A2F00018ECBA /* httpindexloader.cpp in Sources */, + 551C91291390A2F00018ECBA /* httppackinfomanager.cpp in Sources */, + 551C912A1390A2F00018ECBA /* httppackmanager.cpp in Sources */, + 551C912B1390A2F00018ECBA /* httprequest.cpp in Sources */, + 551C912C1390A2F00018ECBA /* lobby.cpp in Sources */, + 551C912D1390A2F00018ECBA /* lobbyform.cpp in Sources */, + 551C912E1390A2F00018ECBA /* loginform.cpp in Sources */, + 551C912F1390A2F00018ECBA /* loginhandler.cpp in Sources */, + 551C91301390A2F00018ECBA /* map.cpp in Sources */, + 551C91311390A2F10018ECBA /* mempdbreader.cpp in Sources */, + 551C91321390A2F10018ECBA /* missionlist.cpp in Sources */, + 551C91331390A2F10018ECBA /* Multiplayer.cpp in Sources */, + 551C91341390A2F10018ECBA /* picktransportform.cpp in Sources */, + 551C91351390A2F10018ECBA /* roomform.cpp in Sources */, + 551C91361390A2F10018ECBA /* display.cpp in Sources */, + 551C91371390A2F10018ECBA /* host.cpp in Sources */, + 551C91391390A2F10018ECBA /* serviceurls.cpp in Sources */, + 551C913A1390A2F10018ECBA /* simplerequest.cpp in Sources */, + 551C913B1390A2F10018ECBA /* stateframe.cpp in Sources */, + 551C913C1390A2F10018ECBA /* statetracker.cpp in Sources */, + 551C913D1390A2F10018ECBA /* stylushandler.cpp in Sources */, + 551C913E1390A2F10018ECBA /* uploader.cpp in Sources */, + 551C90DA1390A1860018ECBA /* alertcontrol.cpp in Sources */, + 551C90DB1390A1860018ECBA /* Andy.cpp in Sources */, + 551C90DC1390A1860018ECBA /* Animation.cpp in Sources */, + 551C90DD1390A1860018ECBA /* Artillery.cpp in Sources */, + 551C90DE1390A1860018ECBA /* bitmap.cpp in Sources */, + 551C90DF1390A1860018ECBA /* Builder.cpp in Sources */, + 551C90E01390A1860018ECBA /* BuildMgr.cpp in Sources */, + 551C90E11390A1860018ECBA /* cachemgr.cpp in Sources */, + 551C90E21390A1860018ECBA /* compression.cpp in Sources */, + 551C90E31390A1860018ECBA /* CutScene.cpp in Sources */, + 551C90E41390A1860018ECBA /* drm.cpp in Sources */, + 551C90E51390A1860018ECBA /* Ecom.cpp in Sources */, + 551C90E61390A1860018ECBA /* event.cpp in Sources */, + 551C90E71390A1860018ECBA /* fogmap.cpp in Sources */, + 551C90E81390A1860018ECBA /* font.cpp in Sources */, + 551C90E91390A1860018ECBA /* form.cpp in Sources */, + 551C90EA1390A1860018ECBA /* formmgr.cpp in Sources */, + 551C90EB1390A1860018ECBA /* game.cpp in Sources */, + 551C90EC1390A1860018ECBA /* GameObjects.cpp in Sources */, + 551C90ED1390A1860018ECBA /* GameOptions.cpp in Sources */, + 551C90EE1390A1860018ECBA /* Headquarters.cpp in Sources */, + 551C90EF1390A1860018ECBA /* Help.cpp in Sources */, + 551C90F01390A1860018ECBA /* HRC.cpp in Sources */, + 551C90F11390A1860018ECBA /* InputUI.cpp in Sources */, + 551C90F21390A1860018ECBA /* Level.cpp in Sources */, + 551C90F31390A1860018ECBA /* loadsave.cpp in Sources */, + 551C90F41390A1860018ECBA /* LRInfantry.cpp in Sources */, + 551C90F51390A1860018ECBA /* memmgr.cpp in Sources */, + 551C90F61390A1860018ECBA /* Miner.cpp in Sources */, + 551C90F71390A1860018ECBA /* misc.cpp in Sources */, + 551C90F81390A1860018ECBA /* misccontrols.cpp in Sources */, + 551C90F91390A1860018ECBA /* mixer.cpp in Sources */, + 551C90FA1390A1860018ECBA /* MobileBuilder.cpp in Sources */, + 551C90FB1390A1860018ECBA /* MobileHQ.cpp in Sources */, + 551C90FC1390A1860018ECBA /* MobileUnit.cpp in Sources */, + 551C90FD1390A1860018ECBA /* Overmind.cpp in Sources */, + 551C90FE1390A1860018ECBA /* Player.cpp in Sources */, + 551C90FF1390A1860018ECBA /* Processor.cpp in Sources */, + 551C91001390A1860018ECBA /* Radar.cpp in Sources */, + 551C91011390A1860018ECBA /* RawBitmap.cpp in Sources */, + 551C91021390A1860018ECBA /* Reactor.cpp in Sources */, + 551C91031390A1860018ECBA /* Replicator.cpp in Sources */, + 551C91041390A1860018ECBA /* Research.cpp in Sources */, + 551C91051390A1860018ECBA /* Shell.cpp in Sources */, + 551C91061390A1860018ECBA /* SimUI.cpp in Sources */, + 551C91071390A1860018ECBA /* Simulation.cpp in Sources */, + 551C91081390A1860018ECBA /* soundmgr.cpp in Sources */, + 551C91091390A1860018ECBA /* SpInfantry.cpp in Sources */, + 551C910A1390A1860018ECBA /* SRInfantry.cpp in Sources */, + 551C910B1390A1860018ECBA /* StateMachine.cpp in Sources */, + 551C910C1390A1860018ECBA /* Struct.cpp in Sources */, + 551C910D1390A1860018ECBA /* Tank.cpp in Sources */, + 551C910E1390A1860018ECBA /* tbitmap.cpp in Sources */, + 551C910F1390A1860018ECBA /* terrainmap.cpp in Sources */, + 551C91101390A1860018ECBA /* tilemap.cpp in Sources */, + 551C91111390A1860018ECBA /* timer.cpp in Sources */, + 551C91121390A1860018ECBA /* Tower.cpp in Sources */, + 551C91131390A1860018ECBA /* TriggerActions.cpp in Sources */, + 551C91141390A1860018ECBA /* TriggerConditions.cpp in Sources */, + 551C91151390A1860018ECBA /* triggermgr.cpp in Sources */, + 551C91161390A1860018ECBA /* Unit.cpp in Sources */, + 551C91171390A1860018ECBA /* UnitGroupMgr.cpp in Sources */, + 551C91181390A1860018ECBA /* updatemap.cpp in Sources */, + 551C91191390A1860018ECBA /* VTS.cpp in Sources */, + 551C911A1390A1860018ECBA /* Warehouse.cpp in Sources */, + 5501E616139076E20048AAFE /* main.cpp in Sources */, + 5501E632139079710048AAFE /* SDLMain.m in Sources */, + 551C91601390A40F0018ECBA /* yajl.c in Sources */, + 551C91611390A40F0018ECBA /* yajl_buf.c in Sources */, + 551C91621390A40F0018ECBA /* yajl_encode.c in Sources */, + 551C91641390A40F0018ECBA /* yajl_lex.c in Sources */, + 551C91651390A40F0018ECBA /* yajl_parser.c in Sources */, + 551C91681390A40F0018ECBA /* jsonbuilder.cpp in Sources */, + 551C916A1390A40F0018ECBA /* jsontypes.cpp in Sources */, + 551C91951390A42C0018ECBA /* base64.cpp in Sources */, + 551C91961390A42C0018ECBA /* bytebuffer.cpp in Sources */, + 551C91971390A42C0018ECBA /* deletetracker.cpp in Sources */, + 551C91991390A42C0018ECBA /* eventer.cpp in Sources */, + 551C919A1390A42C0018ECBA /* format.cpp in Sources */, + 551C919B1390A42C0018ECBA /* md5c.cpp in Sources */, + 551C919C1390A42C0018ECBA /* messagehandler.cpp in Sources */, + 551C919D1390A42C0018ECBA /* messagequeue.cpp in Sources */, + 551C919E1390A42C0018ECBA /* misc.cpp in Sources */, + 551C919F1390A42D0018ECBA /* selectserver.cpp in Sources */, + 551C91A01390A42D0018ECBA /* socket.cpp in Sources */, + 551C91A11390A42D0018ECBA /* socketaddress.cpp in Sources */, + 551C91A21390A42D0018ECBA /* socketserver.cpp in Sources */, + 551C91A41390A42D0018ECBA /* thread.cpp in Sources */, + 551C91A51390A42D0018ECBA /* tick.cpp in Sources */, + 551C91C41390A43E0018ECBA /* decompress.cpp in Sources */, + 551C91C51390A43E0018ECBA /* indexloader.cpp in Sources */, + 551C91C61390A43E0018ECBA /* ini.cpp in Sources */, + 551C91C71390A43E0018ECBA /* messages.cpp in Sources */, + 551C91C81390A43F0018ECBA /* misc.cpp in Sources */, + 551C91CA1390A43F0018ECBA /* netmessage.cpp in Sources */, + 551C91CB1390A43F0018ECBA /* packfile.cpp in Sources */, + 551C91CC1390A43F0018ECBA /* packinfomanager.cpp in Sources */, + 551C91CD1390A43F0018ECBA /* packmanager.cpp in Sources */, + 551C91CE1390A43F0018ECBA /* xmsglog.cpp in Sources */, + 551C91CF1390A43F0018ECBA /* xpump.cpp in Sources */, + 55D631DF13919455002061DF /* hosthelpers.mm in Sources */, + 5595A9701392DCCE00988594 /* sdlselectionsprite.cpp in Sources */, + 55D1DC5A1393FAE50035F179 /* sdlsounddev.cpp in Sources */, + C1647925139C7C22007473A8 /* xtransport.cpp in Sources */, + C169F5AE139C972D00B4491D /* transportmgr.cpp in Sources */, + C1C82A1F13A57D6C009071DC /* machttprequest.mm in Sources */, + C1C82A2013A57D6C009071DC /* machttpservice.mm in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 5501E61C139079710048AAFE /* Credits.rtf */ = { + isa = PBXVariantGroup; + children = ( + 5501E61D139079710048AAFE /* en */, + ); + name = Credits.rtf; + sourceTree = ""; + }; + 5501E61E139079710048AAFE /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 5501E61F139079710048AAFE /* en */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; + 5501E620139079710048AAFE /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 5501E621139079710048AAFE /* en */, + ); + name = MainMenu.xib; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 55BAE1EF139074B40046D160 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = YES; + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + COPY_PHASE_STRIP = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/sdl/mac\"", + ); + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = sdl/mac/Prefix.pch; + GCC_PREPROCESSOR_DEFINITIONS = DEBUG; + GCC_REUSE_STRINGS = YES; + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_VERSION = com.apple.compilers.llvmgcc42; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_FILE = sdl/mac/Info.plist; + MACOSX_DEPLOYMENT_TARGET = 10.6; + ONLY_ACTIVE_ARCH = YES; + OTHER_CFLAGS = ""; + OTHER_CPLUSPLUSFLAGS = ( + "-Wno-write-strings", + "-DSDL", + "-DMULTIPLAYER", + "-DTRACKSTATE", + "-DDEV_BUILD", + "-DDEBUG_LOGGING", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + USER_HEADER_SEARCH_PATHS = ".. ../inc sdl ../SDL/include"; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + 55BAE1F0139074B40046D160 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = YES; + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/sdl/mac\"", + ); + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = sdl/mac/Prefix.pch; + GCC_REUSE_STRINGS = YES; + GCC_VERSION = com.apple.compilers.llvmgcc42; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_FILE = sdl/mac/Info.plist; + MACOSX_DEPLOYMENT_TARGET = 10.6; + ONLY_ACTIVE_ARCH = NO; + OTHER_CFLAGS = ""; + OTHER_CPLUSPLUSFLAGS = ( + "-Wno-write-strings", + "-fshort-double", + "-DSDL", + "-DMULTIPLAYER", + "-DTRACKSTATE", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + USER_HEADER_SEARCH_PATHS = ".. ../inc sdl ../SDL/include"; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; + 55C5AA5013906055003B15C5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + }; + name = Debug; + }; + 55C5AA5113906055003B15C5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 55BAE1F1139074B40046D160 /* Build configuration list for PBXNativeTarget "Warfare Incorporated" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 55BAE1EF139074B40046D160 /* Debug */, + 55BAE1F0139074B40046D160 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 55C5AA4E13906055003B15C5 /* Build configuration list for PBXProject "wisdl" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 55C5AA5013906055003B15C5 /* Debug */, + 55C5AA5113906055003B15C5 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 55C5AA4B13906055003B15C5 /* Project object */; +} diff --git a/game/wisdl.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/game/wisdl.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..83c7181 --- /dev/null +++ b/game/wisdl.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/game/worklist.txt b/game/worklist.txt new file mode 100644 index 0000000..510d78b --- /dev/null +++ b/game/worklist.txt @@ -0,0 +1,142 @@ +* need "Connecting..." from Specify panel. And "not found" if not found! Plus timeout (from 4155) is too long +- wifi game name not shown when hosting? +- finish M_16 fixes +- get someone to test Wc13Transport.dll +- WI exited looking for devices. Bad name? Name too long? Stack problem? +- if host crashes or otherwise doesn't ever respond while client is "Waiting for all players..." the client hangs forever +- pause bringing up join screen +- add (space) to input form + +------------------------------------------------------------------------------ +Bugs +- Bullpups ignoring fog. Related?: players feel that Bullpup Gx search is broken +- some clients see black screen while others see "Waiting for all players" +- handle device/network turning off while system network UI forms are up +- Treo 600 sometimes crashes immediately upon pressing "multiplayer" +- crash after OK on TooManyTCPConnections (ux50) + +UI +- need some sort of client timeout between connecting and receiving first game info in case the game info never arrives +- remember specified addresses? +- multiplayer-appropriate summary screens (win/lose/objectives) +- show real game duration at summary + +Misc +- updates to WI startup screen (hi/med/lo) + - copyright year +- alliances +- Handmark MP launch +- rendezvous/packet forwarding server +? buffer AsyncSends to send full packets at update time (combine BT MTU work above?) +- replace gpbScratch accesses with Lock/UnlockScratchBuffer() + +Notes +- anti-lingering code doesn't seem to do the job on the ux50. Works fine on TC +- w/ latest changes allowing user to select amongst IP transports and passing + IP addr to SocTransport constructor is moot (uses INADDR_ANY instead)- why does OnStatusUpdate callback block during BtConnection destruction? [same r2 trashing problem?] +- "batteries are low" popup on ux50, afterwards game is suspended. ux50 is host -- + eventually TC client disconnected and then UX resumed! + +------------------------------------------------------------------------------ +FAQ +- PalmOne Tungsten T devices require a patch from PalmOne +- Sony Clie TG-50 devices require a patch from Sony +- PalmOne Tungsten TT and T2 devices can crash if the device +they're connected to disconnects uncleanly (moves out of range or +crashes). Unfortunately PalmOne has not provided a patch for this OS bug +- iPAQ h2210/2215 can spontaneously drop connections, especially if the game speed is set high +- iPAQ h4155 when hosting will only allow a single client to join. It has no problems as a client +x all iPAQs exhibit 'jumpiness' when joining high-speed games. As hosts they're fine + + +Level Creator FAQ +- Side 1-N must have their Intelligence set to "Human" if a human is going to be able to play that side +- Level MinPlayers must be >1 +- MP games should have "[side 1, side 2, side 3, side 4] Enemy owns Exactly 0 Any Unit : End Mission: Win" trigger +- MP games should have "[side 1, side 2, side 3, side 4] Allies owns Exactly 0 Any Unit : End Mission: Lose" trigger +? win trigger must come before lose trigger + + +--------------- +4-24-04 +UNEXPECTED GAME TERMINATION WHEN PPC (H2210) IS CLIENT OF A 4X GAME SERVED BY A PALM (TT) +@ update 5826, 1791, 184(!!) + +TT reports +BtConnection::SocketProc EventDisconnect L2DiscLincDisc +GenericMEProc::EventACLDisconnect MeStatusPowerOff ("other end terminated (about to power off)") + +H2210 reports +WcConnection::OnRemoteDisconnected(reason = 0) ("Zero (0) if the cause of disconnect was a “disconnect indicator callback” from the L2CAP stack layer.") + +WcTransport::OnStackStatusChange(DEVST_DOWN) ("Device is present, but down") +"Connection to host has been severed" +long timeout trying to perform inquiry +! Can't game search thereafter, even after exiting/restarting WI +!! Attempting to launch Bluetooth Manager after reports + "The Bluetooth Radio failed to turn ON due to insufficient driver memory available. + You must perform a normal reet befor you can turn ON the Bluetooth Radio." +? How does it know this? No such error codes are exposed through the BTW-CE DK + +Sequence: +- play game, end due to congestion-related failure (strange client halt, due to missed Update from server?) +- exit game normally +- restart game +- quickly hit dropped connection +- BT stack hosed, confirmed by Bluetooth Manager error +- reset +- host crashed + Assert(pnmCopy != NULL, "Out of memory allocating pnmCopy!"); +- "Your connection to the host has been severed." +- exit game + +iPAQ h2210 (before update) +BTW-CE v. 1.4.1 Build 04 +BtCeStack.dll 1.2.4.1 +BtCeProfiles.dll 1.2.4.1 +BtChooserLib.dll 1.4.1.04 +BtCeStack.dll 1.2.4.1 (! it's shown twice !) +BtCeSendTo_Poom.dll 1.0.0.1 +BtCeIf.dll 1.2.4.1 +BtCeOsif4.dll 1.2.4.1 +BtRez.dll 1.0.0.1 + +iPAQ h2210 (after update) +BTW-CE v. 1.4.1 Build 58 +BtCeStack.dll 1.2.4.1 +BtCeProfiles.dll 1.2.4.1 +BtChooserLib.dll 1.4.1.58 +BtCeStack.dll 1.2.4.1 (! it's shown twice !) +BtCeSendTo_Poom.dll 1.0.0.1 +BtCeIf.dll 1.2.4.1 +BtCeOsif4.dll 1.2.4.1 +BtRez.dll 1.0.0.1 + +iPAQ 3875 +BT Browser 1.1 +BTM Firmware 114d +bc01 Chip Version 1.65h +HCI Version 1.72h +LMP Version 1.72h + + +- "Waiting for radio to initialize" flashes twice. BtTransport::FindAndOpenBluetoothLibrary being called more than once? + +TT (H), H2210 (C), 4x, w/ tracing +09:52 - 09:59 dropped (10,100 updates) +10:08 - 10:24 host choked (~17,000 updates) +10:26 - 10:27 dropped (1,538 updates) -- following non-reset +10:31 - 10:35 dropped (6,036 updates) -- following reset + +TT (H), H2210 (C), 1x, w/ tracing +10:51 - 11:15 host choked (~18,000 updates) -- following a reset +11:19 - 11:44 host choked (~18,300 updates) -- following a non-reset + +TT (H), H2210 (C), 1x, w/o tracing +11:50 - ??:?? dropped (992,703 updates [22 hrs]) -- following a non-reset + +TT (H), H2210 (C), 4x, w/o tracing +02:38 - 02:40 dropped (? updates) + +TT (H), H2210 (C), 4x, w/o tracing +11:17 - 11:18 dropped (? updates) diff --git a/game/xtransport.cpp b/game/xtransport.cpp new file mode 100644 index 0000000..3575d37 --- /dev/null +++ b/game/xtransport.cpp @@ -0,0 +1,946 @@ +#include "base/log.h" +#include "base/thread.h" +#include "game/xtransport.h" +#include "game/serviceurls.h" +#include "game/chooseserverform.h" +#include "mpshared/xpump.h" + +namespace wi { + +//--------------------------------------------------------------------------- +// XTransport implementation + +XTransport::XTransport(const base::SocketAddress& address) : Transport(), + address_(address), state_(XTS_CLOSED), + waiting_(false), + error_(false), id_(0), roomidCreate_(0), gameidCreate_(0), + resultLogin_(knLoginResultFail), + resultSignOut_(knSignOutResultSuccess), + resultLobbyJoin_(knLobbyJoinResultFail), + resultRoomJoin_(knRoomJoinResultFail), + resultRoomCreate_(knLobbyCreateRoomResultFail), + resultGameJoin_(knGameJoinResultFail), + resultGameCreate_(knRoomCreateGameResultFail) { +} + +XTransport::~XTransport() { +} + +int XTransport::GetTransportDescriptions(TransportDescription *atrad, + int ctradMax) { + // This transport is opened directly, currently +#if 0 + TransportDescription *ptrad = atrad; + ptrad->trat = ktratX; + strcpy(ptrad->szName, "Connection"); + ptrad->pfnOpen = XTransport::Open; + ptrad->dwTransportSpecific = 0; + return 1; +#else + return 0; +#endif +} + +dword XTransport::Open(TransportDescription *ptrad, Transport **pptra) { + // This transport is opened directly, currently +#if 0 + Transport *ptra = new XTransport(ptrad->dwTransportSpecific); + if (ptra == NULL) { + return knTransportOpenResultFail; + } + dword result = ptra->Open(); + if (result == knTransportOpenResultSuccess) { + *pptra = ptra; + return result; + } + delete ptra; + return result; +#else + return knTransportOpenResultFail; +#endif +} + +void XTransport::SetState(State state) { + LOG() << "From: " << XtsLabels.Find(state_) + << " To: " << XtsLabels.Find(state); + state_ = state; + + // If waiting for a state change, wake up + if (waiting_) { + base::Thread::current().Post(base::kidmNullEvent, NULL); + } +} + +bool XTransport::CheckState(State state0, State state1) { + Assert(state0 == state_ || state1 == state_); + if (state0 != state_ && state1 != state_) { + LOG() << "Error! Current: " << XtsLabels.Find(state_); + if (state1 == XTS_INVALID) { + LOG() << " Expected: " << XtsLabels.Find(state0); + } else { + LOG() << " Expected: " << XtsLabels.Find(state0) + << " or " << XtsLabels.Find(state1); + } + return false; + } + return true; +} + +dword XTransport::Open() { + // The caller of Transport::Open expects a synchronous Open, + // yet the connect and handshake need to stay responsive to UI + // needs (like exit application, and timeout). + error_ = false; + base::Socket *sock = base::Thread::current().ss().CreateSocket(SOCK_STREAM, + this); + if (sock == NULL) { + LOG() << "CreateSocket() failed"; + return knTransportOpenResultNoNetwork; + } + + // Synchronous Connect to the server! There is "connecting..." UI + // showing during this. + if (sock->Connect(address_) < 0) { + if (error_ || !sock->IsBlocking()) { + LOG() << "Connect() failed"; + delete sock; + return knTransportOpenResultCantConnect; + } + } + SetState(XTS_CONNECTING); + + // Wait for the connect to occur with a modal loop that dispatches + // nothing. If there is no connect after the timeout, assume error. + + LOG() << "Waiting to connect to " << address_.ToString(); + + if (!WaitForStateChange()) { + LOG() << "Timed out waiting for connect"; + } + if (state_ != XTS_CONNECTED) { + LOG() << "Could not connect, closing socket"; + delete sock; + SetState(XTS_CLOSED); + return knTransportOpenResultNotResponding; + } + + // From here on, the socket is owned by xpump. + xpump_.Attach(sock, this); + + // Now send a handshake message, and await a reply before + // returning + if (!Handshake() || error_) { + LOG() << "Handshake error"; + xpump_.Close(); + SetState(XTS_CLOSED); + return knTransportOpenResultCantConnect; + } + + // Await a proper reply. + LOG() << "Waiting for handshake reply"; + if (!WaitForStateChange()) { + LOG() << "Timed out Waiting for handshake reply"; + xpump_.Close(); + SetState(XTS_CLOSED); + return knTransportOpenResultNotResponding; + } + + if (state_ == XTS_HANDSHAKEERROR) { + LOG() << "Received handshake error"; + xpump_.Close(); + SetState(XTS_CLOSED); + return knTransportOpenResultProtocolMismatch; + } + + if (state_ == XTS_HANDSHAKESERVERFULL) { + LOG() << "Received handshake server full."; + xpump_.Close(); + SetState(XTS_CLOSED); + return knTransportOpenResultServerFull; + } + + // Now in opened state + SetState(XTS_OPEN); + LOG() << "XTransport successfully opened!"; + return knTransportOpenResultSuccess; +} + +void XTransport::Close() { + SetState(XTS_CLOSED); + xpump_.Close(); +} + +bool XTransport::IsClosed() { + return state_ == XTS_CLOSED; +} + +bool XTransport::WaitForStateChange(long ctWait) { + waiting_ = true; + gtimm.Enable(false); + long t = HostGetTickCount(); + int state = state_; + while (state == state_) { + if (error_) { + return false; + } + if (HostGetTickCount() - t >= ctWait) { + gtimm.Enable(true); + waiting_ = false; + return false; + } + Event evt; + if (gevm.GetEvent(&evt, 50, false)) { + if (gevm.IsAppStopping()) { + gtimm.Enable(true); + waiting_ = false; + return false; + } + } + OnEvent(&evt); + } + gtimm.Enable(true); + waiting_ = false; + return true; +} + +bool XTransport::Handshake() { + if (!CheckState(XTS_CONNECTED)) { + return false; + } + xpump_.Send(XMsgHandshake::ToBuffer(kdwClientID, kdwProtocolCurrent)); + SetState(XTS_HANDSHAKING); + return true; +} + +void XTransport::OnHandshakeResult(dword result, dword id) { + LOG() << HandshakeResults.Find(result); + if (!CheckState(XTS_HANDSHAKING)) { + return; + } + + // These should be results not states, but not changing now... + switch (result) { + case knHandshakeResultSuccess: + id_ = id; + SetState(XTS_HANDSHAKESUCCESS); + return; + + case knHandshakeResultServerFull: + SetState(XTS_HANDSHAKESERVERFULL); + return; + + default: + SetState(XTS_HANDSHAKEERROR); + return; + } +} + +void XTransport::OnShowMessage(const char *message, dword ipRedirect, + bool disconnect) { + if (error_ || xpump_.IsClosed()) { + return; + } + // The server is asking to show a message. This is allowed during + // any state. + // TODO: show message + // TODO: handle ipRedirect, disconnect + if (m_ptcb != NULL) { + if (strlen(message) != 0) { + m_ptcb->OnShowMessage(message); + } + } +} + +void XTransport::OnEcho() { + xpump_.Send(XMsgEcho::ToBuffer()); +} + +dword XTransport::Login(const char *username, const char *token) { + LOG() << "Logging in..."; + + if (error_ || xpump_.IsClosed()) { + return knLoginResultFail; + } + if (!CheckState(XTS_OPEN)) { + return knLoginResultFail; + } + + // Login and wait synchronously for reply. + SetState(XTS_LOGGINGIN); + xpump_.Send(XMsgLogin::ToBuffer(username, token, gszDeviceId)); + + if (!WaitForStateChange()) { + LOG() << "Timed out waiting for Login"; + resultLogin_ = knLoginResultFail; + } + + if (state_ != XTS_LOGGEDIN) { + LOG() << LoginResults.Find(resultLogin_); + SetState(XTS_OPEN); + return resultLogin_; + } + + // Login successful. Keep state XTS_LOGGEDIN. + return resultLogin_; +} + +void XTransport::OnLoginResult(dword resultLogin) { + LOG() << LoginResults.Find(resultLogin); + if (!CheckState(XTS_LOGGINGIN)) { + resultLogin_ = knLoginResultFail; + return; + } + if (error_ || xpump_.IsClosed()) { + resultLogin_ = knLoginResultFail; + return; + } + resultLogin_ = resultLogin; + + if (resultLogin_ != knLoginResultSuccess && + resultLogin_ != knLoginResultAnonymousSuccess) { + SetState(XTS_LOGINFAILED); + return; + } + SetState(XTS_LOGGEDIN); +} + +dword XTransport::SignOut() { + LOG() << "Signing Out..."; + if (error_ || xpump_.IsClosed()) { + return knSignOutResultFail; + } + + // Only know how to do this from XTS_LOGGEDIN state, currently. + if (!CheckState(XTS_LOGGEDIN)) { + return knSignOutResultFail; + } + + // Sign out and wait synchronously for reply. + SetState(XTS_SIGNINGOUT); + xpump_.Send(XMsgSignOut::ToBuffer()); + + if (!WaitForStateChange()) { + LOG() << "Timed out waiting for sign out"; + resultSignOut_ = knSignOutResultFail; + } + + if (state_ != XTS_SIGNEDOUT) { + LOG() << SignOutResults.Find(resultSignOut_); + SetState(XTS_LOGGEDIN); + return resultSignOut_; + } + + // Sign Out successful. + SetState(XTS_OPEN); + return resultSignOut_; +} + +void XTransport::OnSignOutResult(dword result) { + LOG() << SignOutResults.Find(result); + if (!CheckState(XTS_SIGNINGOUT)) { + resultSignOut_ = knSignOutResultFail; + return; + } + if (error_ || xpump_.IsClosed()) { + resultSignOut_ = knSignOutResultFail; + return; + } + resultSignOut_ = result; + + if (resultSignOut_ != knSignOutResultSuccess) { + SetState(XTS_SIGNOUTFAILED); + return; + } + SetState(XTS_SIGNEDOUT); +} + +const char *XTransport::GetAnonymousUsername() { + return base::Format::ToString("anon%d", id_); +} + +dword XTransport::JoinLobby(ILobbyCallback *plcb) { + if (error_ || xpump_.IsClosed()) { + return knLobbyJoinResultFail; + } + if (!CheckState(XTS_LOGGEDIN)) { + return knLobbyJoinResultFail; + } + + xpump_.Send(XMsgLobbyJoin::ToBuffer()); + SetState(XTS_JOINLOBBY); + + LOG() << "Waiting for LobbyJoinResult"; + if (!WaitForStateChange()) { + LOG() << "Timed out waiting for LobbyJoinResult"; + } + if (state_ != XTS_JOINLOBBYSUCCESS) { + LOG() << "Could not enter lobby reason: " + << LobbyJoinResults.Find(resultLobbyJoin_); + SetState(XTS_LOGGEDIN); + return resultLobbyJoin_; + } + SetState(XTS_LOBBY); + m_plcb = plcb; + return resultLobbyJoin_; +} + +void XTransport::OnLobbyJoinResult(dword result) { + LOG() << LobbyJoinResults.Find(result); + if (error_ || xpump_.IsClosed()) { + return; + } + if (!CheckState(XTS_JOINLOBBY)) { + return; + } + resultLobbyJoin_ = result; + if (result == knLobbyJoinResultSuccess) { + SetState(XTS_JOINLOBBYSUCCESS); + return; + } + SetState(XTS_JOINLOBBYFAILED); +} + +void XTransport::OnLobbyAddRoom(const char *room, dword roomid, bool priv, + dword cPlayers, dword cGames) { + if (m_plcb != NULL) { + m_plcb->OnAddRoom(room, roomid, priv, cPlayers, cGames); + } +} + +void XTransport::OnLobbyRemoveRoom(dword roomid) { + if (m_plcb != NULL) { + m_plcb->OnRemoveRoom(roomid); + } +} + +void XTransport::OnLobbyUpdateRoom(dword roomid, dword cPlayers, dword cGames) { + if (m_plcb != NULL) { + m_plcb->OnUpdateRoom(roomid, cPlayers, cGames); + } +} + +bool XTransport::LeaveLobby() { + m_plcb = NULL; + if (error_ || xpump_.IsClosed()) { + return false; + } + if (!CheckState(XTS_LOBBY)) { + return false; + } + xpump_.Send(XMsgLobbyLeave::ToBuffer()); + SetState(XTS_LOGGEDIN); + return true; +} + +void XTransport::OnLobbyLeaveResult(dword result) { +} + +dword XTransport::CreateRoom(const char *roomname, const char *password, + dword *roomid) { + LOG() << base::Format::ToString("CreateRoom: %s %s", roomname, password); + + if (error_ || xpump_.IsClosed()) { + return knLobbyCreateRoomResultFail; + } + if (!CheckState(XTS_LOBBY)) { + return knLobbyCreateRoomResultFail; + } + + SetState(XTS_CREATINGROOM); + xpump_.Send(XMsgLobbyCreateRoom::ToBuffer(roomname, password)); + + // Wait for reply + + if (!WaitForStateChange()) { + LOG() << "Timed out waiting room create"; + resultRoomCreate_ = knLobbyCreateRoomResultFail; + } + + if (state_ != XTS_CREATEROOMSUCCESS) { + LOG() << LobbyCreateRoomResults.Find(resultRoomCreate_); + SetState(XTS_LOBBY); + return resultRoomCreate_; + } + + *roomid = roomidCreate_; + SetState(XTS_LOBBY); + return resultRoomCreate_; +} + +void XTransport::OnLobbyCreateRoomResult(dword result, dword roomid) { + LOG() << LobbyCreateRoomResults.Find(result); + if (!CheckState(XTS_CREATINGROOM)) { + resultRoomCreate_ = knLobbyCreateRoomResultFail; + return; + } + if (error_ || xpump_.IsClosed()) { + resultRoomCreate_ = knLobbyCreateRoomResultFail; + return; + } + resultRoomCreate_ = result; + roomidCreate_ = roomid; + if (resultRoomCreate_ != knLobbyCreateRoomResultSuccess) { + SetState(XTS_CREATEROOMERROR); + return; + } + SetState(XTS_CREATEROOMSUCCESS); +} + +dword XTransport::CanJoinRoom(dword roomid, const char *password) { + if (error_ || xpump_.IsClosed()) { + return knRoomJoinResultFail; + } + if (!CheckState(XTS_LOBBY)) { + return knRoomJoinResultFail; + } + + SetState(XTS_CANJOINROOM); + xpump_.Send(XMsgLobbyCanJoinRoom::ToBuffer(roomid, password)); + + // Wait for the reply + + if (!WaitForStateChange()) { + LOG() << "Timed out can wait joining room"; + resultRoomJoin_ = knRoomJoinResultFail; + } + SetState(XTS_LOBBY); + return resultRoomJoin_; +} + +void XTransport::OnLobbyCanJoinRoomResult(dword result) { + if (!CheckState(XTS_CANJOINROOM)) { + return; + } + resultRoomJoin_ = result; + SetState(XTS_LOBBY); +} + +void XTransport::OnLobbyLurkerCount(dword count) { + if (m_plcb != NULL) { + m_plcb->OnLurkerCount(count); + } +} + +dword XTransport::JoinRoom(dword roomid, const char *password, + IRoomCallback *prcb) { + // Synchronously join a room. It may fail. + + if (error_ || xpump_.IsClosed()) { + return knRoomJoinResultFail; + } + if (!CheckState(XTS_LOGGEDIN)) { + return knRoomJoinResultFail; + } + + SetState(XTS_JOININGROOM); + xpump_.Send(XMsgRoomJoin::ToBuffer(roomid, password)); + + // Wait for the reply + + if (!WaitForStateChange()) { + LOG() << "Timed out waiting room join"; + resultRoomJoin_ = knRoomJoinResultFail; + } + + if (state_ != XTS_JOINROOMSUCCESS) { + LOG() << RoomJoinResults.Find(resultRoomJoin_); + SetState(XTS_LOGGEDIN); + return resultRoomJoin_; + } + + // Entering a new room; clear the games being tracked + + m_prcb = prcb; + SetState(XTS_ROOM); + return resultRoomJoin_; +} + +void XTransport::OnRoomJoinResult(dword result) { + LOG() << RoomJoinResults.Find(result); + if (!CheckState(XTS_JOININGROOM)) { + resultRoomJoin_ = knRoomJoinResultFail; + return; + } + if (error_ || xpump_.IsClosed()) { + resultRoomJoin_ = knRoomJoinResultFail; + return; + } + resultRoomJoin_ = result; + if (resultRoomJoin_ != knRoomJoinResultSuccess) { + SetState(XTS_JOINROOMERROR); + return; + } + SetState(XTS_JOINROOMSUCCESS); +} + +void XTransport::OnRoomAddPlayer(const char *player) { + if (m_prcb != NULL) { + m_prcb->OnAddPlayer(player); + } +} + +void XTransport::OnRoomRemovePlayer(dword hint, const char *player) { + if (m_prcb != NULL) { + m_prcb->OnRemovePlayer(hint, player); + } +} + +bool XTransport::SendChat(const char *chat) { + if (error_ || xpump_.IsClosed()) { + return false; + } + if (state_ == XTS_ROOM) { + xpump_.Send(XMsgRoomSendChat::ToBuffer(chat)); + return true; + } + if (state_ == XTS_GAME) { + xpump_.Send(XMsgGameSendChat::ToBuffer(chat)); + return true; + } + Assert(); + return false; +} + +void XTransport::OnRoomReceiveChat(const char *player, const char *chat) { + if (m_prcb != NULL) { + m_prcb->OnReceiveChat(player, chat); + } +} + +void XTransport::OnRoomAddGame(const char *player, dword gameid, + const GameParams& params, dword minplayers, dword maxplayers, + const char *title, dword ctotal) { + if (m_prcb != NULL) { + m_prcb->OnAddGame(player, gameid, params, minplayers, maxplayers, + title, ctotal); + } +} + +void XTransport::OnRoomRemoveGame(dword gameid, dword ctotal) { + if (m_prcb != NULL) { + m_prcb->OnRemoveGame(gameid, ctotal); + } +} + +void XTransport::OnRoomGameInProgress(dword gameid) { + if (m_prcb != NULL) { + m_prcb->OnGameInProgress(gameid); + } +} + +void XTransport::OnRoomGamePlayerNames(dword gameid, dword cnames, + const PlayerName *anames) { + if (m_prcb != NULL) { + m_prcb->OnGamePlayerNames(gameid, cnames, anames); + } +} + +void XTransport::OnRoomStatusComplete() { + if (m_prcb != NULL) { + m_prcb->OnStatusComplete(); + } +} + +dword XTransport::CanJoinGame(dword gameid) { + if (error_ || xpump_.IsClosed()) { + return knRoomJoinResultFail; + } + if (!CheckState(XTS_ROOM)) { + return knGameJoinResultFail; + } + + SetState(XTS_CANJOINGAME); + xpump_.Send(XMsgRoomCanJoinGame::ToBuffer(gameid)); + + // Wait for the reply + + if (!WaitForStateChange()) { + LOG() << "Timed out can wait joining game"; + resultGameJoin_ = knGameJoinResultFail; + } + SetState(XTS_ROOM); + return resultRoomJoin_; +} + +void XTransport::OnRoomCanJoinGameResult(dword result) { + if (!CheckState(XTS_CANJOINGAME)) { + return; + } + resultGameJoin_ = result; + SetState(XTS_ROOM); +} + +void XTransport::SetGameCountStatus(dword ctotal) { + if (m_ptcb != NULL) { + char szT[64]; + if (ctotal == 1) { + sprintf(szT, "%d game.", (int)ctotal); + } else { + sprintf(szT, "%d games.", (int)ctotal); + } + m_ptcb->OnStatusUpdate(szT); + } +} + +dword XTransport::CreateGame(GameParams *params, PackId *ppackidUpgrade, + dword *gameid) { + if (error_ || xpump_.IsClosed()) { + LOG() << "Error or closed"; + return knRoomCreateGameResultFail; + } + if (!CheckState(XTS_ROOM)) { + return knRoomCreateGameResultFail; + } + xpump_.Send(XMsgRoomCreateGame::ToBuffer(params)); + + // Wait synchronously for the result, since the UI needs it. + SetState(XTS_CREATINGGAME); + if (!WaitForStateChange()) { + LOG() << "timed out waiting for XMsgRoomCreateGameResult"; + SetState(XTS_ROOM); + return knRoomCreateGameResultFail; + } + + if (!CheckState(XTS_CREATEGAMEERROR, XTS_CREATEGAMESUCCESS)) { + // Don't know what state it is, so don't state transition + LOG() << "expected CREATEGAMESUCCESS or ERROR state"; + return knRoomCreateGameResultFail; + } + + if (state_ == XTS_CREATEGAMEERROR) { + *ppackidUpgrade = packidCreate_; + SetState(XTS_ROOM); + return resultGameCreate_; + } + + SetState(XTS_ROOM); + *gameid = gameidCreate_; + return resultGameCreate_; +} + +void XTransport::OnRoomCreateGameResult(dword gameid, dword result, + const PackId *ppackid) { + LOG() << CreateGameResults.Find(result); + if (!CheckState(XTS_CREATINGGAME)) { + resultGameCreate_ = knRoomCreateGameResultFail; + return; + } + if (error_ || xpump_.IsClosed()) { + resultGameCreate_ = knRoomCreateGameResultFail; + return; + } + + // Remember these for later + resultGameCreate_ = result; + packidCreate_ = *ppackid; + gameidCreate_ = gameid; + + if (result != knRoomCreateGameResultSuccess) { + SetState(XTS_CREATEGAMEERROR); + return; + } + SetState(XTS_CREATEGAMESUCCESS); +} + +void XTransport::LeaveRoom(dword hint) { + m_prcb = NULL; + if (error_ || xpump_.IsClosed()) { + return; + } + if (!CheckState(XTS_ROOM)) { + return; + } + SetState(XTS_LEAVINGROOM); + xpump_.Send(XMsgRoomLeave::ToBuffer(hint)); + WaitForStateChange(); + SetState(XTS_LOGGEDIN); +} + +void XTransport::OnRoomLeaveResult() { + if (error_ || xpump_.IsClosed()) { + return; + } + if (!CheckState(XTS_LEAVINGROOM)) { + return; + } + SetState(XTS_LOGGEDIN); +} + +dword XTransport::JoinGame(dword gameid, dword roomid) { + if (error_ || xpump_.IsClosed()) { + LOG() << "Error or closed"; + return knGameJoinResultFail; + } + + // Must be in XTS_LOGGEDIN state, not in room, lobby, or game. + if (!CheckState(XTS_LOGGEDIN)) { + return knGameJoinResultFail; + } + + // Wait synchronously for the result + SetState(XTS_JOININGGAME); + xpump_.Send(XMsgGameJoin::ToBuffer(gameid, roomid)); + if (!WaitForStateChange()) { + LOG() << "timed out waiting for XMsgGameJoinResult"; + SetState(XTS_LOGGEDIN); + return knGameJoinResultFail; + } + + if (!CheckState(XTS_JOINGAMEERROR, XTS_JOINGAMESUCCESS)) { + // Don't know what state it is, so don't state transition + LOG() << "expected JOINGAMESUCCESS or ERROR state"; + return knGameJoinResultFail; + } + + if (state_ == XTS_JOINGAMEERROR) { + SetState(XTS_LOGGEDIN); + return resultGameJoin_; + } + + SetState(XTS_GAME); + return resultGameJoin_; +} + +void XTransport::OnGameJoinResult(dword result) { + LOG() << GameJoinResults.Find(result); + if (!CheckState(XTS_JOININGGAME)) { + return; + } + if (error_ || xpump_.IsClosed()) { + resultGameJoin_ = knGameJoinResultFail; + return; + } + + resultGameJoin_ = result; + + if (result != knGameJoinResultSuccess) { + LOG() << "Error connecting to game."; + SetState(XTS_JOINGAMEERROR); + return; + } + + SetState(XTS_JOINGAMESUCCESS); +} + +void XTransport::OnGameReceiveChat(const char *player, const char *chat) { + if (m_pgcb != NULL) { + m_pgcb->OnReceiveChat(player, chat); + } +} + +bool XTransport::SendNetMessage(NetMessage *pnm) { + if (error_ || xpump_.IsClosed()) { + return false; + } + if (!CheckState(XTS_GAME)) { + return false; + } + return xpump_.Send(XMsgGameNetMessage::ToBuffer(pnm)); +} + +void XTransport::OnGameNetMessage(NetMessage **ppnm) { + if (!CheckState(XTS_GAME)) { + return; + } + if (m_pgcb != NULL) { + m_pgcb->OnNetMessage(ppnm); + } +} + +void XTransport::OnGameKilled(dword gameid) { + if (!CheckState(XTS_GAME)) { + return; + } + + // The server removed this client from the game, so this client's + // state is no longer GAME. + SetState(XTS_LOGGEDIN); + + if (m_pgcb != NULL) { + m_pgcb->OnGameDisconnect(); + } +} + +void XTransport::LeaveGame() { + if (error_ || xpump_.IsClosed()) { + LOG() << "Error or closed"; + return; + } + + // If the game was killed by the server, the state gets reverted to + // XTS_LOGGEDIN. In this case, the client may call LeaveGame anyway, + // so ignore this case without an assert. + if (state_ == XTS_LOGGEDIN) { + return; + } + + // The client should bin the game + if (!CheckState(XTS_GAME)) { + LOG() << "Not in game state!"; + return; + } + + // Disconnect this game synchronously. This is so that a game + // in progress doesn't continue to send updates when the client + // thinks it is disconnected. + + xpump_.Send(XMsgGameLeave::ToBuffer()); + if (!WaitForStateChange()) { + LOG() << "timed out waiting for disconnect game result"; + } + + // Go to XTS_LOGGEDIN, whether it was successful or not + SetState(XTS_LOGGEDIN); +} + +void XTransport::OnGameLeaveResult(dword result) { + LOG() << GameLeaveResults.Find(result); + SetState(XTS_LOGGEDIN); +} + +void XTransport::OnEvent(Event *pevt) { + if (xpump_.Dispatch()) { + OnMessages(); + } +} + +bool XTransport::OnMessages() { + // Pump the message loop for each xmsg. Important since xmsg's change + // state, and message loops evaluate state. + base::Thread::current().Post(base::kidmTransportEvent, NULL); + return true; +} + +void XTransport::OnError(int error) { + LOG() << base::Socket::GetErrorString(error); + error_ = true; + OnClose(0); +} + +void XTransport::OnClose(int error) { + LOG() << error; + if (m_ptcb != NULL) { + m_ptcb->OnConnectionClose(); + } + SetState(XTS_CLOSED); + + // Cause a message to be pumped through the game input loop to cause it + // to wake up, since processing here was done in MessageQueue dispatch. + + base::Thread::current().Post(base::kidmNullEvent, NULL); +} + +void XTransport::OnConnectEvent(base::Socket *socket) { + SetState(XTS_CONNECTED); +} + +void XTransport::OnReadEvent(base::Socket *socket) { +} + +void XTransport::OnWriteEvent(base::Socket *socket) { +} + +void XTransport::OnCloseEvent(base::Socket *socket) { +} + +} // namespace wi diff --git a/game/xtransport.h b/game/xtransport.h new file mode 100644 index 0000000..fc8144f --- /dev/null +++ b/game/xtransport.h @@ -0,0 +1,182 @@ +#ifndef __XTRANSPORT_H__ +#define __XTRANSPORT_H__ + +#include "game/ht.h" +#include "base/socketaddress.h" +#undef SetState + +#include "mpshared/xmsg.h" +#include "mpshared/xpump.h" +#include "base/socket.h" + +namespace wi { + +class XTransport : public Transport, XPumpNotify, base::SocketNotify +{ +public: + XTransport(const base::SocketAddress& address); + virtual ~XTransport(); + + static int GetTransportDescriptions(TransportDescription *atrad, + int ctradMax); + static dword Open(TransportDescription *ptrad, Transport **pptra); + + // Transport implementation + virtual dword Open(); + virtual void Close(); + virtual bool IsClosed(); + virtual dword Login(const char *username, const char *token); + virtual dword SignOut(); + virtual const char *GetAnonymousUsername(); + virtual dword JoinLobby(ILobbyCallback *plcb); + virtual bool LeaveLobby(); + virtual dword CreateRoom(const char *roomname, const char *password, + dword *roomid); + virtual dword CanJoinRoom(dword roomid, const char *password); + virtual dword JoinRoom(dword roomid, const char *password, + IRoomCallback *prcb); + virtual bool SendChat(const char *chat); + virtual dword CreateGame(GameParams *params, PackId *ppackidUpgrade, + dword *gameid); + virtual dword CanJoinGame(dword gameid); + virtual void LeaveRoom(dword hint); + + virtual dword JoinGame(dword gameid, dword roomid); + virtual bool SendNetMessage(NetMessage *pnm); + virtual void LeaveGame(); + + virtual void OnEvent(Event *pevt); + + enum State { + XTS_INVALID = -1, + XTS_CLOSED, XTS_CONNECTING, XTS_CONNECTED, + XTS_HANDSHAKING, XTS_HANDSHAKEERROR, XTS_HANDSHAKESERVERFULL, + XTS_HANDSHAKESUCCESS, + XTS_OPEN, + XTS_LOGGINGIN, XTS_LOGINFAILED, XTS_LOGGEDIN, + XTS_SIGNINGOUT, XTS_SIGNOUTFAILED, XTS_SIGNEDOUT, + XTS_JOINLOBBY, XTS_JOINLOBBYFAILED, XTS_JOINLOBBYSUCCESS, + XTS_LOBBY, + XTS_CREATINGROOM, XTS_CREATEROOMERROR, XTS_CREATEROOMSUCCESS, + XTS_CANJOINROOM, + XTS_JOININGROOM, XTS_JOINROOMERROR, XTS_JOINROOMSUCCESS, + XTS_ROOM, + XTS_CREATINGGAME, XTS_CREATEGAMEERROR, XTS_CREATEGAMESUCCESS, + XTS_CANJOINGAME, XTS_LEAVINGROOM, + XTS_JOININGGAME, XTS_JOINGAMEERROR, XTS_JOINGAMESUCCESS, + XTS_GAME, + }; + +private: + bool WaitForStateChange(long ctWait = 30 * 100); + void SetState(State state); + bool CheckState(State state0, State state1 = (State)-1); + bool Handshake(); + bool ConnectGame(dword gameid); + void SetGameCountStatus(dword ctotal); + + // Socket notify methods + virtual void OnConnectEvent(base::Socket *socket); + virtual void OnReadEvent(base::Socket *socket); + virtual void OnWriteEvent(base::Socket *socket); + virtual void OnCloseEvent(base::Socket *socket); + + // XPump methods + virtual void OnHandshakeResult(dword result, dword id); + virtual void OnShowMessage(const char *message, dword ipRedirect, + bool disconnect); + virtual void OnEcho(); + virtual void OnLoginResult(dword result); + virtual void OnSignOutResult(dword result); + virtual void OnLobbyJoinResult(dword result); + virtual void OnLobbyLeaveResult(dword result); + virtual void OnLobbyCreateRoomResult(dword result, dword roomid); + virtual void OnLobbyCanJoinRoomResult(dword result); + virtual void OnLobbyLurkerCount(dword count); + virtual void OnLobbyAddRoom(const char *room, dword idRoom, bool priv, + dword cPlayers, dword cGames); + virtual void OnLobbyRemoveRoom(dword idRoom); + virtual void OnLobbyUpdateRoom(dword idRoom, dword cPlayers, dword cGames); + virtual void OnRoomJoinResult(dword result); + virtual void OnRoomAddPlayer(const char *player); + virtual void OnRoomRemovePlayer(dword hint, const char *player); + virtual void OnRoomReceiveChat(const char *player, const char *chat); + virtual void OnRoomAddGame(const char *player, dword gameid, + const GameParams& params, dword minplayers, dword maxplayers, + const char *title, dword ctotal); + virtual void OnRoomRemoveGame(dword gameid, dword ctotal); + virtual void OnRoomGameInProgress(dword gameid); + virtual void OnRoomGamePlayerNames(dword gameid, dword cnames, + const PlayerName *anames); + virtual void OnRoomStatusComplete(); + virtual void OnRoomCreateGameResult(dword gameid, dword result, + const PackId *ppackid); + virtual void OnRoomCanJoinGameResult(dword result); + virtual void OnRoomLeaveResult(); + virtual void OnGameJoinResult(dword gameid); + virtual void OnGameReceiveChat(const char *player, const char *chat); + virtual void OnGameNetMessage(NetMessage **ppnm); + virtual void OnGameKilled(dword gameid); + virtual void OnGameLeaveResult(dword gameid); + virtual bool OnMessages(); + virtual void OnError(int error); + virtual void OnClose(int error); + + base::SocketAddress address_; + XPump xpump_; + State state_; + bool waiting_; + bool error_; + dword resultLogin_; + dword resultSignOut_; + dword resultLobbyJoin_; + dword resultRoomJoin_; + dword resultRoomCreate_; + dword resultGameJoin_; + dword resultGameCreate_; + dword roomidCreate_; + dword gameidCreate_; + PackId packidCreate_; + dword id_; +}; + +STARTLABEL(XtsLabels) + LABEL(XTransport::XTS_CLOSED) + LABEL(XTransport::XTS_CONNECTING) + LABEL(XTransport::XTS_CONNECTED) + LABEL(XTransport::XTS_HANDSHAKING) + LABEL(XTransport::XTS_HANDSHAKEERROR) + LABEL(XTransport::XTS_HANDSHAKESUCCESS) + LABEL(XTransport::XTS_OPEN) + LABEL(XTransport::XTS_LOGGINGIN) + LABEL(XTransport::XTS_LOGINFAILED) + LABEL(XTransport::XTS_LOGGEDIN) + LABEL(XTransport::XTS_SIGNINGOUT) + LABEL(XTransport::XTS_SIGNOUTFAILED) + LABEL(XTransport::XTS_SIGNEDOUT) + LABEL(XTransport::XTS_JOINLOBBY) + LABEL(XTransport::XTS_JOINLOBBYFAILED) + LABEL(XTransport::XTS_JOINLOBBYSUCCESS) + LABEL(XTransport::XTS_LOBBY) + LABEL(XTransport::XTS_CREATINGROOM) + LABEL(XTransport::XTS_CREATEROOMERROR) + LABEL(XTransport::XTS_CREATEROOMSUCCESS) + LABEL(XTransport::XTS_CANJOINROOM) + LABEL(XTransport::XTS_JOININGROOM) + LABEL(XTransport::XTS_JOINROOMERROR) + LABEL(XTransport::XTS_JOINROOMSUCCESS) + LABEL(XTransport::XTS_ROOM) + LABEL(XTransport::XTS_CREATINGGAME) + LABEL(XTransport::XTS_CREATEGAMEERROR) + LABEL(XTransport::XTS_CREATEGAMESUCCESS) + LABEL(XTransport::XTS_CANJOINGAME) + LABEL(XTransport::XTS_LEAVINGROOM) + LABEL(XTransport::XTS_JOININGGAME) + LABEL(XTransport::XTS_JOINGAMEERROR) + LABEL(XTransport::XTS_JOINGAMESUCCESS) + LABEL(XTransport::XTS_GAME) +ENDLABEL(XtsLabels) + +} // namespace wi + +#endif // __XTRANSPORT_H__ diff --git a/inc/AdnDebugMgr.h b/inc/AdnDebugMgr.h new file mode 100644 index 0000000..25f09d6 --- /dev/null +++ b/inc/AdnDebugMgr.h @@ -0,0 +1,454 @@ +/****************************************************************************** + * + * Copyright (c) 2003 PalmSource, Inc. All rights reserved. + * + * File: AdnDebugMgr.h + * + * Release: DebugNub for Palm OS 5.x Native ARM debugging + * + * Description: + * API used to communicate with the Palm OS 5.x DebugNub. + * + *****************************************************************************/ + +#ifndef __ADNDEBUGMGR_H__ +#define __ADNDEBUGMGR_H__ + +// Uses unsigned shorts and longs to avoid needing PalmTypes.h for UInt32 typedef + +// Define creator ID and featureNum used by DebugNub for Palm OS 5.x +#define adnFtrCreator 'adbg' +#define adnFtrNumVersion 0 + + +/*********************************************************************** + * Palm OS SWI-related Definitions (largely from future Palm OS PUD.h) + ***********************************************************************/ + +// Generic Semihosting SWI op-codes (non-exhaustive list) +#define kAdnSemihostArmWrite0 0x0004 // Write a C-string to stdout +#define kAdnSemihostArmReportException 0x0018 // Angel SWI reason report exception + +// Palm-specific Semihosting SWI op-codes (non-exhaustive list) +#define kAdnSemihostPUDConnect 0x0101 +#define kAdnSemihostPUDProcessCreate 0x0110 +#define kAdnSemihostPUDProcessDestroy 0x0111 +#define kAdnSemihostPUDThreadCreate 0x0120 +#define kAdnSemihostPUDThreadDestroy 0x0121 +#define kAdnSemihostPUDModulePostLoad 0x0130 +#define kAdnSemihostPUDModulePostUnload 0x0131 +#define kAdnSemihostPUDDebugBreak 0x0132 +#define kAdnSemihostPUDFaultNotification 0x0133 +#define kAdnSemihostPUDModuleTableUpdate 0x0134 +#define kAdnSemihostPUDDownloadEvent 0x0140 +#define kAdnSemihostPUDProfilerEvent 0x0141 + +// Palm Adn-specific Semihosting SWI op-codes +#define kAdnSemihostNativeRegister 0x0150 +#define kAdnSemihostNativeUnregister 0x0151 +#define kAdnSemihostDebugEnableSet 0x0152 +#define kAdnSemihostDebugEnableGet 0x0153 +#define kAdnSemihostLicenseeSpecific 0x0154 +#define kAdnSemihostDebugEnableGetSupported 0x0155 + + +/*********************************************************************** + * Features to be enabled and disabled + ***********************************************************************/ + +#define kAdnEnableMasterSwitch 0x00000001 // [off] Master switch. Nothing works with this off, except + // AdnDebugEnableSet/Get() which is used to turn it on. + // + // With the master switch turned ON, the nub will: + // - Catch (by entering the debugger) fatal (ARM) exceptions + // (illegal memory access, undefined instructions). + // - Enter the debugger if AdnDebugBreak() is called. Even + // if a debugger is not initially present, one can later + // be connected for "after the crash" debugging. + // [Some nub implementations may also:] + // - Catch ErrFatalDisplay() calls. Note: currently, won't + // catch 68K fatal alerts if you've dropped in the 68K + // debugger at least once since the last reset. + // + // In addition, if the nub has already communicated with a + // debugger, then additional commands which otherwise required + // kAdnEnableFullDebugging will also be allowed. + +#define kAdnEnableDebugIndicator 0x00000002 // [on] Enable visual indicator that shows when the debugger + // nub is performing (or waiting for) serial communications. + +#define kAdnEnableFullDebugging 0x00000004 // [off] Allow FULL interactive debugging. + // - This MUST be turned ON in order for application calls to + // AdnDebugNativeRegister() to be processed correctly + // (i.e. sent to the debugger to register loaded PACE Native + // Object code for source level debugging). + // - If this option is off, then other native debugging calls + // (ModulePostLoad, ModulePostUnload, etc.) are also disabled. + +#define kAdnEnableShowSafeFatalAlerts 0x00000008 // [off] If on, don't catch calls to ErrFatalDisplay() + // iff the Reset/Debug/Continue options are displayed + // and we're pretty sure they'll work. [May not be supported.] + + +/*********************************************************************** + * Structure Definitions (largely from future Palm OS PUD.h) + ***********************************************************************/ +typedef struct DbgPostLoadParamsType { + unsigned long processID; + unsigned long moduleID; + void *codeAddr; + void *dataAddr; + unsigned long type; + unsigned long creator; + unsigned long rsrcType; + unsigned short rsrcID; + unsigned short reserved; +} DbgPostLoadParamsType; + +typedef struct DbgPostUnloadParamsType { + unsigned long processID; + unsigned long moduleID; +} DbgPostUnloadParamsType; + + +/*********************************************************************** + * Semihosting SWI number (from future Palm OS ARM.h) + ***********************************************************************/ +#define SEMI_SWI_ARM 0x123456L +#define SEMI_SWI_THUMB 0xAB + +#ifdef __thumb + #define SEMI_SWI_NUM SEMI_SWI_THUMB +#else + #define SEMI_SWI_NUM SEMI_SWI_ARM +#endif + + +/*********************************************************************** + * Palm OS SWI-related Prototypes + ***********************************************************************/ + +#if !defined(__arm) // Included in non-ARM build; silently ignore everything + + #define AdnGetNativeCodeBaseAddr() 0 + #define _SemihostOp0(op) + #define _SemihostOp0r(op) + #define _SemihostOp1(op,p1) + #define _SemihostOp3r(op,p1,p2,p3) + #define _SemihostWrite0(op, s) + #define _SemihostPostLoad(op, p) + #define _SemihostPostUnload(op, p) + + +#elif defined(__MWERKS__) // CodeWarrior for Palm OS R9.2 (beta 2 or greater) + + // This function must be defined for the current toolset, to retrieve and/or + // calculate the base address of our code resource. This function is called + // called by AdnDebugNativeRegister() which tells the desktop debugger where + // exactly in memory the code is located (to enable source level debugging). + // Ideally, we just reference the startup function, but unfortunately without + // PIC, that resolves to zero (it's not automatically relocated at runtime). + // Here is code that works for CodeWarrior with or without PIC support. + #if __option(PIC) // Post 9.2 - just reference it + extern + #ifdef __cplusplus + "C" + #endif + void __ARMlet_Startup__(void); + #define AdnGetNativeCodeBaseAddr() ((void *)__ARMlet_Startup__) + #else // otherwise, calculate it + #pragma thumb off + static void * AdnGetNativeCodeBaseAddr(void); + static asm void * AdnGetNativeCodeBaseAddr(void) + { + sub r0, pc, #8 // get real address of this function + lda r1, AdnGetNativeCodeBaseAddr // get zero-based offset to this function + sub r0, r0, r1 // subtract to get real base of code + bx lr // and return + } + #pragma thumb reset + #endif + + // These declarations work, but the functions don't get + // inlined as requested (and thus we need the "bx lr"). + // We could remove the "inline" directive but then we need function prototypes. + inline __asm void _SemihostOp0(unsigned op) + { swi SEMI_SWI_NUM; bx lr; } + + inline __asm unsigned _SemihostOp0r(unsigned op) + { swi SEMI_SWI_NUM; bx lr; } + + inline __asm void _SemihostOp1(unsigned op, unsigned param1) + { swi SEMI_SWI_NUM; bx lr; } + + inline __asm unsigned _SemihostOp3r(unsigned op, unsigned param1, unsigned param2, unsigned param3) + { swi SEMI_SWI_NUM; bx lr; } + + inline __asm void _SemihostWrite0(unsigned op, const char *string) + { swi SEMI_SWI_NUM; bx lr; } + + inline __asm void _SemihostPostLoad(unsigned op, const DbgPostLoadParamsType *) + { swi SEMI_SWI_NUM; bx lr; } + + inline __asm void _SemihostPostUnload(unsigned op, const DbgPostUnloadParamsType *) + { swi SEMI_SWI_NUM; bx lr; } + + // You might think that something like the following would work, but it doesn't. + // The SWI instruction does get inlined, but the required function parameters + // do not get properly moved into registers r0, r1, etc. + // inline /*asm*/ void _SemihostOp0(unsigned op) + // { asm { swi SEMI_SWI_NUM } } + + +#elif defined(__ARMCC_VERSION) // ARM ADS + + // Note: In order to debug ADS-generated PACE Native Objects, we must have + // a valid definition for the AdnGetNativeCodeBaseAddr() [see above]. + + // Since we don't currently support building in ADS, we force a build error + // if anyone attempts to call this (as yet undefined) function (which is + // called by AdnDebugNativeRegister(), below). If you need to use this in + // ADS, either fix this, or if you know your base address, you can instead + // call the AdnDebugNativeRegisterAddr() variation. + #define AdnGetNativeCodeBaseAddr() AdnThisShouldCauseABuildError() + + __swi(SEMI_SWI_NUM) void _SemihostOp0 (unsigned op); + __swi(SEMI_SWI_NUM) unsigned _SemihostOp0r (unsigned op); + __swi(SEMI_SWI_NUM) void _SemihostOp1 (unsigned op, unsigned param1); + __swi(SEMI_SWI_NUM) unsigned _SemihostOp3r (unsigned op, unsigned param1, unsigned param2, unsigned param3); + __swi(SEMI_SWI_NUM) void _SemihostWrite0 (unsigned op, const char *string); + __swi(SEMI_SWI_NUM) void _SemihostPostLoad (unsigned op, const DbgPostLoadParamsType *); + __swi(SEMI_SWI_NUM) void _SemihostPostUnload (unsigned op, const DbgPostUnloadParamsType *); + + +#elif defined(__GNUC__) // gcc + + // Note: In order to debug gcc-generated PACE Native Objects, we must + // either come up with a valid definition for the AdnGetNativeCodeBaseAddr() + // function [see above] or else PNO code will have to use the alternate + // registration function, AdnDebugNativeRegisterAddr(), instead of the + // usual AdnDebugNativeRegister(). In order to do so, the PNO must be + // passed-in the base address of its code resource from the 68K side of + // the world, and that value can then in turn be registered with POD. + + // preprocessor nastiness + #define WRAP(x) #x + #define PUT_QUOTES_AROUND(x) WRAP(x) + #define SWI_NUM_WITH_QUOTES_AROUND_IT PUT_QUOTES_AROUND(SEMI_SWI_NUM) + + inline static void _SemihostOp0(unsigned op) + { asm("swi " SWI_NUM_WITH_QUOTES_AROUND_IT); } + + inline static unsigned _SemihostOp0r(unsigned op) + { asm("swi " SWI_NUM_WITH_QUOTES_AROUND_IT); } + + inline static void _SemihostOp1(unsigned op, unsigned param1) + { asm("swi " SWI_NUM_WITH_QUOTES_AROUND_IT); } + + inline static unsigned _SemihostOp3r(unsigned op, unsigned param1, unsigned param2, unsigned param3) + { asm("swi " SWI_NUM_WITH_QUOTES_AROUND_IT); } + + inline static void _SemihostWrite0(unsigned op, const char *string) + { asm("swi " SWI_NUM_WITH_QUOTES_AROUND_IT); } + + inline static void _SemihostPostLoad(unsigned op, const DbgPostLoadParamsType *params) + { asm("swi " SWI_NUM_WITH_QUOTES_AROUND_IT); } + + inline static void _SemihostPostUnload(unsigned op, const DbgPostUnloadParamsType *params) + { asm("swi " SWI_NUM_WITH_QUOTES_AROUND_IT); } + + +#else // Unrecognized ARM compiler; don't know how to do a SWI, so punt safely... + + // This function must be defined for the current toolset, to retrieve and/or + // calculate the base address of our code resource. This function is called + // called by AdnDebugNativeRegister() which tells the desktop debugger where + // exactly in memory the code is located (to enable source level debugging). + #define AdnGetNativeCodeBaseAddr() AdnThisShouldCauseABuildError() + + // The preferred solution is to just reference the startup function, but if + // that isn't relocated at runtime then something like the following can be used. +// #pragma thumb off +// static void * AdnGetNativeCodeBaseAddr(void); +// static asm void * AdnGetNativeCodeBaseAddr(void) +// { +// sub r0, pc, #8 // get real address of this function +// lda r1, AdnGetNativeCodeBaseAddr // get zero-based offset to this function +// sub r0, r0, r1 // subtract to get real base of code +// bx lr // and return +// } +// #pragma thumb reset + + // To support other compilers, inline SWI calls must be implemented: + // (The AdnDebugMgr APIs should not be called for non-ARM builds.) + #define _SemihostOp0(op) AdnThisShouldCauseABuildError() + #define _SemihostOp0r(op) AdnThisShouldCauseABuildError() + #define _SemihostOp1(op,p1) AdnThisShouldCauseABuildError() + #define _SemihostOp3r(op,p1,p2,p3) AdnThisShouldCauseABuildError() + #define _SemihostWrite0(op, s) AdnThisShouldCauseABuildError() + #define _SemihostPostLoad(op, p) AdnThisShouldCauseABuildError() + #define _SemihostPostUnload(op, p) AdnThisShouldCauseABuildError() + +#endif + + +/*********************************************************************** + * FUNCTION: AdnDebugEnableSet + * DESCRIPTION: Enable debugger nub features (kAdnEnable*). + * PARAMETERS: Flags indicating which features to enable + * RETURNED: nothing + ***********************************************************************/ +#define AdnDebugEnableSet(flags) \ + _SemihostOp1(kAdnSemihostDebugEnableSet, flags) + + +/*********************************************************************** + * FUNCTION: AdnDebugEnableGet + * DESCRIPTION: Get enabled debugger nub features (kAdnEnable*). + * PARAMETERS: none + * RETURNED: Flags indicating which features are enabled + ***********************************************************************/ +#define AdnDebugEnableGet() \ + _SemihostOp0r(kAdnSemihostDebugEnableGet) + + +/*********************************************************************** + * FUNCTION: AdnDebugEnableGetSupported + * DESCRIPTION: Get supported debugger nub features (kAdnEnable*). + * PARAMETERS: none + * RETURNED: Flags indicating which features are supported + ***********************************************************************/ +#define AdnDebugEnableGetSupported() \ + _SemihostOp0r(kAdnSemihostDebugEnableGetSupported) + + +/*********************************************************************** + * FUNCTION: AdnDebugLicenseeSpecific + * DESCRIPTION: Make licensee-specific call to ArmDebugNub. + * PARAMETERS: oemID - PalmSource-registered OEM ID (creator code) + * selector - Licensee-specific function selector + * param - Function-specific parameter + * RETURNED: result - Function-specific result + * (kAdnErrUnsupportedCall == not supported) + ***********************************************************************/ +#define kAdnErrUnsupportedCall 0xFFFFFFFF + +#define AdnDebugLicenseeSpecific(oemID, selector, param) \ + _SemihostOp3r(kAdnSemihostLicenseeSpecific, oemID, selector, param) + + + +/*********************************************************************** + * FUNCTION: AdnDebugMessage + * DESCRIPTION: Display a debug message in the desktop debugger. + * PARAMETERS: messageP - pointer to null-terminated string to display + * RETURNED: nothing + ***********************************************************************/ +#define AdnDebugMessage(messageP) \ + _SemihostWrite0(kAdnSemihostArmWrite0, messageP) + +#define AdnDebugMessageIf(condition, messageP) \ + do {if (condition) AdnDebugMessage(messageP);} while (0) + + +/*********************************************************************** + * FUNCTION: AdnDebugBreak + * DESCRIPTION: Break into the desktop debugger. + * PARAMETERS: none + * RETURNED: nothing + ***********************************************************************/ +#define AdnDebugBreak() \ + _SemihostOp0(kAdnSemihostPUDDebugBreak) + + + +/*********************************************************************** + * FUNCTION: AdnDebugUpdateLoadedModules + * DESCRIPTION: Notify debugger of any recently loaded/unloaded modules. + * PARAMETERS: none + * RETURNED: nothing + ***********************************************************************/ +#define AdnDebugUpdateLoadedModules() \ + _SemihostOp0(kAdnSemihostPUDModuleTableUpdate) + + +/*********************************************************************** + * FUNCTION: AdnDebugNativeRegisterAddr + * DESCRIPTION: Ask debugger to register PACE Native Object. This is + * useful in the case where the code making this call + * needs to register Native code that lives elsewhere (e.g. + * in a different chunk), and thus AdnGetNativeCodeBaseAddr() + * won't retrieve the correct code base address. Generally, + * PACE Native Objects which are registering themselves should + * use the simpler form, AdnDebugNativeRegister(), below. + * PARAMETERS: dbType - application database type (e.g. 'appl') + * dbCreator - application database creator code + * rsrcType - PACE Native Object resource type (e.g. 'ARMC') + * rsrcID - PACE Native Object resource ID + * codeAddr - PACE Native Object code base address + * RETURNED: nothing + ***********************************************************************/ +#define AdnDebugNativeRegisterAddr(_dbType, _dbCreator, _rsrcType, _rsrcID, _codeAddr) \ + { \ + DbgPostLoadParamsType _postLoadParams; \ + _postLoadParams.processID = 0; \ + _postLoadParams.moduleID = (unsigned long)_codeAddr /*sectionCookie*/; \ + _postLoadParams.codeAddr = _codeAddr; \ + _postLoadParams.dataAddr = 0; \ + _postLoadParams.type = _dbType; \ + _postLoadParams.creator = _dbCreator; \ + _postLoadParams.rsrcType = _rsrcType; \ + _postLoadParams.rsrcID = _rsrcID; \ + _postLoadParams.reserved = 0; \ + _SemihostPostLoad(kAdnSemihostNativeRegister, &_postLoadParams); \ + } + + +/*********************************************************************** + * FUNCTION: AdnDebugNativeRegister + * DESCRIPTION: Ask debugger to register PACE Native Object. + * This should be done after locking the code. Any + * previously unresolved breakpoints will be activated. + * PARAMETERS: dbType - application database type (e.g. 'appl') + * dbCreator - application database creator code + * rsrcType - PACE Native Object resource type (e.g. 'ARMC') + * rsrcID - PACE Native Object resource ID + * RETURNED: nothing + ***********************************************************************/ +#define AdnDebugNativeRegister(_dbType, _dbCreator, _rsrcType, _rsrcID) \ + AdnDebugNativeRegisterAddr(_dbType, _dbCreator, _rsrcType, _rsrcID, AdnGetNativeCodeBaseAddr()); + + +/*********************************************************************** + * FUNCTION: AdnDebugNativeUnregisterAddr + * DESCRIPTION: Ask debugger to unregister PACE Native Object. This is + * useful in the case where the code making this call + * needs to unregister Native code that lives elsewhere (e.g. + * in a different chunk), and thus AdnGetNativeCodeBaseAddr() + * won't retrieve the correct code base address. Generally, + * PACE Native Objects which are unregistering themselves should + * use the simpler form, AdnDebugNativeUnregister(), below. + * PARAMETERS: codeAddr - PACE Native Object code base address + * RETURNED: nothing + ***********************************************************************/ +#define AdnDebugNativeUnregisterAddr(_codeAddr) \ + { \ + DbgPostUnloadParamsType _postUnloadParams; \ + _postUnloadParams.processID = 0; \ + _postUnloadParams.moduleID = (unsigned long)_codeAddr /*sectionCookie*/; \ + _SemihostPostUnload(kAdnSemihostNativeUnregister, &_postUnloadParams); \ + } + +/*********************************************************************** + * FUNCTION: AdnDebugNativeUnregister + * DESCRIPTION: Ask debugger to unregister PACE Native Object. + * This should be done prior to unlocking the code since + * breakpoints in the code may need to be removed. + * PARAMETERS: none + * RETURNED: nothing + ***********************************************************************/ +#define AdnDebugNativeUnregister() \ + AdnDebugNativeUnregisterAddr(AdnGetNativeCodeBaseAddr()); + + +#endif // __ADNDEBUGMGR_H__ diff --git a/inc/BtIfClasses.h b/inc/BtIfClasses.h new file mode 100644 index 0000000..7d6b9f7 --- /dev/null +++ b/inc/BtIfClasses.h @@ -0,0 +1,1287 @@ +///////////////////////////////////////////////////////////////////////////// +// +// Name BtIfClasses.h +// $Header: +// +// Function this file contains Widcomm SDK class definitions +// +// Date Modification +// ---------------------------------- +// 12/17/2000 JF Create +// +// Copyright (c) 2000-2002, WIDCOMM Inc., All Rights Reserved. +// Proprietary and confidential. +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef _BTIFCLASSES_H +#define _BTIFCLASSES_H + +#ifdef WIDCOMMSDK_EXPORTS +#define WIDCOMMSDK __declspec(dllexport) +#else +#define WIDCOMMSDK __declspec(dllimport) +#endif + +#include "BtIfDefinitions.h" + +// Resolve forward references +// +class CSdpDiscoveryRec; + + +// Ensure alignment across all builds +// +#ifdef _WIN32_WCE //for CE only + #pragma pack (8) + #define BT_CHAR TCHAR + #define BT_CB_LPSTR LPCTSTR //char used in call back function +#else //for windows + #pragma pack (1) + #define BT_CHAR char + #define BT_CB_LPSTR WCHAR * //char used in call back function +#endif + +class CWBtAPI; +// +// Define status values that are returned in the "OnDeviceStatus" callback +// +#define BT_DEVST_DOWN 0 // Device is present, but down +#define BT_DEVST_UP 1 // Device is present and UP +#define BT_DEVST_ERROR 2 // Device is in error (maybe being removed) +#define BT_DEVST_UNLOADED 3 // Stack is being unloaded + +// Define master/slave role code for SwitchRole function +// +typedef enum +{ + NEW_MASTER, + NEW_SLAVE +} MASTER_SLAVE_ROLE; + + +// Define return code for Audio functions +// +typedef enum +{ + AUDIO_SUCCESS, + AUDIO_UNKNOWN_ADDR, //if the ACL connection is not up + AUDIO_BUSY, //if another SCO being set up to the same BD address + AUDIO_NO_RESOURCES, //if the max SCO limit has been reached + AUDIO_ALREADY_STARTED, //connection is already up. + AUDIO_UNKNOWN_ERROR, + AUDIO_INVALID_PARAM, + AUDIO_INVALID_HANDLE = 0xffff +} AUDIO_RETURN_CODE; + + +// +// Since CE seems to have some problems with dll unloading, +// define a function that can be called to shut down the SDK +// +WIDCOMMSDK void WIDCOMMSDK_ShutDown(void); + +//////////////////////////////////////////////////////////////////////////// +// +// Define a class for interfacing to the stack +// +class CBtIf_Impl; + +class WIDCOMMSDK CBtIf +{ +public: + CBtIf(); + virtual ~CBtIf(); + + // Define return code for Bond function + // + typedef enum + { + SUCCESS, + ALREADY_BONDED, + BAD_PARAMETER, + FAIL + } BOND_RETURN_CODE; + + typedef enum { + DISCOVERY_RESULT_SUCCESS, + DISCOVERY_RESULT_CONNECT_ERR, // Could not connect to remote device + DISCOVERY_RESULT_CONNECT_REJ, // Remote device rejected the connection + DISCOVERY_RESULT_SECURITY, // Security failed + DISCOVERY_RESULT_BAD_RECORD, // Remote Service Record Error + DISCOVERY_RESULT_OTHER_ERROR // Other error + } DISCOVERY_RESULT; + + typedef enum { + DEVST_DOWN, // Device is present, but down + DEVST_UP, // Device is present and UP + DEVST_ERROR, // Device is in error (maybe being removed) + DEVST_UNLOADED, // Stack is being unloaded + DEVST_RELOADED // Stack reloaded after being unloaded + } STACK_STATUS; + + // standard GUID values for common Bluetooth service classes + static const GUID guid_SERVCLASS_SERVICE_DISCOVERY_SERVER; + static const GUID guid_SERVCLASS_BROWSE_GROUP_DESCRIPTOR; + static const GUID guid_SERVCLASS_PUBLIC_BROWSE_GROUP; + static const GUID guid_SERVCLASS_SERIAL_PORT; + static const GUID guid_SERVCLASS_LAN_ACCESS_USING_PPP; + static const GUID guid_SERVCLASS_PANU; + static const GUID guid_SERVCLASS_NAP; + static const GUID guid_SERVCLASS_GN; + static const GUID guid_SERVCLASS_DIALUP_NETWORKING; + static const GUID guid_SERVCLASS_IRMC_SYNC; + static const GUID guid_SERVCLASS_OBEX_OBJECT_PUSH; + static const GUID guid_SERVCLASS_OBEX_FILE_TRANSFER; + static const GUID guid_SERVCLASS_IRMC_SYNC_COMMAND; + static const GUID guid_SERVCLASS_HEADSET; + static const GUID guid_SERVCLASS_CORDLESS_TELEPHONY; + static const GUID guid_SERVCLASS_INTERCOM; + static const GUID guid_SERVCLASS_FAX; + static const GUID guid_SERVCLASS_HEADSET_AUDIO_GATEWAY; + static const GUID guid_SERVCLASS_PNP_INFORMATION; + static const GUID guid_SERVCLASS_GENERIC_NETWORKING; + static const GUID guid_SERVCLASS_GENERIC_FILETRANSFER; + static const GUID guid_SERVCLASS_GENERIC_AUDIO; + static const GUID guid_SERVCLASS_GENERIC_TELEPHONY; + static const GUID guid_SERVCLASS_BPP_PRINTING; + static const GUID guid_SERVCLASS_HCRP_PRINTING; + static const GUID guid_SERVCLASS_SPP_PRINTING; + +#ifdef _WIN32_WCE + typedef enum + { + CONNECT_ALLOW_NONE, + CONNECT_ALLOW_ALL, + CONNECT_ALLOW_PAIRED, + CONNECT_ALLOW_FILTERED + } CONNECT_ALLOW_TYPE; + +#endif + + // Application can use this function to start an inquiry. + BOOL StartInquiry(); + + // Application can use this function to stop an inquiry. + void StopInquiry(); + + // Application can use this function to start service discovery + BOOL StartDiscovery (BD_ADDR p_bda, GUID *p_service_guid); + + BOND_RETURN_CODE Bond(BD_ADDR bda, BT_CHAR* pin_code); + + // query if a device is bonded + BOOL BondQuery(BD_ADDR bda); + + // Remove Bonding + BOOL UnBond(BD_ADDR bda); + + //Crate AudioConnection + AUDIO_RETURN_CODE CreateAudioConnection(BD_ADDR bda, BOOL bIsClient, UINT16 *audioHandle); + + //Disconnect AudioConnection + static AUDIO_RETURN_CODE RemoveAudioConnection(UINT16 audioHandle); + + //audion callback functions + virtual void OnAudioConnected(UINT16 audioHandle){}; + virtual void OnAudioDisconnect(UINT16 audioHandle){}; + virtual void OnStackStatusChange(CBtIf::STACK_STATUS new_status) {} + virtual void OnInquiryComplete (BOOL success, short num_responses) {}// {} + virtual void OnDeviceResponded (BD_ADDR bda, DEV_CLASS devClass, BD_NAME bdName, BOOL bConnected) {} // = 0; + virtual void OnDiscoveryComplete () {}// = 0; + + // Application can use this function to get list of services on the remote device + int ReadDiscoveryRecords (BD_ADDR p_bda, int max_size, CSdpDiscoveryRec *p_list, GUID *p_guid_filter = NULL); + + // application can use this function from within the OnDiscoveryComplete callback + // to find out the discovery results + CBtIf::DISCOVERY_RESULT GetLastDiscoveryResult(BD_ADDR p_bda, UINT16 *p_num_recs); + + // server should call this method to switch role to master if + // they want to accept multiple connections + static BOOL SwitchRole(BD_ADDR bd_addr, MASTER_SLAVE_ROLE new_role); + + // sets the public variable m_BdAddr + BOOL GetLocalDeviceInfo(); + + //audio internal function + typedef void (tAudio_CB) (UINT16); + + //internal use + static AUDIO_RETURN_CODE CreateAudioConnection(BD_ADDR bda, BOOL bIsClient, UINT16 *audioHandle, + tAudio_CB *p_conn_cb, tAudio_CB *p_disc_cb); + + BOOL GetLocalServiceName(BT_CHAR *p_ServiceName, int bBuffLen); + BOOL GetNextLocalServiceName(BT_CHAR *p_ServiceName, int bBuffLen); + + static BOOL SetLinkSupervisionTimeOut(BD_ADDR BdAddr, UINT16 timeout); + + BD_ADDR m_BdAddr; // Bluetooth address of local device + +#ifdef _WIN32_WCE + BOOL RadioOn(); + BOOL RadioOff(); + + static AUDIO_RETURN_CODE ReadAudioData(void *pBuff, DWORD dwLen, DWORD *dwByteR); + static AUDIO_RETURN_CODE WriteAudioData(void *pBuff, DWORD dwLen, DWORD *dwByteW); + void AllowToConnect(CONNECT_ALLOW_TYPE ConnectType); + void SetDiscoverable(BOOL bDiscoverable); + BOOL ReloadStack(); +#endif +protected: + void SetOnDeviceStatusCallback(); + +private: + + CBtIf_Impl *m_pImpl; + + friend class CBtIfFriend; + friend class CBtIf_Impl; + + HANDLE GetMutexHandle(); + void SetMutexHandle(HANDLE hMutex); + + // This class will not support the compiler-supplied copy constructor or assignment operator, + // so these are declared private to prevent inadvertent use by the application. + CBtIf(const CBtIf & x); + CBtIf& operator= (const CBtIf & x); +}; + + +//////////////////////////////////////////////////////////////////////////// +// +// Define a class to control an L2CAP interface (for a specific PSM) +// +class CL2CapIf_Impl; + +class WIDCOMMSDK CL2CapIf +{ +public: + CL2CapIf(); + ~CL2CapIf(); + + // Server should call this method without any parameter + // to assign a new PSM value, or with a PSM value if it + // is using a fixed PSM. Client should call this method + // with PSM found from service discovery + // + BOOL AssignPsmValue (GUID *p_guid, UINT16 psm = 0); + + // Both client and server sides should call this function + // to register a PSM with L2CAP, once the PSM value is known + // + BOOL Register (); + + // Both client and server sides should call this function + // to de-register a PSM from L2CAP, when application is exiting + // + void Deregister (); + + // Both client and server MUST call this function to set + // the security level for connections on the assigned PSM. + // + BOOL SetSecurityLevel (BT_CHAR *p_service_name, UINT8 security_level, BOOL is_server); + + // Returns the PSM value currently in use. + // + UINT16 GetPsm() ; + +private: + friend class CL2CapConn; + friend class CL2CapIf_Impl; + CL2CapIf_Impl *m_pImpl; + + BOOL GetIsReg(); + // This class will not support the compiler-supplied copy constructor or assignment operator, + // so these are declared private to prevent inadvertent use by the application. + CL2CapIf(const CL2CapIf & x); + CL2CapIf& operator= (const CL2CapIf & x); +}; + + +//////////////////////////////////////////////////////////////////////////// +// +// Define a class to control the L2CAP connections (both client and server +// + +class Cl2CapConn_Impl; + +class WIDCOMMSDK CL2CapConn +{ +public: + + // Construction/destruction + // + CL2CapConn (); + virtual ~CL2CapConn(); + + // Public variables + // + // BOOL m_isCongested, Removed, please call GetCongested() + //UINT16 m_RemoteMtu; Removed, please call GetRemoteMtu() + //BD_ADDR m_RemoteBdAddr; Please call GetRemoteBdAddr() + + BOOL GetCongested(); + UINT16 GetRemoteMtu(); + void GetRemoteBdAddr(BD_ADDR bda); + + // Server should call this method to listen for an incoming + // connection. Client should not call this method. + // + BOOL Listen (CL2CapIf *p_if); + + // Server should call this method to switch role to master if + // it wants to accept multiple connections + // + BOOL SwitchRole(MASTER_SLAVE_ROLE new_role); + + BOOL SetLinkSupervisionTimeOut(UINT16 timeout); + + //Crate AudioConnection + AUDIO_RETURN_CODE CreateAudioConnection(BOOL bIsClient, UINT16 *audioHandle); + + //Disconnect AudioConnection + AUDIO_RETURN_CODE RemoveAudioConnection(UINT16 audioHandle); + + //audion callback functions + virtual void OnAudioConnected(UINT16 audioHandle){}; + virtual void OnAudioDisconnect(UINT16 audioHandle){}; + + // Server should call this method to accept an incoming + // connection, after he is notified of that connection. + // If anything other than the default MTU is desired, + // it should be passed as a parameter. + // + BOOL Accept (UINT16 desired_mtu = L2CAP_DEFAULT_MTU); + + // Server should call this method to reject an incoming + // connection, after he is notified of that connection. + // + BOOL Reject (UINT16 reason); + + // Client should call thi smethod to create a connection + // to a remote device. If anything other than the default + // MTU is desired, it should be passed as a parameter + // + BOOL Connect (CL2CapIf *p_if, BD_ADDR p_bd_addr, UINT16 desired_mtu = L2CAP_DEFAULT_MTU); + + // Client or server may call this function to reconfigure + // an existing connection. + // + BOOL Reconfigure (tL2CAP_CONFIG_INFO *p_cfg); + + // Client or server may call this function to disconnect + // an existing connection. + // + void Disconnect (void); + + // Client or server may call this function to send data to + // an existing connection. + // + BOOL Write (void *p_data, UINT16 length, UINT16 *p_len_written); + + // Get Current Connection Statistics + // + BOOL GetConnectionStats (tBT_CONN_STATS *p_conn_stats); + + // Server may provide a function to handle incoming connection + // notifications. Client should not. + // + virtual void OnIncomingConnection (); + + // Client may provide a function to handle connection pending + // notifications. + // + virtual void OnConnectPendingReceived (void) {} + + // Client and server may provide a method to be notified + // when a connection is established. + // + virtual void OnConnected() {} + + // Client and server may provide a method to be notified + // when data is received from the remote side. + // + virtual void OnDataReceived (void *p_data, UINT16 length) {} + + // Client and server may provide a method to be notified + // when a connection becomes congested or uncongested. + // + virtual void OnCongestionStatus (BOOL is_congested) {} + + // Client and server may provide a method to be notified + // when a connection is disconnected. + // + virtual void OnRemoteDisconnected (UINT16 reason) {} + +#ifdef _WIN32_WCE + AUDIO_RETURN_CODE ReadAudioData(void *pBuff, DWORD dwLen, DWORD *dwByteR); + AUDIO_RETURN_CODE WriteAudioData(void *pBuff, DWORD dwLen, DWORD *dwByteW); +#endif + + +private: + + static CL2CapConn *m_p_first_conn; + CL2CapConn *m_p_next_conn; + void SetIdle(); + + friend class CL2CapFriend; + friend class CL2CapConn_Impl; + CL2CapConn_Impl *m_pImpl; + + // This class will not support the compiler-supplied copy constructor or assignment operator, + // so these are declared private to prevent inadvertent use by the application. + CL2CapConn(const CL2CapConn & x); + CL2CapConn& operator= (const CL2CapConn & x); +}; + + + +//////////////////////////////////////////////////////////////////////////// +// +// Define a class to create and manage SDP service records +// +class CSdpService_Impl; + +class WIDCOMMSDK CSdpService +{ +public: + CSdpService(); + virtual ~CSdpService(); + + // This function adds a service class ID list to a service record. The service class ID + // list should be the first attribute added for a service record. + // + SDP_RETURN_CODE AddServiceClassIdList (int num_guids, GUID *p_service_guids); + + // This function adds a name field to a service record. + // +// SDP_RETURN_CODE AddServiceName (char *p_service_name); + SDP_RETURN_CODE AddServiceName (BT_CHAR *p_service_name);//CE defs + + // This function adds a profile descriptor list to a service record. + // + SDP_RETURN_CODE AddProfileDescriptorList (GUID *p_profile_guid, UINT16 version); + + // This function adds an L2CAP protocol descriptor list to a service record. + // + SDP_RETURN_CODE AddL2CapProtocolDescriptor (UINT16 psm); + + // This function adds an RFCOMM protocol descriptor list to a service record. + // + SDP_RETURN_CODE AddRFCommProtocolDescriptor (UINT8 scn); + + // This function adds a generic protocol descriptor list to a service record. + // It should be only needed if the specific RFCOMM and L2CAP functions above + // do not suffice. + // + SDP_RETURN_CODE AddProtocolList (int num_elem, tSDP_PROTOCOL_ELEM *p_elem_list); + + // This function adds the additional sequence of generic protocol descriptor lists to a service record. + // It should be only needed if the specific RFCOMM and L2CAP functions above + // do not suffice. + // + SDP_RETURN_CODE AddAdditionProtoLists (int num_list_elem, tSDP_PROTO_LIST_ELEM *p_proto_list); + + // This function adds a language base to a service record. + // + SDP_RETURN_CODE AddLanguageBaseAttrIDList (UINT16 lang, UINT16 char_enc, UINT16 base_id); + + // This function makes a service record public browsable. + // + SDP_RETURN_CODE MakePublicBrowseable (void); + + // This function sets the 'service availability' field of a service record. + // + SDP_RETURN_CODE SetAvailability (UINT8 availability); + + // This function adds an attribute to a service record. It is intended + // to be used to add some other attribute not covered by the existing + // functions. Note that the parameter should be in Big Endian order. + // + SDP_RETURN_CODE AddAttribute (UINT16 attr_id, UINT8 attr_type, UINT32 attr_len, UINT8 *p_val); + + // This function deletes an attribute from a service record. + // + SDP_RETURN_CODE DeleteAttribute (UINT16 attr_id); + + // This functions add a list (sequence) for the 'supported formats' attribute + // + SDP_RETURN_CODE AddSupportedFormatsList(UINT8 num_formats, + UINT8 pDataType[], UINT8 pDataTypeLength[], UINT8 *pDataTypeValue[]); + +private: + + friend class CSdpService_Impl; + CSdpService_Impl *m_pImpl; + + // This class will not support the compiler-supplied copy constructor or assignment operator, + // so these are declared private to prevent inadvertent use by the application. + CSdpService(const CSdpService & x); + CSdpService& operator= (const CSdpService & x); +}; + + +class CSdpDiscoveryRec_Impl; + +class WIDCOMMSDK CSdpDiscoveryRec +{ +public: + + CSdpDiscoveryRec(); + ~CSdpDiscoveryRec(); + + //char m_service_name[BT_MAX_SERVICE_NAME_LEN + 1]; + BT_CHAR m_service_name[BT_MAX_SERVICE_NAME_LEN + 1]; + + + GUID m_service_guid; + + BOOL FindRFCommScn (UINT8 *pScn); + + BOOL FindL2CapPsm (UINT16 *pPsm); + + BOOL FindProtocolListElem (UINT16 layer_uuid, tSDP_PROTOCOL_ELEM *p_elem); + + BOOL FindAdditionalProtocolListElem (UINT16 layer_uuid, tSDP_PROTOCOL_ELEM *p_elem); + + BOOL FindProfileVersion (GUID *p_profile_guid, UINT16 *p_version); + + BOOL FindAttribute (UINT16 attr_id, SDP_DISC_ATTTR_VAL *p_val); + +private: + + friend class CBtIf; + friend class CSdpDiscoveryRec_Impl; + CSdpDiscoveryRec_Impl *m_pImpl; + void Create(void *p); + // This class will not support the compiler-supplied copy constructor or assignment operator, + // so these are declared private to prevent inadvertent use by the application. + CSdpDiscoveryRec(const CSdpDiscoveryRec & x); + CSdpDiscoveryRec& operator= (const CSdpDiscoveryRec & x); +}; + + +//////////////////////////////////////////////////////////////////////////// +// +// Define a class to control the RfComm interface +// +class CRfCommIf_Impl; + +class WIDCOMMSDK CRfCommIf +{ +public: + CRfCommIf(); + ~CRfCommIf(); + + // Server should call this method without any parameter + // to assign a new SCN value, or with a SCN value if it + // is using a fixed SCN. Client should call this method + // with SCN found from service discovery + // + BOOL AssignScnValue (GUID *p_service_guid, UINT8 scn = 0); + + // Returns the SCN value currently in use. + // + UINT8 GetScn(); + + // Both client and server MUST call this function to set + // the security level for connections on the assigned SCN. + // + BOOL SetSecurityLevel (BT_CHAR *p_service_name, UINT8 security_level, BOOL is_server); + + // Server should call this method to switch role to master if + // it wants to accept multiple connections + // + BOOL SwitchRole(MASTER_SLAVE_ROLE new_role); + +private: + friend class CRfCommIf_Impl; + CRfCommIf_Impl *m_pImpl; + + // This class will not support the compiler-supplied copy constructor or assignment operator, + // so these are declared private to prevent inadvertent use by the application. + CRfCommIf(const CRfCommIf & x); + CRfCommIf& operator= (const CRfCommIf & x); +}; + + + +/////////////////////////////////////////////////////////////////////////////////////// +// Define a class to control the RFCOMM connections (both client and server) +// +class CRfCommPort_Impl; + +class WIDCOMMSDK CRfCommPort +{ +public: + + // Construction/destruction + // + CRfCommPort (); + virtual ~CRfCommPort(); +// +// Define return code for RFCOMM port functions +// +typedef enum +{ + SUCCESS, + UNKNOWN_ERROR, + ALREADY_OPENED, // Client tried to open port to existing DLCI/BD_ADDR + NOT_OPENED, // Function called before conn opened, or after closed + LINE_ERR, // Line error + START_FAILED, // Connection attempt failed + PAR_NEG_FAILED, // Parameter negotiation failed, currently only MTU + PORT_NEG_FAILED, // Port negotiation failed + PEER_CONNECTION_FAILED, // Connection ended by remote side + PEER_TIMEOUT, + INVALID_PARAMETER + +} PORT_RETURN_CODE; + + + // Open the RFComm serial port as a server (i.e. listen for + // remote side to connect to us). + // + PORT_RETURN_CODE OpenServer (UINT8 scn, UINT16 desired_mtu = RFCOMM_DEFAULT_MTU); + + // Open the RFComm serial port as a client (i.e. initiate + // the connection). + // + PORT_RETURN_CODE OpenClient (UINT8 scn, BD_ADDR RemoteBdAddr, UINT16 desired_mtu = RFCOMM_DEFAULT_MTU); + + // Close the RFComm serial port + // + PORT_RETURN_CODE Close(void); + + // Check if connection is up, and if so to whom + // + BOOL IsConnected (BD_ADDR *p_remote_bdaddr); + + // Set port flow control - application level, not low level + // + PORT_RETURN_CODE SetFlowEnabled (BOOL enabled); + + // Set control leads see BtIfDefinitions.h + // + PORT_RETURN_CODE SetModemSignal (UINT8 signal); + + // Get control lead status + // + PORT_RETURN_CODE GetModemStatus (UINT8 *p_signal); + + // Send an error to the peer + // + PORT_RETURN_CODE SendError (UINT8 errors); + + // Purge transmit queue + // + PORT_RETURN_CODE Purge (UINT8 purge_flags); + + // Write data + // + PORT_RETURN_CODE Write (void *p_data, UINT16 len_to_write, UINT16 *p_len_written); + + // Get Current Connection Statistics + // + PORT_RETURN_CODE GetConnectionStats (tBT_CONN_STATS *p_conn_stats); + + // App may provide these functions + // + virtual void OnDataReceived (void *p_data, UINT16 len) {} + virtual void OnEventReceived (UINT32 event_code) {} + virtual void OnModemSignalChanged (UINT8 signals) {} + virtual void OnFlowEnabled (BOOL enabled) {} + + BOOL SwitchRole(MASTER_SLAVE_ROLE new_role); + + //Crate AudioConnection + AUDIO_RETURN_CODE CreateAudioConnection(BOOL bIsClient, UINT16 *audioHandle); + + //Disconnect AudioConnection + AUDIO_RETURN_CODE RemoveAudioConnection(UINT16 audioHandle); + + //audion callback functions + virtual void OnAudioConnected(UINT16 audioHandle){}; + virtual void OnAudioDisconnect(UINT16 aidioHandle){}; + + PORT_RETURN_CODE SetLinkSupervisionTimeOut(UINT16 timeout); + +#ifdef _WIN32_WCE + AUDIO_RETURN_CODE ReadAudioData(void *pBuff, DWORD dwLen, DWORD *dwByteR); + AUDIO_RETURN_CODE WriteAudioData(void *pBuff, DWORD dwLen, DWORD *dwByteW); +#endif + +private: + + static CRfCommPort *m_p_first_port; + CRfCommPort *m_p_next_port; + + friend class CRfCommFriend; + friend class CRfCommPort_Impl; + CRfCommPort_Impl *m_pImpl; + + // This class will not support the compiler-supplied copy constructor or assignment operator, + // so these are declared private to prevent inadvertent use by the application. + CRfCommPort(const CRfCommPort & x); + CRfCommPort& operator= (const CRfCommPort & x); +}; + +/////////////////////////////////////////////////////////////////////////////////////// +// Define a class to control the FTP client sessions +// +class CFtpClient_Impl; + +class WIDCOMMSDK CFtpClient +{ + +public: + +// +// Define return code for FTP Client functions +// +typedef enum +{ + SUCCESS, // Operation initiated without error + OUT_OF_MEMORY, // Not enough memory to initiate operation + SECURITY_ERROR, // Error implementing requested security level + FTP_RETURN_ERROR, // FTP-specific error + NO_BT_SERVER, // cannot access the local Bluetooth COM server + ALREADY_CONNECTED, // Only one connection at a time supported for each instantiated CFtpClient object + NOT_OPENED, // Connection must be opened before requesting this operation + UNKNOWN_RETURN_ERROR // Any condition other than the above +} FTP_RETURN_CODE; + +// +// Define return code for FTP response functions +// +typedef enum +{ + COMPLETED, // operation completed without error + BAD_ADDR, // bad BD_ADDR + FILE_EXISTS, // file already exists + BAD_STATE, // could not handle request in present state + BAD_REQUEST, // invalid request + NOT_FOUND, // no such file + NO_SERVICE, // could not find the specified FTP server + DISCONNECT, // connection lost + READ, // read error + WRITE, // write error + OBEX_AUTHEN, // OBEX Authentication required + DENIED, // request could not be honored + DATA_NOT_SUPPORTED, // server does not support the requested data + CONNECT, // error establishing connection + PERMISSIONS, // incorrect file or service permissions + NOT_INITIALIZED, // not initialized + PARAM, // invalid parameter + RESOURCES, // out of file system resources (handles, disk space, etc) + SHARING, // sharing violation + UNKNOWN_RESULT_ERROR // Any condition other than the above +} FTP_RESULT_CODE; + +// FTP +enum FtpFolder +{ + FTP_ROOT_FOLDER = 1, + FTP_PARENT_FOLDER, + FTP_SUBFOLDER +}; +typedef enum FtpFolder tFtpFolder; + + // Construction/destruction + // + CFtpClient (); + virtual ~CFtpClient(); + + FTP_RETURN_CODE OpenConnection (BD_ADDR bdAddr, CSdpDiscoveryRec & sdp_rec); + FTP_RETURN_CODE CloseConnection(); + FTP_RETURN_CODE PutFile(WCHAR * localFileName); + FTP_RETURN_CODE GetFile(WCHAR * remoteFileName, WCHAR * localFolder); + FTP_RETURN_CODE FolderListing(); + FTP_RETURN_CODE ChangeFolder(WCHAR * szFolder); + FTP_RETURN_CODE DeleteFile(WCHAR * szFile); + FTP_RETURN_CODE Abort(); + FTP_RETURN_CODE Parent(); + FTP_RETURN_CODE Root(); + FTP_RETURN_CODE CreateEmpty(WCHAR * szFile); + FTP_RETURN_CODE CreateFolder(WCHAR * szFolder); + void SetSecurity(BOOL authentication, BOOL encryption); + + // Application may provide these functions to facilitate managing the connection + virtual void OnOpenResponse(FTP_RESULT_CODE result_code) {} + virtual void OnCloseResponse(FTP_RESULT_CODE result_code){} + + +//windows +#ifndef _WIN32_WCE + virtual void OnProgress(FTP_RESULT_CODE result_code, WCHAR * name, long current, long total) {} + virtual void OnPutResponse(FTP_RESULT_CODE result_code, WCHAR * name) {} + virtual void OnGetResponse(FTP_RESULT_CODE result_code, WCHAR * name) {} + virtual void OnCreateResponse(FTP_RESULT_CODE result_code, WCHAR * name) {} + virtual void OnDeleteResponse(FTP_RESULT_CODE result_code, WCHAR * name){} + + virtual void OnChangeFolderResponse(FTP_RESULT_CODE result_code, tFtpFolder folder_type, WCHAR * szFolder) {} + virtual void OnFolderListingResponse(FTP_RESULT_CODE result_code, tFTP_FILE_ENTRY * listing, long entries) {} + virtual void OnXmlFolderListingResponse(FTP_RESULT_CODE rc, WCHAR * pfolder_listing, long folder_length ){} +#else //for ce + virtual void OnProgress(FTP_RESULT_CODE result_code, LPCTSTR name, long current, long total) {} + virtual void OnPutResponse(FTP_RESULT_CODE result_code, LPCTSTR name) {} + virtual void OnGetResponse(FTP_RESULT_CODE result_code, LPCTSTR name) {} + virtual void OnCreateResponse(FTP_RESULT_CODE result_code, LPCTSTR name) {} + virtual void OnDeleteResponse(FTP_RESULT_CODE result_code, LPCTSTR name){} + + virtual void OnFolderListingResponse(FTP_RESULT_CODE result_code, tFTP_FILE_ENTRY * listing, BOOL last) {} + virtual void OnXmlFolderListingResponse(FTP_RESULT_CODE rc, WCHAR * pfolder_listing, long folder_length, BOOL last ){} + virtual void OnChangeFolderResponse(FTP_RESULT_CODE result_code, tFtpFolder folder_type, LPCTSTR szFolder) {} +#endif + + +virtual void OnAbortResponse(FTP_RESULT_CODE result_code) {} + +private: + + friend class CFtpClientFriend; + friend class CFtpClient_Impl; + CFtpClient_Impl *m_pImpl; + + // This class will not support the compiler-supplied copy constructor or assignment operator, + // so these are declared private to prevent inadvertent use by the application. + CFtpClient(const CFtpClient & x); + CFtpClient& operator= (const CFtpClient & x); +}; + + +/////////////////////////////////////////////////////////////////////////////////////// +// Define a class to control the OPP client sessions +// +class COppClient_Impl; + +class WIDCOMMSDK COppClient +{ +public: + +// +// Define return code for OPP Client functions +// +typedef enum +{ + OPP_CLIENT_SUCCESS, // Operation initiated without error + OUT_OF_MEMORY, // Not enough memory to initiate operation + SECURITY_ERROR, // Error implementing requested security level + OPP_ERROR, // OPP-specific error + ABORT_INVALID, // Abort not valid, no operation is in progress + UNKNOWN_ERROR // Any condition other than the above +} OPP_RETURN_CODE; + +// +// Define return code for OPP response functions +// +typedef enum +{ + COMPLETED, // operation completed without error + BAD_ADDR, // bad BD_ADDR + BAD_STATE, // could not handle request in present state + BAD_REQUEST, // invalid request + NOT_FOUND, // no such file + NO_SERVICE, // could not find the specified FTP server + DISCONNECT, // connection lost + READ, // read error + WRITE, // write error + OBEX_AUTH, // OBEX Authentication required + DENIED, // request could not be honored + DATA_NOT_SUPPORTED, // server does not support the requested data + CONNECT, // error establishing connection + NOT_INITIALIZED, // not initialized + PARAM, // bad parameter + BAD_INBOX, // inbox is not valid + BAD_NAME, // bad name for object + PERMISSIONS, // prohibited by file permissions + SHARING, // file is shared + RESOURCES, // file system resource limit reached - may be file handles, disk space, etc. + FILE_EXISTS, // is closedfile alto attempt to perform function after connectionready exists + UNKNOWN_RESULT_ERROR // Any condition other than the above +} OPP_RESULT_CODE; + +// +// Define return code for OPP response functions +// (NOTE: these values match the ones defined in \middleware\opp\oppapp.h) +// +typedef enum +{ + OPP_PUT_TRANS = 1, + OPP_GET_TRANS = 2, + OPP_EXCHANGE_PUT_TRANS = 3, + OPP_EXCHANGE_GET_TRANS = 4, + OPP_ABORT_TRANS = 5 + +} OPP_TRANSACTION_CODE; + +public: + + // Construction/destruction + // + COppClient (); + virtual ~COppClient(); + + OPP_RETURN_CODE Push(BD_ADDR bda, WCHAR * pszPathName, CSdpDiscoveryRec & sdp_rec); + OPP_RETURN_CODE Pull(BD_ADDR bda, WCHAR * pszPathName, CSdpDiscoveryRec & sdp_rec); + OPP_RETURN_CODE Exchange(BD_ADDR bda, WCHAR * pszName, WCHAR * pszFolder, CSdpDiscoveryRec & sdp_rec); + OPP_RETURN_CODE Abort(); + void SetSecurity(BOOL authentication, BOOL encryption); + + + virtual void OnAbortResponse (OPP_RESULT_CODE result_code) {} + +#ifndef _WIN32_WCE + virtual void OnProgress(OPP_RESULT_CODE result_code, BD_ADDR bda, WCHAR * string, long current, long total) {} + virtual void OnPushResponse(OPP_RESULT_CODE result_code, BD_ADDR bda, WCHAR * string) {} + virtual void OnPullResponse(OPP_RESULT_CODE result_code , BD_ADDR bda, WCHAR * string) {} + virtual void OnExchangeResponse(OPP_RESULT_CODE result_code, BD_ADDR bda, WCHAR * string) {} + virtual void OnExchangeResponse(OPP_RESULT_CODE result_code, BD_ADDR bda, WCHAR * string, OPP_TRANSACTION_CODE transaction_code) {} +#else //for CE + virtual void OnProgress(OPP_RESULT_CODE result_code, BD_ADDR bda, LPCTSTR string, long current, long total) {} + virtual void OnPushResponse(OPP_RESULT_CODE result_code, BD_ADDR bda, LPCTSTR string) {} + virtual void OnPullResponse(OPP_RESULT_CODE result_code , BD_ADDR bda, LPCTSTR string) {} + virtual void OnExchangeResponse(OPP_RESULT_CODE result_code, BD_ADDR bda, LPCTSTR string) {} + virtual void OnExchangeResponse(OPP_RESULT_CODE result_code, BD_ADDR bda, LPCTSTR string, OPP_TRANSACTION_CODE transaction_code) {} +#endif + +private: + friend class COppClientFriend; + friend class COppClient_Impl; + COppClient_Impl *m_pImpl; + + // This class will not support the compiler-supplied copy constructor or assignment operator, + // so these are declared private to prevent inadvertent use by the application. + COppClient(const COppClient & x); + COppClient& operator= (const COppClient & x); +}; + +/////////////////////////////////////////////////////////////////////////////////////// +// Define a class to control the LAP client sessions +// +class CLapClient_Impl; + +class WIDCOMMSDK CLapClient +{ +public: + +// +// Define return code for LAP Client functions +// +typedef enum +{ + SUCCESS, // Operation initiated without error + NO_BT_SERVER, // COM server could not be started + ALREADY_CONNECTED, // attempt to connect before previous connection closed + NOT_CONNECTED, // attempt to close unopened connection + NOT_ENOUGH_MEMORY, // local processor could not allocate memory for open + UNKNOWN_ERROR, // Any condition other than the above + INVALID_PARAMETER // One or more of function parameters are not valid +} LAP_RETURN_CODE; + +// +// Define connection states +// +typedef enum +{ + LAP_CONNECTED, // port now connected + LAP_DISCONNECTED // port now disconnected +} LAP_STATE_CODE; + + +public: + + // Construction/destruction + // + CLapClient (); + virtual ~CLapClient(); + + LAP_RETURN_CODE CreateConnection(BD_ADDR bda, GUID guid, CSdpDiscoveryRec & sdp_rec); + LAP_RETURN_CODE CreateConnection(BD_ADDR bda, CSdpDiscoveryRec & sdp_rec); + LAP_RETURN_CODE RemoveConnection(); + void SetSecurity(BOOL authentication, BOOL encryption); + LAP_RETURN_CODE GetConnectionStats (tBT_CONN_STATS *p_conn_stats); + + virtual void OnStateChange(BD_ADDR bda, DEV_CLASS dev_class, BD_NAME name, short com_port, LAP_STATE_CODE state) = 0; + +private: + friend class CLapClientFriend; + friend class CLapClient_Impl; + CLapClient_Impl *m_pImpl; + + // This class will not support the compiler-supplied copy constructor or assignment operator, + // so these are declared private to prevent inadvertent use by the application. + CLapClient(const CLapClient & x); + CLapClient& operator= (const CLapClient & x); +}; + +/////////////////////////////////////////////////////////////////////////////////////// +// Define a class to control the DUN client sessions +// +class CDunClient_Impl; + +class WIDCOMMSDK CDunClient +{ +public: + +// +// Define return code for DUN Client functions +// +typedef enum +{ + SUCCESS, // Operation initiated without error + NO_BT_SERVER, // COM server could not be started + ALREADY_CONNECTED, // attempt to connect before previous connection closed + NOT_CONNECTED, // attempt to close unopened connection + NOT_ENOUGH_MEMORY, // local processor could not allocate memory for open + UNKNOWN_ERROR, // Any condition other than the above + INVALID_PARAMETER // One or more of function parameters are not valid +} DUN_RETURN_CODE; + +// +// Define connection states +// +typedef enum +{ + DUN_CONNECTED, // port now connected + DUN_DISCONNECTED // port now disconnected +} DUN_STATE_CODE; + + +public: + + // Construction/destruction + // + CDunClient (); + virtual ~CDunClient(); + + DUN_RETURN_CODE CreateConnection(BD_ADDR bda, CSdpDiscoveryRec & sdp_rec); + DUN_RETURN_CODE RemoveConnection(); + void SetSecurity(BOOL authentication, BOOL encryption); + DUN_RETURN_CODE GetConnectionStats (tBT_CONN_STATS *p_conn_stats); + + virtual void OnStateChange(BD_ADDR bda, DEV_CLASS dev_class, BD_NAME name, short com_port, DUN_STATE_CODE state) = 0; + +private: + friend class CDunClientFriend; + friend class CDunClient_Impl; + CDunClient_Impl *m_pImpl; + + // This class will not support the compiler-supplied copy constructor or assignment operator, + // so these are declared private to prevent inadvertent use by the application. + CDunClient(const CDunClient & x); + CDunClient& operator= (const CDunClient & x); +}; + +/////////////////////////////////////////////////////////////////////////////////////// +// Define a class to control the SPP client sessions +// +class CSppClient_Impl; + +class WIDCOMMSDK CSppClient +{ +public: + +// +// Define return code for SPP Client functions +// +typedef enum +{ + SUCCESS, // Operation initiated without error + NO_BT_SERVER, // COM server could not be started + ALREADY_CONNECTED, // attempt to connect before previous connection closed + NOT_CONNECTED, // attempt to close unopened connection + NOT_ENOUGH_MEMORY, // local processor could not allocate memory for open + UNKNOWN_ERROR, // Any condition other than the above + INVALID_PARAMETER // One or more of function parameters are not valid +} SPP_CLIENT_RETURN_CODE; + + +public: + + // Construction/destruction + // + CSppClient (); + virtual ~CSppClient(); + + SPP_CLIENT_RETURN_CODE CreateConnection(BD_ADDR bda, BT_CHAR *szServiceName); + SPP_CLIENT_RETURN_CODE RemoveConnection(); + SPP_CLIENT_RETURN_CODE GetConnectionStats (tBT_CONN_STATS *p_conn_stats); + + virtual void OnClientStateChange(BD_ADDR bda, DEV_CLASS dev_class, BD_NAME name, short com_port, SPP_STATE_CODE state) = 0; + +private: + + friend class CSppClientFriend; + friend class CSppClient_Impl; + CSppClient_Impl *m_pImpl; + + // This class will not support the compiler-supplied copy constructor or assignment operator, + // so these are declared private to prevent inadvertent use by the application. + CSppClient(const CSppClient & x); + CSppClient& operator= (const CSppClient & x); +}; + + +/////////////////////////////////////////////////////////////////////////////////////// +// Define a class to control the SPP server sessions +// +class CSppServer_Impl; + +class WIDCOMMSDK CSppServer +{ +public: + +// +// Define return code for SPP Server functions +// +typedef enum +{ + SUCCESS, // Operation initiated without error + NO_BT_SERVER, // COM server could not be started + ALREADY_CONNECTED, // attempt to connect before previous connection closed + NOT_CONNECTED, // attempt to close unopened connection + NOT_ENOUGH_MEMORY, // local processor could not allocate memory for open + NOT_SUPPORTED, // requested service not available locally + UNKNOWN_ERROR, // Any condition other than the above + INVALID_PARAMETER // One or more of function parameters are not valid +} SPP_SERVER_RETURN_CODE; + +public: + + // Construction/destruction + // + CSppServer (); + virtual ~CSppServer(); + + SPP_SERVER_RETURN_CODE CreateConnection(BT_CHAR * szServiceName); + SPP_SERVER_RETURN_CODE RemoveConnection(); + virtual void OnServerStateChange(BD_ADDR bda, DEV_CLASS dev_class, BD_NAME name, short com_port, SPP_STATE_CODE state) = 0; + SPP_SERVER_RETURN_CODE GetConnectionStats (tBT_CONN_STATS *p_conn_stats); + +private: + friend class CSppServerFriend; + friend class CSppServer_Impl; + CSppServer_Impl *m_pImpl; + + // This class will not support the compiler-supplied copy constructor or assignment operator, + // so these are declared private to prevent inadvertent use by the application. + CSppServer(const CSppServer & x); + CSppServer& operator= (const CSppServer & x); +}; + + + +//////////////////////////////////////////////////////////////////////////// +// The CPrintClient class +// +//file types +#define TYPE_STRING_GIF _T("image/gif:89A") + +#define BPSF_TYPE 0x01 //Mask value + +struct BTPRINTSTRUCT + { + DWORD dwSize; // Must be sizeof(BTPRINTSTRUCT) + UINT mask; // 0 or BPSF_TYPE + LPCTSTR pszType; // Data type + }; + + +class CPrintInternal; +class CPrintClient_Impl; + +class WIDCOMMSDK CPrintClient +{ +public: + + // Define the profiles supported by the Printing SDK + // + typedef enum + { + PRINT_PROFILE_BPP, + PRINT_PROFILE_HCRP, + PRINT_PROFILE_SPP + + } ePRINT_PROFILE; + + // Define the current state of the Printing SDK + // + typedef enum + { + PRINT_STATE_IDLE, + PRINT_STATE_CONNECTING, + PRINT_STATE_PRINTING, + PRINT_STATE_FLOW_CONTROLLED, + PRINT_STATE_DISCONNECTING, + PRINT_STATE_DONE + + } ePRINT_STATE; + + // Define error codes returned by the Printing SDK + // + typedef enum + { + // Generic to all profiles + // + PRINT_RC_OK, + PRINT_RC_FILE_PRINTED_OK, + PRINT_RC_FILE_NOT_FOUND, + PRINT_RC_FILE_READ_ERROR, + PRINT_RC_ALREADY_PRINTING, + PRINT_RC_UNKNOWN_PROFILE, + PRINT_RC_SERVICE_NOT_FOUND, + PRINT_RC_SECURITY_ERROR, + PRINT_RC_CONNECT_ERROR, + PRINT_RC_WRITE_ERROR, + PRINT_RC_REMOTE_DISCONNECTED, + PRINT_RC_INVALID_PARAM, + + + // BPP Specific errors + // + PRINT_RC_BPP_SCN_NOT_FOUND, + PRINT_RC_BPP_SCN_NOT_ASSIGNED, + PRINT_RC_BPP_OBEX_ABORTED, + PRINT_RC_BPP_OBEX_MISMATCH, + + // HCRP Specific errors + // + PRINT_RC_HCRP_CTL_PSM_NOT_FOUND, + PRINT_RC_HCRP_DATA_PSM_NOT_FOUND, + + // SPP Specific errors + // + PRINT_RC_SPP_SCN_NOT_FOUND, + + } ePRINT_ERROR; + + public: + CPrintClient(); + ~CPrintClient(); + + ePRINT_ERROR Start (BD_ADDR pBDA, ePRINT_PROFILE eProfile, LPCTSTR pszFile, + BTPRINTSTRUCT * pBtPrintStruct = NULL); + + void Cancel(); + ePRINT_STATE GetState(); + UINT GetBytesSent(); + UINT GetBytesTotal(); + ePRINT_ERROR GetLastError(TCHAR **pDescr); + + virtual void OnStateChange (ePRINT_STATE NewState) { }; + static CPrintInternal *pIp; + + private: + friend class CPrintClient_Impl; + CPrintClient_Impl *m_pImpl; + +}; + + +#pragma pack () + + +#endif // !defined(AFX_WIDCOMMSDK_H__1F5ED990_6FC6_4B0D_882C_8D7C98C16A06__INCLUDED_) diff --git a/inc/BtIfClasses13.h b/inc/BtIfClasses13.h new file mode 100644 index 0000000..158774c --- /dev/null +++ b/inc/BtIfClasses13.h @@ -0,0 +1,1316 @@ +///////////////////////////////////////////////////////////////////////////// +// +// Name BtIfClasses.h +// $Header: +// +// Function this file contains Widcomm SDK class definitions +// +// Date Modification +// ---------------------------------- +// 12/17/2000 JF Create +// +// Copyright (c) 2000-2002, WIDCOMM Inc., All Rights Reserved. +// Proprietary and confidential. +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef _BTIFCLASSES_H +#define _BTIFCLASSES_H + +#ifdef WIDCOMMSDK_EXPORTS +#define WIDCOMMSDK __declspec(dllexport) +#else +#define WIDCOMMSDK __declspec(dllimport) +#endif + +#include "BtIfDefinitions13.h" + +// Resolve forward references +// +class CSdpDiscoveryRec; + + +// Ensure alignment across all builds +// +#ifdef _WIN32_WCE //for CE only + #pragma pack (8) + #define BT_CHAR TCHAR + #define BT_CB_LPSTR LPCTSTR //char used in call back function +#else //for windows + #pragma pack (1) + #define BT_CHAR char + #define BT_CB_LPSTR WCHAR * //char used in call back function +#endif + +class CWBtAPI; +// +// Define status values that are returned in the "OnDeviceStatus" callback +// +#define BT_DEVST_DOWN 0 // Device is present, but down +#define BT_DEVST_UP 1 // Device is present and UP +#define BT_DEVST_ERROR 2 // Device is in error (maybe being removed) +#define BT_DEVST_UNLOADED 3 // Stack is being unloaded + +// Define master/slave role code for SwitchRole function +// +typedef enum +{ + NEW_MASTER, + NEW_SLAVE +} MASTER_SLAVE_ROLE; + + +// Define return code for Audio functions +// +typedef enum +{ + AUDIO_SUCCESS, + AUDIO_UNKNOWN_ADDR, //if the ACL connection is not up + AUDIO_BUSY, //if another SCO being set up to the same BD address + AUDIO_NO_RESOURCES, //if the max SCO limit has been reached + AUDIO_ALREADY_STARTED, //connection is already up. + AUDIO_UNKNOWN_ERROR, + AUDIO_INVALID_HANDLE = 0xffff +} AUDIO_RETURN_CODE; + + +// +// Since CE seems to have some problems with dll unloading, +// define a function that can be called to shut down the SDK +// +WIDCOMMSDK void WIDCOMMSDK_ShutDown(void); + +//////////////////////////////////////////////////////////////////////////// +// +// Define a class for interfacing to the stack +// +class WIDCOMMSDK CBtIf +{ +public: + CBtIf(); + virtual ~CBtIf(); + + // Application can use this function to start an inquiry. + BOOL StartInquiry(); + + // Application can use this function to stop an inquiry. + void StopInquiry(); + + // Application can use this function to start service discovery + BOOL StartDiscovery (BD_ADDR p_bda, GUID *p_service_guid); + +// Define return code for Bond function +// +typedef enum +{ + SUCCESS, + ALREADY_BONDED, + BAD_PARAMETER, + FAIL +} BOND_RETURN_CODE; + + BOND_RETURN_CODE Bond(BD_ADDR bda, BT_CHAR* pin_code); + + // query if a device is bonded + BOOL BondQuery(BD_ADDR bda); + + // Remove Bonding + BOOL UnBond(BD_ADDR bda); + +#ifndef _WIN32_WCE + //Crate AudioConnection + AUDIO_RETURN_CODE CreateAudioConnection(BD_ADDR bda, BOOL bIsClient, UINT16 *audioHandle); + + //Disconnect AudioConnection + static AUDIO_RETURN_CODE RemoveAudioConnection(UINT16 audioHandle); + + //audion callback functions + virtual void OnAudioConnected(UINT16 audioHandle){}; + virtual void OnAudioDisconnect(UINT16 audioHandle){}; +#endif + + // Application can use this function to get list of services on the remote device + int ReadDiscoveryRecords (BD_ADDR p_bda, int max_size, CSdpDiscoveryRec *p_list, GUID *p_guid_filter = NULL); + + typedef enum { + DISCOVERY_RESULT_SUCCESS, + DISCOVERY_RESULT_CONNECT_ERR, // Could not connect to remote device + DISCOVERY_RESULT_CONNECT_REJ, // Remote device rejected the connection + DISCOVERY_RESULT_SECURITY, // Security failed + DISCOVERY_RESULT_BAD_RECORD, // Remote Service Record Error + DISCOVERY_RESULT_OTHER_ERROR // Other error + } DISCOVERY_RESULT; + + // application can use this function from within the OnDiscoveryComplete callback + // to find out the discovery results + CBtIf::DISCOVERY_RESULT GetLastDiscoveryResult(BD_ADDR p_bda, UINT16 *p_num_recs); + + // server should call this method to switch role to master if + // they want to accept multiple connections + static BOOL SwitchRole(BD_ADDR bd_addr, MASTER_SLAVE_ROLE new_role); + + + virtual void OnInquiryComplete (BOOL success, short num_responses) {}// {} + virtual void OnDeviceResponded (BD_ADDR bda, DEV_CLASS devClass, BD_NAME bdName, BOOL bConnected) {} // = 0; + virtual void OnDiscoveryComplete () {}// = 0; + + + // sets the public variable m_BdAddr + BOOL GetLocalDeviceInfo(); + +#ifndef _WIN32_WCE + typedef void (tAudio_CB) (UINT16); + static AUDIO_RETURN_CODE CreateAudioConnection(BD_ADDR bda, BOOL bIsClient, UINT16 *audioHandle, + tAudio_CB *p_conn_cb, tAudio_CB *p_disc_cb); +#endif + + +private: + CWBtAPI *m_pBtApi; +public: + typedef enum { + DEVST_DOWN, // Device is present, but down + DEVST_UP, // Device is present and UP + DEVST_ERROR, // Device is in error (maybe being removed) + DEVST_UNLOADED, // Stack is being unloaded + DEVST_RELOADED // Stack reloaded after being unloaded + } STACK_STATUS; + + virtual void OnStackStatusChange(CBtIf::STACK_STATUS new_status) {} + + // standard GUID values for common Bluetooth service classes + static const GUID guid_SERVCLASS_SERVICE_DISCOVERY_SERVER; + static const GUID guid_SERVCLASS_BROWSE_GROUP_DESCRIPTOR; + static const GUID guid_SERVCLASS_PUBLIC_BROWSE_GROUP; + static const GUID guid_SERVCLASS_SERIAL_PORT; + static const GUID guid_SERVCLASS_LAN_ACCESS_USING_PPP; + static const GUID guid_SERVCLASS_DIALUP_NETWORKING; + static const GUID guid_SERVCLASS_IRMC_SYNC; + static const GUID guid_SERVCLASS_OBEX_OBJECT_PUSH; + static const GUID guid_SERVCLASS_OBEX_FILE_TRANSFER; + static const GUID guid_SERVCLASS_IRMC_SYNC_COMMAND; + static const GUID guid_SERVCLASS_HEADSET; + static const GUID guid_SERVCLASS_CORDLESS_TELEPHONY; + static const GUID guid_SERVCLASS_INTERCOM; + static const GUID guid_SERVCLASS_FAX; + static const GUID guid_SERVCLASS_HEADSET_AUDIO_GATEWAY; + static const GUID guid_SERVCLASS_PNP_INFORMATION; + static const GUID guid_SERVCLASS_GENERIC_NETWORKING; + static const GUID guid_SERVCLASS_GENERIC_FILETRANSFER; + static const GUID guid_SERVCLASS_GENERIC_AUDIO; + static const GUID guid_SERVCLASS_GENERIC_TELEPHONY; + static const GUID guid_SERVCLASS_BPP_PRINTING; + static const GUID guid_SERVCLASS_HCRP_PRINTING; + static const GUID guid_SERVCLASS_SPP_PRINTING; + + BD_ADDR m_BdAddr; // Bluetooth address of local device + +protected: + void SetOnDeviceStatusCallback(); + BD_ADDR m_DiscoveryBdAddr; + UINT16 m_DiscoveryNumRecs; + DISCOVERY_RESULT m_DiscoveryResult; + +private: + BOOL m_bInquiryActive; + BOOL m_bDiscoveryActive; + + BOOL m_tempRegistry; + GUID m_TempRegistryGUID; + void DeleteTempRegistry(); + BOOL AddTempRegistry(GUID *p_GUID); + UINT16 m_audioHandle; + HANDLE m_hKrnl; + + friend class CBtIfFriend; + + // This class will not support the compiler-supplied copy constructor or assignment operator, + // so these are declared private to prevent inadvertent use by the application. + CBtIf(const CBtIf & x); + CBtIf& operator= (const CBtIf & x); +}; + + +//////////////////////////////////////////////////////////////////////////// +// +// Define a class to control an L2CAP interface (for a specific PSM) +// +class WIDCOMMSDK CL2CapIf +{ +public: + CL2CapIf(); + ~CL2CapIf(); + + // Server should call this method without any parameter + // to assign a new PSM value, or with a PSM value if it + // is using a fixed PSM. Client should call this method + // with PSM found from service discovery + // + BOOL AssignPsmValue (GUID *p_guid, UINT16 psm = 0); + + // Both client and server sides should call this function + // to register a PSM with L2CAP, once the PSM value is known + // + BOOL Register (); + + // Both client and server sides should call this function + // to de-register a PSM from L2CAP, when application is exiting + // + void Deregister (); + + // Both client and server MUST call this function to set + // the security level for connections on the assigned PSM. + // + BOOL SetSecurityLevel (BT_CHAR *p_service_name, UINT8 security_level, BOOL is_server); + + // Returns the PSM value currently in use. + // + inline UINT16 GetPsm() { return m_psm; } + +private: + UINT16 m_psm; + BOOL m_is_registered; + CL2CapIf *m_p_next_psm; + GUID m_service_guid; + UINT8 m_security_index; + + friend class CL2CapConn; + + // This class will not support the compiler-supplied copy constructor or assignment operator, + // so these are declared private to prevent inadvertent use by the application. + CL2CapIf(const CL2CapIf & x); + CL2CapIf& operator= (const CL2CapIf & x); +}; + + + + + +//////////////////////////////////////////////////////////////////////////// +// +// Define a class to control the L2CAP connections (both client and server +// +class WIDCOMMSDK CL2CapConn +{ +public: + + // Construction/destruction + // + CL2CapConn (); + virtual ~CL2CapConn(); + + // Public variables + // + BOOL m_isCongested; + UINT16 m_RemoteMtu; + BD_ADDR m_RemoteBdAddr; + + // Server should call this method to listen for an incoming + // connection. Client should not call this method. + // + BOOL Listen (CL2CapIf *p_if); + + // Server should call this method to switch role to master if + // it wants to accept multiple connections + // + BOOL SwitchRole(MASTER_SLAVE_ROLE new_role); + +#ifndef _WIN32_WCE + //Crate AudioConnection + AUDIO_RETURN_CODE CreateAudioConnection(BOOL bIsClient, UINT16 *audioHandle); + + //Disconnect AudioConnection + AUDIO_RETURN_CODE RemoveAudioConnection(UINT16 audioHandle); + + //audion callback functions + virtual void OnAudioConnected(UINT16 audioHandle){}; + virtual void OnAudioDisconnect(UINT16 audioHandle){}; +#endif + + // Server should call this method to accept an incoming + // connection, after he is notified of that connection. + // If anything other than the default MTU is desired, + // it should be passed as a parameter. + // + BOOL Accept (UINT16 desired_mtu = L2CAP_DEFAULT_MTU); + + // Server should call this method to reject an incoming + // connection, after he is notified of that connection. + // + BOOL Reject (UINT16 reason); + + // Client should call thi smethod to create a connection + // to a remote device. If anything other than the default + // MTU is desired, it should be passed as a parameter + // + BOOL Connect (CL2CapIf *p_if, BD_ADDR p_bd_addr, UINT16 desired_mtu = L2CAP_DEFAULT_MTU); + + // Client or server may call this function to reconfigure + // an existing connection. + // + BOOL Reconfigure (tL2CAP_CONFIG_INFO *p_cfg); + + // Client or server may call this function to disconnect + // an existing connection. + // + void Disconnect (void); + + // Client or server may call this function to send data to + // an existing connection. + // + BOOL Write (void *p_data, UINT16 length, UINT16 *p_len_written); + + // Get Current Connection Statistics + // + BOOL GetConnectionStats (tBT_CONN_STATS *p_conn_stats); + + // Server may provide a function to handle incoming connection + // notifications. Client should not. + // + virtual void OnIncomingConnection (); + + // Client may provide a function to handle connection pending + // notifications. + // + virtual void OnConnectPendingReceived (void) {} + + // Client and server may provide a method to be notified + // when a connection is established. + // + virtual void OnConnected() {} + + // Client and server may provide a method to be notified + // when data is received from the remote side. + // + virtual void OnDataReceived (void *p_data, UINT16 length) {} + + // Client and server may provide a method to be notified + // when a connection becomes congested or uncongested. + // + virtual void OnCongestionStatus (BOOL is_congested) {} + + // Client and server may provide a method to be notified + // when a connection is disconnected. + // + virtual void OnRemoteDisconnected (UINT16 reason) {} + + +private: + void SetIdle(); + +#define CL2CAP_STATE_IDLE 0 +#define CL2CAP_STATE_LISTENING 1 +#define CL2CAP_STATE_WAITING_ACCEPT 2 +#define CL2CAP_STATE_CONNECTING 3 +#define CL2CAP_STATE_CONFIG 4 +#define CL2CAP_STATE_CONNECTED 5 + + int m_state; + + UINT16 m_psm; + UINT16 m_desired_mtu; + UINT8 m_cfg_flags; + UINT16 m_cid; + UINT8 m_id; + + static CL2CapConn *m_p_first_conn; + CL2CapConn *m_p_next_conn; + UINT16 m_audioHandle; + + friend class CL2CapFriend; + + // This class will not support the compiler-supplied copy constructor or assignment operator, + // so these are declared private to prevent inadvertent use by the application. + CL2CapConn(const CL2CapConn & x); + CL2CapConn& operator= (const CL2CapConn & x); +}; + + + +//////////////////////////////////////////////////////////////////////////// +// +// Define a class to create and manage SDP service records +// +class WIDCOMMSDK CSdpService +{ +public: + CSdpService(); + virtual ~CSdpService(); + + // This function adds a service class ID list to a service record. The service class ID + // list should be the first attribute added for a service record. + // + SDP_RETURN_CODE AddServiceClassIdList (int num_guids, GUID *p_service_guids); + + // This function adds a name field to a service record. + // +// SDP_RETURN_CODE AddServiceName (char *p_service_name); + SDP_RETURN_CODE AddServiceName (BT_CHAR *p_service_name);//CE defs + + // This function adds a profile descriptor list to a service record. + // + SDP_RETURN_CODE AddProfileDescriptorList (GUID *p_profile_guid, UINT16 version); + + // This function adds an L2CAP protocol descriptor list to a service record. + // + SDP_RETURN_CODE AddL2CapProtocolDescriptor (UINT16 psm); + + // This function adds an RFCOMM protocol descriptor list to a service record. + // + SDP_RETURN_CODE AddRFCommProtocolDescriptor (UINT8 scn); + + // This function adds a generic protocol descriptor list to a service record. + // It should be only needed if the specific RFCOMM and L2CAP functions above + // do not suffice. + // + SDP_RETURN_CODE AddProtocolList (int num_elem, tSDP_PROTOCOL_ELEM *p_elem_list); + + // This function adds the additional sequence of generic protocol descriptor lists to a service record. + // It should be only needed if the specific RFCOMM and L2CAP functions above + // do not suffice. + // + SDP_RETURN_CODE AddAdditionProtoLists (int num_list_elem, tSDP_PROTO_LIST_ELEM *p_proto_list); + + // This function adds a language base to a service record. + // + SDP_RETURN_CODE AddLanguageBaseAttrIDList (UINT16 lang, UINT16 char_enc, UINT16 base_id); + + // This function makes a service record public browsable. + // + SDP_RETURN_CODE MakePublicBrowseable (void); + + // This function sets the 'service availability' field of a service record. + // + SDP_RETURN_CODE SetAvailability (UINT8 availability); + + // This function adds an attribute to a service record. It is intended + // to be used to add some other attribute not covered by the existing + // functions. Note that the parameter should be in Big Endian order. + // + SDP_RETURN_CODE AddAttribute (UINT16 attr_id, UINT8 attr_type, UINT32 attr_len, UINT8 *p_val); + + // This function deletes an attribute from a service record. + // + SDP_RETURN_CODE DeleteAttribute (UINT16 attr_id); + + // This functions add a list (sequence) for the 'supported formats' attribute + // + SDP_RETURN_CODE AddSupportedFormatsList(UINT8 num_formats, + UINT8 pDataType[], UINT8 pDataTypeLength[], UINT8 *pDataTypeValue[]); + +private: + UINT32 m_sdp_record_handle; + + // This class will not support the compiler-supplied copy constructor or assignment operator, + // so these are declared private to prevent inadvertent use by the application. + CSdpService(const CSdpService & x); + CSdpService& operator= (const CSdpService & x); +}; + + +class WIDCOMMSDK CSdpDiscoveryRec +{ +public: + + CSdpDiscoveryRec(); + ~CSdpDiscoveryRec(); + + //char m_service_name[BT_MAX_SERVICE_NAME_LEN + 1]; + BT_CHAR m_service_name[BT_MAX_SERVICE_NAME_LEN + 1]; + + + GUID m_service_guid; + + BOOL FindRFCommScn (UINT8 *pScn); + + BOOL FindL2CapPsm (UINT16 *pPsm); + + BOOL FindProtocolListElem (UINT16 layer_uuid, tSDP_PROTOCOL_ELEM *p_elem); + + BOOL FindAdditionalProtocolListElem (UINT16 layer_uuid, tSDP_PROTOCOL_ELEM *p_elem); + + BOOL FindProfileVersion (GUID *p_profile_guid, UINT16 *p_version); + + BOOL FindAttribute (UINT16 attr_id, SDP_DISC_ATTTR_VAL *p_val); + +private: + + void *m_p_rec; + UINT8 *m_p_free_mem; + UINT16 m_mem_free; + + void Create(void *p); + UINT8 *AddDiscoveryAttr (UINT8 *pp, int len, void *p_parent, int nest_level); + + friend class CBtIf; + + // This class will not support the compiler-supplied copy constructor or assignment operator, + // so these are declared private to prevent inadvertent use by the application. + CSdpDiscoveryRec(const CSdpDiscoveryRec & x); + CSdpDiscoveryRec& operator= (const CSdpDiscoveryRec & x); +}; + + +//////////////////////////////////////////////////////////////////////////// +// +// Define a class to control the RfComm interface +// +class WIDCOMMSDK CRfCommIf +{ +public: + CRfCommIf(); + ~CRfCommIf(); + + // Server should call this method without any parameter + // to assign a new SCN value, or with a SCN value if it + // is using a fixed SCN. Client should call this method + // with SCN found from service discovery + // + BOOL AssignScnValue (GUID *p_service_guid, UINT8 scn = 0); + + // Returns the SCN value currently in use. + // + inline UINT8 GetScn() { return m_scn; } + + // Both client and server MUST call this function to set + // the security level for connections on the assigned SCN. + // + BOOL SetSecurityLevel (BT_CHAR *p_service_name, UINT8 security_level, BOOL is_server); + + // Server should call this method to switch role to master if + // it wants to accept multiple connections + // + BOOL SwitchRole(MASTER_SLAVE_ROLE new_role); + +private: + UINT8 m_scn; + BOOL m_scnWasAllocated; + GUID m_service_guid; + UINT8 m_security_index; + + // This class will not support the compiler-supplied copy constructor or assignment operator, + // so these are declared private to prevent inadvertent use by the application. + CRfCommIf(const CRfCommIf & x); + CRfCommIf& operator= (const CRfCommIf & x); +}; + + + +/////////////////////////////////////////////////////////////////////////////////////// +// Define a class to control the RFCOMM connections (both client and server) +// +class WIDCOMMSDK CRfCommPort +{ +public: + + // Construction/destruction + // + CRfCommPort (); + virtual ~CRfCommPort(); +// +// Define return code for RFCOMM port functions +// +typedef enum +{ + SUCCESS, + UNKNOWN_ERROR, + ALREADY_OPENED, // Client tried to open port to existing DLCI/BD_ADDR + HANDLE_ERROR, // Use of a bad connection handle within SDK + NOT_OPENED, // Function called before conn opened, or after closed + LINE_ERR, // Line error + START_FAILED, // Connection attempt failed + PAR_NEG_FAILED, // Parameter negotiation failed, currently only MTU + PORT_NEG_FAILED, // Port negotiation failed + PEER_CONNECTION_FAILED, // Connection ended by remote side + PEER_TIMEOUT, + +} PORT_RETURN_CODE; + + + // Open the RFComm serial port as a server (i.e. listen for + // remote side to connect to us). + // + PORT_RETURN_CODE OpenServer (UINT8 scn, UINT16 desired_mtu = RFCOMM_DEFAULT_MTU); + + // Open the RFComm serial port as a client (i.e. initiate + // the connection). + // + PORT_RETURN_CODE OpenClient (UINT8 scn, BD_ADDR RemoteBdAddr, UINT16 desired_mtu = RFCOMM_DEFAULT_MTU); + + // Close the RFComm serial port + // + PORT_RETURN_CODE Close(void); + + // Check if connection is up, and if so to whom + // + BOOL IsConnected (BD_ADDR *p_remote_bdaddr); + + // Set port flow control - application level, not low level + // + PORT_RETURN_CODE SetFlowEnabled (BOOL enabled); + + // Set control leads see BtIfDefinitions.h + // + PORT_RETURN_CODE SetModemSignal (UINT8 signal); + + // Get control lead status + // + PORT_RETURN_CODE GetModemStatus (UINT8 *p_signal); + + // Send an error to the peer + // + PORT_RETURN_CODE SendError (UINT8 errors); + + // Purge transmit queue + // + PORT_RETURN_CODE Purge (UINT8 purge_flags); + + // Write data + // + PORT_RETURN_CODE Write (void *p_data, UINT16 len_to_write, UINT16 *p_len_written); + + // Get Current Connection Statistics + // + PORT_RETURN_CODE GetConnectionStats (tBT_CONN_STATS *p_conn_stats); + + // App may provide these functions + // + virtual void OnDataReceived (void *p_data, UINT16 len) {} + virtual void OnEventReceived (UINT32 event_code) {} + virtual void OnModemSignalChanged (UINT8 signals) {} + virtual void OnFlowEnabled (BOOL enabled) {} + + BOOL SwitchRole(MASTER_SLAVE_ROLE new_role); + +#ifndef _WIN32_WCE + //Crate AudioConnection + AUDIO_RETURN_CODE CreateAudioConnection(BOOL bIsClient, UINT16 *audioHandle); + + //Disconnect AudioConnection + AUDIO_RETURN_CODE RemoveAudioConnection(UINT16 audioHandle); + + //audion callback functions + virtual void OnAudioConnected(UINT16 audioHandle){}; + virtual void OnAudioDisconnect(UINT16 aidioHandle){}; +#endif + +private: + + UINT8 m_scn; + BOOL m_is_server; + UINT16 m_RemoteMtu; + + UINT16 m_desired_mtu; + + static CRfCommPort *m_p_first_port; + CRfCommPort *m_p_next_port; + + UINT16 m_handle; + BOOL m_is_connected; + BD_ADDR m_RemoteBdAddr; + UINT16 m_audioHandle; + + friend class CRfCommFriend; + + // This class will not support the compiler-supplied copy constructor or assignment operator, + // so these are declared private to prevent inadvertent use by the application. + CRfCommPort(const CRfCommPort & x); + CRfCommPort& operator= (const CRfCommPort & x); +}; + +/////////////////////////////////////////////////////////////////////////////////////// +// Define a class to control the FTP client sessions +// +class WIDCOMMSDK CFtpClient +{ + +public: + +// +// Define return code for FTP Client functions +// +typedef enum +{ + SUCCESS, // Operation initiated without error + OUT_OF_MEMORY, // Not enough memory to initiate operation + SECURITY_ERROR, // Error implementing requested security level + FTP_RETURN_ERROR, // FTP-specific error + NO_BT_SERVER, // cannot access the local Bluetooth COM server + ALREADY_CONNECTED, // Only one connection at a time supported for each instantiated CFtpClient object + NOT_OPENED, // Connection must be opened before requesting this operation + UNKNOWN_RETURN_ERROR // Any condition other than the above +} FTP_RETURN_CODE; + +// +// Define return code for FTP response functions +// +typedef enum +{ + COMPLETED, // operation completed without error + BAD_ADDR, // bad BD_ADDR + FILE_EXISTS, // file already exists + BAD_STATE, // could not handle request in present state + BAD_REQUEST, // invalid request + NOT_FOUND, // no such file + NO_SERVICE, // could not find the specified FTP server + DISCONNECT, // connection lost + READ, // read error + WRITE, // write error + OBEX_AUTHEN, // OBEX Authentication required + DENIED, // request could not be honored + DATA_NOT_SUPPORTED, // server does not support the requested data + CONNECT, // error establishing connection + PERMISSIONS, // incorrect file or service permissions + NOT_INITIALIZED, // not initialized + PARAM, // invalid parameter + RESOURCES, // out of file system resources (handles, disk space, etc) + SHARING, // sharing violation + UNKNOWN_RESULT_ERROR // Any condition other than the above +} FTP_RESULT_CODE; + +// FTP +enum FtpFolder +{ + FTP_ROOT_FOLDER = 1, + FTP_PARENT_FOLDER, + FTP_SUBFOLDER +}; +typedef enum FtpFolder tFtpFolder; + + +public: + + // Construction/destruction + // + CFtpClient (); + virtual ~CFtpClient(); + + FTP_RETURN_CODE OpenConnection (BD_ADDR bdAddr, CSdpDiscoveryRec & sdp_rec); + FTP_RETURN_CODE CloseConnection(); + FTP_RETURN_CODE PutFile(WCHAR * localFileName); + FTP_RETURN_CODE GetFile(WCHAR * remoteFileName, WCHAR * localFolder); + FTP_RETURN_CODE FolderListing(); + FTP_RETURN_CODE ChangeFolder(WCHAR * szFolder); + FTP_RETURN_CODE DeleteFile(WCHAR * szFile); + FTP_RETURN_CODE Abort(); + FTP_RETURN_CODE Parent(); + FTP_RETURN_CODE Root(); + FTP_RETURN_CODE CreateEmpty(WCHAR * szFile); + FTP_RETURN_CODE CreateFolder(WCHAR * szFolder); + void SetSecurity(BOOL authentication, BOOL encryption); + + // Application may provide these functions to facilitate managing the connection + virtual void OnOpenResponse(FTP_RESULT_CODE result_code) {} + virtual void OnCloseResponse(FTP_RESULT_CODE result_code){} + + +//windows +#ifndef _WIN32_WCE + virtual void OnProgress(FTP_RESULT_CODE result_code, WCHAR * name, long current, long total) {} + virtual void OnPutResponse(FTP_RESULT_CODE result_code, WCHAR * name) {} + virtual void OnGetResponse(FTP_RESULT_CODE result_code, WCHAR * name) {} + virtual void OnCreateResponse(FTP_RESULT_CODE result_code, WCHAR * name) {} + virtual void OnDeleteResponse(FTP_RESULT_CODE result_code, WCHAR * name){} + + virtual void OnChangeFolderResponse(FTP_RESULT_CODE result_code, tFtpFolder folder_type, WCHAR * szFolder) {} + virtual void OnFolderListingResponse(FTP_RESULT_CODE result_code, tFTP_FILE_ENTRY * listing, long entries) {} + virtual void OnXmlFolderListingResponse(FTP_RESULT_CODE rc, WCHAR * pfolder_listing, long folder_length ){} +#else //for ce + virtual void OnProgress(FTP_RESULT_CODE result_code, LPCTSTR name, long current, long total) {} + virtual void OnPutResponse(FTP_RESULT_CODE result_code, LPCTSTR name) {} + virtual void OnGetResponse(FTP_RESULT_CODE result_code, LPCTSTR name) {} + virtual void OnCreateResponse(FTP_RESULT_CODE result_code, LPCTSTR name) {} + virtual void OnDeleteResponse(FTP_RESULT_CODE result_code, LPCTSTR name){} + + virtual void OnFolderListingResponse(FTP_RESULT_CODE result_code, tFTP_FILE_ENTRY * listing, BOOL last) {} + virtual void OnXmlFolderListingResponse(FTP_RESULT_CODE rc, WCHAR * pfolder_listing, long folder_length, BOOL last ){} + virtual void OnChangeFolderResponse(FTP_RESULT_CODE result_code, tFtpFolder folder_type, LPCTSTR szFolder) {} +#endif + + +virtual void OnAbortResponse(FTP_RESULT_CODE result_code) {} + +private: + + long m_FtpHandle; + CWBtAPI *m_pBtApi; + BOOL m_authentication_requested_by_app; + BOOL m_encryption_requested_by_app; + BOOL m_authentication_saved; + BOOL m_encryption_saved; + void SaveSecurity(); + void RestoreSecurity(); + + friend class CFtpClientFriend; + + // This class will not support the compiler-supplied copy constructor or assignment operator, + // so these are declared private to prevent inadvertent use by the application. + CFtpClient(const CFtpClient & x); + CFtpClient& operator= (const CFtpClient & x); +}; + + +/////////////////////////////////////////////////////////////////////////////////////// +// Define a class to control the OPP client sessions +// +class WIDCOMMSDK COppClient +{ +public: + +// +// Define return code for OPP Client functions +// +typedef enum +{ + OPP_CLIENT_SUCCESS, // Operation initiated without error + OUT_OF_MEMORY, // Not enough memory to initiate operation + SECURITY_ERROR, // Error implementing requested security level + OPP_ERROR, // OPP-specific error + ABORT_INVALID, // Abort not valid, no operation is in progress + UNKNOWN_ERROR // Any condition other than the above +} OPP_RETURN_CODE; + +// +// Define return code for OPP response functions +// +typedef enum +{ + COMPLETED, // operation completed without error + BAD_ADDR, // bad BD_ADDR + BAD_STATE, // could not handle request in present state + BAD_REQUEST, // invalid request + NOT_FOUND, // no such file + NO_SERVICE, // could not find the specified FTP server + DISCONNECT, // connection lost + READ, // read error + WRITE, // write error + OBEX_AUTH, // OBEX Authentication required + DENIED, // request could not be honored + DATA_NOT_SUPPORTED, // server does not support the requested data + CONNECT, // error establishing connection + NOT_INITIALIZED, // not initialized + PARAM, // bad parameter + BAD_INBOX, // inbox is not valid + BAD_NAME, // bad name for object + PERMISSIONS, // prohibited by file permissions + SHARING, // file is shared + RESOURCES, // file system resource limit reached - may be file handles, disk space, etc. + FILE_EXISTS, // is closedfile alto attempt to perform function after connectionready exists + UNKNOWN_RESULT_ERROR // Any condition other than the above +} OPP_RESULT_CODE; + +// +// Define return code for OPP response functions +// (NOTE: these values match the ones defined in \middleware\opp\oppapp.h) +// +typedef enum +{ + OPP_PUT_TRANS = 1, + OPP_GET_TRANS = 2, + OPP_EXCHANGE_PUT_TRANS = 3, + OPP_EXCHANGE_GET_TRANS = 4, + OPP_ABORT_TRANS = 5 + +} OPP_TRANSACTION_CODE; + +public: + + // Construction/destruction + // + COppClient (); + virtual ~COppClient(); + + OPP_RETURN_CODE Push(BD_ADDR bda, WCHAR * pszPathName, CSdpDiscoveryRec & sdp_rec); + OPP_RETURN_CODE Pull(BD_ADDR bda, WCHAR * pszPathName, CSdpDiscoveryRec & sdp_rec); + OPP_RETURN_CODE Exchange(BD_ADDR bda, WCHAR * pszName, WCHAR * pszFolder, CSdpDiscoveryRec & sdp_rec); + OPP_RETURN_CODE Abort(); + void SetSecurity(BOOL authentication, BOOL encryption); + + + virtual void OnAbortResponse (OPP_RESULT_CODE result_code) {} + +#ifndef _WIN32_WCE + virtual void OnProgress(OPP_RESULT_CODE result_code, BD_ADDR bda, WCHAR * string, long current, long total) {} + virtual void OnPushResponse(OPP_RESULT_CODE result_code, BD_ADDR bda, WCHAR * string) {} + virtual void OnPullResponse(OPP_RESULT_CODE result_code , BD_ADDR bda, WCHAR * string) {} + virtual void OnExchangeResponse(OPP_RESULT_CODE result_code, BD_ADDR bda, WCHAR * string) {} + virtual void OnExchangeResponse(OPP_RESULT_CODE result_code, BD_ADDR bda, WCHAR * string, OPP_TRANSACTION_CODE transaction_code) {} +#else //for CE + virtual void OnProgress(OPP_RESULT_CODE result_code, BD_ADDR bda, LPCTSTR string, long current, long total) {} + virtual void OnPushResponse(OPP_RESULT_CODE result_code, BD_ADDR bda, LPCTSTR string) {} + virtual void OnPullResponse(OPP_RESULT_CODE result_code , BD_ADDR bda, LPCTSTR string) {} + virtual void OnExchangeResponse(OPP_RESULT_CODE result_code, BD_ADDR bda, LPCTSTR string) {} + virtual void OnExchangeResponse(OPP_RESULT_CODE result_code, BD_ADDR bda, LPCTSTR string, OPP_TRANSACTION_CODE transaction_code) {} +#endif + +private: + long m_OppHandle; + CWBtAPI *m_pBtApi; + BOOL m_authentication_requested_by_app; + BOOL m_encryption_requested_by_app; + BOOL m_authentication_saved; + BOOL m_encryption_saved; + void SaveSecurity(); + void RestoreSecurity(); + + friend class COppClientFriend; + + // This class will not support the compiler-supplied copy constructor or assignment operator, + // so these are declared private to prevent inadvertent use by the application. + COppClient(const COppClient & x); + COppClient& operator= (const COppClient & x); +}; + +/////////////////////////////////////////////////////////////////////////////////////// +// Define a class to control the LAP client sessions +// +class WIDCOMMSDK CLapClient +{ +public: + +// +// Define return code for LAP Client functions +// +typedef enum +{ + SUCCESS, // Operation initiated without error + NO_BT_SERVER, // COM server could not be started + ALREADY_CONNECTED, // attempt to connect before previous connection closed + NOT_CONNECTED, // attempt to close unopened connection + NOT_ENOUGH_MEMORY, // local processor could not allocate memory for open + UNKNOWN_ERROR // Any condition other than the above +} LAP_RETURN_CODE; + +// +// Define connection states +// +typedef enum +{ + LAP_CONNECTED, // port now connected + LAP_DISCONNECTED // port now disconnected +} LAP_STATE_CODE; + + +public: + + // Construction/destruction + // + CLapClient (); + virtual ~CLapClient(); + + LAP_RETURN_CODE CreateConnection(BD_ADDR bda, CSdpDiscoveryRec & sdp_rec); + LAP_RETURN_CODE RemoveConnection(); + void SetSecurity(BOOL authentication, BOOL encryption); + + virtual void OnStateChange(BD_ADDR bda, DEV_CLASS dev_class, BD_NAME name, short com_port, LAP_STATE_CODE state) = 0; + +private: + BD_ADDR m_RemoteBda; + short m_ComPort; + CWBtAPI *m_pBtApi; + BOOL m_authentication_requested_by_app; + BOOL m_encryption_requested_by_app; + BOOL m_authentication_saved; + BOOL m_encryption_saved; + void SaveSecurity(); + void RestoreSecurity(); + + + friend class CLapClientFriend; + + // This class will not support the compiler-supplied copy constructor or assignment operator, + // so these are declared private to prevent inadvertent use by the application. + CLapClient(const CLapClient & x); + CLapClient& operator= (const CLapClient & x); +}; + +/////////////////////////////////////////////////////////////////////////////////////// +// Define a class to control the DUN client sessions +// +class WIDCOMMSDK CDunClient +{ +public: + +// +// Define return code for DUN Client functions +// +typedef enum +{ + SUCCESS, // Operation initiated without error + NO_BT_SERVER, // COM server could not be started + ALREADY_CONNECTED, // attempt to connect before previous connection closed + NOT_CONNECTED, // attempt to close unopened connection + NOT_ENOUGH_MEMORY, // local processor could not allocate memory for open + UNKNOWN_ERROR // Any condition other than the above +} DUN_RETURN_CODE; + +// +// Define connection states +// +typedef enum +{ + DUN_CONNECTED, // port now connected + DUN_DISCONNECTED // port now disconnected +} DUN_STATE_CODE; + + +public: + + // Construction/destruction + // + CDunClient (); + virtual ~CDunClient(); + + DUN_RETURN_CODE CreateConnection(BD_ADDR bda, CSdpDiscoveryRec & sdp_rec); + DUN_RETURN_CODE RemoveConnection(); + void SetSecurity(BOOL authentication, BOOL encryption); + + virtual void OnStateChange(BD_ADDR bda, DEV_CLASS dev_class, BD_NAME name, short com_port, DUN_STATE_CODE state) = 0; + +private: + short m_ComPort; + CWBtAPI *m_pBtApi; + BOOL m_authentication_requested_by_app; + BOOL m_encryption_requested_by_app; + BOOL m_authentication_saved; + BOOL m_encryption_saved; + void SaveSecurity(); + void RestoreSecurity(); + + + friend class CDunClientFriend; + + // This class will not support the compiler-supplied copy constructor or assignment operator, + // so these are declared private to prevent inadvertent use by the application. + CDunClient(const CDunClient & x); + CDunClient& operator= (const CDunClient & x); +}; + +/////////////////////////////////////////////////////////////////////////////////////// +// Define a class to control the SPP client sessions +// +class WIDCOMMSDK CSppClient +{ +public: + +// +// Define return code for SPP Client functions +// +typedef enum +{ + SUCCESS, // Operation initiated without error + NO_BT_SERVER, // COM server could not be started + ALREADY_CONNECTED, // attempt to connect before previous connection closed + NOT_CONNECTED, // attempt to close unopened connection + NOT_ENOUGH_MEMORY, // local processor could not allocate memory for open + UNKNOWN_ERROR // Any condition other than the above +} SPP_CLIENT_RETURN_CODE; + + +public: + + // Construction/destruction + // + CSppClient (); + virtual ~CSppClient(); + + SPP_CLIENT_RETURN_CODE CreateConnection(BD_ADDR bda, BT_CHAR *szServiceName); + SPP_CLIENT_RETURN_CODE RemoveConnection(); + + virtual void OnClientStateChange(BD_ADDR bda, DEV_CLASS dev_class, BD_NAME name, short com_port, SPP_STATE_CODE state) = 0; + +private: + short m_ComPort; + CWBtAPI *m_pBtApi; + BOOL m_connected; + + friend class CSppClientFriend; + + // This class will not support the compiler-supplied copy constructor or assignment operator, + // so these are declared private to prevent inadvertent use by the application. + CSppClient(const CSppClient & x); + CSppClient& operator= (const CSppClient & x); +}; + + +/////////////////////////////////////////////////////////////////////////////////////// +// Define a class to control the SPP server sessions +// +class WIDCOMMSDK CSppServer +{ +public: + +// +// Define return code for SPP Server functions +// +typedef enum +{ + SUCCESS, // Operation initiated without error + NO_BT_SERVER, // COM server could not be started + ALREADY_CONNECTED, // attempt to connect before previous connection closed + NOT_CONNECTED, // attempt to close unopened connection + NOT_ENOUGH_MEMORY, // local processor could not allocate memory for open + NOT_SUPPORTED, // requested service not available locally + UNKNOWN_ERROR // Any condition other than the above +} SPP_SERVER_RETURN_CODE; + +private: + +typedef struct { + short comPort; + BOOL authentication; + BOOL authorization; + BOOL encryption; + BOOL automatic; // TRUE if this service is automatically started by stack server + //unsigned char serviceName[BT_MAX_SERVICE_NAME_LEN]; + BT_CHAR serviceName[BT_MAX_SERVICE_NAME_LEN]; +} SPP_LOCAL_SERVICE; + +#define MAX_LOCAL_SERVICES 5 + +public: + + // Construction/destruction + // + CSppServer (); + virtual ~CSppServer(); + + SPP_SERVER_RETURN_CODE CreateConnection(BT_CHAR * szServiceName); + SPP_SERVER_RETURN_CODE RemoveConnection(); + virtual void OnServerStateChange(BD_ADDR bda, DEV_CLASS dev_class, BD_NAME name, short com_port, SPP_STATE_CODE state) = 0; + +private: + short m_comPort; + CWBtAPI *m_pBtApi; + BOOL m_authentication; // future + BOOL m_authorization; // future + BOOL m_encryption; // future + BOOL m_startup; + BT_CHAR m_serviceName[BT_MAX_SERVICE_NAME_LEN]; +#ifndef _WIN32_WCE + BOOL serviceAvailable(LPCSTR szServiceName); +#else + BOOL serviceAvailable(LPCTSTR szServiceName); +#endif + BOOL m_connected; + BOOL m_automatic; + short m_cntLocalServices; + SPP_LOCAL_SERVICE m_localServiceList[MAX_LOCAL_SERVICES]; + + void SppLocalServices(SPP_LOCAL_SERVICE *pLocalServiceList, short maxLocalServices, short *pCntLocalServices); + + + friend class CSppServerFriend; + + // This class will not support the compiler-supplied copy constructor or assignment operator, + // so these are declared private to prevent inadvertent use by the application. + CSppServer(const CSppServer & x); + CSppServer& operator= (const CSppServer & x); +}; + + + +//////////////////////////////////////////////////////////////////////////// +// The CPrintClient class +// +//file types +#define TYPE_STRING_GIF _T("image/gif:89A") + +#define BPSF_TYPE 0x01 //Mask value + +struct BTPRINTSTRUCT + { + DWORD dwSize; // Must be sizeof(BTPRINTSTRUCT) + UINT mask; // 0 or BPSF_TYPE + LPCTSTR pszType; // Data type + }; + + +class CPrintInternal; +class WIDCOMMSDK CPrintClient +{ +public: + + // Define the profiles supported by the Printing SDK + // + typedef enum + { + PRINT_PROFILE_BPP, + PRINT_PROFILE_HCRP, + PRINT_PROFILE_SPP + + } ePRINT_PROFILE; + + // Define the current state of the Printing SDK + // + typedef enum + { + PRINT_STATE_IDLE, + PRINT_STATE_CONNECTING, + PRINT_STATE_PRINTING, + PRINT_STATE_FLOW_CONTROLLED, + PRINT_STATE_DISCONNECTING, + PRINT_STATE_DONE + + } ePRINT_STATE; + + // Define error codes returned by the Printing SDK + // + typedef enum + { + // Generic to all profiles + // + PRINT_RC_OK, + PRINT_RC_FILE_PRINTED_OK, + PRINT_RC_FILE_NOT_FOUND, + PRINT_RC_FILE_READ_ERROR, + PRINT_RC_ALREADY_PRINTING, + PRINT_RC_UNKNOWN_PROFILE, + PRINT_RC_SERVICE_NOT_FOUND, + PRINT_RC_SECURITY_ERROR, + PRINT_RC_CONNECT_ERROR, + PRINT_RC_WRITE_ERROR, + PRINT_RC_REMOTE_DISCONNECTED, + PRINT_RC_INVALID_PARAM, + + + // BPP Specific errors + // + PRINT_RC_BPP_SCN_NOT_FOUND, + PRINT_RC_BPP_SCN_NOT_ASSIGNED, + PRINT_RC_BPP_OBEX_ABORTED, + PRINT_RC_BPP_OBEX_MISMATCH, + + // HCRP Specific errors + // + PRINT_RC_HCRP_CTL_PSM_NOT_FOUND, + PRINT_RC_HCRP_DATA_PSM_NOT_FOUND, + + // SPP Specific errors + // + PRINT_RC_SPP_SCN_NOT_FOUND, + + } ePRINT_ERROR; + + public: + CPrintClient(); + ~CPrintClient(); + + ePRINT_ERROR Start (BD_ADDR pBDA, ePRINT_PROFILE eProfile, LPCTSTR pszFile, + BTPRINTSTRUCT * pBtPrintStruct = NULL); + + void Cancel(); + ePRINT_STATE GetState(); + UINT GetBytesSent(); + UINT GetBytesTotal(); + ePRINT_ERROR GetLastError(TCHAR **pDescr); + + virtual void OnStateChange (ePRINT_STATE NewState) { }; + static CPrintInternal *pIp; +}; + + +#pragma pack () + + +#endif // !defined(AFX_WIDCOMMSDK_H__1F5ED990_6FC6_4B0D_882C_8D7C98C16A06__INCLUDED_) diff --git a/inc/BtIfDefinitions.h b/inc/BtIfDefinitions.h new file mode 100644 index 0000000..e6ab1e5 --- /dev/null +++ b/inc/BtIfDefinitions.h @@ -0,0 +1,1222 @@ +///////////////////////////////////////////////////////////////////////////// +// +// Name BtIfDefinitions.h +// $Header: +// +// Function this file contains Widcomm SDK type definitions +// +// Date Modification +// ---------------------------------- +// 12/17/2000 JF Create +// +// Copyright (c) 2000-2002, WIDCOMM Inc., All Rights Reserved. +// Proprietary and confidential. +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef _BTIFDEFINITIONS_H +#define _BTIFDEFINITIONS_H + + +// Ensure alignment across all builds +// +#ifdef _WIN32_WCE +#pragma pack (8) +#else +#pragma pack (1) +#endif + +// +// Define return codes from the SDP service functions +// +typedef enum +{ + SDP_OK, + SDP_COULD_NOT_ADD_RECORD, + SDP_INVALID_RECORD, + SDP_INVALID_PARAMETERS + +} SDP_RETURN_CODE; + +// +// Define SPP connection states +// +typedef enum +{ + SPP_CONNECTED, // port now connected + SPP_DISCONNECTED // port now disconnected +} SPP_STATE_CODE; + + +// Maximum number of UUIDs in a service class ID list +// +#define MAX_UUIDS_PER_SEQUENCE 3 + +// Maximum service name length +// +#define BT_MAX_SERVICE_NAME_LEN 100 + +// Maximum protocol list elements in a record +// +#define MAX_PROTOCOL_LIST_ELEM 3 + +#define SDP_MAX_LIST_ELEMS 3 +#define MAX_ELEM_IN_SEQ 10 + + +// +// Define a structure to hold attribute values when +// read from the discovery database. Note that the +// attribute may be a sequence, in which case the +// number of elements will be greater than 1. +// +#define MAX_SEQ_ENTRIES 20 +#define MAX_ATTR_LEN 256 + +typedef struct +{ + int num_elem; + + struct + { + #define ATTR_TYPE_INT 0 // Attribute value is an integer + #define ATTR_TYPE_TWO_COMP 1 // Attribute value is an 2's complement integer + #define ATTR_TYPE_UUID 2 // Attribute value is a UUID + #define ATTR_TYPE_BOOL 3 // Attribute value is a boolean + #define ATTR_TYPE_ARRAY 4 // Attribute value is an array of bytes + + int type; + int len; // Length of the attribute + BOOL start_of_seq; // TRUE for each start of sequence + union + { + unsigned char u8; // 8-bit integer + unsigned short u16; // 16-bit integer + unsigned long u32; // 32-bit integer + BOOL b; // Boolean + unsigned char array[MAX_ATTR_LEN]; // Variable length array + } val; + + } elem [MAX_SEQ_ENTRIES]; + +} SDP_DISC_ATTTR_VAL; + +/////////////////////////////////////////////////////////////////////////////// +///// definitions used in FTP client apps to present remote server directory listings +// +#define MAX_NAME_SIZE 255 +#define MAX_ENTRY_SIZE 1000 +#define DATE_TIME_SIZE 15 + +typedef struct +{ + WCHAR name[MAX_NAME_SIZE + 1]; + BOOL is_folder; + WCHAR date_created[DATE_TIME_SIZE + 1]; + WCHAR date_modified[DATE_TIME_SIZE + 1]; + WCHAR date_accessed[DATE_TIME_SIZE + 1]; + ULONG file_size; + +#define FTP_READ_PERM (0x01) +#define FTP_WRITE_PERM (0x02) +#define FTP_DELETE_PERM (0x04) + + WCHAR user_perm; + WCHAR group_perm; + WCHAR other_perm; +} tFTP_FILE_ENTRY; + + + +#ifndef BT_CONN_STATS_DEFINED +#define BT_CONN_STATS_DEFINED +typedef struct +{ + UINT32 bIsConnected; + INT32 Rssi; + UINT32 BytesSent; + UINT32 BytesRcvd; + UINT32 Duration; +} tBT_CONN_STATS; +#endif //BT_CONN_STATS_DEFINED + +#define L2CAP_MIN_MTU 48 + +///////////////////////////////////////////////////////////////////////////////////////// +// Definitions for applications only +// + +#if !defined (WIDCOMMSDK_EXPORTS) + +// +// Define some types used by the Widcomm SDK +// +typedef unsigned char UINT8; +typedef unsigned short UINT16; +typedef unsigned int UINT32; + +// +// Common Bluetooth field definitions +// +#define BD_ADDR_LEN 6 /* Device address length */ +typedef UINT8 BD_ADDR[BD_ADDR_LEN]; /* Device address */ +typedef UINT8 *BD_ADDR_PTR; /* Pointer to Device Address */ + +#define LINK_KEY_LEN 16 +typedef UINT8 LINK_KEY[LINK_KEY_LEN]; /* Link Key */ + +#define PIN_CODE_LEN 16 +typedef UINT8 PIN_CODE[PIN_CODE_LEN]; /* Pin Code (upto 128 bits) MSB is 0 */ +typedef UINT8 *PIN_CODE_PTR; /* Pointer to Pin Code */ + +#define DEV_CLASS_LEN 3 +typedef UINT8 DEV_CLASS[DEV_CLASS_LEN]; /* Device class */ +typedef UINT8 *DEV_CLASS_PTR; /* Pointer to Device class */ + +// +// Coding of the DEV_CLASS field. Refer to Bluetooth Assigned Numbers +// specification for further information on device class. +// +// First byte - Service class of the CoD, byte [0] +#define SERV_CLASS_NETWORKING (1 << 1) +#define SERV_CLASS_RENDERING (1 << 2) +#define SERV_CLASS_CAPTURING (1 << 3) +#define SERV_CLASS_OBJECT_TRANSFER (1 << 4) +#define SERV_CLASS_OBJECT_AUDIO (1 << 5) +#define SERV_CLASS_OBJECT_TELEPHONY (1 << 6) +#define SERV_CLASS_OBJECT_INFORMATION (1 << 7) + +// Second byte - high order +#define SERV_CLASS_LIMITED_DISC_MODE (0x20) + +// Also 2nd byte - Major Device class - low 5 bits +#define MAJOR_DEV_CLASS_MASK 0x1F + +#define MAJOR_DEV_CLASS_MISC 0x00 +#define MAJOR_DEV_CLASS_COMPUTER 0x01 +#define MAJOR_DEV_CLASS_PHONE 0x02 +#define MAJOR_DEV_CLASS_LAN_ACCESS 0x03 +#define MAJOR_DEV_CLASS_AUDIO 0x04 +#define MAJOR_DEV_CLASS_PERIPHERAL 0x05 +#define MAJOR_DEV_CLASS_IMAGING 0x06 +#define MAJOR_DEV_CLASS_UNSPECIFIED 0x1F + +// 3rd byte - Minor Device class, hi 6 bits +#define MINOR_DEV_CLASS_MASK 0xFC + +// Minor device class if major is computer +#define MINOR_DEV_CLASS_COMP_UNCLASSIFIED 0x00 +#define MINOR_DEV_CLASS_COMP_WORKSTATION (0x01 << 2) +#define MINOR_DEV_CLASS_COMP_SERVER (0x02 << 2) +#define MINOR_DEV_CLASS_COMP_LAPTOP (0x03 << 2) +#define MINOR_DEV_CLASS_COMP_HANDHELD (0x04 << 2) +#define MINOR_DEV_CLASS_COMP_PALM (0x05 << 2) + +// Minor device class if major is phone +#define MINOR_DEV_CLASS_PHONE_UNCLASSIFIED 0x00 +#define MINOR_DEV_CLASS_PHONE_CELLULAR (0x01 << 2) +#define MINOR_DEV_CLASS_PHONE_CORDLESS (0x02 << 2) +#define MINOR_DEV_CLASS_PHONE_SMART (0x03 << 2) +#define MINOR_DEV_CLASS_PHONE_MODEM (0x04 << 2) + +// Minor device class if major is imaging +#define MINOR_DEV_CLASS_IMAGING_UNCLASSIFIED 0x00 +#define MINOR_DEV_CLASS_IMAGING_DISPLAY (0x04 << 2) +#define MINOR_DEV_CLASS_IMAGING_CAMERA (0x08 << 2) +#define MINOR_DEV_CLASS_IMAGING_SCANNER (0x10 << 2) +#define MINOR_DEV_CLASS_IMAGING_PRINTER (0x20 << 2) + +#define MINOR_DEV_CLASS_AUDIO_UNCLASSIFIED 0x00 + +// Minor device class if major is audio +#define MINOR_DEV_CLASS_AUDIO_UNCLASSIFIED 0x00 +#define MINOR_DEV_CLASS_AUDIO_HEADSET (0x01 << 2) + + +#define BD_NAME_LEN 248 +typedef UINT8 BD_NAME[BD_NAME_LEN]; /* Device name */ +typedef UINT8 *BD_NAME_PTR; /* Pointer to Device name */ + + +// L2CAP Configuration QOS structure +// +#ifndef BT_TYPES_H +typedef struct +{ + UINT8 qos_flags; /* TBD */ + +// Values for service_type +// +#define NO_TRAFFIC 0 +#define BEST_EFFORT 1 +#define GUARANTEED 2 + + UINT8 service_type; /* see below */ + UINT32 token_rate; /* bytes/second */ + UINT32 token_bucket_size; /* bytes */ + UINT32 peak_bandwidth; /* bytes/second */ + UINT32 latency; /* microseconds */ + UINT32 delay_variation; /* microseconds */ + +} FLOW_SPEC; +#endif + + + +//Zong, need to talk to ask to see if we can make them the same +#ifdef _WIN32_WCE + #define L2CAP_MAX_MTU 1400 + #define L2CAP_DEFAULT_MTU 672 +#else + #define L2CAP_MAX_MTU 1696 + #define L2CAP_DEFAULT_MTU 1691 +#endif + +#define L2CAP_DEFAULT_FLUSH_TO 0xFFFF + + +// This structure is used to add protocol lists and find protocol elements +// +#ifndef SDP_MAX_PROTOCOL_PARAMS +#define SDP_MAX_PROTOCOL_PARAMS 1 +#endif + +typedef struct +{ + UINT16 protocol_uuid; + UINT16 num_params; + UINT16 params[SDP_MAX_PROTOCOL_PARAMS]; +} tSDP_PROTOCOL_ELEM; + +typedef struct +{ + UINT16 num_elems; + tSDP_PROTOCOL_ELEM list_elem[SDP_MAX_LIST_ELEMS]; +} tSDP_PROTO_LIST_ELEM; + +// Maximum UUID size - 16 bytes, and structure to hold any type of UUID. +// +#define MAX_UUID_SIZE 16 + +#ifndef BT_TYPES_H +typedef struct +{ +#define LEN_UUID_16 2 +#define LEN_UUID_32 4 +#define LEN_UUID_128 16 + + UINT16 len; + UINT8 pad[14]; + + union + { + UINT16 uuid16; + UINT32 uuid32; + UINT8 uuid128[MAX_UUID_SIZE]; + } uu; + +} tBT_UUID; +#endif + + +// Define the service record attribute IDs. +// +#define ATTR_ID_SERVICE_RECORD_HDL 0x0000 +#define ATTR_ID_SERVICE_CLASS_ID_LIST 0x0001 +#define ATTR_ID_SERVICE_RECORD_STATE 0x0002 +#define ATTR_ID_SERVICE_ID 0x0003 +#define ATTR_ID_PROTOCOL_DESC_LIST 0x0004 +#define ATTR_ID_BROWSE_GROUP_LIST 0x0005 +#define ATTR_ID_LANGUAGE_BASE_ATTR_ID_LIST 0x0006 +#define ATTR_ID_SERVICE_INFO_TIME_TO_LIVE 0x0007 +#define ATTR_ID_SERVICE_AVAILABILITY 0x0008 +#define ATTR_ID_BT_PROFILE_DESC_LIST 0x0009 +#define ATTR_ID_DOCUMENTATION_URL 0x000A +#define ATTR_ID_CLIENT_EXE_URL 0x000B +#define ATTR_ID_ICON_URL 0x000C +#define ATTR_ID_ADDITION_PROTO_DESC_LISTS 0x000D + +#define LANGUAGE_BASE_ID 0x0100 +#define ATTR_ID_SERVICE_NAME LANGUAGE_BASE_ID + 0x0000 +#define ATTR_ID_SERVICE_DESCRIPTION LANGUAGE_BASE_ID + 0x0001 +#define ATTR_ID_PROVIDER_NAME LANGUAGE_BASE_ID + 0x0002 + +#define ATTR_ID_VERSION_OR_GROUP 0x0200 +#define ATTR_ID_VERSION_NUMBER_LIST ATTR_ID_VERSION_OR_GROUP +#define ATTR_ID_GROUP_ID ATTR_ID_VERSION_OR_GROUP +#define ATTR_ID_SERVICE_DATABASE_STATE 0x0201 +#define ATTR_ID_DATA_STORES_OR_NETWORK 0x0301 +#define ATTR_ID_SUPPORTED_DATA_STORES ATTR_ID_DATA_STORES_OR_NETWORK +#define ATTR_ID_EXTERNAL_NETWORK ATTR_ID_DATA_STORES_OR_NETWORK +#define ATTR_ID_FAX_CLASS_1_OR_AUDIO_VOLUME 0x0302 +#define ATTR_ID_FAX_CLASS_1_SUPPORT ATTR_ID_FAX_1_OR_AUD_VOL_OR_DEV_NAME +#define ATTR_ID_REMOTE_AUDIO_VOLUME_CONTROL ATTR_ID_FAX_1_OR_AUD_VOL_OR_DEV_NAME +#define ATTR_ID_DEVICE_NAME ATTR_ID_FAX_1_OR_AUD_VOL_OR_DEV_NAME +#define ATTR_ID_FORMATS_OR_FAX_2_0 0x0303 +#define ATTR_ID_SUPPORTED_FORMATS_LIST ATTR_ID_FORMATS_OR_FAX_2_0 +#define ATTR_ID_FAX_CLASS_2_0_SUPPORT ATTR_ID_FORMATS_OR_FAX_2_0 +#define ATTR_ID_FAX_CLASS_2_OR_FRIENDLY_NAME 0x0304 +#define ATTR_ID_FAX_CLASS_2_SUPPORT ATTR_ID_FAX_CLASS_2_OR_FRIENDLY_NAME +#define ATTR_ID_FRIENDLY_NAME ATTR_ID_FAX_CLASS_2_OR_FRIENDLY_NAME +#define ATTR_ID_AUDIO_FEEDBACK_SUPPORT 0x0305 + +// Define for service attribute, all the 'Descriptor Type' values. +// These are also referred to as 'attribute type' values +#define NULL_DESC_TYPE 0 +#define UINT_DESC_TYPE 1 +#define TWO_COMP_INT_DESC_TYPE 2 +#define UUID_DESC_TYPE 3 +#define TEXT_STR_DESC_TYPE 4 +#define BOOLEAN_DESC_TYPE 5 +#define DATA_ELE_SEQ_DESC_TYPE 6 +#define DATA_ELE_ALT_DESC_TYPE 7 +#define URL_DESC_TYPE 8 + + +// Define common 16-bit protocol UUIDs +// +#define UUID_PROTOCOL_SDP 0x0001 +#define UUID_PROTOCOL_UDP 0x0002 +#define UUID_PROTOCOL_RFCOMM 0x0003 +#define UUID_PROTOCOL_TCP 0x0004 +#define UUID_PROTOCOL_TCS_BIN 0x0005 +#define UUID_PROTOCOL_TCS_AT 0x0006 +#define UUID_PROTOCOL_OBEX 0x0008 +#define UUID_PROTOCOL_IP 0x0009 +#define UUID_PROTOCOL_FTP 0x000A +#define UUID_PROTOCOL_HTTP 0x000C +#define UUID_PROTOCOL_WSP 0x000E +#define UUID_PROTOCOL_BNEP 0x000F +#define UUID_PROTOCOL_UPNP 0x0010 +#define UUID_PROTOCOL_HIDP 0x0011 +#define UUID_PROTOCOL_HCRP_CTRL 0x0012 +#define UUID_PROTOCOL_HCRP_DATA 0x0014 +#define UUID_PROTOCOL_HCRP_NOTIF 0x0016 +#define UUID_PROTOCOL_AVCTP 0x0017 +#define UUID_PROTOCOL_AVDTP 0x0019 +#define UUID_PROTOCOL_L2CAP 0x0100 + +// Define common 16-bit service class UUIDs +// +#define UUID_SERVCLASS_SERVICE_DISCOVERY_SERVER 0X1000 +#define UUID_SERVCLASS_BROWSE_GROUP_DESCRIPTOR 0X1001 +#define UUID_SERVCLASS_PUBLIC_BROWSE_GROUP 0X1002 +#define UUID_SERVCLASS_SERIAL_PORT 0X1101 +#define UUID_SERVCLASS_LAN_ACCESS_USING_PPP 0X1102 +#define UUID_SERVCLASS_DIALUP_NETWORKING 0X1103 +#define UUID_SERVCLASS_IRMC_SYNC 0X1104 +#define UUID_SERVCLASS_OBEX_OBJECT_PUSH 0X1105 +#define UUID_SERVCLASS_OBEX_FILE_TRANSFER 0X1106 +#define UUID_SERVCLASS_IRMC_SYNC_COMMAND 0X1107 +#define UUID_SERVCLASS_HEADSET 0X1108 +#define UUID_SERVCLASS_CORDLESS_TELEPHONY 0X1109 +#define UUID_SERVCLASS_INTERCOM 0X1110 +#define UUID_SERVCLASS_FAX 0X1111 +#define UUID_SERVCLASS_HEADSET_AUDIO_GATEWAY 0X1112 +#define UUID_SERVCLASS_PNP_INFORMATION 0X1200 +#define UUID_SERVCLASS_GENERIC_NETWORKING 0X1201 +#define UUID_SERVCLASS_GENERIC_FILETRANSFER 0X1202 +#define UUID_SERVCLASS_GENERIC_AUDIO 0X1203 +#define UUID_SERVCLASS_GENERIC_TELEPHONY 0X1204 + + +//////////////////////////////////////////////////////////////////////////////// +// Definitions for RFCOMM and PORT functions +// + +// +// Define port settings structure send from the application in the +// set settings request, or to the application in the set settings indication. +// +typedef struct +{ + +#define PORT_BAUD_RATE_2400 0x00 +#define PORT_BAUD_RATE_4800 0x01 +#define PORT_BAUD_RATE_7200 0x02 +#define PORT_BAUD_RATE_9600 0x03 +#define PORT_BAUD_RATE_19200 0x04 +#define PORT_BAUD_RATE_38400 0x05 +#define PORT_BAUD_RATE_57600 0x06 +#define PORT_BAUD_RATE_115200 0x07 +#define PORT_BAUD_RATE_230400 0x08 + + UINT8 baud_rate; + +#define PORT_5_BITS 0x00 +#define PORT_6_BITS 0x01 +#define PORT_7_BITS 0x02 +#define PORT_8_BITS 0x03 + + UINT8 byte_size; + +#define PORT_ONESTOPBIT 0x00 +#define PORT_ONE5STOPBITS 0x01 + UINT8 stop_bits; + +#define PORT_PARITY_NO 0x00 +#define PORT_PARITY_YES 0x01 + UINT8 parity; + +#define PORT_ODD_PARITY 0x00 +#define PORT_EVEN_PARITY 0x01 +#define PORT_MARK_PARITY 0x02 +#define PORT_SPACE_PARITY 0x03 + + UINT8 parity_type; + +#define PORT_FC_OFF 0x00 +#define PORT_FC_XONXOFF_ON_INPUT 0x01 +#define PORT_FC_XONXOFF_ON_OUTPUT 0x02 +#define PORT_FC_CTS_ON_INPUT 0x04 +#define PORT_FC_CTS_ON_OUTPUT 0x08 +#define PORT_FC_DSR_ON_INPUT 0x10 +#define PORT_FC_DSR_ON_OUTPUT 0x20 + + UINT8 fc_type; + + UINT8 rx_char1; + +#define PORT_XON_DC1 0x11 + UINT8 xon_char; + +#define PORT_XOFF_DC3 0x13 + UINT8 xoff_char; + +} tPORT_STATE; + +typedef struct +{ + +#define PORT_FLAG_CTS_HOLD 0x01 /* Tx is waiting for CTS signal */ +#define PORT_FLAG_DSR_HOLD 0x02 /* Tx is waiting for DSR signal */ +#define PORT_FLAG_RLSD_HOLD 0x04 /* Tx is waiting for RLSD signal */ + UINT16 flags; + UINT16 in_queue_size; /* Number of bytes in the input queue */ + UINT16 out_queue_size; /* Number of bytes in the output queue */ +} tPORT_STATUS; + + +// +// Define RFCOMM Port events that registered application can receive +// in the callback +// +#define PORT_EV_RXCHAR 0x00000001 /* Any Character received */ +#define PORT_EV_RXFLAG 0x00000002 /* Received certain character */ +#define PORT_EV_TXEMPTY 0x00000004 /* Transmitt Queue Empty */ +#define PORT_EV_CTS 0x00000008 /* CTS changed state */ +#define PORT_EV_DSR 0x00000010 /* DSR changed state */ +#define PORT_EV_RLSD 0x00000020 /* RLSD changed state */ +#define PORT_EV_BREAK 0x00000040 /* BREAK received */ +#define PORT_EV_ERR 0x00000080 /* Line status error occurred */ +#define PORT_EV_RING 0x00000100 /* Ring signal detected */ +#define PORT_EV_CTSS 0x00000400 /* CTS state */ +#define PORT_EV_DSRS 0x00000800 /* DSR state */ +#define PORT_EV_RLSDS 0x00001000 /* RLSD state */ +#define PORT_EV_OVERRUN 0x00002000 /* receiver buffer overrun */ +#define PORT_EV_TXCHAR 0x00004000 /* Any character transmitted */ + +#define PORT_EV_CONNECTED 0x00000200 /* RFCOMM connection established */ +#define PORT_EV_CONNECT_ERR 0x00008000 /* Was not able to establish connection */ + /* or disconnected */ +#define PORT_EV_FC 0x00010000 /* flow control enabled flag changed by remote */ +#define PORT_EV_FCS 0x00020000 /* flow control status true = enabled */ + +// +// To register for RFCOMM events application should provide bitmask with +// corresponding bit set +// +#define PORT_MASK_ALL (PORT_EV_RXCHAR | PORT_EV_TXEMPTY | PORT_EV_CTS | \ + PORT_EV_DSR | PORT_EV_RLSD | PORT_EV_BREAK | \ + PORT_EV_ERR | PORT_EV_RING | PORT_EV_CONNECT_ERR | \ + PORT_EV_DSRS | PORT_EV_CTSS | PORT_EV_RLSDS | \ + PORT_EV_RXFLAG | PORT_EV_TXCHAR | PORT_EV_OVERRUN | \ + PORT_EV_CONNECTED | PORT_EV_FC | PORT_EV_FCS) + + + +// +// Definitions used by RFCOMM to set control leads +// +#define PORT_SET_DTRDSR 0x01 +#define PORT_CLR_DTRDSR 0x02 +#define PORT_SET_CTSRTS 0x03 +#define PORT_CLR_CTSRTS 0x04 +#define PORT_SET_RI 0x05 /* DCE only */ +#define PORT_CLR_RI 0x06 /* DCE only */ +#define PORT_SET_DCD 0x07 /* DCE only */ +#define PORT_CLR_DCD 0x08 /* DCE only */ +#define PORT_SET_BREAK 0x09 +#define PORT_CLR_BREAK 0x0A + +// +// RFCOMM Port modem control leads +// +#define PORT_DTRDSR_ON 0x01 +#define PORT_CTSRTS_ON 0x02 +#define PORT_RING_ON 0x04 +#define PORT_DCD_ON 0x08 + + +// +// RFCOMM Port errors +// +#define PORT_ERR_BREAK 0x01 /* Break condition occured on the peer device */ +#define PORT_ERR_OVERRUN 0x02 /* Overrun is reported by peer device */ +#define PORT_ERR_FRAME 0x04 /* Framing error reported by peer device */ +#define PORT_ERR_RXOVER 0x08 /* Input queue overflow occured */ +#define PORT_ERR_TXFULL 0x10 /* Output queue overflow occured */ + +// +// Flags used in the RFCOMM Port purge function +// +#define PORT_PURGE_TXCLEAR 0x01 +#define PORT_PURGE_RXCLEAR 0x02 + +// +// RFCOMM default MTU size +// +#define RFCOMM_DEFAULT_MTU 127 + + +// +// Valid Security Service Levels +// +#define BTM_SEC_NONE 0x00 /* Nothing required */ +#define BTM_SEC_IN_AUTHORIZE 0x01 /* Inbound call requires authorization */ +#define BTM_SEC_IN_AUTHENTICATE 0x02 /* Inbound call requires authentication */ +#define BTM_SEC_IN_ENCRYPT 0x04 /* Inbound call requires encryption */ +#define BTM_SEC_OUT_AUTHORIZE 0x08 /* Outbound call requires authorization */ +#define BTM_SEC_OUT_AUTHENTICATE 0x10 /* Outbound call requires authentication */ +#define BTM_SEC_OUT_ENCRYPT 0x20 /* Outbound call requires encryption */ +#define BTM_SEC_BOND 0x40 /* Bonding */ + +// +// definitions for security +// + +// +// Predefined security services +// + +#define BTM_SEC_SERVICE_SDP_SERVER 0 +#define BTM_SEC_SERVICE_SERIAL_PORT 1 +#define BTM_SEC_SERVICE_LAN_ACCESS 2 +#define BTM_SEC_SERVICE_DUN 3 +#define BTM_SEC_SERVICE_IRMCSYBC 4 +#define BTM_SEC_SERVICE_OBEX_PUSH 5 +#define BTM_SEC_SERVICE_OBEX_FTP 6 +#define BTM_SEC_SERVICE_IRMCSYNCCMD 7 +#define BTM_SEC_SERVICE_HEADSET 8 +#define BTM_SEC_SERVICE_CORDLESS 9 +#define BTM_SEC_SERVICE_INTERCOM 10 +#define BTM_SEC_SERVICE_FAX 11 +#define BTM_SEC_SERVICE_HEADSET_AG 12 +#define BTM_SEC_SERVICE_PNP_INFO 13 +#define BTM_SEC_SERVICE_GEN_NET 14 +#define BTM_SEC_SERVICE_GEN_FILE 15 +#define BTM_SEC_SERVICE_GEN_AUDIO 16 +#define BTM_SEC_SERVICE_GEN_TEL 17 + +#define BTM_SEC_SERVICE_FIRST_EMPTY 18 + +// Number of services that can be registered with security manager +#define BTM_SEC_MAX_SERVICE_RECORDS 32 + +// Following bits can be provided by host in the trusted_mask field +#define BTM_SEC_TRUST_SDP_SERVER (1 << BTM_SEC_SERVICE_SDP_SERVER) +#define BTM_SEC_TRUST_SERIAL_PORT (1 << BTM_SEC_SERVICE_SERIAL_PORT) +#define BTM_SEC_TRUST_LAN_ACCESS (1 << BTM_SEC_SERVICE_LAN_ACCESS) +#define BTM_SEC_TRUST_DUN (1 << BTM_SEC_SERVICE_DUN) +#define BTM_SEC_TRUST_IRMCSYBC (1 << BTM_SEC_SERVICE_IRMCSYBC) +#define BTM_SEC_TRUST_OBEX_PUSH (1 << BTM_SEC_SERVICE_OBEX_PUSH) +#define BTM_SEC_TRUST_OBEX_FTP (1 << BTM_SEC_SERVICE_OBEX_FTP) +#define BTM_SEC_TRUST_IRMCSYNCCMD (1 << BTM_SEC_SERVICE_IRMCSYNCCMD) +#define BTM_SEC_TRUST_HEADSET (1 << BTM_SEC_SERVICE_HEADSET) +#define BTM_SEC_TRUST_CORDLESS (1 << BTM_SEC_SERVICE_CORDLESS) +#define BTM_SEC_TRUST_IINTERCOM (1 << BTM_SEC_SERVICE_IINTERCOM) +#define BTM_SEC_TRUST_FAX (1 << BTM_SEC_SERVICE_FAX) +#define BTM_SEC_TRUST_HEADSET_AG (1 << BTM_SEC_SERVICE_HEADSET_AG) +#define BTM_SEC_TRUST_PNP_INFO (1 << BTM_SEC_SERVICE_PNP_INFO) +#define BTM_SEC_TRUST_GEN_NET (1 << BTM_SEC_SERVICE_GEN_NET) +#define BTM_SEC_TRUST_GEN_FILE (1 << BTM_SEC_SERVICE_GEN_FILE) +#define BTM_SEC_TRUST_GEN_AUDIO (1 << BTM_SEC_SERVICE_GEN_AUDIO) +#define BTM_SEC_TRUST_GEN_TEL (1 << BTM_SEC_SERVICE_GEN_TEL) + +#define BTM_SEC_TRUST_ALL 0xFFFFFFFF + + + +// BtIfObexHeaders.cpp : Definitions for the OBEX headers objects. +// + +#define OBEX_MINIMUM_MTU (255) +#define OBEX_MAX_CLIENTS 4 +#define OBEX_MAX_SERVERS 4 + + +#ifdef _WIN32_WCE + #define OBEX_MAX_CSESSIONS 1 + #define OBEX_MAX_SSESSIONS 1 /* Current version only allows 1 session per server */ +#else + #define OBEX_MAX_CSESSIONS 4 + #define OBEX_MAX_SSESSIONS 3 +#endif + +#define OBEX_MAX_TARGET 3 +#define OBEX_MAX_AUTH_CHALLENGE 3 +#define OBEX_MAX_AUTH_RESPONSE 3 +#define OBEX_MAX_HTTP 3 +#define OBEX_MAX_APP_PARAM 3 +#define OBEX_MAX_USER_HDR 4 +#define OBEX_TIMER_SEED 60 + +#define OBEX_TIME_LOCAL 0x01 +#define OBEX_TIME_UTC 0x02 +#define OBEX_API +#define OBEX_CLIENT_INCLUDED (TRUE) +#define OBEX_SERVER_INCLUDED (TRUE) + +#ifndef BT_TYPES_H +#define GKI_BUF3_SIZE 700 +#endif +#define OBEX_DATA_POOL_SIZE (GKI_BUF3_SIZE) + +#define OBEX_DATA_BUFFER_OVERHEAD (41) +// for now, limit MTU to 640 - overhead +#ifndef BT_TYPES_H +#define OBEX_DESIRED_MTU (640 - OBEX_DATA_BUFFER_OVERHEAD) +#endif +#if (OBEX_DESIRED_MTU + OBEX_DATA_BUFFER_OVERHEAD > OBEX_DATA_POOL_SIZE) +#define OBEX_DEFAULT_MTU (OBEX_DATA_POOL_SIZE - OBEX_DATA_BUFFER_OVERHEAD) +#else +#define OBEX_DEFAULT_MTU (OBEX_DESIRED_MTU) +#endif + +/* +** Define OBEX Request Codes +*/ +#define OBEX_REQ_CONNECT 0x00 +#define OBEX_REQ_DISCONNECT 0x01 +#define OBEX_REQ_PUT 0x02 +#define OBEX_REQ_GET 0x03 +#define OBEX_REQ_SETPATH 0x05 +#define OBEX_REQ_ABORT 0x7f +#define OBEX_FINAL 0x80 +#define OBEX_UNREGISTER 0xff + +/* +** Define well-known OBEX Client data types +*/ +typedef UINT32 tOBEX_CLIENT_HANDLE; /* Application Handle */ +typedef UINT32 tOBEX_CSESSION_HANDLE; /* Session Handle */ + +/* +** Define well-known OBEX Server data types +*/ +typedef UINT32 tOBEX_SERVER_HANDLE; /* Application Handle */ +typedef UINT32 tOBEX_SSESSION_HANDLE; /* Session Handle */ + + +/* +** Define OBEX error codes returned by OBEX API Functions and Application +** Callback Functions +*/ +typedef enum +{ + OBEX_SUCCESS = 0, /* Operation was successful or accepted */ + OBEX_FAIL, /* Operation failed or was rejected */ + OBEX_ERROR, /* Internal OBEX error */ + OBEX_ERR_RESOURCES, /* Insufficient resources */ + OBEX_ERR_NO_CB, /* Calback for request is missing */ + OBEX_ERR_DUP_SERVER, /* Server for 'Target' already register with OBEX */ + OBEX_ERR_RESPONSE, /* Peer rejected request */ + OBEX_ERR_UNK_APP, /* Unknown Application Handle (unregistered?) */ + OBEX_ERR_PARAM, /* Invalid or missing parameter value */ + OBEX_ERR_CLOSED, /* Session is closed */ + OBEX_ERR_ABORTED, /* Operation was aborted */ + OBEX_ERR_STATE, /* Request is invalid for current state */ + OBEX_ERR_NA, /* API call not allowed at this time */ + OBEX_ERR_HEADER, /* Invalid data in tOBEX_HEADERS */ + OBEX_ERR_TOO_BIG, /* The data presented in the tOBEX_HEADERS */ + /* structure is larger than the maximum */ + /* size allowed for the request */ + OBEX_ERR_TIMEOUT /* Timeout */ +} tOBEX_ERRORS; +#define OBEX_MAX_ERROR OBEX_ERR_TIMEOUT + +/* +** Define OBEX Response Code values used in OBEX Responses sent by +** the OBEX Server to the OBEX Client. These codes are taken +** from the 'IrDA Object Exchange Protocol (IROBEX)' specification +** with the high bit (final bit) removed. The OBEX Core internally +** manages the 'final bit'. +*/ +typedef enum +{ + OBEX_RSP_DEFAULT = 0x00, + OBEX_RSP_CONTINUE = 0x10, + OBEX_RSP_OK = 0x20, + OBEX_RSP_CREATED = 0x21, + OBEX_RSP_ACCEPTED = 0x22, + OBEX_RSP_NON_AUTHORITATIVE_INFO = 0x23, + OBEX_RSP_NO_CONTENT = 0x24, + OBEX_RSP_RESET_CONTENT = 0x25, + OBEX_RSP_PARTIAL_CONTENT = 0x26, + OBEX_RSP_MULTIPLE_CHOICES = 0x30, + OBEX_RSP_MOVED_PERMANENTLY = 0x31, + OBEX_RSP_MOVED_TEMPORARILY = 0x32, + OBEX_RSP_SEE_OTHER = 0x33, + OBEX_RSP_NOT_MODIFIED = 0x34, + OBEX_RSP_USE_PROXY = 0x35, + OBEX_RSP_BAD_REQUEST = 0x40, + OBEX_RSP_UNAUTHORIZED = 0x41, + OBEX_RSP_PAYMENT_REQUIRED = 0x42, + OBEX_RSP_FORBIDDEN = 0x43, + OBEX_RSP_NOT_FOUND = 0x44, + OBEX_RSP_METHOD_NOT_ALLOWED = 0x45, + OBEX_RSP_NOT_ACCEPTABLE = 0x46, + OBEX_RSP_PROXY_AUTHENTICATION_REQUIRED = 0x47, + OBEX_RSP_REQUEST_TIME_OUT = 0x48, + OBEX_RSP_CONFLICT = 0x49, + OBEX_RSP_GONE = 0x4A, + OBEX_RSP_LENGTH_REQUIRED = 0x4B, + OBEX_RSP_PRECONDITION_FAILED = 0x4C, + OBEX_RSP_REQUESTED_ENTITY_TOO_LARGE = 0x4D, + OBEX_RSP_REQUEST_URL_TOO_LARGE = 0x4E, + OBEX_RSP_UNSUPPORTED_MEDIA_TYPE = 0x4F, + OBEX_RSP_INTERNAL_SERVER_ERROR = 0x50, + OBEX_RSP_NOT_IMPLEMENTED = 0x51, + OBEX_RSP_BAD_GATEWAY = 0x52, + OBEX_RSP_SERVICE_UNAVAILABLE = 0x53, + OBEX_RSP_GATEWAY_TIMEOUT = 0x54, + OBEX_RSP_HTTP_VERSION_NOT_SUPPORTED = 0x55, + OBEX_RSP_DATABASE_FULL = 0x60, + OBEX_RSP_DATABASE_LOCKED = 0x61 +} tOBEX_RESPONSE_CODE; + +/* +** The tOBEX_CLIENT_HANDLE, tOBEX_SERVER_HANDLE, tOBEX_CSESSION_HANDLE +** and tOBEX_SSESSION_HANDLE each consist of two 16 bit values. The high +** order 16 bits contain an arbitrary ordinal assigned when the client +** or server registers or when a session is opened. The low order 16 +** bits contain the tOBEX_GLOBAL_CLIENT.client_ctrl, +** tOBEX_GLOBAL_SERVER.server_ctrl, tOBEX_CLIENT_CTRL.cses_ctrl or +** tOBEX_SERVER_CTRL.sses_ctrl index value for the client, server or +** session. The following macros encode an ordinal and index into a +** tOBEX_CLIENT_HANDLE, tOBEX_SERVER_HANDLE, tOBEX_CSESSION_HANDLE or +** tOBEX_SSESSION_HANDLE and decode an ordinal and index from a +** handle. +*/ +#define OBEX_ENCODE_HANDLE(handle,ordinal,index) (handle = ((ordinal << 16) | (index & 0xFFFF))) +#define OBEX_DECODE_HANDLE(handle,ordinal,index) (ordinal = (handle >> 16), index = (handle & 0xFFFF)) + +/* +* The following defines and describes the tOBEX_HEADERS structure +* and the other structures that are embedded in the tOBEX_HEADERS +* structure. +* +* The tOBEX_HEADERS structure is used by many calls to OBEX API +* and Application Callback Functions. It contains the information +* found in the OBEX Headers supported by this implementation of +* OBEX as defined in the 'IrDA Object Exchange Protocol (IrOBEX)' +* specification. +* +* When an application wants to send information to the peer +* application it creates an instance of the tOBEX_HEADERS structure +* and passes a pointer to the structure to the appropriate OBEX API +* Function. OBEX will process the information found in the +* structure and return to the application. If the application has +* registered with OBEX as being 'well behaved' (see OBEX_ClientRegister +* and OBEX_ServerRegister functions), OBEX will assume that the +* tOBEX_HEADERS pointer points to a GKI buffer (without the BT_HDR +* prefix) and will assume ownership of the buffer. If the application +* has not registered as 'well behaved', OBEX will assume that the +* tOBEX_HEADERS pointer points to static memory and will not alter +* the memory pointed to by the pointer. For best performance, it +* is recommended that applications be well behaved. +* +* When OBEX receives information from the peer OBEX that it needs to +* deliver to the application, it creates an instance of the +* tOBEX_HEADERS structure and passes a pointer to it to the appropriate +* Application Callback Function. The tOBEX_HEADERS structure resides +* in a GKI buffer (without the BT_HDR prefix). If the application has +* registered as 'well behaved', OBEX assumes that the application will +* assume ownership of the buffer and release it as appropriate. If the +* application has not registered as 'well behaved', OBEX assumes that +* the application treats the buffer as static memory and once the +* Application Callback Function returns to OBEX, OBEX will dispose of +* the buffer appropriately. +*/ + + +/* +* The tOBEX_UNI_HDR structure is embedded in the tOBEX_HEADERS +* structure. It is used to hold the contents of an OBEX +* 'UNICODE' type header. These headers contain null-terminated +* UNICODE text strings. The struct contains a 'p_string' member +* that points to the null-terminated UNICODE string and a 'str_len' +* member that indicates the number of characters in the UNICODE string. +* NOTE: The 'str_len' member includes the terminating NULL +* character. Therefore, by example, if a Name header contains +* the UNICODE text string "JUMAR.TXT", the 'p_string' member of the +* struct will point to a data area containing +* the hex values (displayed in Big-endian format): +* "00 4A 00 55 00 4D 00 41 00 52 00 2E 00 54 00 58 00 54 00 00" +* and the 'str_len' member will contain the value 10. +* +* If a 'UNICODE' type header present in an OBEX request or +* response, the contents of that header will be stored in a +* tOBEX_UNI_HDR struct and the 'flag' member of the tOBEX_HEADERS +* struct will indicate that the header is present. Some headers may +* be present but contain no data. In such a case, the 'flag' member +* of the tOBEX_HEADERS struct will indicate that the header is +* present but the 'str_len' member and 'p_string' member of the +* associated header struct will be zero. +* +* If the application has registered with OBEX as being 'well behaved', +* it is assumed that each 'p_string' member in use points to a GKI +* buffer and ownership of each buffer will follow the rules specified +* above for the tOBEX_HEADERS structure. If the application has not +* registered with OBEX as being 'well behaved', the OBEX API functions +* assume that each 'p_string' member points to static memory it does +* not alter that memory. OBEX also assumes that the Application +* Callback Functions will treat the memory pointed to by each 'p_string' +* member as static and will not alter that memory. +*/ + +/* OBEX UNICODE Type Header - null terminated UNICODE string */ +typedef struct +{ + UINT32 str_len; + WCHAR *p_string; +} tOBEX_UNI_HDR; + + +/* +* The tOBEX_OCTET_HDR structure is embedded in the tOBEX_HEADERS +* structure. It is used to hold the contents of an OBEX +* 'octet-array' type header. These headers contain unstructured octet +* arrays. The struct contains a 'p_array' member that points to an +* array of octets and a 'length' member that indicates the length of +* the array. +* +* If a header of the 'octet-array' type is present in an OBEX request or +* response, the contents of that header will be stored in its +* corresponding struct and the 'flag' member of the tOBEX_HEADERS +* struct will indicate that the header is present. Some headers may +* be present but contain no data. In such a case, the 'flag' member +* of the tOBEX_HEADERS struct will indicate that the header is +* present but the 'length' member and 'p_array' member of the +* associated header struct will be zero. +* +* Some header types may occur multiple times in a request or response. +* For these types of headers, the tOBEX_HEADERS structure contains +* an array of embedded structures for that header type and a counter +* indicating the number of those headers that are present. The +* 'flag' member of the tOBEX_HEADERS struct will indicate that at +* least one instance of the header is present in the request/response +* and the corresponding 'num_...' member of the tOBEX_HEADERS struct +* will indicate the actual number of instances included in the array +* of structures. +* +* If the application has registered with OBEX as being 'well behaved', +* it is assumed that each 'p_array' member in use points to a GKI +* buffer and ownership of each buffer will follow the rules specified +* above for the tOBEX_HEADERS structure. If the application has not +* registered with OBEX as being 'well behaved', the OBEX API functions +* assume that each 'p_array' member points to static memory it does +* not alter that memory. OBEX also assumes that the Application +* Callback Functions will treat the memory pointed to by each 'p_array' +* member as static and will not alter that memory. +*/ + +/* OBEX Octet-Array Type Header - unstructured octet array */ +typedef struct +{ + UINT32 length; + UINT8 *p_array; +} tOBEX_OCTET_HDR; + + + +/* +* The tOBEX_HEADERS structure contains three arrays of the +* following tOBEX_HDR_TRIPLET structure to hold information +* found in the OBEX Application Request-Response Parameters +* Header, the Authenticate Challenge Header and the +* Authenticate Response Header. Each of these headers contain +* one or more 'Triplets' and each triplet is stored in an +* entry in its associated tOBEX_HDR_TRIPLET array. The +* tOBEX_HEADERS structure contains a 'num_app_params' member +* that indicates the number of triplets contained in the +* Application Request-Response Parameters Header, a +* 'num_auth_challenge' member that indicates the number of +* triplets contained in the Authenticate Challenge Header and +* a 'num_auth_response' member that indicates the number of +* triplets contains in the Authenticate Response Header. +* +* If the application has registered with OBEX as being 'well behaved', +* it is assumed that each 'p_array' member in use points to a GKI +* buffer and ownership of each buffer will follow the rules specified +* above for the tOBEX_HEADERS structure. If the application has not +* registered with OBEX as being 'well behaved', the OBEX API functions +* assume that each 'p_array' member points to static memory it does +* not alter that memory. OBEX also assumes that the Application +* Callback Functions will treat the memory pointed to by each 'p_array' +* member as static and will not alter that memory. +*/ +typedef struct obex_hdr_triplet_s +{ + UINT8 tag; + UINT8 length; + UINT8 *p_array; +} tOBEX_HDR_TRIPLET; + +/* +* The tOBEX_HEADERS structure contains an array of the +* following tOBEX_USER_HDR structure to hold information +* found in the OBEX User Defined Headers. The OBEX User +* Defined Header is formatted according to the 'IrDA Object +* Exchange Protocol (IrOBEX)' specification. The specification +* requires that the two high-order bits of the first octet of +* the header indicate the format and, by implication, the size +* of the data contained in the header and the remaining bits of +* the first octet be within the range of 0x30 to 0x3F. +* +* By performing a logical AND on the 'id' member of the +* tOBEX_USER_HDR structure and the OBEX_USER_TYPE_MASK constant +* and comparing the results with each of the other +* 'OBEX_USER_TYPE_....' constants, the User Defined Header +* format can be determined. The appropriate member of the +* 'value' union can then be accessed and the 'length' member +* can be interpreted as follows: +* +* ---------------------- ----------- ------------------- +* | If the high-order | | | +* | 2 bits of id are | Then | Contains | +* |----------------------|-----------|-------------------| +* | OBEX_USER_TYPE_UNI | p_string | Pointer to null | +* | | | terminated | +* | | | UNICODE text | +* | | | string | +* | |-----------|-------------------| +* | | length | Number of | +* | | | characters in | +* | | | the UNICODE text | +* | | | string (including | +* | | | the terminating | +* | | | NULL character) | +* |----------------------|-----------|-------------------| +* | OBEX_USER_TYPE_ARRAY | p_array | Pointer to | +* | | | unstructured | +* | | | octet array | +* | |-----------|-------------------| +* | | length | Number octets of | +* | | | data in the array | +* |----------------------|-----------|-------------------| +* | OBEX_USER_TYPE_BYTE | user_byte | One byte of | +* | | | user data | +* | |-----------|-------------------| +* | | length | N/A | +* |----------------------|-----------|-------------------| +* | OBEX_USER_TYPE_INT | user_int | One 32-bit | +* | | | integer of user | +* | | | data | +* | |-----------|-------------------| +* | | length | N/A | +* ---------------------- ----------- ------------------- +* +* If the application has registered with OBEX as being 'well behaved', +* it is assumed that each 'p_array' and 'p_string' member in use +* points to a GKI buffer and ownership of each buffer will follow the +* rules specified above for the tOBEX_HEADERS structure. If the +* application has not registered with OBEX as being 'well behaved', +* the OBEX API functions assume that each 'p_array' member points to +* static memory it does not alter that memory. OBEX also assumes +* that the Application Callback Functions will treat the memory +* pointed to by each 'p_array' member as static and will not alter +* that memory. +*/ +typedef struct +{ + UINT8 id; +#define OBEX_USER_TYPE_MASK 0xC0 +#define OBEX_USER_TYPE_UNI 0x00 /* Null terminated ASCII text */ +#define OBEX_USER_TYPE_ARRAY 0x40 /* Unstructured octet array */ +#define OBEX_USER_TYPE_BYTE 0x80 /* Single byte */ +#define OBEX_USER_TYPE_INT 0xC0 /* 32 bit integer */ + UINT16 length; + union + { + WCHAR *p_string; + UINT8 *p_array; + UINT8 user_byte; + UINT32 user_int; + } value; +} tOBEX_USER_HDR; + + + +/* +* The following is the tOBEX_HEADERS structure. +* +* The 'rsp_code' member contains the OBEX Response Code used in the +* OBEX Response Packet. It is stored without the high-order (final) +* bit set. The OBEX Core manages the 'final bit' in Response +* Packets internally. When the Server Application calls an OBEX +* Server API Function it indicates the success or failure of the request +* by passing a tOBEX_ERRORS enumerator to the appropriate API. If the +* Server Application wants to be more specific in the type of error +* returned to the Client Application, it can set the specific OBEX +* Response Code desired in the 'rsp_code' member of the tOBEX_HEADERS +* structure. If the Server Application does not set the 'rsp_code' +* member to a valid tOBEX_ERRORS enumerator, the OBEX Core will +* generate an appropriate OBEX Response Code based on the tOBEX_ERRORS +* enumerator passed as a parameter to the Confirmation API Function and +* the current operation being performed. +* +* The 'flag' member indicates which headers are present. +* +* If the 'flag' member indicates that the Count Header is +* present, the 'count' member of the tOBEX_HEADERS structure +* will contain the value found in the Count Header. +* +* If the 'flag' member indicates that the Name Header, Type Header, +* Description Header, Target Header, HTTP Header, Body (or +* End-of-Body) Header, Who Header, Application Request-Response Header, +* Authentication Challenge Header, Authentication Response Header, +* Object Class Header or User Defined Header is present, the +* corresponding data will be present in the tOBEX_HEADERS structure +* as specified above. +* +* If the 'flag' member indicates that the Length Header is +* present, the 'hint_of_length' member of the tOBEX_HEADERS +* structure will contain the value found in the Length Header. +* +* THE FOLLOWING DESCRIPTION APPLIES ONLY TO THE OBEX NON_LEGACY API: +* If the 'flag' member indicates that the Time Header is present, +* the 'time' member contains the contents of the Time Header +* as seconds since midnight, January 1, 1970, UTC ('Universal +* Coordinated Time' also known as GMT or 'Greenwich Mean Time') or +* Local Time. The 'time_qualifier' member of the tOBEX_HEADERS +* structure indicates whether the time in the header is UTC or Local +* Time. The 'IrDA Object Exchange Protocol (IrOBEX)' specification +* recommends that the Time Header be formatted according to ISO 8601. +* The OBEX core performs the conversion between the ISO 8601 format +* and the tOBEX_HEADERS structure format as appropriate. +* END OF OBEX NON-LEGACY API DESCRIPTION +* +* NOTE!!!!! +* The bit positions for each header represented in the 'flag' member +* must not be altered because the OBEX Core relies on them being +* assigned as defined here. +* +*/ +typedef struct +{ + UINT32 flag; +#define OBEX_FLAG_INTERNAL1 0x00000001 /* Used internally by OBEX Core */ +#define OBEX_FLAG_COUNT 0x00000002 +#define OBEX_FLAG_NAME 0x00000004 +#define OBEX_FLAG_TYPE 0x00000008 +#define OBEX_FLAG_LENGTH 0x00000010 +#define OBEX_FLAG_OBJECT_CLASS 0x00000020 +#define OBEX_FLAG_TARGET 0x00000040 +#define OBEX_FLAG_WHO 0x00000080 +#define OBEX_FLAG_TIME 0x00000100 +#define OBEX_FLAG_INTERNAL2 0x00000200 /* Used internally by OBEX Core */ +#define OBEX_FLAG_DESCRIPTION 0x00000400 +#define OBEX_FLAG_AUTH_CHALLENGE 0x00000800 +#define OBEX_FLAG_AUTH_RESPONSE 0x00001000 +#define OBEX_FLAG_HTTP 0x00002000 +#define OBEX_FLAG_APPL_REQ_RSP 0x00004000 +#define OBEX_FLAG_USER_DEFINED 0x00008000 +#define OBEX_FLAG_BODY 0x00010000 +#define OBEX_FLAG_BODY_END 0x00020000 +/* flag bits legal for applications to set */ +#define OBEX_FLAG_LEGAL_BITS \ + (OBEX_FLAG_COUNT + \ + OBEX_FLAG_NAME + \ + OBEX_FLAG_TYPE + \ + OBEX_FLAG_LENGTH + \ + OBEX_FLAG_OBJECT_CLASS + \ + OBEX_FLAG_TARGET + \ + OBEX_FLAG_WHO + \ + OBEX_FLAG_TIME + \ + OBEX_FLAG_DESCRIPTION + \ + OBEX_FLAG_AUTH_CHALLENGE + \ + OBEX_FLAG_AUTH_RESPONSE + \ + OBEX_FLAG_HTTP + \ + OBEX_FLAG_APPL_REQ_RSP + \ + OBEX_FLAG_USER_DEFINED + \ + OBEX_FLAG_BODY + \ + OBEX_FLAG_BODY_END) + UINT32 internal1; /* Used internally by OBEX Core */ + UINT32 count; + tOBEX_UNI_HDR uni_name; + tOBEX_OCTET_HDR type; + UINT32 hint_of_length; + tOBEX_OCTET_HDR object_class; + UINT32 num_target; + tOBEX_OCTET_HDR target[OBEX_MAX_TARGET]; + tOBEX_OCTET_HDR who; + UINT32 time_value; + UINT8 time_qualifier; +#define OBEX_TIME_LOCAL 0x01 +#define OBEX_TIME_UTC 0x02 + tOBEX_UNI_HDR uni_description; + UINT32 num_auth_challenge; + tOBEX_HDR_TRIPLET auth_challenge[OBEX_MAX_AUTH_CHALLENGE]; + UINT32 num_auth_response; + tOBEX_HDR_TRIPLET auth_response[OBEX_MAX_AUTH_RESPONSE]; + UINT32 num_http; + tOBEX_OCTET_HDR http[OBEX_MAX_HTTP]; + UINT32 num_app_param; + tOBEX_HDR_TRIPLET app_param[OBEX_MAX_APP_PARAM]; + UINT32 num_user; + tOBEX_USER_HDR user[OBEX_MAX_USER_HDR]; + tOBEX_OCTET_HDR body; +} tOBEX_HEADERS; + + +#endif // !defined (WIDCOMMSDK_EXPORTS) + +/* Define the L2CAP connection result codes +*/ +#define L2CAP_CONN_OK 0 +#define L2CAP_CONN_PENDING 1 +#define L2CAP_CONN_NO_PSM 2 +#define L2CAP_CONN_SECURITY_BLOCK 3 +#define L2CAP_CONN_NO_RESOURCES 4 +#define L2CAP_CONN_TIMEOUT 0xEEEE + +#define L2CAP_CONN_NO_LINK 255 /* Add a couple of our own for internal use */ + + +// Define a structure to hold the configuration parameters. Since the +// parameters are optional, for each parameter there is a boolean to +// use to signify its presence or absemce. +// +// NOTE: This structure is used externally from (or above) the SDK +// and is mapped to the tL2CAP_CFG_INFO structure when used +// in the CL2CapConn::Reconfigure method. This is done so we +// only expose BOOL types externally. +// +typedef struct +{ + UINT16 result; // Only used in confirm messages + BOOL mtu_present; + UINT16 mtu; + BOOL qos_present; + FLOW_SPEC qos; + BOOL flush_to_present; + UINT16 flush_to; + + UINT16 flags; // Internally used by L2CAP + +} tL2CAP_CONFIG_INFO; + +#pragma pack () + +#endif // !defined(AFX_WIDCOMMTYPES_H__1F5ED990_6FC6_4B0D_882C_8D7C98C16A06__INCLUDED_) diff --git a/inc/BtIfDefinitions13.h b/inc/BtIfDefinitions13.h new file mode 100644 index 0000000..5de0b1b --- /dev/null +++ b/inc/BtIfDefinitions13.h @@ -0,0 +1,1221 @@ +///////////////////////////////////////////////////////////////////////////// +// +// Name BtIfDefinitions.h +// $Header: +// +// Function this file contains Widcomm SDK type definitions +// +// Date Modification +// ---------------------------------- +// 12/17/2000 JF Create +// +// Copyright (c) 2000-2002, WIDCOMM Inc., All Rights Reserved. +// Proprietary and confidential. +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef _BTIFDEFINITIONS_H +#define _BTIFDEFINITIONS_H + + +// Ensure alignment across all builds +// +#ifdef _WIN32_WCE +#pragma pack (8) +#else +#pragma pack (1) +#endif + +// +// Define return codes from the SDP service functions +// +typedef enum +{ + SDP_OK, + SDP_COULD_NOT_ADD_RECORD, + SDP_INVALID_RECORD, + SDP_INVALID_PARAMETERS + +} SDP_RETURN_CODE; + +// +// Define SPP connection states +// +typedef enum +{ + SPP_CONNECTED, // port now connected + SPP_DISCONNECTED // port now disconnected +} SPP_STATE_CODE; + + +// Maximum number of UUIDs in a service class ID list +// +#define MAX_UUIDS_PER_SEQUENCE 3 + +// Maximum service name length +// +#define BT_MAX_SERVICE_NAME_LEN 100 + +// Maximum protocol list elements in a record +// +#define MAX_PROTOCOL_LIST_ELEM 3 + +#define SDP_MAX_LIST_ELEMS 3 +#define MAX_ELEM_IN_SEQ 10 + + +// +// Define a structure to hold attribute values when +// read from the discovery database. Note that the +// attribute may be a sequence, in which case the +// number of elements will be greater than 1. +// +#define MAX_SEQ_ENTRIES 20 +#define MAX_ATTR_LEN 256 + +typedef struct +{ + int num_elem; + + struct + { + #define ATTR_TYPE_INT 0 // Attribute value is an integer + #define ATTR_TYPE_TWO_COMP 1 // Attribute value is an 2's complement integer + #define ATTR_TYPE_UUID 2 // Attribute value is a UUID + #define ATTR_TYPE_BOOL 3 // Attribute value is a boolean + #define ATTR_TYPE_ARRAY 4 // Attribute value is an array of bytes + + int type; + int len; // Length of the attribute + BOOL start_of_seq; // TRUE for each start of sequence + union + { + unsigned char u8; // 8-bit integer + unsigned short u16; // 16-bit integer + unsigned long u32; // 32-bit integer + BOOL b; // Boolean + unsigned char array[MAX_ATTR_LEN]; // Variable length array + } val; + + } elem [MAX_SEQ_ENTRIES]; + +} SDP_DISC_ATTTR_VAL; + +/////////////////////////////////////////////////////////////////////////////// +///// definitions used in FTP client apps to present remote server directory listings +// +#define MAX_NAME_SIZE 255 +#define MAX_ENTRY_SIZE 1000 +#define DATE_TIME_SIZE 15 + +typedef struct +{ + WCHAR name[MAX_NAME_SIZE + 1]; + BOOL is_folder; + WCHAR date_created[DATE_TIME_SIZE + 1]; + WCHAR date_modified[DATE_TIME_SIZE + 1]; + WCHAR date_accessed[DATE_TIME_SIZE + 1]; + ULONG file_size; + +#define FTP_READ_PERM (0x01) +#define FTP_WRITE_PERM (0x02) +#define FTP_DELETE_PERM (0x04) + + WCHAR user_perm; + WCHAR group_perm; + WCHAR other_perm; +} tFTP_FILE_ENTRY; + + + +#ifndef BT_CONN_STATS_DEFINED +#define BT_CONN_STATS_DEFINED +typedef struct +{ + UINT32 bIsConnected; + INT32 Rssi; + UINT32 BytesSent; + UINT32 BytesRcvd; + UINT32 Duration; +} tBT_CONN_STATS; +#endif //BT_CONN_STATS_DEFINED + +///////////////////////////////////////////////////////////////////////////////////////// +// Definitions for applications only +// + +#if !defined (WIDCOMMSDK_EXPORTS) + +// +// Define some types used by the Widcomm SDK +// +typedef unsigned char UINT8; +typedef unsigned short UINT16; +typedef unsigned int UINT32; + +// +// Common Bluetooth field definitions +// +#define BD_ADDR_LEN 6 /* Device address length */ +typedef UINT8 BD_ADDR[BD_ADDR_LEN]; /* Device address */ +typedef UINT8 *BD_ADDR_PTR; /* Pointer to Device Address */ + +#define LINK_KEY_LEN 16 +typedef UINT8 LINK_KEY[LINK_KEY_LEN]; /* Link Key */ + +#define PIN_CODE_LEN 16 +typedef UINT8 PIN_CODE[PIN_CODE_LEN]; /* Pin Code (upto 128 bits) MSB is 0 */ +typedef UINT8 *PIN_CODE_PTR; /* Pointer to Pin Code */ + +#define DEV_CLASS_LEN 3 +typedef UINT8 DEV_CLASS[DEV_CLASS_LEN]; /* Device class */ +typedef UINT8 *DEV_CLASS_PTR; /* Pointer to Device class */ + +// +// Coding of the DEV_CLASS field. Refer to Bluetooth Assigned Numbers +// specification for further information on device class. +// +// First byte - Service class of the CoD, byte [0] +#define SERV_CLASS_NETWORKING (1 << 1) +#define SERV_CLASS_RENDERING (1 << 2) +#define SERV_CLASS_CAPTURING (1 << 3) +#define SERV_CLASS_OBJECT_TRANSFER (1 << 4) +#define SERV_CLASS_OBJECT_AUDIO (1 << 5) +#define SERV_CLASS_OBJECT_TELEPHONY (1 << 6) +#define SERV_CLASS_OBJECT_INFORMATION (1 << 7) + +// Second byte - high order +#define SERV_CLASS_LIMITED_DISC_MODE (0x20) + +// Also 2nd byte - Major Device class - low 5 bits +#define MAJOR_DEV_CLASS_MASK 0x1F + +#define MAJOR_DEV_CLASS_MISC 0x00 +#define MAJOR_DEV_CLASS_COMPUTER 0x01 +#define MAJOR_DEV_CLASS_PHONE 0x02 +#define MAJOR_DEV_CLASS_LAN_ACCESS 0x03 +#define MAJOR_DEV_CLASS_AUDIO 0x04 +#define MAJOR_DEV_CLASS_PERIPHERAL 0x05 +#define MAJOR_DEV_CLASS_IMAGING 0x06 +#define MAJOR_DEV_CLASS_UNSPECIFIED 0x1F + +// 3rd byte - Minor Device class, hi 6 bits +#define MINOR_DEV_CLASS_MASK 0xFC + +// Minor device class if major is computer +#define MINOR_DEV_CLASS_COMP_UNCLASSIFIED 0x00 +#define MINOR_DEV_CLASS_COMP_WORKSTATION (0x01 << 2) +#define MINOR_DEV_CLASS_COMP_SERVER (0x02 << 2) +#define MINOR_DEV_CLASS_COMP_LAPTOP (0x03 << 2) +#define MINOR_DEV_CLASS_COMP_HANDHELD (0x04 << 2) +#define MINOR_DEV_CLASS_COMP_PALM (0x05 << 2) + +// Minor device class if major is phone +#define MINOR_DEV_CLASS_PHONE_UNCLASSIFIED 0x00 +#define MINOR_DEV_CLASS_PHONE_CELLULAR (0x01 << 2) +#define MINOR_DEV_CLASS_PHONE_CORDLESS (0x02 << 2) +#define MINOR_DEV_CLASS_PHONE_SMART (0x03 << 2) +#define MINOR_DEV_CLASS_PHONE_MODEM (0x04 << 2) + +// Minor device class if major is imaging +#define MINOR_DEV_CLASS_IMAGING_UNCLASSIFIED 0x00 +#define MINOR_DEV_CLASS_IMAGING_DISPLAY (0x04 << 2) +#define MINOR_DEV_CLASS_IMAGING_CAMERA (0x08 << 2) +#define MINOR_DEV_CLASS_IMAGING_SCANNER (0x10 << 2) +#define MINOR_DEV_CLASS_IMAGING_PRINTER (0x20 << 2) + +#define MINOR_DEV_CLASS_AUDIO_UNCLASSIFIED 0x00 + +// Minor device class if major is audio +#define MINOR_DEV_CLASS_AUDIO_UNCLASSIFIED 0x00 +#define MINOR_DEV_CLASS_AUDIO_HEADSET (0x01 << 2) + + +#define BD_NAME_LEN 248 +typedef UINT8 BD_NAME[BD_NAME_LEN]; /* Device name */ +typedef UINT8 *BD_NAME_PTR; /* Pointer to Device name */ + + +// L2CAP Configuration QOS structure +// +#ifndef BT_TYPES_H +typedef struct +{ + UINT8 qos_flags; /* TBD */ + +// Values for service_type +// +#define NO_TRAFFIC 0 +#define BEST_EFFORT 1 +#define GUARANTEED 2 + + UINT8 service_type; /* see below */ + UINT32 token_rate; /* bytes/second */ + UINT32 token_bucket_size; /* bytes */ + UINT32 peak_bandwidth; /* bytes/second */ + UINT32 latency; /* microseconds */ + UINT32 delay_variation; /* microseconds */ + +} FLOW_SPEC; +#endif + + +#define L2CAP_MIN_MTU 48 + +//Zong, need to talk to ask to see if we can make them the same +#ifdef _WIN32_WCE + #define L2CAP_MAX_MTU 1400 + #define L2CAP_DEFAULT_MTU 672 +#else + #define L2CAP_MAX_MTU 1696 + #define L2CAP_DEFAULT_MTU 1691 +#endif + +#define L2CAP_DEFAULT_FLUSH_TO 0xFFFF + + +// This structure is used to add protocol lists and find protocol elements +// +#ifndef SDP_MAX_PROTOCOL_PARAMS +#define SDP_MAX_PROTOCOL_PARAMS 1 +#endif + +typedef struct +{ + UINT16 protocol_uuid; + UINT16 num_params; + UINT16 params[SDP_MAX_PROTOCOL_PARAMS]; +} tSDP_PROTOCOL_ELEM; + +typedef struct +{ + UINT16 num_elems; + tSDP_PROTOCOL_ELEM list_elem[SDP_MAX_LIST_ELEMS]; +} tSDP_PROTO_LIST_ELEM; + +// Maximum UUID size - 16 bytes, and structure to hold any type of UUID. +// +#define MAX_UUID_SIZE 16 + +#ifndef BT_TYPES_H +typedef struct +{ +#define LEN_UUID_16 2 +#define LEN_UUID_32 4 +#define LEN_UUID_128 16 + + UINT16 len; + UINT8 pad[14]; + + union + { + UINT16 uuid16; + UINT32 uuid32; + UINT8 uuid128[MAX_UUID_SIZE]; + } uu; + +} tBT_UUID; +#endif + + +// Define the service record attribute IDs. +// +#define ATTR_ID_SERVICE_RECORD_HDL 0x0000 +#define ATTR_ID_SERVICE_CLASS_ID_LIST 0x0001 +#define ATTR_ID_SERVICE_RECORD_STATE 0x0002 +#define ATTR_ID_SERVICE_ID 0x0003 +#define ATTR_ID_PROTOCOL_DESC_LIST 0x0004 +#define ATTR_ID_BROWSE_GROUP_LIST 0x0005 +#define ATTR_ID_LANGUAGE_BASE_ATTR_ID_LIST 0x0006 +#define ATTR_ID_SERVICE_INFO_TIME_TO_LIVE 0x0007 +#define ATTR_ID_SERVICE_AVAILABILITY 0x0008 +#define ATTR_ID_BT_PROFILE_DESC_LIST 0x0009 +#define ATTR_ID_DOCUMENTATION_URL 0x000A +#define ATTR_ID_CLIENT_EXE_URL 0x000B +#define ATTR_ID_ICON_URL 0x000C +#define ATTR_ID_ADDITION_PROTO_DESC_LISTS 0x000D + +#define LANGUAGE_BASE_ID 0x0100 +#define ATTR_ID_SERVICE_NAME LANGUAGE_BASE_ID + 0x0000 +#define ATTR_ID_SERVICE_DESCRIPTION LANGUAGE_BASE_ID + 0x0001 +#define ATTR_ID_PROVIDER_NAME LANGUAGE_BASE_ID + 0x0002 + +#define ATTR_ID_VERSION_OR_GROUP 0x0200 +#define ATTR_ID_VERSION_NUMBER_LIST ATTR_ID_VERSION_OR_GROUP +#define ATTR_ID_GROUP_ID ATTR_ID_VERSION_OR_GROUP +#define ATTR_ID_SERVICE_DATABASE_STATE 0x0201 +#define ATTR_ID_DATA_STORES_OR_NETWORK 0x0301 +#define ATTR_ID_SUPPORTED_DATA_STORES ATTR_ID_DATA_STORES_OR_NETWORK +#define ATTR_ID_EXTERNAL_NETWORK ATTR_ID_DATA_STORES_OR_NETWORK +#define ATTR_ID_FAX_CLASS_1_OR_AUDIO_VOLUME 0x0302 +#define ATTR_ID_FAX_CLASS_1_SUPPORT ATTR_ID_FAX_1_OR_AUD_VOL_OR_DEV_NAME +#define ATTR_ID_REMOTE_AUDIO_VOLUME_CONTROL ATTR_ID_FAX_1_OR_AUD_VOL_OR_DEV_NAME +#define ATTR_ID_DEVICE_NAME ATTR_ID_FAX_1_OR_AUD_VOL_OR_DEV_NAME +#define ATTR_ID_FORMATS_OR_FAX_2_0 0x0303 +#define ATTR_ID_SUPPORTED_FORMATS_LIST ATTR_ID_FORMATS_OR_FAX_2_0 +#define ATTR_ID_FAX_CLASS_2_0_SUPPORT ATTR_ID_FORMATS_OR_FAX_2_0 +#define ATTR_ID_FAX_CLASS_2_OR_FRIENDLY_NAME 0x0304 +#define ATTR_ID_FAX_CLASS_2_SUPPORT ATTR_ID_FAX_CLASS_2_OR_FRIENDLY_NAME +#define ATTR_ID_FRIENDLY_NAME ATTR_ID_FAX_CLASS_2_OR_FRIENDLY_NAME +#define ATTR_ID_AUDIO_FEEDBACK_SUPPORT 0x0305 + +// Define for service attribute, all the 'Descriptor Type' values. +// These are also referred to as 'attribute type' values +#define NULL_DESC_TYPE 0 +#define UINT_DESC_TYPE 1 +#define TWO_COMP_INT_DESC_TYPE 2 +#define UUID_DESC_TYPE 3 +#define TEXT_STR_DESC_TYPE 4 +#define BOOLEAN_DESC_TYPE 5 +#define DATA_ELE_SEQ_DESC_TYPE 6 +#define DATA_ELE_ALT_DESC_TYPE 7 +#define URL_DESC_TYPE 8 + + +// Define common 16-bit protocol UUIDs +// +#define UUID_PROTOCOL_SDP 0x0001 +#define UUID_PROTOCOL_UDP 0x0002 +#define UUID_PROTOCOL_RFCOMM 0x0003 +#define UUID_PROTOCOL_TCP 0x0004 +#define UUID_PROTOCOL_TCS_BIN 0x0005 +#define UUID_PROTOCOL_TCS_AT 0x0006 +#define UUID_PROTOCOL_OBEX 0x0008 +#define UUID_PROTOCOL_IP 0x0009 +#define UUID_PROTOCOL_FTP 0x000A +#define UUID_PROTOCOL_HTTP 0x000C +#define UUID_PROTOCOL_WSP 0x000E +#define UUID_PROTOCOL_BNEP 0x000F +#define UUID_PROTOCOL_UPNP 0x0010 +#define UUID_PROTOCOL_HIDP 0x0011 +#define UUID_PROTOCOL_HCRP_CTRL 0x0012 +#define UUID_PROTOCOL_HCRP_DATA 0x0014 +#define UUID_PROTOCOL_HCRP_NOTIF 0x0016 +#define UUID_PROTOCOL_AVCTP 0x0017 +#define UUID_PROTOCOL_AVDTP 0x0019 +#define UUID_PROTOCOL_L2CAP 0x0100 + +// Define common 16-bit service class UUIDs +// +#define UUID_SERVCLASS_SERVICE_DISCOVERY_SERVER 0X1000 +#define UUID_SERVCLASS_BROWSE_GROUP_DESCRIPTOR 0X1001 +#define UUID_SERVCLASS_PUBLIC_BROWSE_GROUP 0X1002 +#define UUID_SERVCLASS_SERIAL_PORT 0X1101 +#define UUID_SERVCLASS_LAN_ACCESS_USING_PPP 0X1102 +#define UUID_SERVCLASS_DIALUP_NETWORKING 0X1103 +#define UUID_SERVCLASS_IRMC_SYNC 0X1104 +#define UUID_SERVCLASS_OBEX_OBJECT_PUSH 0X1105 +#define UUID_SERVCLASS_OBEX_FILE_TRANSFER 0X1106 +#define UUID_SERVCLASS_IRMC_SYNC_COMMAND 0X1107 +#define UUID_SERVCLASS_HEADSET 0X1108 +#define UUID_SERVCLASS_CORDLESS_TELEPHONY 0X1109 +#define UUID_SERVCLASS_INTERCOM 0X1110 +#define UUID_SERVCLASS_FAX 0X1111 +#define UUID_SERVCLASS_HEADSET_AUDIO_GATEWAY 0X1112 +#define UUID_SERVCLASS_PNP_INFORMATION 0X1200 +#define UUID_SERVCLASS_GENERIC_NETWORKING 0X1201 +#define UUID_SERVCLASS_GENERIC_FILETRANSFER 0X1202 +#define UUID_SERVCLASS_GENERIC_AUDIO 0X1203 +#define UUID_SERVCLASS_GENERIC_TELEPHONY 0X1204 + + +//////////////////////////////////////////////////////////////////////////////// +// Definitions for RFCOMM and PORT functions +// + +// +// Define port settings structure send from the application in the +// set settings request, or to the application in the set settings indication. +// +typedef struct +{ + +#define PORT_BAUD_RATE_2400 0x00 +#define PORT_BAUD_RATE_4800 0x01 +#define PORT_BAUD_RATE_7200 0x02 +#define PORT_BAUD_RATE_9600 0x03 +#define PORT_BAUD_RATE_19200 0x04 +#define PORT_BAUD_RATE_38400 0x05 +#define PORT_BAUD_RATE_57600 0x06 +#define PORT_BAUD_RATE_115200 0x07 +#define PORT_BAUD_RATE_230400 0x08 + + UINT8 baud_rate; + +#define PORT_5_BITS 0x00 +#define PORT_6_BITS 0x01 +#define PORT_7_BITS 0x02 +#define PORT_8_BITS 0x03 + + UINT8 byte_size; + +#define PORT_ONESTOPBIT 0x00 +#define PORT_ONE5STOPBITS 0x01 + UINT8 stop_bits; + +#define PORT_PARITY_NO 0x00 +#define PORT_PARITY_YES 0x01 + UINT8 parity; + +#define PORT_ODD_PARITY 0x00 +#define PORT_EVEN_PARITY 0x01 +#define PORT_MARK_PARITY 0x02 +#define PORT_SPACE_PARITY 0x03 + + UINT8 parity_type; + +#define PORT_FC_OFF 0x00 +#define PORT_FC_XONXOFF_ON_INPUT 0x01 +#define PORT_FC_XONXOFF_ON_OUTPUT 0x02 +#define PORT_FC_CTS_ON_INPUT 0x04 +#define PORT_FC_CTS_ON_OUTPUT 0x08 +#define PORT_FC_DSR_ON_INPUT 0x10 +#define PORT_FC_DSR_ON_OUTPUT 0x20 + + UINT8 fc_type; + + UINT8 rx_char1; + +#define PORT_XON_DC1 0x11 + UINT8 xon_char; + +#define PORT_XOFF_DC3 0x13 + UINT8 xoff_char; + +} tPORT_STATE; + +typedef struct +{ + +#define PORT_FLAG_CTS_HOLD 0x01 /* Tx is waiting for CTS signal */ +#define PORT_FLAG_DSR_HOLD 0x02 /* Tx is waiting for DSR signal */ +#define PORT_FLAG_RLSD_HOLD 0x04 /* Tx is waiting for RLSD signal */ + UINT16 flags; + UINT16 in_queue_size; /* Number of bytes in the input queue */ + UINT16 out_queue_size; /* Number of bytes in the output queue */ +} tPORT_STATUS; + + +// +// Define RFCOMM Port events that registered application can receive +// in the callback +// +#define PORT_EV_RXCHAR 0x00000001 /* Any Character received */ +#define PORT_EV_RXFLAG 0x00000002 /* Received certain character */ +#define PORT_EV_TXEMPTY 0x00000004 /* Transmitt Queue Empty */ +#define PORT_EV_CTS 0x00000008 /* CTS changed state */ +#define PORT_EV_DSR 0x00000010 /* DSR changed state */ +#define PORT_EV_RLSD 0x00000020 /* RLSD changed state */ +#define PORT_EV_BREAK 0x00000040 /* BREAK received */ +#define PORT_EV_ERR 0x00000080 /* Line status error occurred */ +#define PORT_EV_RING 0x00000100 /* Ring signal detected */ +#define PORT_EV_CTSS 0x00000400 /* CTS state */ +#define PORT_EV_DSRS 0x00000800 /* DSR state */ +#define PORT_EV_RLSDS 0x00001000 /* RLSD state */ +#define PORT_EV_OVERRUN 0x00002000 /* receiver buffer overrun */ +#define PORT_EV_TXCHAR 0x00004000 /* Any character transmitted */ + +#define PORT_EV_CONNECTED 0x00000200 /* RFCOMM connection established */ +#define PORT_EV_CONNECT_ERR 0x00008000 /* Was not able to establish connection */ + /* or disconnected */ +#define PORT_EV_FC 0x00010000 /* flow control enabled flag changed by remote */ +#define PORT_EV_FCS 0x00020000 /* flow control status true = enabled */ + +// +// To register for RFCOMM events application should provide bitmask with +// corresponding bit set +// +#define PORT_MASK_ALL (PORT_EV_RXCHAR | PORT_EV_TXEMPTY | PORT_EV_CTS | \ + PORT_EV_DSR | PORT_EV_RLSD | PORT_EV_BREAK | \ + PORT_EV_ERR | PORT_EV_RING | PORT_EV_CONNECT_ERR | \ + PORT_EV_DSRS | PORT_EV_CTSS | PORT_EV_RLSDS | \ + PORT_EV_RXFLAG | PORT_EV_TXCHAR | PORT_EV_OVERRUN | \ + PORT_EV_CONNECTED | PORT_EV_FC | PORT_EV_FCS) + + + +// +// Definitions used by RFCOMM to set control leads +// +#define PORT_SET_DTRDSR 0x01 +#define PORT_CLR_DTRDSR 0x02 +#define PORT_SET_CTSRTS 0x03 +#define PORT_CLR_CTSRTS 0x04 +#define PORT_SET_RI 0x05 /* DCE only */ +#define PORT_CLR_RI 0x06 /* DCE only */ +#define PORT_SET_DCD 0x07 /* DCE only */ +#define PORT_CLR_DCD 0x08 /* DCE only */ +#define PORT_SET_BREAK 0x09 +#define PORT_CLR_BREAK 0x0A + +// +// RFCOMM Port modem control leads +// +#define PORT_DTRDSR_ON 0x01 +#define PORT_CTSRTS_ON 0x02 +#define PORT_RING_ON 0x04 +#define PORT_DCD_ON 0x08 + + +// +// RFCOMM Port errors +// +#define PORT_ERR_BREAK 0x01 /* Break condition occured on the peer device */ +#define PORT_ERR_OVERRUN 0x02 /* Overrun is reported by peer device */ +#define PORT_ERR_FRAME 0x04 /* Framing error reported by peer device */ +#define PORT_ERR_RXOVER 0x08 /* Input queue overflow occured */ +#define PORT_ERR_TXFULL 0x10 /* Output queue overflow occured */ + +// +// Flags used in the RFCOMM Port purge function +// +#define PORT_PURGE_TXCLEAR 0x01 +#define PORT_PURGE_RXCLEAR 0x02 + +// +// RFCOMM default MTU size +// +#define RFCOMM_DEFAULT_MTU 127 + + +// +// Valid Security Service Levels +// +#define BTM_SEC_NONE 0x00 /* Nothing required */ +#define BTM_SEC_IN_AUTHORIZE 0x01 /* Inbound call requires authorization */ +#define BTM_SEC_IN_AUTHENTICATE 0x02 /* Inbound call requires authentication */ +#define BTM_SEC_IN_ENCRYPT 0x04 /* Inbound call requires encryption */ +#define BTM_SEC_OUT_AUTHORIZE 0x08 /* Outbound call requires authorization */ +#define BTM_SEC_OUT_AUTHENTICATE 0x10 /* Outbound call requires authentication */ +#define BTM_SEC_OUT_ENCRYPT 0x20 /* Outbound call requires encryption */ +#define BTM_SEC_BOND 0x40 /* Bonding */ + +// +// definitions for security +// + +// +// Predefined security services +// + +#define BTM_SEC_SERVICE_SDP_SERVER 0 +#define BTM_SEC_SERVICE_SERIAL_PORT 1 +#define BTM_SEC_SERVICE_LAN_ACCESS 2 +#define BTM_SEC_SERVICE_DUN 3 +#define BTM_SEC_SERVICE_IRMCSYBC 4 +#define BTM_SEC_SERVICE_OBEX_PUSH 5 +#define BTM_SEC_SERVICE_OBEX_FTP 6 +#define BTM_SEC_SERVICE_IRMCSYNCCMD 7 +#define BTM_SEC_SERVICE_HEADSET 8 +#define BTM_SEC_SERVICE_CORDLESS 9 +#define BTM_SEC_SERVICE_INTERCOM 10 +#define BTM_SEC_SERVICE_FAX 11 +#define BTM_SEC_SERVICE_HEADSET_AG 12 +#define BTM_SEC_SERVICE_PNP_INFO 13 +#define BTM_SEC_SERVICE_GEN_NET 14 +#define BTM_SEC_SERVICE_GEN_FILE 15 +#define BTM_SEC_SERVICE_GEN_AUDIO 16 +#define BTM_SEC_SERVICE_GEN_TEL 17 + +#define BTM_SEC_SERVICE_FIRST_EMPTY 18 + +// Number of services that can be registered with security manager +#define BTM_SEC_MAX_SERVICE_RECORDS 32 + +// Following bits can be provided by host in the trusted_mask field +#define BTM_SEC_TRUST_SDP_SERVER (1 << BTM_SEC_SERVICE_SDP_SERVER) +#define BTM_SEC_TRUST_SERIAL_PORT (1 << BTM_SEC_SERVICE_SERIAL_PORT) +#define BTM_SEC_TRUST_LAN_ACCESS (1 << BTM_SEC_SERVICE_LAN_ACCESS) +#define BTM_SEC_TRUST_DUN (1 << BTM_SEC_SERVICE_DUN) +#define BTM_SEC_TRUST_IRMCSYBC (1 << BTM_SEC_SERVICE_IRMCSYBC) +#define BTM_SEC_TRUST_OBEX_PUSH (1 << BTM_SEC_SERVICE_OBEX_PUSH) +#define BTM_SEC_TRUST_OBEX_FTP (1 << BTM_SEC_SERVICE_OBEX_FTP) +#define BTM_SEC_TRUST_IRMCSYNCCMD (1 << BTM_SEC_SERVICE_IRMCSYNCCMD) +#define BTM_SEC_TRUST_HEADSET (1 << BTM_SEC_SERVICE_HEADSET) +#define BTM_SEC_TRUST_CORDLESS (1 << BTM_SEC_SERVICE_CORDLESS) +#define BTM_SEC_TRUST_IINTERCOM (1 << BTM_SEC_SERVICE_IINTERCOM) +#define BTM_SEC_TRUST_FAX (1 << BTM_SEC_SERVICE_FAX) +#define BTM_SEC_TRUST_HEADSET_AG (1 << BTM_SEC_SERVICE_HEADSET_AG) +#define BTM_SEC_TRUST_PNP_INFO (1 << BTM_SEC_SERVICE_PNP_INFO) +#define BTM_SEC_TRUST_GEN_NET (1 << BTM_SEC_SERVICE_GEN_NET) +#define BTM_SEC_TRUST_GEN_FILE (1 << BTM_SEC_SERVICE_GEN_FILE) +#define BTM_SEC_TRUST_GEN_AUDIO (1 << BTM_SEC_SERVICE_GEN_AUDIO) +#define BTM_SEC_TRUST_GEN_TEL (1 << BTM_SEC_SERVICE_GEN_TEL) + +#define BTM_SEC_TRUST_ALL 0xFFFFFFFF + + + +// BtIfObexHeaders.cpp : Definitions for the OBEX headers objects. +// + +#define OBEX_MINIMUM_MTU (255) +#define OBEX_MAX_CLIENTS 4 +#define OBEX_MAX_SERVERS 4 + + +#ifdef _WIN32_WCE + #define OBEX_MAX_CSESSIONS 1 + #define OBEX_MAX_SSESSIONS 1 /* Current version only allows 1 session per server */ +#else + #define OBEX_MAX_CSESSIONS 4 + #define OBEX_MAX_SSESSIONS 3 +#endif + +#define OBEX_MAX_TARGET 3 +#define OBEX_MAX_AUTH_CHALLENGE 3 +#define OBEX_MAX_AUTH_RESPONSE 3 +#define OBEX_MAX_HTTP 3 +#define OBEX_MAX_APP_PARAM 3 +#define OBEX_MAX_USER_HDR 4 +#define OBEX_TIMER_SEED 60 + +#define OBEX_TIME_LOCAL 0x01 +#define OBEX_TIME_UTC 0x02 +#define OBEX_API +#define OBEX_CLIENT_INCLUDED (TRUE) +#define OBEX_SERVER_INCLUDED (TRUE) + +#ifndef BT_TYPES_H +#define GKI_BUF3_SIZE 700 +#endif +#define OBEX_DATA_POOL_SIZE (GKI_BUF3_SIZE) + +#define OBEX_DATA_BUFFER_OVERHEAD (41) +// for now, limit MTU to 640 - overhead +#ifndef BT_TYPES_H +#define OBEX_DESIRED_MTU (640 - OBEX_DATA_BUFFER_OVERHEAD) +#endif +#if (OBEX_DESIRED_MTU + OBEX_DATA_BUFFER_OVERHEAD > OBEX_DATA_POOL_SIZE) +#define OBEX_DEFAULT_MTU (OBEX_DATA_POOL_SIZE - OBEX_DATA_BUFFER_OVERHEAD) +#else +#define OBEX_DEFAULT_MTU (OBEX_DESIRED_MTU) +#endif + +/* +** Define OBEX Request Codes +*/ +#define OBEX_REQ_CONNECT 0x00 +#define OBEX_REQ_DISCONNECT 0x01 +#define OBEX_REQ_PUT 0x02 +#define OBEX_REQ_GET 0x03 +#define OBEX_REQ_SETPATH 0x05 +#define OBEX_REQ_ABORT 0x7f +#define OBEX_FINAL 0x80 +#define OBEX_UNREGISTER 0xff + +/* +** Define well-known OBEX Client data types +*/ +typedef UINT32 tOBEX_CLIENT_HANDLE; /* Application Handle */ +typedef UINT32 tOBEX_CSESSION_HANDLE; /* Session Handle */ + +/* +** Define well-known OBEX Server data types +*/ +typedef UINT32 tOBEX_SERVER_HANDLE; /* Application Handle */ +typedef UINT32 tOBEX_SSESSION_HANDLE; /* Session Handle */ + + +/* +** Define OBEX error codes returned by OBEX API Functions and Application +** Callback Functions +*/ +typedef enum +{ + OBEX_SUCCESS = 0, /* Operation was successful or accepted */ + OBEX_FAIL, /* Operation failed or was rejected */ + OBEX_ERROR, /* Internal OBEX error */ + OBEX_ERR_RESOURCES, /* Insufficient resources */ + OBEX_ERR_NO_CB, /* Calback for request is missing */ + OBEX_ERR_DUP_SERVER, /* Server for 'Target' already register with OBEX */ + OBEX_ERR_RESPONSE, /* Peer rejected request */ + OBEX_ERR_UNK_APP, /* Unknown Application Handle (unregistered?) */ + OBEX_ERR_PARAM, /* Invalid or missing parameter value */ + OBEX_ERR_CLOSED, /* Session is closed */ + OBEX_ERR_ABORTED, /* Operation was aborted */ + OBEX_ERR_STATE, /* Request is invalid for current state */ + OBEX_ERR_NA, /* API call not allowed at this time */ + OBEX_ERR_HEADER, /* Invalid data in tOBEX_HEADERS */ + OBEX_ERR_TOO_BIG, /* The data presented in the tOBEX_HEADERS */ + /* structure is larger than the maximum */ + /* size allowed for the request */ + OBEX_ERR_TIMEOUT /* Timeout */ +} tOBEX_ERRORS; +#define OBEX_MAX_ERROR OBEX_ERR_TIMEOUT + +/* +** Define OBEX Response Code values used in OBEX Responses sent by +** the OBEX Server to the OBEX Client. These codes are taken +** from the 'IrDA Object Exchange Protocol (IROBEX)' specification +** with the high bit (final bit) removed. The OBEX Core internally +** manages the 'final bit'. +*/ +typedef enum +{ + OBEX_RSP_DEFAULT = 0x00, + OBEX_RSP_CONTINUE = 0x10, + OBEX_RSP_OK = 0x20, + OBEX_RSP_CREATED = 0x21, + OBEX_RSP_ACCEPTED = 0x22, + OBEX_RSP_NON_AUTHORITATIVE_INFO = 0x23, + OBEX_RSP_NO_CONTENT = 0x24, + OBEX_RSP_RESET_CONTENT = 0x25, + OBEX_RSP_PARTIAL_CONTENT = 0x26, + OBEX_RSP_MULTIPLE_CHOICES = 0x30, + OBEX_RSP_MOVED_PERMANENTLY = 0x31, + OBEX_RSP_MOVED_TEMPORARILY = 0x32, + OBEX_RSP_SEE_OTHER = 0x33, + OBEX_RSP_NOT_MODIFIED = 0x34, + OBEX_RSP_USE_PROXY = 0x35, + OBEX_RSP_BAD_REQUEST = 0x40, + OBEX_RSP_UNAUTHORIZED = 0x41, + OBEX_RSP_PAYMENT_REQUIRED = 0x42, + OBEX_RSP_FORBIDDEN = 0x43, + OBEX_RSP_NOT_FOUND = 0x44, + OBEX_RSP_METHOD_NOT_ALLOWED = 0x45, + OBEX_RSP_NOT_ACCEPTABLE = 0x46, + OBEX_RSP_PROXY_AUTHENTICATION_REQUIRED = 0x47, + OBEX_RSP_REQUEST_TIME_OUT = 0x48, + OBEX_RSP_CONFLICT = 0x49, + OBEX_RSP_GONE = 0x4A, + OBEX_RSP_LENGTH_REQUIRED = 0x4B, + OBEX_RSP_PRECONDITION_FAILED = 0x4C, + OBEX_RSP_REQUESTED_ENTITY_TOO_LARGE = 0x4D, + OBEX_RSP_REQUEST_URL_TOO_LARGE = 0x4E, + OBEX_RSP_UNSUPPORTED_MEDIA_TYPE = 0x4F, + OBEX_RSP_INTERNAL_SERVER_ERROR = 0x50, + OBEX_RSP_NOT_IMPLEMENTED = 0x51, + OBEX_RSP_BAD_GATEWAY = 0x52, + OBEX_RSP_SERVICE_UNAVAILABLE = 0x53, + OBEX_RSP_GATEWAY_TIMEOUT = 0x54, + OBEX_RSP_HTTP_VERSION_NOT_SUPPORTED = 0x55, + OBEX_RSP_DATABASE_FULL = 0x60, + OBEX_RSP_DATABASE_LOCKED = 0x61 +} tOBEX_RESPONSE_CODE; + +/* +** The tOBEX_CLIENT_HANDLE, tOBEX_SERVER_HANDLE, tOBEX_CSESSION_HANDLE +** and tOBEX_SSESSION_HANDLE each consist of two 16 bit values. The high +** order 16 bits contain an arbitrary ordinal assigned when the client +** or server registers or when a session is opened. The low order 16 +** bits contain the tOBEX_GLOBAL_CLIENT.client_ctrl, +** tOBEX_GLOBAL_SERVER.server_ctrl, tOBEX_CLIENT_CTRL.cses_ctrl or +** tOBEX_SERVER_CTRL.sses_ctrl index value for the client, server or +** session. The following macros encode an ordinal and index into a +** tOBEX_CLIENT_HANDLE, tOBEX_SERVER_HANDLE, tOBEX_CSESSION_HANDLE or +** tOBEX_SSESSION_HANDLE and decode an ordinal and index from a +** handle. +*/ +#define OBEX_ENCODE_HANDLE(handle,ordinal,index) (handle = ((ordinal << 16) | (index & 0xFFFF))) +#define OBEX_DECODE_HANDLE(handle,ordinal,index) (ordinal = (handle >> 16), index = (handle & 0xFFFF)) + +/* +* The following defines and describes the tOBEX_HEADERS structure +* and the other structures that are embedded in the tOBEX_HEADERS +* structure. +* +* The tOBEX_HEADERS structure is used by many calls to OBEX API +* and Application Callback Functions. It contains the information +* found in the OBEX Headers supported by this implementation of +* OBEX as defined in the 'IrDA Object Exchange Protocol (IrOBEX)' +* specification. +* +* When an application wants to send information to the peer +* application it creates an instance of the tOBEX_HEADERS structure +* and passes a pointer to the structure to the appropriate OBEX API +* Function. OBEX will process the information found in the +* structure and return to the application. If the application has +* registered with OBEX as being 'well behaved' (see OBEX_ClientRegister +* and OBEX_ServerRegister functions), OBEX will assume that the +* tOBEX_HEADERS pointer points to a GKI buffer (without the BT_HDR +* prefix) and will assume ownership of the buffer. If the application +* has not registered as 'well behaved', OBEX will assume that the +* tOBEX_HEADERS pointer points to static memory and will not alter +* the memory pointed to by the pointer. For best performance, it +* is recommended that applications be well behaved. +* +* When OBEX receives information from the peer OBEX that it needs to +* deliver to the application, it creates an instance of the +* tOBEX_HEADERS structure and passes a pointer to it to the appropriate +* Application Callback Function. The tOBEX_HEADERS structure resides +* in a GKI buffer (without the BT_HDR prefix). If the application has +* registered as 'well behaved', OBEX assumes that the application will +* assume ownership of the buffer and release it as appropriate. If the +* application has not registered as 'well behaved', OBEX assumes that +* the application treats the buffer as static memory and once the +* Application Callback Function returns to OBEX, OBEX will dispose of +* the buffer appropriately. +*/ + + +/* +* The tOBEX_UNI_HDR structure is embedded in the tOBEX_HEADERS +* structure. It is used to hold the contents of an OBEX +* 'UNICODE' type header. These headers contain null-terminated +* UNICODE text strings. The struct contains a 'p_string' member +* that points to the null-terminated UNICODE string and a 'str_len' +* member that indicates the number of characters in the UNICODE string. +* NOTE: The 'str_len' member includes the terminating NULL +* character. Therefore, by example, if a Name header contains +* the UNICODE text string "JUMAR.TXT", the 'p_string' member of the +* struct will point to a data area containing +* the hex values (displayed in Big-endian format): +* "00 4A 00 55 00 4D 00 41 00 52 00 2E 00 54 00 58 00 54 00 00" +* and the 'str_len' member will contain the value 10. +* +* If a 'UNICODE' type header present in an OBEX request or +* response, the contents of that header will be stored in a +* tOBEX_UNI_HDR struct and the 'flag' member of the tOBEX_HEADERS +* struct will indicate that the header is present. Some headers may +* be present but contain no data. In such a case, the 'flag' member +* of the tOBEX_HEADERS struct will indicate that the header is +* present but the 'str_len' member and 'p_string' member of the +* associated header struct will be zero. +* +* If the application has registered with OBEX as being 'well behaved', +* it is assumed that each 'p_string' member in use points to a GKI +* buffer and ownership of each buffer will follow the rules specified +* above for the tOBEX_HEADERS structure. If the application has not +* registered with OBEX as being 'well behaved', the OBEX API functions +* assume that each 'p_string' member points to static memory it does +* not alter that memory. OBEX also assumes that the Application +* Callback Functions will treat the memory pointed to by each 'p_string' +* member as static and will not alter that memory. +*/ + +/* OBEX UNICODE Type Header - null terminated UNICODE string */ +typedef struct +{ + UINT32 str_len; + WCHAR *p_string; +} tOBEX_UNI_HDR; + + +/* +* The tOBEX_OCTET_HDR structure is embedded in the tOBEX_HEADERS +* structure. It is used to hold the contents of an OBEX +* 'octet-array' type header. These headers contain unstructured octet +* arrays. The struct contains a 'p_array' member that points to an +* array of octets and a 'length' member that indicates the length of +* the array. +* +* If a header of the 'octet-array' type is present in an OBEX request or +* response, the contents of that header will be stored in its +* corresponding struct and the 'flag' member of the tOBEX_HEADERS +* struct will indicate that the header is present. Some headers may +* be present but contain no data. In such a case, the 'flag' member +* of the tOBEX_HEADERS struct will indicate that the header is +* present but the 'length' member and 'p_array' member of the +* associated header struct will be zero. +* +* Some header types may occur multiple times in a request or response. +* For these types of headers, the tOBEX_HEADERS structure contains +* an array of embedded structures for that header type and a counter +* indicating the number of those headers that are present. The +* 'flag' member of the tOBEX_HEADERS struct will indicate that at +* least one instance of the header is present in the request/response +* and the corresponding 'num_...' member of the tOBEX_HEADERS struct +* will indicate the actual number of instances included in the array +* of structures. +* +* If the application has registered with OBEX as being 'well behaved', +* it is assumed that each 'p_array' member in use points to a GKI +* buffer and ownership of each buffer will follow the rules specified +* above for the tOBEX_HEADERS structure. If the application has not +* registered with OBEX as being 'well behaved', the OBEX API functions +* assume that each 'p_array' member points to static memory it does +* not alter that memory. OBEX also assumes that the Application +* Callback Functions will treat the memory pointed to by each 'p_array' +* member as static and will not alter that memory. +*/ + +/* OBEX Octet-Array Type Header - unstructured octet array */ +typedef struct +{ + UINT32 length; + UINT8 *p_array; +} tOBEX_OCTET_HDR; + + + +/* +* The tOBEX_HEADERS structure contains three arrays of the +* following tOBEX_HDR_TRIPLET structure to hold information +* found in the OBEX Application Request-Response Parameters +* Header, the Authenticate Challenge Header and the +* Authenticate Response Header. Each of these headers contain +* one or more 'Triplets' and each triplet is stored in an +* entry in its associated tOBEX_HDR_TRIPLET array. The +* tOBEX_HEADERS structure contains a 'num_app_params' member +* that indicates the number of triplets contained in the +* Application Request-Response Parameters Header, a +* 'num_auth_challenge' member that indicates the number of +* triplets contained in the Authenticate Challenge Header and +* a 'num_auth_response' member that indicates the number of +* triplets contains in the Authenticate Response Header. +* +* If the application has registered with OBEX as being 'well behaved', +* it is assumed that each 'p_array' member in use points to a GKI +* buffer and ownership of each buffer will follow the rules specified +* above for the tOBEX_HEADERS structure. If the application has not +* registered with OBEX as being 'well behaved', the OBEX API functions +* assume that each 'p_array' member points to static memory it does +* not alter that memory. OBEX also assumes that the Application +* Callback Functions will treat the memory pointed to by each 'p_array' +* member as static and will not alter that memory. +*/ +typedef struct obex_hdr_triplet_s +{ + UINT8 tag; + UINT8 length; + UINT8 *p_array; +} tOBEX_HDR_TRIPLET; + +/* +* The tOBEX_HEADERS structure contains an array of the +* following tOBEX_USER_HDR structure to hold information +* found in the OBEX User Defined Headers. The OBEX User +* Defined Header is formatted according to the 'IrDA Object +* Exchange Protocol (IrOBEX)' specification. The specification +* requires that the two high-order bits of the first octet of +* the header indicate the format and, by implication, the size +* of the data contained in the header and the remaining bits of +* the first octet be within the range of 0x30 to 0x3F. +* +* By performing a logical AND on the 'id' member of the +* tOBEX_USER_HDR structure and the OBEX_USER_TYPE_MASK constant +* and comparing the results with each of the other +* 'OBEX_USER_TYPE_....' constants, the User Defined Header +* format can be determined. The appropriate member of the +* 'value' union can then be accessed and the 'length' member +* can be interpreted as follows: +* +* ---------------------- ----------- ------------------- +* | If the high-order | | | +* | 2 bits of id are | Then | Contains | +* |----------------------|-----------|-------------------| +* | OBEX_USER_TYPE_UNI | p_string | Pointer to null | +* | | | terminated | +* | | | UNICODE text | +* | | | string | +* | |-----------|-------------------| +* | | length | Number of | +* | | | characters in | +* | | | the UNICODE text | +* | | | string (including | +* | | | the terminating | +* | | | NULL character) | +* |----------------------|-----------|-------------------| +* | OBEX_USER_TYPE_ARRAY | p_array | Pointer to | +* | | | unstructured | +* | | | octet array | +* | |-----------|-------------------| +* | | length | Number octets of | +* | | | data in the array | +* |----------------------|-----------|-------------------| +* | OBEX_USER_TYPE_BYTE | user_byte | One byte of | +* | | | user data | +* | |-----------|-------------------| +* | | length | N/A | +* |----------------------|-----------|-------------------| +* | OBEX_USER_TYPE_INT | user_int | One 32-bit | +* | | | integer of user | +* | | | data | +* | |-----------|-------------------| +* | | length | N/A | +* ---------------------- ----------- ------------------- +* +* If the application has registered with OBEX as being 'well behaved', +* it is assumed that each 'p_array' and 'p_string' member in use +* points to a GKI buffer and ownership of each buffer will follow the +* rules specified above for the tOBEX_HEADERS structure. If the +* application has not registered with OBEX as being 'well behaved', +* the OBEX API functions assume that each 'p_array' member points to +* static memory it does not alter that memory. OBEX also assumes +* that the Application Callback Functions will treat the memory +* pointed to by each 'p_array' member as static and will not alter +* that memory. +*/ +typedef struct +{ + UINT8 id; +#define OBEX_USER_TYPE_MASK 0xC0 +#define OBEX_USER_TYPE_UNI 0x00 /* Null terminated ASCII text */ +#define OBEX_USER_TYPE_ARRAY 0x40 /* Unstructured octet array */ +#define OBEX_USER_TYPE_BYTE 0x80 /* Single byte */ +#define OBEX_USER_TYPE_INT 0xC0 /* 32 bit integer */ + UINT16 length; + union + { + WCHAR *p_string; + UINT8 *p_array; + UINT8 user_byte; + UINT32 user_int; + } value; +} tOBEX_USER_HDR; + + + +/* +* The following is the tOBEX_HEADERS structure. +* +* The 'rsp_code' member contains the OBEX Response Code used in the +* OBEX Response Packet. It is stored without the high-order (final) +* bit set. The OBEX Core manages the 'final bit' in Response +* Packets internally. When the Server Application calls an OBEX +* Server API Function it indicates the success or failure of the request +* by passing a tOBEX_ERRORS enumerator to the appropriate API. If the +* Server Application wants to be more specific in the type of error +* returned to the Client Application, it can set the specific OBEX +* Response Code desired in the 'rsp_code' member of the tOBEX_HEADERS +* structure. If the Server Application does not set the 'rsp_code' +* member to a valid tOBEX_ERRORS enumerator, the OBEX Core will +* generate an appropriate OBEX Response Code based on the tOBEX_ERRORS +* enumerator passed as a parameter to the Confirmation API Function and +* the current operation being performed. +* +* The 'flag' member indicates which headers are present. +* +* If the 'flag' member indicates that the Count Header is +* present, the 'count' member of the tOBEX_HEADERS structure +* will contain the value found in the Count Header. +* +* If the 'flag' member indicates that the Name Header, Type Header, +* Description Header, Target Header, HTTP Header, Body (or +* End-of-Body) Header, Who Header, Application Request-Response Header, +* Authentication Challenge Header, Authentication Response Header, +* Object Class Header or User Defined Header is present, the +* corresponding data will be present in the tOBEX_HEADERS structure +* as specified above. +* +* If the 'flag' member indicates that the Length Header is +* present, the 'hint_of_length' member of the tOBEX_HEADERS +* structure will contain the value found in the Length Header. +* +* THE FOLLOWING DESCRIPTION APPLIES ONLY TO THE OBEX NON_LEGACY API: +* If the 'flag' member indicates that the Time Header is present, +* the 'time' member contains the contents of the Time Header +* as seconds since midnight, January 1, 1970, UTC ('Universal +* Coordinated Time' also known as GMT or 'Greenwich Mean Time') or +* Local Time. The 'time_qualifier' member of the tOBEX_HEADERS +* structure indicates whether the time in the header is UTC or Local +* Time. The 'IrDA Object Exchange Protocol (IrOBEX)' specification +* recommends that the Time Header be formatted according to ISO 8601. +* The OBEX core performs the conversion between the ISO 8601 format +* and the tOBEX_HEADERS structure format as appropriate. +* END OF OBEX NON-LEGACY API DESCRIPTION +* +* NOTE!!!!! +* The bit positions for each header represented in the 'flag' member +* must not be altered because the OBEX Core relies on them being +* assigned as defined here. +* +*/ +typedef struct +{ + UINT32 flag; +#define OBEX_FLAG_INTERNAL1 0x00000001 /* Used internally by OBEX Core */ +#define OBEX_FLAG_COUNT 0x00000002 +#define OBEX_FLAG_NAME 0x00000004 +#define OBEX_FLAG_TYPE 0x00000008 +#define OBEX_FLAG_LENGTH 0x00000010 +#define OBEX_FLAG_OBJECT_CLASS 0x00000020 +#define OBEX_FLAG_TARGET 0x00000040 +#define OBEX_FLAG_WHO 0x00000080 +#define OBEX_FLAG_TIME 0x00000100 +#define OBEX_FLAG_INTERNAL2 0x00000200 /* Used internally by OBEX Core */ +#define OBEX_FLAG_DESCRIPTION 0x00000400 +#define OBEX_FLAG_AUTH_CHALLENGE 0x00000800 +#define OBEX_FLAG_AUTH_RESPONSE 0x00001000 +#define OBEX_FLAG_HTTP 0x00002000 +#define OBEX_FLAG_APPL_REQ_RSP 0x00004000 +#define OBEX_FLAG_USER_DEFINED 0x00008000 +#define OBEX_FLAG_BODY 0x00010000 +#define OBEX_FLAG_BODY_END 0x00020000 +/* flag bits legal for applications to set */ +#define OBEX_FLAG_LEGAL_BITS \ + (OBEX_FLAG_COUNT + \ + OBEX_FLAG_NAME + \ + OBEX_FLAG_TYPE + \ + OBEX_FLAG_LENGTH + \ + OBEX_FLAG_OBJECT_CLASS + \ + OBEX_FLAG_TARGET + \ + OBEX_FLAG_WHO + \ + OBEX_FLAG_TIME + \ + OBEX_FLAG_DESCRIPTION + \ + OBEX_FLAG_AUTH_CHALLENGE + \ + OBEX_FLAG_AUTH_RESPONSE + \ + OBEX_FLAG_HTTP + \ + OBEX_FLAG_APPL_REQ_RSP + \ + OBEX_FLAG_USER_DEFINED + \ + OBEX_FLAG_BODY + \ + OBEX_FLAG_BODY_END) + UINT32 internal1; /* Used internally by OBEX Core */ + UINT32 count; + tOBEX_UNI_HDR uni_name; + tOBEX_OCTET_HDR type; + UINT32 hint_of_length; + tOBEX_OCTET_HDR object_class; + UINT32 num_target; + tOBEX_OCTET_HDR target[OBEX_MAX_TARGET]; + tOBEX_OCTET_HDR who; + UINT32 time_value; + UINT8 time_qualifier; +#define OBEX_TIME_LOCAL 0x01 +#define OBEX_TIME_UTC 0x02 + tOBEX_UNI_HDR uni_description; + UINT32 num_auth_challenge; + tOBEX_HDR_TRIPLET auth_challenge[OBEX_MAX_AUTH_CHALLENGE]; + UINT32 num_auth_response; + tOBEX_HDR_TRIPLET auth_response[OBEX_MAX_AUTH_RESPONSE]; + UINT32 num_http; + tOBEX_OCTET_HDR http[OBEX_MAX_HTTP]; + UINT32 num_app_param; + tOBEX_HDR_TRIPLET app_param[OBEX_MAX_APP_PARAM]; + UINT32 num_user; + tOBEX_USER_HDR user[OBEX_MAX_USER_HDR]; + tOBEX_OCTET_HDR body; +} tOBEX_HEADERS; + + +#endif // !defined (WIDCOMMSDK_EXPORTS) + +/* Define the L2CAP connection result codes +*/ +#define L2CAP_CONN_OK 0 +#define L2CAP_CONN_PENDING 1 +#define L2CAP_CONN_NO_PSM 2 +#define L2CAP_CONN_SECURITY_BLOCK 3 +#define L2CAP_CONN_NO_RESOURCES 4 +#define L2CAP_CONN_TIMEOUT 0xEEEE + +#define L2CAP_CONN_NO_LINK 255 /* Add a couple of our own for internal use */ + + +// Define a structure to hold the configuration parameters. Since the +// parameters are optional, for each parameter there is a boolean to +// use to signify its presence or absemce. +// +// NOTE: This structure is used externally from (or above) the SDK +// and is mapped to the tL2CAP_CFG_INFO structure when used +// in the CL2CapConn::Reconfigure method. This is done so we +// only expose BOOL types externally. +// +typedef struct +{ + UINT16 result; // Only used in confirm messages + BOOL mtu_present; + UINT16 mtu; + BOOL qos_present; + FLOW_SPEC qos; + BOOL flush_to_present; + UINT16 flush_to; + + UINT16 flags; // Internally used by L2CAP + +} tL2CAP_CONFIG_INFO; + +#pragma pack () + +#endif // !defined(AFX_WIDCOMMTYPES_H__1F5ED990_6FC6_4B0D_882C_8D7C98C16A06__INCLUDED_) diff --git a/inc/BtIfObexHeaders.h b/inc/BtIfObexHeaders.h new file mode 100644 index 0000000..ae236d1 --- /dev/null +++ b/inc/BtIfObexHeaders.h @@ -0,0 +1,325 @@ +///////////////////////////////////////////////////////////////////////////// +// +// Name BtIfObexHeaders.h +// $Header: +// +// Function this file contains Widcomm SDK class definitions +// +// Date Modification +// ---------------------------------- +// 24Apr2001 JWF Create +// +// Copyright (c) 2000-2002, WIDCOMM Inc., All Rights Reserved. +// Proprietary and confidential. +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef _BTIFOBEXHEADERS_H +#define _BTIFOBEXHEADERS_H + +#ifdef WIDCOMMSDK_EXPORTS +#define WIDCOMMSDK __declspec(dllexport) +#else +#define WIDCOMMSDK __declspec(dllimport) +#endif + +//#include "obex_api.h" +//#include "obex_capi.h" +//#include "obex_sapi.h" +#include "BtIfDefinitions.h" +#include "BtIfClasses.h" + +// Ensure alignment across all builds +// +#pragma pack (1) + +class CObexHeaders; + +//////////////////////////////////////////////////////////////////////////// +// +// Class for OBEX client side applications +// +class CObexClient_Impl; + +class WIDCOMMSDK CObexClient +{ +public: + + ~CObexClient(); + CObexClient(); + + tOBEX_ERRORS Open (UINT8 scn, BD_ADDR bd_addr, CObexHeaders *p_request, UINT16 mtu = OBEX_DEFAULT_MTU); + tOBEX_ERRORS SetPath (CObexHeaders *p_request, BOOL backup, BOOL create); + tOBEX_ERRORS Put (CObexHeaders *p_request, BOOL final); + tOBEX_ERRORS Get (CObexHeaders *p_request, BOOL final); + tOBEX_ERRORS Abort (CObexHeaders *p_request); + tOBEX_ERRORS Close (CObexHeaders *p_request); + + virtual void OnOpen(CObexHeaders *p_confirm, UINT16 tx_mtu, tOBEX_ERRORS code, tOBEX_RESPONSE_CODE response) = 0; + virtual void OnClose(CObexHeaders *p_confirm, tOBEX_ERRORS code, tOBEX_RESPONSE_CODE response) = 0; + virtual void OnAbort (CObexHeaders *p_confirm, tOBEX_ERRORS code, tOBEX_RESPONSE_CODE response) {} + virtual void OnPut (CObexHeaders *p_confirm, tOBEX_ERRORS code, tOBEX_RESPONSE_CODE response) {} + virtual void OnGet (CObexHeaders *p_confirm, tOBEX_ERRORS code, BOOL final, tOBEX_RESPONSE_CODE response) {} + virtual void OnSetPath (CObexHeaders *p_confirm, tOBEX_ERRORS code, tOBEX_RESPONSE_CODE response) {} + + //Create AudioConnection + AUDIO_RETURN_CODE CreateAudioConnection(BOOL bIsClient, UINT16 *audioHandle); + + //Disconnect AudioConnection + AUDIO_RETURN_CODE RemoveAudioConnection(UINT16 audioHandle); + + //Audio callback functions + virtual void OnAudioConnected(UINT16 audioHandle){}; + virtual void OnAudioDisconnect(UINT16 audioHandle){}; + +#ifdef _WIN32_WCE + AUDIO_RETURN_CODE ReadAudioData(void *pBuff, DWORD dwLen, DWORD *dwByteR); + AUDIO_RETURN_CODE WriteAudioData(void *pBuff, DWORD dwLen, DWORD *dwByteW); +#endif + + tOBEX_ERRORS SetLinkSupervisionTimeOut(UINT16 timeout); + +private: + + static CObexClient *m_p_first_client; + CObexClient *m_p_next_client; + friend class CObexClientFriend; + friend class CObexClient_Impl; + CObexClient_Impl *m_pImpl; + tOBEX_HEADERS * GetBinaryPtr(CObexHeaders *p_headers_object); + +}; + + +//////////////////////////////////////////////////////////////////////////// +// +// Class for OBEX server side applications +// +class CObexServer_Impl; + +class WIDCOMMSDK CObexServer +{ +public: + + ~CObexServer(); + CObexServer(); + + tOBEX_ERRORS Register (UINT8 scn, UINT8 *p_target = NULL); + tOBEX_ERRORS Unregister(); + tOBEX_ERRORS OpenCnf (tOBEX_ERRORS obex_errors, tOBEX_RESPONSE_CODE rsp_code, CObexHeaders * p_response, UINT16 mtu= OBEX_DEFAULT_MTU); + tOBEX_ERRORS SetPathCnf (tOBEX_ERRORS obex_errors, tOBEX_RESPONSE_CODE rsp_code, CObexHeaders * p_response); + tOBEX_ERRORS PutCnf (tOBEX_ERRORS obex_errors, tOBEX_RESPONSE_CODE rsp_code, CObexHeaders * p_response); + tOBEX_ERRORS PutCreateCnf (tOBEX_ERRORS obex_errors, tOBEX_RESPONSE_CODE rsp_code, CObexHeaders * p_response); + tOBEX_ERRORS PutDeleteCnf (tOBEX_ERRORS obex_errors, tOBEX_RESPONSE_CODE rsp_code, CObexHeaders * p_response); + tOBEX_ERRORS GetCnf (tOBEX_ERRORS obex_errors, tOBEX_RESPONSE_CODE rsp_code, BOOL final, CObexHeaders * p_response); + tOBEX_ERRORS AbortCnf (tOBEX_ERRORS obex_errors, tOBEX_RESPONSE_CODE rsp_code, CObexHeaders * p_response); + tOBEX_ERRORS CloseCnf (tOBEX_ERRORS obex_errors, tOBEX_RESPONSE_CODE rsp_code, CObexHeaders * p_response); + BOOL SwitchRole(MASTER_SLAVE_ROLE new_role); + + //Create AudioConnection + AUDIO_RETURN_CODE CreateAudioConnection(BOOL bIsClient, UINT16 *audioHandle); + + //Disconnect AudioConnection + AUDIO_RETURN_CODE RemoveAudioConnection(UINT16 audioHandle); + + //Audio callback functions + virtual void OnAudioConnected(UINT16 audioHandle){}; + virtual void OnAudioDisconnect(UINT16 audinHandle){}; + +#ifdef _WIN32_WCE + AUDIO_RETURN_CODE ReadAudioData(void *pBuff, DWORD dwLen, DWORD *dwByteR); + AUDIO_RETURN_CODE WriteAudioData(void *pBuff, DWORD dwLen, DWORD *dwByteW); +#endif + + + void GetRemoteBDAddr(BD_ADDR_PTR p_bd_addr); + + virtual void OnOpenInd(CObexHeaders *p_request) = 0; + virtual void OnSetPathInd (CObexHeaders * p_request, BOOL backup, BOOL create) = 0; + virtual void OnPutInd (CObexHeaders * p_request, BOOL final) = 0; + virtual void OnPutCreateInd(CObexHeaders * p_request) = 0; + virtual void OnPutDeleteInd (CObexHeaders * p_request) = 0; + virtual void OnGetInd (CObexHeaders * p_request, BOOL final) = 0; + virtual void OnAbortInd (CObexHeaders * p_request) = 0; + virtual void OnCloseInd(CObexHeaders *p_request) = 0; + + tOBEX_ERRORS SetLinkSupervisionTimeOut(UINT16 timeout); +private: + + +static CObexServer *m_p_first_server; + CObexServer *m_p_next_server; + friend class CObexServerFriend; + friend class CObexServer_Impl; + CObexServer_Impl *m_pImpl; + tOBEX_HEADERS * GetBinaryPtr(CObexHeaders *p_headers_object); +}; + +//////////////////////////////////////////////////////////////////////////// +// +// Class for OBEX user defined parameter header +// +class CObexUserDefined_Impl; + +class WIDCOMMSDK CObexUserDefined +{ +public: + + ~CObexUserDefined(); + CObexUserDefined(); + BOOL SetHeader (UINT8 id, UINT8 byte); + BOOL SetHeader (UINT8 id, UINT32 four_byte); + BOOL SetHeader (UINT8 id, UINT16 * p_text); + BOOL SetHeader (UINT8 id, UINT8 * p_array, UINT16 length); + UINT8 GetUserType (UINT8 * p_id); + BOOL GetByte (UINT8 *p_byte); + BOOL GetFourByte (UINT32 *p_fourbyte); + UINT16 GetLength (); + BOOL GetText (UINT16 *p_text); + BOOL GetOctets (UINT8 *p_octet_array); + +private: + tOBEX_USER_HDR ud_hdr; + friend class CObexUserDefined_Impl; + CObexUserDefined_Impl *m_pImpl; + + + // This class will not support the compiler-supplied copy constructor or assignment operator, tOBEX_CSESSION_HANDLE session_handle, + // so these are declared private to prevent inadvertant use by the application. + CObexUserDefined(const CObexUserDefined & x); + CObexUserDefined& operator= (const CObexUserDefined & x); +}; + + +//////////////////////////////////////////////////////////////////////////// +// +// Class for OBEX headers object, tOBEX_CSESSION_HANDLE session_handle, a container for all possible OBEX header values +// +class CObexHeaders_Impl; + +class WIDCOMMSDK CObexHeaders +{ + friend tOBEX_HEADERS * CObexClient::GetBinaryPtr(CObexHeaders *p_headers_object); + friend class CObexClientFriend; + friend tOBEX_HEADERS * CObexServer::GetBinaryPtr(CObexHeaders *p_headers_object); + friend class CObexServerFriend; + +public: + + ~CObexHeaders(); + CObexHeaders(); // locally allocate container, tOBEX_CSESSION_HANDLE session_handle, with all null header values + +private: + + // These functions are accessable only from friends on the lower OBEX levels + // They permit efficient use of existing OBEX header structures + CObexHeaders(tOBEX_HEADERS *p_external_hdrs); // externally constructed container + tOBEX_HEADERS * GetBinaryPtr(); + + friend class CObexHeaders_Impl; + CObexHeaders_Impl *m_pImpl; + + // This class will not support the compiler-supplied copy constructor or assignment operator, tOBEX_CSESSION_HANDLE session_handle, + // so these are declared private to prevent inadvertant use by the application. + CObexHeaders(const CObexHeaders & x); + CObexHeaders& operator= (const CObexHeaders & x); + +public: + + void SetCount(UINT32 count); + void DeleteCount(); + BOOL GetCount(UINT32 * p_count); + + BOOL SetName (UINT16 * p_array); + void DeleteName(); + BOOL GetNameLength (UINT32 * p_len_incl_null); + BOOL GetName (UINT16 * p_array); + + BOOL SetType (UINT8 * p_array, UINT32 length); + void DeleteType(); + BOOL GetTypeLength (UINT32 * p_length); + BOOL GetType (UINT8 * p_array); + + void SetLength(UINT32 length); + void DeleteLength(); + BOOL GetLength(UINT32 * p_length); + + BOOL SetTime(char * p_str_8601); + void DeleteTime(); + BOOL GetTime(char * p_str_8601); + + BOOL SetDescription (UINT16 * p_array); + void DeleteDescription(); + BOOL GetDescriptionLength (UINT32 * p_len_incl_null); + BOOL GetDescription (UINT16 * p_array); + + BOOL AddTarget (UINT8 * p_array, UINT32 length); + UINT32 GetTargetCnt (); + BOOL DeleteTarget (UINT16 index); + BOOL GetTargetLength (UINT32 * p_length, UINT16 index); + BOOL GetTarget (UINT8 * p_array, UINT16 index); + + BOOL AddHttp (UINT8 * p_array, UINT32 length); + UINT32 GetHttpCnt (); + BOOL DeleteHttp (UINT16 index); + BOOL GetHttpLength (UINT32 * p_length, UINT16 index); + BOOL GetHttp (UINT8 * p_array, UINT16 index); + + BOOL SetBody (UINT8 * p_array, UINT32 length, BOOL body_end); + void DeleteBody(); + BOOL GetBodyLength ( UINT32 * p_length); + BOOL GetBody (UINT8 * p_array, BOOL *p_body_end); + + BOOL SetWho (UINT8 * p_array, UINT32 length); + void DeleteWho(); + BOOL GetWhoLength ( UINT32 * p_length); + BOOL GetWho (UINT8 * p_array); + + BOOL AddAppParam (UINT8 tag, UINT8 length, UINT8 * p_array); + UINT32 GetAppParamCnt (); + BOOL DeleteAppParam(UINT16 index); + BOOL GetAppParamLength(UINT8 * p_length, UINT16 index); + BOOL GetAppParam (UINT8 * p_tag, UINT8 *p_array, UINT16 index); + + BOOL AddAuthChallenge (UINT8 tag, UINT8 length, UINT8 * p_array); + UINT32 GetAuthChallengeCnt (); + BOOL DeleteAuthChallenge (UINT16 index); + BOOL GetAuthChallengeLength (UINT8 * p_length, UINT16 index); + BOOL GetAuthChallenge (UINT8 * p_tag, UINT8 *p_array, UINT16 index); + + BOOL AddAuthResponse (UINT8 tag, UINT8 length, UINT8 * p_array); + UINT32 GetAuthResponseCnt (); + BOOL DeleteAuthResponse (UINT16 index); + BOOL GetAuthResponseLength (UINT8 * p_length, UINT16 index); + BOOL GetAuthResponse (UINT8 * p_tag, UINT8 *p_array, UINT16 index); + + BOOL SetObjectClass (UINT8 * p_array, UINT32 length); + void DeleteObjectClass (); + BOOL GetObjectClassLength ( UINT32 * p_length); + BOOL GetObjectClass (UINT8 * p_array); + + BOOL AddUserDefined (CObexUserDefined * p_user_defined); + UINT32 GetUserDefinedCnt (); + BOOL DeleteUserDefined (UINT16 index); + BOOL GetUserDefinedLength (UINT16 * p_length, UINT16 index); + BOOL GetUserDefined (CObexUserDefined * p_user_defined, UINT16 index); + +private: + + void SetDefaults(tOBEX_HEADERS *p_headers); // all header fields become null + void FreeInternal(tOBEX_HEADERS *p_headers); // release all memory malloced to this object + void SetFlag (UINT32 mask); + void ClearFlag(UINT32 mask); + BOOL TestFlag(UINT32 mask); + BOOL locally_allocated; + +private: + + tOBEX_HEADERS *p_hdrs; +}; + + + +#pragma pack () + +#endif diff --git a/inc/BtSdkCE.h b/inc/BtSdkCE.h new file mode 100644 index 0000000..30bb06a --- /dev/null +++ b/inc/BtSdkCE.h @@ -0,0 +1,17 @@ +//btwcelib.h + +#pragma once +#ifdef WC13 +#pragma comment(lib, "sdkce.lib") + +#include "BtIfClasses13.h" + +#else + +#pragma comment(lib, "btsdkce30.lib") + +#include "BtIfDefinitions.h" +#include "BtIfClasses.h" +#include "BtIfObexHeaders.h" +#endif + diff --git a/inc/MQCommon.h b/inc/MQCommon.h new file mode 100644 index 0000000..9913cfe --- /dev/null +++ b/inc/MQCommon.h @@ -0,0 +1,247 @@ +/* $Header: + * + * ======================================================================== + * Copyright (c) 2002 by MediaQ, Incorporated. All Rights Reserved. + * + * Confidential and Proprietary to MediaQ, Incorporated. + * ======================================================================== + * + * $Log: + */ + +#ifndef _MQCOMMON_H_ +#define _MQCOMMON_H_ +/*-------------------------------------------------------* + | Configurations that can be modified by API developers.| + *-------------------------------------------------------*/ +//#define MQAPI_STATIC_LIBRARY + +/*-------------------------------------------------------* + | Stop! Don't modified anything below. | + *-------------------------------------------------------*/ + +#ifdef UNDER_CE + +#define MQVx_LIB_TRAP(trapNum) +#define MQGx_LIB_TRAP(trapNum) + +#if !defined(QUERYESCSUPPORT) +#define QUERYESCSUPPORT 8 // Just in case it is not defined +#endif + +#if !defined(MQAPI_RETTYPE) +typedef int MQAPI_RETTYPE; +#endif +#if !defined(PVOID) +typedef void * PVOID; +#endif + +#if !defined(MQAPI_HANDLE) +typedef UINT32 MQAPI_HANDLE; +#endif + +#if !defined(MQAPI_OPEN_HANDLE) +typedef MQAPI_HANDLE MQAPI_OPEN_HANDLE; +#endif + +#if !defined(MQAPI_OPEN_RETTYPE) +typedef MQAPI_HANDLE MQAPI_OPEN_RETTYPE; +#endif + +#else /* Palm OS*/ + +#include +#include +#include +#include // for RectangleType +#include // for WinDrawRectangle + +#if !defined(INT8) +typedef signed char INT8; +#endif + +#if !defined(UINT8) +typedef unsigned char UINT8; +#endif + +#if !defined(INT16) +typedef Int16 INT16; +#endif + +#if !defined(UINT16) +typedef UInt16 UINT16; +#endif + +#if !defined(INT32) +typedef Int32 INT32; +#endif + +#if !defined(UINT32) +typedef UInt32 UINT32; +#endif + +#if !defined(BYTE) +typedef unsigned char BYTE; +#endif + +#if !defined(PVOID) +typedef void * PVOID; +#endif + +#if !defined(MQAPI_STATIC_LIBRARY) +#ifdef BUILDING_MQVx_LIB +#define MQVx_LIB_TRAP(trapNum) +#define MQGx_LIB_TRAP(trapNum) +#else +#define MQVx_LIB_TRAP(trapNum) SYS_TRAP(trapNum) +#define MQGx_LIB_TRAP(trapNum) SYS_TRAP(trapNum) +#endif +#endif + +#if defined(MQAPI_STATIC_LIBRARY) +#define MQVx_LIB_TRAP(trapNum) +#define MQGx_LIB_TRAP(trapNum) +#endif + +#if !defined(MQAPI_OPEN_HANDLE) +typedef UINT16 MQAPI_OPEN_HANDLE; +#endif + +#if !defined(MQAPI_OPEN_RETTYPE) +typedef Err MQAPI_OPEN_RETTYPE; +#endif + +#if !defined(MQAPI_RETTYPE) +typedef Err MQAPI_RETTYPE; +#endif + +#if !defined(MQAPI_HANDLE) +typedef UINT16 MQAPI_HANDLE; +#endif + +#if !defined(MQAPI_STATIC_LIBRARY) + +#if 0 +typedef enum { + MQGxLibTrapGetProperty = sysLibTrapCustom, + MQGxLibTrapFillRect, + MQGxLibTrapCopyRect, + MQGxLibTrapBlt, + MQGxLibTrapCopyMonoBitmap, + MQGxLibTrapCopyTransMonoBitmap, + MQGxLibTrapCopyColorBitmap, + MQGxLibTrapLine, + MQGxLibTrapSetClip, + MQGxLibTrapSetDisplayAddr, + MQGxLibTrapVSync, + MQGxLibTrapSetPal, + MQGxLibTrapSetPalRange, + MQGxLibTrapGetPal, + MQGxLibTrapGetPalRange, + MQGxLibTrapBltFullScreen, + MQGxLibTrapCopyRectDirect, + MQGxLibTrapBltPackedBitmap, + + MQGxLibTrapLast + } MQGxLibTrapNumberEnum; + +typedef enum { + MQVxLibTrapGetProperty = sysLibTrapCustom, + MQVxLibTrapSetVideo, + MQVxLibTrapShowVideo, + MQVxLibTrapConvertImage, + MQVxLibTrapSetVIP, + MQVxLibTrapVIPStart, + MQVxLibTrapVIPStop, + MQVxLibTrapVIPCapture, + MQVxLibTrapAllocSurface, + MQVxLibTrapFreeSurface, + MQVxLibTrapSurfaceLock, + MQVxLibTrapSurfaceUnlock, + + MQVxLibTrapLast + } MQVxLibTrapNumberEnum; +#else +#define MQGxLibTrapGetProperty (sysLibTrapCustom + 0) +#define MQGxLibTrapFillRect (sysLibTrapCustom + 1) +#define MQGxLibTrapCopyRect (sysLibTrapCustom + 2) +#define MQGxLibTrapBlt (sysLibTrapCustom + 3) +#define MQGxLibTrapCopyMonoBitmap (sysLibTrapCustom + 4) +#define MQGxLibTrapCopyTransMonoBitmap (sysLibTrapCustom + 5) +#define MQGxLibTrapCopyColorBitmap (sysLibTrapCustom + 6) +#define MQGxLibTrapLine (sysLibTrapCustom + 7) +#define MQGxLibTrapSetClip (sysLibTrapCustom + 8) +#define MQGxLibTrapSetDisplayAddr (sysLibTrapCustom + 9) +#define MQGxLibTrapVSync (sysLibTrapCustom + 10) +#define MQGxLibTrapSetPal (sysLibTrapCustom + 11) +#define MQGxLibTrapSetPalRange (sysLibTrapCustom + 12) +#define MQGxLibTrapGetPal (sysLibTrapCustom + 13) +#define MQGxLibTrapGetPalRange (sysLibTrapCustom + 14) +#define MQGxLibTrapBltFullScreen (sysLibTrapCustom + 15) +#define MQGxLibTrapCopyRectDirect (sysLibTrapCustom + 16) +#define MQGxLibTrapBltPackedBitmap (sysLibTrapCustom + 17) +#define MQGxLibTrapLas (sysLibTrapCustom + 18) + +#define MQVxLibTrapGetProperty (sysLibTrapCustom + 0) +#define MQVxLibTrapSetVideo (sysLibTrapCustom + 1) +#define MQVxLibTrapShowVideo (sysLibTrapCustom + 2) +#define MQVxLibTrapConvertImage (sysLibTrapCustom + 3) +#define MQVxLibTrapSetVIP (sysLibTrapCustom + 4) +#define MQVxLibTrapVIPStart (sysLibTrapCustom + 5) +#define MQVxLibTrapVIPStop (sysLibTrapCustom + 6) +#define MQVxLibTrapVIPCapture (sysLibTrapCustom + 7) +#define MQVxLibTrapAllocSurface (sysLibTrapCustom + 8) +#define MQVxLibTrapFreeSurface (sysLibTrapCustom + 9) +#define MQVxLibTrapSurfaceLock (sysLibTrapCustom + 10) +#define MQVxLibTrapSurfaceUnlock (sysLibTrapCustom + 11) +#define MQVxLibTrapLas (sysLibTrapCustom + 12) +#endif + +#endif + +typedef struct _MQGXBLTPARAM +{ + // Destination location and Dimension parameters + INT16 dx; // destination x in pixels + // (relative to left) + INT16 dy; // destination y in scan lines + // (relative to top) + INT16 w; // width of the rectangle in pixels + INT16 h; // height of the rectangle in scan lines + + // Source bitmap parameters + INT16 sx; // source x in pixels (relative to left) + INT16 sy; // source y in scan lines + // (relative to top) + INT16 sw; // width of the source rectangle in + // pixels (for Stretch Blt only) + INT16 sh; // height of the source rectangle in + // scanlines (for Stretch Blt only) + UINT16 bpp; // color depth + INT16 stride; // number of bytes per scanline + // for bitmap + UINT32 fgColor; // foreground color (used if pixel data + // is 1 on 1bpp source bitmap) + UINT32 bgColor; // background color (used if pixel data + // is 0 on 1bpp source bitmap) + UINT8 *pBits; // pointer to source bitmap + + // Pattern bitmap parameters + INT16 px; // pattern offset in x direction + INT16 py; // pattern offset in y direction + UINT32 patFgColor; // foreground color (used if pixel data + // is 1 on 1bpp source bitmap) + UINT32 patBgColor; // background color (used if pixel data + // is 0 on 1bpp source bitmap) + UINT32 pattern0; // First 32 bits of Pattern data (1bpp) + UINT32 pattern1; // Second 32 bits of Pattern data (1bpp) + + // Miscellaneous parameters + UINT32 colorCompare;// ColorKey for Transparent Blt + UINT32 flags; // (see below) + UINT16 rop3; // ROP3 code +} MQGXBLTPARAM; + +#endif + +#endif //_MQCOMMON_H_ \ No newline at end of file diff --git a/inc/MQGX.h b/inc/MQGX.h new file mode 100644 index 0000000..aeaf1a8 --- /dev/null +++ b/inc/MQGX.h @@ -0,0 +1,300 @@ +/* $Header: + * + * ======================================================================== + * Copyright (c) 2002 by MediaQ, Incorporated. All Rights Reserved. + * + * Confidential and Proprietary to MediaQ, Incorporated. + * ======================================================================== + * + * $Log: + * + */ + + +// The following ifdef block is the standard way of creating macros which make exporting +// from a DLL simpler. All files within this DLL are compiled with the MQGX_EXPORTS +// symbol defined on the command line. this symbol should not be defined on any project +// that uses this DLL. This way any other project whose source files include this file see +// MQGAPIEXT_API functions as being imported from a DLL, wheras this DLL sees symbols +// defined with this macro as being exported. +#ifndef _MQGX_H_ +#define _MQGX_H_ + +#ifdef UNDER_CE +#include "windows.h" +#else /* Palm OS */ +#endif + +#include "mqcommon.h" + +#define MQGX_MAJOR_VERSION 0x0000 +#define MQGX_MINOR_VERSION 0x0006 + +#define MQ_GX_ERROR 0 +#define MQ_GX_SUCCESS 1 + +#define MQ_ERROR MQ_GX_ERROR +#define MQ_SUCCESS MQ_GX_SUCCESS + +#define MQ_GX_WAITFORVSYNC 0 // waits for new VSync +#define MQ_GX_IS_VSYNC 1 // returns 1 if in VSync else 0 +#define MQ_GX_GETSCANLINE 2 // returns current scanline + // being refreshed +typedef struct _MQPROPERTY +{ + UINT16 VendorID; // vendorID (0x4D51) + UINT16 DeviceID; + UINT16 DeviceRev; + UINT32 VideoMemSize; // size in bytes + UINT32 BuildNumber; // Build Number +} MQPROPERTY, *PMQPROPERTY; + +typedef struct _MQRECT +{ + UINT16 top; + UINT16 left; + UINT16 right; + UINT16 bottom; +} MQRECT, *PMQRECT; + +typedef struct _MQGXPROPERTY +{ + MQPROPERTY Property; + UINT32 GXVersion; // Upper 16bit: Major number + // Lower 16bit: Minor number + UINT32 GXCapFlags; // see definition of CapFlags +} MQGXPROPERTY, *PMQGXPROPERTY; + +// GXCapFlags definitions: +#define MQ_GX_CAP_BLT 0x00000001 // MQGxFillRect(), MQGxCopyRect() + // and MQGxBltRect() +#define MQ_GX_CAP_LINE 0x00000002 // MQGxLine() is available +#define MQ_GX_CAP_CLIP 0x00000004 // MQGxSetClip() is available +//#define MQ_GX_CAP_STRETCHBLT 0x00000008 + +// Miscellaneous flag +#define MQ_GX_BLT_CLIP 0x00000001 +// enable clipping(MQSetClip must be called first) + +// Transparent flags +#define MQ_GX_BLT_TRANSPARENT_SRC_COLOR 0x00000004 +// color source transparent blt (if source pixel +// matches colorCompare, do not overwrite) +#define MQ_GX_BLT_TRANSPARENT_SRC_MONO 0x00000008 +// mono source transparent blt (if source bit is 0, no +// overwrite on corresponding destination location) +#define MQ_GX_BLT_TRANSPARENT_SRC_MONO_INV 0x00000010 +// mono source inverted transparent blt (if source bit +// is 1, no overwrite on corresponding destination +// location) +#define MQ_GX_BLT_TRANSPARENT_DST 0x00000020 +// destination transparent blt (if destination pixel +// matches colorCompare, overwrite) + +// Source flags +#define MQ_GX_BLT_SRC_SYSMEM_COLOR 0x00000040 +// color source in system memory +#define MQ_GX_BLT_SRC_SYSMEM_MONO 0x00000080 +// mono source in system memory +#define MQ_GX_BLT_SRC_SCREEN_MONO 0x00000100 +// mono source in video/screen memory +#define MQ_GX_BLT_SRC_SOLID 0x00000400 +// source is solid color + +// Pattern flags +#define MQ_GX_BLT_PAT_MONO 0x00000800 +// pattern is mono 8x8 bitmap +#define MQ_GX_BLT_PAT_SOLID 0x00001000 +// pattern is solid color + +#if defined(UNDER_CE) +#if !defined(MQAPI_STATIC_LIBRARY) + +#ifdef MQGX_EXPORTS +#define MQAPI_FUNC __declspec(dllexport) +#else +#define MQAPI_FUNC __declspec(dllimport) +#endif +#endif + +#if defined(MQAPI_STATIC_LIBRARY) +#define MQAPI_FUNC extern "C" +#endif + +#else /* Palm OS */ + +//#ifdef BUILDING_MQGx_LIB +//#define MQAPI_FUNC +//#else +#define MQAPI_FUNC extern +//#endif + +// PalmPilot common definitions +//#include +//#include + + +/******************************************************************** + * Type and creator of MQGx Library database + ********************************************************************/ +#define MQGxLibCreatorID 'MEDQ' // MQGx Library database creator +#define MQGxLibTypeID 'libr' // Standard library database type + + +/******************************************************************** + * Internal library name which can be passed to SysLibFind() + ********************************************************************/ +#define MQGxLibName "MQGx.lib" + + +/************************************************************ + * MQGx Library result codes + * (appErrorClass is reserved for 3rd party apps/libraries. + * It is defined in SystemMgr.h) + *************************************************************/ + +#define MQGxErrParam (appErrorClass | 1) // invalid parameter +#define MQGxErrNotOpen (appErrorClass | 2) // library is not open +#define MQGxErrStillOpen (appErrorClass | 3) // returned from MQGxLibClose() if + // the library is still open by others +#define MQGxErrMemory (appErrorClass | 4) // memory error occurred + +#endif + +#ifdef __cplusplus +extern "C" { // only need to export C interface if + // used by C++ source code +#endif + + +// Exported functions +MQAPI_FUNC MQAPI_OPEN_RETTYPE MQGxOpen(MQAPI_OPEN_HANDLE GXhandle) + MQGx_LIB_TRAP(sysLibTrapOpen); + +MQAPI_FUNC MQAPI_RETTYPE MQGxClose(MQAPI_HANDLE GXhandle) + MQGx_LIB_TRAP(sysLibTrapClose); + +MQAPI_FUNC MQAPI_RETTYPE MQGxGetProperty(MQAPI_HANDLE GXhandle, + PMQGXPROPERTY pGxProp) + MQGx_LIB_TRAP(MQGxLibTrapGetProperty); +MQAPI_FUNC MQAPI_RETTYPE MQGxFillRect(MQAPI_HANDLE GXhandle, + INT16 x, + INT16 y, + INT16 w, + INT16 h, + UINT32 color) + MQGx_LIB_TRAP(MQGxLibTrapFillRect); +MQAPI_FUNC MQAPI_RETTYPE MQGxCopyRect(MQAPI_HANDLE GXhandle, + INT16 dx, + INT16 dy, + INT16 w, + INT16 h, + INT16 sx, + INT16 sy) + MQGx_LIB_TRAP(MQGxLibTrapCopyRect); +MQAPI_FUNC MQAPI_RETTYPE MQGxBlt(MQAPI_HANDLE GXhandle, + MQGXBLTPARAM *pBltParam) + MQGx_LIB_TRAP(MQGxLibTrapBlt); + +MQAPI_FUNC MQAPI_RETTYPE MQGxCopyMonoBitmap(MQAPI_HANDLE GXhandle, + INT16 dx, INT16 dy, + INT16 w, INT16 h, + INT16 sx, INT16 sy, + UINT16 fgColor, UINT16 bgColor, + INT16 srcStride, + UINT8 *pMonoBits) + MQGx_LIB_TRAP(MQGxLibTrapCopyMonoBitmap); + +MQAPI_FUNC MQAPI_RETTYPE MQGxCopyTransMonoBitmap(MQAPI_HANDLE GXhandle, + INT16 dx, INT16 dy, + INT16 w, INT16 h, + INT16 sx, INT16 sy, + UINT16 color, + INT16 srcStride, + UINT8 *pMonoBits, + INT16 selectTrans ) + MQGx_LIB_TRAP(MQGxLibTrapCopyTransMonoBitmap); + +MQAPI_FUNC MQAPI_RETTYPE MQGxCopyColorBitmap(MQAPI_HANDLE GXhandle, + INT16 dx, INT16 dy, + INT16 w, INT16 h, + INT16 sx, INT16 sy, + INT16 srcStride, + UINT8 *pColorBits ) + MQGx_LIB_TRAP(MQGxLibTrapCopyColorBitmap); + +MQAPI_FUNC MQAPI_RETTYPE MQGxLine(MQAPI_HANDLE GXhandle, + UINT16 x1, + UINT16 y1, + UINT16 x2, + UINT16 y2, + UINT16 rop2, + UINT32 color, + UINT16 flags) + MQGx_LIB_TRAP(MQGxLibTrapLine); +MQAPI_FUNC MQAPI_RETTYPE MQGxSetClip(MQAPI_HANDLE GXhandle, PMQRECT pClipRect) + MQGx_LIB_TRAP(MQGxLibTrapSetClip); +MQAPI_FUNC MQAPI_RETTYPE MQGxSetDisplayAddr(MQAPI_HANDLE GXhandle, + UINT32 uiDispAddr32) + MQGx_LIB_TRAP(MQGxLibTrapSetDisplayAddr); +MQAPI_FUNC MQAPI_RETTYPE MQGxVSync(MQAPI_HANDLE GXhandle, UINT16 option) + MQGx_LIB_TRAP(MQGxLibTrapVSync); +MQAPI_FUNC MQAPI_RETTYPE MQGxSetPal(MQAPI_HANDLE GXhandle, + UINT32 palVal, UINT16 index) + MQGx_LIB_TRAP(MQGxLibTrapSetPal); + +MQAPI_FUNC MQAPI_RETTYPE MQGxSetPalRange(MQAPI_HANDLE GXhandle, + UINT32 * palRange, UINT16 startIndex, + UINT16 endIndex) + MQGx_LIB_TRAP(MQGxLibTrapSetPalRange); + +MQAPI_FUNC MQAPI_RETTYPE MQGxGetPal(MQAPI_HANDLE GXhandle, + UINT32 * pPalVal, UINT16 index) + MQGx_LIB_TRAP(MQGxLibTrapGetPal); +MQAPI_FUNC MQAPI_RETTYPE MQGxGetPalRange(MQAPI_HANDLE GXhandle, + UINT32 * pPalArray, + UINT16 startIndex, UINT16 endIndex) + MQGx_LIB_TRAP(MQGxLibTrapGetPalRange); +MQAPI_FUNC MQAPI_RETTYPE MQGxBltFullScreen(MQAPI_HANDLE GXhandle, + MQGXBLTPARAM *pBltParam) + MQGx_LIB_TRAP(MQGxLibTrapBltFullScreen); +MQAPI_FUNC MQAPI_RETTYPE MQGxCopyRectDirect(MQAPI_HANDLE GXhandle, + INT16 dx, + INT16 dy, + INT16 w, + INT16 h, + INT16 sx, + INT16 sy) + MQGx_LIB_TRAP(MQGxLibTrapCopyRectDirect); +MQAPI_FUNC MQAPI_RETTYPE MQGxCopyPackedColorBitmap(MQAPI_HANDLE GXhandle, + UINT32 *pColorBits, + UINT16 bitmapSize, + MQRECT rect) + MQGx_LIB_TRAP(MQGxLibTrapBltPackedBitmap); + +#ifndef UNDER_CE +//-------------------------------------------------- +// Standard library open, close, sleep and wake functions +//-------------------------------------------------- + +extern Err MQGxSleep(UInt16 refNum) + MQGx_LIB_TRAP(sysLibTrapSleep); + +extern Err MQGxWake(UInt16 refNum) + MQGx_LIB_TRAP(sysLibTrapWake); + + +//-------------------------------------------------- +// Custom library API functions +//-------------------------------------------------- + +// For loading the library in Palm OS Mac emulation mode +extern Err MQGxLibInstall(UInt16 refNum, SysLibTblEntryPtr entryP); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif // _MQGX_H_ \ No newline at end of file diff --git a/inc/MQGX.prc b/inc/MQGX.prc new file mode 100644 index 0000000..a9c0f13 Binary files /dev/null and b/inc/MQGX.prc differ diff --git a/inc/PalmGoLCD.h b/inc/PalmGoLCD.h new file mode 100644 index 0000000..ccbc788 --- /dev/null +++ b/inc/PalmGoLCD.h @@ -0,0 +1,463 @@ +/****************************************************************************** + * Copyright (c) 2003 Palm, Inc. or its subsidiaries. + * All rights reserved. + *****************************************************************************/ + +/** + * @file PalmGoLCD.h + * @version 1.0 + * @date 03/03/2003 + * + * Public API for the GoLCD Library. + * + * You can use the functions in this API to enable or disable fullscreen + * writing, enable and disable the Graffiti 2 shift indicator (GSI) as a control for + * full-screen writing, enable and disable Graffiti 2 inking, and even change the colors + * of Graffiti 2 inking and the GSI. + * + * You can check whether the GoLCD Manager is installed by examining its feature set. + * Call FtrGet as follows: + * @sample + * @code err = FtrGet (goLcdCreator, goLcdFtrNumVersion, &value); @endcode + * + *
+ */ + +#ifndef __PALMGOLCD_H__ +#define __PALMGOLCD_H__ + +#include +#include + +/******************************************************************** + * Library type and creator + ********************************************************************/ + +#define goLcdLibName "GoLcdLib" /**< GoLCD library name. */ +#define goLcdLibType 'libr' /**< GoLCD library type. */ +#define goLcdLibCreator 'GAny' /**< GoLCD creator ID. */ +#define goLcdLibFtrNum 0 /**< GoLCd feature number. */ + + +/******************************************************************** + * Constants + ********************************************************************/ + +/** + * GoLCD Status. + * One of the following: + * - goLcdNotAvailable + * - goLcdDisabled + * - goLcdEnabled + */ +typedef Int32 GoLcdStatusType; + +#define goLcdNotAvailable -1 /**< GoLCD not available on this device. */ +#define goLcdDisabled 0 /**< GoLCD functionality is disabled. */ +#define goLcdEnabled 1 /**< GoLCD functionality is enabled. */ + + +/** + * GoLCD Ink State. + * One of the following: + * - goLcdInkDisabled + * - goLcdInkEnabled + */ +typedef Int32 GoLcdInkStateType; + +#define goLcdInkDisabled 0 /**< GoLCD inking is disabled. */ +#define goLcdInkEnabled 1 /**< GoLCD inking is enabled. */ + + +/** + * GoLCD Timeout Mode. + * One of the following: + * - goLcdGraffitiMode + * - goLcdPenTapMode + */ +typedef UInt32 GoLcdModeType; + +/** + * When it enters goLcdGraffitiMode, GoLCD continuously + * interprets all pen events as Graffiti 2 strokes. There is a timeout + * value associated with goLcdGraffitiMode, however. If it + * does not receive a new pen event within the allotted time, it + * returns to goLcdPenTapMode. + *
+ * The default time-out value for goLcdGraffitiMode is + * 150 system ticks, or 1500 milliseconds. + */ +#define goLcdGraffitiMode 0 +/** + * GoLCD starts in goLcdPenTapMode. When a penDownEvent is + * received, a timer is started. If a penUpEvent is received before + * the timer reaches the time-out value for this mode, the pen + * events are passed on to the event-handler for the application + * control. Otherwise, if the pen events exceed the time-out + * value and the x, y coordinates change significantly, GoLCD + * enters goLcdGraffitiMode and treats the pen events as a + * Graffiti 2 stroke. + *
+ * The default time-out value for goLcdPenTapMode is 15 system + * ticks, or 150 milliseconds. + */ +#define goLcdPenTapMode 1 +#define goLcdModeCount 2 + + +/** + * GoLCD GSI State. + * One of the following: + * - goLcdGsiNormal + * - goLcdGsiOverride + */ +typedef UInt32 GoLcdGsiStateType; + +#define goLcdGsiNormal 0 /**< GSI is handled as normal. */ +#define goLcdGsiOverride 1 /**< GoLCD overrides the GSI with its own control. */ + + +/** + * GoLCD Color Mode. + * One of the following: + * - goLcdColorDefault + * - goLcdColorOverride + * - goLcdColorInverted + */ +typedef UInt32 GoLcdColorModeType; + +#define goLcdColorDefault 0 /**< Use the default color scheme. */ +#define goLcdColorOverride 1 /**< Use the specified color (passed in as a separate argument). */ +#define goLcdColorInverted 2 /**< Use only with ink color. Inverts the color of the display area beneath the Graffiti 2 stroke. */ + + +/******************************************************************** + * Traps + ********************************************************************/ +#define kGoLcdLibTrapOpen sysLibTrapOpen +#define kGoLcdLibTrapClose sysLibTrapClose + +#define kGoLcdLibTrapGetStatus (sysLibTrapCustom) +#define kGoLcdLibTrapSetStatus (sysLibTrapCustom+1) +#define kGoLcdLibTrapGetInkState (sysLibTrapCustom+2) +#define kGoLcdLibTrapSetInkState (sysLibTrapCustom+3) +#define kGoLcdLibTrapGetTimeout (sysLibTrapCustom+4) +#define kGoLcdLibTrapSetTimeout (sysLibTrapCustom+5) +#define kGoLcdLibTrapGetBounds (sysLibTrapCustom+6) +#define kGoLcdLibTrapSetBounds (sysLibTrapCustom+7) +#define kGoLcdLibTrapGetGsiState (sysLibTrapCustom+8) +#define kGoLcdLibTrapSetGsiState (sysLibTrapCustom+9) + +#define GoLcdLibAPIVersion (sysMakeROMVersion(3, 6, 0, sysROMStageBeta, 0)) + +/******************************************************************** + * Prototypes + ********************************************************************/ +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Opens the GoLCD library. + * + * This function should be called prior to calling the other GoLCD functions. + * + * @param libRefNum Reference number of the GoLCD library. + * @return The error code returned from the library. If this is errNone, the + * function was sucessful. + * + * @sample + * @code + * UInt16 gGoLcdLibRefNum = 0; + * err = SysLibFind(goLcdLibName, &gGoLcdLibRefNum); + * if( err == sysErrLibNotFound ) + * { + * err = SysLibLoad(goLcdLibType, goLcdLibCreator, &gGoLcdLibRefNum); + * if( !err ) { + * err = GoLcdLibOpen(gGoLcdLibRefNum); + * } + * } @endcode + * + * @see GoLcdLibClose + */ +Err GoLcdLibOpen(UInt16 libRefNum) + SYS_TRAP(kGoLcdLibTrapOpen); + +/** + * Closes the GoLCD library. + * + * This function should be called after your application has finished with the GoLCD + * library. + * + * @param libRefNum Reference number of the GoLCD library. + * @return The error code returned from the library. If this is errNone, the + * function was sucessful. + * + * @sample + * @code + * err = GoLcdLibClose(gGoLcdLibRefNum); + * if( err == errNone ) + * err = SysLibRemove(gGoLcdLibRefNum); @endcode + * + * @see GoLcdLibOpen + */ +Err GoLcdLibClose(UInt16 libRefNum) + SYS_TRAP(kGoLcdLibTrapClose); + +/** + * Returns whether the GoLCD functionality is enabled, disabled, or not available on + * this device. + * + * @param libRefNum Reference number of the GoLCD library. + * @return Returns the current GoLCD status. + * + * @see GoLcdStatusType + * + * @sample + * @code + * GoLcdStatusType gCurrentGoLcdStatus = goLcdNotAvailable; + * gCurrentGoLcdStatus = GoLcdGetStatus(gGoLcdLibRefNum); + * switch(gCurrentGoLcdStatus) { + * case goLcdDisabled: + * // GoLcd is disabled + * break; + * case goLcdEnabled: + * // GoLcd is Enabled + * break; + * case goLcdNotAvailable: + * default: + * // Not available + * break; + * } @endcode + * + * @see GoLcdSetStatus + */ +GoLcdStatusType GoLcdGetStatus(UInt16 libRefNum) + SYS_TRAP(kGoLcdLibTrapGetStatus); + +/** + * Enables or disables the GoLCD functionality. + * + * @param libRefNum Reference number of the GoLCD library. + * @param status Sets the new GoLCD status. + * @return Returns the previous GoLCD status. + * + * @sample + * @code + * GoLcdStatusType gPreviousGoLcdStatus = goLcdNotAvailable; + * gPreviousGoLcdStatus = GoLcdSetStatus(gGoLcdLibRefNum, goLcdEnabled); @endcode + * + * @see GoLcdGetStatus + */ +GoLcdStatusType GoLcdSetStatus(UInt16 libRefNum, GoLcdStatusType status) + SYS_TRAP(kGoLcdLibTrapSetStatus); + +/** + * Returns the current state of GoLCD inking. + * + * @param libRefNum Reference number of the GoLCD library. + * @param stateP Points to the returned GoLCD ink state. + * @param colorModeP Points to the returned GoLCD color mode for ink. + * @param rgbP Valid only if colorMode is set to goLcdColorOverride. Points to an RGB color + * value corresponding to the current color used for inking. (The RGBColorType is + * defined in the header file Bitmap.h.) + * + * @sample + * @code + * GoLcdInkStateType gCurrentGoLcdInkState = goLcdInkDisabled; + * GoLcdColorModeType gCurrentGoLcdColorMode = goLcdColorDefault; + * RGBColorType gCurrentColor; + * GoLcdGetInkState(gGoLcdLibRefNum, &gCurrentGoLcdInkState, &gCurrentGoLcdColorMode, &gCurrentColor); @endcode + * + * @see GoLcdSetInkState + */ +void GoLcdGetInkState(UInt16 libRefNum, GoLcdInkStateType *stateP, GoLcdColorModeType *colorModeP, RGBColorType *rgbP) + SYS_TRAP(kGoLcdLibTrapGetInkState); + +/** + * Sets the state of GoLCD inking. + * + * @param libRefNum Reference number of the GoLCD library. + * @param state Sets the new GoLCD ink state. + * @param colorMode Sets the new GoLCD color mode for ink. + * @param rgbP Valid only if colorMode is set to goLcdColorOverride. Points to an RGB color + * value corresponding to the current color used for inking. (The RGBColorType is + * defined in the header file Bitmap.h.) + * + * @remarks Inking, also known as echoing, refers to the drawing of Graffiti 2 strokes in the + * application area of the display. + * + * @sample + * @code + * RGBColorType gNewColor = { 0, 255, 0, 0}; // Red + * GoLcdSetInkState(gGoLcdLibRefNum, goLcdInkEnabled, goLcdColorOverride, &gNewColor); @endcode + * + * @see GoLcdGetInkState + */ +void GoLcdSetInkState(UInt16 libRefNum, GoLcdInkStateType state, GoLcdColorModeType colorMode, RGBColorType *rgbP) + SYS_TRAP(kGoLcdLibTrapSetInkState); + +/** + * Returns the GSI state associated with GoLCD. + * + * @param libRefNum Reference number of the GoLCD library. + * @param stateP Points to the returned GoLCD GSI state. + * @param colorModeP Points to the returned GoLCD color mode for GSI. + * @param rgbP Valid only if colorMode is set to goLcdColorOverride. Points to an RGB color + * value corresponding to the current color used for inking. (The RGBColorType is + * defined in the header file Bitmap.h.) + * + * @remarks In normal operation, the GSI is drawn in the lower-right portion of the screen when + * the user enters the shift keystroke. The functionality of the GSI can be changed into + * an enable and disable control for GoLCD. This function returns the current state of + * the GSI. + * + * @sample + * @code + * GoLcdGsiStateType gCurrentGoLcdGsiState = goLcdGsiNormal; + * GoLcdColorModeType gCurrentGoLcdColorMode = goLcdColorDefault; + * RGBColorType gCurrentColor; + * GoLcdGetGsiState(gGoLcdLibRefNum, &gCurrentGoLcdGsiState, &gCurrentGoLcdColorMode, &gCurrentColor); @endcode + * + * @see GoLcdSetGsiState + */ +void GoLcdGetGsiState(UInt16 libRefNum, GoLcdGsiStateType *stateP, GoLcdColorModeType *colorModeP, RGBColorType *rgbP) + SYS_TRAP(kGoLcdLibTrapGetGsiState); + +/** + * Sets the GSI state associated with GoLCD. + * + * @param libRefNum Reference number of the GoLCD library. + * @param state Sets the GoLCD GSI state. + * @param colorMode Sets the GoLCD color mode for GSI. + * @param rgbP Valid only if colorMode is set to goLcdColorOverride. Points to an RGB color + * value corresponding to the current color used for inking. (The RGBColorType is + * defined in the header file Bitmap.h.) + * + * @remarks In normal operation, the GSI is drawn in the lower-right portion of the screen when + * the user enters the shift keystroke. The functionality of the GSI can be changed into + * an enable and disable control for GoLCD. This function determines whether the + * GSI is converted into a GoLCD control. This setting will apply to all GSIs in any + * active form. + * + * @sample + * @code + * RGBColorType gNewColor = { 0, 255, 0, 0}; // Red + * GoLcdSetGsiState(gGoLcdLibRefNum, goLcdGsiOverride, goLcdColorOverride, &gNewColor); @endcode + * + * @see GoLcdGetGsiState + */ +void GoLcdSetGsiState(UInt16 libRefNum, GoLcdGsiStateType state, GoLcdColorModeType colorMode, RGBColorType *rgbP) + SYS_TRAP(kGoLcdLibTrapSetGsiState); + +/** + * Returns the length, in system ticks, of the time-out value of GoLCD. + * + * @param libRefNum Reference number of the GoLCD library. + * @param mode Specifies the GoLCD mode. + * @return Returns the length (in system ticks) of the time-out value. + * + * @remarks GoLCD starts in goLcdPenTapMode. When a penDownEvent is received, a timer is + * started. If a penUpEvent is received before the timer reaches the time-out value for + * this mode, the pen events are passed on to the event handler for the application + * control. Otherwise, if the pen events exceed the time-out value and the x, y + * coordinates change significantly, GoLCD enters goLcdGraffitiMode and treats the + * pen events as a Graffiti 2 stroke. + *
The default time-out value for goLcdPenTapMode is 15 system ticks, or + * 150 milliseconds. + *
When it enters goLcdGraffitiMode, GoLCD continuously interprets all pen events + * as Graffiti 2 strokes. There is a time-out value associated with goLcdGraffitiMode, + * however. If it does not receive a new pen event within the allotted time, it returns + * to goLcdPenTapMode. + *
The default time-out value for goLcdGraffitiMode is 150 system ticks, or + * 1500 milliseconds. + * + * @sample + * @code + * UInt32 gCurrentTimeout = GoLcdGetTimeout(gGoLcdLibRefNum, goLcdPenTapMode); @endcode + * + * @see GoLcdSetTimeout + */ +UInt32 GoLcdGetTimeout(UInt16 libRefNum, GoLcdModeType mode) + SYS_TRAP(kGoLcdLibTrapGetTimeout); + +/** + * Sets the length, in system ticks, of the time-out value of GoLCD. + * + * @param libRefNum Reference number of the GoLCD library. + * @param mode Specifies the GoLCD mode. + * @param ticks The new time-out length in system ticks. + * + * @return Returns the length (in system ticks) of the previous time-out value. + * + * @remarks GoLCD starts in goLcdPenTapMode. When a penDownEvent is received, a timer is + * started. If a penUpEvent is received before the timer reaches the time-out value for + * this mode, the pen events are passed on to the event handler for the application + * control. Otherwise, if the pen events exceed the time-out value and the x, y + * coordinates change significantly, GoLCD enters goLcdGraffitiMode and treats the + * pen events as a Graffiti 2 stroke. + *
The default time-out value for goLcdPenTapMode is 15 system ticks, or + * 150 milliseconds. + *
When it enters goLcdGraffitiMode, GoLCD continuously interprets all pen events + * as Graffiti 2 strokes. There is a time-out value associated with goLcdGraffitiMode, + * however. If it does not receive a new pen event within the allotted time, it returns + * to goLcdPenTapMode. + *
The default time-out value for goLcdGraffitiMode is 150 system ticks, or + * 1500 milliseconds. + * + * @sample + * @code + * UInt32 gPreviousTimeout = GoLcdSetTimeout(gGoLcdLibRefNum, goLcdPenTapMode, 100); @endcode + * + * @see GoLcdGetTimeout + */ +UInt32 GoLcdSetTimeout(UInt16 libRefNum, GoLcdModeType mode, UInt32 ticks) + SYS_TRAP(kGoLcdLibTrapSetTimeout); + +/** + * Returns the current bounds defined for use with GoLCD. + * + * @param libRefNum Reference number of the GoLCD library. + * @param rectP Points to the returned rectangle. + * + * @remarks GoLCD ignores any succession of pen events (a series of penDownEvents followed + * by a penUpEvent) if the initial penDownEvent occurs outside its bounds. By default, + * its bounds are the entire display and users can start drawing full-screen writing + * characters anywhere except for the right-most scroll bar and the menu bar area. + * + * @sample + * @code + * RectangleType gCurrentBounds; + * GoLcdGetBounds(gGoLcdLibRefNum, &gCurrentBounds); @endcode + * + * @see GoLcdSetBounds + */ +void GoLcdGetBounds(UInt16 libRefNum, RectangleType *rectP) + SYS_TRAP(kGoLcdLibTrapGetBounds); + +/** + * Sets the bounds defined for use with GoLCD. + * + * @param libRefNum Reference number of the GoLCD library. + * @param rectP Points to the rectangle value to set. + * + * @remarks GoLCD ignores any succession of pen events (a series of penDownEvents followed + * by a penUpEvent) if the initial penDownEvent occurs outside its bounds. By default, + * its bounds are the entire display and users can start drawing full-screen writing + * characters anywhere except for the right-most scroll bar and the menu bar area. + * + * @sample + * @code + * RectangleType gNewBounds = { { 0, 0 } , { 50, 50 } }; + * GoLcdGetBounds(gGoLcdLibRefNum, &gNewBounds); @endcode + * + * @see GoLcdGetBounds + */ +void GoLcdSetBounds(UInt16 libRefNum, RectangleType *rectP) + SYS_TRAP(kGoLcdLibTrapSetBounds); + +#ifdef __cplusplus +} +#endif + +#endif //__PALMGOLCD_H__ diff --git a/inc/PalmPin.h b/inc/PalmPin.h new file mode 100644 index 0000000..4cfd009 --- /dev/null +++ b/inc/PalmPin.h @@ -0,0 +1,140 @@ +/****************************************************************************** + * + * Copyright (c) 2003 Palm, Inc. or its subsidiaries. + * All rights reserved. + * + * File: PalmPin.h + * + * Description: + * Public API for the Pen Input Manager Library + * + * History: + * Version 1.0 - Intial Revision (03/03/03) + * + *****************************************************************************/ + +#ifndef __PINLIB_h__ +#define __PINLIB_H__ + +#include +#include + +/******************************************************************** + * Library type and creator + ********************************************************************/ +#define pinLibType sysFileTLibrary +#define pinLibCreator 'pinM' +#define pinLibName "PinLib" +#define pinFtrNumVersion 0 + +/******************************************************************** + * Pinlet IDs + ********************************************************************/ +#define pinClassic "Classic" +#define pinTriCell "Tri Cell" +#define pinStdKeyboard "StdKB" +#define pinletStdKbNum "StdKBNum" +#define pinletStdKbIntl "StdKBIntl" + +/******************************************************************** + * PIN Manager Errors + ********************************************************************/ +#define pinErrorClass (appErrorClass | 0x0A00) +#define pinErrInvalidState (pinErrorClass + 1) +#define pinErrUnknownID (pinErrorClass + 2) +#define pinErrInvalidInputMode (pinErrorClass + 3) + +/******************************************************************** + * Input area states + ********************************************************************/ +#define pinInputAreaNone 0 +#define pinInputAreaShow 1 +#define pinInputAreaHide 2 +#define pinInputAreaLegacyMode 3 +#define pinInputAreaFullScreen 4 + +/******************************************************************** + * Input Modes + ********************************************************************/ +#define pinInputModeNormal 0 +#define pinInputModeShift 1 +#define pinInputModeCapsLock 2 +#define pinInputModePunctuation 3 +#define pinInputModeNumeric 4 +#define pinInputModeExtended 5 +#define pinInputModeHiragana 6 +#define pinInputModeKatakana 7 + +/******************************************************************** + * AIA event and notification + ********************************************************************/ +#define sysNotifyAiaEvent 'Aian' +#define AiaExtentChangedEvent ((eventsEnum)(firstUserEvent+2)) + +typedef struct AiaExtentChangedEventDataType +{ + RectangleType newDim; + RectangleType oldDim; +} AiaExtentChangedEventDataType; + +// Macro to simplify getting the data out of the event structure. +// Example: +// yDiff = AiaExtentChangedData(eventP)->newDim->extent.y - +// aiaExtentChangedData(eventP)->oldDim->extent.y; +#define aiaExtentChangedData(eventP) ((AiaExtentChangedEventDataType *)(&((eventP)->data.generic))) + + +/******************************************************************** + * Library Traps + ********************************************************************/ +#define kPinLibTrapOpen sysLibTrapOpen +#define kPinLibTrapClose sysLibTrapClose +#define kPinLibTrapPinGetInputAreaState (sysLibTrapCustom + 17) +#define kPinLibTrapPinSetInputAreaState (sysLibTrapCustom + 18) +#define kPinLibTrapPinResetInputState (sysLibTrapCustom + 24) +#define kPinLibTrapPinGetCurrentPinletID (sysLibTrapCustom + 20) +#define kPinLibTrapPinSwitchToPinlet (sysLibTrapCustom + 21) +#define kPinLibTrapPinGetInputMode (sysLibTrapCustom + 23) +#define kPinLibTrapPinSetInputMode (sysLibTrapCustom + 22) +#define kPinLibTrapPINShowReferenceDialog (sysLibTrapCustom + 25) + + +/******************************************************************** + * Prototypes + ********************************************************************/ +#ifdef __cplusplus +extern "C" { +#endif + +Err PinLibOpen(UInt16 refnum) + SYS_TRAP(kPinLibTrapOpen); +Err PinLibClose(UInt16 refnum) + SYS_TRAP(kPinLibTrapClose); + +UInt16 PinGetInputAreaState(UInt16 refnum) + SYS_TRAP(kPinLibTrapPinGetInputAreaState); + +Err PinSetInputAreaState(UInt16 refnum, UInt16 state) + SYS_TRAP(kPinLibTrapPinSetInputAreaState); + +void PinResetInputState(UInt16 refnum) + SYS_TRAP(kPinLibTrapPinResetInputState); + +const char* PinGetCurrentPinletID(UInt16 refnum) + SYS_TRAP(kPinLibTrapPinGetCurrentPinletID); + +Err PinSwitchToPinlet(UInt16 refnum, const char* pinletID, UInt16 initialInputMode) + SYS_TRAP(kPinLibTrapPinSwitchToPinlet); + +UInt16 PinGetInputMode(UInt16 refnum) + SYS_TRAP(kPinLibTrapPinGetInputMode); + +void PinSetInputMode(UInt16 refnum, UInt16 inputMode) + SYS_TRAP(kPinLibTrapPinSetInputMode); + +#ifdef __cplusplus +} +#endif + + +#endif //__PINLIB_h__ diff --git a/inc/PalmStatusBar.h b/inc/PalmStatusBar.h new file mode 100644 index 0000000..2f9e225 --- /dev/null +++ b/inc/PalmStatusBar.h @@ -0,0 +1,86 @@ +/****************************************************************************** + * + * Copyright (c) 2003 Palm, Inc. or its subsidiaries. + * All rights reserved. + * + * File: PalmStatusBar.h + * + * Description: + * Public API for the Status Bar Library + * + * History: + * Version 1.0 - Intial Revision (03/03/03) + * + *****************************************************************************/ + +#ifndef __STATUSBARMGRLIB_H__ +#define __STATUSBARMGRLIB_H__ + +#include +#include + +/******************************************************************** + * Library type and creator + ********************************************************************/ +#define statLibName "StatusBarMgrLib" +#define statLibCreator 'sBar' +#define statLibType sysFileTLibrary +#define statFtrNumVersion (0) + +/******************************************************************** + * Status Bar Errors + ********************************************************************/ +#define T3statErrorClass (appErrorClass | 0x0900) +#define T3statErrNoStatusBar (statErrorClass | 1) +#define T3statErrInvalidSelector (statErrorClass | 2) +#define T3statErrInputWindowOpen (statErrorClass | 3) +#define T3statErrBadParam (statErrorClass | 101) +#define T3statErrInvalidState (statErrorClass | 102) + +/******************************************************************** + * Status Bar Attributes + ********************************************************************/ +typedef enum StatAttrTypeTag +{ + T3statAttrExists = 0, // device supports the status bar + T3statAttrBarVisible, // status bar is visible + T3statAttrDimension // bounds of status bar window +} StatAttrType; + +/******************************************************************** + * Library Traps + ********************************************************************/ +#define kStatusBarMgrLibTrapOpen sysLibTrapOpen +#define kStatusBarMgrLibTrapClose sysLibTrapClose +#define kStatusBarMgrLibGetAttribute (sysLibTrapCustom) +#define kStatusBarMgrLibHide (sysLibTrapCustom + 1) +#define kStatusBarMgrLibShow (sysLibTrapCustom + 2) + + +/******************************************************************** + * Prototypes + ********************************************************************/ +#ifdef __cplusplus +extern "C" { +#endif + +Err T3StatLibOpen(UInt16 refnum) + SYS_TRAP(kStatusBarMgrLibTrapOpen); + +Err T3StatLibClose(UInt16 refnum) + SYS_TRAP(kStatusBarMgrLibTrapClose); + +Err T3StatGetAttribute(UInt16 refnum, StatAttrType selector, UInt32 *dataP) + SYS_TRAP(kStatusBarMgrLibGetAttribute); + +Err T3StatHide(UInt16 refnum) + SYS_TRAP(kStatusBarMgrLibHide); + +Err T3StatShow(UInt16 refnum) + SYS_TRAP(kStatusBarMgrLibShow); + +#ifdef __cplusplus +} +#endif + +#endif //__STATUSBARMGRLIB_H__ diff --git a/inc/SonyErrorBase.h b/inc/SonyErrorBase.h new file mode 100644 index 0000000..62a4db7 --- /dev/null +++ b/inc/SonyErrorBase.h @@ -0,0 +1,33 @@ +/****************************************************************************** + * * + * (C) Copyright 2000, Sony Corporation * + * * + *----------------------------------------------------------------------------* + * * + * * + * file name : $Workfile: SonyErrorBase.h $ + * * + * * + * The defines of error base in sony CLIE system * + * * + * * + * Started on : 00/10/30 * + * Last Modified: $Date: 2002/02/11 08:28:28 $ + * * + ******************************************************************************/ +/* this file is best viewed by setting TAB-stop as 3 */ + +#ifndef __SONYERRORBASE_H__ +#define __SONYERRORBASE_H__ + +#include + +// error code +#define sonyErrorClass (oemErrorClass) +#define sonyVFSErrorClass (sonyErrorClass | 0x100) +#define sonyHRErrorClass (sonyErrorClass | 0x200) +#define sonyMsaErrorClass (sonyErrorClass | 0x300) +#define sonyRmcErrorClass (sonyErrorClass | 0x400) + +#endif // __SONYERRORBASE_H__ + diff --git a/inc/SonyHRLib.h b/inc/SonyHRLib.h new file mode 100644 index 0000000..cc163a9 --- /dev/null +++ b/inc/SonyHRLib.h @@ -0,0 +1,439 @@ +/****************************************************************************** + * + * Copyright (c) 2000,2001 Sony Corporation. + * All rights reserved. + * + * File: SonyHighResLib.h + * + * Description: + * Include file for High Resolution API + * + * History: + * Ver 0.10 : 2000/12/17 + * + *****************************************************************************/ +/* this file is best viewed by setting TAB-stop as 3 */ + +#ifndef __SONYHIGHRESLIB_H__ +#define __SONYHIGHRESLIB_H__ + +#include +#include + +// BUILDING_APPLICATION +#if BUILDING_APP_OR_LIB_HIGH_RES + // direct link to library code + #define HR_TRAP(trapNum) +#else + // else someone else is including this public header file; use traps + #define HR_TRAP(trapNum) SYS_TRAP(trapNum) +#endif + +/******************************************************************** + * define SCREEN SIZE + ********************************************************************/ + +#define hrWidth 320 +#define hrHeight 320 +#define stdWidth 160 +#define stdHeight 160 + +/******************************************************************** + * define hrErrorClass + ********************************************************************/ + +#define hrErrNone 0 +#define hrErrorClass (sonyHRErrorClass) +#define hrErrParam (hrErrorClass | 1) +#define hrErrNotOpen (hrErrorClass | 2) +#define hrErrStillOpen (hrErrorClass | 3) +#define hrErrNoGlobals (hrErrorClass | 4) +#define hrErrMemory (hrErrorClass | 5) +#define hrErrNoFeature (hrErrorClass | 6) + +/******************************************************************** + * define High Resolution Font + ********************************************************************/ + +enum hrFontID { + hrTinyFont = 0x00, + hrTinyBoldFont, + hrSmallFont, + hrSmallSymbolFont, + hrSmallSymbol11Font, + hrSmallSymbol7Font, + hrSmallLedFont, + hrSmallBoldFont, + hrStdFont, + hrBoldFont, + hrLargeFont, + hrSymbolFont, + hrSymbol11Font, + hrSymbol7Font, + hrLedFont, + hrLargeBoldFont, + hrFntAppFontCustomBase = 0x80 +}; + +typedef enum hrFontID HRFontID; + +/******************************************************************** + * define HRTrapNumEnum + ********************************************************************/ + +#if 0 +typedef enum tagHRTrapNumEnum +{ + HRTrapGetAPIVersion = sysLibTrapCustom, + HRTrapWinClipRectangle, + HRTrapWinCopyRectangle, + HRTrapWinCreateBitmapWindow, + HRTrapWinCreateOffscreenWindow, + HRTrapWinCreateWindow, + HRTrapWinDisplayToWindowPt, + HRTrapWinDrawBitmap, + HRTrapWinDrawChar, + HRTrapWinDrawChars, + HRTrapWinDrawGrayLine, + HRTrapWinDrawGrayRectangleFrame, + HRTrapWinDrawInvertedChars, + HRTrapWinDrawLine, + HRTrapWinDrawPixel, + HRTrapWinDrawRectangle, + HRTrapWinDrawRectangleFrame, + HRTrapWinDrawTruncChars, + HRTrapWinEraseChars, + HRTrapWinEraseLine, + HRTrapWinErasePixel, + HRTrapWinEraseRectangle, + HRTrapWinEraseRectangleFrame, + HRTrapWinFillLine, + HRTrapWinFillRectangle, + HRTrapWinGetClip, + HRTrapWinGetDisplayExtent, + HRTrapWinGetFramesRectangle, + HRTrapWinGetPixel, + HRTrapWinGetWindowBounds, + HRTrapWinGetWindowExtent, + HRTrapWinGetWindowFrameRect, + HRTrapWinInvertChars, + HRTrapWinInvertLine, + HRTrapWinInvertPixel, + HRTrapWinInvertRectangle, + HRTrapWinInvertRectangleFrame, + HRTrapWinPaintBitmap, + HRTrapWinPaintChar, + HRTrapWinPaintChars, + HRTrapWinPaintLine, + HRTrapWinPaintLines, + HRTrapWinPaintPixel, + HRTrapWinPaintPixels, + HRTrapWinPaintRectangle, + HRTrapWinPaintRectangleFrame, + HRTrapWinRestoreBits, + HRTrapWinSaveBits, + HRTrapWinScreenMode, + HRTrapWinScrollRectangle, + HRTrapWinSetClip, + HRTrapWinSetWindowBounds, + HRTrapWinWindowToDisplayPt, + HRTrapBmpBitsSize, + HRTrapBmpSize, + HRTrapBmpCreate, + HRTrapFntGetFont, + HRTrapFntSetFont, + HRTrapFontSelect, + HRTrapSystem, + HRTrapWinGetPixelRGB, + HRTrapGetInfo + +} HRTrapNumEnum; +#else +#define HRTrapGetAPIVersion (sysLibTrapCustom+0) +#define HRTrapWinClipRectangle (sysLibTrapCustom+1) +#define HRTrapWinCopyRectangle (sysLibTrapCustom+2) +#define HRTrapWinCreateBitmapWindow (sysLibTrapCustom+3) +#define HRTrapWinCreateOffscreenWindow (sysLibTrapCustom+4) +#define HRTrapWinCreateWindow (sysLibTrapCustom+5) +#define HRTrapWinDisplayToWindowPt (sysLibTrapCustom+6) +#define HRTrapWinDrawBitmap (sysLibTrapCustom+7) +#define HRTrapWinDrawChar (sysLibTrapCustom+8) +#define HRTrapWinDrawChars (sysLibTrapCustom+9) +#define HRTrapWinDrawGrayLine (sysLibTrapCustom+10) +#define HRTrapWinDrawGrayRectangleFrame (sysLibTrapCustom+11) +#define HRTrapWinDrawInvertedChars (sysLibTrapCustom+12) +#define HRTrapWinDrawLine (sysLibTrapCustom+13) +#define HRTrapWinDrawPixel (sysLibTrapCustom+14) +#define HRTrapWinDrawRectangle (sysLibTrapCustom+15) +#define HRTrapWinDrawRectangleFrame (sysLibTrapCustom+16) +#define HRTrapWinDrawTruncChars (sysLibTrapCustom+17) +#define HRTrapWinEraseChars (sysLibTrapCustom+18) +#define HRTrapWinEraseLine (sysLibTrapCustom+19) +#define HRTrapWinErasePixel (sysLibTrapCustom+20) +#define HRTrapWinEraseRectangle (sysLibTrapCustom+21) +#define HRTrapWinEraseRectangleFrame (sysLibTrapCustom+22) +#define HRTrapWinFillLine (sysLibTrapCustom+23) +#define HRTrapWinFillRectangle (sysLibTrapCustom+24) +#define HRTrapWinGetClip (sysLibTrapCustom+25) +#define HRTrapWinGetDisplayExtent (sysLibTrapCustom+26) +#define HRTrapWinGetFramesRectangle (sysLibTrapCustom+27) +#define HRTrapWinGetPixel (sysLibTrapCustom+28) +#define HRTrapWinGetWindowBounds (sysLibTrapCustom+29) +#define HRTrapWinGetWindowExtent (sysLibTrapCustom+30) +#define HRTrapWinGetWindowFrameRect (sysLibTrapCustom+31) +#define HRTrapWinInvertChars (sysLibTrapCustom+32) +#define HRTrapWinInvertLine (sysLibTrapCustom+33) +#define HRTrapWinInvertPixel (sysLibTrapCustom+34) +#define HRTrapWinInvertRectangle (sysLibTrapCustom+35) +#define HRTrapWinInvertRectangleFrame (sysLibTrapCustom+36) +#define HRTrapWinPaintBitmap (sysLibTrapCustom+37) +#define HRTrapWinPaintChar (sysLibTrapCustom+38) +#define HRTrapWinPaintChars (sysLibTrapCustom+39) +#define HRTrapWinPaintLine (sysLibTrapCustom+40) +#define HRTrapWinPaintLines (sysLibTrapCustom+41) +#define HRTrapWinPaintPixel (sysLibTrapCustom+42) +#define HRTrapWinPaintPixels (sysLibTrapCustom+43) +#define HRTrapWinPaintRectangle (sysLibTrapCustom+44) +#define HRTrapWinPaintRectangleFrame (sysLibTrapCustom+45) +#define HRTrapWinRestoreBits (sysLibTrapCustom+46) +#define HRTrapWinSaveBits (sysLibTrapCustom+47) +#define HRTrapWinScreenMode (sysLibTrapCustom+48) +#define HRTrapWinScrollRectangle (sysLibTrapCustom+49) +#define HRTrapWinSetClip (sysLibTrapCustom+50) +#define HRTrapWinSetWindowBounds (sysLibTrapCustom+51) +#define HRTrapWinWindowToDisplayPt (sysLibTrapCustom+52) +#define HRTrapBmpBitsSize (sysLibTrapCustom+53) +#define HRTrapBmpSize (sysLibTrapCustom+54) +#define HRTrapBmpCreate (sysLibTrapCustom+55) +#define HRTrapFntGetFont (sysLibTrapCustom+56) +#define HRTrapFntSetFont (sysLibTrapCustom+57) +#define HRTrapFontSelect (sysLibTrapCustom+58) +#define HRTrapSystem (sysLibTrapCustom+59) +#define HRTrapWinGetPixelRGB (sysLibTrapCustom+60) +#define HRTrapGetInfo (sysLibTrapCustom+61) +#endif + +/******************************************************************** + * API Prototypes + ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +/******************************************************************** + * Standard library open, close, sleep and wake functions + ********************************************************************/ + +extern Err HROpen(UInt16 refNum) HR_TRAP(sysLibTrapOpen); + +extern Err HRClose(UInt16 refNum) HR_TRAP(sysLibTrapClose); + +extern Err HRSleep(UInt16 refNum) HR_TRAP(sysLibTrapSleep); + +extern Err HRWake(UInt16 refNum) HR_TRAP(sysLibTrapWake); + + +/******************************************************************** + * Custom library API functions + ********************************************************************/ + +extern Err HRGetAPIVersion(UInt16 refNum, UInt16 *versionP) + HR_TRAP(HRTrapGetAPIVersion); + +extern void HRWinClipRectangle(UInt16 refNum, RectangleType *rP) + HR_TRAP(HRTrapWinClipRectangle); + +extern void HRWinCopyRectangle(UInt16 refNum, WinHandle srcWin, WinHandle dstWin, RectangleType *srcRect, Coord destX, Coord destY, WinDrawOperation mode) + HR_TRAP(HRTrapWinCopyRectangle); + +extern WinHandle HRWinCreateBitmapWindow(UInt16 refNum, BitmapType *bitmapP, UInt16 *error) + HR_TRAP(HRTrapWinCreateBitmapWindow); + +extern WinHandle HRWinCreateOffscreenWindow(UInt16 refNum, Coord width, Coord height, WindowFormatType format, UInt16 *error) + HR_TRAP(HRTrapWinCreateOffscreenWindow); + +extern WinHandle HRWinCreateWindow(UInt16 refNum, RectangleType *bounds, FrameType frame, Boolean modal, Boolean focusable, UInt16 *error) + HR_TRAP(HRTrapWinCreateWindow); + +extern void HRWinDisplayToWindowPt(UInt16 refNum, Coord *extentX, Coord *extentY) + HR_TRAP(HRTrapWinDisplayToWindowPt); + +extern void HRWinDrawBitmap(UInt16 refNum, BitmapPtr bitmapP, Coord x, Coord Y) + HR_TRAP(HRTrapWinDrawBitmap); + +extern void HRWinDrawChar(UInt16 refNum, WChar theChar, Coord x, Coord Y) + HR_TRAP(HRTrapWinDrawChar); + +extern void HRWinDrawChars(UInt16 refNum, const Char *chars, Int16 len, Coord x, Coord y) + HR_TRAP(HRTrapWinDrawChars); + +extern void HRWinDrawGrayLine(UInt16 refNum, Coord x1, Coord y1, Coord x2, Coord y2) + HR_TRAP(HRTrapWinDrawGrayLine); + +extern void HRWinDrawGrayRectangleFrame(UInt16 refNum, FrameType frame, RectangleType *rP) + HR_TRAP(HRTrapWinDrawGrayRectangleFrame); + +extern void HRWinDrawInvertedChars(UInt16 refNum, const Char *chars, Int16 len, Coord x, Coord y) + HR_TRAP(HRTrapWinDrawInvertedChars); + +extern void HRWinDrawLine(UInt16 refNum, Coord x1, Coord y1, Coord x2, Coord y2) + HR_TRAP(HRTrapWinDrawLine); + +extern void HRWinDrawPixel(UInt16 refNum, Coord x, Coord y) + HR_TRAP(HRTrapWinDrawPixel); + +extern void HRWinDrawRectangle(UInt16 refNum, RectangleType *rP, UInt16 cornerDiam) + HR_TRAP(HRTrapWinDrawRectangle); + +extern void HRWinDrawRectangleFrame(UInt16 refNum, FrameType frame, RectangleType *rP) + HR_TRAP(HRTrapWinDrawRectangleFrame); + +extern void HRWinDrawTruncChars(UInt16 refNum, const Char *chars, Int16 len, Coord x, Coord y, Coord maxWidth) + HR_TRAP(HRTrapWinDrawTruncChars); + +extern void HRWinEraseChars(UInt16 refNum, const Char *chars, Int16 len, Coord x, Coord y) + HR_TRAP(HRTrapWinEraseChars); + +extern void HRWinEraseLine(UInt16 refNum, Coord x1, Coord y1, Coord x2, Coord y2) + HR_TRAP(HRTrapWinEraseLine); + +extern void HRWinErasePixel(UInt16 refNum, Coord x, Coord y) + HR_TRAP(HRTrapWinErasePixel); + +extern void HRWinEraseRectangle(UInt16 refNum, RectangleType *rP, UInt16 cornerDiam) + HR_TRAP(HRTrapWinEraseRectangle); + +extern void HRWinEraseRectangleFrame(UInt16 refNum, FrameType frame, RectangleType *rP) + HR_TRAP(HRTrapWinEraseRectangleFrame); + +extern void HRWinFillLine(UInt16 refNum, Coord x1, Coord y1, Coord x2, Coord y2) + HR_TRAP(HRTrapWinFillLine); + +extern void HRWinFillRectangle(UInt16 refNum, RectangleType *rP, UInt16 cornerDiam) + HR_TRAP(HRTrapWinFillRectangle); + +extern void HRWinGetClip(UInt16 refNum, RectangleType *rP) + HR_TRAP(HRTrapWinGetClip); + +extern void HRWinGetDisplayExtent(UInt16 refNum, Coord *extentX, Coord *extentY) + HR_TRAP(HRTrapWinGetDisplayExtent); + +extern void HRWinGetFramesRectangle(UInt16 refNum, FrameType frame, RectangleType *rP, RectangleType *obscuredRectP) + HR_TRAP(HRTrapWinGetFramesRectangle); + +extern IndexedColorType HRWinGetPixel(UInt16 refNum, Coord x, Coord y) + HR_TRAP(HRTrapWinGetPixel); + +extern void HRWinGetWindowBounds(UInt16 refNum, RectangleType *rP) + HR_TRAP(HRTrapWinGetWindowBounds); + +extern void HRWinGetWindowExtent(UInt16 refNum, Coord *extentX, Coord *extentY) + HR_TRAP(HRTrapWinGetWindowExtent); + +extern void HRWinGetWindowFrameRect(UInt16 refNum, WinHandle winHandle, RectangleType *rP) + HR_TRAP(HRTrapWinGetWindowFrameRect); + +extern void HRWinInvertChars(UInt16 refNum, const Char *chars, Int16 len, Coord x, Coord y) + HR_TRAP(HRTrapWinInvertChars); + +extern void HRWinInvertLine(UInt16 refNum, Coord x1, Coord y1, Coord x2, Coord y2) + HR_TRAP(HRTrapWinInvertLine); + +extern void HRWinInvertPixel(UInt16 refNum, Coord x, Coord y) + HR_TRAP(HRTrapWinInvertPixel); + +extern void HRWinInvertRectangle(UInt16 refNum, RectangleType *rP, UInt16 cornerDiam) + HR_TRAP(HRTrapWinInvertRectangle); + +extern void HRWinInvertRectangleFrame(UInt16 refNum, FrameType frame, RectangleType *rP) + HR_TRAP(HRTrapWinInvertRectangleFrame); + +extern void HRWinPaintBitmap(UInt16 refNum, BitmapType *bitmapP, Coord x, Coord y) + HR_TRAP(HRTrapWinPaintBitmap); + +extern void HRWinPaintChar(UInt16 refNum, WChar theChar, Coord x, Coord y) + HR_TRAP(HRTrapWinPaintChar); + +extern void HRWinPaintChars(UInt16 refNum, const Char *chars, Int16 len, Coord x, Coord y) + HR_TRAP(HRTrapWinPaintChars); + +extern void HRWinPaintLine(UInt16 refNum, Coord x1, Coord y1, Coord x2, Coord y2) + HR_TRAP(HRTrapWinPaintLine); + +extern void HRWinPaintLines(UInt16 refNum, UInt16 numLines, WinLineType lines[]) + HR_TRAP(HRTrapWinPaintLines); + +extern void HRWinPaintPixel(UInt16 refNum, Coord x, Coord y) + HR_TRAP(HRTrapWinPaintPixel); + +extern void HRWinPaintPixels(UInt16 refNum, UInt16 numPoints, PointType pts[]) + HR_TRAP(HRTrapWinPaintPixels); + +extern void HRWinPaintRectangle(UInt16 refNum, RectangleType *rP, UInt16 cornerDiam) + HR_TRAP(HRTrapWinPaintRectangle); + +extern void HRWinPaintRectangleFrame(UInt16 refNum, FrameType frame, RectangleType *rP) + HR_TRAP(HRTrapWinPaintRectangleFrame); + +extern void HRWinRestoreBits(UInt16 refNum, WinHandle winHandle, Coord destX, Coord destY) + HR_TRAP(HRTrapWinRestoreBits); + +extern WinHandle HRWinSaveBits(UInt16 refNum, RectangleType *sourceP, UInt16 *error) + HR_TRAP(HRTrapWinSaveBits); + +extern Err HRWinScreenMode(UInt16 refNum, WinScreenModeOperation operation, UInt32 *widthP, UInt32 *heightP, UInt32 *depthP, Boolean *enableColorP) + HR_TRAP(HRTrapWinScreenMode); + +extern void HRWinScrollRectangle(UInt16 refNum, RectangleType *rP, WinDirectionType direction, Coord distance, RectangleType *vacatedP) + HR_TRAP(HRTrapWinScrollRectangle); + +extern void HRWinSetClip(UInt16 refNum, RectangleType *rP) + HR_TRAP(HRTrapWinSetClip); + +extern void HRWinSetWindowBounds(UInt16 refNum, WinHandle winHandle, RectangleType *rP) + HR_TRAP(HRTrapWinSetWindowBounds); + +extern void HRWinWindowToDisplayPt(UInt16 refNum, Coord *extentX, Coord *extentY) + HR_TRAP(HRTrapWinWindowToDisplayPt); + +extern Err HRWinGetPixelRGB(UInt16 refNum, Coord x, Coord y, RGBColorType *rgbP) + HR_TRAP(HRTrapWinGetPixelRGB); + + + +extern UInt32 HRBmpBitsSize(UInt16 refNum, BitmapType *bitmapP) + HR_TRAP(HRTrapBmpBitsSize); + +extern UInt32 HRBmpSize(UInt16 refNum, BitmapType *bitmapP) + HR_TRAP(HRTrapBmpSize); + +extern BitmapType *HRBmpCreate(UInt16 refNum, Coord width, Coord height, UInt8 depth, ColorTableType *colortableP, UInt16 *error) + HR_TRAP( HRTrapBmpCreate); + + + +extern HRFontID HRFntGetFont(UInt16 refNum) + HR_TRAP(HRTrapFntGetFont); + +extern HRFontID HRFntSetFont(UInt16 refNum, HRFontID font) + HR_TRAP(HRTrapFntSetFont); + +extern HRFontID HRFontSelect(UInt16 refNum, HRFontID font) + HR_TRAP(HRTrapFontSelect); + + +/* System Use Only */ +extern Err HRSystem(UInt16 refNum, UInt16 operation, UInt32 *param1, UInt32 *param2, UInt32 *param3) + HR_TRAP(HRTrapSystem); + +extern Err HRGetInfo(UInt16 refNum, UInt16 *ptr1, UInt16 *ptr2) + HR_TRAP(HRTrapGetInfo); + +/** Number of API : 66 **/ + +#ifdef __cplusplus +} +#endif + +#endif /* __SONYHIGHRESLINB_H__ */ \ No newline at end of file diff --git a/inc/SonySilkLib.h b/inc/SonySilkLib.h new file mode 100644 index 0000000..64e5428 --- /dev/null +++ b/inc/SonySilkLib.h @@ -0,0 +1,305 @@ +/****************************************************************************** + * * + * (C) Copyright 2002, Sony Corporation * + * * + *----------------------------------------------------------------------------* + * * + * * + * file name : $Workfile: SonySilkLib.h $ + * * + * * + * Virtual Silk Lib API * + * * + * * + * Started on : 00/11/01 * + * Last Modified: $Date: 2004/03/10 23:13:50 $ + * * + ******************************************************************************/ +/* this file is best viewed by setting TAB-stop as 3 */ + + +#ifndef __SILK_LIB_H__ +#define __SILK_LIB_H__ + +#include + + +#if CPU_TYPE != CPU_68K + #define SILK_LIB_TRAP(trapNum) // direct link to library code +#else + #define SILK_LIB_TRAP(trapNum) SYS_TRAP(trapNum) +#endif + + +/* ------------------------------------- */ +/* Constant def */ +/* ------------------------------------- */ +/* SilkMgr Errors */ +#define silkLibErrParam (sonySilkErrorClass | 1) // invalid parameter +#define silkLibErrNotOpen (sonySilkErrorClass | 2) // library is not open +#define silkLibErrStillOpen (sonySilkErrorClass | 3) // returned from SilkLibClose() if + // the library is still open by others +#define silkLibErrNotAvailable (sonySilkErrorClass | 4) // memory error occurred +#define silkLibErrResizeDisabled (sonySilkErrorClass | 5) // cannot resize + +#define vskErrParam silkLibErrParam +#define vskErrNotOpen silkLibErrNotOpen +#define vskErrStillOpen silkLibErrStillOpen +#define vskErrNotAvailable silkLibErrNotAvailable +#define vskErrResizeDisabled silkLibErrResizeDisabled +#define vskErrSlkwNotFound (sonySilkErrorClass | 6) +#define vskErrSlkwOpenFailed (sonySilkErrorClass | 7) +#define vskErrSlkwCloseFailed (sonySilkErrorClass | 8) +#define vskErrSlkwStartFailed (sonySilkErrorClass | 9) +#define vskErrSlkwStopFailed (sonySilkErrorClass | 10) +#define vskErrSlkwLoadFailed (sonySilkErrorClass | 11) +#define vskErrFuncNotAvailable (sonySilkErrorClass | 12) +//#define vskErr (sonySilkErrorClass | ) +// up to (sonySilkErrorClass | 15) + + +/* Window Extent */ +#define hrSilkHeight (130) +#define hrSilkWidth (320) +#define stdSilkHeight (65) +#define stdSilkWidth (160) + +#define hrStatusHeight (30) +#define hrStatusWidth (320) +#define stdStatusHeight (15) +#define stdStatusWidth (160) + +#define silkLibAPIVertion (0x00000002) +#define vskAPIVertion silkLibAPIVertion +#define vskVersionNum1 (0x00010000) +#define vskVersionNum2 (0x00020000) + +/* stateType */ +#define vskStateResize (0) + #define silkResizeNormal (0) + #define silkResizeToStatus (1) + #define silkResizeMax (2) + #define vskResizeMax silkResizeNormal + #define vskResizeMin silkResizeToStatus + #define vskResizeNone silkResizeMax +#define vskStateEnable (1) + #define vskResizeDisable (0) + #define vskResizeVertically (1<<0) + #define vskResizeHorizontally (1<<1) +#define vskStateAnimation (2) + #define vskAnimationNone (0) + #define vskAnimationFast (1) + #define vskAnimationSlow (2) +#define vskStateAutoResize (3) + // on (1) + // off (0) + + +/* slkwType */ +#define vskSlkwTypeSilk (0) +#define vskSlkwTypeStatus (1) + +#if CPU_TYPE == CPU_68K +/* ------------------------------------- */ +/* Type def */ +/* ------------------------------------- */ + +#if 0 +typedef enum { + silkLibTrapResizeDispWin = sysLibTrapCustom, + silkLibTrapEnableResize, + silkLibTrapDisableResize, + silkLibTrapGetAPIVersion, + silkLibLastTrap +} SilkLibTrapNumberEnum; +#endif + +#if 0 +typedef enum { + VskTrapGetAPIVersion = silkLibTrapGetAPIVersion, + VskTrapSetCurrentSlkw, + VskTrapGetCurrentSlkw, + VskTrapSetState, + VskTrapGetState, + VskTrapEnablePalmSilk, + VskTrapGetPalmSilkEnabled, + VskTrapTimerWrite, + VskTrapDoCommand, + VskTrapSetDrawWindow, + VskTrapRestoreDrawWindow, + VskLastTrap +} VskTrapNumberEnum; +#endif + +#endif + +#define silkLibTrapResizeDispWin (sysLibTrapCustom + 0) +#define silkLibTrapEnableResize (sysLibTrapCustom + 1) +#define silkLibTrapDisableResize (sysLibTrapCustom + 2) +#define silkLibTrapGetAPIVersion (sysLibTrapCustom + 3) +#define silkLibLastTrap (sysLibTrapCustom + 4) + +#define VskTrapGetAPIVersion (silkLibTrapGetAPIVersion + 0) +#define VskTrapSetCurrentSlkw (silkLibTrapGetAPIVersion + 1) +#define VskTrapGetCurrentSlkw (silkLibTrapGetAPIVersion + 2) +#define VskTrapSetState (silkLibTrapGetAPIVersion + 3) +#define VskTrapGetState (silkLibTrapGetAPIVersion + 4) +#define VskTrapEnablePalmSilk (silkLibTrapGetAPIVersion + 5) +#define VskTrapGetPalmSilkEnabled (silkLibTrapGetAPIVersion + 6) +#define VskTrapTimerWrite (silkLibTrapGetAPIVersion + 7) +#define VskTrapDoCommand (silkLibTrapGetAPIVersion + 8) +#define VskTrapSetDrawWindow (silkLibTrapGetAPIVersion + 9) +#define VskTrapRestoreDrawWindow (silkLibTrapGetAPIVersion + 10) +#define VskLastTrap (silkLibTrapGetAPIVersion + 11) + + + +/* ------------------------------------- */ +/* API Prototypes */ +/* ------------------------------------- */ +#ifdef __cplusplus +extern "C" { +#endif + +#if CPU_TYPE != CPU_68K + +// Nice prototypes for Sony in-house maybe, but not for WI - +// we need the library versions +#if 0 +// new APIs +extern Err VskOpen(void); +extern Err VskClose(void); +extern Err VskSleep(void); +extern Err VskWake(void); +extern UInt32 VskGetAPIVersion(void); +extern Err VskSetCurrentSlkw(UInt16 slkwType, UInt32 creator); +extern Err VskGetCurrentSlkw(UInt16 slkwType, UInt32 *creatorP); +extern Err VskSetState(UInt16 stateType, UInt16 state); +extern Err VskGetState(UInt16 stateType, UInt16 *stateP); +extern Err VskEnablePalmSilk(Boolean enable); +extern Err VskGetPalmSilkEnabled(Boolean *graffiti, Boolean *penButton); +extern Err VskTimerWrite(UInt16 slkwType, UInt32 interval/*msec*/); +extern Err VskDoCommand(UInt32 creator, UInt16 command, + UInt32 data1, UInt32 data2); +extern Err VskSetDrawWindow(UInt16 slkwType); +extern Err VskRestoreDrawWindow(UInt16 slkwType); + +#else + +extern Err VskOpen(UInt16 refNum); +extern Err VskClose(UInt16 refNum); +extern Err VskSleep(UInt16 refNum); +extern Err VskWake(UInt16 refNum); +extern UInt32 VskGetAPIVersion(UInt16 refNum); +extern Err VskSetCurrentSlkw(UInt16 refNum, UInt16 slkwType, UInt32 creator); +extern Err VskGetCurrentSlkw(UInt16 refNum, UInt16 slkwType, UInt32 *creatorP); +extern Err VskSetState(UInt16 refNum, UInt16 stateType, UInt16 state); +extern Err VskGetState(UInt16 refNum, UInt16 stateType, UInt16 *stateP); +extern Err VskEnablePalmSilk(UInt16 refNum, Boolean enable); +extern Err VskGetPalmSilkEnabled(UInt16 refNum, Boolean *graffiti, Boolean *penButton); +extern Err VskTimerWrite(UInt16 refNum, UInt16 slkwType, UInt32 interval/*msec*/); +extern Err VskDoCommand(UInt16 refNum, UInt32 creator, UInt16 command, + UInt32 data1, UInt32 data2); +extern Err VskSetDrawWindow(UInt16 refNum, UInt16 slkwType); +extern Err VskRestoreDrawWindow(UInt16 refNum, UInt16 slkwType); +#endif + + +extern Err SilkLibOpen(UInt16 refNum); +extern Err SilkLibClose(UInt16 refNum); +extern Err SilkLibSleep(UInt16 refNum); +extern Err SilkLibWake(UInt16 refNum); +extern Err SilkLibResizeDispWin(UInt16 refNum, UInt8 win); +extern Err SilkLibEnableResize(UInt16 refNum); +extern Err SilkLibDisableResize(UInt16 refNum); +extern UInt32 SilkLibGetAPIVersion(UInt16 refNum); + +#else + +extern Err SilkLibOpen(UInt16 refNum) + SILK_LIB_TRAP(sysLibTrapOpen); + +extern Err SilkLibClose(UInt16 refNum) + SILK_LIB_TRAP(sysLibTrapClose); + +extern Err SilkLibSleep(UInt16 refNum) + SILK_LIB_TRAP(sysLibTrapSleep); + +extern Err SilkLibWake(UInt16 refNum) + SILK_LIB_TRAP(sysLibTrapWake); + +extern Err SilkLibResizeDispWin(UInt16 refNum, UInt8 win) + SILK_LIB_TRAP(silkLibTrapResizeDispWin); + +extern Err SilkLibEnableResize(UInt16 refNum) + SILK_LIB_TRAP(silkLibTrapEnableResize); + +extern Err SilkLibDisableResize(UInt16 refNum) + SILK_LIB_TRAP(silkLibTrapDisableResize); + +extern UInt32 SilkLibGetAPIVersion(UInt16 refNum) + SILK_LIB_TRAP(silkLibTrapGetAPIVersion); + +// new APIs +extern Err VskOpen(UInt16 refNum) + SILK_LIB_TRAP(sysLibTrapOpen); + +extern Err VskClose(UInt16 refNum) + SILK_LIB_TRAP(sysLibTrapClose); + +extern Err VskSleep(UInt16 refNum) + SILK_LIB_TRAP(sysLibTrapSleep); + +extern Err VskWake(UInt16 refNum) + SILK_LIB_TRAP(sysLibTrapWake); + +// this will be removed +//extern Err VskResize(UInt16 refNum, UInt8 reqSize); + +// this will be removed +//extern Err VskEnableResize(UInt16 refNum); + +// this will be removed +//extern Err VskDisableResize(UInt16 refNum); + +extern UInt32 VskGetAPIVersion(UInt16 refNum) + SILK_LIB_TRAP(VskTrapGetAPIVersion); + +extern Err VskSetCurrentSlkw(UInt16 refNum, UInt16 slkwType, UInt32 creator) + SILK_LIB_TRAP(VskTrapSetCurrentSlkw); + +extern Err VskGetCurrentSlkw(UInt16 refNum, UInt16 slkwType, UInt32 *creatorP) + SILK_LIB_TRAP(VskTrapGetCurrentSlkw); + +extern Err VskSetState(UInt16 refNum, UInt16 stateType, UInt16 state) + SILK_LIB_TRAP(VskTrapSetState); + +extern Err VskGetState(UInt16 refNum, UInt16 stateType, UInt16 *stateP) + SILK_LIB_TRAP(VskTrapGetState); + +extern Err VskEnablePalmSilk(UInt16 refNum, Boolean enable) + SILK_LIB_TRAP(VskTrapEnablePalmSilk); + +extern Err VskGetPalmSilkEnabled(UInt16 refNum, Boolean *graffiti, Boolean *penButton) + SILK_LIB_TRAP(VskTrapGetPalmSilkEnabled); + +extern Err VskTimerWrite(UInt16 refNum, UInt16 slkwType, UInt32 interval/*msec*/) + SILK_LIB_TRAP(VskTrapTimerWrite); + +extern Err VskDoCommand(UInt16 refNum, UInt32 creator, UInt16 command, + UInt32 data1, UInt32 data2) + SILK_LIB_TRAP(VskTrapDoCommand); + +extern Err VskSetDrawWindow(UInt16 refNum, UInt16 slkwType) + SILK_LIB_TRAP(VskTrapSetDrawWindow); + +extern Err VskRestoreDrawWindow(UInt16 refNum, UInt16 slkwType) + SILK_LIB_TRAP(VskTrapRestoreDrawWindow); +#endif //CPU_TYPE != CPU_68K + +#ifdef __cplusplus +} +#endif + + +#endif // __SLK_LIB_H__ diff --git a/inc/SonySystemFtr.h b/inc/SonySystemFtr.h new file mode 100644 index 0000000..08dfc56 --- /dev/null +++ b/inc/SonySystemFtr.h @@ -0,0 +1,194 @@ +/****************************************************************************** + * * + * (C) Copyright 2000-2002, Sony Corporation * + * * + *----------------------------------------------------------------------------* + * * + * * + * file name : $Workfile: SonySystemFtr.h $ + * * + * * + * Feature related definitions for Sony System * + * * + * * + * Started on : 00/11/28 * + * Last Modified: $Date: 2003/08/28 00:13:29 $ + * * + ******************************************************************************/ +/* this file is best viewed by setting TAB-stop as 3 */ +/* this file could be used in both ARM-native and 68K applications */ + +#ifndef __SONYSYSTEMFTR_H__ +#define __SONYSYSTEMFTR_H__ + +/****************************************************************************** + * Includes * + ******************************************************************************/ +#include +#include +#include + + +/****************************************************************************** + * Utility Definitions * + ******************************************************************************/ +#ifndef CPU_ARM +#define CPU_ARM (3) +#endif +#if CPU_TYPE == CPU_ARM /* || CPU_ENDIAN == CPU_ENDIAN_LITTLE */ + // Do we have to care about x86? +#define _EndianSwap16(x) \ + ((((x) >> 8) & 0xFF) | \ + (((x) & 0xFF) << 8)) + +#define _EndianSwap32(x) \ + ((((x) >> 24) & 0x000000FFL) | \ + (((x) >> 8) & 0x0000FF00L) | \ + (((x) & 0x0000FF00L) << 8) | \ + (((x) & 0x000000FFL) << 24)) +#else +// CPU_68K || CPU_ENDIAN_BIG +#define _EndianSwap16(x) (x) /* nop */ +#define _EndianSwap32(x) (x) /* nop */ +#endif + + +/****************************************************************************** + * Features * + ******************************************************************************/ + +/*** Sony Ftr Creator ***/ +#define sonySysFtrCreator sonySysFileCSony + +/*** Ftr Number ***/ /* UInt16 */ + /* for Global System information */ +#define sonySysFtrNumSysInfoP (1) /* ptr to SysInfo */ +#define sonySysFtrNumStringInfoP (2) /* ptr to StringInfo */ + + /* for JogAssist */ +#define sonySysFtrNumJogAstMaskP (3) /* ptr to JogAstMask */ +#define sonySysFtrNumJogAstMOCardNoP (4) /* ptr to JogAstMaskOwnerCardNo */ +#define sonySysFtrNumJogAstMODbIDP (5) /* ptr to JogAstMaskOwnerDbID */ + /* for PalmOS5 or later */ +#define sonySysFtrNumJogAstControlP (6) /* ptr to JogAssistControl flag */ + + /* for Version */ +#define sonySysFtrNumSystemVersion (0x0100) /* SonySys */ +#define sonySysFtrNumJogAstVersion (0x0101) /* JogAssistExtn */ +#define sonySysFtrNumVskVersion (0x0102) /* VirtualSilkLibExtn */ +#define sonySysFtrNumRmcVersion (0x0103) /* RemoteContollerLibExtn */ +#define sonySysFtrNumIrcVersion (0x0104) /* IrCommanderLibExtn */ +#define sonySysFtrNumSndVersion (0x0105) /* SndLibExtn */ + + /* should we define ver for lib as well? */ +#define sonySysFtrNumHRLibVersion (0x0110) /* HiResoLib */ +#define sonySysFtrNumScsiLibVersion (0x0111) /* SCSILib */ +#define sonySysFtrNumJpegUtilLibVersion (0x0112) /* JpegUitlLib */ +#define sonySysFtrNumJpegLibVersion (0x0113) /* JpegLib */ +#define sonySysFtrNumMMLibVersion (0x0114) /* MMLib */ +#define sonySysFtrNumMMUtilLibVersion (0x0115) /* MMUtilLib */ +#define sonySysFtrNumMMUrlLibVersion (0x0116) /* MMUrlLib */ + + +/****************************************************************************** + * Structures for Featrures * + ******************************************************************************/ +/* Those definitions are depricated on PalmOS_5, exist only for compatibilities */ + +/*** SysInfoP ***/ +typedef struct S_SonySysFtrSysInfo { + UInt16 revision; + UInt16 rsv16_00; + UInt32 extn; /* loaded extension */ + UInt32 libr; /* loaded libr */ + UInt32 rsv32_00; + UInt32 rsv32_01; + + void *rsvP; + UInt32 status; /* current system status */ + UInt32 msStatus; /* current MemoryStick status */ + UInt32 rsv32_10; + + UInt16 msSlotNum; /* number of slot of MemoryStick */ + UInt16 jogType; + UInt16 rmcType; +} SonySysFtrSysInfoType; +typedef SonySysFtrSysInfoType *SonySysFtrSysInfoP; + + /* revision field */ +#define sonySysFtrSysInfoRevision (1) + + /* extn field */ +#define sonySysFtrSysInfoExtnJog (0x00000001L) /* vchrJogEvent usable */ +#define sonySysFtrSysInfoExtnRmc (0x00000002L) /* vchrRmcEvent usable */ +#define sonySysFtrSysInfoExtnHold (0x00000004L) /* Hold switch usable */ +#define sonySysFtrSysInfoExtnJogAst (0x00000008L) /* JogAssist usable */ +#define sonySysFtrSysInfoExtnSilk (0x00000010L) /* Software silk usable */ +#define sonySysFtrSysInfoExtnCapBtn (0x00000020L) /* vchrCapEvent usable */ +#define sonySysFtrSysInfoExtnKB (0x00000040L) /* Hardware Keyboard usable */ + + /* libr field */ +#define sonySysFtrSysInfoLibrHR (0x00000001L) /* HR-Lib usable */ +#define sonySysFtrSysInfoLibrMsa (0x00000002L) /* Msa-Lib usable */ +#define sonySysFtrSysInfoLibrRmc (0x00000004L) /* Rmc-Lib usable */ +#define sonySysFtrSysInfoLibrMsScsi (0x00000008L) /* MsScsi-Lib usable */ +#define sonySysFtrSysInfoLibrIrc (0x00000010L) /* Irc-Lib usable */ +#define sonySysFtrSysInfoLibrFm (0x00000020L) /* Sound-Lib usable */ +#define sonySysFtrSysInfoLibrCap (0x00000040L) /* Capture-Lib usable */ +#define sonySysFtrSysInfoLibrJpeg (0x00000080L) /* Jpeg-Lib usable */ +#define sonySysFtrSysInfoLibrSilk (0x00000100L) /* Silk-Lib usable */ + + /* status field */ /* 1: on(inserted/enabled), 0: off(removed/disabled) */ +#define sonySysFtrSysInfoStatusHP (0x00000001) /* HeadPhone */ +#define sonySysFtrSysInfoStatusHoldOn (0x00000002) /* Hold switch */ +#define sonySysFtrSysInfoStatusLcdRotate (0x00000004) /* Lcd Rotate status */ +#define sonySysFtrSysInfoStatusLcdFlip (0x00000008) /* Lcd Flip status*/ +#define sonySysFtrSysInfoStatusCamRotate (0x00000010) /* Cam Rotate status */ + + /* msStatus field */ /* 1: inserted, 0: removed */ +#define sonySysFtrSysInfoMsStatus1MS (0x00000001) /* MS in Slot 1*/ +#define sonySysFtrSysInfoMsStatus1StrgMS (0x00000002) /* StorageMS in Slot 1*/ +#define sonySysFtrSysInfoMsStatus1MGMS (0x00000004) /* MG-MS in Slot 1*/ +#define sonySysFtrSysInfoMsStatus1WP (0x00000008) /* WriteProtected */ +#define sonySysFtrSysInfoMsStatus1ReadOnly (0x00000010) /* Read Only */ +#define sonySysFtrSysInfoMsStatus1IO (0x00000020) /* IO Expansion Module */ +#define sonySysFtrSysInfoMsStatus1Mask (0x000000FF) /* Mask for Slot 1 */ + + /* jogType field */ +#define sonySysFtrSysInfoJogTypeNone (0) /* No Jog available */ +#define sonySysFtrSysInfoJogType1 (1) /* 2D Jog (PEG-S300/500) */ +#define sonySysFtrSysInfoJogType2 (2) /* 2D Jog with Back */ + + /* rmcType field */ +#define sonySysFtrSysInfoRmcTypeNone (0) /* No Rmc available */ +#define sonySysFtrSysInfoRmcType1 (1) /* 6 buttons w/o display */ +#define sonySysFtrSysInfoRmcType2 (2) /* Audio Adapter */ + + +/*** StringInfoP ***/ +typedef struct S_SonySysFtrStringInfo { + /* All chars are described with ASCII */ /* must be null-terminated */ + /* offset: ex. */ + Char maker[16]; /* 0/0x0000: ex. "Sony Corp." */ + Char model[16]; /* 16/0x0010: ex. "PEG-S300" */ + Char ship[16]; /* 32/0x0020: ex. "Japan" */ + Char os[32]; /* 48/0x0030: ex. "Palm OS 3.5" */ + Char cpu[32]; /* 80/0x0050: ex. "Motorola DragonBall-VZ(33MHz)" */ + Char comment[128]; /* 112/0x0070: ex. "Personal Entertainment..." */ + UInt16 code; /* 240/0x00F0: code for comment2 */ + Char comment2[254]; /* 242/0x00F2: ex. "ƒp[ƒ\ƒiƒ‹EƒGƒ“ƒ^ƒeƒCƒƒ“ƒg..." */ + /* 496/0x01F0: */ +} SonySysFtrStringInfoType; +typedef SonySysFtrStringInfoType *SonySysFtrStringInfoP; + /* CAUTION: those strings is not guaranteed to be correct by Sony. Null + strings are possible. */ + /* code for 'code' field' */ +#define sonySysFtrStingInfoCodeASCII (0x0001) +#define sonySysFtrStingInfoCode8859 (0x0003) +#define sonySysFtrStingInfoCodeMSJIS (0x0081) + + +/*** JogAstMaskP ***/ +typedef void *JogAstMaskP; + /* related specs are defined in JogAst.h */ +#endif // __SONYSYSTEMFTR_H__ diff --git a/inc/SonySystemResources.h b/inc/SonySystemResources.h new file mode 100644 index 0000000..1af0392 --- /dev/null +++ b/inc/SonySystemResources.h @@ -0,0 +1,95 @@ +/****************************************************************************** + * * + * (C) Copyright 2000, Sony Corporation * + * * + *----------------------------------------------------------------------------* + * * + * * + * file name : $Workfile: SonySystemResources.h $ + * * + * * + * CreatorID/Type of DB, ID/Type of Resoueces for Sony System * + * * + * * + * Started on : 00/11/28 * + * Last Modified: $Date: 2002/10/17 00:56:25 $ + * * + ******************************************************************************/ +/* this file is best viewed by setting TAB-stop as 3 */ + +#ifndef __SONYSYSTEMRESOURCES_H__ +#define __SONYSYSTEMRESOURCES_H__ + +/****************************************************************************** + * Includes * + ******************************************************************************/ +#include +#include + +/****************************************************************************** + * CreatorID and Type for Databases * + ******************************************************************************/ + +/*** Sony oveall ***/ +#define sonySysFileCSony 'SoNy' /* Sony overall */ +#define sonySysVfsCstmApiCreator sonySysFileCSony /* VFS Custom Control */ + +#define sonySysFileCSystem 'SsYs' /* Sony overall System */ + +/*** Application ***/ + + +/*** Extension ***/ + + +/*** Library ***/ +/* HR-Lib */ +#define sonySysFileCHRLib 'SlHr' /* High Resolution */ +#define sonySysFileTHRLib sysFileTLibrary /* 'libr' */ +#define sonySysLibNameHR "Sony HR Library" + +/* Msa-Lib */ +#define sonySysFileCMsaLib 'SlMa' /* MS Audio */ +#define sonySysFileTMsaLib sysFileTLibrary /* 'libr' */ +#define sonySysLibNameMsa "Sony Msa Library" + +/* Rmc-Lib */ +#define sonySysFileCRmcLib 'SlRm' /* Remote Control */ +#define sonySysFileTRmcLib sysFileTLibrary /* 'libr' */ +#define sonySysLibNameRmc "Sony Rmc Library" + +/* SonySoundLib */ +#define sonySysFileTPcmLib 'pcmR' + +#define sonySysFileCSoundLib 'SlSd' /* Sony Sound Lib */ +#define sonySysFileTSoundLib sysFileTLibrary /* 'libr' */ +#define sonySysLibNameSound "Sony Sound Library" + +/* Silk-Lib */ +#define sonySysFileCSilkLib 'SlSi' +#define sonySysFileTSilkLib sysFileTLibrary /* 'libr' */ +#define sonySysLibNameSilk "Sony Silk Library" + +/* JpegUtil-Lib */ +#define sonySysFileCJpegUtilLib 'SlJU' /* Jpeg Util Lib */ +#define sonySysFileTJpegUtilLib sysFileTLibrary /* 'libr' */ +#define sonySysLibNameJpegUtil "Sony Jpeg Util Library" + +/* Capture-Lib */ +#define sonySysFileCCaptureLib 'SlCp' /* Capture Lib */ +#define sonySysFileTCaptureLib sysFileTLibrary /* 'libr' */ +#define sonySysLibNameCapture "Sony Capture Library" + +/*** System Application ***/ +/* JogPanel Preference */ +#define sonySysFileCJogPanel 'SnJp' /* Jog Panel */ +#define sonySysFileTJogPanel sysFileTPanel /* 'panl' */ + + +/****************************************************************************** + * Misc CreatorID * + ******************************************************************************/ + + +#endif // __SONYSYSTEMRESOURCES_H__ + diff --git a/inc/SonyTypes.h b/inc/SonyTypes.h new file mode 100644 index 0000000..5d7a77e --- /dev/null +++ b/inc/SonyTypes.h @@ -0,0 +1,32 @@ +/****************************************************************************** + * * + * (C) Copyright 2000-2002, Sony Corporation * + * * + *----------------------------------------------------------------------------* + * * + * * + * file name : $Workfile: SonyTypes.h $ + * * + * * + * General defitinions for Sony System & Libraries * + * * + * * + * Started on : 02/05/25 * + * Last Modified: $Date: 2003/08/28 00:13:30 $ + * * + ******************************************************************************/ +/* this file is best viewed by setting TAB-stop as 3 */ + +#ifndef __SONYTYPES_H__ +#define __SONYTYPES_H__ + +/****************************************************************************** + * Environment configuration + ******************************************************************************/ +#define _BUILD_FOR_PALMOS_5_ + /* this definition must not appear in SDK for PalmOS 4 or older (for 3.5) */ + /* Defining this here and not in SonyCLIE.h is reasonable, since SonyCLIE.h + is not included in all libraries. */ + +#endif // __SONYTYPES_H__ + diff --git a/inc/TwDefs.h b/inc/TwDefs.h new file mode 100644 index 0000000..4ab9119 --- /dev/null +++ b/inc/TwDefs.h @@ -0,0 +1,136 @@ +/* Copyright (c) 2002-2003 Tapwave, Inc. All rights reserved. */ + +#ifndef __TWDEFS_H__ +#define __TWDEFS_H__ + +/* + * Define common preprocessor variables for existing 68K code. + */ +#ifndef EMULATION_NONE +#define EMULATION_NONE 0 /* native environment */ +#define EMULATION_WINDOWS 1 /* emulate on Windows */ +#define EMULATION_DOS 2 /* emulate on DOS */ +#define EMULATION_MAC 3 /* emulate on Macintosh */ +#define EMULATION_UNIX 4 /* emulate on Linux */ +#endif + +#ifndef EMULATION_LEVEL +#define EMULATION_LEVEL EMULATION_NONE +#endif + +#ifndef CPU_x86 +#define CPU_x86 1 /* x86 type */ +#endif + +#ifndef CPU_ARM +#define CPU_ARM 3 /* ARM type */ +#endif + +#ifdef __arm +#undef CPU_TYPE +#define CPU_TYPE CPU_ARM +#endif + +#ifdef _MSC_VER +#undef CPU_TYPE +#define CPU_TYPE CPU_x86 +#endif + +/* + * Special processor variable for cross compilation 16-bit/32-bit app. + */ +#ifndef __palm__ +#if CPU_TYPE == CPU_68K +#define __palm__ 0x0400 +#else +#define __palm__ 0x0500 +#endif +#endif + +/* + * Special compiler option for armlet callback from PalmOS. + */ +#if defined(__PALMOS_ARMLET__) + #if defined(__MWERKS__) && defined(__arm) + #define ARMLET_CALLBACK __declspec(armlet_callback) + #elif defined(_MSC_VER) && defined(_WIN32) + #define ARMLET_CALLBACK __declspec(dllexport) + #endif +#endif /* __PALMOS_ARMLET__ */ + +#ifndef ARMLET_CALLBACK +#define ARMLET_CALLBACK /* nothing */ +#endif + +/* + * For unkonwn reason, this resource type is missing from ARM headers. + */ +#ifndef iconType +#define iconType 'tAIB' +#endif + +#if !defined(inline) && !defined(__cplusplus) + +#if defined(__arm) + #define inline __inline +#elif defined(_MSC_VER) + #define inline __inline +#elif defined(__MWERKS__) + #define inline inline +#else + #define inline static +#endif + +#endif /* !defined(inline) && !defined(__cplusplus) */ + +#ifndef UNUSED +#define UNUSED(v) if (v) {} +#endif + +/* + * Additional data types for writing portable code. + */ +#if defined(_MSC_VER) +typedef signed __int64 Int64; +typedef unsigned __int64 UInt64; +#else +typedef signed long long Int64; +typedef unsigned long long UInt64; +#endif + +/* + * This data type is necessary for casting between data pointers and + * function pointers, and it is also more generic than void*. + */ +typedef signed long IntPtr; +typedef unsigned long UIntPtr; + +#ifdef __MWERKS__ +typedef signed long ssize_t; +typedef unsigned long size_t; +#endif + +/* + * This is the generic creator for Tapwave products, assigned by PalmSource + */ +#define twCreatorID 'Tpwv' +#define twFtrCreator twCreatorID + +/* + * This is the PalmOS feature number for TapWave API version. + */ +#define twFtrAPIVersion 0x0000 + +/* + * This is the PalmOS feature number for TapWave ARMlet Glue. + */ +#define twFtrAPIGlue 0x0001 + +/* + * This is the compile-time version for TapWave API. + */ +#ifndef TAPWAVE_API_VERSION +#define TAPWAVE_API_VERSION 0x0100 +#endif + +#endif /* __TWDEFS_H__ */ diff --git a/inc/TwError.h b/inc/TwError.h new file mode 100644 index 0000000..e4b16f4 --- /dev/null +++ b/inc/TwError.h @@ -0,0 +1,17 @@ +/* -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * Copyright (c) 2003 Tapwave, Inc. (unpublished) + * + * All rights reserved. This is private and not for public distribution. + */ +#ifndef __TWERROR_H__ +#define __TWERROR_H__ + +#include + +// Define base values for error codes unique to Tapwave api's + +#define twGfxErrorBase (oemErrorClass + 0x100) +#define twHighScoreErrorBase (oemErrorClass + 0x200) + +#endif /* __TWERROR_H__ */ diff --git a/inc/TwGfx.h b/inc/TwGfx.h new file mode 100644 index 0000000..87f544e --- /dev/null +++ b/inc/TwGfx.h @@ -0,0 +1,1034 @@ +/* -*- mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * Copyright (c) 2003 Tapwave, Inc. All rights reserved. + * + * All rights reserved. This is private and not for public distribution. + */ +#ifndef __TWGFX_H__ +#define __TWGFX_H__ + +#include "TwError.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Constants + */ + +/* + * TwGfx specific error codes + */ +#define twGfxErrorLibraryOpen (0 + twGfxErrorBase) +#define twGfxErrorNullPointer (1 + twGfxErrorBase) +#define twGfxErrorBadObjectVersion (2 + twGfxErrorBase) +#define twGfxErrorSurfaceAllocFailed (3 + twGfxErrorBase) +#define twGfxErrorLibraryClosed (4 + twGfxErrorBase) + +#define twGfxErrorInvalidAlignment (9 + twGfxErrorBase) +#define twGfxErrorInvalidHandle (10 + twGfxErrorBase) +#define twGfxErrorInvalidPixelFormat (11 + twGfxErrorBase) +#define twGfxErrorInvalidLocation (12 + twGfxErrorBase) +#define twGfxErrorInvalidSize (13 + twGfxErrorBase) +#define twGfxErrorInvalidRotation (14 + twGfxErrorBase) +#define twGfxErrorInvalidMirror (15 + twGfxErrorBase) +#define twGfxErrorInvalidCount (16 + twGfxErrorBase) +#define twGfxErrorInvalidSurface (17 + twGfxErrorBase) +#define twGfxErrorInvalidCoord (18 + twGfxErrorBase) +#define twGfxErrorInvalidFlags (19 + twGfxErrorBase) + +#define twGfxErrorSurfaceNotLocked (20 + twGfxErrorBase) +#define twGfxErrorOperationInProgress (21 + twGfxErrorBase) + +/* Memory location for surfaces */ +#define twGfxLocationAcceleratorMemory 1 + +/* Pixel formats. Note that most api's support a subset of the formats. */ +#define twGfxPixelFormatMonochrome 0 /* 1 bit per pixel (colormap) */ +#define twGfxPixelFormat1bpp 0 /* 1 bit per pixel (colormap) */ +#define twGfxPixelFormatRGB565 1 /* little endian */ +#define twGfxPixelFormatRGB565_LE 1 /* little endian */ +#define twGfxPixelFormatRGB565_BE 2 /* big endian */ +#define twGfxPixelFormat2bpp 3 /* 2 bits per pixel (colormap) */ +#define twGfxPixelFormat4bpp 4 /* 4 bits per pixel (colormap) */ +#define twGfxPixelFormat8bpp 5 /* 8 bits per pixel (colormap) */ + +/* Rotation for TwGfxTransformBlt */ +#define twGfxRotateNone 0 +#define twGfxRotateCW90 1 /* clockwise 90 degrees */ +#define twGfxRotateCW180 2 /* clockwise 180 degrees */ +#define twGfxRotateCW270 3 /* clockwise 270 degrees */ +#define twGfxRotateCCW90 twGfxRotateCW270 /* counter-clockwise 90 degrees */ +#define twGfxRotateCCW180 twGfxRotateCW180 /* counter-clockwise 180 degrees */ +#define twGfxRotateCCW270 twGfxRotateCW90 /* counter-clockwise 270 degrees */ + +/* Mirror for TwGfxTransformBlt */ +#define twGfxMirrorNone 0 +#define twGfxMirrorHorizontal 1 +#define twGfxMirrorVertical 2 +#define twGfxMirrorBoth 3 + +/***********************************************************************/ + +/* + * Macro to construct a frame buffer compatible rgb565 color given + * three 8 bit components. This is a manually optimized version that + * is consistent with the other color macros here. + * + * Note that the 68k version of this macro generates data that is + * byte-swapped into little-endian format. This means that data passed + * to TwGfxDrawBitmap and TwGfxWriteSurface will be properly arranged + * if you use this macro. + */ +#define TwGfxMakeDisplayRGB_BigEndian(_r,_g,_b) \ + ( (((_g) & 0xFC) << 11) | (((_b) & 0xF8) << 5) | ((_r) & 0xF8) | (((_g) & 0xFF) >> 5) ) + +#define TwGfxMakeDisplayRGB_LittleEndian(_r,_g,_b) \ + ( (((_r) & 0xF8) << 8) | (((_g) & 0xFC) << 3) | (((_b) & 0xF8) >> 3) ) + +#if CPU_TYPE == CPU_68K +#define TwGfxMakeDisplayRGB(_r,_g,_b) TwGfxMakeDisplayRGB_BigEndian(_r,_g,_b) +#else +#define TwGfxMakeDisplayRGB(_r,_g,_b) TwGfxMakeDisplayRGB_LittleEndian(_r,_g,_b) +#endif + +/* + * This macro converts a RGBColorType structure to a frame buffer + * compatible display value. + */ +#define TwGfxRGBToDisplayRGB(_rgb) TwGfxMakeDisplayRGB((_rgb).r, (_rgb).g, (_rgb).b) + +/* + * These macros take an 8 bit color component and adjust them to be + * sized to match the rgb565 display framebuffer + */ +#define TwGfxRComponentToDisplayComponent(_r) (((_r) & 0xF8) >> 3) +#define TwGfxGComponentToDisplayComponent(_g) (((_g) & 0xFC) >> 2) +#define TwGfxBComponentToDisplayComponent(_b) (((_b) & 0xF8) >> 3) + +/* + * Format information for the rgb565 display buffer + */ +#define twGfxRShift 11 +#define twGfxRMask 0xF800 +#define twGfxGShift 5 +#define twGfxGMask 0x07E0 +#define twGfxBShift 0 +#define twGfxBMask 0x001F + +/* + * This macro converts from packed component RGB to packed display RGB + */ +#define TwGfxPackedRGBToDisplayRGB(_rgb) \ + ( (TwGfxRComponentToDisplayComponent((_rgb) >> 16) << twGfxRShift) | \ + (TwGfxGComponentToDisplayComponent((_rgb) >> 8) << twGfxGShift) | \ + (TwGfxBComponentToDisplayComponent((_rgb)) << twGfxBShift) ) + +/* + * This macro converts a RGBColorType structure to a + * TwGfxPackedRGBType value. + */ +#define TwGfxRGBToPackedRGB(_rgb) \ + ((TwGfxPackedRGBType) ( (((UInt32)(_rgb).r) << 16) | \ + ((UInt32)((_rgb).g) << 8) | \ + ((UInt32)(_rgb).b) ) ) + +/* + * This macro converts rgb components to a TwGfxPackedRGBType + */ +#define TwGfxComponentsToPackedRGB(_r,_g,_b) \ + ((TwGfxPackedRGBType) ( ((((UInt32)(_r)) & 0xFF) << 16) | \ + ((((UInt32)(_g)) & 0xFF) << 8) | \ + (((UInt32)(_b)) & 0xFF) ) ) + +/* + * This macro helps fill in a TwGfxPointType + */ +#define TwGfxMakePoint(_point, _x, _y) \ + ((_point).x = (_x), (_point).y = (_y)) + +/* + * This macro helps fill in a TwGfxRectType + */ +#define TwGfxMakeRect(_rect, _x, _y, _w, _h) \ + ((_rect).x = (_x), (_rect).y = (_y), (_rect).w = (_w), (_rect).h = (_h)) + +/***********************************************************************/ + +/* + * Opaque structures + */ +typedef struct TwGfxTypeTag TwGfxType; +typedef struct TwGfxSurfaceTypeTag TwGfxSurfaceType; + +/* + * Structures + */ + + +/* + * Color information. This is the "abstract" RGB color value. This is + * not the same as the display pixel format. This type is used for the + * API entry points. Use the TwGfxComponentsToPackedRGB to construct + * instances of this type. + */ +typedef UInt32 TwGfxPackedRGBType; + +/* + * TwGfx library state information structure + */ +typedef struct TwGfxInfoTypeTag { + Int32 size; /* caller MUST set this to sizeof(TwGfxInfoType) */ + + Int32 displayWidth, displayHeight; /* current dimensions of display */ + Int32 displayRowBytes; /* byte width of entire row */ + Int32 displayPixelFormat; /* format of display */ + + Int32 freeAcceleratorMemory; /* free accelerator memory */ + Int32 totalAcceleratorMemory; /* total accelerator memory */ +} TwGfxInfoType; + +/* + * Surface information structure + */ +typedef struct TwGfxSurfaceInfoTypeTag { + Int32 size; /* caller MUST set this to sizeof(TwGfxSurfaceInfoType) */ + + Int32 width, height; /* dimensions */ + Int32 rowBytes; /* byte width of entire row */ + + Int32 location; /* which memory the surface is located */ + Int32 pixelFormat; /* format of the surface */ +} TwGfxSurfaceInfoType; + +/* + * Bitmap structure + */ +typedef struct TwGfxBitmapTypeTag { + Int32 size; /* caller MUST set this to sizeof(TwGfxBitmapType) */ + + Int32 width, height; /* size of bitmap */ + Int32 rowBytes; /* bytes per row */ + Int32 pixelFormat; /* format of data */ + void* data; /* actual data */ + + UInt16* palette; /* In native display format: twGfxPixelFormatRGB565_LE */ +} TwGfxBitmapType; + +typedef struct TwGfxPointTypeTag { + Int32 x, y; +} TwGfxPointType; + +typedef struct TwGfxRectTypeTag { + Int32 x, y, w, h; +} TwGfxRectType; + +typedef struct TwGfxSpanTypeTag { + Int32 x, y, w; +} TwGfxSpanType; + +/***********************************************************************/ + + +/* + * Methods + */ + +/* + * Open access to the graphics library. This must be called at least + * once. aResult is filled in with an opaque handle to the + * library. aInfoResult is filled in (if not NULL) with information + * describing the current state of the display. + * + * Note that the display state will be affected by the + * TwSetOrientationState call as well as the presence/absence/size of + * the pen input area and the status bar. + * + * Errors: + * twGfxErrorLibraryOpen + * twGfxErrorBadObjectVersion + * + * See also: + * TwSetOrientationState + * PINSetInputAreaState + * StatHide + * StatShow + */ +Err TwGfxOpen(TwGfxType** aResult, + TwGfxInfoType* aInfoResult) + TAL_TRAP(trapTwGfxOpen); + +/* + * Release an instance of the TwGfx library. This call should be + * called to free all memory resources associated with the instance. + * This call will also call TwFreeSurface for all allocated surfaces + * that have not yet been freed that were allocated using this + * instance of the library. + * + * Errors: + * twGfxErrorInvalidHandle + */ +Err TwGfxClose(TwGfxType* aGfx) + TAL_TRAP(trapTwGfxClose); + +/* + * Query the current state of the TwGfx library. This can be used, for + * example, to determine how much free memory is available as well as + * determine the display state. + * + * Errors: + * twGfxErrorInvalidHandle + * twGfxErrorNullPointer + * twGfxErrorBadObjectVersion + */ +Err TwGfxGetInfo(TwGfxType* aGfx, + TwGfxInfoType* aResult) + TAL_TRAP(trapTwGfxGetInfo); + +/* + * Query the current usage of a given memory location and return how + * much of the total memory is allocated. For example, to determine + * how much memory is available on the graphics accelerator set + * aLocation to twGfxLocationAcceleratorMemory. + * + * Errors: + * twGfxErrorInvalidHandle + * twGfxErrorNullPointer + */ +Err TwGfxGetMemoryUsage(TwGfxType* aGfx, + Int32 aLocation, + Int32* aUsedResult) + TAL_TRAP(trapTwGfxGetMemoryUsage); + +/* + * Return the surface used by the display + * + * Errors: + * twGfxErrorInvalidHandle + * twGfxErrorNullPointer + */ +Err TwGfxGetDisplaySurface(TwGfxType* aGfx, + TwGfxSurfaceType** aResult) + TAL_TRAP(trapTwGfxGetDisplaySurface); + +/* + * Return the surface used by PalmOS. This will be a surface that + * represents the subset of the display used by PalmOS. + * + * Errors: + * twGfxErrorInvalidHandle + * twGfxErrorNullPointer + */ +Err TwGfxGetPalmDisplaySurface(TwGfxType* aGfx, + TwGfxSurfaceType** aResult) + TAL_TRAP(trapTwGfxGetPalmDisplaySurface); + +/* + * See if the display is currently in the vertical blanking + * period. *aInVBlankResult will be set to true if it is, false + * otherwise. + * + * Errors: + * twGfxErrorInvalidHandle + * twGfxErrorNullPointer + */ +Err TwGfxInVBlank(TwGfxType* aGfx, + Boolean* aInVBlankResult) + TAL_TRAP(trapTwGfxInVBlank); + +/* + * Wait until the display is is in the vertical blanking period. + * + * Errors: + * twGfxErrorInvalidHandle + */ +Err TwGfxWaitForVBlank(TwGfxType* aGfx) + TAL_TRAP(trapTwGfxWaitForVBlank); + +/******************************************/ + +/* + * Attempt to allocate a surface using the parameters defined in + * aDescription. + * + * The aDescription.size field should be set to sizeof(TwSurfaceInfo). + * + * aDescription.width and aDescription.height should be to the desired dimensions. + * + * aDescription.pixelFormat should be set to the desired pixel + * format. The following pixel formats are supported: + * + * twGfxPixelFormatRGB565_LE + * + * aDescription.location should be set to the choice of memory to allocate from. + * + * All other fields of aDescription are ignored. Upon success + * aDescription will be filled in with exact information describing + * the surface. + * + * Notes: + * + * Only surfaces that are allocated in the same format as the display + * will work. There are no pixel format conversions done by the blt + * operations. + * + * Errors: + * twGfxErrorInvalidHandle + * twGfxErrorOutOfMemory + * twGfxErrorInvalidPixelFormat + * twGfxErrorInvalidLocation + * twGfxErrorInvalidSize + * twGfxErrorBadObjectVersion + * twGfxErrorSurfaceAllocFailed + */ +Err TwGfxAllocSurface(TwGfxType* aGfx, + TwGfxSurfaceType** aResult, + TwGfxSurfaceInfoType* aDescription) + TAL_TRAP(trapTwGfxAllocSurface); + +/* + * Release the surface memory. If an operation is pending for the + * surface, this call will wait until the surface is idle before + * releasing it. + * + * Errors: + * twGfxErrorInvalidHandle + * twGfxErrorInvalidSurface + */ +Err TwGfxFreeSurface(TwGfxSurfaceType* aSurface) + TAL_TRAP(trapTwGfxFreeSurface); + +/* + * Set the clipping rectangle used when rendering with the given + * surface. If you pass in a NULL pointer for aClipRect then clipping + * is disabled for the given surface. + * + * The clipping rectangle for a surface applies to the following + * drawing operations: + * + * TwGfxDrawPoints + * TwGfxDrawColorPoints + * TwGfxDrawLines + * TwGfxDrawLineSegments + * TwGfxDrawRect + * TwGfxFillRect + * TwGfxDrawSpans + * + * The clipping rectangle for a surface does NOT apply to the following + * drawing operations: + * + * TwGfxWriteSurface + * TwGfxWriteSurfaceRegion + * + * Errors: + * twGfxErrorInvalidHandle + */ +Err TwGfxSetClip(TwGfxSurfaceType* aSurface, + const TwGfxRectType* aClipRect) + TAL_TRAP(trapTwGfxSetClip); + +/* + * Get the current clipping rectangle for the surface. When clipping + * is disabled for the surface the fields in aClipRectResult will be + * zero. + * + * Errors: + * twGfxErrorInvalidHandle + * twGfxErrorNullPointer + */ +Err TwGfxGetClip(TwGfxSurfaceType* aSurface, + TwGfxRectType* aClipRectResult) + TAL_TRAP(trapTwGfxGetClip); + +/* + * Query the surface information. + * + * Errors: + * twGfxErrorInvalidHandle + * twGfxErrorNullPointer + * twGfxErrorBadObjectVersion + */ +Err TwGfxGetSurfaceInfo(TwGfxSurfaceType* aSurface, + TwGfxSurfaceInfoType* aResult) + TAL_TRAP(trapTwGfxGetSurfaceInfo); + +/* + * Lock the surface down, returning a pointer to the surface bitmap + * data. The caller can then manipulate the data directly. Direct + * manipulation of the surface data on the internal or external memory + * will be slow compared to using DMA, but doesn't suffer any of the + * latency issues that DMA has. + * + * The lock call can be nested; there should be a matching unlock for + * every lock, however. The API only guarantees that the modifications + * to the surface are valid when the final unlock is peformed. + * + * Errors: + * twGfxErrorInvalidHandle + * twGfxErrorNullPointer + */ +Err TwGfxLockSurface(TwGfxSurfaceType* aSurface, void** aAddressResult) + TAL_TRAP(trapTwGfxLockSurface); + +/* + * Release the lock on the surface. When the lock counter goes to zero + * the surface will be guaranteed to be updated to either the internal + * or external memory if aUpdateFlags is true. + * + * Errors: + * twGfxErrorInvalidHandle + * twGfxErrorSurfaceNotLocked + */ +Err TwGfxUnlockSurface(TwGfxSurfaceType* aSurface, + Boolean aUpdate) + TAL_TRAP(trapTwGfxUnlockSurface); + +/* + * Make a copy of a surface. The surface bitmap data is copied to + * aDestPixels which must be aligned on a 16 bit boundary. It is the + * callers responsibility to provide enough valid memory for the + * copy. If aAsync is true then the copy is made asynchronously. You + * can invoke TwGfxIsSurfaceReady to see when the operation has + * completed. It is an error to attempt a second copy operation on a + * surface while one is pending. + * + * Errors: + * twGfxErrorInvalidHandle + * twGfxErrorNullPointer + * twGfxErrorOperationInProgress + * twGfxErrorInvalidAlignment + */ +Err TwGfxReadSurface(TwGfxSurfaceType* aSurface, + void* aDestPixels, + Boolean aAsync) + TAL_TRAP(trapTwGfxReadSurface); + +/* + * Make a copy of a subset of a surface. The surface bitmap data is + * copied to aDestPixels which must be aligned on a 16 bit + * boundary. It is the callers responsibility to provide enough valid + * memory for the copy. If aAsync is true then the copy is made + * asynchronously. You can invoke TwGfxIsSurfaceReady to see when the + * operation has completed. It is an error to attempt a second copy + * operation on a surface while one is pending. + * + * Errors: + * twGfxErrorInvalidHandle + * twGfxErrorNullPointer + * twGfxErrorOperationInProgress + * twGfxErrorInvalidAlignment + */ +Err TwGfxReadSurfaceRegion(TwGfxSurfaceType* aSurface, + const TwGfxRectType* aBounds, + void* aDestPixels, + UInt32 aDestRowBytes, + Boolean aAsync) + TAL_TRAP(trapTwGfxReadSurfaceRegion); + +/* + * Copy memory to a surface. The memory is copied to the surface + * bitmap. + * + * Source data is arranged linearly (left to right, top to bottom) + * with the rowbytes being aDestRect->w * sizeof(pixel). + * + * If aAsync is true then the copy is made asynchronously. You can + * invoke TwGfxIsSurfaceReady to see when the operation has + * completed. It is an error to attempt a second copy operation on a + * surface while one is pending. + * + * Errors: + * twGfxErrorInvalidHandle + * twGfxErrorNullPointer + * twGfxErrorOperationInProgress + */ +Err TwGfxWriteSurface(TwGfxSurfaceType* aSurface, + const void* aSourcePixels, + Boolean aAsync) + TAL_TRAP(trapTwGfxWriteSurface); + +/* + * Copy memory to a subset of a surface. The memory is copied to the + * surface bitmap at aDestPoint. + * + * Source data is arranged linearly (left to right, top to bottom) + * with the rowbytes being specified by aSourceRowBytes. There will be + * a performance impact if aSourceRowBytes is not the same as + * aDestRect->w * sizeof(pixel). + * + * If aAsync is true then the copy is made asynchronously. You can + * invoke TwGfxIsSurfaceReady to see when the operation has + * completed. It is an error to attempt a second copy operation on a + * surface while one is pending. + * + * Errors: + * twGfxErrorInvalidHandle + * twGfxErrorNullPointer + * twGfxErrorOperationInProgress + */ +Err TwGfxWriteSurfaceRegion(TwGfxSurfaceType* aSurface, + const TwGfxRectType* aBounds, + const void* aSourcePixels, + UInt32 aSourceRowBytes, + Boolean aAsync) + TAL_TRAP(trapTwGfxWriteSurfaceRegion); + +/* + * See if a surface is ready for an asynchronous operation. If an + * operation is in progress with the surface then + * twGfxErrorOperationInProgress is returned. If no operation is in + * progress and no other error occurs then zero is returned. + * + * Surfaces are busy when: + * + * An async TwGfxWriteSurface/TwGfxWriteSurfaceRegion is in progress. + * An async TwGfxReadSurface/TwGfxReadSurfaceRegion is in progress. + * An async blt is in progress (TwGfxAsyncBlt). + * + * Errors: + * twGfxErrorInvalidHandle + * twGfxErrorOperationInProgress + */ +Err TwGfxIsSurfaceReady(TwGfxSurfaceType* aSurface) + TAL_TRAP(trapTwGfxIsSurfaceReady); + +/******************************************/ + +/* + * Basic bitblt routine. + * + * The source surface will be copied to the destination surface at the + * destination point using the source rectangle. + * + * If the coordinate values address pixels outside the source surface + * a twGfxErrorInvalidCoord error will be returned. It is ok to access + * pixels outside the destination surface: clipping will ensure that + * nothing improper happens. + * + * Errors: + * twGfxErrorInvalidHandle + * twGfxErrorNullPointer + * twGfxErrorInvalidCoord + * twGfxErrorOperationInProgress + */ +Err TwGfxBitBlt(TwGfxSurfaceType* aDestSurface, + const TwGfxPointType* aDestPoint, + TwGfxSurfaceType* aSourceSurface, + const TwGfxRectType* aSourceRect) + TAL_TRAP(trapTwGfxBitBlt); + +/* + * Asynchronous bitblt routine. Same idea as TwGfxBitBlt except that + * the transfer is queued and occurs during the next vertical blanking + * period that the graphics accelerator is idle. If the graphics + * accelerator is kept constantly busy then when the bitblt occurs is + * undefined. + * + * If the coordinate values address pixels outside the source surface + * a twGfxErrorInvalidCoord error will be returned. It is ok to access + * pixels outside the destination surface: clipping will ensure that + * nothing improper happens. + * + * It is safe to queue the same surface for an async blt multiple + * times before the next vblank. Once the vblank occurs, the async blt + * is done and the surface is no longer busy. + * + * Errors: + * twGfxErrorInvalidHandle + * twGfxErrorNullPointer + * twGfxErrorInvalidCoord + * twGfxErrorOperationInProgress + */ +Err TwGfxAsyncBlt(TwGfxSurfaceType* aDestSurface, + const TwGfxPointType* aDestPoint, + TwGfxSurfaceType* aSourceSurface, + const TwGfxRectType* aSourceRect) + TAL_TRAP(trapTwGfxAsyncBlt); + +/* + * Transparent bitblt routine. + * + * The source surface will be copied to the destination surface at the + * destination point from the source surface using the source + * rectangle. + * + * Pixels in the source surface whose value matches aTransparentColor + * will not be copied to the destination surface. + * + * If the coordinate values address pixels outside the source surface + * a twGfxErrorInvalidCoord error will be returned. It is ok to access + * pixels outside the destination surface: clipping will ensure that + * nothing improper happens. + * + * Errors: + * twGfxErrorInvalidHandle + * twGfxErrorNullPointer + * twGfxErrorInvalidCoord + * twGfxErrorOperationInProgress + */ +Err TwGfxTransparentBlt(TwGfxSurfaceType* aDestSurface, + const TwGfxPointType* aDestPoint, + TwGfxSurfaceType* aSourceSurface, + const TwGfxRectType* aSourceRect, + TwGfxPackedRGBType aTransparentColor) + TAL_TRAP(trapTwGfxTransparentBlt); + +/* + * Masking bitblt routine. + * + * The source surface will be copied to the destination surface at the + * destination point from the source surface using the source + * rectangle. The source pixels will be masked using the mask bitmap; + * one bits in the mask will allow the source surface pixel to draw + * through to the destination surface while zero bits in the mask will + * have the source surface pixel ignored. + * + * The only acceptable pixel format for the mask bitmap is + * twGfxPixelFormatMonochrome. + * + * If the coordinate values address pixels outside the source surface + * a twGfxErrorInvalidCoord error will be returned. It is ok to access + * pixels outside the destination surface: clipping will ensure that + * nothing improper happens. + * + * The width and height of the source mask must equal the width and + * height of the aSourceRect otherwise a twGfxErrorInvalidSize error + * will be returned. + * + * Errors: + * twGfxErrorInvalidHandle + * twGfxErrorNullPointer + * twGfxErrorInvalidCoord + * twGfxErrorInvalidSize + * twGfxErrorInvalidPixelFormat + * twGfxErrorOperationInProgress + */ +Err TwGfxMaskBlt(TwGfxSurfaceType* aDestSurface, + const TwGfxPointType* aDestPoint, + TwGfxSurfaceType* aSourceSurface, + const TwGfxRectType* aSourceRect, + const TwGfxBitmapType* aMask) + TAL_TRAP(trapTwGfxMaskBlt); + +/* + * Blending bitblt routine. + * + * The source surface will be copied to the destination surface at the + * destination point from the source surface using the source + * rectangle. The source pixels will be blended with the destination + * pixels using the aSourceAlpha value. + * + * If the coordinate values address pixels outside the source surface + * a twGfxErrorInvalidCoord error will be returned. It is ok to access + * pixels outside the destination surface: clipping will ensure that + * nothing improper happens. + * + * Errors: + * twGfxErrorInvalidHandle + * twGfxErrorNullPointer + * twGfxErrorInvalidCoord + * twGfxErrorOperationInProgress + */ +Err TwGfxBlendBlt(TwGfxSurfaceType* aDestSurface, + const TwGfxPointType* aDestPoint, + TwGfxSurfaceType* aSourceSurface, + const TwGfxRectType* aSourceRect, + TwGfxPackedRGBType aSourceAlpha) + TAL_TRAP(trapTwGfxBlendBlt); + +/* + * Masking blending bitblt routine. + * + * The source surface will be copied to the destination surface at the + * destination point from the source surface using the source + * rectangle. The source pixels will be blended with the destination + * pixels using the provided alpha mask bitmap. + * + * Alpha values are provided using an index bitmap (for example, 4 + * bits per pixel). A zero value in the index data means no source + * color is used, while the largest index value indicates all of the + * color is used. At this time the following index pixel formats are + * supported: + * + * twGfxPixelFormat4bpp + * + * If the coordinate values address pixels outside the source surface + * a twGfxErrorInvalidCoord error will be returned. It is ok to access + * pixels outside the destination surface: clipping will ensure that + * nothing improper happens. + * + * The width and height of the alpha mask must equal the width and + * height of the aSourceRect otherwise a twGfxErrorInvalidSize error + * will be returned. + * + * Errors: + * twGfxErrorInvalidHandle + * twGfxErrorNullPointer + * twGfxErrorInvalidCoord + * twGfxErrorInvalidSize + * twGfxErrorInvalidPixelFormat + * twGfxErrorOperationInProgress + */ +Err TwGfxMaskBlendBlt(TwGfxSurfaceType* aDestSurface, + const TwGfxPointType* aDestPoint, + TwGfxSurfaceType* aSourceSurface, + const TwGfxRectType* aSourceRect, + const TwGfxBitmapType* aAlphaMask) + TAL_TRAP(trapTwGfxMaskBlendBlt); + +/* + * Stretching bitblt routine. + * + * The source surface will be copied to the destination surface at the + * destination rectangle from the source surface using the source + * rectangle. + * + * If the coordinate values address pixels outside the source surface + * a twGfxErrorInvalidCoord error will be returned. It is ok to access + * pixels outside the destination surface: clipping will ensure that + * nothing improper happens. + * + * Errors: + * twGfxErrorInvalidHandle + * twGfxErrorNullPointer + * twGfxErrorInvalidCoord + * twGfxErrorOperationInProgress + */ +Err TwGfxStretchBlt(TwGfxSurfaceType* aDestSurface, + const TwGfxRectType* aDestRect, + TwGfxSurfaceType* aSourceSurface, + const TwGfxRectType* aSourceRect) + TAL_TRAP(trapTwGfxStretchBlt); + +/* + * Tiling bitblt routine. + * + * The source surface will be tiled to the destination surface in the + * destination rectangle area, using the source alignment point to + * define a relative origin for the tiling. + * + * If the coordinate values address pixels outside the source surface + * a twGfxErrorInvalidCoord error will be returned. It is ok to access + * pixels outside the destination surface: clipping will ensure that + * nothing improper happens. + * + * Errors: + * twGfxErrorInvalidHandle + * twGfxErrorNullPointer + * twGfxErrorInvalidCoord + * twGfxErrorOperationInProgress + */ +Err TwGfxTileBlt(TwGfxSurfaceType* aDestSurface, + const TwGfxRectType* aDestRect, + TwGfxSurfaceType* aSourceSurface, + const TwGfxPointType* aSourceAlignmentPoint) + TAL_TRAP(trapTwGfxTileBlt); + +/* + * Transformation bitblt routine. Can rotate (none,90,180,27) and/or + * mirror (none, horz, vert, both) the source surface during the + * transfer to the destination surface. + * + * The source surface will be copied to the destination surface at the + * destination point from the source surface using the source + * rectangle. + * + * If the coordinate values address pixels outside the source surface + * a twGfxErrorInvalidCoord error will be returned. It is ok to access + * pixels outside the destination surface: clipping will ensure that + * nothing improper happens. + * + * When mixing rotation and mirroring, the mirroring is applied first + * then the rotation. + * + * Assuming the following example source bitmap: + * + * 123 + * 456 + * + * Here are all the combinations of rotations and mirrorings: + * + * +----------+--------+--------+--------+--------+ + * + Rot/Mir + None + Horz + Vert + Both + + * +----------+--------+--------+--------+--------+ + * + + 123 + 321 + 456 + 654 + + * + None + 456 + 654 + 123 + 321 + + * + + + + + + + * +----------+--------+--------+--------+--------+ + * + + 41 + 63 + 14 + 36 + + * + CW90 + 52 + 52 + 25 + 25 + + * + + 63 + 41 + 36 + 14 + + * +----------+--------+--------+--------+--------+ + * + + 654 + 456 + 321 + 123 + + * + CW180 + 321 + 123 + 654 + 456 + + * + + + + + + + * +----------+--------+--------+--------+--------+ + * + + 36 + 14 + 63 + 41 + + * + CW270 + 25 + 25 + 52 + 52 + + * + + 14 + 36 + 41 + 63 + + * +----------+--------+--------+--------+--------+ + * + * Errors: + * twGfxErrorInvalidHandle + * twGfxErrorNullPointer + * twGfxErrorInvalidCoord + * twGfxErrorInvalidRotation + * twGfxErrorInvalidMirror + * twGfxErrorOperationInProgress + */ +Err TwGfxTransformBlt(TwGfxSurfaceType* aDestSurface, + const TwGfxPointType* aDestPoint, + TwGfxSurfaceType* aSourceSurface, + const TwGfxRectType* aSourceRect, + Int32 aRotationFlags, + Int32 aMirrorFlags) + TAL_TRAP(trapTwGfxTransformBlt); + +/* + * Draw a set of points with a single color into the surface. This is + * equivalent to locking the surface and plotting the points, but more + * efficiently. Invalid coordinates are clipped. + * + * Errors: + * twGfxErrorInvalidHandle + * twGfxErrorNullPointer + * twGfxErrorInvalidCount + * twGfxErrorOperationInProgress + */ +Err TwGfxDrawPoints(TwGfxSurfaceType* aDestSurface, + const TwGfxPointType* aPoints, + Int32 aNumberOfPoints, + TwGfxPackedRGBType aColor) + TAL_TRAP(trapTwGfxDrawPoints); + +/* + * Same idea as TwGfxDrawPoints except that each point has a distinct + * color. Invalid coordinates are clipped. + * + * Errors: + * twGfxErrorInvalidHandle + * twGfxErrorNullPointer + * twGfxErrorInvalidCount + * twGfxErrorOperationInProgress + */ +Err TwGfxDrawColorPoints(TwGfxSurfaceType* aDestSurface, + const TwGfxPointType* aPoints, + Int32 aNumberOfPoints, + const TwGfxPackedRGBType* aColors) + TAL_TRAP(trapTwGfxDrawColorPoints); + +/* TODO: describe which pixels are rendered */ +/* TODO: the chip can draw stippled lines; does anybody except PalmOS care? */ + +/* + * Draw one or more connected lines using the given color. There must + * be 2 or more vertices provided. Lines are clipped to the + * destination surface. + * + * Errors: + * twGfxErrorInvalidHandle + * twGfxErrorNullPointer + * twGfxErrorInvalidCount + * twGfxErrorOperationInProgress + */ +Err TwGfxDrawLines(TwGfxSurfaceType* aDestSurface, + const TwGfxPointType* aPoints, + Int32 aNumberOfPoints, + TwGfxPackedRGBType aColor) + TAL_TRAP(trapTwGfxDrawLines); + +/* + * Draw one or more line segments. There must be 2 or more vertices + * provided, and the count must be even. Lines are clipped to the + * destination surface. + * + * Errors: + * twGfxErrorInvalidHandle + * twGfxErrorNullPointer + * twGfxErrorInvalidCount + * twGfxErrorOperationInProgress + */ +Err TwGfxDrawLineSegments(TwGfxSurfaceType* aDestSurface, + const TwGfxPointType* aPoints, + Int32 aNumberOfPoints, + TwGfxPackedRGBType aColor) + TAL_TRAP(trapTwGfxDrawLineSegments); + +// TODO: describe which pixels are rendered +/* + * Draw the outline of a rectangle. The resulting lines are clipped to + * the destination surface. + * + * Errors: + * twGfxErrorInvalidHandle + * twGfxErrorNullPointer + * twGfxErrorOperationInProgress + */ +Err TwGfxDrawRect(TwGfxSurfaceType* aDestSurface, + const TwGfxRectType* aRect, + TwGfxPackedRGBType aColor) + TAL_TRAP(trapTwGfxDrawRect); + +// TODO: describe which pixels are rendered +/* + * Fill a rectangle. The rectangle is clipped to the destination + * surface. + * + * Errors: + * twGfxErrorInvalidHandle + * twGfxErrorNullPointer + * twGfxErrorOperationInProgress + */ +Err TwGfxFillRect(TwGfxSurfaceType* aDestSurface, + const TwGfxRectType* aRect, + TwGfxPackedRGBType aColor) + TAL_TRAP(trapTwGfxFillRect); + +// TODO: describe which pixels are rendered +/* + * Draw a set of constant colored horizontal spans. This is similar to + * drawing a set of horizontal lines, but with smaller data overhead + * and higher efficiency. The spans are clipped to the destination + * surface. + * + * Errors: + * twGfxErrorInvalidHandle + * twGfxErrorNullPointer + * twGfxErrorInvalidCount + * twGfxErrorOperationInProgress + */ +Err TwGfxDrawSpans(TwGfxSurfaceType* aDestSurface, + const TwGfxSpanType* aSpans, + Int32 aNumberOfSpans, + TwGfxPackedRGBType aColor) + TAL_TRAP(trapTwGfxDrawSpans); + +// TODO: describe which pixels are rendered +/* + * Render a bitmap to the destination surface. The bitmap is clipped + * to the destination surface. + * + * The following pixel formats are supported: + * + * twGfxPixelFormatMonochrome + * twGfxPixelFormatRGB565_LE + * twGfxPixelFormatRGB565_BE + * + * Errors: + * twGfxErrorInvalidHandle + * twGfxErrorNullPointer + * twGfxErrorInvalidPixelFormat + * twGfxErrorBadObjectVersion + * twGfxErrorOperationInProgress + */ +Err TwGfxDrawBitmap(TwGfxSurfaceType* aDestSurface, + const TwGfxPointType* aDestPoint, + const TwGfxBitmapType* aBitmap) + TAL_TRAP(trapTwGfxDrawBitmap); + +#ifdef __cplusplus +} +#endif + +#endif /* __TWGFX_H__ */ diff --git a/inc/TwTraps.h b/inc/TwTraps.h new file mode 100644 index 0000000..89e1d2b --- /dev/null +++ b/inc/TwTraps.h @@ -0,0 +1,206 @@ +/* Copyright (c) 2002-2003 Tapwave, Inc. All rights reserved. */ + +#ifndef __TWTRAPS_H__ +#define __TWTRAPS_H__ + +#include + +#ifndef sysTrapOEMDispatch2 +#define sysTrapOEMDispatch2 0xA443 +#endif + +/* this actually is the oem dispatch trap 2 */ +#define sysTrapTwDispatch sysTrapOEMDispatch2 + +#if CPU_TYPE == CPU_68K + +#if defined(__GNUC__) +#if 0 + #define TAL_TRAP(selector) \ + __attribute__ ((__callseq__ ( \ + "move.w #" _Str(selector) ",%%d2; " \ + "trap #15; " \ + "dc.w " _Str(sysTrapTwDispatch) ";"))) +#else + #define _TW_CALL_WITH_SELECTOR(table, vector, selector)\ + __attribute__ ((__callseq__ (\ + "move.w #" _Str(selector) ",%%d2; "\ + "trap #" _Str(table) "; dc.w " _Str(vector)))) + #define TAL_TRAP(selector) _TW_CALL_WITH_SELECTOR(15, sysTrapTwDispatch, selector) +#endif +#elif defined(__MWERKS__) + #define TAL_TRAP(selector) \ + = { 0x343C, selector, 0x4E40 + 15, sysTrapTwDispatch } +#endif + +#else + +#define TAL_TRAP(selector) + +#endif + +#if 0 +#define _TW_CALL_WITH_SELECTOR(table, vector, selector)\ + __attribute__ ((__callseq__ (\ + "move.w #" _Str(selector) ",%%d2; "\ + "trap #" _Str(table) "; dc.w " _Str(vector)))) +#define TAL_TRAP(selector) _TW_CALL_WITH_SELECTOR(15, sysTrapTwDispatch, selector) +#endif + +/* this gives the selector base for tapwave interface */ +#define trapTwSelectorBase 256 + +/* selector numbers for 68K calls */ +#define trapTwDeviceOpen 256 +#define trapTwDeviceClose 257 +#define trapTwDeviceRead 258 +#define trapTwDeviceWrite 259 +#define trapTwDeviceGetProperty 260 +#define trapTwDeviceSetProperty 261 +#define trapTwDeviceControl 262 + +#define trapTwDisplayGetState 263 +#define trapTwDisplaySetState 264 + +#define trapTwHighScoreRegister 265 +#define trapTwHighScoreUnregister 266 +#define trapTwHighScoreReport 267 +#define trapTwHighScoreGetSummary 268 +#define trapTwHighScoreGetDetails 269 + +#define trapTwInputOpen 270 +#define trapTwInputClose 271 +#define trapTwInputActivate 272 +#define trapTwInputDeactivate 273 +#define trapTwInputGetPeriod 274 +#define trapTwInputSetPeriod 275 +#define trapTwInputGetCapacity 276 +#define trapTwInputSetCapacity 277 +#define trapTwInputGetFormat 278 +#define trapTwInputSetFormat 279 +#define trapTwInputPeek 280 +#define trapTwInputRead 281 +#define trapTwInputPoll 282 +#define trapTwInputControl 283 + +#define trapTwNavResetCalibration 284 +#define trapTwNavCalibrate 285 + +#define trapTwSndPlaySystemSound 286 +#define trapTwSndGetVolume 287 +#define trapTwSndSetMute 288 +#define trapTwSndSetVolume 289 +#define trapTwSndSetBassBoost 290 +#define trapTwSndGetMute 291 +#define trapTwSndGetBassBoost 292 + +#define trapTwCtlSetFrameStyle 293 +#define trapWinGetBitmapDimensions 294 +#define trapTwOSReserved1 295 +#define trapTwOSReserved2 296 +#define trapTwOSReserved3 297 +#define trapTwOSReserved4 298 +#define trapTwOSReserved5 299 + +#define trapTwDrawTitleBar 300 +#define trapTwSetTapwaveScrollBar 301 +#define trapTwGetSlotNumberForVolume 302 + +#define trapTwGfxOpen 303 +#define trapTwGfxClose 304 +#define trapTwGfxGetInfo 305 +#define trapTwGfxGetMemoryUsage 306 +#define trapTwGfxGetDisplaySurface 307 +#define trapTwGfxGetPalmDisplaySurface 308 +#define trapTwGfxInVBlank 309 +#define trapTwGfxWaitForVBlank 310 +#define trapTwGfxAllocSurface 311 +#define trapTwGfxFreeSurface 312 +#define trapTwGfxSetClip 313 +#define trapTwGfxGetClip 314 +#define trapTwGfxGetSurfaceInfo 315 +#define trapTwGfxLockSurface 316 +#define trapTwGfxUnlockSurface 317 +#define trapTwGfxReadSurface 318 +#define trapTwGfxWriteSurface 319 +#define trapTwGfxIsSurfaceReady 320 +#define trapTwGfxBitBlt 321 +#define trapTwGfxStretchBlt 322 +#define trapTwGfxTransformBlt 323 +#define trapTwGfxDrawPoints 324 +#define trapTwGfxDrawColorPoints 325 +#define trapTwGfxDrawLines 326 +#define trapTwGfxDrawLineSegments 327 +#define trapTwGfxDrawRect 328 +#define trapTwGfxFillRect 329 +#define trapTwGfxDrawSpans 330 +#define trapTwGfxDrawBitmap 331 +#define trapTwGfxReadSurfaceRegion 332 +#define trapTwGfxWriteSurfaceRegion 333 +#define trapTwGfxBlendBlt 334 +#define trapTwGfxTileBlt 335 +#define trapTwGfxMaskBlt 336 +#define trapTwGfxAsyncBlt 337 +#define trapTwGfxMaskBlendBlt 338 +#define trapTwGfxTransparentBlt 339 +#define trapTwGfxReserved3 340 +#define trapTwGfxReserved4 341 +#define trapTwGfxReserved5 342 +#define trapTwGfxReserved6 343 +#define trapTwGfxReserved7 344 +#define trapTwGfxReserved8 345 +#define trapTwGfxReserved9 346 +#define trapTwGfxReserved10 347 +#define trapTwGfxReserved11 348 +#define trapTwGfxReserved12 349 +#define trapTwGfxReserved13 350 +#define trapTwGfxReserved14 351 +#define trapTwGfxReserved15 352 +#define trapTwGfxReserved16 353 +#define trapTwGfxReserved17 354 +#define trapTwGfxReserved18 355 +#define trapTwGfxReserved19 356 +#define trapTwGfxDrawPalmBitmap 357 + +#define trapTwSecGetFunctions 358 +#define trapTwSecReserved1 359 +#define trapTwSecReserved2 360 +#define trapTwSecReserved3 361 +#define trapTwSecReserved4 362 +#define trapTwSecReserved5 363 +#define trapTwSecReserved6 364 +#define trapTwSecReserved7 365 +#define trapTwSecReserved8 366 +#define trapTwSecReserved9 367 +#define trapTwSecReserved10 368 +#define trapTwSecReserved11 369 +#define trapTwSecReserved12 370 +#define trapTwSecReserved13 371 +#define trapTwSecReserved14 372 +#define trapTwSecReserved15 373 +#define trapTwSecReserved16 374 + +#define trapTwCreateDatabaseFromImage 375 +#define trapTwGetGraphicForButton 376 +#define trapTwHighScoreGetTournament 377 +#define trapTwBlendMask 378 +#define trapTwGetPRCDataDirectory 379 +#define trapTwGetDBDataDirectory 380 +#define trapTwGetSlotRefNumForSlot 381 +#define trapTwGetMicroSeconds 382 +#define trapTwPickColor 383 + +// ADD NEW TRAPS ABOVE THIS LINE AND THEN RENUMBER THE ONES BELOW + +#define trapTwFutureReserved0 384 +#define trapTwFutureReserved1 (trapTwFutureReserved0 + 1) +#define trapTwFutureReserved2 (trapTwFutureReserved0 + 2) +#define trapTwFutureReserved3 (trapTwFutureReserved0 + 3) +#define trapTwFutureReserved4 (trapTwFutureReserved0 + 4) +#define trapTwFutureReserved5 (trapTwFutureReserved0 + 5) +#define trapTwFutureReserved6 (trapTwFutureReserved0 + 6) +#define trapTwFutureReserved7 (trapTwFutureReserved0 + 7) +#define trapTwFutureReserved8 (trapTwFutureReserved0 + 8) +#define trapTwFutureReserved9 (trapTwFutureReserved0 + 9) + +#endif /* __TWTRAPS_H__ */ diff --git a/inc/basictypes.h b/inc/basictypes.h new file mode 100644 index 0000000..f1bd73c --- /dev/null +++ b/inc/basictypes.h @@ -0,0 +1,17 @@ +#ifndef __BASICTYPES_H__ +#define __BASICTYPES_H__ + +typedef unsigned char byte; +typedef unsigned short word; +typedef unsigned long dword; +typedef unsigned long long ddword; +typedef unsigned long long dword64; +typedef long long long64; +#undef NULL +#define NULL 0 + +#define ELEMENTSIZE(a) (sizeof(a[0])) +#define ARRAYSIZE(a) (sizeof(a) / sizeof(a[0])) +#define OFFSETOF(t, m) ((int)&(((t *)0)->m)) + +#endif // __BASICTYPES_H__ diff --git a/inc/hostcontrol.h b/inc/hostcontrol.h new file mode 100644 index 0000000..3647408 --- /dev/null +++ b/inc/hostcontrol.h @@ -0,0 +1,527 @@ +/* -*- mode: C++; tab-width: 4 -*- */ +/* ====================================================================================== */ +/* Copyright (c) 1998-2000 Palm Computing, Inc. or its subsidiaries. All rights reserved. */ +/* ====================================================================================== */ + +#ifndef _HOSTCONTROL_H_ +#define _HOSTCONTROL_H_ + +#if !defined (__SYSTRAPS_H_) && !defined (__CORETRAPS_H_) +#error "Please #include either or , depending on what's in your Palm Includes directory." +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* + Set the base value for selectors. Note that this value MUST be + two bytes long and have the high byte be non-zero. The reason + for this has to do with the way SysGremlins was originally + declared. It took a GremlinsSelector enumerated value. Originally, + there was only one value, and it was zero. The way the 68K compiler + works, it decides that GremlinsSelectors are only one byte long, + so a call to SysGremlins would push one byte onto the stack. Because + all values on the stack need to be word-aligned, the processor + subtracts 1 from the stack before pushing on the byte. Therefore, + the stack looks like: + + previous contents + garbage byte + selector + return address + + With this setup, we have two choices: leave the selector size at + one byte and limit ourselves to 256 functions, or define the selector + to be a two byte value, with the first 256 values (all those with 0x00 + in the upper byte) to be GremlinIsOn. The latter sounds preferable, so + we start the new selectors at 0x0100. +*/ + +#define hostSelectorBase 0x0100 + + // Host information selectors + +#define hostSelectorGetHostVersion 0x0100 +#define hostSelectorGetHostID 0x0101 +#define hostSelectorGetHostPlatform 0x0102 +#define hostSelectorIsSelectorImplemented 0x0103 +#define hostSelectorGestalt 0x0104 +#define hostSelectorIsCallingTrap 0x0105 + + // Profiler selectors + +#define hostSelectorProfileInit 0x0200 +#define hostSelectorProfileStart 0x0201 +#define hostSelectorProfileStop 0x0202 +#define hostSelectorProfileDump 0x0203 +#define hostSelectorProfileCleanup 0x0204 +#define hostSelectorProfileDetailFn 0x0205 + + // Std C Library wrapper selectors + +#define hostSelectorErrNo 0x0300 + +#define hostSelectorFClose 0x0301 +#define hostSelectorFEOF 0x0302 +#define hostSelectorFError 0x0303 +#define hostSelectorFFlush 0x0304 +#define hostSelectorFGetC 0x0305 +#define hostSelectorFGetPos 0x0306 +#define hostSelectorFGetS 0x0307 +#define hostSelectorFOpen 0x0308 +#define hostSelectorFPrintF 0x0309 /* Floating point not yet supported in Poser */ +#define hostSelectorFPutC 0x030A +#define hostSelectorFPutS 0x030B +#define hostSelectorFRead 0x030C +#define hostSelectorRemove 0x030D /* Not yet implemented in Poser */ +#define hostSelectorRename 0x030E /* Not yet implemented in Poser */ +#define hostSelectorFReopen 0x030F /* Not yet implemented in Poser */ +#define hostSelectorFScanF 0x0310 /* Not yet implemented */ +#define hostSelectorFSeek 0x0311 +#define hostSelectorFSetPos 0x0312 +#define hostSelectorFTell 0x0313 +#define hostSelectorFWrite 0x0314 +#define hostSelectorTmpFile 0x0315 +#define hostSelectorTmpNam 0x0316 /* Not yet implemented in Poser */ +#define hostSelectorGetEnv 0x0317 + +#define hostSelectorMalloc 0x0318 /* Not yet implemented in Poser */ +#define hostSelectorRealloc 0x0319 /* Not yet implemented in Poser */ +#define hostSelectorFree 0x031A /* Not yet implemented in Poser */ + + // Gremlin selectors + +#define hostSelectorGremlinIsRunning 0x0400 +#define hostSelectorGremlinNumber 0x0401 +#define hostSelectorGremlinCounter 0x0402 +#define hostSelectorGremlinLimit 0x0403 +#define hostSelectorGremlinNew 0x0404 + + // Database selectors + +#define hostSelectorImportFile 0x0500 +#define hostSelectorExportFile 0x0501 + + // Preferences selectors + +#define hostSelectorGetPreference 0x0600 +#define hostSelectorSetPreference 0x0601 + + // Logging selectors + +#define hostSelectorLogFile 0x0700 +#define hostSelectorSetLogFileSize 0x0701 + + // RPC selectors + +#define hostSelectorSessionCreate 0x0800 /* Not yet implemented in Poser */ +#define hostSelectorSessionOpen 0x0801 /* Not yet implemented in Poser */ +#define hostSelectorSessionClose 0x0802 +#define hostSelectorSessionQuit 0x0803 +#define hostSelectorSignalSend 0x0804 +#define hostSelectorSignalWait 0x0805 +#define hostSelectorSignalResume 0x0806 + + // External tracing tool support + +#define hostSelectorTraceInit 0x0900 +#define hostSelectorTraceClose 0x0901 +#define hostSelectorTraceOutputT 0x0902 +#define hostSelectorTraceOutputTL 0x0903 +#define hostSelectorTraceOutputVT 0x0904 +#define hostSelectorTraceOutputVTL 0x0905 +#define hostSelectorTraceOutputB 0x0906 + +#define hostSelectorLastTrapNumber 0x0907 + +typedef UInt16 HostControlTrapNumber; + + +struct HostFILE +{ + long _field; +}; + +typedef struct HostFILE HostFILE; + + +typedef long HostBool; +typedef long HostErr; +typedef long HostID; +typedef long HostPlatform; +typedef long HostSignal; + +#ifndef hostErrorClass + #define hostErrorClass 0x1C00 // Host Control Manager +#else + #if hostErrorClass != 0x1C00 + #error "You cannot change hostErrorClass without telling us." + #endif +#endif + +enum // HostErr values +{ + hostErrNone = 0, + + hostErrBase = hostErrorClass, + + hostErrUnknownGestaltSelector, + hostErrDiskError, + hostErrOutOfMemory, + hostErrMemReadOutOfRange, + hostErrMemWriteOutOfRange, + hostErrMemInvalidPtr, + hostErrInvalidParameter, + hostErrTimeout, + hostErrInvalidDeviceType, + hostErrInvalidRAMSize, + hostErrFileNotFound, + hostErrRPCCall, // Issued if the following functions are not called remotely: + // HostSessionCreate + // HostSessionOpen + // HostSessionClose + // HostSessionQuit + // HostSignalWait + // HostSignalResume + hostErrSessionRunning, // Issued by HostSessionCreate, HostSessionOpen, and + // HostSessionQuit if a session is running. + hostErrSessionNotRunning, // Issued by HostSessionClose if no session is running. + hostErrNoSignalWaiters, // Issued by HostSendSignal if no one's waiting for a signal. + hostErrSessionNotPaused // Issued when HostSignalResume, but the session was not + // halted from a HostSignalSend call. +}; + + +enum // HostID values +{ + hostIDPalmOS, // The plastic thingy + hostIDPalmOSEmulator, // The Copilot thingy + hostIDPalmOSSimulator // The Mac libraries you link with thingy +}; + + +enum // HostPlatform values +{ + hostPlatformPalmOS, + hostPlatformWindows, + hostPlatformMacintosh, + hostPlatformUnix +}; + +enum // HostSignal values +{ + hostSignalReserved, + hostSignalIdle, + hostSignalQuit, +#if 0 + // (Proposed...not supported yet) + hostSignalSessionStarted, + hostSignalSessionStopped, + hostSignalHordeStarted, + hostSignalGremlinStarted, + hostSignalGremlinSuspended, + hostSignalGremlinResumed, + hostSignalGremlinStopped, + hostSignalHordeStopped, +#endif + hostSignalUser = 0x40000000 // User-defined values start here and go up. +}; + + +// Use these to call FtrGet to see if you're running under the +// Palm OS Emulator. If not, FtrGet will return ftrErrNoSuchFeature. + +#define kPalmOSEmulatorFeatureCreator ('pose') +#define kPalmOSEmulatorFeatureNumber (0) + + +struct HostGremlinInfo +{ + long fFirstGremlin; + long fLastGremlin; + long fSaveFrequency; + long fSwitchDepth; + long fMaxDepth; + char fAppNames[200]; // Comma-seperated list of application names + // to run Gremlins on. If the string is empty, + // all applications are fair game. If the string + // begins with a '-' (e.g., "-Address,Datebook"), + // then all applications named in the list are + // excluded instead of included. +}; + +typedef struct HostGremlinInfo HostGremlinInfo; + + +// Define this, since SysTraps.h doesn't have it. + +#ifdef __SYSTRAPS_H_ +#define sysTrapHostControl sysTrapSysGremlins +#endif + + +// Define HOST_TRAP + +#if defined (_SYSTEM_API) + +#define HOST_TRAP(selector) \ + _SYSTEM_API(_CALL_WITH_16BIT_SELECTOR)(_SYSTEM_TABLE, sysTrapHostControl, selector) + +#else + +#define HOST_TRAP(selector) \ + FIVEWORD_INLINE( \ + 0x3F3C, selector, /* MOVE.W #selector, -(A7) */ \ + m68kTrapInstr + sysDispatchTrapNum, /* TRAP $F */ \ + sysTrapHostControl, /* sysTrapHostControl */ \ + 0x544F) /* ADD.Q #2, A7 */ +#endif + + +/* ==================================================================== */ +/* Host environment-related calls */ +/* ==================================================================== */ + +long HostGetHostVersion(void) + HOST_TRAP(hostSelectorGetHostVersion); + +HostID HostGetHostID(void) + HOST_TRAP(hostSelectorGetHostID); + +HostPlatform HostGetHostPlatform(void) + HOST_TRAP(hostSelectorGetHostPlatform); + +HostBool HostIsSelectorImplemented(long selector) + HOST_TRAP(hostSelectorIsSelectorImplemented); + +HostErr HostGestalt(long gestSel, long* response) + HOST_TRAP(hostSelectorGestalt); + +HostBool HostIsCallingTrap(void) + HOST_TRAP(hostSelectorIsCallingTrap); + + +/* ==================================================================== */ +/* Profiling-related calls */ +/* ==================================================================== */ + +HostErr HostProfileInit(long maxCalls, long maxDepth) + HOST_TRAP(hostSelectorProfileInit); + +HostErr HostProfileDetailFn(void* addr, HostBool logDetails) + HOST_TRAP(hostSelectorProfileDetailFn); + +HostErr HostProfileStart(void) + HOST_TRAP(hostSelectorProfileStart); + +HostErr HostProfileStop(void) + HOST_TRAP(hostSelectorProfileStop); + +HostErr HostProfileDump(const char* filename) + HOST_TRAP(hostSelectorProfileDump); + +HostErr HostProfileCleanup(void) + HOST_TRAP(hostSelectorProfileCleanup); + + +/* ==================================================================== */ +/* Std C Library-related calls */ +/* ==================================================================== */ + +long HostErrNo(void) + HOST_TRAP(hostSelectorErrNo); + + +long HostFClose(HostFILE* f) + HOST_TRAP(hostSelectorFClose); + +long HostFEOF(HostFILE* f) + HOST_TRAP(hostSelectorFEOF); + +long HostFError(HostFILE* f) + HOST_TRAP(hostSelectorFError); + +long HostFFlush(HostFILE* f) + HOST_TRAP(hostSelectorFFlush); + +long HostFGetC(HostFILE* f) + HOST_TRAP(hostSelectorFGetC); + +long HostFGetPos(HostFILE* f, long* posP) + HOST_TRAP(hostSelectorFGetPos); + +char* HostFGetS(char* s, long n, HostFILE* f) + HOST_TRAP(hostSelectorFGetS); + +HostFILE* HostFOpen(const char* name, const char* mode) + HOST_TRAP(hostSelectorFOpen); + +long HostFPrintF(HostFILE* f, const char* fmt, ...) + HOST_TRAP(hostSelectorFPrintF); + +long HostFPutC(long c, HostFILE* f) + HOST_TRAP(hostSelectorFPutC); + +long HostFPutS(const char* s, HostFILE* f) + HOST_TRAP(hostSelectorFPutS); + +long HostFRead(void* buffer, long size, long count, HostFILE* f) + HOST_TRAP(hostSelectorFRead); + +long HostRemove(const char* name) + HOST_TRAP(hostSelectorRemove); + +long HostRename(const char* oldName, const char* newName) + HOST_TRAP(hostSelectorRename); + +HostFILE* HostFReopen(const char* name, const char* mode, HostFILE *f) + HOST_TRAP(hostSelectorFReopen); + +long HostFScanF(HostFILE* f, const char *fmt, ...) + HOST_TRAP(hostSelectorFScanF); + +long HostFSeek(HostFILE* f, long offset, long origin) + HOST_TRAP(hostSelectorFSeek); + +long HostFSetPos(HostFILE* f, long* pos) + HOST_TRAP(hostSelectorFSetPos); + +long HostFTell(HostFILE* f) + HOST_TRAP(hostSelectorFTell); + +long HostFWrite(const void* buffer, long size, long count, HostFILE* f) + HOST_TRAP(hostSelectorFWrite); + +HostFILE* HostTmpFile(void) + HOST_TRAP(hostSelectorTmpFile); + +char* HostTmpNam(char *name) + HOST_TRAP(hostSelectorTmpNam); + +char* HostGetEnv(const char*) + HOST_TRAP(hostSelectorGetEnv); + + +void* HostMalloc(long size) + HOST_TRAP(hostSelectorMalloc); + +void* HostRealloc(void* p, long size) + HOST_TRAP(hostSelectorRealloc); + +void HostFree(void* p) + HOST_TRAP(hostSelectorFree); + + +/* ==================================================================== */ +/* Gremlin-related calls */ +/* ==================================================================== */ + +HostBool HostGremlinIsRunning(void) + HOST_TRAP(hostSelectorGremlinIsRunning); + +long HostGremlinNumber(void) + HOST_TRAP(hostSelectorGremlinNumber); + +long HostGremlinCounter(void) + HOST_TRAP(hostSelectorGremlinCounter); + +long HostGremlinLimit(void) + HOST_TRAP(hostSelectorGremlinLimit); + +HostErr HostGremlinNew(const HostGremlinInfo*) + HOST_TRAP(hostSelectorGremlinNew); + + +/* ==================================================================== */ +/* Import/export-related calls */ +/* ==================================================================== */ + +HostErr HostImportFile(const char* fileName, long cardNum) + HOST_TRAP(hostSelectorImportFile); + +HostErr HostExportFile(const char* fileName, long cardNum, const char* dbName) + HOST_TRAP(hostSelectorExportFile); + + +/* ==================================================================== */ +/* Preference-related calls */ +/* ==================================================================== */ + +HostBool HostGetPreference(const char*, char*) + HOST_TRAP(hostSelectorGetPreference); + +void HostSetPreference(const char*, const char*) + HOST_TRAP(hostSelectorSetPreference); + + +/* ==================================================================== */ +/* Logging-related calls */ +/* ==================================================================== */ + +HostFILE* HostLogFile(void) + HOST_TRAP(hostSelectorLogFile); + +void HostSetLogFileSize(long) + HOST_TRAP(hostSelectorSetLogFileSize); + + +/* ==================================================================== */ +/* RPC-related calls */ +/* ==================================================================== */ + +HostErr HostSessionCreate(const char* device, long ramSize, const char* romPath) + HOST_TRAP(hostSelectorSessionCreate); + +HostErr HostSessionOpen(const char* psfFileName) + HOST_TRAP(hostSelectorSessionOpen); + +HostErr HostSessionClose(const char* saveFileName) + HOST_TRAP(hostSelectorSessionClose); + +HostErr HostSessionQuit(void) + HOST_TRAP(hostSelectorSessionQuit); + +HostErr HostSignalSend(HostSignal signalNumber) + HOST_TRAP(hostSelectorSignalSend); + +HostErr HostSignalWait(long timeout, HostSignal* signalNumber) + HOST_TRAP(hostSelectorSignalWait); + +HostErr HostSignalResume(void) + HOST_TRAP(hostSelectorSignalResume); + +/* ==================================================================== */ +/* Tracing calls */ +/* ==================================================================== */ + + +void HostTraceInit(void) + HOST_TRAP(hostSelectorTraceInit); + +void HostTraceClose(void) + HOST_TRAP(hostSelectorTraceClose); + +void HostTraceOutputT(unsigned short, const char*, ...) + HOST_TRAP(hostSelectorTraceOutputT); + +void HostTraceOutputTL(unsigned short, const char*, ...) + HOST_TRAP(hostSelectorTraceOutputTL); + +void HostTraceOutputVT(unsigned short, const char*, char* /*va_list*/) + HOST_TRAP(hostSelectorTraceOutputVT); + +void HostTraceOutputVTL(unsigned short, const char*, char* /*va_list*/) + HOST_TRAP(hostSelectorTraceOutputVTL); + +void HostTraceOutputB(unsigned short, const unsigned char*, unsigned long/*size_t*/) + HOST_TRAP(hostSelectorTraceOutputB); + + +#ifdef __cplusplus +} +#endif + +#endif /* _HOSTCONTROL_H_ */ diff --git a/inc/rip.cpp b/inc/rip.cpp new file mode 100644 index 0000000..d33a510 --- /dev/null +++ b/inc/rip.cpp @@ -0,0 +1,432 @@ +///////////////////////////////////////////////////////////////////////////// +// rip.cpp +///////////////////////////////////////////////////////////////////////////// +// This module contains support for communicating errors or messages to a +// developer or tester. +///////////////////////////////////////////////////////////////////////////// + +#if defined(_DEBUG) || defined(DEBUG) + +namespace wi { +char *gpszRipFile; +int giRipLine; +} + +#if defined(WIN) && !defined(CE) + +#include "windows.h" +#include "rip.h" +#ifdef USE_PALM_UNIX_HEADERS +#include +#else +#include +#endif + +#define RIP_DEFINED + +namespace wi { + +///////////////////////////////////////////////////////////////////////////// +// DoRip +// +// Gets called to output a message to a developer or tester. +///////////////////////////////////////////////////////////////////////////// + +// Turn off c++ exception handler unwind semantics not enabled warning + +#pragma warning(disable : 4530) + +void DoRip(TCHAR *pszTitle, TCHAR *psz, va_list va) +{ + int idT; + static bool gfRipActive = FALSE; + + // If gpszRipFile is not NULL, Format the string + // "File: name.cpp, Line: nnn, ..." + // Otherwise, format the string without the File/Line info. + + TCHAR szT[256]; + TCHAR szT1[128]; + wvsprintf(szT1, psz, va); + + if (gpszRipFile != NULL) + wsprintf(szT, TEXT("\n%s: File: %s, Line: %d. %s\n"), pszTitle, gpszRipFile, giRipLine, szT1); + else + wsprintf(szT, TEXT("%s: %s"), pszTitle, szT1); + + // This'll break into the debugger if the debugger is present, + // otherwise it'll fall through to the message box case. + + OutputDebugString(szT); + +#if 0 + bool fBeingDebugged = TRUE; +#if defined(WIN) && !defined(CE) + try { + DebugBreak(); + } catch (...) { + fBeingDebugged = FALSE; + } +#else + DebugBreak(); +#endif + + // If we are not being debugged, bring up the message box + + if (fBeingDebugged) + return; +#endif + + // Bring up a message box with these options: + // Abort: terminate app + // Retry: break + // Ignore: keep running + + // We don't want to recurse, which may happen when this message + // box comes up due to WM_ACTIVATE messages and other being + // sent back to the app + + if (gfRipActive) + return; + + gfRipActive = TRUE; + idT = MessageBox(NULL, szT, pszTitle, + MB_ABORTRETRYIGNORE | MB_SETFOREGROUND); + gfRipActive = FALSE; + + switch (idT) { + case IDABORT: + // The process should exit now + // Terminate the process so we don't go through all that + // .dll cleanup code. This'll avoid mfc dumping heap + // allocation info to the debugger, making termination + // faster. + + TerminateProcess(GetCurrentProcess(), 0); + return; + + case IDRETRY: + // This'll break into the debugger or cause the just + // in time debugger to load. If neither are present, the app + // will terminate. + + OutputDebugString(szT); + + DebugBreak(); + + // Just continue and hope things get better + + return; + + case IDIGNORE: + // Output assertion string. Can also be used to ignore + // asserts. + + OutputDebugString(szT); + return; + } +} + +void DoAssertRip(int fNoAssert, TCHAR *psz, ...) +{ + if (fNoAssert) + return; + + va_list va; + va_start(va, psz); + DoRip(TEXT("Assertion failed!"), psz, va); + va_end(va); +} + +void DoAssertRip(int fNoAssert) +{ + if (fNoAssert) + return; + + DoRip(TEXT("Assertion failed!"), TEXT(""), NULL); +} + +void DoAssertRip(TCHAR *psz, ...) +{ + va_list va; + va_start(va, psz); + DoRip(TEXT("Assert!"), psz, va); + va_end(va); +} + +void dvprintf(TCHAR *psz, va_list va) +{ + TCHAR szT1[2048]; + wvsprintf(szT1, psz, va); + OutputDebugString(szT1); +} + +void dprintf(TCHAR *psz, ...) +{ + va_list va; + va_start(va, psz); + dvprintf(psz, va); + va_end(va); +} + +void DoAssertRip() +{ + DoAssertRip(TEXT("Unknown error")); +} + +} // namespace wi + +#endif // WIN + +#ifdef PIL + +#define RIP_DEFINED + +#include +#include "rip.h" + +namespace wi { + +#ifdef PNO +void DebugBreak() +{ +} +#else +extern "C" void DebugBreak() secCode14; +#endif + +void Break() +{ + ErrDisplayFileLineMsg(gpszRipFile, giRipLine, "Assert!"); +// DebugBreak(); +} + +void DoAssertRip(int fNoAssert, char *psz, ...) +{ + if (fNoAssert) + return; + Break(); +} + +void DoAssertRip(int fNoAssert) +{ + if (!fNoAssert) + Break(); +} + +void DoAssertRip(char *psz, ...) +{ + Break(); +} + +void DoAssertRip() +{ + Break(); +} + +} // namespace wi + +#endif // PIL + +#ifdef CE + +#define RIP_DEFINED + +#include "windows.h" + +#include "rip.h" + +namespace wi { + +void Break() +{ + WCHAR wszT[300]; + WCHAR wszT2[300]; + + MultiByteToWideChar(CP_ACP, 0, gpszRipFile, -1, wszT2, sizeof(wszT) - 1); + wsprintf(wszT, TEXT("File: %s, Line: %d"), wszT2, giRipLine); + MessageBox(NULL, wszT, TEXT("Assert"), MB_OK); + DebugBreak(); +} + +void DoRip(char *psz, va_list va) +{ + char szT[300]; + sprintf(szT, "File: %s, Line: %d. ", gpszRipFile, giRipLine); + vsprintf(szT + strlen(szT), psz, va); + + WCHAR wszT[300]; + MultiByteToWideChar(CP_ACP, 0, szT, -1, wszT, sizeof(wszT) - 1); + MessageBox(NULL, wszT, TEXT("Assert"), MB_OK); + + DebugBreak(); +} + +void DoAssertRip(int fNoAssert, char *psz, ...) +{ + if (fNoAssert) + return; + va_list va; + va_start(va, psz); + DoRip(psz, va); + va_end(va); +} + +void DoAssertRip(int fNoAssert) +{ + if (fNoAssert) + return; + DoRip("", NULL); +} + +void DoAssertRip(char *psz, ...) +{ + va_list va; + va_start(va, psz); + DoRip(psz, va); + va_end(va); +} + +void DoAssertRip() +{ + Break(); +} + +} // namespace wi + +#endif // CE + +#ifdef IPHONE + +#define RIP_DEFINED + +#include +#include "rip.h" +#include "iphone.h" + +namespace wi { + +void Break() +{ + IPhone::Log("Break:"); + IPhone::Log("File: %s, Line: %d", gpszRipFile, giRipLine); + IPhone::Break(); +} + +void DoRip(char *psz, va_list va) +{ + IPhone::Log("Assert:"); + IPhone::Log("File: %s, Line: %d", gpszRipFile, giRipLine); + IPhone::Log(psz, va); + IPhone::Break(); +} + +void DoAssertRip(int fNoAssert, char *psz, ...) +{ + if (fNoAssert) + return; + va_list va; + va_start(va, psz); + DoRip(psz, va); + va_end(va); +} + +void DoAssertRip(int fNoAssert) +{ + if (fNoAssert) + return; + DoRip("", 0); +} + +void DoAssertRip(char *psz, ...) +{ + va_list va; + va_start(va, psz); + DoRip(psz, va); + va_end(va); +} + +void DoAssertRip() +{ + Break(); +} + +void dprintf(const char *psz, ...) +{ + va_list va; + va_start(va, psz); + vprintf(psz, va); + va_end(va); +} + +} // namespace wi + +#endif // IPHONE + +#ifndef RIP_DEFINED + +#include +#include +#include +#include "inc/rip.h" + +namespace wi { + +void Break() +{ + printf("Break:\n"); + printf("File: %s, Line: %d\n", gpszRipFile, giRipLine); + abort(); +} + +void DoRip(char *psz, va_list va) +{ + printf("Assert:\n"); + printf("File: %s, Line: %d\n", gpszRipFile, giRipLine); + vprintf(psz, va); + Break(); +} + +void DoAssertRip(int fNoAssert, char *psz, ...) +{ + if (fNoAssert) + return; + va_list va; + va_start(va, psz); + DoRip(psz, va); + va_end(va); +} + +void DoAssertRip(int fNoAssert) +{ + if (fNoAssert) + return; + DoRip("", 0); +} + +void DoAssertRip(char *psz, ...) +{ + va_list va; + va_start(va, psz); + DoRip(psz, va); + va_end(va); +} + +void DoAssertRip() +{ + Break(); +} + +void dprintf(const char *psz, ...) +{ + va_list va; + va_start(va, psz); + vprintf(psz, va); + va_end(va); +} + +} // namespace wi + +#endif // !RIP_DEFINED + +#endif // defined(_DEBUG) || defined(DEBUG) diff --git a/inc/rip.h b/inc/rip.h new file mode 100644 index 0000000..5aaa984 --- /dev/null +++ b/inc/rip.h @@ -0,0 +1,71 @@ +#ifndef __RIP_H__ +#define __RIP_H__ + +namespace wi { + +#if defined(_DEBUG) || defined(DEBUG) + +// For Palm we need to specify the section these functions will end up in +// or bad things will happen when Assert is used in inline functions/methods. + +#if defined(__GNUC__) && defined(__CPU_68K) +#define secRip __attribute__((section("code2"))) +#define secCode14 __attribute__((section("code14"))) +typedef char TCHAR; +#define TEXT(str) str +#else +#define secRip +#define secCode14 +#endif + +// These are macroized solely so they can be redefined. This is useful +// if the below macros are going to be used in helper functions, where you +// want the file and line of the caller and not the helper function. + +#define __XYZFILE __FILE__ +#define __XYZLINE __LINE__ + +// +// The below macros constitute system support for asserts, warnings, +// bad param reporting, and rips. +// +// The macros are: +// +// Assert(f, ...) +// Standard assertion macro. If present, the second parameter is a +// sprintf format string. + +// These globals need to be defined for rip to work. + +extern char *gpszRipFile; +extern int giRipLine; + +void DoAssertRip(int fAssert, char *psz, ...) secRip; +void DoAssertRip(int fAssert) secRip; +void DoAssertRip(char *psz, ...) secRip; +void DoAssertRip() secRip; +void Break() secRip; +void dprintf(const char *psz, ...); + +#define Assert wi::gpszRipFile = __XYZFILE, wi::giRipLine = __XYZLINE, ::wi::DoAssertRip + +#else // !defined(_DEBUG) && !defined(DEBUG) + +// This hack allows us to create varargs macros that compile to nothing +// on release. + +inline void DoConditionalRip(int, ...) { } +inline void DoConditionalRip(char *psz, ...) { } +inline void DoConditionalRip() { } +inline void Break() { } +inline void dprintf(const char *psz, ...) { } + +#define Assert 1 ? (void)0 : ::wi::DoConditionalRip + +#endif // defined(_DEBUG) || defined(DEBUG) + +#define CompileAssert(expr) typedef char __COMPILE_ASSERT__[(expr) ? 1 : -1] + +} // namespace wi + +#endif // __RIP_H__ diff --git a/inc/sonyhwroemids.h b/inc/sonyhwroemids.h new file mode 100644 index 0000000..71ae258 --- /dev/null +++ b/inc/sonyhwroemids.h @@ -0,0 +1,135 @@ +/****************************************************************************** + * * + * (C) Copyright 2000, Sony Corporation * + * * + *----------------------------------------------------------------------------* + * * + * * + * file name : $Workfile: SonyHwrOEMIDs.h $ + * * + * * + * GHwrOEMIDs (CompanyID, HALID, DeviceID) definitions * + * * + * * + * Started on : 00/12/02 * + * Last Modified: $Date: 2003/03/22 01:46:03 $ + * * + ******************************************************************************/ +/* this file is best viewed by setting TAB-stop as 3 */ + +#ifndef __SONYHWROEMIDS_H__ +#define __SONYHWROEMIDS_H__ + +/****************************************************************************** + * Include * + ******************************************************************************/ +#include + + +/****************************************************************************** + * Definitions * + ******************************************************************************/ + +/*** GHwrOEMCompanyID ***/ + /* All sony-based model has this ID. other one is not permitted */ + /* can be obtained by FtrGet(sysFtrCreator, sysFtrNumOEMCompanyID, &value) */ + /* hwrOEMCompanyIDSony may be defined in HwrMiscFlags.h someday :-) */ +#define sonyHwrOEMCompanyID_Sony 'sony' /* CAN'T be changed!! */ + +/*** GHwrOEMHALID ***/ + /* defined for each HAL source code, same codes have the same ID */ + /* can be obtained by FtrGet(sysFtrCreator, sysFtrNumOEMHALID, &value) */ +#define sonyHwrOEMHALIDPda1Mono hwrOEMHALIDEZRef /* 'eref' */ +#define sonyHwrOEMHALIDPda1Color hwrOEMHALIDEZRefColor /* 'cref' */ +#define sonyHwrOEMHALIDYosemite 'ysmt' /* 3.5 */ +#define sonyHwrOEMHALIDNasca 'nsca' /* 4.0 */ +#define sonyHwrOEMHALIDYellowstone 'ystn' /* 4.0 */ +#define sonyHwrOEMHALIDYosemite2 sonyHwrOEMHALIDYosemite + // instead of #define sonyHwrOEMHALIDYosemite2 'ysm2' /* 4.0 */ +#define sonyHwrOEMHALIDVenice 'vnce' /* 4.1 */ +#define sonyHwrOEMHALIDModena 'mdna' /* 4.1 */ +#define sonyHwrOEMHALIDNasca2 'nsc2' /* 4.0 */ +#define sonyHwrOEMHALIDRedwood 'rdwd' /* 4.1 */ + +/*** GHwrOEMDeviceID ***/ + /* defined for each model. more than one models have the same HALID, but + not DeviceID */ + /* can be obtained by FtrGet(sysFtrCreator, sysFtrNumOEMDeviceID, &value) */ +#define sonyHwrOEMDeviceIDPda1Mono (0x00010001) +#define sonyHwrOEMDeviceIDPda1Color (0x00010002) +#define sonyHwrOEMDeviceIDYosemite 'ysmt' +#define sonyHwrOEMDeviceIDNasca 'nsca' +#define sonyHwrOEMDeviceIDYellowstone 'ystn' +#define sonyHwrOEMDeviceIDYosemite2 'ysm2' +#define sonyHwrOEMDeviceIDVenice 'vnce' +#define sonyHwrOEMDeviceIDModena 'mdna' +#define sonyHwrOEMDeviceIDNasca2 'nsc2' +#define sonyHwrOEMDeviceIDRedwood 'rdwd' + +/****************************************************************************** + * References * + ******************************************************************************/ +/* CAUTIONS: This information is provided just for your information, and not + guaranteed to be correct all the time */ + +/* PEG-S300 */ +#define sonyHwrOEMHALID_S300 sonyHwrOEMHALIDPda1Mono +#define sonyHwrOEMDeviceID_S300 sonyHwrOEMDeviceIDPda1Mono + +/* PEG-S500C(J) */ +#define sonyHwrOEMHALID_S500C sonyHwrOEMHALIDPda1Color +#define sonyHwrOEMDeviceID_S500C sonyHwrOEMDeviceIDPda1Color + +/* PEG-N700C(J),N710C(US) */ +#define sonyHwrOEMHALID_N700C sonyHwrOEMHALIDYosemite +#define sonyHwrOEMHALID_N710C sonyHwrOEMHALIDYosemite +#define sonyHwrOEMDeviceID_N700C sonyHwrOEMDeviceIDYosemite +#define sonyHwrOEMDeviceID_N710C sonyHwrOEMDeviceIDYosemite + +/* PEG-S320(US) */ +#define sonyHwrOEMHALID_S320 sonyHwrOEMHALIDNasca +#define sonyHwrOEMDeviceID_S320 sonyHwrOEMDeviceIDNasca + +/* PEG-N600C(J),N610C(US) */ +#define sonyHwrOEMHALID_N600C sonyHwrOEMHALIDYellowstone +#define sonyHwrOEMHALID_N610C sonyHwrOEMHALIDYellowstone +#define sonyHwrOEMDeviceID_N600C sonyHwrOEMDeviceIDYellowstone +#define sonyHwrOEMDeviceID_N610C sonyHwrOEMDeviceIDYellowstone + +/* PEG-N750C(J),N760C(US),N760C/G(GVD),N770C/U(UK),N770C/E(EFG) */ +#define sonyHwrOEMHALID_N750C sonyHwrOEMHALIDYosemite2 +#define sonyHwrOEMHALID_N760C sonyHwrOEMHALIDYosemite2 +#define sonyHwrOEMHALID_N770C sonyHwrOEMHALIDYosemite2 +#define sonyHwrOEMDeviceID_N750C sonyHwrOEMDeviceIDYosemite2 +#define sonyHwrOEMDeviceID_N760C sonyHwrOEMDeviceIDYosemite2 +#define sonyHwrOEMDeviceID_N770C sonyHwrOEMDeviceIDYosemite2 + +/* PEG-T400(J),T415(US),T415/G(GVD),T425/U(UK),T425/E(EFG) */ + +#define sonyHwrOEMHALID_T400 sonyHwrOEMHALIDVenice +#define sonyHwrOEMHALID_T415 sonyHwrOEMHALIDVenice +#define sonyHwrOEMHALID_T425 sonyHwrOEMHALIDVenice +#define sonyHwrOEMDeviceID_T400 sonyHwrOEMDeviceIDVenice +#define sonyHwrOEMDeviceID_T415 sonyHwrOEMDeviceIDVenice +#define sonyHwrOEMDeviceID_T425 sonyHwrOEMDeviceIDVenice + +/* PEG-T600C(J),T615C(US),T615C/G(GVD),T625C/U(UK),T625C/E(EFG) */ + +#define sonyHwrOEMHALID_T600C sonyHwrOEMHALIDModena +#define sonyHwrOEMHALID_T615C sonyHwrOEMHALIDModena +#define sonyHwrOEMHALID_T625C sonyHwrOEMHALIDModena +#define sonyHwrOEMDeviceID_T600C sonyHwrOEMDeviceIDModena +#define sonyHwrOEMDeviceID_T615C sonyHwrOEMDeviceIDModena +#define sonyHwrOEMDeviceID_T625C sonyHwrOEMDeviceIDModena + +/* PEG-S360(US) */ +#define sonyHwrOEMHALID_S360 sonyHwrOEMHALIDNasca2 +#define sonyHwrOEMDeviceID_S360 sonyHwrOEMDeviceIDNasca2 + +/* PEG-NR70,NR70V(J) */ +#define sonyHwrOEMHALID_NR70 sonyHwrOEMHALIDRedwood +#define sonyHwrOEMDeviceID_NR70 sonyHwrOEMDeviceIDRedwood + + +#endif // __SONYHWROEMIDS_H__ + diff --git a/inc/sys_types.h b/inc/sys_types.h new file mode 100644 index 0000000..10b09f6 --- /dev/null +++ b/inc/sys_types.h @@ -0,0 +1,57 @@ +/****************************************************************************** + * + * Copyright (c) 1996-2003 PalmSource, Inc. All rights reserved. + * + * File: sys_types.h + * + * Release: eclipse 5 SDK (68K) R3. + * + * Description: + * This module contains the Pilot equivalent of the "standard" unix + * header file + * + *****************************************************************************/ + +#ifndef __UNIX_SYS_TYPES_H__ +#define __UNIX_SYS_TYPES_H__ + +#include // Include Pilot Net Manager equates + +// Common Unix types +typedef UInt8 u_char; +typedef UInt16 u_short; +typedef UInt16 u_int; +typedef UInt32 u_long; + +typedef UInt16 ushort; /* System V compatibility */ +typedef UInt16 uint; /* System V compatibility */ + +// Define this so we don't get duplicate define warnings. +#define __size_t__ +#ifndef PNO // DWM: added to eliminate conflict when building x86 PNO +typedef UInt32 size_t; // must be signed for some unix apps +#endif + +// File Descriptor Utilities, Pilot native forms in +typedef NetFDSetType fd_set; +#define FD_SETSIZE netFDSetSize + +#define FD_SET(n,p) netFDSet(n,p) +#define FD_CLR(n,p) netFDClr(n,p) +#define FD_ISSET(n,p) netFDIsSet(n,p) +#define FD_ZERO(p) netFDZero(p) + + +// Basic functions +#define bcopy(b1,b2,len) \ + MemMove(b2,b1,len) + +#define bzero(b,len) \ + MemSet(b,len,0) + +#define bcmp(b1,b2,len) \ + MemCmp(b1,b2,len) + + + +#endif /* __UNIX_SYS_TYPES_H__ */ diff --git a/inicrunch/ini.cpp b/inicrunch/ini.cpp new file mode 100644 index 0000000..b6688ce --- /dev/null +++ b/inicrunch/ini.cpp @@ -0,0 +1,259 @@ +#include +#include +#include +#include +#include "ini.h" + +// IniScanf / VIniScanf +// +// Supports: +// %s : "trimmed string", matches a string with interleaving spaces. Removes +// leading and trailing white space, with ini-friendly delimiters +// %s+: means %s plus store index of matching string +// %+ : means store current buffer index +// '*' after % is supported and means match but don't store +// ' ', '\t', '\n' : match for optional white space (space, tab, newline) + +#define IsWhiteSpace(ch) ((ch) == ' ' || (ch) == '\t' || (ch) == '\n' || (ch) == '\r') +#ifdef IsDigit +#undef IsDigit +#endif +#define IsDigit(ch) ((ch) >= '0' && (ch) <= '9') + +int IniScanf(char *pszBuff, char *pszFmt, ...) +{ + va_list va; + va_start(va, pszFmt); + int c = VIniScanf(pszBuff, pszFmt, va); + va_end(va); + return c; +} + +int VIniScanf(char *pszBuff, char *pszFmt, va_list va) +{ + char *pszBuffT = pszBuff; + char *pszFmtT = pszFmt; + int cArgs = 0; + while (*pszFmtT != 0) { + // Make this special check before checking for end + // of buffer + + if (pszFmtT[0] == '%' && pszFmtT[1] == '+') { + pszFmtT += 2; + *va_arg(va, int *) = pszBuffT - pszBuff; + cArgs++; + continue; + } + + switch (*pszFmtT) { + case ' ': + case '\n': + case '\t': + // Eat white space (space, tab, newline) + + while (IsWhiteSpace(*pszBuffT)) + pszBuffT++; + pszFmtT++; + continue; + } + + // End of buffer? Bail. + + if (*pszBuffT == 0) + break; + + bool fSuppress; + switch (*pszFmtT) { + case '%': + // Suppress? + + pszFmtT++; + fSuppress = false; + if (*pszFmtT == '*') { + fSuppress = true; + pszFmtT++; + } + + // Format specification + + switch (*pszFmtT) { + case 's': + // Trimmed string. + { + pszFmtT++; + + // If %s+, it means save the location of substring + + bool fSaveLocation = false; + if (*pszFmtT == '+') { + fSaveLocation = true; + pszFmtT++; + } + + // Go past initial whitespace + + int nchStart = pszBuffT - pszBuff; + while (IsWhiteSpace(*pszBuffT)) { + nchStart++; + pszBuffT++; + } + + // Copy the string into pszT. Figure out when to stop + // and remember trimming information. + + char chStop = *pszFmtT; + char *pszT = va_arg(va, char *); + char *pszWhiteLast = NULL; + while (*pszBuffT != chStop) { + if (*pszBuffT == 0) + break; + if (!IsWhiteSpace(*pszBuffT)) { + pszWhiteLast = NULL; + } else if (pszWhiteLast == NULL) { + pszWhiteLast = pszT; + } + if (!fSuppress) + *pszT++ = *pszBuffT++; + } + if (!fSuppress) { + *pszT = 0; + if (pszWhiteLast != NULL) + *pszWhiteLast = 0; + cArgs++; + } + + // %s+ means save location string started + + if (!fSuppress && fSaveLocation) { + *va_arg(va, int *) = nchStart; + cArgs++; + } + } + continue; + + default: + // Non-recognized % command + + return cArgs; + } + break; + + default: + // Match character + + if (*pszBuffT == *pszFmtT) { + pszBuffT++; + pszFmtT++; + continue; + } + break; + } + + // If we're here it means stop + + break; + } + + return cArgs; +} + +bool NewSection(char *pszSec, dword nch, IniSection **ppsec, int *pcsec) +{ + IniSection *psec = new IniSection[(*pcsec) + 1]; + if (psec == NULL) + return false; + if (*ppsec != NULL) { + memcpy(psec, *ppsec, sizeof(IniSection) * (*pcsec)); + delete *ppsec; + } + *ppsec = psec; + psec = &(*ppsec)[*pcsec]; + (*pcsec)++; + + psec->nchSec = nch; + psec->cchSec = strlen(pszSec); + psec->cprop = 0; + psec->pprop = NULL; + return true; +} + +bool NewProperty(IniSection *psec, char *pszProp, dword nchProp, char *pszValue, dword nchValue) +{ + IniProperty *pprop = new IniProperty[psec->cprop + 1]; + if (pprop == NULL) + return false; + if (psec->pprop != NULL) { + memcpy(pprop, psec->pprop, sizeof(IniProperty) * psec->cprop); + delete psec->pprop; + } + psec->pprop = pprop; + pprop = &psec->pprop[psec->cprop]; + psec->cprop++; + + pprop->nchProp = nchProp; + pprop->cchProp = strlen(pszProp); + pprop->nchValue = nchValue; + pprop->cchValue = strlen(pszValue); + return true; +} + +IniSection *LoadIniFile(char *pszFn, int *pcSections) +{ + IniSection *psec = NULL; + int csec = 0; + + FILE *pf = fopen(pszFn, "rb"); + if (pf == NULL) + return false; + + while (true) { + // Get the next line + + dword nchLine = ftell(pf); + char szLine[50 * 1024]; + if (fgets(szLine, sizeof(szLine) - 1, pf) == NULL) + break; + + // Is it a section header? If so, add it + + char szT[1024]; + int nchSec; + if (IniScanf(szLine, "\t[%s+]\t", szT, &nchSec) == 2) { + if (!NewSection(szT, nchLine + nchSec, &psec, &csec)) + return NULL; + continue; + } + + // Is it just whitespace? Allow leading ; for comments + + bool fAllWhiteSpace = true; + int cch = strlen(szLine); + for (int n = 0; n < cch; n++) { + if (!IsWhiteSpace(szLine[n])) { + if (szLine[n] == ';') + break; + fAllWhiteSpace = false; + break; + } + } + if (fAllWhiteSpace) + continue; + + // See if it is property = value + + int nchProp; + int nchValue; + char szValue[50 * 1024]; + if (IniScanf(szLine, "%s+=%s+", szT, &nchProp, szValue, &nchValue) == 4) { + if (!NewProperty(&psec[csec - 1], szT, nchLine + nchProp, szValue, nchLine + nchValue)) + return NULL; + continue; + } + + // Otherwise ignore it + } + + *pcSections = csec; + return psec; +} + diff --git a/inicrunch/ini.h b/inicrunch/ini.h new file mode 100644 index 0000000..ef5d787 --- /dev/null +++ b/inicrunch/ini.h @@ -0,0 +1,28 @@ +typedef unsigned long dword; +typedef unsigned char byte; +typedef unsigned short word; +#define NULL 0 + +#include + +struct IniProperty // prop +{ + dword nchProp; + int cchProp; + word hashProp; + dword nchValue; + int cchValue; +}; + +struct IniSection // sec +{ + dword nchSec; + int cchSec; + word hashSec; + int cprop; + IniProperty *pprop; +}; + +IniSection *LoadIniFile(char *pszFn, int *pcSections); +int IniScanf(char *pszBuff, char *pszFmt, ...); +int VIniScanf(char *pszBuff, char *pszFmt, va_list va); diff --git a/inicrunch/inicrunch.sln b/inicrunch/inicrunch.sln new file mode 100644 index 0000000..020a1b3 --- /dev/null +++ b/inicrunch/inicrunch.sln @@ -0,0 +1,21 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "inicrunch", "inicrunch.vcproj", "{9F0CD78F-48F3-41A8-9EBD-C14B0725F857}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Release = Release + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {9F0CD78F-48F3-41A8-9EBD-C14B0725F857}.Debug.ActiveCfg = Debug|Win32 + {9F0CD78F-48F3-41A8-9EBD-C14B0725F857}.Debug.Build.0 = Debug|Win32 + {9F0CD78F-48F3-41A8-9EBD-C14B0725F857}.Release.ActiveCfg = Release|Win32 + {9F0CD78F-48F3-41A8-9EBD-C14B0725F857}.Release.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/inicrunch/inicrunch.vcproj b/inicrunch/inicrunch.vcproj new file mode 100644 index 0000000..050b1ec --- /dev/null +++ b/inicrunch/inicrunch.vcproj @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inicrunch/main.cpp b/inicrunch/main.cpp new file mode 100644 index 0000000..8cbee7c --- /dev/null +++ b/inicrunch/main.cpp @@ -0,0 +1,514 @@ +#include +#include +#include +#include +#include "ini.h" + +#ifndef WINDOWS +#include +#define _MAX_PATH PATH_MAX +#define _MAX_FNAME NAME_MAX +#define _MAX_DRIVE NAME_MAX +#define _MAX_DIR (PATH_MAX-NAME_MAX) +#define _MAX_EXT NAME_MAX +#define _unlink remove +#define _fullpath(resolved_path, path, length) realpath(path, resolved_path) + +void _splitpath(const char* path, char* drv, char* dir, char* name, char* ext) +{ + const char* end; /* end of processed string */ + const char* p; /* search pointer */ + const char* s; /* copy pointer */ + + /* extract drive name */ + if (path[0] && path[1]==':') { + if (drv) { + *drv++ = *path++; + *drv++ = *path++; + *drv = '\0'; + } + } else if (drv) + *drv = '\0'; + + /* search for end of string or stream separator */ + for(end=path; *end && *end!=':'; ) + end++; + + /* search for begin of file extension */ + for(p=end; p>path && *--p!='\\' && *p!='/'; ) + if (*p == '.') { + end = p; + break; + } + + if (ext) + for(s=end; (*ext=*s++); ) + ext++; + + /* search for end of directory name */ + for(p=end; p>path; ) + if (*--p=='\\' || *p=='/') { + p++; + break; + } + + if (name) { + for(s=p; s>8)) +#define SwapDword(x) ((((x)&0xFF)<<24) | (((x)&0xFF00)<<8) | (((x)&0xFF0000)>>8) | (((x)&0xFF000000)>>24)) + +struct RecordChunk // rck +{ + byte *pb; + byte *pbMax; +}; + +struct SecChunk // sck +{ + word offSecNext; + short cprop; + // char szSecName[]; +}; + +struct PropChunk // pck +{ + // char szProp[]; + // char szPropValue[]; +}; + +bool WriteDatabase(char *pszFn, RecordChunk *arck, int cChunks, long *pcbFile); +bool WriteBinary(char *pszFn, RecordChunk *arck, int cChunks, long *pcbFile); +int CalcSectionSize(IniSection *psec); +bool ReadString(FILE *pf, int nch, int cch, byte *pb); + +void Usage() +{ + printf("\n"); + printf("Usage: inicrunch infile outfile\n"); + printf("\n"); + printf("This will read the infile and\n"); + printf("output outfile which is a straight image binary\n"); + printf("\n"); + printf("Usage: inicrunch -pdb infile outfile\n"); + printf("\n"); + printf("This will read the infile and output outfile\n"); + printf("in .pdb format.\n"); + printf("\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + bool fPdbOnly = false; + char *pszFnIn; + char *pszFnOut; + if (argc == 4 && strcmp(argv[1], "-pdb") == 0) { + fPdbOnly = true; + pszFnIn = argv[2]; + pszFnOut = argv[3]; + } else if (argc == 3) { + pszFnIn = argv[1]; + pszFnOut = argv[2]; + } else { + Usage(); + } + + // File exists? + + FILE *pf = fopen(pszFnIn, "rb"); + if (pf == NULL) { + printf("Error opening %s for reading.\n", pszFnIn); + exit(1); + } + + // Parse .ini file + + int cSections; + IniSection *psec = LoadIniFile(pszFnIn, &cSections); + if (psec == NULL) { + printf("Error parsing .ini file %s.\n", pszFnIn); + fclose(pf); + exit(1); + } + + // Build in memory format + + RecordChunk arck[256]; + memset(arck, 0, sizeof(arck)); + int cChunks = 0; + + IniSection *psecT = psec; + IniSection *psecMax = &psec[cSections]; + + bool fDone = false; + while (!fDone) { + RecordChunk *prck = &arck[cChunks++]; + prck->pb = new byte[kcbRecordMax]; + byte *pbT = prck->pb; + + while (!fDone) { + // If this section does not fit in this chunk, start a new chunk + + if (&pbT[CalcSectionSize(psecT)] > &prck->pb[kcbRecordMax]) + break; + + // Add this section to this chunk + + SecChunk *psck = (SecChunk *)pbT; + psck->cprop = SwapWord(psecT->cprop); + + // Copy in the section name and zero extend + + pbT = (byte *)(psck + 1); + if (!ReadString(pf, psecT->nchSec, psecT->cchSec, pbT)) { + printf("Error reading %s.\n", pszFnIn); + fclose(pf); + exit(1); + } + pbT += psecT->cchSec + 1; + + // Copy in the property / value pairs + + IniProperty *ppropT = psecT->pprop; + for (int n = 0; n < psecT->cprop; n++) { + if (!ReadString(pf, ppropT->nchProp, ppropT->cchProp, pbT)) { + printf("Error reading %s.\n", pszFnIn); + fclose(pf); + exit(1); + } + pbT += ppropT->cchProp + 1; + if (!ReadString(pf, ppropT->nchValue, ppropT->cchValue, pbT)) { + printf("Error reading %s.\n", pszFnIn); + fclose(pf); + exit(1); + } + pbT += ppropT->cchValue + 1; + ppropT++; + } + + // Even align + + if ((pbT - prck->pb) & 1) + *pbT++ = 0; + + // Backfill the "next section" offset + + psck->offSecNext = SwapWord(pbT - (byte *)psck); + + // Next section... + + psecT++; + if (psecT >= psecMax) { + psck->offSecNext = 0; + fDone = true; + } + } + + // Remember how much of this chunk we wrote to + + prck->pbMax = pbT; + } + fclose(pf); + + // Write the chunks out to the database + + long cbFile; + bool fSuccess; + if (fPdbOnly) { + fSuccess = WriteDatabase(pszFnOut, arck, cChunks, &cbFile); + } else { + fSuccess = WriteBinary(pszFnOut, arck, cChunks, &cbFile); + } + + // Free chunks + + int n; + for (n = 0; n < cChunks; n++) + delete arck[n].pb; + + // Free property sections + + for (n = 0; n < cSections; n++) + delete psec[n].pprop; + delete psec; + + return fSuccess ? 0 : 1; +} + +int CalcSectionSize(IniSection *psec) +{ + int cb = sizeof(SecChunk); + cb += psec->cchSec + 1; + for (int n = 0; n < psec->cprop; n++) { + cb += psec->pprop[n].cchProp + 1; + cb += psec->pprop[n].cchValue + 1; + } + cb = (cb + 1) & ~1; + return cb; +} + +bool ReadString(FILE *pf, int nch, int cch, byte *pb) +{ + *pb = 0; + if (cch == 0) + return true; + fseek(pf, nch, SEEK_SET); + if (fread(pb, cch, 1, pf) != 1) + return false; + pb[cch] = 0; + return true; +} + +bool WriteBinary(char *pszFn, RecordChunk *arck, int cChunks, long *pcbFile) +{ + // Create bin file + + FILE *pf = fopen(pszFn, "wb"); + if (pf == NULL) { + printf("Unable to create \"%s\".\n", pszFn); + return false; + } + + // Write out the chunks + + for (int n = 0; n < cChunks; n++) { + if (fwrite(arck[n].pb, arck[n].pbMax - arck[n].pb, 1, pf) != 1) { + printf("Error writing record data.\n"); + fclose(pf); + _unlink(pszFn); + return false; + } + } + + // Close & success + + *pcbFile = ftell(pf); + fclose(pf); + return true; +} + +#define dmDBNameLength 32 + +typedef unsigned char UInt8; +typedef unsigned short UInt16; +typedef unsigned long UInt32; +typedef unsigned long LocalID; +typedef char Char; + +struct RecordEntryType { + LocalID localChunkID; // local chunkID of a record + UInt8 attributes; // record attributes; + UInt8 uniqueID[3]; // unique ID of record; should + // not be 0 for a legal record. +}; + +struct RecordListType { + LocalID nextRecordListID; // local chunkID of next list + UInt16 numRecords; // number of records in this list + UInt16 firstEntry; // array of Record/Rsrc entries + // starts here +}; + +struct DatabaseHdrType { + char name[dmDBNameLength]; // name of database + UInt16 attributes; // database attributes + UInt16 version; // version of database + UInt32 creationDate; // creation date of database + UInt32 modificationDate; // latest modification date + UInt32 lastBackupDate; // latest backup date + UInt32 modificationNumber; // modification number of database + LocalID appInfoID; // application specific info + LocalID sortInfoID; // app specific sorting info + UInt32 type; // database type + UInt32 creator; // database creator + UInt32 uniqueIDSeed; // used to generate unique IDs. + // Note that only the low order + // 3 bytes of this is used (in + // RecordEntryType.uniqueID). + // We are keeping 4 bytes for + // alignment purposes. + RecordListType recordList; // first record list +}; + +#ifdef WINDOWS +#include +dword GetCurrentTimePalmUnits() +{ + // Palm keeps time as a count of seconds from Jan 1, 1904. + + FILETIME timCurrent; + GetSystemTimeAsFileTime(&timCurrent); + + SYSTEMTIME timsys1904; + timsys1904.wYear = 1904; + timsys1904.wMonth = 1; + timsys1904.wDayOfWeek = 6; + timsys1904.wDay = 1; + timsys1904.wHour = 0; + timsys1904.wMinute = 0; + timsys1904.wSecond = 0; + timsys1904.wMilliseconds = 0; + + FILETIME tim1904; + SystemTimeToFileTime(&timsys1904, &tim1904); + + // Now we have 2 64 bit unsigned ints that specifies time in terms of 100 nanosecond + // units. Subtract the two and we have the time between now and Jan 1 1904 in 100 nanosecond + // units. 1 s == 1e9 ns, so there are 1e7 filetime units in 1 second. + + unsigned __int64 tCurrent; + tCurrent = ((unsigned __int64)timCurrent.dwHighDateTime << 32) | timCurrent.dwLowDateTime; + unsigned __int64 t1904; + t1904 = ((unsigned __int64)tim1904.dwHighDateTime << 32) | tim1904.dwLowDateTime; + unsigned __int64 tDiff = tCurrent - t1904; + + // Divide by 1024*1024 so it fits in a dword + + dword dw = (dword)(tDiff >> 20); + + // Divide by 1e9 / (1024*1024) + + return (dword)((double)dw / (10000000.0 / (double)(1024 * 1024))); + +#else // TODO(darrinm): maybe this is fine on Windows too? +#include +dword GetCurrentTimePalmUnits() +{ + // Palm keeps time as a count of seconds from Jan 1, 1904. + struct tm tm1904; + tm1904.tm_year = 1904 - 1900; // tm_year 0=1900 + tm1904.tm_mon = 1; + tm1904.tm_wday = 6; + tm1904.tm_mday = 1; + tm1904.tm_yday = 0; + tm1904.tm_hour = 0; + tm1904.tm_min = 0; + tm1904.tm_sec = 0; + tm1904.tm_isdst = -1; + time_t time1904 = mktime(&tm1904); + time_t timeCurrent = time(NULL); + return (dword)difftime(timeCurrent, time1904); +} +#endif + +bool WriteDatabase(char *pszFnPdb, RecordChunk *arck, int cChunks, long *pcbFile) +{ + // Construct the new filename, which is same as the current with an extension of ".pdb" + + char szFnT[_MAX_PATH]; + _fullpath(szFnT, pszFnPdb, _MAX_PATH); + char szDrive[_MAX_DRIVE]; + char szDir[_MAX_DIR]; + char szFname[_MAX_FNAME]; + char szExt[_MAX_EXT]; + _splitpath(szFnT, szDrive, szDir, szFname, szExt); + + // Create pdb file + + FILE *pf = fopen(pszFnPdb, "wb"); + if (pf == NULL) { + printf("Unable to create \"%s\".\n", pszFnPdb); + return false; + } + + // Figure out the creator + + //dword dwCreator = (pszCreator[0] << 24) | (pszCreator[1] << 16) | (pszCreator[2] << 8) | pszCreator[3]; + // TODO(darrinm): huh? Is this right for WI? + dword dwCreator = 'Vexd'; + + // Init the .pdb header + + DatabaseHdrType hdr; + memset(&hdr, 0, sizeof(hdr)); + + // Use the filename as the database name + + strcpy(hdr.name, szFname); + if (strlen(hdr.name) > dmDBNameLength - 1) { + printf("Pdb filename too long.\n"); +Error: + fclose(pf); + _unlink(pszFnPdb); + return false; + } + + // Hardwire a bunch of stuff about this .pdb. + + hdr.attributes = SwapWord(0); + hdr.version = SwapWord(1); + dword dwDate = GetCurrentTimePalmUnits(); + hdr.creationDate = SwapDword(dwDate); + hdr.modificationDate = SwapDword(dwDate); + hdr.lastBackupDate = SwapDword(dwDate); + hdr.modificationNumber = SwapDword(1); + hdr.appInfoID = SwapDword(0); + hdr.sortInfoID = SwapDword(0); + hdr.type = SwapDword('DATA'); + hdr.creator = SwapDword(dwCreator); + hdr.uniqueIDSeed = SwapDword(cChunks + 1); + hdr.recordList.nextRecordListID = SwapDword(0); + hdr.recordList.numRecords = SwapWord((word)cChunks); + + // Write the header out + + int cbHdr = sizeof(hdr) - sizeof(hdr.recordList.firstEntry); + if (fwrite(&hdr, cbHdr, 1, pf) != 1) { + printf("Error writing database header.\n"); + goto Error; + } + + // Calculate where the actual records begin, which is after the record headers + + int offNext = cbHdr + cChunks * sizeof(RecordEntryType); + + // Write out the record headers. + + int n; + for (n = 0; n < cChunks; n++) { + RecordEntryType entry; + memset(&entry, 0, sizeof(entry)); + entry.localChunkID = SwapDword(offNext); + entry.attributes = 0; + dword id = n + 1; + entry.uniqueID[2] = (byte)(id & 0xff); + entry.uniqueID[1] = (byte)((id >> 8) & 0xff); + entry.uniqueID[0] = (byte)((id >> 16) & 0xff); + id++; + offNext += arck[n].pbMax - arck[n].pb; + if (fwrite(&entry, sizeof(entry), 1, pf) != 1) { + printf("Error writing database record header.\n"); + goto Error; + } + } + + // Write out the chunks + + for (n = 0; n < cChunks; n++) { + if (fwrite(arck[n].pb, arck[n].pbMax - arck[n].pb, 1, pf) != 1) { + printf("Error writing record data.\n"); + goto Error; + } + } + + *pcbFile = ftell(pf); + fclose(pf); + + return true; +} diff --git a/inicrunch/make.sh b/inicrunch/make.sh new file mode 100755 index 0000000..f26c7cd --- /dev/null +++ b/inicrunch/make.sh @@ -0,0 +1 @@ +gcc -lstdc++ main.cpp ini.cpp -o ../bin/inicrunch \ No newline at end of file diff --git a/legacy/AuthorKit/.cvsignore b/legacy/AuthorKit/.cvsignore new file mode 100644 index 0000000..8c38ced --- /dev/null +++ b/legacy/AuthorKit/.cvsignore @@ -0,0 +1 @@ +m.ini diff --git a/legacy/AuthorKit/AuthorKit.chm b/legacy/AuthorKit/AuthorKit.chm new file mode 100644 index 0000000..ea51258 Binary files /dev/null and b/legacy/AuthorKit/AuthorKit.chm differ diff --git a/legacy/AuthorKit/GobTemplates.ini.pp b/legacy/AuthorKit/GobTemplates.ini.pp new file mode 100644 index 0000000..78d370f --- /dev/null +++ b/legacy/AuthorKit/GobTemplates.ini.pp @@ -0,0 +1,456 @@ +#include + +// MoveRate (in wc/upd) - WCoord moved per update. MUST BE LESS THAN 64! +// FiringRate (in msecs) - delay between firings +// TimeToBuild (in secs) - amount of time required to build this structure/unit. MUST BE LESS THAN 1000! +// ArmorStrength (in hitpoints) - when this falls to 0 the structure/unit dies +// Infantry/Vehicle/StructureDamage (in hitpoints) - amount of hitpoints subtracted from target class when hit +// SightRange (in tiles) - distance the structure/unit can 'see' +// FiringRange (in tiles) - distance the structure/unit can fire +// Cost - # of credits consumed by creating this unit + +// Infantry Units + +[kgtShortRangeInfantry] +Name=Guard +LongName=Security Guard +Cost=150 +MoveRate=18 +ArmorStrength=39 +InfantryDamage=9 +VehicleDamage=3 +StructureDamage=3 +FiringRate=750 +FiringRange=2 +TimeToBuild=12 +UIBoundsLeft=-6 +UIBoundsTop=-7 +UIBoundsRight=7 +UIBoundsBottom=7 +SortOffset=5 +Animation=sri.anir +Description=Cheap and cheerful. Limited firepower and defensive capability. + +[kgtLongRangeInfantry] +Name=Trooper +LongName=Rocket Trooper +Cost=300 +MoveRate=11 +ArmorStrength=50 +InfantryDamage=6 +VehicleDamage=17 +StructureDamage=6 +FiringRate=1600 +FiringRange=3 +TimeToBuild=16 +UIBoundsLeft=-6 +UIBoundsTop=-7 +UIBoundsRight=7 +UIBoundsBottom=7 +SortOffset=5 +Animation=lri.anir +Description=For those hard to reach places. Rockets are particularly effective against armored vehicles. + +// Corporate Raider's StructureDamage and FiringRate are set to match kfxDamagePerSecMax +// so it will show 8 structure icons for its damage in the build form + +[kgtTakeoverSpecialist] +Name=Raider +LongName=Corporate Raider +Cost=550 +MoveRate=10 +ArmorStrength=50 +InfantryDamage=0 +VehicleDamage=0 +StructureDamage=16 +FiringRate=1000 +FiringRange=1 +TimeToBuild=16 +UIBoundsLeft=-6 +UIBoundsTop=-7 +UIBoundsRight=7 +UIBoundsBottom=7 +SortOffset=5 +Animation=spi.anir +Description=Send the Raider to take over ememy buildings. Keep in mind he has no conventional weapons and little armor! + +[kgtAndy] +Name=Andy +LongName=Andy +Cost=550 +MoveRate=22 +ArmorStrength=100 +InfantryDamage=56 +VehicleDamage=24 +StructureDamage=8 +FiringRate=2300 +FiringRange=4 +TimeToBuild=16 +UIBoundsLeft=-6 +UIBoundsTop=-7 +UIBoundsRight=7 +UIBoundsBottom=7 +SortOffset=5 +Animation=andy.anir +Description=Long range scout and sniper + +[kgtFox] +Name=Fox +LongName=Fox +Cost=550 +MoveRate=18 +ArmorStrength=80 +InfantryDamage=56 +VehicleDamage=24 +StructureDamage=8 +FiringRate=2300 +FiringRange=4 +TimeToBuild=16 +UIBoundsLeft=-6 +UIBoundsTop=-7 +UIBoundsRight=7 +UIBoundsBottom=7 +SortOffset=5 +Animation=andy.anir +Description=Bad guy + +// Mechanical Units + +[kgtMachineGunVehicle] +Name=Eagle +LongName=SR-98 Eagle +Cost=300 +MoveRate=24 +ArmorStrength=75 +InfantryDamage=8 +VehicleDamage=4 +StructureDamage=4 +FiringRate=1000 +FiringRange=3 +TimeToBuild=12 +UIBoundsLeft=-10 +UIBoundsTop=-10 +UIBoundsRight=10 +UIBoundsBottom=11 +SortOffset=7 +Animation=tmac.anir +Description=Fast but not particularly powerful or well armored. Makes a nice scout. + +[kgtLightTank] +Name=Broadsword +LongName=T-29 Broadsword +Cost=450 +MoveRate=16 +ArmorStrength=125 +InfantryDamage=6 +VehicleDamage=9 +StructureDamage=15 +FiringRate=1500 +FiringRange=3 +TimeToBuild=16 +UIBoundsLeft=-10 +UIBoundsTop=-10 +UIBoundsRight=10 +UIBoundsBottom=11 +SortOffset=7 +Animation=ltank.anir +Description=Equipped with a fine mortor cannon. Increased damage against buildings. + +[kgtRocketVehicle] +Name=Hydra +LongName=M-18 Hydra +Cost=450 +MoveRate=14 +ArmorStrength=100 +InfantryDamage=5 +VehicleDamage=20 +StructureDamage=8 +FiringRate=1250 +FiringRange=3 +TimeToBuild=16 +UIBoundsLeft=-10 +UIBoundsTop=-10 +UIBoundsRight=10 +UIBoundsBottom=11 +SortOffset=7 +Animation=troc.anir +Description=Well armored and equipped with high-explosive rockets that really pack a punch against vehicles. + +[kgtMediumTank] +Name=Liberator +LongName=T-33 Liberator +Cost=600 +MoveRate=15 +ArmorStrength=150 +InfantryDamage=16 +VehicleDamage=6 +StructureDamage=4 +FiringRate=1000 +FiringRange=3 +TimeToBuild=18 +UIBoundsLeft=-10 +UIBoundsTop=-10 +UIBoundsRight=10 +UIBoundsBottom=11 +SortOffset=7 +Animation=mtank.anir +Description=This tank's dual cannons can quickly do serious damage to enemy infantry. + +[kgtArtillery] +Name=Cyclops +LongName=A-3 Cyclops +Cost=500 +MoveRate=10 +ArmorStrength=50 +InfantryDamage=7 +VehicleDamage=24 +StructureDamage=48 +FiringRate=3000 +FiringRange=4 +TimeToBuild=16 +UIBoundsLeft=-12 +UIBoundsTop=-12 +UIBoundsRight=12 +UIBoundsBottom=13 +SortOffset=7 +Animation=artillery.anir +Description=Long range of fire. Slow moving. Packs a punch but is not accurate against smaller units. + +[kgtGalaxMiner] +Name=Bullpup +LongName=G-4 Bullpup +Cost=600 +MoveRate=14 +ArmorStrength=200 +FiringRate=0 +FiringRange=0 +TimeToBuild=13 +UIBoundsLeft=-10 +UIBoundsTop=-10 +UIBoundsRight=10 +UIBoundsBottom=11 +SortOffset=7 +Animation=miner.anir +Menu=kidfMinerMenu +Description=Increase your mining rate with additional Bullpups. It has no weapons but can take a lot of punishment. + +[kgtMobileHeadquarters] +Name=Dominion +LongName=H-7 Dominion +Cost=1200 +MoveRate=12 +ArmorStrength=200 +FiringRate=0 +FiringRange=0 +TimeToBuild=20 +UIBoundsLeft=-10 +UIBoundsTop=-10 +UIBoundsRight=10 +UIBoundsBottom=11 +SortOffset=7 +Animation=mobilehq.anir +Menu=kidfMobileHqMenu +Description=Not happy where you are? Start a new base at a remote location with this Mobile Headquarters. + +// Structures + +[kgtHumanResourceCenter] +Name=H.R.C. +LongName=Human Resource Center (HRC) +TimeToBuild=15 +Cost=1000 +PowerDemand=10 +ArmorStrength=250 +UIBoundsLeft=-1 +UIBoundsTop=-1 +UIBoundsRight=49 +UIBoundsBottom=33 +SortOffset=29 +Animation=HRC.anir +TileDimensions=3,2 +ReserveDimensions=3,3 +Menu=kidfHrcMenu +BuildRate=2 +UpgradeCost=750 +Description=Use this building to recruit new personnel. Upgrade the HRC to gain access to more advanced units. + +[kgtProcessor] +Name=Processor +LongName=Galaxite Processor +TimeToBuild=15 +Cost=1500 +PowerDemand=10 +ArmorStrength=350 +UIBoundsLeft=0 +UIBoundsTop=-1 +UIBoundsRight=48 +UIBoundsBottom=32 +SortOffset=32 +Animation=proc.anir +TileDimensions=3,2 +ReserveDimensions=3,3 +Description=Earth HQ provides credit for processed Galaxite. This building comes complete with one mining vehicle (Bullpup)! A Processor can store 3000 credits worth of Galaxite. + +[kgtReactor] +Name=Generator +LongName=Power Generator +TimeToBuild=10 +Cost=750 +PowerSupply=40 +ArmorStrength=200 +UIBoundsLeft=1 +UIBoundsTop=3 +UIBoundsRight=33 +UIBoundsBottom=31 +SortOffset=30 +Animation=Reactor.anir +TileDimensions=2,2 +Description=Most buildings will not operate without sufficient power. Keep an eye on your power supply and demand at all times. + +[kgtHeadquarters] +Name=H.Q. +LongName=Headquarters (HQ) +TimeToBuild=15 +Cost=750 +ArmorStrength=500 +UIBoundsLeft=0 +UIBoundsTop=0 +UIBoundsRight=48 +UIBoundsBottom=32 +SortOffset=30 +Animation=HQ.anir +TileDimensions=3,2 +Menu=kidfHqMenu +BuildRate=100 + +[kgtRadar] +Name=S.C. +LongName=Surveillance Center (SC) +TimeToBuild=15 +Cost=750 +PowerDemand=15 +ArmorStrength=250 +UIBoundsLeft=-1 +UIBoundsTop=-1 +UIBoundsRight=33 +UIBoundsBottom=33 +SortOffset=30 +Animation=Radar.anir +TileDimensions=2,2 +Description=A Surveillance Center provides guidance control for Gun and Rocket Towers and is required to build them. + +[kgtResearchCenter] +Name=R&D Center +LongName=Research & Development Center (R&D) +TimeToBuild=15 +Cost=750 +PowerDemand=15 +ArmorStrength=250 +UIBoundsLeft=-1 +UIBoundsTop=-1 +UIBoundsRight=33 +UIBoundsBottom=33 +SortOffset=30 +Animation=Research.anir +TileDimensions=2,2 +Menu=kidfResearchMenu +Description=Invest in an R&D Center and its scientists will work hard to produce technological advances to aid you in your operations. + +[kgtVehicleTransportStation] +Name=V.T.S. +LongName=Vehicle Transport Station (VTS) +TimeToBuild=15 +Cost=1250 +PowerDemand=10 +ArmorStrength=300 +UIBoundsLeft=-1 +UIBoundsTop=-1 +UIBoundsRight=49 +UIBoundsBottom=33 +SortOffset=30 +Animation=VTS.anir +TileDimensions=3,2 +ReserveDimensions=3,3 +Menu=kidfVtsMenu +BuildRate=10 +UpgradeCost=1000 +Description=Use this building to order new vehicles. Upgrade the VTS to gain access to more advanced units. + +[kgtWarehouse] +Name=Warehouse +LongName=Galaxite Storage Warehouse +TimeToBuild=15 +Cost=750 +PowerDemand=7 +ArmorStrength=200 +UIBoundsLeft=0 +UIBoundsTop=0 +UIBoundsRight=33 +UIBoundsBottom=33 +SortOffset=30 +Animation=Warehouse.anir +TileDimensions=2,2 +Description=Add Warehouses to increase processed Galaxite inventory capacity. Each Warehouse can store up to 5000 credits worth of Galaxite. + +[kgtReplicator] +Name=Replicator +LongName=Replicator +TimeToBuild=15 +Cost=1000 +PowerDemand=0 +ArmorStrength=300 +UIBoundsLeft=3 +UIBoundsTop=0 +UIBoundsRight=77 +UIBoundsBottom=56 +SortOffset=84 +Animation=replicator.anir +TileDimensions=5,4 +ReserveDimensions=5,4 +BuildRate=2 +UpgradeCost=400 +Description=You're not supposed to be able to build this! + +// Towers + +[kgtMachineGunTower] +Name=Gatling +LongName=Gatling Tower +TimeToBuild=10 +Cost=750 +PowerDemand=10 +ArmorStrength=150 +InfantryDamage=16 +VehicleDamage=5 +StructureDamage=6 +FiringRate=1333 +FiringRange=3 +UIBoundsLeft=0 +UIBoundsTop=0 +UIBoundsRight=16 +UIBoundsBottom=16 +SortOffset=30 +Animation=mtower.anir +TileDimensions=1,1 +Description=Defend your base from unwanted visits by the competition. Gatling Towers are particularly effective against troops. NOTE: a Surveillance Center is required to guide their automatic targeting systems. + +[kgtRocketTower] +Name=Rocket Twr +LongName=Rocket Tower +TimeToBuild=10 +Cost=850 +PowerDemand=10 +ArmorStrength=150 +InfantryDamage=6 +VehicleDamage=18 +StructureDamage=6 +FiringRate=1500 +FiringRange=3 +UIBoundsLeft=0 +UIBoundsTop=0 +UIBoundsRight=16 +UIBoundsBottom=16 +SortOffset=30 +Animation=rtower.anir +TileDimensions=1,1 +Description=Rocket Towers are particularly effective against armored vehicles. NOTE: a Surveillance Center is required to guide their automatic targeting systems. diff --git a/legacy/AuthorKit/TestLevel.ld b/legacy/AuthorKit/TestLevel.ld new file mode 100644 index 0000000..e35b608 Binary files /dev/null and b/legacy/AuthorKit/TestLevel.ld differ diff --git a/legacy/AuthorKit/art824/desert_8bpp.pal b/legacy/AuthorKit/art824/desert_8bpp.pal new file mode 100644 index 0000000..5ec4fc8 --- /dev/null +++ b/legacy/AuthorKit/art824/desert_8bpp.pal @@ -0,0 +1,259 @@ +JASC-PAL +0100 +256 +0 0 0 +252 252 252 +252 0 0 +0 252 0 +252 252 0 +0 192 196 +184 248 248 +52 96 108 +28 96 112 +36 68 76 +16 40 44 +104 252 252 +84 160 172 +56 120 132 +40 80 88 +28 56 60 +0 116 232 +0 96 196 +0 64 120 +0 48 92 +0 32 64 +232 32 0 +196 28 0 +120 8 0 +92 8 0 +64 8 0 +232 228 0 +196 192 0 +120 116 0 +92 88 0 +64 60 0 +156 88 140 +216 216 216 +168 168 168 +144 144 144 +132 128 128 +120 120 120 +112 112 112 +100 100 100 +96 96 96 +92 92 92 +84 84 84 +76 76 76 +72 72 72 +68 68 68 +64 64 64 +60 60 60 +52 52 48 +48 48 44 +24 24 24 +20 20 16 +8 8 8 +252 0 252 +4 12 24 +12 24 44 +32 68 76 +116 116 116 +156 156 188 +36 32 28 +40 36 32 +44 40 36 +104 104 104 +128 128 172 +0 60 64 +208 200 180 +0 116 120 +92 40 76 +84 80 88 +176 164 144 +188 176 152 +164 152 136 +160 148 128 +128 120 104 +224 160 12 +200 184 160 +144 132 112 +208 148 4 +136 124 108 +204 108 4 +148 136 120 +152 144 124 +248 236 36 +120 112 100 +108 96 84 +240 192 0 +100 92 80 +128 56 112 +252 244 140 +56 48 40 +92 84 72 +120 108 96 +112 104 92 +32 28 24 +204 136 0 +72 64 56 +84 76 68 +88 80 68 +60 56 52 +108 100 88 +80 72 64 +68 60 52 +68 64 56 +136 132 116 +96 88 76 +112 104 96 +48 44 40 +184 116 172 +72 68 64 +228 180 24 +56 52 48 +84 80 76 +212 144 200 +88 88 80 +244 212 0 +132 92 72 +76 72 64 +252 220 100 +52 48 44 +120 116 112 +252 224 100 +216 156 132 +240 172 232 +68 64 52 +192 104 0 +12 196 4 +12 136 4 +52 48 40 +28 96 20 +200 160 100 +200 4 4 +164 132 4 +172 108 68 +220 152 92 +220 136 76 +228 160 96 +212 132 72 +232 152 84 +228 140 76 +232 168 100 +196 128 80 +228 152 88 +220 144 84 +236 160 92 +212 140 84 +236 152 88 +232 144 80 +232 176 108 +196 120 72 +224 152 88 +220 144 80 +232 160 96 +212 136 80 +232 156 92 +224 148 80 +240 168 100 +204 132 80 +228 156 88 +220 148 88 +244 160 92 +212 144 88 +240 156 88 +236 148 80 +244 180 112 +8 0 0 +56 108 92 +64 36 24 +188 128 96 +40 32 36 +88 120 96 +88 44 28 +236 212 152 +40 16 12 +68 116 96 +64 80 84 +228 196 136 +48 68 72 +88 136 104 +112 64 44 +244 232 172 +24 16 8 +80 104 88 +68 48 40 +228 184 124 +48 40 44 +148 104 80 +88 64 48 +252 208 148 +40 28 20 +76 124 100 +64 92 84 +252 192 124 +56 76 80 +116 140 104 +136 84 56 +252 236 184 +24 8 4 +76 108 92 +68 44 32 +240 180 112 +56 32 28 +112 120 92 +88 56 44 +248 212 136 +40 24 20 +84 120 96 +68 84 84 +244 196 128 +56 68 80 +92 148 108 +112 76 60 +252 236 168 +32 16 12 +80 112 92 +68 64 64 +248 188 112 +48 60 72 +168 120 92 +92 76 64 +252 220 156 +52 28 20 +80 136 108 +72 96 88 +252 204 132 +56 84 80 +116 164 120 +152 92 64 +252 248 196 +16 8 4 +72 104 92 +68 40 28 +212 156 116 +48 32 28 +88 128 100 +88 52 36 +244 212 148 +40 20 16 +80 116 100 +64 84 84 +240 196 128 +52 68 76 +96 136 104 +112 72 48 +252 232 156 +28 16 20 +80 108 92 +76 48 36 +240 188 116 +56 40 32 +164 108 84 +100 64 44 +252 212 152 +44 28 20 +84 124 100 +68 96 88 +252 196 124 +60 76 84 diff --git a/legacy/AuthorKit/art824/fixed_8bpp.pal b/legacy/AuthorKit/art824/fixed_8bpp.pal new file mode 100644 index 0000000..f0b9c89 --- /dev/null +++ b/legacy/AuthorKit/art824/fixed_8bpp.pal @@ -0,0 +1,259 @@ +JASC-PAL +0100 +256 +0 0 0 +252 252 252 +252 0 0 +0 252 0 +252 252 0 +0 192 196 +184 248 248 +52 96 108 +28 96 112 +36 68 76 +16 40 44 +104 252 252 +84 160 172 +56 120 132 +40 80 88 +28 56 60 +0 116 232 +0 96 196 +0 64 120 +0 48 92 +0 32 64 +232 32 0 +196 28 0 +120 8 0 +92 8 0 +64 8 0 +232 228 0 +196 192 0 +120 116 0 +92 88 0 +64 60 0 +156 88 140 +216 216 216 +168 168 168 +144 144 144 +132 128 128 +120 120 120 +112 112 112 +100 100 100 +96 96 96 +92 92 92 +84 84 84 +76 76 76 +72 72 72 +68 68 68 +64 64 64 +60 60 60 +52 52 48 +48 48 44 +24 24 24 +20 20 16 +8 8 8 +252 0 252 +4 12 24 +12 24 44 +32 68 76 +116 116 116 +156 156 188 +36 32 28 +40 36 32 +44 40 36 +104 104 104 +128 128 172 +0 60 64 +208 200 180 +0 116 120 +92 40 76 +84 80 88 +176 164 144 +188 176 152 +164 152 136 +160 148 128 +128 120 104 +224 160 12 +200 184 160 +144 132 112 +208 148 4 +136 124 108 +204 108 4 +148 136 120 +152 144 124 +248 236 36 +120 112 100 +108 96 84 +240 192 0 +100 92 80 +128 56 112 +252 244 140 +56 48 40 +92 84 72 +120 108 96 +112 104 92 +32 28 24 +204 136 0 +72 64 56 +84 76 68 +88 80 68 +60 56 52 +108 100 88 +80 72 64 +68 60 52 +68 64 56 +136 132 116 +96 88 76 +112 104 96 +48 44 40 +184 116 172 +72 68 64 +228 180 24 +56 52 48 +84 80 76 +212 144 200 +88 88 80 +244 212 0 +132 92 72 +76 72 64 +252 220 100 +52 48 44 +120 116 112 +252 224 100 +216 156 132 +240 172 232 +68 64 52 +192 104 0 +12 196 4 +12 136 4 +52 48 40 +28 96 20 +200 160 100 +200 4 4 +164 132 4 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 diff --git a/legacy/AuthorKit/art824/grassy_8bpp.pal b/legacy/AuthorKit/art824/grassy_8bpp.pal new file mode 100644 index 0000000..613c14e --- /dev/null +++ b/legacy/AuthorKit/art824/grassy_8bpp.pal @@ -0,0 +1,259 @@ +JASC-PAL +0100 +256 +0 0 0 +252 252 252 +252 0 0 +0 252 0 +252 252 0 +0 192 196 +184 248 248 +52 96 108 +28 96 112 +36 68 76 +16 40 44 +104 252 252 +84 160 172 +56 120 132 +40 80 88 +28 56 60 +0 116 232 +0 96 196 +0 64 120 +0 48 92 +0 32 64 +232 32 0 +196 28 0 +120 8 0 +92 8 0 +64 8 0 +232 228 0 +196 192 0 +120 116 0 +92 88 0 +64 60 0 +156 88 140 +216 216 216 +168 168 168 +144 144 144 +132 128 128 +120 120 120 +112 112 112 +100 100 100 +96 96 96 +92 92 92 +84 84 84 +76 76 76 +72 72 72 +68 68 68 +64 64 64 +60 60 60 +52 52 48 +48 48 44 +24 24 24 +20 20 16 +8 8 8 +252 0 252 +4 12 24 +12 24 44 +32 68 76 +116 116 116 +156 156 188 +36 32 28 +40 36 32 +44 40 36 +104 104 104 +128 128 172 +0 60 64 +208 200 180 +0 116 120 +92 40 76 +84 80 88 +176 164 144 +188 176 152 +164 152 136 +160 148 128 +128 120 104 +224 160 12 +200 184 160 +144 132 112 +208 148 4 +136 124 108 +204 108 4 +148 136 120 +152 144 124 +248 236 36 +120 112 100 +108 96 84 +240 192 0 +100 92 80 +128 56 112 +252 244 140 +56 48 40 +92 84 72 +120 108 96 +112 104 92 +32 28 24 +204 136 0 +72 64 56 +84 76 68 +88 80 68 +60 56 52 +108 100 88 +80 72 64 +68 60 52 +68 64 56 +136 132 116 +96 88 76 +112 104 96 +48 44 40 +184 116 172 +72 68 64 +228 180 24 +56 52 48 +84 80 76 +212 144 200 +88 88 80 +244 212 0 +132 92 72 +76 72 64 +252 220 100 +52 48 44 +120 116 112 +252 224 100 +216 156 132 +240 172 232 +68 64 52 +192 104 0 +12 196 4 +12 136 4 +52 48 40 +28 96 20 +92 120 28 +136 180 44 +124 164 40 +148 192 44 +116 152 36 +144 184 44 +132 168 40 +156 200 44 +108 144 36 +140 184 40 +128 168 44 +156 196 44 +124 156 36 +144 188 44 +132 176 44 +164 208 44 +104 132 32 +140 180 44 +128 164 40 +152 192 48 +120 156 36 +148 184 44 +132 172 44 +160 204 44 +116 144 36 +144 184 40 +128 172 40 +156 200 44 +124 160 40 +152 188 44 +140 176 44 +172 216 44 +20 28 20 +80 148 28 +60 76 60 +196 160 148 +40 76 60 +144 156 60 +124 112 76 +216 216 156 +36 68 52 +92 160 36 +68 124 40 +208 200 136 +44 84 60 +148 164 60 +132 108 96 +248 216 192 +28 56 24 +132 144 52 +64 112 44 +204 184 140 +48 76 68 +176 144 132 +120 136 48 +240 216 188 +44 68 64 +96 168 36 +112 120 40 +232 196 172 +48 88 60 +160 180 72 +152 124 112 +248 244 208 +40 36 56 +124 148 48 +72 100 48 +208 172 156 +44 76 64 +164 148 108 +120 132 48 +228 232 192 +40 68 60 +140 156 52 +92 132 48 +224 200 172 +52 84 64 +180 164 112 +132 140 52 +248 236 208 +44 56 60 +136 148 52 +108 116 44 +220 184 164 +52 76 68 +188 152 140 +124 140 48 +236 236 208 +48 72 68 +128 172 64 +116 128 44 +240 204 180 +52 104 40 +176 196 100 +164 136 124 +248 248 216 +24 40 12 +96 148 44 +64 92 52 +200 168 144 +44 76 64 +148 160 60 +120 132 44 +232 216 184 +40 68 60 +120 156 56 +72 136 32 +216 200 160 +48 84 64 +152 168 60 +144 120 108 +248 228 196 +36 60 48 +136 144 56 +96 112 40 +212 180 156 +48 80 72 +180 152 128 +124 136 48 +240 224 192 +52 64 72 +100 184 40 +116 124 40 +232 204 176 +52 88 64 +180 180 100 +156 132 112 +252 244 220 diff --git a/legacy/AuthorKit/art824/startup_8bpp.pal b/legacy/AuthorKit/art824/startup_8bpp.pal new file mode 100644 index 0000000..11d2e76 --- /dev/null +++ b/legacy/AuthorKit/art824/startup_8bpp.pal @@ -0,0 +1,259 @@ +JASC-PAL +0100 +256 +0 0 0 +252 252 252 +252 0 0 +0 252 0 +252 252 0 +0 192 196 +184 248 248 +52 96 108 +28 96 112 +36 68 76 +16 40 44 +104 252 252 +84 160 172 +56 120 132 +40 80 88 +28 56 60 +0 116 232 +0 96 196 +0 64 120 +0 48 92 +0 32 64 +232 32 0 +196 28 0 +120 8 0 +92 8 0 +64 8 0 +232 228 0 +196 192 0 +120 116 0 +92 88 0 +64 60 0 +156 88 140 +216 216 216 +168 168 168 +144 144 144 +132 128 128 +120 120 120 +112 112 112 +100 100 100 +96 96 96 +92 92 92 +84 84 84 +76 76 76 +72 72 72 +68 68 68 +64 64 64 +60 60 60 +52 52 48 +48 48 44 +24 24 24 +20 20 16 +8 8 8 +252 0 252 +176 164 144 +208 200 180 +188 176 152 +164 152 136 +160 148 128 +128 120 104 +224 160 12 +200 184 160 +144 132 112 +40 36 32 +208 148 4 +136 124 108 +204 108 4 +148 136 120 +152 144 124 +248 236 36 +120 112 100 +108 96 84 +240 192 0 +100 92 80 +36 32 28 +128 56 112 +252 244 140 +56 48 40 +92 84 72 +120 108 96 +44 40 36 +112 104 92 +104 104 104 +32 28 24 +204 136 0 +72 64 56 +156 156 188 +84 76 68 +60 56 52 +88 80 68 +108 100 88 +80 72 64 +92 40 76 +68 60 52 +68 64 56 +136 132 116 +96 88 76 +112 104 96 +48 44 40 +184 116 172 +72 68 64 +228 180 24 +56 52 48 +84 80 76 +212 144 200 +88 88 80 +244 212 0 +132 92 72 +12 24 44 +76 72 64 +52 48 44 +252 220 100 +120 116 112 +4 12 24 +252 224 100 +216 156 132 +240 172 232 +68 64 52 +192 104 0 +12 196 4 +12 136 4 +52 48 40 +28 96 20 +0 116 120 +200 160 100 +200 4 4 +164 132 4 +0 60 64 +188 164 140 +188 160 132 +196 180 164 +4 4 4 +200 188 176 +180 204 208 +136 152 152 +168 188 188 +120 132 120 +188 172 148 +152 168 164 +168 196 200 +152 164 152 +180 196 192 +188 212 216 +120 136 136 +200 196 192 +108 120 104 +164 180 184 +104 120 120 +168 180 168 +160 188 196 +136 148 136 +184 172 156 +88 108 108 +136 148 120 +168 172 120 +168 180 152 +152 164 136 +120 144 148 +168 200 212 +120 152 168 +152 180 188 +148 156 148 +156 164 120 +132 140 120 +148 156 120 +152 172 180 +148 152 136 +12 20 12 +80 100 100 +188 164 148 +204 232 240 +164 168 152 +120 132 104 +100 132 136 +184 184 180 +176 156 136 +184 180 168 +164 168 136 +180 164 136 +168 180 132 +136 164 168 +8 20 20 +132 140 132 +180 188 128 +132 156 164 +180 196 172 +56 68 64 +164 172 164 +136 168 180 +180 188 152 +68 84 80 +180 180 124 +12 12 12 +72 92 92 +88 116 128 +152 152 104 +4 4 12 +172 152 120 +4 12 12 +196 220 224 +132 140 104 +132 140 148 +184 220 232 +40 52 48 +24 36 32 +136 148 104 +12 28 36 +188 188 124 +4 12 20 +152 180 168 +76 136 172 +184 172 164 +16 28 24 +172 212 228 +104 108 88 +20 40 56 +136 180 196 +24 56 76 +40 80 108 +104 172 204 +148 192 204 +148 208 232 +164 172 180 +196 172 164 +120 164 180 +192 196 140 +172 164 116 +196 200 160 +148 140 132 +124 192 220 +224 220 188 +192 172 136 +180 172 144 +148 140 104 +180 172 124 +148 140 120 +128 168 152 +20 20 4 +220 200 152 +20 12 4 +92 48 4 +132 68 4 +12 12 4 +188 124 36 +48 36 8 +4 12 4 +12 4 4 +156 108 44 +200 176 124 +136 180 172 +184 140 72 +100 84 60 +216 148 52 +108 76 32 +255 0 255 +255 0 255 +255 0 255 diff --git a/legacy/AuthorKit/desert.tc b/legacy/AuthorKit/desert.tc new file mode 100644 index 0000000..6879641 Binary files /dev/null and b/legacy/AuthorKit/desert.tc differ diff --git a/legacy/AuthorKit/desertNames.txt b/legacy/AuthorKit/desertNames.txt new file mode 100644 index 0000000..46ec3f2 --- /dev/null +++ b/legacy/AuthorKit/desertNames.txt @@ -0,0 +1,93 @@ +road01 +road02 +road03 +road04 +road05 +road06 +road07 +road08 +roadintersection +roadnorthsouth +roadeastwest +road12 +road13 +road14 +road15 +road16 +road17 +road18 +road19 +road20 +road21 +road22 +road24 +road25 +road26 +road27 +road28 +road29 +Background +river01 +river02 +river03 +river04 +river05 +pond01 +river07 +river08 +river09 +river10 +river11 +river12 +river13 +river14 +river15 +river16 +river17 +river18 +river19 +river20 +river21 +river22 +rock01 +cliff01 +cliff02 +cliff03 +cliff04 +cliff05 +cliff06 +cliff07 +cliff08 +cliff09 +cliff10 +cliff11 +cliff12 +cliff13 +cliff14 +cliff15 +cliff16 +cliff17 +cliff18 +rock02 +rock03 +cliff21 +CliffSwtop +cliffSWbottom +riverNEcrossing +riverNWcrossing +cliffSEbottom +cliffSEtop +CliffNEtop +CliffNEbottom +CliffNWbottom +CliffNWtop +crater +spike +oblisk +monolith +monolith1 +duplicator +excavation +rcliff01 +rcliff02 +rcliff03 diff --git a/legacy/AuthorKit/desertTemplates.png b/legacy/AuthorKit/desertTemplates.png new file mode 100644 index 0000000..2e77b3d Binary files /dev/null and b/legacy/AuthorKit/desertTemplates.png differ diff --git a/legacy/AuthorKit/desertTemplates_32.png b/legacy/AuthorKit/desertTemplates_32.png new file mode 100755 index 0000000..9873360 Binary files /dev/null and b/legacy/AuthorKit/desertTemplates_32.png differ diff --git a/legacy/AuthorKit/desertTerrain.png b/legacy/AuthorKit/desertTerrain.png new file mode 100644 index 0000000..5955b07 Binary files /dev/null and b/legacy/AuthorKit/desertTerrain.png differ diff --git a/legacy/AuthorKit/desertTerrain_32.png b/legacy/AuthorKit/desertTerrain_32.png new file mode 100755 index 0000000..98d0424 Binary files /dev/null and b/legacy/AuthorKit/desertTerrain_32.png differ diff --git a/legacy/AuthorKit/desert_32.tc b/legacy/AuthorKit/desert_32.tc new file mode 100755 index 0000000..300c4e3 Binary files /dev/null and b/legacy/AuthorKit/desert_32.tc differ diff --git a/legacy/AuthorKit/desert_test.ld b/legacy/AuthorKit/desert_test.ld new file mode 100644 index 0000000..01ed4e4 Binary files /dev/null and b/legacy/AuthorKit/desert_test.ld differ diff --git a/legacy/AuthorKit/exp24to32.py b/legacy/AuthorKit/exp24to32.py new file mode 100644 index 0000000..e2af034 --- /dev/null +++ b/legacy/AuthorKit/exp24to32.py @@ -0,0 +1,29 @@ +import sys +import Image + +if __name__ == "__main__": + i24 = Image.open(sys.argv[1]).convert('RGB') + width_24 = i24.size[0] + height_24 = i24.size[1] + print 'old size: %d, %d' % (width_24, height_24) + + width_32 = width_24 / 3 * 4 + height_32 = height_24 / 3 * 4 + print 'new size: %d, %d' % (width_32, height_32) + + i32 = Image.new('RGB', (width_32, height_32)) + + x24 = 0 + y24 = 0 + for y in xrange(height_24): + for x in xrange(0, width_24, 3): + avg = (0, 0, 0) + for j in xrange(3): + p = i24.getpixel((x+j, y)) + i32.putpixel((x24+j, y24), p) + avg = (avg[0] + p[0], avg[1] + p[1], avg[2] + p[2]) + i32.putpixel((x24+3, y24), (int(avg[0] / 3), int(avg[1] / 3), int(avg[2] / 3))) + x24 += 4 + x24 = 0 + y24 += 1 + i32.save('expanded_32.png') diff --git a/legacy/AuthorKit/extrAll.bat b/legacy/AuthorKit/extrAll.bat new file mode 100644 index 0000000..f840590 --- /dev/null +++ b/legacy/AuthorKit/extrAll.bat @@ -0,0 +1,6 @@ +@echo off +set TS=%1 +if "%TS%" == "" set TS=24 +echo Tile size: %TS% +TemplateExtractor -ts %TS% -n grassyNames.txt -tc grassy.tc -art grassyTemplates.png -ter grassyTerrain.png +TemplateExtractor -ts %TS% -n desertNames.txt -tc desert.tc -art desertTemplates.png -ter desertTerrain.png diff --git a/legacy/AuthorKit/extrDesert.bat b/legacy/AuthorKit/extrDesert.bat new file mode 100644 index 0000000..bb33e4c --- /dev/null +++ b/legacy/AuthorKit/extrDesert.bat @@ -0,0 +1,5 @@ +@echo off +set TS=%1 +if "%TS%" == "" set TS=24 +echo Tile size: %TS% +TemplateExtractor -ts %TS% -n desertNames.txt -tc desert.tc -art desertTemplates.png -ter desertTerrain.png diff --git a/legacy/AuthorKit/extrGrassy.bat b/legacy/AuthorKit/extrGrassy.bat new file mode 100644 index 0000000..5f3f52d --- /dev/null +++ b/legacy/AuthorKit/extrGrassy.bat @@ -0,0 +1,5 @@ +@echo off +set TS=%1 +if "%TS%" == "" set TS=24 +echo Tile size: %TS% +TemplateExtractor -ts %TS% -n grassyNames.txt -tc grassy.tc -art grassyTemplates.png -ter grassyTerrain.png diff --git a/legacy/AuthorKit/fixed_8bpp.pal b/legacy/AuthorKit/fixed_8bpp.pal new file mode 100644 index 0000000..f0b9c89 --- /dev/null +++ b/legacy/AuthorKit/fixed_8bpp.pal @@ -0,0 +1,259 @@ +JASC-PAL +0100 +256 +0 0 0 +252 252 252 +252 0 0 +0 252 0 +252 252 0 +0 192 196 +184 248 248 +52 96 108 +28 96 112 +36 68 76 +16 40 44 +104 252 252 +84 160 172 +56 120 132 +40 80 88 +28 56 60 +0 116 232 +0 96 196 +0 64 120 +0 48 92 +0 32 64 +232 32 0 +196 28 0 +120 8 0 +92 8 0 +64 8 0 +232 228 0 +196 192 0 +120 116 0 +92 88 0 +64 60 0 +156 88 140 +216 216 216 +168 168 168 +144 144 144 +132 128 128 +120 120 120 +112 112 112 +100 100 100 +96 96 96 +92 92 92 +84 84 84 +76 76 76 +72 72 72 +68 68 68 +64 64 64 +60 60 60 +52 52 48 +48 48 44 +24 24 24 +20 20 16 +8 8 8 +252 0 252 +4 12 24 +12 24 44 +32 68 76 +116 116 116 +156 156 188 +36 32 28 +40 36 32 +44 40 36 +104 104 104 +128 128 172 +0 60 64 +208 200 180 +0 116 120 +92 40 76 +84 80 88 +176 164 144 +188 176 152 +164 152 136 +160 148 128 +128 120 104 +224 160 12 +200 184 160 +144 132 112 +208 148 4 +136 124 108 +204 108 4 +148 136 120 +152 144 124 +248 236 36 +120 112 100 +108 96 84 +240 192 0 +100 92 80 +128 56 112 +252 244 140 +56 48 40 +92 84 72 +120 108 96 +112 104 92 +32 28 24 +204 136 0 +72 64 56 +84 76 68 +88 80 68 +60 56 52 +108 100 88 +80 72 64 +68 60 52 +68 64 56 +136 132 116 +96 88 76 +112 104 96 +48 44 40 +184 116 172 +72 68 64 +228 180 24 +56 52 48 +84 80 76 +212 144 200 +88 88 80 +244 212 0 +132 92 72 +76 72 64 +252 220 100 +52 48 44 +120 116 112 +252 224 100 +216 156 132 +240 172 232 +68 64 52 +192 104 0 +12 196 4 +12 136 4 +52 48 40 +28 96 20 +200 160 100 +200 4 4 +164 132 4 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 +255 0 255 diff --git a/legacy/AuthorKit/grassy.tc b/legacy/AuthorKit/grassy.tc new file mode 100644 index 0000000..0ab29a0 Binary files /dev/null and b/legacy/AuthorKit/grassy.tc differ diff --git a/legacy/AuthorKit/grassyColors.png b/legacy/AuthorKit/grassyColors.png new file mode 100644 index 0000000..8af87b2 Binary files /dev/null and b/legacy/AuthorKit/grassyColors.png differ diff --git a/legacy/AuthorKit/grassyNames.txt b/legacy/AuthorKit/grassyNames.txt new file mode 100644 index 0000000..4cf4435 --- /dev/null +++ b/legacy/AuthorKit/grassyNames.txt @@ -0,0 +1,81 @@ +road01 +road02 +road03 +road04 +road05 +road06 +road07 +road08 +roadintersection +road10 +road11 +road12 +road13 +road14 +road15 +roadeastwest +road17 +road18 +road19 +road20 +road21 +road22 +Background +road24 +road25 +road26 +road27 +roadnorthsouth +road29 +road30 +river01 +river02 +river03 +riverNEcrossing +river04 +river05 +river06 +river07 +river08 +river09 +pond01 +pond02 +river10 +river11 +riverSEcrossing +cliff01 +cliff02 +cliff03 +cliff04 +cliff05 +cliff06 +cliff07 +cliff08 +cliff09 +cliff10 +cliff11 +cliff12 +cliff13 +cliff14 +cliff15 +cliff16 +cliff17 +cliff18 +cliff19 +cliff20 +cliff21 +cliff22 +cliff23 +cliff24 +cliffeasttop +cliffeastbottom +cliffnorthleft +cliffnorthright +cliffsouthleft +cliffsouthright +cliffwestbottom +cliffwesttop +monolith +rcliff01 +rcliff02 +rcliff03 diff --git a/legacy/AuthorKit/grassyTemplates.png b/legacy/AuthorKit/grassyTemplates.png new file mode 100644 index 0000000..e116afa Binary files /dev/null and b/legacy/AuthorKit/grassyTemplates.png differ diff --git a/legacy/AuthorKit/grassyTemplates_32.png b/legacy/AuthorKit/grassyTemplates_32.png new file mode 100755 index 0000000..e5aec7a Binary files /dev/null and b/legacy/AuthorKit/grassyTemplates_32.png differ diff --git a/legacy/AuthorKit/grassyTerrain.png b/legacy/AuthorKit/grassyTerrain.png new file mode 100644 index 0000000..643fcee Binary files /dev/null and b/legacy/AuthorKit/grassyTerrain.png differ diff --git a/legacy/AuthorKit/grassyTerrain_32.png b/legacy/AuthorKit/grassyTerrain_32.png new file mode 100755 index 0000000..b46be5c Binary files /dev/null and b/legacy/AuthorKit/grassyTerrain_32.png differ diff --git a/legacy/AuthorKit/grassy_32.tc b/legacy/AuthorKit/grassy_32.tc new file mode 100755 index 0000000..7d6a1eb Binary files /dev/null and b/legacy/AuthorKit/grassy_32.tc differ diff --git a/legacy/AuthorKit/grassy_test.ld b/legacy/AuthorKit/grassy_test.ld new file mode 100644 index 0000000..4aa51a4 Binary files /dev/null and b/legacy/AuthorKit/grassy_test.ld differ diff --git a/legacy/AuthorKit/htdata816.pdb b/legacy/AuthorKit/htdata816.pdb new file mode 100644 index 0000000..2b5220d Binary files /dev/null and b/legacy/AuthorKit/htdata816.pdb differ diff --git a/legacy/AuthorKit/htdata824.pdb b/legacy/AuthorKit/htdata824.pdb new file mode 100644 index 0000000..b9b470e Binary files /dev/null and b/legacy/AuthorKit/htdata824.pdb differ diff --git a/legacy/AuthorKit/htsfx.pdb b/legacy/AuthorKit/htsfx.pdb new file mode 100644 index 0000000..00a55ec Binary files /dev/null and b/legacy/AuthorKit/htsfx.pdb differ diff --git a/legacy/AuthorKit/makeAll.bat b/legacy/AuthorKit/makeAll.bat new file mode 100644 index 0000000..c9c9c6c --- /dev/null +++ b/legacy/AuthorKit/makeAll.bat @@ -0,0 +1,16 @@ +@echo off + +set TS=%1 +if "%TS%" == "" set TS=24 +echo Input Tile size: %TS% +echo --- Extracting templates --- +TemplateExtractor -ts %TS% -n grassyNames.txt -tc grassy.tc -art grassyTemplates.png +echo. +echo --- Building 16-pixel tile .pdb --- +call updGrassy16 +echo. +echo --- Building 24-pixel tile .pdb --- +call updGrassy24 +echo. +echo --- Building 32-pixel tile .pdb --- +call updGrassy32 diff --git a/legacy/AuthorKit/missiontext.rtf b/legacy/AuthorKit/missiontext.rtf new file mode 100644 index 0000000..9d5624b --- /dev/null +++ b/legacy/AuthorKit/missiontext.rtf @@ -0,0 +1,210 @@ +{\rtf1\ansi\ansicpg1252\deff0{\fonttbl{\f0\fswiss\fcharset0 Arial;}} +{\colortbl ;\red0\green0\blue128;} +{\*\generator Msftedit 5.41.15.1503;}\viewkind4\uc1\pard\lang1033\f0\fs20\par +\b\par +Mission 1\b0\par +\b "Core Competencies"\par +\b0\par +\cf1\i Mission Setup.\par +In which, Andy (Player) is instructed on the basics of the game. Building, Mining Galaxite, Scouting, Use of Mini map, Attacking. Its revealed that OMNI inc have arrived on Icarus sometime before ACME. Jana (Andy's Sister) has been planetside as part of an advanced scouting party. Andy is commander one of several ACME Galaxite Mining bases, it's his first field experience. The ACME colonoy ship Persephone is in high orbit above Icarus where Olstrom (Andy and Jana's Divisional Vice President) is. Olstrom knows Andy as a rising star of his division, but his tone in the early part of the game should reflect the distance in senority between the low level Andy and the Executive Olstrom. As the missions progress and Andy's rank increases it would be good to have the tone change appropriatly, so that by the end of the game its clear Andy is Olstrom's #1 guy. \par +\par +Notes: Key concepts.\par +It's important people understand what a Dominion is for Mission 2. \par +It's important people understand how to deploy a Dominion and where to 'go' to order additional structures. \par +It's important people understand the mini map\par +Its important people understand the importance of 'power'\par +Its important people understand the need to Store Galaxite \par +\cf0\i0\par +\par +@OAndy, I'm deploying you to a safe galaxite rich area on the Surface of Icarus. Jana will instruct you on setting up a galaxite mining base with our new equipment. Collect 3500.00 Credits worth of galaxite.\par +@JHey Andy, On your Remote Control Display you should see a birds-eye view of your 'Dominion', a mobile Headquarters or (MHQ). Dark areas of the map will be revealed to you as your units explore them.\par +@JSelect any unit by tapping on it and then tap again where you want it to go. For practice, move the MHQ a little distance north.\par +@JNow you can direct the MHQ to transform into a HQ. This building is important as it enables the construction of other types of buildings and units. Tap and hold the pen on the MHQ then Select Transform from the popup menu.\par +@JGood! Notice the power indicator at the upper-left of the display. HQs have just enough power for their own operation. To support other buildings you're going to need more power. Notice in the upper left of the display the credits indicator, this is how much money you have. You've enough credits to construct a Power Generator.\par +@JTo order new buildings, tap and hold on the HQ and select build from the popup menu. Then tap on Power Generator and the build button.\par +@JPosition the Power Generator by tapping on the placement indicator and dragging it to where you want it. If the location is valid a check will appear. Tap the check to finalize placement.\par +@JExcellent! Plenty of power. Now you're ready to order a Galaxite Processor and begin mining! Tap-hold on the HQ and follow the same procedure as for the Power Generator, this time ordering a Galaxite Processor.\par +@JEach Galaxite Processor comes with a G-4 Bullpup mining vehicle which will gather Galaxite and deliver it to the Processor for processing. Credits accrue as Galaxite is processed. You can direct the Bullpup to a particular patch of Galaxite by tapping it and tapping on the desired Galaxite.\par +@JLet's mine 3500 Credits worth of Galaxite to be sure all the kinks are worked out of the system.\par +@JLooks like you've consumed all the nearby Galaxite. Explore the map with your units to locate more. In the bottom right of the screen you will see a mini map, the white rectangle represents your current view. You can move the rectangle to adjust where you want to look on the map, Try it.\par +@JYour Galaxite Processor is full but you haven't reached your 3500 Credit quota yet. Use the HQ to build a Warehouse to store more Galaxite.\par +@ODammit Andy! You don't even have your HQ established yet? Maybe this is too much responsibility for you.\par +@OUnbelievable! You managed to sabatoge the simplest of missions. We can't afford to let this happen again. You're fired!\par +@O Andy, Urgent news, long range scans from up here in orbit reveal an unkonwn force not far from your location, moving toward you. Build a human resource center (HRC) some security gaurds and proceed to check it out. The unknown force is North East of your current base, this doesnt look good. \par +@JWhilst we are waiting for the miner to collect, you should explore your surroundings a bit more. In the bottom right of the screen you will see a mini map, the white rectangle represents your current view. You can move the rectangle to adjust where you want to look on the map, Try it.\par +@AIve detected a convoy of OMNI Corp units down here, what should I do?\par +@OOh no, I dont belive it, OMNI got here before us, Icarus was supposed to be our secret. Andy..we cant let them get a foothold here on Icarus, build several security gaurds at your HRC and wipe the OMNI forces out quickly.\par +@J Your security gaurds, and Eagle scout vehicles can be used to attack enemies. Simply select your units and click on the enemy you want to attack. \par +@OExcellent, good job getting the galaxite, and it looks like you know how to handle yourself in combat too. Omni being here, changes everything. lets redeploy you closer to Jana's base. \par +\par +\par +\b Mission 2\b0\par +\b "Touching Base"\b0\par +\cf1\i\par +In which, Andy attempts to escort a Dominion to Jana's position. (We eventually get there in Mission 4). Olstrom who seems concerned about wasting time, does'nt really want you to deploy the domion, and wants you to avoid combat if possible. The mission can be finsihed in 3 ways, not deploying the dominion involves taking out the power generators that power the towers, this can be done from the north or South of the map, though South is easier. Jana beckons you to help her, currently which should be omitted in the final pass. \cf0\i0\par +\par +@O Long range scans reveal more OMNI forces in this area, try to avoid a fight and escort this Dominion mobile base to Jana's location just beyond the east end of this Canyon. If you run into combat then deploy the base and wipe out the OMNI forces quickly, and send some units east.\par +\par +@J Please hurry, OMNI forces are deploying nearby and I fear they are about to attack.\par +\par +@O I see you have deployed the base, thats unfortunate but probably unavoidable. Create a force and get some of the units to the East end of the canyon ASAP\par +\par +@O Good job getting here, now escort the dominion to this location.\par +\par +@O Amazing job, getting through unscathed, your quite the strategist. Nice work.\par +\par +\par +\b Mission 3\par +"Action Learning"\b0\par +\par +@O More reports are coming in more Omni forces are reported in the area blocking our path to Jana's base . Build a stike force and wipe them out. There is a rich seam of galaxite in this region, after destroying them mine 5000 credits of Galaxite. I'll be considering you for a promotion if you can accomplish this quickly. \par +\par +@O Complete failure, they where pretty aggresive I'll give you that, but your performance in this mission was less than satisfactory. I've seen you can do better than this!\par +\par +@O Incredible, your fast becoming the best mining specialist, ACME has seen in a good long while, Hell you even remind me a bit of myself in my youth. I'm promiting you two levels and giving you a massive number of stock options. Under my guidance you will go far!.\par +\par +@O Pretty impresive, I'm starting to belive you might have the right stuff to make it in this company. I'm giving you a single level increase and some stock options. Keep up the good work\par +\par +@O Ok, that wasnt the fastest I've ever seen, nor the slowest but there is certainly some room for improvement in your combat strategies. I'll consider you for a promotion next time.\par +\par +@O Yes, well you've got some improvments to make, but on the whole that was satisfactory. You need to be slightly more aggressive in offense and your resource colelction was a little on the cautious side. No promotion for you unless we see some improvement.\par +\par +\par +\b Mission 4\par +"Point of Contact"\par +\b0\par +@O Jana is surrounded and under siege from a sizable OMNI force, she has lost her HQ and won't last long without your help. Your orders, break through to her position, deploy your dominion inside the walled base and wipe out all the surrounding OMNI forces. Good Luck!\par +\par +@J Thankyou brother, we couldnt have held out much longer. We've discovered an Alien Monolith down here, and are attempting to decipher the glyphs on its surface to find out more about the civilization that created it. Build up the base and wipe out the OMNI forces surrounding us so I can continue the research.\par +\par +@OThanks to your incompetence, weve lost our foothold on Icarus, all is lost.\par +\par +@OExcellent, we finally have a good foothold on Icarus, thanks to you. Great work. I'll let Jana brief you on the nature of the Monolith weve discovered.\par +\par +@J Andy, the runes on this monolith, tell of a civilization of aliens that inhabited this place many eons ago. The runes reference all sorts of things that our translators can make no sense of, but one thing that seems clear, is that the monolith was designed to warn whomever found it about the danger of a weapon somehow built into this planet. A fascinating find, My team and I are going to take a shuttle and investigate further.\par +\par +\par +\b Mission 5\par +"Personal Goals"\b0\par +@O Andy, Bad news.. Jana's shuttle has been shot down close to a large OMNI base, there is no contact with the shuttle. I'm deploying you to locate the wreck and rescue any surviors. Due to the proximity of the Omni base its better that you go in alone, be very careful. I'll have an evac team standing by to extract you when you find them. \par +\par +@J Andy, thank goodness. I'm ok, but the rest of the crew were killed in the crash. I think OMNI targeted this shuttle knowing there's some critical data about the Alien super weapon located on the Shuttle's computer. I supect they are trying to find the crash site. \par +\par +@A I'll call in the extraction team, I'll try and hold any OMNI units from getting to the Shuttle.\par +\par +@O The rescue force is on its way, there should be an advance group showing up shortly to help secure the crash site, ETA on the rescue is 10 mins, Dont let OMNI get to the crash site.\par +\par +@O You were killed, Jana was captured. \par +\par +@O Well done.\par +\par +@J Jana and the secrets of the monolith have been captured by OMNI forces. Mission Failed.\par +\par +\par +\cf1\i In which, Andy we first see the replicator. \par +\cf0\i0\par +\b Mission 6\par +"Embrace and Extend"\b0\par +@O Jana's advanced team established a R+D center before we arrived. Its ill suited to Defense, and we are running low on funds. Build the base up to withstand attacks, and start exploiting the local galaxite. Make sure and protect the R+D centers where Jana is working on finding the location of this alien 'Superweapon'. \par +\par +@O You lost the research center and Jana has been captured. I'm sorry Andy but we've lost too much time and resources pursuing Jana and her wild ideas about Alien weapons. I recomend you concentrate on exploiting all the local galaxite and... @A No way, Olstrom! Jana's life is on the line, she's given 10 years to this corporation and I know she's on to something big. I'm going after her.\par +\par +@O WHAT!, I.. I don't expect my subordiantes to second guess executive orders, you will NOT try and rescue Jana, and you will MINE ALL THE GALAXITE!!! @A Your transmisson, is breaking up, your communications are totally corrupted, I hope you can read this, I will rescue Jana from the enemy base and try to report back.\par +\par +@O you lost the R+D center. \par +\par +@J Andy, they are holding me inside thier research center, see you soon.\par +\par +@A testing one two three... are you recieving this.... ? hello?. \par +\par +@O Don't play games with me.. I'm Glad Jana is ok, but your conduct was unacceptable.\par +\par +\par +\par +\b Mission 7\par +"Performance Improvement Plan"\b0\par +@O After that kind of conduct your career is on the line. I'm putting you on a Performance Improvement Plan, and sending you to the remote northern desert to mine Galaxite. Get a big mining operation under way and mine 20000 credits of Galaxite. We will use the money to broker a cease fire with OMNI. Don't screw this simple task up, your career with ACME is on the line. \par +\par +@O Ok, you've redeemed yourself slightly. The Free Radicals arent usually so bold, or this well organized. This is a worrying development, we should probe the scale of their presence here. \par +\par +@A I've run into some units with the marking Free Radicals, seems the secret of Icarus is out. What should I do?\par +\par +@O What is it with you?, are you looking for trouble?. The Free Radicals are an anarchic bunch of anti corporate activists, They will try to disrupt your activities, stay focused on the collecting the galaxite. Building a large base with lots of units will only slow your progress. Attack them only if you think it will be cost effective. \par +\par +@O You lost everything, your career is in tatters, I'm replacing you with someone else.\par +\par +\par +\b Mission 8\par +"Hostile Takeover"\b0\par +@O Andy, we have located what we think is one of the main Free Radical base's on Icarus. The base is large but still under construction and doesnt appear to be well defended. Due to budget concerns I'm deploying you personally with some takeover specialists to takeover thier base. You won't be able to build structures on this level, so use your raider's carefully. \par +\par +@O I'm sending you some more Raider units, they will arrive shortly. I won't be able to send you any more reinforcements so use them carefully. \par +\par +@O Well done.\par +\par +@O You died. Mission Failure.\par +\par +@O You lost all the Raiders, and you have no way to finish the mission without more units. \par +\par +\par +\b Mission 9\par +"Limited Partnership"\b0\par +@OAndy, as I mentioned earlier we didnt come to Icarus to fight a war on two fronts. I've decided to make a deal with OMNI to cease hostilities and combine together to crush the Free Radical Menace. Omni forces will be your friends in this mission, build a base work with OMNI forces to crush the Free Radicals.\par +\par +@OExcellent, The Free Radical scum appear defeated, the loss of a base of this size will really hurt them. Grab as much of the remaining galaxite before OMNI gets it, and report back when you're done. \par +\par +@O Arghhh! OMNI have gone back on thier word, several OMNI takeover specialists have taken over your headquaters and are hacking into ACME computers systems right now, looking for something about the "Limited Replicator". Stop them at once, then take a big force and crush the treacherous bastards!, show no mercy.\par +\par +@OYou lost your headquaters, thats unacceptable incompetance, I'm replacing you, the mission is over.\par +\par +@OCongratulations Andy, that was an impressive show of force, and a decisive day for ACME security forces. Your becoming a formidable and respected leader, well done!\par +\par +\par +\b Mission 10\par +"Leak Prevention"\b0\par +@O The Free Radicals are leaving Icarus. Intelligence reports a massive convoy of their remaining units trying to reach transport ships located at the north end of a canyon. I'm transfering you and as many units as we can spare nearby. Dont let any of the Free Radical units get to through the canyon. A scout has located the point at the North of the map they will try to reach.\par +\par +@O Oh one more thing, another of the Alien Replicators is located somwhere, see if you can find it, it maybe useful. \par +\par +@O Unacceptable, Free Radical units have gotten through the canyon to the dropships, this should have been a turkey shoot, you're relieved of active duty. \par +\par +@O Ok, that seems to be the last of them, there was a few more left than we had suspected. This should set them back many years, in there pitiful efforts to thwart big buisness and over exploitation of the environment. Great work!\par +\par +\par +\b Mission 11\par +"Taking Ownership"\b0\par +@O With the Free Radical menace gone, they have left some abandoned bases which OMNI are quickly taking over. I want you to personally escort a small force or Corporate raiders to an abandoned base, in the open desert. Get the base quickly up and running and attack the local OMNI base. \par +\par +@O Excellent work, very efficient.\par +\par +@O Andy, my most promising protoge... sad. Ah well, next....\par +\par +\b\par +Mission 12\par +\b0 "\b Damage Control"\par +\par +\par +\b0\par +\b Mission 13\par +\b0 "\b Strategic Summit"\b0\par +@O Extensive orbital survilance has finally located the main OMNI base on Icarus, its built onto the top of a mountain and looks incredibly well defended. We belive Fox is located in an HQ building in the Heart of this base. Your mission is to iinfiltrate the base and Capture that building taking Fox alive.\par +\par +@O You failed.\par +\par +@O argh you blew up the HQ\par +\par +@O Win nice work\par +\par +\par +\b Mission 14\par +"Bringing Closure"\b0\par +\par +\par +\par +\par +\par +\par +\par +\par +} + \ No newline at end of file diff --git a/legacy/AuthorKit/mkpdb.bat b/legacy/AuthorKit/mkpdb.bat new file mode 100644 index 0000000..e071a52 --- /dev/null +++ b/legacy/AuthorKit/mkpdb.bat @@ -0,0 +1,183 @@ +@echo off + +REM Usage: +REM mkpdb <-pdb database> [-tc template collection] [-ld level] [-size size] [-nocleanup] +REM -pdb database: e.g., htdata816.pdb +REM -tc template collection: e.g., grassy.tc +REM -ld level: e.g., grassy_test.ld +REM -size size: tile size (16, 24, or 32) +REM -gt +REM -lvl level: e.g., grassy_test.lvl + +REM Example: +REM mkpdb -pdb htdata816.pdb -tc enva.tc -ld enva.ld -size 16 +REM or +REM mkpdb -pdb htdata824.pdb -gt + +SETLOCAL + +REM Miscellaneous constants + +set DEPTH=8 +set FIXED_PAL=fixed_%DEPTH%bpp.pal +set TMPDIR=mkdb_temp +set TOOLDIR=. +set CREATORID=WARI + +REM Parse command-line arguments + +:NextArg +if "%1"=="" ( + goto :ProcessArgs +) else if "%1"=="-tc" ( + set TC=%2 + shift +) else if "%1"=="-pdb" ( + set PDB=%2 + shift +) else if "%1"=="-ld" ( + set LD=%2 + shift +) else if "%1"=="-size" ( + set TILESIZE=%2 + shift +) else if "%1"=="-nocleanup" ( + set NOCLEANUP=1 +) else if "%1"=="-gt" ( + set GOBTEMPLATES=1 +) else if "%1"=="-lvl" ( + set LVL=%TMPDIR%\%2 + shift +) else ( + echo Error: unknown argument "%1" + goto :EOF +) + +shift +goto :NextArg + +:ProcessArgs + +REM Prompt user for any missing required arguments and fill in +REM defaults for missing optional ones. + +if "%PDB%"=="" ( + set /P PDB="Enter database (.pdb) file name: " +) +if "%PDB%"=="" ( + echo Error: database file name required + goto :EOF +) + +if "%TILESIZE%"=="" ( + set TILESIZE=24 +) +echo Tile size: %TILESIZE% + +REM + +set ARTDIR=art%DEPTH%%TILESIZE% +set ENV_PAL=%ARTDIR%\%TC:.tc=%_%DEPTH%bpp.pal + +REM + +md %TMPDIR% +cd %TMPDIR% +echo Unpacking database... +..\%TOOLDIR%\packpdb2 -u ..\%PDB% >stdout.txt +if %ERRORLEVEL% NEQ 0 ( + cd .. + echo Error unpacking database + goto :Error +) +del stdout.txt +cd .. + +if NOT "%TC%"=="" ( + echo Calculating palette... + %TOOLDIR%\mcl -makepal %TILESIZE% %TC% 256 128 32 %FIXED_PAL% %ENV_PAL% + if %ERRORLEVEL% NEQ 0 ( + echo Error creating palette + goto :Error + ) + + echo Crunching palette... + %TOOLDIR%\palbin %ENV_PAL% %TMPDIR%\%TC:.tc=%.palbin + if %ERRORLEVEL% NEQ 0 ( + echo Error crunching palette + goto :Error + ) + + echo Creating shadow map... + %TOOLDIR%\shadowmap %ENV_PAL% %TMPDIR%\%TC:.tc=%.palbin.shadowmap 0.6 + if %ERRORLEVEL% NEQ 0 ( + echo Error creating shadow map + goto :Error + ) +) + +if "%LVL%"=="" ( + set LVL=%TMPDIR%\%LD:.ld=%.lvl +) + +if NOT "%LD%"=="" ( + echo Crunching level... + %TOOLDIR%\mcl -levels %TILESIZE% %DEPTH% 0.8 0.9 0.9 1.1 1.3 %ARTDIR% %TMPDIR% %LD% + if %ERRORLEVEL% NEQ 0 ( + echo Error compiling level + goto :Error + ) + %TOOLDIR%\cl /nologo /EP /FI ..\res.h %LVL% > ini.tmp + if %ERRORLEVEL% NEQ 0 ( + echo Error preprocessing level + goto :Error + ) + %TOOLDIR%\inicrunch ini.tmp %LVL% + if %ERRORLEVEL% NEQ 0 ( + del ini.tmp + echo Error inicrunching level + goto :Error + ) + del ini.tmp +) + +if NOT "%GOBTEMPLATES%"=="" ( + echo Crunching GobTemplates... + %TOOLDIR%\cl /nologo /EP /I. GobTemplates.ini.pp > ini.tmp + if %ERRORLEVEL% NEQ 0 ( + echo Error preprocessing GobTemplates + goto :Error + ) + %TOOLDIR%\inicrunch ini.tmp %TMPDIR%\GobTemplates.ini + if %ERRORLEVEL% NEQ 0 ( + del ini.tmp + echo Error inicrunching GobTemplates + goto :Error + ) + del ini.tmp +) + +%TOOLDIR%\packpdb2 -p %CREATORID% %PDB% %TMPDIR%\*.* -nocompress *.* +if %ERRORLEVEL% NEQ 0 ( + echo Error packing database + goto :Error +) +endlocal + +echo :NoError +if "%NOCLEANUP%"=="" ( + @echo cleaning up... + if exist %TMPDIR% rd /q /s %TMPDIR% +) +endlocal +set MKPDB_RET=0 +goto :EOF + +:Error +echo :Error +if "%NOCLEANUP%"=="" ( + @echo cleaning up... + if exist %TMPDIR% rd /q /s %TMPDIR% +) +endlocal +set MKPDB_RET=1 diff --git a/legacy/AuthorKit/res.h b/legacy/AuthorKit/res.h new file mode 100644 index 0000000..784a859 --- /dev/null +++ b/legacy/AuthorKit/res.h @@ -0,0 +1,751 @@ +#define kidfDefault 1000 +#define kidfAreYouSure 1001 +#define kidfEmpty 1002 +#define kidfLoseSummary 1003 +#define kidfGameOptions 1004 +#define kidfTileMapTest 1005 +#define kidfContinueGame 1006 +#define kidfHrcMenu 1008 +#define kidfPickLevel 1009 +#define kidfWinSummary 1010 +#define kidfBuildInfantry 1011 +#define kidfResearchMenu 1012 +#define kidfStructMenu 1013 +#define kidfVtsMenu 1014 +#define kidfBuildVehicle 1015 +#define kidfHqMenu 1016 +#define kidfBuildStructure 1017 +#define kidfPlaceStructure 1018 +#define kidfTestOptions 1019 +#define kidfStartup 1021 +#define kidfPickTransport 1022 +#define kidfHostMultiplayer 1024 +#define kidfObjectives 1025 +#define kidfMeeting 1026 +#define kidfChangeDisplayMode 1028 +#define kidfMessageBox 1029 +#define kidfUnitMenu 1030 +#define kidfMobileHqMenu 1031 +#define kidfMinerMenu 1032 +#define kidfMemoryUse 1033 +#define kidfJoinOrHostMultiplayer 1034 +#define kidfCreateNewMultiplayer 1035 +#define kidfSimUI 1036 +#define kidfInputUI 1037 +#define kidfLoadGame 1038 +#define kidfSaveGame 1039 +#define kidfEcomLarge 1040 +#define kidfEcomSmall 1041 +#define kidfHelp 1042 +#define kidfPlaySolo 1043 +#define kidfInGameMenu 1044 +#define kidfUpgrade 1045 +#define kidfMiniMap 1046 +#define kidfCutScene 1047 +#define kidfInGameOptions 1049 +#define kidfSoundOptions 1050 +#define kidfColorOptions 1051 +#define kidfDisplayOptions 1052 +#define kidfPerformanceOptions 1053 +#define kidfGobCount 1054 +#define kidfDrmCode 1055 +#define kidfDrmKey 1056 +#define kidfRegisterNow 1057 +#define kidfMessageBoxQuery 1058 +#define kidfInputPanel 1059 +#define kidfDeleteMissionPack 1060 +#define kidfLoading 1061 +#define kidfWaiting 1062 +#define kidfMultiplayerLoseSummary 1063 +#define kidfMultiplayerWinSummary 1064 +#define kidfMultiplayerObjectives 1065 +#define kidfBluetoothPatchQuery 1066 +#define kidfJoinOrHostMultiplayerServer 1067 +#define kidfCreateNewMultiplayerServer 1068 +#define kidfSelectMissionWide 1069 +#define kidfDownloadMissionPackWide 1070 +#define kidfDownloadBox 1071 +#define kidfHelpWide 1072 + +#define kifntDefault 0 +#define kifntShadow 1 +#define kifntButton 2 +#define kifntTitle 3 +#define kifntEcom 4 +#define kifntHud 5 +#define kcFonts 6 + +#define kidcOk 2000 +#define kidcCancel 2001 +#define kidcRunTests 2002 +#define kidcMiniMap 2003 +#define kidcHelp 2004 +#define kidcDefault 2005 +#define kidcAlert 2006 + +// Gob types + +// WARNING: the value and order of these constants cannot be +// changed without updating 'M' + +#define kgtNone 0 +#define kgtShortRangeInfantry 1 +#define kgtLongRangeInfantry 2 +#define kgtHumanResourceCenter 3 +#define kgtSurfaceDecal 4 +#define kgtScenery 5 +#define kgtAnimation 6 +#define kgtReactor 7 +#define kgtProcessor 8 +#define kgtStructure 9 +#define kgtUnit 10 +#define kgtGalaxMiner 11 +#define kgtHeadquarters 12 +#define kgtResearchCenter 13 +#define kgtVehicleTransportStation 14 +#define kgtRadar 15 +#define kgtLightTank 16 +#define kgtMediumTank 17 +#define kgtMachineGunVehicle 18 +#define kgtRocketVehicle 19 +#define kgtTakeoverSpecialist 20 +#define kgtWarehouse 21 +#define kgtMobileHeadquarters 22 +#define kgtOvermind 23 +#define kgtTankShot 24 +#define kgtRocket 25 +#define kgtMachineGunTower 26 +#define kgtRocketTower 27 +#define kgtScorch 28 +#define kgtSmoke 29 +#define kgtPuff 30 +#define kgtBullet 31 +#define kgtArtillery 32 +#define kgtArtilleryShot 33 +#define kgtAndy 34 +#define kgtReplicator 35 +#define kgtActivator 36 +#define kgtFox 37 +#define kgtAndyShot 38 + +#include "../mpshared/side.h" + +// Startup form constants + +#define kidcPlaySinglePlayer 1001 +#define kidcPlayMultiPlayer 1002 +#define kidcPlay 1003 +#define kidcSetupGame 1004 +#define kidcBuyMe 1005 +#define kidcCredits 1006 +#define kidcExitGame 1007 +#define kidcLoadSavedGame 1008 +#define kidcPlayDemo 1500 +#define kidcPlayMission 1010 +#define kidcVersion 1011 +#define kidcDownloadMissions 1501 +#define kidcForums 1502 + +// Play Solo form constants + +#define kidcBeginNewGame 1012 +#define kidcPlayChallengeLevel 1013 +#define kidcPlayStoryMission 1014 +#define kidcPlaybackGame 1015 +//#define kidcLoadSavedGame 1008 + +// SimUIForm constants + +#define kidcStatusLabel 1001 +#define kidcFps 1004 +#define kidcObjective 1005 +#define kidcCountdown 1006 + +// InputUIForm constants + +#define kidcMenuButton 1002 +#define kidcGraffitiScroll 1012 +#define kidcAppsSilkButton 1013 +#define kidcMenuSilkButton 1014 +#define kidcCalcSilkButton 1015 +#define kidcFindSilkButton 1016 +#define kidcCreditsLabel 1018 +#define kidcPower 1019 + +// InGameMenuForm constants + +#define kidcSaveGame 1001 +#define kidcLoadGame 1002 +#define kidcOptions 1003 +#define kidcRestartMission 1004 +#define kidcAbortMission 1005 +#define kidcObjectives 1006 +//#define kidcHelp 1006 + +// TestOptionsForm constants + +#define kidcDrawPaths 1002 +#define kidcOvermind 1003 +#define kidcClearFog 1004 +#define kidcDrawLines 1005 +#define kidcShowStats 1006 +#define kidcShowFPS 1007 +#define kidcSuspendUpdates 1008 +#define kidcMaxRepaint 1009 +#define kidcAutosave 1010 +#define kidcMemoryUse 1011 +#define kidcLockStep 1012 +#define kidcDrawUpdateRects 1013 +//#define kidcGraphStats 1014 +#define kidcGodMode 1015 +//#define kidcFlashEnemyTargets 1016 +//#define kidcHelp 1017 +#define kidcNextPage 1018 +#define kidcPrevPage 1019 +#define kidcIndex 1020 +#define kidcBack 1021 +#define kidcGobCount 1022 +#define kidcBreak 1023 +#define kidcStylusUI 1024 +#define kidcMoveIndicator 1025 +#define kidcHoldSelect 1026 + +// MemoryUseForm constants + +#define kidcDynDbInitial 1001 +#define kidcMmgrDynDbReserve 1002 +#define kidcDynUse 1003 +#define kidcMmgrUse 1004 +#define kidcCacheUse 1005 +#define kidcClearCache 1006 +#define kidcLimitCache 1007 +#define kidcAdd10KCache 1008 +#define kidcSub10KCache 1009 +#define kidcCacheLimit 1010 + +// Game Over form constants + +#define kidcMessage 1001 + +// PlayMultiplayer form constants + +#define kidcJoinGame 1001 +#define kidcNewGame 1002 +#define kidcGameList 1003 +#define kidcRefresh 1004 +#define kidcPlayerName 1005 +#define kidcSearching 1006 +#define kidcPlayerNamePanel 1007 +#define kidcPlayerNameLabel 1008 +#define kidcStatus 1009 +#define kidcSpecify 1010 + +// CreateNewMultiplayer form constants + +#define kidcPassword 1101 +#define kidcMapList 1102 + +// MeetingForm constants + +#define kidcGameName 1001 +#define kidcMapName 1002 +#define kidcNumPlayers 1003 +#define kidcPlayerList 1004 +#define kidcGameNameLabel 1005 +#define kidcGameNamePanel 1006 +#define kidcPasswordLabel 1007 +#define kidcPasswordPanel 1008 +#define kidcAddress 1009 +#define kidcAddressLabel 1010 + +// PickLevelForm constants + +#define kidcLevelList 1001 +#define kidcCategories 1500 +#define kidcAddOnMessage 1501 +#define kidcStoryList 1502 +#define kidcChallengeList 1503 +#define kidcAddOnList 1504 + +// DownloadMissionPack constants + +#define kidcMissionPackList 1001 +#define kidcMissionPackInfo 1002 +#define kidcNumMissions 1010 +#define kidcDiscuss 1011 + +// PickTransportForm constants + +#define kidcNoTransportsAvailable 1001 +#define kidcTransport1 1101 +#define kidcTransport2 1102 +#define kidcTransport3 1103 +#define kidcTransport4 1104 +#define kidcTransport5 1105 +#define kidcTransport6 1106 + +// UnitMenu constants + +#define kidcTitle 1200 +#define kidcCantTransform 1201 + +// Relocatable UnitMenu buttons + +#define kidcRelocButtonMin 1300 + +#define kidcRepair 1300 +#define kidcSelfDestruct 1301 +#define kidcAbortBuild 1302 +#define kidcAbortRepair 1303 +#define kidcBuild 1304 +#define kidcAbortUpgrade 1304 +#define kidcResearch 1305 +#define kidcTransform 1306 +#define kidcDeliver 1306 + +#define kidcRelocButtonMax 1307 + +// UnitBuildForm constants + +#define kidcName 1001 +#define kidcCost 1002 +#define kidcMoveRate 1003 +#define kidcMoveRateMeter 1103 +#define kidcArmorStrength 1004 +#define kidcArmorStrengthMeter 1104 +#define kidcWeaponStrength 1005 +#define kidcWeaponStrengthMeter 1105 +#define kidcWeaponRange 1006 +#define kidcWeaponRangeMeter 1106 +#define kidcList 1007 +#define kidcOrder 1008 +#define kidcCancelOrder 1009 +#define kidcDescription 1010 +#define kidcCostMeter 1101 +#define kidcLimitReached 1200 + +// BuildStructure form constants (reuses many BuildVehicle form constants) + +#define kidcPowerSupply 1011 +#define kidcPowerSupplyMeter 1111 +#define kidcPowerDemand 1012 +#define kidcPowerDemandMeter 1112 + +// EcomForm constants + +//#define kidcMessage 1001 +#define kidcFrom 1011 +#define kidcTo 1012 +#define kidcFromBitmap 1014 +#define kidcToBitmap 1015 +#define kidcEcomText 1016 + +// CutSceneForm constants + +//#define kidcMessage 1001 +#define kidcBitmap 1002 + +// Upgrade form constants + +#define kidcPrerequisites 1201 +#define kidcPrerequisitesLabel 1202 +#define kidcCostLabel 1203 + +// Objectives form constants + +#define kidcMissionTitle 1001 +#define kidcMissionResult 1002 +#define kidcStatistics 1003 +//#define kidcRestartMission 1004 +//#define kidcAbortMission 1005 +#define kidcObjectiveText1 1101 +#define kidcObjectiveText2 1102 +#define kidcObjectiveText3 1103 +#define kidcObjectiveText4 1104 +#define kidcObjectiveStatus1 1201 +#define kidcObjectiveStatus2 1202 +#define kidcObjectiveStatus3 1203 +#define kidcObjectiveStatus4 1204 +#define kidcPage1 1300 +#define kidcObjectiveInfo 1301 +#define kidcPage2 1400 +#define kidcMobileUnitsKilled 1401 +#define kidcStructuresKilled 1402 +#define kidcMobileUnitsLost 1403 +#define kidcStructuresLost 1404 +#define kidcCreditsAction 1405 +#define kidcGameTime 1406 +#define kidcInfo 1407 +#define kidcRankTitle 1408 +#define kidcPage3 1500 + +// GameOptions constants + +#define kidcInGameOptions 1001 +#define kidcSoundOptions 1002 +#define kidcPerformanceOptions 1003 +#define kidcColorOptions 1004 +#define kidcDisplayOptions 1005 +#define kidcDeleteMissionPack 1007 + +// InGameOptions constants + +#define kidcGameSpeed 1201 +#define kidcGameSpeedLabel 1202 +#define kidcLassoSelection 1003 +#define kidcEasy 1004 +#define kidcNormal 1005 +#define kidcHard 1006 +#define kidc1Select2Scroll 1203 +#define kidcScrollSpeed 1204 +#define kidcScrollSpeedLabel 1205 + +// SoundOptions constants + +#define kidcVol 1001 +#define kidcVolLabel 1002 +#define kidcMute 1003 +#define kidcVolumeString 1004 + +// ColorOptions constants + +#define kidcHueLabelString 1001 +#define kidcHue 1002 +#define kidcHueLabel 1003 +#define kidcSatLabelString 1004 +#define kidcSat 1005 +#define kidcSatLabel 1006 +#define kidcLumLabelString 1007 +#define kidcLum 1008 +#define kidcLumLabel 1009 + +// DisplayOptions constants + +#define kidcModesList 1001 + +// PerformanceOptions constants + +#define kidcRocketShots 1001 +#define kidcRocketTrails 1002 +#define kidcRocketImpacts 1003 +#define kidcShots 1004 +#define kidcShotImpacts 1005 +#define kidcSelectionBrackets 1006 +#define kidcSmoke 1007 +#define kidcEnemyDamageIndicator 1008 +#define kidcScorchMarks 1009 +#define kidcSymbolFlashing 1010 + +// DRM Code + +#define kidcCode 1001 +#define kidcEnterKey 1002 +//#define kidcPlayDemo 1003 + +// DRM Key + +#define kidc0 1010 +#define kidc1 1011 +#define kidc2 1012 +#define kidc3 1013 +#define kidc4 1014 +#define kidc5 1015 +#define kidc6 1016 +#define kidc7 1017 +#define kidc8 1018 +#define kidc9 1019 +#define kidcA 1020 +#define kidcB 1021 +#define kidcC 1022 +#define kidcD 1023 +#define kidcE 1024 +#define kidcF 1025 +#define kidcBackspace 1026 +#define kidcKey 1027 + +// Input Panel + +#define kidcInputLabel 1010 +#define kidcInputEdit 1011 + +// Colors + +#define kiclrBlack 0 +#define kiclrWhite 1 +#define kiclrRed 2 +#define kiclrGreen 3 +#define kiclrYellow 4 +#define kiclrSide1 5 +#define kiclrSide2 6 +#define kiclrSide3 7 +#define kiclrSide4 8 +#define kiclrButtonFill 9 +#define kiclrButtonBorder 10 +#define kiclrMenuBack 11 +#define kiclrFormBackground 12 +#define kiclrMiniMapBorder 13 +#define kiclrGalaxite 14 +#define kiclrButtonFillHighlight 15 +#define kiclrMediumGray 16 +#define kiclrBlueSideFirst 17 +#define kiclr0BlueSide 17 +#define kiclr1BlueSide 18 +#define kiclr2BlueSide 19 +#define kiclr3BlueSide 20 +#define kiclr4BlueSide 21 +#define kiclrBlueSideLast 21 +#define kiclrRedSideFirst 22 +#define kiclr0RedSide 22 +#define kiclr1RedSide 23 +#define kiclr2RedSide 24 +#define kiclr3RedSide 25 +#define kiclr4RedSide 26 +#define kiclrRedSideLast 26 +#define kiclrYellowSideFirst 27 +#define kiclr0YellowSide 27 +#define kiclr1YellowSide 28 +#define kiclr2YellowSide 29 +#define kiclr3YellowSide 30 +#define kiclr4YellowSide 31 +#define kiclrYellowSideLast 31 +#define kiclrCyanSideFirst 32 +#define kiclr0CyanSide 32 +#define kiclr1CyanSide 33 +#define kiclr2CyanSide 34 +#define kiclr3CyanSide 35 +#define kiclr4CyanSide 36 +#define kiclrCyanSideLast 36 +#define kiclrListBackground 37 +#define kiclrListBorder 38 +#define kiclrJana 39 +#define kiclrAndy 40 +#define kiclrOlstrom 41 +#define kiclrFox 42 +#define kiclrSideNeutral 43 +#define kiclrNeutralSideFirst 43 +#define kiclr0NeutralSide 43 +#define kiclr1NeutralSide 44 +#define kiclr2NeutralSide 45 +#define kiclr3NeutralSide 46 +#define kiclr4NeutralSide 47 +#define kiclrNeutralSideLast 47 +#define kiclrFullnessIndicator 48 + +// ARM code resource ids + +#define kidrArmCode 1 + +// Datatypes used for conditions & actions + +// NOTE: kut values can not be changed without breaking old levels + +#define kutNone -1 + +// Infantry Units (NOTE: this is the order they will appear in the build form) +#define kutShortRangeInfantry 0 +#define kutLongRangeInfantry 1 +#define kutTakeoverSpecialist 2 + +// Vehicle Units (NOTE: this is the order they will appear in the build form) +#define kutMachineGunVehicle 3 +#define kutLightTank 4 +#define kutRocketVehicle 5 +#define kutMediumTank 6 +#define kutGalaxMiner 7 +#define kutMobileHeadquarters 8 + +// Structures (NOTE: this is the order they will appear in the build form) +#define kutReactor 9 +#define kutProcessor 10 +#define kutWarehouse 11 +#define kutHumanResourceCenter 12 +#define kutVehicleTransportStation 13 +#define kutRadar 14 +#define kutResearchCenter 15 +#define kutHeadquarters 16 + +// Special structures that like to kill +#define kutMachineGunTower 17 +#define kutRocketTower 18 + +#define kutAndy 19 +#define kutArtillery 20 +#define kutReplicator 21 +#define kutFox 22 + +#define kutMax 23 + +#define knCaTypeNumber 0 +#define knCaTypeQualifiedNumber 1 +#define knCaTypeSide 2 +#define knCaTypeCounter 3 +#define knCaTypeUnit 4 +#define knCaTypeModifier 5 +#define knCaTypeResource 6 +#define knCaTypeWinLose 7 +#define knCaTypeOnOff 8 +#define knCaTypeCharacter 9 +#define knCaTypeText 10 +#define knCaTypeRichText 11 +#define knCaTypeUnitTypes 12 +#define knCaTypeSwitch 13 +#define knCaTypeArea 14 + +// Related enumerations + +#define knQualifierAtLeast 0 +#define knQualifierAtMost 1 +#define knQualifierExactly 2 + +#define knCaSideNeutral 0 +#define knCaSideSide1 1 +#define knCaSideSide2 2 +#define knCaSideSide3 3 +#define knCaSideSide4 4 +#define knCaSideEnemies 5 +#define knCaSideAllies 6 +#define knCaSideAllSides 7 +#define knCaSideCurrentSide 8 + +#define knCounterTypeNone 0 +#define knCounterTypeTotal 1 +#define knCounterTypeUnits 2 +#define knCounterTypeBuildings 3 +#define knCounterTypeUnitsAndBuildings 4 +#define knCounterTypeKills 5 +#define knCounterTypeCustom 6 + +#define knModifierTypeSet 0 +#define knModifierTypeAdd 1 +#define knModifierTypeSubtract 2 + +#define knWinLoseTypeNone 0 +#define knWinLoseTypeWin 1 +#define knWinLoseTypeLose 2 + +#define knOnOffTypeNone 0 +#define knOnOffTypeOn 1 +#define knOnOffTypeOff 2 + +#define knSmallLargeTypeNone 0 +#define knSmallLargeTypeSmallBottom 1 +#define knSmallLargeTypeLarge 2 +#define knSmallLargeTypeSmallTop 3 + +#define knMoreCloseTypeNone 0 +#define knMoreCloseTypeMore 1 +#define knMoreCloseTypeClose 2 + +#define knModifyCountdownTypeNone 0 +#define knModifyCountdownTypeStop 1 +#define knModifyCountdownTypeResume 2 +#define knModifyCountdownTypeHide 3 +#define knModifyCountdownTypeShow 4 + +#define knCharacterTypeNone 0 +#define knCharacterTypeAndy 1 +#define knCharacterTypeJana 2 +#define knCharacterTypeOlstrom 3 +#define knCharacterTypeFox 4 +#define knCharacterTypeACME_Security 5 +#define knCharacterTypeOMNI_Security 6 +#define knCharacterTypeAnonymous 7 +#define knCharacterTypeBlank 8 + +#define knAggressivenessCoward 0 +#define knAggressivenessPacifist 1 +#define knAggressivenessSelfDefense 2 +#define knAggressivenessDefender 3 +#define knAggressivenessPitbull 4 + +#define knModifyNumberTypeNone 0 +#define knModifyNumberTypeSet 1 +#define knModifyNumberTypeAdd 2 +#define knModifyNumberTypeSubtract 3 + +// Conditions + +#define knMissionLoadedCondition 0 +#define knCreditsCondition 1 +#define knAreaContainsUnitsCondition 2 +#define knGalaxiteCapacityReachedCondition 3 +#define knMobileHQDeployedCondition 4 +#define knPlaceStructureModeCondition 5 +#define knElapsedTimeCondition 6 +#define knOwnsUnitsCondition 7 +#define knMinerCantFindGalaxiteCondition 8 +#define knUnitSeesUnitCondition 9 +#define knUnitDestroyedCondition 10 +#define knSwitchCondition 11 +#define knPeriodicTimerCondition 12 +#define knDiscoversSideCondition 13 +#define knCountdownTimerCondition 14 +#define knCounterCondition 15 +#define knTestPvarCondition 16 +#define knHasUpgradesCondition 17 +#define knConditionMax 18 + +// TriggerActions + +#define knSetResourcesTriggerAction 0 +#define knSetAllowedUnitsTriggerAction 1 +#define knEcomTriggerAction 2 +#define knSetObjectiveTriggerAction 3 +#define knSetNextMissionTriggerAction 4 +#define knEndMissionTriggerAction 5 +#define knWaitTriggerAction 6 +#define knSetSwitchTriggerAction 7 +#define knSetPlayerControlsTriggerAction 8 +#define knPreserveTriggerTriggerAction 9 +#define knCenterViewTriggerAction 10 +#define knPanViewTriggerAction 11 +#define knDefogAreaTriggerAction 12 +#define knMoveUnitTriggerAction 13 +#define knTargetUnitTriggerAction 14 +#define knCreateUnitGroupTriggerAction 15 +#define knCreateUnitAtAreaTriggerAction 15 // THis is deliberately the same value as knCreateUnitGroupTriggerAction +#define knHuntTriggerAction 16 +#define knCreateRandomUnitGroupTriggerAction 17 +#define knAlliesTriggerAction 18 +#define knStartCountdownTimerTriggerAction 19 +#define knModifyCountdownTimerTriggerAction 20 +#define knRepairTriggerAction 21 +#define knEnableReplicatorTriggerAction 22 +#define knModifyCreditsTriggerAction 23 +#define knModifyCounterTriggerAction 24 +#define knMoveUnitsInAreaTriggerAction 25 +#define knSetFormalObjectiveTextTriggerAction 26 +#define knSetFormalObjectiveStatusTriggerAction 27 +#define knShowObjectivesTriggerAction 28 +#define knSetFormalObjectiveInfoTriggerAction 29 +#define knCutSceneTriggerAction 30 +#define knJumpToMissionTriggerAction 31 +#define knModifyPvarTriggerAction 32 +#define knSetPvarTextTriggerAction 33 +#define knShowAlertTriggerAction 34 +#define knSetAllowedUpgradesTriggerAction 35 +#define knSetUpgradesTriggerAction 36 + +// UnitGroupActions + +#define knWaitUnitGroupAction 0 +#define knSetSwitchUnitGroupAction 1 +#define knMoveUnitGroupAction 2 +#define knAttackUnitGroupAction 3 +#define knGuardUnitGroupAction 4 +#define knMineUnitGroupAction 5 +#define knGuardVicinityUnitGroupAction 6 + +// UnitActions + +#define knGuardUnitAction 0 +#define knGuardVicinityUnitAction 1 +#define knGuardAreaUnitAction 2 +#define knMoveUnitAction 3 +#define knHuntEnemiesUnitAction 4 +#define knMineUnitAction 5 + +// sound effects for triggers + +#define knsfxHappyEnding 0 +#define knsfxExplosion 1 diff --git a/legacy/AuthorKit/run.bat b/legacy/AuthorKit/run.bat new file mode 100644 index 0000000..fdb3378 --- /dev/null +++ b/legacy/AuthorKit/run.bat @@ -0,0 +1,6 @@ +@echo off +call mkpdb -pdb htdata824.pdb -ld %1 -lvl %2 +if "%MKPDB_RET%"=="0" ( + echo Launching Hostile Takeover... + "Warfare Incorporated" -l %2 +) diff --git a/legacy/AuthorKit/updDesert16.bat b/legacy/AuthorKit/updDesert16.bat new file mode 100644 index 0000000..e5a3367 --- /dev/null +++ b/legacy/AuthorKit/updDesert16.bat @@ -0,0 +1 @@ +@mkpdb -pdb htdata816.pdb -tc desert.tc -ld desert_test.ld -size 16 diff --git a/legacy/AuthorKit/updDesert24.bat b/legacy/AuthorKit/updDesert24.bat new file mode 100644 index 0000000..5d9b19d --- /dev/null +++ b/legacy/AuthorKit/updDesert24.bat @@ -0,0 +1 @@ +@mkpdb -pdb htdata824.pdb -tc desert.tc -ld desert_test.ld -size 24 diff --git a/legacy/AuthorKit/updGobTemplates.bat b/legacy/AuthorKit/updGobTemplates.bat new file mode 100644 index 0000000..6d3278d --- /dev/null +++ b/legacy/AuthorKit/updGobTemplates.bat @@ -0,0 +1 @@ +@mkpdb -pdb htdata824.pdb -gt diff --git a/legacy/AuthorKit/updGrassy16.bat b/legacy/AuthorKit/updGrassy16.bat new file mode 100644 index 0000000..39deb2b --- /dev/null +++ b/legacy/AuthorKit/updGrassy16.bat @@ -0,0 +1 @@ +@mkpdb -pdb htdata816.pdb -tc grassy.tc -ld grassy_test.ld -size 16 diff --git a/legacy/AuthorKit/updGrassy24.bat b/legacy/AuthorKit/updGrassy24.bat new file mode 100644 index 0000000..94da165 --- /dev/null +++ b/legacy/AuthorKit/updGrassy24.bat @@ -0,0 +1 @@ +@mkpdb -pdb htdata824.pdb -tc grassy.tc -ld grassy_test.ld -size 24 diff --git a/legacy/docs/AuthorKit/AuthorKit.css b/legacy/docs/AuthorKit/AuthorKit.css new file mode 100644 index 0000000..8960fd9 --- /dev/null +++ b/legacy/docs/AuthorKit/AuthorKit.css @@ -0,0 +1,5 @@ + +body { font-family: Verdana; font-size: 10pt } +.labelproc { color: #000080; font-weight: bold } +h4 { font-size: 11pt } +.commandline { font-family: Courier } \ No newline at end of file diff --git a/legacy/docs/AuthorKit/AuthorKit.hhp b/legacy/docs/AuthorKit/AuthorKit.hhp new file mode 100644 index 0000000..3389330 --- /dev/null +++ b/legacy/docs/AuthorKit/AuthorKit.hhp @@ -0,0 +1,25 @@ +[OPTIONS] +Compatibility=1.1 or later +Compiled file=AuthorKit.chm +Contents file=Table of Contents.hhc +Default topic=Overview.htm +Display compile progress=No +Index file=Index.hhk +Language=0x409 English (United States) +Title=Hostile Takeover Author Kit + + +[FILES] +Guidelines.htm +HowTo.htm +HowToAddATemplateCollectionToAGameDatabase.htm +HowToCreateATemplateCollection.htm +mkpdbRef.htm +Reference.htm +TemplateArtGuidelines.htm +TemplateExtractorRef.htm +TemplateNamingGuidelines.htm +TemplateTerrainGuidelines.htm + +[INFOTYPES] + diff --git a/legacy/docs/AuthorKit/Guidelines.htm b/legacy/docs/AuthorKit/Guidelines.htm new file mode 100644 index 0000000..baf584e --- /dev/null +++ b/legacy/docs/AuthorKit/Guidelines.htm @@ -0,0 +1,17 @@ + + + + + + + +Guidelines + + + + +

not implemented yet

+ + + + diff --git a/legacy/docs/AuthorKit/HostileTakeoverRef.htm b/legacy/docs/AuthorKit/HostileTakeoverRef.htm new file mode 100644 index 0000000..09cda9c --- /dev/null +++ b/legacy/docs/AuthorKit/HostileTakeoverRef.htm @@ -0,0 +1,26 @@ + + + + + + + +Hostile Takeover Reference + + + + + +

Hostile Takeover Reference

+

HT.exe is a GUI application but it can be passed arguments either through a +Windows shortcut or via the command line.

+

HT usage:

+

HT [-m mode]

+

The mode value specifies which display mode to +start HT in. The default is 0 which his 8-bit color, low resolution if +htdata816.pdb is present. Mode 1 is 8-bit color, high resolution if +htdata824.pdb is present.

+ + + + \ No newline at end of file diff --git a/legacy/docs/AuthorKit/HowTo.htm b/legacy/docs/AuthorKit/HowTo.htm new file mode 100644 index 0000000..75eba8b --- /dev/null +++ b/legacy/docs/AuthorKit/HowTo.htm @@ -0,0 +1,16 @@ + + + + + + +New Page 1 + + + + +This collection of topics give step by step instructions on how to perform common Hostile Takeover authoring operations. + + + + diff --git a/legacy/docs/AuthorKit/HowToAddALevelToAGameDatabase.htm b/legacy/docs/AuthorKit/HowToAddALevelToAGameDatabase.htm new file mode 100644 index 0000000..7616bd7 --- /dev/null +++ b/legacy/docs/AuthorKit/HowToAddALevelToAGameDatabase.htm @@ -0,0 +1,25 @@ + + + + + + + +How to add a Level to a Game Database + + + + + +

How to add a Level to a Game Database

+

First create or edit the Level (.ld) file using the +Map Editor (M.exe). Then use the helper batch file mkpdb.bat +to insert the Level into a Game Database (.pdb).

+

To add a Level to a Game Database:

+

mkpdb -pdb <database> -ld <level> -size <16 | 24>

+

Example:

+

mkpdb -pdb htdata816.pdb -ld grassy_test.ld -size 16

+ + + + diff --git a/legacy/docs/AuthorKit/HowToAddATemplateCollectionToAGameDatabase.htm b/legacy/docs/AuthorKit/HowToAddATemplateCollectionToAGameDatabase.htm new file mode 100644 index 0000000..b843750 --- /dev/null +++ b/legacy/docs/AuthorKit/HowToAddATemplateCollectionToAGameDatabase.htm @@ -0,0 +1,27 @@ + + + + + + + +How to add a Template Collection to a Game Database + + + + + +

How to add a Template Collection to a Game Database

+

Use the helper batch file mkpdb.bat to insert a +new Template Collection (.tc) into Game Database (.pdb). mkpdb invokes a number +of tools to produce an 8-bit palette from the Template Collection and reduce the +Templates to 8-bit before adding them to the Game Database.

+

To add a Template Collection into a Game Database:

+

mkpdb -pdb <database> -tc <template collection> -size <16 +| 24>

+

Example:

+

mkpdb -pdb htdata816.pdb -tc grassy.tc -size 16

+ + + + diff --git a/legacy/docs/AuthorKit/HowToCreateALevel.htm b/legacy/docs/AuthorKit/HowToCreateALevel.htm new file mode 100644 index 0000000..a910a3e --- /dev/null +++ b/legacy/docs/AuthorKit/HowToCreateALevel.htm @@ -0,0 +1,34 @@ + + + + + + + +How to create a Level + + + + + +

How to create a Level

+

launch Map Editor

+

open Template Collection. Create +if necessary.

+

create a new Level (menu File, New)

+

adjust level Settings (min/max players, bounds, title)

+

drag Templates onto the map

+

arrange Templates as desired

+

add initial structures/units for each side

+

adjust side Settings (e.g., initial view, initial credits)

+

save as .ld

+

<NOTE: must save over a pre-existing Level at the moment. Fix: provide +preprocessor and document use of [Levels] section of Game.ini.pp>    +

+

incorporate into a Game Database

+

test with HT.exe

+

 

+ + + + diff --git a/legacy/docs/AuthorKit/HowToCreateATemplateCollection.htm b/legacy/docs/AuthorKit/HowToCreateATemplateCollection.htm new file mode 100644 index 0000000..afb7cae --- /dev/null +++ b/legacy/docs/AuthorKit/HowToCreateATemplateCollection.htm @@ -0,0 +1,52 @@ + + + + +How to create a Template Collection (.tc) + + + + + + +

How to create a Template Collection

+

A Template Collection (.tc) is a file that +contains, surprise, a collection of Templates. Templates are the puzzle pieces +of terrain that are placed on a map in the Map Editor (M) when creating a +Level.

+

To create a Template +Collection:

+
    +
  1. Create a .png file containing all the templates constrained and arranged + as defined by the Template Art Guidelines. + Follow the naming convention: <style>Templates.png, e.g., grassyTemplates.png
  2. +
  3. Create a .png file containing template + terrain information (i.e., which parts of the template block unit movement in + the game). See the Template Terrain + Guidelines for details. Follow the naming convention: <style>Terrain.png, + e.g., grassyTerrain.png
  4. +
  5. Create a .txt file containing a name for + each template. See the Template Naming + Guidelines for details. Follow the naming convention: <style>Names.txt, + e.g., grassyNames.txt
  6. +
  7. Use the TemplateExtractor tool to extract + the templates, terrain info, and names from the .png and .txt files and + compile them into a Map Editor friendly Template Collection (.tc) file.
  8. +
+

Once the source files are ready invoke the +TemplateExtractor from the command-line +to create the .tc file:

+

TemplateExtractor -tc <style>.tc -n <style>Names.txt -art +<style>Templates.png -ter <style>Terrain.png

+ + +

The result will be a Template Collection you can load into the Map Editor or +add to a Game Database +for previewing in-game.

+ + + + \ No newline at end of file diff --git a/legacy/docs/AuthorKit/Index.hhk b/legacy/docs/AuthorKit/Index.hhk new file mode 100644 index 0000000..c0ba08a --- /dev/null +++ b/legacy/docs/AuthorKit/Index.hhk @@ -0,0 +1,9 @@ + + + + + + +
    +
+ diff --git a/legacy/docs/AuthorKit/MapEditorRef.htm b/legacy/docs/AuthorKit/MapEditorRef.htm new file mode 100644 index 0000000..32b9569 --- /dev/null +++ b/legacy/docs/AuthorKit/MapEditorRef.htm @@ -0,0 +1,20 @@ + + + + + + + +Map Editor Reference + + + + + +

Map Editor Reference

+

to be written

+

 

+ + + + diff --git a/legacy/docs/AuthorKit/Overview.htm b/legacy/docs/AuthorKit/Overview.htm new file mode 100644 index 0000000..c65b769 --- /dev/null +++ b/legacy/docs/AuthorKit/Overview.htm @@ -0,0 +1,78 @@ + + + + + + + +Overview + + + + + +

Hostile Takeover Author Kit Overview

+

The Hostile Takeover Author Kit contains the tools needed to create content +like new levels and graphics for Hostile Takeover.

+

Game Databases

+

Hostile Takeover makes use of "Game Database" (.pdb) files to store its +content. There is a custom game database for each major graphics format Hostile +Takeover supports. For example htdata816.pdb contains graphics tuned for 8-bit, +lores (16-pixel tile) devices whereas htdata424.pdb contains graphics tuned for +4-bit hires (24-pixel tile) devices. There is a separate database (htsfx.pdb) +which stores sound effects. The tool packpdb2.exe +is able to create Game Databases and extract or view their contents.

+

Levels

+

Single and multiplayer levels are created with the +Map Editor (M.exe) then added to +a Game Database with the utility mkpdb.bat. Level +files, also known as Level Description files, have the extension .ld. See +how to create a Level for details.

+

Templates, Template Collections

+

Maps are created in the Map Editor by assembling puzzle piece-like Templates +which have been designed to fit together to form roads, cliff ranges, rivers, +etc. The TemplateExtractor utility is +used to extract Template art, terrain information and name from artist-friendly +source files and convert them into Map Editor and game-friendly Template +Collection (.tc) files.

+

A single Template Collection contains all the Templates for maps of a region, +e.g., grassy or sandy. Template art is authored as 24-bit color images that +break down into 24x24 tiles. Automated tools perform the job of reducing the +Template art to Hostile Takeover-compatible 8-bit color art including scaling +down the art to accommodate lores devices. A Template Terrain info image is +scanned to determine which areas of the templates block the movement and +placement of units/structures in the game. For details on all of this see +how to create a Template Collection.

+

Testing new Levels and Templates

+

Once a Template Collection has been assembled and a Level(s) created add them +to a Game Database (see +adding Template Collections to a Game Database and +adding Levels to a Game Database) +and launch Hostile Takeover to give them a +play test.

+

Helper utilities

+

Several shortcut utilities have been included in this kit to reduce the +number of steps needed for common authoring operations. They are:

+ + + + + + + + + + + + + +
makeall.batExtracts grassy Templates to create grassy.tc, then adds it + and the grassy_test.ld Level to the htdata816.pdb and htdata824.pdb Game + Databases
updGrassy16.bat   Adds grassy.tc and grassy_test.ld to htdata816.pdb
updGrassy24.batAdds grassy.tc and grassy_test.ld to htdata824.pdb
+

Use these batch files as a starting point to create your own helpers. It may +be as simple as searching for each "grassy" and replacing with the name of your +environment, e.g., "sandy".

+ + + + diff --git a/legacy/docs/AuthorKit/Reference.htm b/legacy/docs/AuthorKit/Reference.htm new file mode 100644 index 0000000..532d7c3 --- /dev/null +++ b/legacy/docs/AuthorKit/Reference.htm @@ -0,0 +1,12 @@ + + + + +Reference + + + +This section contains topics for each tool that is part of the Author Kit. + + + diff --git a/legacy/docs/AuthorKit/Table of Contents.hhc b/legacy/docs/AuthorKit/Table of Contents.hhc new file mode 100644 index 0000000..a9864f6 --- /dev/null +++ b/legacy/docs/AuthorKit/Table of Contents.hhc @@ -0,0 +1,78 @@ + + + + + + + + + +
    +
  • + + + +
  • + + + +
      +
    • + + + +
    • + + + +
    • + + + +
    • + + + +
    +
  • + + + +
      +
    • + + + +
    • + + + +
    • + + + +
    +
  • + + + +
      +
    • + + + +
    • + + + +
    • + + + +
    • + + + +
    +
+ diff --git a/legacy/docs/AuthorKit/TemplateArtGuidelines.htm b/legacy/docs/AuthorKit/TemplateArtGuidelines.htm new file mode 100644 index 0000000..e1353fd --- /dev/null +++ b/legacy/docs/AuthorKit/TemplateArtGuidelines.htm @@ -0,0 +1,44 @@ + + + + + + + +Template Art Guidelines + + + + + +

Template Art Guidelines

+ +

24-bit color

+ +

24x24 tile

+ +

aligned on a tile grid

+ +

each template must be surrounded by at least one tile's worth of empty space +including at the edges of the document

+ +

surrounding area must be filled with the RGB value 255,0,255

+ +

Templates are extracted from left to right, top to bottom. The names in the +Template Names file must match this order.

+ +

naming convention: <style>Templates.png

+ +

over-brightening and saturating to compensate for device deficiencies

+ +

must have matching Template Terrain +file

+ +

must have matching Template Names +file

+ +

see grassyTemplates.png as an example

+ + + + \ No newline at end of file diff --git a/legacy/docs/AuthorKit/TemplateExtractorRef.htm b/legacy/docs/AuthorKit/TemplateExtractorRef.htm new file mode 100644 index 0000000..503c171 --- /dev/null +++ b/legacy/docs/AuthorKit/TemplateExtractorRef.htm @@ -0,0 +1,39 @@ + + + + +TemplateExtractor reference + + + + +

TemplateExtractor Reference

+

TemplateExtractor.exe is a utility for extracting all the information needed +in Template Collection from three source files: a .png file with Template +images, a .png file with Template terrain info, and a .txt file with Template +names.

+

For details on the formats of these source files see the +Template Art Guidelines, +Template Terrain Guidelines, and +Template Naming Guidelines.

+

TemplateExtractor usage:

+

TemplateExtractor [-tc <output.tc>] [-n <names.txt>] -art +<art.png> -ter <terrain.png>

+ + +

If the output Template Collection is not specified each Template will be +written as a separate .png file using the name given in the names.txt file. If +no names file is specified the Templates are named "templateNN" where NN starts +at 00 and increments for each Template.

+ + +

Example:

+

TemplateExtractor -tc grassy.tc -n grassyNames.txt -art +grassyTemplates.png -ter grassyTerrain.png

+

This will produce the Template Collection grassy.tc which may then be loaded +into the Map Editor for use or +added to a Game +Database.

+ + + \ No newline at end of file diff --git a/legacy/docs/AuthorKit/TemplateNamingGuidelines.htm b/legacy/docs/AuthorKit/TemplateNamingGuidelines.htm new file mode 100644 index 0000000..c58e219 --- /dev/null +++ b/legacy/docs/AuthorKit/TemplateNamingGuidelines.htm @@ -0,0 +1,25 @@ + + + + + + + +Template Naming Guidelines + + + + + +

Template Naming Guidelines

+ +

order must match left to right, top to bottom order in Template art .png

+ +

if Template art is reordered so must the names

+ +

give related Templates the same prefix (e.g., road00, road01, cliff00, +cliff01)

+ + + + \ No newline at end of file diff --git a/legacy/docs/AuthorKit/TemplateTerrainGuidelines.htm b/legacy/docs/AuthorKit/TemplateTerrainGuidelines.htm new file mode 100644 index 0000000..016f18c --- /dev/null +++ b/legacy/docs/AuthorKit/TemplateTerrainGuidelines.htm @@ -0,0 +1,27 @@ + + + + + + + +Template Terrain Guidelines + + + + + +

Template Terrain Guidelines

+ +

Template terrain info is extracted from left to right, top to bottom. Order +must match Template art and names.

+ +

Black tiles are unobstructed

+ +

Red tiles are obstructed

+ +

surrounding tiles are irrelevant but should be filled RGB 255, 0, 255

+ + + + \ No newline at end of file diff --git a/legacy/docs/AuthorKit/mkpdbRef.htm b/legacy/docs/AuthorKit/mkpdbRef.htm new file mode 100644 index 0000000..1844c5b --- /dev/null +++ b/legacy/docs/AuthorKit/mkpdbRef.htm @@ -0,0 +1,31 @@ + + + + + + + +mkpdb Reference + + + + + +

mkpdb.bat Reference

+ +

mkpdb.bat is a helper utility which will insert a Template Collection (.tc) +and/or Level (.ld) into an existing Game Database (.pdb) after applying any +necessary 'crunching' operations. mkpdb.bat must be invoked from a command line.

+ +

mkpdb.bat usage:

+ +

mkpdb -pdb <.pdb> [-tc <.tc>] [-ld <.ld>] [-size <16|24>]

+ +

Example:

+ +

mkpdb -pdb htdata16.pdb -tc grassy.tc -ld grassy_test.ld +-size 16

+ + + + \ No newline at end of file diff --git a/legacy/docs/AuthorKit/packpdb2Ref.htm b/legacy/docs/AuthorKit/packpdb2Ref.htm new file mode 100644 index 0000000..4ffd878 --- /dev/null +++ b/legacy/docs/AuthorKit/packpdb2Ref.htm @@ -0,0 +1,32 @@ + + + + + + + +packpdb2 Reference + + + + + +

packpdb2 Reference

+

packpdb2.exe can be used to create (aka 'pack') data files into a Game +Database, to extract (aka 'unpack') the contents of a Game Database, and to view +the contents of a Game Database without extracting them.

+

packpdb2 usage (creating/packing):

+

packpdb2 -p <CRID> <pdb file> [filespec]

+

Pack [filespec] into <pdb file> with creator id <CRID>. Optional [filespec] +defaults to .\*.*

+

packpdb2 usage (extracting/unpacking):

+

packpdb2 -p <CRID> <pdb file> [filespec]

+

Unpacks files from <pdb file>. Optional [filespec] defaults to .\*.*

+ +

packpdb2 usage (viewing):

+

packpdb2 -v <pdb file>

+

View contents of <pdb file>

+ + + + \ No newline at end of file diff --git a/legacy/docs/Design Notes.doc b/legacy/docs/Design Notes.doc new file mode 100644 index 0000000..82e0000 Binary files /dev/null and b/legacy/docs/Design Notes.doc differ diff --git a/legacy/docs/Functional Spec.doc b/legacy/docs/Functional Spec.doc new file mode 100644 index 0000000..23deefc Binary files /dev/null and b/legacy/docs/Functional Spec.doc differ diff --git a/legacy/docs/Game Concept.doc b/legacy/docs/Game Concept.doc new file mode 100644 index 0000000..5d28670 Binary files /dev/null and b/legacy/docs/Game Concept.doc differ diff --git a/legacy/docs/HT Story and Level Design.doc b/legacy/docs/HT Story and Level Design.doc new file mode 100644 index 0000000..9bc3ec5 Binary files /dev/null and b/legacy/docs/HT Story and Level Design.doc differ diff --git a/legacy/docs/Hostile Takeover.doc b/legacy/docs/Hostile Takeover.doc new file mode 100644 index 0000000..ada1a62 Binary files /dev/null and b/legacy/docs/Hostile Takeover.doc differ diff --git a/legacy/docs/Playtest summaries.doc b/legacy/docs/Playtest summaries.doc new file mode 100644 index 0000000..a1ea9be Binary files /dev/null and b/legacy/docs/Playtest summaries.doc differ diff --git a/legacy/docs/SC and RA2 Analysis.xls b/legacy/docs/SC and RA2 Analysis.xls new file mode 100644 index 0000000..c032aac Binary files /dev/null and b/legacy/docs/SC and RA2 Analysis.xls differ diff --git a/legacy/docs/Sc Buildings.xls b/legacy/docs/Sc Buildings.xls new file mode 100644 index 0000000..25cbacd Binary files /dev/null and b/legacy/docs/Sc Buildings.xls differ diff --git a/legacy/docs/Sound and Music.doc b/legacy/docs/Sound and Music.doc new file mode 100644 index 0000000..8e0dd31 Binary files /dev/null and b/legacy/docs/Sound and Music.doc differ diff --git a/legacy/docs/Sound and Music.xls b/legacy/docs/Sound and Music.xls new file mode 100644 index 0000000..cd4fca7 Binary files /dev/null and b/legacy/docs/Sound and Music.xls differ diff --git a/legacy/docs/Story Matrix.xls b/legacy/docs/Story Matrix.xls new file mode 100644 index 0000000..bc266bc Binary files /dev/null and b/legacy/docs/Story Matrix.xls differ diff --git a/legacy/docs/Story work.doc b/legacy/docs/Story work.doc new file mode 100644 index 0000000..22362b1 Binary files /dev/null and b/legacy/docs/Story work.doc differ diff --git a/legacy/docs/UI diagram.vsd b/legacy/docs/UI diagram.vsd new file mode 100644 index 0000000..444fcc7 Binary files /dev/null and b/legacy/docs/UI diagram.vsd differ diff --git a/legacy/docs/Unit Matrix.xls b/legacy/docs/Unit Matrix.xls new file mode 100644 index 0000000..d149bff Binary files /dev/null and b/legacy/docs/Unit Matrix.xls differ diff --git a/legacy/docs/adpcm.xls b/legacy/docs/adpcm.xls new file mode 100644 index 0000000..ec6f06b Binary files /dev/null and b/legacy/docs/adpcm.xls differ diff --git a/legacy/docs/adpcm16bit.xls b/legacy/docs/adpcm16bit.xls new file mode 100644 index 0000000..3143d15 Binary files /dev/null and b/legacy/docs/adpcm16bit.xls differ diff --git a/legacy/docs/adpcm8bit.xls b/legacy/docs/adpcm8bit.xls new file mode 100644 index 0000000..3d9aa86 Binary files /dev/null and b/legacy/docs/adpcm8bit.xls differ diff --git a/legacy/docs/best_products_2003.pdf b/legacy/docs/best_products_2003.pdf new file mode 100644 index 0000000..8cbe868 Binary files /dev/null and b/legacy/docs/best_products_2003.pdf differ diff --git a/legacy/docs/building & upgrading test cases.doc b/legacy/docs/building & upgrading test cases.doc new file mode 100644 index 0000000..2495854 Binary files /dev/null and b/legacy/docs/building & upgrading test cases.doc differ diff --git a/legacy/docs/mission descriptions.doc b/legacy/docs/mission descriptions.doc new file mode 100644 index 0000000..ae598d7 Binary files /dev/null and b/legacy/docs/mission descriptions.doc differ diff --git a/legacy/docs/mission scripting.doc b/legacy/docs/mission scripting.doc new file mode 100644 index 0000000..5b29921 Binary files /dev/null and b/legacy/docs/mission scripting.doc differ diff --git a/legacy/docs/missiontext.doc b/legacy/docs/missiontext.doc new file mode 100644 index 0000000..338c8bb Binary files /dev/null and b/legacy/docs/missiontext.doc differ diff --git a/legacy/docs/sfx.xls b/legacy/docs/sfx.xls new file mode 100644 index 0000000..ea576a2 Binary files /dev/null and b/legacy/docs/sfx.xls differ diff --git a/legacy/docs/sketches/Artillary Conceptual.png b/legacy/docs/sketches/Artillary Conceptual.png new file mode 100644 index 0000000..b172c48 Binary files /dev/null and b/legacy/docs/sketches/Artillary Conceptual.png differ diff --git a/legacy/docs/sketches/FACTORY.bmp b/legacy/docs/sketches/FACTORY.bmp new file mode 100644 index 0000000..02e24ec Binary files /dev/null and b/legacy/docs/sketches/FACTORY.bmp differ diff --git a/legacy/docs/sketches/air traffic control.jpg b/legacy/docs/sketches/air traffic control.jpg new file mode 100644 index 0000000..0d08ee2 Binary files /dev/null and b/legacy/docs/sketches/air traffic control.jpg differ diff --git a/legacy/docs/sketches/artillary1.tif b/legacy/docs/sketches/artillary1.tif new file mode 100644 index 0000000..9876da5 Binary files /dev/null and b/legacy/docs/sketches/artillary1.tif differ diff --git a/legacy/docs/sketches/artillary2.tif b/legacy/docs/sketches/artillary2.tif new file mode 100644 index 0000000..e0c78da Binary files /dev/null and b/legacy/docs/sketches/artillary2.tif differ diff --git a/legacy/docs/sketches/assult aircraft.bmp b/legacy/docs/sketches/assult aircraft.bmp new file mode 100644 index 0000000..9f6c246 Binary files /dev/null and b/legacy/docs/sketches/assult aircraft.bmp differ diff --git a/legacy/docs/sketches/engine artifact.jpg b/legacy/docs/sketches/engine artifact.jpg new file mode 100644 index 0000000..27cd000 Binary files /dev/null and b/legacy/docs/sketches/engine artifact.jpg differ diff --git a/legacy/docs/sketches/galax warehouse.bmp b/legacy/docs/sketches/galax warehouse.bmp new file mode 100644 index 0000000..7eb5b20 Binary files /dev/null and b/legacy/docs/sketches/galax warehouse.bmp differ diff --git a/legacy/docs/sketches/galax warehouse1.bmp b/legacy/docs/sketches/galax warehouse1.bmp new file mode 100644 index 0000000..5a025f2 Binary files /dev/null and b/legacy/docs/sketches/galax warehouse1.bmp differ diff --git a/legacy/docs/sketches/galaxite miner image front.bmp b/legacy/docs/sketches/galaxite miner image front.bmp new file mode 100644 index 0000000..05e21a3 Binary files /dev/null and b/legacy/docs/sketches/galaxite miner image front.bmp differ diff --git a/legacy/docs/sketches/gun tower.bmp b/legacy/docs/sketches/gun tower.bmp new file mode 100644 index 0000000..5ec3612 Binary files /dev/null and b/legacy/docs/sketches/gun tower.bmp differ diff --git a/legacy/docs/sketches/lite tank vehicle.bmp b/legacy/docs/sketches/lite tank vehicle.bmp new file mode 100644 index 0000000..68e6f78 Binary files /dev/null and b/legacy/docs/sketches/lite tank vehicle.bmp differ diff --git a/legacy/docs/sketches/machinegun vehicle.bmp b/legacy/docs/sketches/machinegun vehicle.bmp new file mode 100644 index 0000000..9bb7043 Binary files /dev/null and b/legacy/docs/sketches/machinegun vehicle.bmp differ diff --git a/legacy/docs/sketches/med tank001.bmp b/legacy/docs/sketches/med tank001.bmp new file mode 100644 index 0000000..674eefd Binary files /dev/null and b/legacy/docs/sketches/med tank001.bmp differ diff --git a/legacy/docs/sketches/mobil hq.bmp b/legacy/docs/sketches/mobil hq.bmp new file mode 100644 index 0000000..9e409cd Binary files /dev/null and b/legacy/docs/sketches/mobil hq.bmp differ diff --git a/legacy/docs/sketches/new radar fac.bmp b/legacy/docs/sketches/new radar fac.bmp new file mode 100644 index 0000000..fa5e435 Binary files /dev/null and b/legacy/docs/sketches/new radar fac.bmp differ diff --git a/legacy/docs/sketches/newhq2.bmp b/legacy/docs/sketches/newhq2.bmp new file mode 100644 index 0000000..3d5e201 Binary files /dev/null and b/legacy/docs/sketches/newhq2.bmp differ diff --git a/legacy/docs/sketches/orbitshot4.bmp b/legacy/docs/sketches/orbitshot4.bmp new file mode 100644 index 0000000..7d03814 Binary files /dev/null and b/legacy/docs/sketches/orbitshot4.bmp differ diff --git a/legacy/docs/sketches/research facility.bmp b/legacy/docs/sketches/research facility.bmp new file mode 100644 index 0000000..8f061ff Binary files /dev/null and b/legacy/docs/sketches/research facility.bmp differ diff --git a/legacy/docs/sketches/rocket tower.bmp b/legacy/docs/sketches/rocket tower.bmp new file mode 100644 index 0000000..6007211 Binary files /dev/null and b/legacy/docs/sketches/rocket tower.bmp differ diff --git a/legacy/docs/sketches/rocket vehicle.bmp b/legacy/docs/sketches/rocket vehicle.bmp new file mode 100644 index 0000000..737ed11 Binary files /dev/null and b/legacy/docs/sketches/rocket vehicle.bmp differ diff --git a/legacy/docs/sprites.psd b/legacy/docs/sprites.psd new file mode 100644 index 0000000..ed2417e Binary files /dev/null and b/legacy/docs/sprites.psd differ diff --git a/legacy/docs/sprites24.psd b/legacy/docs/sprites24.psd new file mode 100644 index 0000000..baae311 Binary files /dev/null and b/legacy/docs/sprites24.psd differ diff --git a/legacy/docs/voice_assignments.xls b/legacy/docs/voice_assignments.xls new file mode 100644 index 0000000..4d67172 Binary files /dev/null and b/legacy/docs/voice_assignments.xls differ diff --git a/legacy/updAuthorKit.bat b/legacy/updAuthorKit.bat new file mode 100644 index 0000000..ef331cd --- /dev/null +++ b/legacy/updAuthorKit.bat @@ -0,0 +1,21 @@ +copy bin\TemplateExtractor.exe authorkit +copy "game\win_debug\Warfare Incorporated.exe" authorkit +copy "game\palm_release\Warfare Incorporated.prc" authorkit +copy game\htdata816.pdb authorkit +copy game\htdata824.pdb authorkit +copy game\htsfx.pdb authorkit +copy bin\m.exe authorkit +copy bin\mcl.exe authorkit +copy bin\m.dll authorkit +copy bin\amx.dll authorkit +copy bin\palbin.exe authorkit +copy bin\shadowmap.exe authorkit +copy bin\packpdb2.exe authorkit +copy data\art824\fixed_8bpp.pal authorkit +copy docs\authorkit\AuthorKit.chm authorkit +copy game\res.h authorkit +copy data\GobTemplates.ini.pp authorkit +copy data\grassy.tc authorkit +copy data\desert.tc authorkit +if not exist authorkit\art824 mkdir authorkit\art824 +copy data\art824\*.pal authorkit\art824 diff --git a/m/.cvsignore b/m/.cvsignore new file mode 100644 index 0000000..0ca40fa --- /dev/null +++ b/m/.cvsignore @@ -0,0 +1,2 @@ +bin +SpiffLib diff --git a/m/AboutForm.cs b/m/AboutForm.cs new file mode 100644 index 0000000..b61f58b --- /dev/null +++ b/m/AboutForm.cs @@ -0,0 +1,193 @@ +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; + +namespace m +{ + /// + /// Summary description for AboutForm. + /// + public class AboutForm : System.Windows.Forms.Form + { + private System.Windows.Forms.PictureBox pictureBox1; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.LinkLabel linkLabel1; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.Label label6; + private System.Windows.Forms.Button button1; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + public AboutForm() + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + // + // TODO: Add any constructor code after InitializeComponent call + // + +#if false + System.Reflection.Assembly ass = typeof(GobImage).Module.Assembly; + Stream stm = ass.GetManifestResourceStream("m.EmbeddedResources.about.jpg"); + pictureBox1.Image = new Bitmap(stm); +#endif + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(AboutForm)); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.linkLabel1 = new System.Windows.Forms.LinkLabel(); + this.label5 = new System.Windows.Forms.Label(); + this.label6 = new System.Windows.Forms.Label(); + this.button1 = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // pictureBox1 + // + this.pictureBox1.Image = ((System.Drawing.Image)(resources.GetObject("pictureBox1.Image"))); + this.pictureBox1.Location = new System.Drawing.Point(8, 8); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(324, 327); + this.pictureBox1.TabIndex = 0; + this.pictureBox1.TabStop = false; + // + // label1 + // + this.label1.Location = new System.Drawing.Point(352, 32); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(352, 16); + this.label1.TabIndex = 1; + this.label1.Text = "Mission Editor for Hostile Takeover"; + this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // label2 + // + this.label2.Location = new System.Drawing.Point(352, 80); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(352, 16); + this.label2.TabIndex = 2; + this.label2.Text = "Copyright 2003, 2004 Spiffcode, Inc."; + this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // label3 + // + this.label3.Location = new System.Drawing.Point(472, 56); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(48, 17); + this.label3.TabIndex = 3; + this.label3.Text = "Version:"; + this.label3.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // label4 + // + this.label4.Location = new System.Drawing.Point(528, 58); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(100, 14); + this.label4.TabIndex = 4; + this.label4.Text = "1.0a-beta1"; + this.label4.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // linkLabel1 + // + this.linkLabel1.Location = new System.Drawing.Point(352, 149); + this.linkLabel1.Name = "linkLabel1"; + this.linkLabel1.Size = new System.Drawing.Size(352, 23); + this.linkLabel1.TabIndex = 5; + this.linkLabel1.TabStop = true; + this.linkLabel1.Text = "REPLACE ME: MAIN SITE FORUM URL"; + this.linkLabel1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + this.linkLabel1.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel1_LinkClicked); + // + // label5 + // + this.label5.Location = new System.Drawing.Point(352, 120); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(352, 23); + this.label5.TabIndex = 6; + this.label5.Text = "See the mission editting forums at:"; + this.label5.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // label6 + // + this.label6.Location = new System.Drawing.Point(352, 208); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(352, 32); + this.label6.TabIndex = 7; + this.label6.Text = "Hostile Takeover is a registered trademark of Spiffcode, Inc. All rights rese" + + "rved."; + // + // button1 + // + this.button1.DialogResult = System.Windows.Forms.DialogResult.OK; + this.button1.Location = new System.Drawing.Point(624, 304); + this.button1.Name = "button1"; + this.button1.TabIndex = 8; + this.button1.Text = "Ok"; + // + // AboutForm + // + this.AcceptButton = this.button1; + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.BackColor = System.Drawing.Color.FromArgb(((System.Byte)(237)), ((System.Byte)(235)), ((System.Byte)(222))); + this.ClientSize = new System.Drawing.Size(728, 342); + this.Controls.Add(this.button1); + this.Controls.Add(this.label6); + this.Controls.Add(this.label5); + this.Controls.Add(this.linkLabel1); + this.Controls.Add(this.label4); + this.Controls.Add(this.label3); + this.Controls.Add(this.label2); + this.Controls.Add(this.label1); + this.Controls.Add(this.pictureBox1); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "AboutForm"; + this.ShowInTaskbar = false; + this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; + this.Text = "About"; + this.ResumeLayout(false); + + } + #endregion + + private void linkLabel1_LinkClicked(object sender, System.Windows.Forms.LinkLabelLinkClickedEventArgs e) { + LinkLabel link = (LinkLabel)sender; + System.Diagnostics.Process.Start(link.Text); + } + } +} diff --git a/m/AboutForm.resources b/m/AboutForm.resources new file mode 100644 index 0000000..faeb995 Binary files /dev/null and b/m/AboutForm.resources differ diff --git a/m/AboutForm.resx b/m/AboutForm.resx new file mode 100644 index 0000000..e42b4af --- /dev/null +++ b/m/AboutForm.resx @@ -0,0 +1,1068 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + False + + + Private + + + Private + + + + /9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAUDBAQEAwUEBAQFBQUGBwwIBwcHBw8LCwkMEQ8SEhEPERET + FhwXExQaFRERGCEYGh0dHx8fExciJCIeJBweHx7/2wBDAQUFBQcGBw4ICA4eFBEUHh4eHh4eHh4eHh4e + Hh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh7/wAARCAFHAUQDASIAAhEBAxEB/8QA + HwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIh + MUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVW + V1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXG + x8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQF + BgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAV + YnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOE + hYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq + 8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDybxNrh0FtI03TdC8KLAvh7SJiZvDOnTu0kmn28kjM8kDO5Z3Z + iSSck1QXxnch97aP4MZNnC/8Ihpec47/AOjVH8Q0DarpfDZPhrQ+/wD1C7WsE20bQbgJNwIBOcDmtklY + 4pTak9TqI/Gbl4GOjeDAoX94p8I6ZknJ9Lb6dMVDB4zu/McyaJ4NKbG2j/hENLHPb/l3rEgtEaEsFdnC + 7hzwOenvV600232QmVJXaTP3XAxzj0OafKR7WXc0IPF95LAFOkeC45AxyW8IaZgjt/y7etPuvF119s/0 + fRPBvkcf8yjpfoP+nbNQW2jxNOYnEhwxUbSBjGeTxVm00WN0MkkUjDcFADY7epFV7Ml1pdytL4s1RVdk + 0Xwc3Zf+KQ0r/wCRqZJ4t1dXI/sXwdj/ALFDSh/7bVu3GgQrcgBZBH5SlehPIzjj60snhjZM8Sxv8oyQ + Kr2YRrtbmNH4r1RiP+JJ4O6D/mUNK9P+vb3/AJU6TxNq42FdF8GnPb/hENL54z/z7dK2V8NZAbyjn647 + VaHhvd/yyJIA6k+lNUkHtn3ObfxLrQ4GieDenbwhpeTx/wBe31/T3qdfEGrlnB0XwfwGxjwfpfGPT/Rv + 8a6dfDWcfuuoznPsPb6f54q5F4ZIdv3J5zk+vP09zT9khfWH3OMbXtZ+fGi+DeM9PCOlcdf+nX/P5046 + 3rJZgNG8H8En/kT9K6dP+fb8f/1Gu5XwuC5/dZOTzn149KsDwsu4s0Oe/X3/APrmn7OPYX1l9zgTrGtZ + IGjeDz1A/wCKP0v/AORfX/PXD5NW1ne+NH8GhRnH/FH6X/8AI3+civQV8KqQQEPX1/Xp9TU48Lrk/uSe + vfv37U/Zx7B9Zfc86TUtaMuDo/g/7+0Y8H6Vjr/17fXv+NOj1HWWI/4k/g8ZI5PhDSvb/p256j8j0r0h + fDKGT/V9T1/HrUsfhpA6/uiOnf8A+t+GPrzRyR7E/WZdzzaO+1g5/wCJN4PPGcf8IhpX1x/x7f5/nLFe + as2c6L4PHBwT4Q0vnr/07D/Ir0tPDcQyDEemP8/lT08PxDgRY6/yPt/nij2cewfWZdzzMXWqlj/xJ/Bu + PfwjpfHB/wCnanSXOqrtxo3g4fLk/wDFI6X6H/p2/wA4r09fD0XBCAnnB6f096V/D0J2nyxjZjk9ePpR + 7OPYX1iXc8sW51fODo/g8kAnH/CIaX6f9e3+f0oNzq+WH9jeDhgf9CjpX/yN7/57+pL4eiBI28c9T16+ + 3vSHw9EA5KDJ59fT2+lHs49h/WZ9zyw3mrgt/wASXwbwDj/iktL9R/07e/6Usd7qhBLaJ4PBHb/hENLw + en/Tt716ePD8fI8rB+nv9Pr/AJxSDw5GVH7luh/nmj2cewvrMu55j9v1TkDRfB7fXwhpY7j/AKdveom1 + DWNpxong8HHB/wCEQ0v2/wCnbrXqT+GoNhBhOOuDVaTw5HyPLwD7deaXso9h/WZdzzP+1tT8vH9jeDt3 + r/wiGlev/Xt/SoJNa1cbmXRvBwwOh8IaX64/59a9Il8Nx7iNnPrmq0nhtM5MXbqW6Uezj2GsQ+55w+v6 + 0uc6L4OGAf8AmUdK9v8Ap196ik8Sa2u7/iSeDsgZH/FIaVjqP+nb3/T8T6BL4XXaf3XGOe3THt/nH5U5 + fC/3l8nJI4I+vpij2cexSxL7nCHxVrYX/kC+Df4QP+KQ0rnOf+nb2/z1qu3i/XAp/wCJP4O7AY8H6USc + 5/6devHpXa3HhgbCFi4z0z16j0/z+tULrwxhWXyDnPT1PPt/nv0o9kilX8zk5PGutJGzf2P4P4IH/Ioa + T7/9Ovt+vbpUb+OtcUHOi+DQQRwfB+l578f8ev4fh+NbV34bYI2IRxwOpz1H17AYrGvNBZVYeUevXqTk + EHt3x6elT7NGiq3I/wDhPtZw2NH8G5zgZ8H6UfX/AKdvb+XocoPiBrDDI0bwd2xjwfpXTn/p1/p2qlc6 + W8akmLgHJAbP94Yzg/T9eKoS2eAwZM85bJ6nnJxgf57g0vZ+RftDorbx5qzkl9G8GhV5P/FIaV6Dj/j1 + 96hHxA1cgMdH8GjLc/8AFIaUPTH/AC69Offt6isGaBorRV2nLsWIJ7DnPT8e34VUcBUHy9CRu3k9geuP + 88+opcq7ApNm78YpY7yz8E6r9g0yzub/AMPvNdfYLCG0jlkXUb2IMY4UVM7I0XIHIUUUz4q/8i98Pcgg + /wDCNy8HOf8AkK6h60VzS3Z0x2O68Y2nm6hpDY+94a0T/wBNlrVSy0lpAEG7B6jHeum16xaZ9DkBb5vD + Wi9Cf+gbbD0rZ8IaG01yu/JB6YJz1/z+dd8KPuKTPIrVLTfqzI0nwXNcKGVHwwyeO1bB8F3dsgGxlA55 + A4ruPHeuX/gGHRo9N0/Trhr6OV5PtUbtjaVAxtZfWq/gTxxrPjHxbBoeo6Zo8EMsckhe2ikVsqpYfecj + t6V3Qy6tKh7dL3dfwMfarm5epzOn+FWMy5Vw2MnA5Oa6fTvBEixF0RzlQMYGOgr0jStBiR5XCjKIWGfU + ZNeRWvxv8WiBQmi+G9uOM283/wAdpYXA1sVdUlewSly25upsS+E9kgaRXPTnHoKuWvhdpJt6oxZuvHWt + j4VeJtS8d2Gq3GrWen2z2csaILVHUHcG67mb0rb8d6jdeEPAt54g0+3tZ7m3kiRVuVZkO5wpyFIPQ+tR + PDzhW9i/iul82JO8eboc5/whjAq/lt04GAO1M/4RU+YFWKuXi+OPi+WeGGTR/DoSSRUYrbzA4Jx3lr3b + xnqPh3wbpT6vrtx5UG7ZGirukmfHCqvcnB9AO5FVicHXw0ownHWW1tQilO7T27nn9t4PkOD5R6dKvReD + XBJ2H371wmo/HzxF9ulfSNA0WCxz+5S7SSSUD1Yq6jJ9AOPfrVf/AIaC8Zf9Anwx/wCA03/x2ulZNjmr + 8qXzI9pSW7f3HpP/AAiJTrEfypknh8oSDH9Oa86Hx/8AGjZA0jwyf+3ab/47XoHwy+K2j+LbmDR9bto9 + K1uVgkQTJguW9EJyUP8AssT7Enisa+V4yhDnlHTy1HF05OyZbg8P+Y3CZ/Cri+GSw/1ePrR8bfE2pfD7 + w9p+oaTZ2FxPdXnkMt2jsoXYzZG1lOeB3ryv/hoDxug/5A/hnHvbTf8Ax2lhcuxeKp+0pJW9SpKEJcsr + 3PVh4XAx8nOeoP8A9aj/AIRnaQRH06V5R/w0N40/6BPhf/wGm/8AjtKP2g/Gr8DSPDB+ltN/8drf+xMf + 2X3k89F9WenvoJU/d7GpIvD5blYzzmqXwq+LWieLrmDRtet49J1uVtsezJguW9EJ5Vj/AHST7E9Kp/G/ + 4l694C8ZWmi6Jp2jzwS6elyzXcMjMGaSRcDa6jGEHb1rjjgsU6/1fltI05KfJz30N4eGX/uU4+GXIHyH + GMfWvJz+0F44A/5A/hjH/XrN/wDHaaf2h/G2edK8Men/AB7Tf/Ha7HkmP7L7yHKgt7/cetHww3PyHGKi + fw8UOCh5968q/wCGiPG3P/Eq8Mc/9O03/wAdruPgb8Ttd8f+MrvRdYsNHggh097lWtIZFYsJI1wdzsMY + c9vSsa+V4yhTdSdrLzHF0ZyUU3dm5H4eLHbtqZPDRxjY34c/0rB+KHxn0nw5eTaN4UtIdV1SBzHPNNn7 + NCQeV4ILt24IA9TyK4P/AIaF8bIf+QT4YH/btN/8dooZVja8FOMbJ99By9hB2buesHwqf4Yz+IqtP4aZ + QfkwMdhXl/8Aw0N41P8AzCfDH/gNN/8AHauaR+0Brv8AaEZ1/QNKm088SiwSSOUe4LuwOPQgZ9RWryXH + pXsn8yOei9mzuf8AhHST80fv3pR4TZgSIuT14+ntXceHdT0PxH4afxBoN0l1a7HIypUq6jJRlPII4r5+ + g+PvjUxAro3hoD/r2n/+O1y4XB4nFOUaa1jvfQqcYwtd79j0R/Bz8/u26VXl8GybSAnGOlcIf2gfGYOD + pPhgf9u03/x2kH7QPjM9NJ8Mf+A03/x2uz+xcd/KvvI9pS7s6668JyIpJiyBxjrn/Oaot4SeXIWLGef5 + 1T8M/Ha5l1MR+LtDsFsH4M2nRurwn+8Vdm3D2GCPfpXp/jzXbTRvhZeeNvDTWOoKqwvatIrNE4eZIzkA + g9GPGQQRzXLXwWIoVI06kbOTSXbXzLiotOSex5vN4BlcFjEfy/Csu7+G8pUqIW6j8vyqBPjv40CDGjeG + f/AWb/47TH+PXjEcNpHhn8bab/47XZ/YmN/lX3i9rBb3+4pXfwwnZGCwNyc/z9vfpWHefCycocW5wSM4 + H19uvNdMfj14uPXSPDH/AIDTf/HahuPjj4olRhJo/hnBHa2m/wDjtL+xMZ/KvvH9ZprqzhF+GM+qX09t + 9rtLIxQyzZuvNCskcRkfHlxt0VCcH8Oar6r8EvE1nodxrZn09rG20yHUvM89wZElCssajYD5gB3FSMDH + B9fcdEE/iXwPYeIpYYrW6u4543FruRdu94io5JwVGCM85PbgcnrOmapJA1ub+/MZRojGbhypQrGu3Gfu + lY4xjpiNR0AryqtKUJOD6aHRTxEbI+ePi9F5OifD6PAGPDcnA/7CuoGitr9paz+wX3gq0xjy/DZ/XUb4 + /wBaK8+fxM9WDvFM9hTSvtOneHpiDhvDWjH8tOtxXVeDtK8q6QkfX65qto2z/hGvDPQn/hG9I3f+AEFd + N4cZPtaYxgEc/wCfpXsqb9jFeSPnasnKvJPuzm/2mI/LuPCoHT7NcD/x6Oud+AILfFjTwP8An3uP/RTV + 0/7UBBufCmOn2a4/9CjrlvgNdWtj8VdOur65gtrdYZw0k0gRRmNgMk8V9JhbvJnbtL82XJWq/NfofStv + BsivGIPET8n6Gvi6z/49k+lfaV54k8LCxuhH4g0gs0TAAXseScH3r4utRiBR6CsOGFK9Vtdv1Na9ueNn + 3/Q91/ZXi83SfEwxnE9v/wCgyV1X7QERj+DmpcY/0m2/9GrXKfsrappGm2XiOPVdTsrLzZYCguJ1j3YD + 5xuIz1FdV+0PregXvwovrPTda026mM8DCKC6SRziVSeAc15+KjP+19tOaP6BG31Z662Z8zW//H3a/wDX + ZP5ivoz9rwZ8IaJn/oJ/+0nr5zt/+Pu1/wCuyfzFfRn7Xf8AyKGif9hP/wBpPXtZn/yMMN8/0Ml8E/ke + GfD/AOyf8J9oH9ofZ/sX2+Lz/Px5ezdzuzxjHXNfVv8Axaz/AKkz/wAlq+Q9F0yfW9bsdGtHhS4vZ1gi + aUkIGY4BJAJx9Aa9Gb9njxsGwdU8Mj2NzN/8aqc6oYepVi6tbkdthwlKLfLG50P7So8Hjwxpf/CNHQfP + +3/vf7P8rdt8tvvbOcZ9a8u+FwH/AAtHwv8A9hOH/wBDFXvH/wAMPEHgPTbXUNYutKmiuZ/JQWksjENt + LZO5F4wKo/C//kqHhf8A7CcP/oYrowtOnTy6cac+dWepMm3K7VttD2/9r/8A5FDQ/wDsJ/8AtJ68B8KX + ljp3irStQ1SDz7C3uo5LiLyw+9AcsNp4OR2Ne+/tfEHwhof/AGE//aT14J4P0y31vxho+i3byxwX15HB + I8RAdVZsEgkEZ/A1z5HyrLHz7a3NcS71ZfL8j3P/AIWx8Gf+hQP/AIJoP8a4X4y+M/AnibQ7K18JaJ/Z + 9zDdeZM/2GODcmxhjKnJ5I4r0OX9n7wEkmxta8SZz0FxB/8AGq4T44fDDw94D8P6dqGjX2qXMl1d+Swu + 5Y2ULsZsjai88Vw4CeXPEw9nKXNfS+wVFW5XzpW+Rj/BPwh4k1jxxomt2ekzf2XY3sc093INke1TkhSf + vHthc++K3P2s/wDkqWn/APYFi/8AR81ZvwV8feLNL8X6J4dj1V7jSbu6jt2trgeYI0Y4+QnlcdgDj2rR + /ayIPxSsMf8AQFi/9HzV3fvv7Xj7W1rO1u3n5k+77F23ur/8A818OXVpY+JdJvtQh86ytr6Ga5j2B98a + upZdp4OQCMHrXvP/AAtj4M/9Cgf/AATQf414P4ZsYNV8VaNpFy8iQX2oQW0rRkB1WSRVJUkEZwTjINe/ + 3f7P/gO3JDaz4kJ9PtEP/wAZp53LBqpH6w5J26Dh7TXkt8ysfi38FgQD4ROT0H9jQf416d4Am0LUI21L + SPB02hB48LNPp8Vs8qkg4AU7scA8gA8YzXLeEfA/grwhtm0vTRc3q5IvbsiWYH1BwAn/AAECu50a9Ms2 + CSe3NfJYyrQfu0E7d2/0OrDufNebXyR8W+LAB4x1/A5/tO5/9GtXuH7OX/CE/wDCATf8JJ/wjv23+0Jd + v2/yfM2bUxjfzjOf1rw/xUf+Kv14n/oJ3P8A6Nauk8CfCbxH450J9a0i80eG3SdoCLuaRW3KAScKjDHz + DvX2mYU6dTAwVSfItNfkcNJuLTirvsfSv/Fqf+pL/wDJavkPxN5P/CT6z9m8vyPt8/leXjZs8xsbccYx + 0xXoyfs6+N2PGp+GT9Lmb/41XmF/ayWGoXmnzMjS2szwuUJKllJU4zg4yKwyShQp1JOlW59PuNKs5ztz + R5T6S/Zj/wCSNal/1+XP/otK+YrX/j1X6V9O/syEf8Ka1L/r8uf/AEWlfMVscWin/Zp5P/vmJ9V+op/B + T9H+h7b8OviJ8MNF8F6bpmveGzd6lAjCeb+zIZdxLsR8zHJ4IHNbl18Vfg7LbSxw+EikjIQjf2PAMEjg + 5zWX8Ofg14S8S+A9L8QanqmtwXN4jM6W80QQYdlGAYyegHetyT4A+CFs7ieHWPETPFGzgNPDjIGf+eVe + VXllntpc0p3u7+tyoe35VZK3yPnrRdK1XWblNO0nT7i/u36RwIWP1PoPc8CvoXxrouoeHv2T5dG1WJIr + 22igE0auGCk3iNjI4PB7V4D4a8Ra74en/tDQdUuLCYgbjG3yyAdAynhh7EGvoHx3rl94l/ZUn1vU/K+2 + XUcBlMS7VJF4i5A7dK9LOHW9vQvbk5o+tzKny8k+/K/uPnBfuV7F8M/iB8NdC8EafpfiLw6bzU4fN86b + +zYpd26V2X5mOThSo59MV46v3K9Z+Hfw7+Hev+DLDVtf8V3FhqUxk863TULeIJtkdV+V0LDKqp5PfNd+ + cKg6K9te1+m+zGua/u7+Z1n/AAtf4Nf9Cif/AATwf41LY/E34S6jeJY6d4JmurqQ4SKLRIWY/gDWZ/wq + P4S9/HV1/wCDW0/+N13fhay+G3hTSms9C1PQ4HZcSTtfRtNL/vOTk/ToOwFfK13g4x/dKbfm7GkVWb1c + UvkVdVuLZLOOG2sF0+IDK26oq+Xk5IwuVzk54rlLlYn5AyM9Pyrf10wTxfaLW4iuIHyVkikDK3Y8jjsf + yrmJnGdvvXDBGVR66nzx+2Kqr4w8KKowB4bH/pfeUUftiHPi/wAKHOf+KbX/ANL7yivMqfG/U92h/Cj6 + I9VttQEGj+HI8/MPDejgc+un25rodA1aNbgEuPbB9zXjXinW5LK70WAEbU8NaL3/AOoZbH+dT6b40+zI + AbK2kIXhnkkz39GH+TXs0ouVOK8keJWov2spebPavib4b1Px62jyaXqGm2/2GOVX+1yOud5XGNqt/drl + v+FI+LJBzrXhrH/XxN/8arM0v4hXFvZR3jaBEbV5PKWdvtAj34JK7t+Ccc4rYg+KkbKAdOshjqPOm9f+ + unpXdQxmMoU1TpvRCcE3d9SEfAvxX1/tnwyP+3mb/wCNVIPgf4v7a14aP/bzP/8AGq3Lrx5cWMqRajoS + WcjLvCTi4RiMkZAL9OKsWPjv7XcLBDplvLK7YVEeZnYn0Ack1p/auPWt19yJ5IR6P+vkc03wL8XN97Wf + DR/7eJv/AI1QvwK8Wg5GseGR/wBvE3/xquvXx0uSpsbYMvyspebIP/fdXbLxa93IIrbSop5SM7IjM59+ + A9Q82xy1v+CFai31OFj+CXi2K4glk1jw6USRWYLPMSQDn/nlXq3xx0K88f6DYafo95Y281reeezXTsql + djLgFVY5yfSqB1bViMf8Ixcf9+Ln/wCKqsfE4glaOXTraJ0PzKzTAg+4L8Vy1cdia1WFWT1jsXenFNdG + cl4S+E3ifQfF2ka1earoUkFjdpO6QzSlyFOeMxgZ/GvX7zX3NwW3nGfWuQbxTG42iwtz2HzS/wDxdTtc + 3znd/wAI3Ic9xDcf/FVli69XFSUq26CMopWgx/xe0TUfH/hrT9N0y7sree2vPPZrt2VSuxlwNqsc5I7V + w/g74SeJtC8Y6PrV7qugy29jdxzyJDPKXYKQSADGBn8RXZzazPYKpudEEAbhTIk65/N6jbxTGRg2Ft/3 + 3L/8XWlDGYijRdGHwu/4ik6cpc0nqafxw0G+8f6Bp+n6Rd2FvNa3nns107qpXYy4G1WOfmFed+E/hN4m + 0DxdpOtXuq6FJb2N0k7rBNKzsFOeAYwM/jXd2+qX0sQmg8PSSRuMq6R3BBH13UXWrX8cJefw+8UY6s8U + 4A/8epUMbXoUXQg/dd/xKnyTfO9zoL3Ws3W8E4z61zfxa0LUfHvhnT9N0u6sbee2vPPY3UjquNjKQNqs + c5IrPbX7ctk6da9f783/AMXV3TtXuJwTZ6IJ1Q4JiSdsfXD1zUeahNVILVC9op3i3ucf4L+EXibRPGWj + a1fatoMltZXcc8iQzylyFOSBmMDP410nxu+H+s+OvGNrrWjajpFvBDp6WzLdzSKxZZJGyNqMMYcd/WtY + 6lqm3/kXJRjv5Nx/8VVP/hKhHIVawt1dTgjMoIP/AH3XY8wxMqyr395K2wN0ox5W9zifD3wg8UaN4p0f + V7vVtAkgsb+C5kWGeUsypIrEDMYGcD1Feu6vrHmSEhifTBrmB4n+0yLDHpsMkjn5VUzEt9AHp7T37Ek+ + GpPX/VXH/wAVWOLxFXFSUq26EnFJ8jNI6hkkkPz71raHqscUgJb2ri7vU1tJBHcaNFbuRkLIZ1JH0L1G + niS1T7tlZj/tpL/8XXM6N1sKNRRe5yWtfBrxTqOualqNtq/h9Ibu7lnjWSeYMFdywBxERnmvR/hXp174 + F8FyaJqV1aT3DXck++2dimGCgcsqnPHpTRqepqu0eHJeP+mFx/8AFVU1DV7iCPzLvQ1hX1lWdB+Zeu2v + ja+IpKlN3SHFwg7xZ2+meIVEwJfj614drXwb8U6jrupajbav4fWG7u5Z41knmDBXcsAcREZwa6tfE1uj + ZFhZk9iJJf8A4upf+ExjRebG0x/10l/+LqMJWrYSTlS0uKVWE7c72Ok+D+i3Pg3wHc+HtUurOe6nnmlE + ls7NGA6qo5ZQe3pXjqfA7xekG0az4cxjH/HxN/8AGq9AvPFRtZRFeaXFbyFdwWYzISPXBfpUJ8b23/Pp + af8AfyX/AOLrWhjMVRnOpDeW+g5SpySi+nqdD4TFx4a8DaX4fup4JrizjZHeFiUJLluMgHv6Vr6dq5aK + aEuB5iFASTjkYriF1m7u4hND4da4ib7rxxXLK3PYhsU0atfxDI8KXA78QXP/AMVXHOm5ycmtWONRq1v1 + OEi+CHi0QhRrPhzGO9xN/wDGq9RuvC99N8B1+H63+n/2mERTKZH8jK3IlPO3d0GPu9fzrmpPiHaQs8Ml + lbRsjFXRjMCD0II31NaeMbi9ia4sfDkt5EDtLwxXDrn0yHPPT869Gvi8ZW5XU+y01p1RMOSN0uqt1OST + 4H+LyoxrXhv/AMCJv/jVI3wL8WHltZ8M/jcTf/Gq7J/FmtIhI8I3oAH/AD7XX/xVc/J8VbYqSNOtSFGS + PMm6c/7ddMcyzGe35ITjDrcyz8C/FQ/5jHhk/wDbxN/8aqKb4IeKEU7tX8N/hcTf/Gq37Px3rF/aC607 + wjNeQNkLLBDcupx1wQ5FVr3xh4pWJ3fwNdqiglne0vMADv8AeprH5heza/APZQfR/wBfI3tCt28M+B9P + 0G9nt5bi18wPJAxKHdKzjBIB6MO1YN3q8CvtaQdehOK4TVviGbgyg6baBuQMPPx+clctqPiV5h8kaR89 + Q5P8yayjg5Sk51N3qQ6bkkuhQ/avuUu/EHg+4j+4/hvj8NQvR/SisX48ztcweBJ2bcW8Nvz9NTvxRXzO + JSjWml3f5n0VBWpRXkjQ+JTbdY0rgn/imtD/AB/4ldt7e/6iuaa6YKpBbGDzv9yPT3/Wuh+JhB1rSlI6 + +G9CGc+ul21ctkF0QDnkcPzw39OfzrqpVpRijlnFczPYbq8eL9mDSZgWz/wlU6j6eRXnMeqTqvBPK8cn + 39qddeJ9Rl8GW3g8mP8Asq3vTeou3D+aV2E7gM4x2rG3qVXgE4P8Qz1Pt/n+fRSxTjuRKmmfVHxl+Iel + wfFjXfA/jexa98OhoGtrmAD7VprtbRkyR/3lySSp689cbTkeDvDureGfi34SuY7hdW8P6her9g1a2OYZ + wQflP9xxg5U88HrivCvG/inU/GHii78Ray8T3t3t80xjYh2IEGF7fKgra+HvxN8XeBhNFod4ptpyrva3 + KiWLeMEOFI+VhjqMe/bCjV5Ycq7a/wBdxtJyuzS8ReJJYvE+porSfLdyY+bGPnP+Fd/4Z8VX2nfAzXdY + 066mtr2XWbe0mmhbbIsPllgAw5ALZHp1rwq9vJb2+nu58F7iRpHI4BLcnjtzXQeDvG+r+F7e9srP7Hd6 + ffqou7G+txPBNtIKsVboR6jB5+ldksRCUUrbWMFFKVzqI/iV4l4I8QaryB1u5PT2P+eK2PFfjjxLqDaV + fa3EbZ306OOCd4mU3USlgJWJJ3MSSCw4OK5gfEvBx/wgvgb8NJ/+zrF8ZeL9X8V6hBd6tJABbwLbWtvb + wJFDBEuSERFAAAyT6/gBWkK9JyT5EiJU9LXPZPgT4ikv/iRYxMySssFxKgIzhlhcg4PoRXNn4leI5XLt + 4gv2Y8km7f8Axrzbw34h1Pw7rdrrWj3TWt/atuilCg4yMHIOQQQSCD2NdTJ8TpppGln8GeCZZXO55G0g + Ase5OGAz+FNzpKo5cqaaX6kqmuXlvY9X+H/jHUdY8OeMrbUr+4ure30hrpRNMXCSK67CNxODyfrgVw3/ + AAl8mPvD8xXN6x8SNXu9EvNDtNO0PRrK9Ki7XTLFYWnCnKq7ZLEA84Bx+ZzyJuCGIJHFaUvY3k5K1yal + JSSW9j6o1zWF/wCEN8ITN40tdAM2nFvLmlnUy/OeR5akHHvVj4cayk3jOwQfEu01NtzEWMEs++5wpOwe + YFQ5x3NfMuueJtS1aw0vT75ozDpUBtrYIgBCFixye5yTUfh7Xr3w9r9prGnMq3dnL5kRdAyhh6jvWP1a + Dptc2uvY1VlNP0PQ9U8aKNSudqNComfEbLgpyeCB6f0rvvAviL7V8K9euhr8OiFdTt1+1SmRQMqfl/dg + tz9K+crvUHurua5lwZJXaR8DHJJJrQtvE+o2/hi88ORtGLC8uI7iVSnzF0BC4PYcmuirToyglHR3RlTp + qEm/U9sXXnDhj8YbKMA53I96zL7gbOT+NYfxU+Iljq/jm/vtEeaaxfy1SUps80qiqW2nBGSD1FeN/aPb + 9Kcs2TjjpmtKdPDxlzPX7v0QON1Y92+Gfii9j8E+OtYtp54ruysYFimUgPGJJcNtI6ZA69sVx3/CyPEX + /Qc1n/wLf/GuV8HeMNW8LX09xpUtuUuYWgure4gWWG4jPVHRuCP8962P+FkD/oSfBX/gq/8Asqm9OM5P + kTT/AMiuROKV7WPQrrxZqep/AebUr65nup7TxEsFtLPIWkVWg3MoY84zzivNW8T3pUjkcdnqv4q8d6t4 + g0y20mSHTtO0u2kM0Vjp9qIIfMIwZCByzY4ySep9a5r7R7fpWlCVKCfMlq7kzgpM+n/jHqE8XxI1WNfi + ZaaKB5OLJ7i7UxfuU7RoV5+9we/rVbwrq08fhXxlcHx0PFRTQpwLC2lmcx5wPtBEwXhO5UEgH6Cvn7xd + 4o1PxX4iu9f1d0e9udnmtHGEX5UVBgfRRT/DPibUNA/tP+zZEX+0tPl0+53qDmCTG8DPQ8DmuVUoqko3 + 1Vui6fK5rdc7kajeKLtupP4SEV6X8F9J1XU4b3xvdaTc6lYaR/x6WUKs73t1xtUdfkUkFjj8+a8LDvhu + GyOnH9K29a8aa7qekaZpMlwLWw0qEx2sFuvlKMkbmOOWYnqT/U10V6sJQ5YWV9zOFOMXdnvcVh4/8f8A + hjUdP8UaBqNn4gsmkvNKvprVo45VY5e1Yngeq5PYDgDnwabWL5JHjmwsikq6spBBHGCPWsqy1/VbO7hv + LXUrqKe3dZY3E5yrKQQR9Din+KvEd54j1661vUUtkvLpg0xgjEaO2OW2jue57nmooVlRbWln+H/DlThG + Sv1Nm38YeJbW2W3tfEOpwQoMLHFcuir9ADxXoP7QXirxDY/Em5trLXtUtoRaWrCOK7dFGYUJOAe5Jrw8 + T+/bqRWr4v8AFOp+KdafWdVdGuXiSNvKQKMIoVRj6CieIpuqp2Wif42Go2jYkn1K4lkeaa5mkkclndpS + SxPJJPc16/4a1G1i/Z/s5b3xdqnh1T4ilUXFlA8ryHyB8hCyJx36np0rwN5uDkn/ACK6jRPiBquleGF8 + O/YNHv8ATkumu0ivrJZ9srLtLAn24rPE4xVIpLox04qLudnfa9pIspzb/GDxbcTCNtkT6fMqyNg4Un7Q + cA9M4OPSvM2uxsznOTwM4raf4gyyRMg8K+EeRjI0eMEe/sa42SbLn5T1AGSORz/9es4Y9wv/AMD9EipR + TZ7R4h1/VdK+AXgaXTNSvbIzX2oBjb3DR7wJBgHBGev6muDi+IHjKCZLiHxVriyI4ZD9vlPPbjPI9jT9 + J+Imr2Xhey8OS6VoeoafYySSW6X9gszRtI2XwW9alt/iTe206z2/hXwdFOhBR/7EhJQ9iMgjIz+lcn1y + 1/dvq/xZrZdzd/aVKwfElp/JSCe70+1ubtFG3980SlyVHQ9yPx968ukuxgFs5PXn6Z/SpfEeuaj4g1i5 + 1nWrqS9vrp9800hGWOAPQADgDAwBj24zXYbBwcDr8309qz+uTUVFD5E3c0vjE/maR8P27/8ACNyZ/wDB + pqFFR/Fr/kBfD3jH/FNSf+nXUKK8qo7ybZ6EFaKPpvTPhV8MvE/hjw1rXiC/8XR6nL4b0g3CWEtsIV2W + ECrtDoWHyhc5PXParM/wF+D9uWdr74gH7oYie0wu7Jz/AKvseCa1PDskX/CDeEQ7OGTw5pQUr2/0GE/5 + z/hW1azSphRNgjpngbiM42npwR+dfKYjOcVTqzhF6JtbdmfT0clw1SlCbWrSf3nC3vwQ+C1rGsjX/wAQ + 2iI+/HJaNt54yPLz39CPWsvxJ8M/2ftA0U6pqGu+OxCF/dolxZs8voEBT5s56jj1IANeoA7SZpZMGU5C + k4AP069vpx+NeN/EW/jj8Zx3UeladfXsN4ILf+0zctaWgJG6Zktv3jNuVcY6F2JDHBWsHm+LxFZUm9/I + eJynBYek6jTdvMU+D/2fVsBdG7+J2fLLGANZtJkZO3AU5Jxxgke4qlZ6L+z3cvNGh+KsbQhS6OLFSM5w + MYz2/lXf67No9rrelTanoVtpy6zpsV3f5Uyi0naFGaMHHVWcZIB3bgTkhscNrSS+GPE5ttM8M6Vq/wDa + Rlf/AE2W4aOeEDMcUJg6SP8AMFeQlVO0nbgk+usRipK2iff0/JPoef8AVcujP2k03B/ZT95J7a9Wvlcv + al4O+AVnoltqwvPiTdRXDShIoJLEyAxgM2QVHRST16A1MfBHwG/siPVEu/iNNbSEYME1hMyqUDBnVATG + PmUYcA5OMcHHNeM9KnsPEl/c6Hd6fcaNbmaGzjJSSWaMI2C2ZB5heTll2lVRnPygFarfDazlh1WzXUJW + t3tmj82IRlllTq0bDI27lJXIzwSCrcY0+tYjXTt1/rb+mZfUcBaPvvrfTqttL7PTW91rdbHqWnfBP4O3 + lil49549tleQrGslzZMzgfxjYjDaeMHPOeM84cfgj8Gwzr/aPjslPvAT2nT1/wBX7102u6tM2s6VcTss + umXVuLWORQSY5VOUXAwBlSw4GSwXHUiku5oG2HzXYJjYyrjHGf8AAc5GfwNeTjc3xlGtyxenTQ7cBlGD + xFFSktepz9x8DPhFACWvfH5AK7is9phc9/8AV9B0NVr34OfBu1VZXvviC8bcl45LRgvOOR5efyFdlazS + phRNgjpngbiM42npwR+dOB2kzSyYMpyFJwAfp17fTj8a4/8AWDGrr+B2R4fwb3X4nJ2nwV+DV6vnR6v4 + 4CMNyt59oVbPoRGc1I3wQ+EHLHUPHRUjO4S2pGcZxkR9Rnp7V0Uun2wmkmSMxyyZDsjsgOTySFIBJ9et + eW+IPiN4n03xTa2lvpcs9hbyi2kjhYEvKIkMy+wTzF27sgnJLHA29OFzjH4iXLGa+4mWQ4NOyj+J14+C + HwkZZJHufHqKGwGM9mQ+e4wh4574+lLN8Efg+mC+o+OhvBIHmWoOPxi4/GtTwTrn9saK8zpHBPBcta39 + uD5nlTIcMGySMDg5BIweGOM1pu6NBMyQXLvG4YusOF2c52jqRx9ax/t/HfzfgOpw/g6cuVr8TkF+Dfwa + llYf2l47D85UzWuRzj/nn696Ln4M/By3VWN/49cMu7KTWhxzjn93WX8R/EGq6NeaabXTJ8O8W2YANube + MxSAAlMqN2DjPQdGx20V5ZtcKiXP2jIDIyYYZYZUDHUlctxnCjJwCM7zznHwpxnzJ3vpba2m/wDX4j/s + DBPXl09f6/r0Zzdp8Hvg3cwmVNQ8fADOVaa0yAD1x5fSrU/wR+ElvlmvfHxA2hiJ7TjcOv8Aq+g6Gty6 + eBNqHenlnC7VA29+O34GvPPin4o8R6D4i0uO0heeyNtLLAkI/eNdYP8ABuztRWU4wV+c5DY4eGzvH4io + oKVr+Qnw/g10/E3rz4QfBy1jSRr34gPGRy8clq2ATjkeXnv2FS2XwW+Dd3GJItX8cCMruVzcWpDfQiM5 + rb8OXN9c2H2/VQ0EtywKwMuwxkKAwK9RllcjOeMVbl0+2E0kyRmOWTIdkdkByeSQpAJPr1rnfEWOWjl+ + BX+r2C1938TnT8EPhCBuOo+OymMhhNakdBxkR9RnpSL8D/hKyu7Xfj5FVsKxuLM7h6jCHj64NdTFJNFI + IftgCmPKxkAsq8A4z26dQTz15FSW5McZAKCMsN4B3/TcD2HrS/1ix3834E/6v4NL4fxOD174UfBjQ4oX + urz4hMk+QpR7TjGPvboxjPbPXBrnLjwp8Do51hih+J902OTHLpw2nOMHcR3Hpg54Jr1iYxS2twPs1xLt + b5mEPyFDnIA6sOPT1rJj0TSpnR5tOtImUAD92okX03ccE46GqXEGOWrl+Ao5Fgnpy/icxpHwv+Dt/p6X + uz4lWkbgMFneyzjsflUgg8H8frSal8OfglY2qXM0/wARGSQsDsa0JXBHX5OM9R7A13Ie2jZd1yJW3bY1 + yGYnnoByeAT7AE1Fe/ZinkyKwVPkC7BxxnGOmO2DxSXEWN6st8PYJ9PxPNb/AMH/AAStpcJa/FC4TapZ + 4pdOCgnt8+DkdD29609O+F/wdvNPS+kT4nWkLgEGZ7EnBPBwitkHg5HHP1x2trp2nJ8osbEHtm3UDcRn + G0j3H51fB2kzSyYMpyFJwAfp17fTj8aj/WHHLRtfcC4fwL6ficfF8E/g3JZC8i1Lx5JCxHzJNaNnPAPE + fI+lEfwS+DsiKwv/AB6CyglTPaZXPY/u+tdzEscS7kKxSBiSd20cnJ7YBJ/Oq024sLhUBjlyQfUjk546 + 9KP7fxrXxfgT/YWC5rcv4nIJ8EPg2yMw1Lx4AuSczWv/AMbqCb4MfBuMsFvfiDJtXPyS2eD64JjrpZjO + jvEpLuBneoy2fcev0/wrG1bWIIJjaywymSJ1PDMolyOfXsB1q4Z3jpvdfcOeRYGP2X95jXHwt+B8Hmlt + Q+IBWIgM4ls8cjtlOev6VWh+GfwLkkeNr34jI2Twz2fzfTCcirsEFxezuYw4V/m8lWzhSeSB6VqtPBZx + PFHEVukXY1sijErY6buvQZJ4/Hmt3m+JXUy/sTCfymNc/Cb4K2yI01x8Rl39B51kT0zz8nH40RfCX4JT + YSG7+Ij9M7ZLM7fQ/c/lmr0Vldaji5tXWGNj915CxMnJIBPb2xWno9teCAC5t3h5BLDo2AM579z6dKiW + cYmKvdFLJMG9LfiZMPwP+DU4IS8+IIAcrzNZ9f8Av337VND8BPgxKWQ6r48QJnBae05PoCIs11Usczh4 + 7SEyqSN7FwAhHXPcjB6fy60Cwtx+9VAkrpsZo3aNSOOcBuvA9wOM4rGWeYpK/MvuLjkeEbtZ/efMP7Xf + h/RfC/ijwjoXh6a9m0y28MqYXvGRpjvvrx23FAFyGZhwOgFFW/21Bt8deFl548MoBn0+23mKK+mw9R1K + UZy3aT/A+axFNU6soLZNo+iPCDyjwJ4UTMRUeG9LdCI/nD/YYABuzyPvdh2qxshgunlRi7tj+LIIA446 + D046965bwT4p0e4tvDvhf7Q0Wp2/hrSSIZRt84HTbdyYznDYDcjhhtJxjmuv8lnjPmKp+XB3HkenNfEY + +M44ialpdv8AM+2wE4PDwcddF+Q63SK4ZWWWJbhuFZ2IJPJAIxz9K88+Iei3drq66xpNmy+RI1y8yAFY + HQgq2OxyST6gEkfeNdotxHaFTNIjyKjPFbKQ08ijqQnfGeo9KzvFP2m70r+0bHWbjSSke+Jg25XJ24Vg + pw3zAAA5XnByKMNVVCopy2DFU5VYSjHcyvHHjDRWto9V1t7K9vJcI8ixDyCxXAKAjqcElu5GeAAF5fQ/ + G2r+JrO4+Hz6fELXzPtE00aAzRRJg7ck4ZGJTAxlehyvTg9a0G+Ot3VvbzLdSX8bhGeEJA+24MLOqpkp + tcNgOicZZcggH0v4Sx+H7TQ7VdPiltr6VAfNeIB7iFnkVZQQx2/NFJlG2uMIcYIC/RYqdaKlNaJfjfr8 + jy8LTwEaMYP3qk/Vctunm3pr2MPXNVttC8SLZjTpb+3FxAo03+04rKKSUgnMs0wZRGQMEEAZOARk5t+K + Z1g+I1n4i03TpLm11KGHU309UKyxiVBJ9m8vGC6jKkKcEgDhshZ/i5oOrQ3dv4h0fMpgKtOv3kmkU/8A + LQZ9MAEdD6Eg1Lq1zbalapqvil5Yr1IpEWGNMspwdoVV+9heMZ57k9ayw2LhCEFGN29x1sA6k5VJyXKt + LXt/S/WxNY+J/EkcVy7eGtVvfDes6iJ4TFHsms5xOJA0c/lyRMVVcbMDO51z8vPb+F/E0PiCG6a1d5Ps + 0mPMkszE+8gbeQ7j7ucqGyMr0yCeH8HWGrx2F74t8JyXGo6hf332OFR5jLbwlMkmBf3LIAEYL1D7Nrfw + norXWNF0HSlEeoT6kC7BXUb5L24JJcRqMBixJYsMJkkls5Nc2Y06lWjGrHX7t3+PfTU7cDGlSlUpSp8r + Ttq3e2u6ta9ra6LyOmSAJdySW8c1zKRnbErSFgBxhR09PlGT6dK5zTPEep3nitLNtBgGlXQH2TU0nkab + ASRvMdNmwRmRDHtYq44YbgDnjvGF5qniHw5FDrEFxZGO/kWWGCSWMIRErRLNgqzDbITwVJIUhcE4wPCf + xK17wvpMaLZ/bpTGkEHmybpZlUFmdsZOSSxJyeuPcxgsNQVF1Kru3pr0Ixqxk8T9XoR1jro99Pu/zPbL + 0s0s1pZgB2Uv5iyHEWCQBgDHTjBx0J54x458TbLUzq/h5tKsGEl9eyShbdEV3Dx22PNI+XeW8w7ieM9Q + Bxq3Xxq1S4+22d3pls1nFAVu3jnAOWUgqhI5OeOQOf13dH1XR7zxqmh6Xb6drWiPItsl49+Vu4QYmYlE + AWJ1VgFI4cls8feF0cLZN4d8z821uu9t/wCuo6tfEYGrCWLhyrW1rPVNX67frtsd3b+QkjW1vaxBxvf9 + z+75YsXZlAw2SxOepPJ5zUBv1WT7DF5ks25VcROP3K55LcgKMdO/1pVTzrWC6t9QllhljWRHIA3gjg9A + Rnrjg89qrLZzW8jSMDHJJgbgCwcZHLDOS4A65B5Gc8AeJ8Tuejy+z0Zx/wAWboa5qPh7wtbfKwvUluZl + bP2UCNlQEcbiwdmxnOF5GGrv4LGzhso4IUZVQllyxBByTknqSSSSScnJJJJrFi06G0v2mka+lmlkE8c0 + cQc7yhQFsKdg25XDZTDZGD02LDT3igF15ySXDkCdpSTIcDO3B44yThePTrWlWUHCKgmrLW9tX5W6f13D + 2lkovZN/1+CJIHuli2TSwysMujrDtk3cBcnJzxnPA59K8j+OGl2ms3mmPY3cc81sxd1EgZXV3iQDBO3P + BOBzhTkdDXrEwaO4Ku+4jORknH0/T6VnS6Lpl5K5mtg0zOJTMZnWQMMFcODuABAwAcDHAowlZUqylPb/ + AD/y6edrjatrEuaJDYm1itbHy4fswFtCXY87BwozncB936g1HfufOns7TaJCCTIJP9Sc8LgDHII4ODjn + niotI+yWk0VlD5aQxKfJCr1GfQ9++fp61ekuRdRt9iI3KCRlciQcce3se2e+KhaO71M2tbININo9vIst + mPPAXe74DsRnBPfoccEd+1TecJZzCIxJMqEBkYocYIOV6EAeh9PpRbSzzgRvavbEDJLsp5z04NQ3FrNH + tP22ZsAZzt5OMdh/Kp59WV7NdSI36rJ9hi8yWbcquInH7lc8luQFGOnf61f84GBSfkVQFA9BjH41kLZz + W8jSMDHJJgbgCwcZHLDOS4A65B5Gc8AW/thhmEc1vJJvVfLeGFmznpuAzsHuflwRzyQFKPRFKS3LMsUM + saltxYHIJO3b6dP8aZA90sWyaWGVhl0dYdsm7gLk5OeM54HPpTobeZ4UuDLD5mcSKQdw4yVx9OuP60k0 + E0YaZzvRW2ttycZOOB6UrNaIXNGW5X2QwXTyoxd2x/FkEAccdB6cde9WLdIrhlZZYluG4VnYgk8kAjHP + 0qNEM0SlkViyDIY/MPQGm2cLq+93QxxA7eMkj6EYz/hQvNjlqtEXlE0UTeXCFUMwWTcNoGfun/I+nFUj + LE1n5C3V9LIZDscONwPGVGRgDHtTJZIUuLh/JkkDKGRowMggAYyeCB7/AErm9Uvpykoe4H7rKbxIwQBh + gHb/ABtjGew5wO52px5tjKStubOoXQskeO3ihTZwdzbMcnHJJyeM/wBBXPzpc6i63fnKpO3y4j6gc8Ac + c5/Osy2LzziK9u5pEi/1SSAsMgcEADn61s3k97p8ataWyWrSR7SS+4seoJHQdSea6VT5HpuZ8zaLF/fy + acIjGzvcysGR9ioExgbc55PJ/wA8VlWVnc/2lFcyWrkNKR5gwdrH+HGeM9M9utS2tpbzSHfF9pJO/hcl + WPc57ZOeM9q3LbS0eTzokkMgOzazgKOBwPr+ZqU1BDa1JLKxSCd53BE+8OoXG2Nu5Hv9c1pS2fmqfs8q + +aVOVlGFZeewPAzjkZ6niopRu3JIVBxgKuSB0zyetWLZo/PXa8q7VJJyAfw9f8+9csqjNVTurkGkE/Yw + QS7bjvx0YnnjB6c/l15zUWpPK081rCy/aGTe8olP7puoGMYyQRwSD1PPFI1xbS6ogiwnycAHAfk54A9N + v5067vlawmmtSuYI2kI2jEgAyR7cd+2ec9KqKfNotyZOyuz5s/bGLHxh4T3h9w8NKDu68X14Of8AOKKf + +2bPFc+NfCs0Lbkbw2uDjH/L9eCivu8NFxowi90l+R8LiZKdaco7Nv8AM6pLeyj+yXrwxJeNoGhLFcH7 + 6lNLs3TB6rhsH5cHODzjFek6Nf6nqN7LoGpyWlreW1us0rOoe4j8w4ClMBQ6gOGPO0sgK53KPM9k8j6L + 5Mk8OzS9JYv9nV43xo1gQmSecEZKjHBGDng8/wCE/FV9pPjya+vAzSvdTTXCQlvn3n9+q/3geHA6lolz + 3rjx+HVam7L3lsdmAxDo1Fd+69z6FGgadK6oIUX5t6uw3vu3bhhmJPB6c4HbFV9W00XKy6bPghlyygEc + 5B3DjHXnHfvnmrVpqUctwIbZluHkw4eEAKiNjbli3Oc546jnHSr0MH2oyxrKySgjZtUEN3OCOw7+nPWv + kOebfvs+vcYQV4rQ890b4c6fEkwvrt7+Mg+QMLGIQS2QMcPw2Bv3FQu0YHBXT/BMWkebN9tub2bZ5asY + whRPvbTj7xzjLsSxwoJ4FdzcoqsHDB2PJOBj2pqeTIdr/dI4BGVPt+o9q3eOrSbUpbmKwdGNnGK0MTSd + RgtYZbbUWNsbSB555HbKyIoZ2JPPCrkk9gpPQVxPj+w0LxJAvivw34m0u80y2n+y/wCjTSkpM6kjzAyg + oPk4OPm65OMDrPFmnX09u40myi88/JMjAYlhdCjr1HykMQRnkcd8Hjbq2mvXv7zUbHSrvVZbxLq+ttVu + J/JmxBPtlmMQLkp9yONe846AV62WwpX5vtP+rnlZnOrt9nqaPww8U3wnNlbCI3shdjEESMzynqW4AaRi + AC7ckgZOAMGtfEGHVmtr3wX9ks9czNLrB1C1XUC9wD+5jjMzgRIT5m9kxhWBAYgqKepaBqHiLTbfWtMR + fDdzMpuZrYuzTWtw8r7o4yAAIyoRlYYYBgDkqc4Ft4dezuLp9WhvhJDaEzG3ljGCOeRtbLEcggEDp3xX + XiMXGnO0Em1/X9Po/UjL8ujXhKdaTiraf5+dtPd6q7T0PU4tf0qeSOUtFaavdxxyXFvhyrz+WisFY5LK + cAITksgTrXnnjfwHp39sxzXOpXkV+uGug1hKsyRoUJZYchmbYeAMbjgA5ya5WxOp3evxXVzpFxqbBiLn + yD5Us6SYIIkKN0KjqrHluM5rvLyfxvdeB44tQXTNUtriAR3EjoHuBGd3Kytl1CZA9MA8gDA43QjCu6tR + 2u733Wurjtt8/wBDtlVX1SCw7vJXTWzVtFJNP00t10e5i6h8PfDOrWEk3hhL+1m0mYR63aatYy2t3AWQ + mIsjynCOFdsj7u3JwucdX4i8DXeuzWmoRmGw1eUJ9rurdArLIv8Ay16/MMAArnng4z04/TB4u1rGlW89 + vaalFZyeTEkaiJIGdwfKXO0ITKw3KDtDOoAPA9X8B6bq+m6EtlrVxbNdRORG8TFkVcDABZQeuevTIHpX + Nj8w5tKDsuui+T9D0cLl86NL/aJ80tGtW9Gl+KejJ/BGm3ml+F7LT9QltftcO4SGCQshJcn5SwB6Edvz + 4z0DSGaN4CSItpVm6FB0yD69f6VmPFLKgWK5VnjkEck2OYz129eT3OcAepIqS5NyLbCONi8HJzjrzXju + Nnc3nVdZylLdnl3xK8QeMtP+IFsmjgR2KBFtHfb5TZXDvg45+YLtJJG3PAcZ7z4eajqF94W0m6uUngN1 + ZKdzw4Xof7qqoBABG3HXAHc+XfGPSr8alpOpGQ3M8d1L5zQqzbIi0axoMLx8vJX1Zj/FXtk08nlDyZ3Z + dv3QcgAnJOR9OleliZfuKStpZ69N+mn6v5GXLF07pK7fzskvPr6L59FkWFTPIyt5asE8wthScf5/Oqmn + TJPPNLsBCMoXJ+UHaOfQ85FX7OV3hAl84owIIY5OOvH5VnakYtLhl8y4WzinbbFKuE8pvUBvlDcjAxjj + oec+erPRArx3MDw7rN1rF94hsdRshYWVhKwtb+U7UzHIyKu9/vMQm4/74B6rnRhtdQgEM8d3DmeMOrom + dwPYc479eefXIFc14B0SHRda8QXk9yl9Fc6oxt7xEVvmbkruUcN84BIAXIIzwK76W4eaYFl+bGGBySR2 + A7enPpXfjuaElzLdLtrp/T11792KCcrwenz+f9f8MRQG4uGDO/lykBWAAKkD8P8AOenFWBh8fOqnOPvf + pz0/D/CoooxlxuEZBwrFRx+f556/zqB4pZUCxXKs8cgjkmxzGeu3rye5zgD1JFealzGs3ymm0hmjeAki + LaVZuhQdMg+vX+lV4o0luZJPN85EG1VUlNg4HGOQffqMnBqC5NyLbCONi8HJzjrzUdtFOkgka8K8HlcZ + ZR2xjpTjdLciUU2Womu4bsxM84SRWAkaI4HJJBOAOnTHPt3q/H5SLuikCurH5i+AR+oGSB9feqE08hjB + indlI+6DkYJyTkfTpU9s9wbNZViYZBxKzcdcYOT+X8uKp6mTQ2bcWFwqAxy5IPqRyc8delZ0xnR3iUl3 + AzvUZbPuPX6f4VZMsTWfkLdX0shkOxw43A8ZUZGAMe1UdQuhZI8dvFCmzg7m2Y5OOSTk8Z/oKpRu9Bxl + y6My9W1iCCY2ssMpkidTwzKJcjn17AdayoILi9ncxhwr/N5KtnCk8kD0qzOlzqLrd+cqk7fLiPqBzwBx + zn86vX9/JpwiMbO9zKwZH2KgTGBtznk8n/PFdS9xJJamb11Y1p4LOJ4o4it0i7GtkUYlbHTd16DJPH48 + 1UisrrUcXNq6wxsfuvIWJk5JAJ7e2Kr2Vnc/2lFcyWrkNKR5gwdrH+HGeM9M9utdLZWKQTvO4In3h1C4 + 2xt3I9/rmiTURK5V0e2vBABc27w8glh0bAGc9+59OlbUsczh47SEyqSN7FwAhHXPcjB6fy606Wz81T9n + lXzSpysowrLz2B4GccjPU8VHpBP2MEEu2478dGJ54wenP5dec1zynrdFpXVh6JHHE7SIAi7V3g7Uzj0P + Tr0yevWq+nTJPPNLsBCMoXJ+UHaOfQ85FaimUMyT72VslhnscHiqDx/YHYCPEcj/ACBFKhPw7HkDAx/M + 1le9+5orqyZNJaRXlpcRhtj4LxSN1DLyMZ/L371ynijTNVXw1qMSMJZrqwmAjjQh5NyMNg54JzjPPPrk + Cuou0SKdUj3bBjIAOV/zg1leN76RfCusXLJ80enzsyknJAjYgA4xzxz6VUJtOyNIU05J306nyt8epJZI + PARnt5bZ18MlPKlJLIF1K+UA556AUVH8cLuS9sfANzKAHfw04IGccanfjv8ASiv0CikqcUuyPgcZKUsR + Uct7u9tt+h6fe29x/ZelXEVyQFstGfaZCgj26JYkEEEZy23g8fJxk8Vzni6xe21GHW9OWMRi4M0I2bTA + rMdqfeGSrbwTyRuweME9feRbfDGj3MaRtL9n0hN7HmIf2HYNuHZT8rDPGBuORjNc9qmrwXWg/YJtTuby + 1eNhHbwxK8UcqELFsY9uNu5egXGcdM3uTHY9S+El3Be+GYCznzYUHkDIytsWKpwMAYZHjHfMR9a7i2tZ + VuBJbSn+INl8YbA6Z6Hr2IPsck/O3wM1HxDpOtx2L6feXFtJdMqwh1z+8AWQYP8AdKxSFjgAA5I3DPvx + ufI82GYOPkJVygb5xhgBx3/Mnp0NfJ5nh3SxDcdnqfWZbiPbYdKW60Lt4sFs6s7Q8Lj5z1x1wRxj24Oe + 1U4TK+pBmRHTYpiXAOeW5A7Dkc1cFtFckShEO4c7yFBGMEduenseeaqtbBLUSTv5kiuikMCFHTJKHIJx + xk+hwBk588707aDNfa2tLOe5u2eBIl3JLGoJ3jGAO2SeOe5rzKXU/C3ipL3xJd6Jq+jtZWzSpcLcpLLe + rHLDGyPBtVonbzlKfOwIWQZ+Qkeg+M7mwtfDd7DNEzW6xsgRcqQ53MrLk7dgwM4OPlx7DjvCN1p/inQb + zwxc2N3p80FyJ4tSSyEW7KFI1mZQAMoCQS+CSckttr2MrUHGTlvsv6W3zPKzFVG1ybLVrTbyvv8ALXqa + nhTxDa6voJu107UdJZJLcxxSXcV0ZIZDKFDYjjMbJ5BDJg4EkRBIYVJ4ssPtF+wW2dm8gsY4Rud/RdvO + SSSMd+a0/GcNppv9maHp8MkFnJA91MNiolxLwHc+WAvG4YHYOcAZNcPrd2kxvNJ0bVrxNSSEwSq0EsEI + VoGkeOO7OEWVYgXCvgEKQpJAVtauFc8TakrLr6dTKji+TCqdV3bvb9DjHvrvwxrG7Up9U0by1Cz2aRvE + 84bBYP8AL8yyQkKAWXGSea7j4Va1NrWt3txBdvcWMaGRYWUt5bs5wS5xhiC3BUbuTkYFW4vCl3b6Taxi + Pe9pPJ5Y1ZWkW2AVVaBTI8u4B0ZipO1XkfaAS2NDS9Zji8xrOxhW3gZTN5IxFAzjIDYJ2q8m4pnA4wBg + EDXMqVSdNKOy6dy8oxdCnzxlC8pLd9Nemh08VvpcU0cQtUjaR9zQrCFxuLY68DJDH8fep7+Gxs5BG7zr + uXKLBHkkY/hwc9SORUU8MrQ2smq2j27bfP8ALdSsqgkdCTnB9QMHHXiuV+LviTUtLMVvZaQ17PKI9jxC + QyTBXyUBXkABPm6cOMdTXh0qLnUUJaHr3c/g1Wr+5XOss7hC32RnJg2jynKj3yGOecfj75xkvnaO5WS3 + jJlU/KwV8FGOCQSDkHBBGOcenFN0ubz9Jt782McQnj3xEHcZg2CBxyo2jGPfOOMVLFOBFiNJgSC3lSDl + O2B1wOP1rKrDkm0vz/yClLmj29Vb8zDtPDmnx3QlNsqSRuGjIdgocNkYGex5HpW3GrwhJC0iQjKsqqWI + x/u5zXlXxG17xhYeO7RdJhENmEVbaSUho5dwO98HGT8wXbkkbc8bxnvfBtzdav4I0i8njMAls1eVWYIS + AAA2QAFDDngDgjBFdNelONGnOTdpX+X9dbFqDSbVrLzOhEsMXlmV1aP724ngLgnd16d6qa42n6nGRLbt + Nb+TwsExjyDgksR2PHHQ45B4p5YR2/2fAHJUKR0B968p8WXniqx8aw3FnJb2L+b9mgt7qRW8+3G4LLuB + 25JeQYfBGV65OM8Fh3Xqcifntf8ABaiqRvbz03t+Z3dja6WmlERRywiQhUlkd0clsYKktlCc46g47dht + WGl20ix7o1ftIWKksVA6sSSSeTn3NVEtkhsrKaS5jnmYILkxp8juRltuAByRnp+XFLb3McEMsXlyRRlX + aMsgbawO4Bcjp2Az6Y4FY8q+yDduu2heurOxtwfMuTCOQqouOOeQc8c++fTrUVqx+a0JaSMKpgwo3Mpy + Oeeg45PrznGTYFtFckShEO4c7yFBGMEduenseeaqtbBLUSTv5kiuikMCFHTJKHIJxxk+hwBk5XQm+u5a + urd1t3fiSMEAjlsMTzx3HTp71StbW4eIF3GWQELn7pPY/wD6zWvapDDCPJKxMrHHz4AUnoOoHOMDp+WB + BNuLC4VAY5ckH1I5OeOvSktFoHM+azK9vE6kMs2yNQdy5P8Ak8/lUbNFFNcExPLldyvGoByMDvwfx+lR + TGdHeJSXcDO9Rls+49fp/hWNq2sQQTG1lhlMkTqeGZRLkc+vYDrV04SkypyjFEWqX05SUPcD91lN4kYI + AwwDt/jbGM9hzgdzi2xeecRXt3NIkX+qSQFhkDggAc/WrUEFxezuYw4V/m8lWzhSeSB6VqtPBZxPFHEV + ukXY1sijErY6buvQZJ4/Hmu1e6rI5nqyG8nvdPjVrS2S1aSPaSX3Fj1BI6DqTzVe1tLeaQ74vtJJ38Lk + qx7nPbJzxntT4rK61HFzausMbH7ryFiZOSQCe3titPR7a8EAFzbvDyCWHRsAZz37n06VEnyoqNtia20t + Hk86JJDIDs2s4CjgcD6/mavtiV3iyrFTsKodwQ4Xgk8++PTHrUssczh47SEyqSN7FwAhHXPcjB6fy60o + fyV8pMOwXO0LgdP4euPXGT1rmnK6uXBXdh8EkaXC4aZQuRuyASfaop1H2hboRNHEo2kKC2Tk5GFB7Y/W + o/LM0sZjZfvZDHPPt9Kmu4nkjCglIgpaQM21gBgfe/hHX+hFZrQ1auyfzoYzG0rIY/vZJ4AwTuHPSsrX + vEWgR3WLq5togIRsHniIZYggljwCcA4B5AGQeKuFhHb/AGfAHJUKR0B96+etW1lNOmudS1XSZtQs4fM8 + xJVLfO6lAevXLA8n17Yr0cswUcS5cz2/U83M8W8Mo8q1f6HvSXyNaSxmUhhJ5PmMcbycEEHOMHdkHPII + YcGub+IunC70mG2bcLe7Ekd0yBclgoC/NzzyzfUHr2z/AIYQ3lzpcGq3HnmC1SPTWyDsbYoIXBxyMMRx + 0z611txf2lhpl39oidLYRyOodFYE53Ki7vXOAM85AGAKyxmFlRqunTevT+vI2weK9tQVSenc+V/j/pr6 + QPA+nPKsph8OON6jAYHU78jjscEZHr60VuftdusnijwjIlvDbo3hpSscQAUA397jpxk9TwOSeB0or7TD + qUaUVPeyv62PjcTNVK05rZtv8Totc1b7Pp2n6bPE9zA+kaLLHavH+7kk/sixB3HGGXCr8vqATweIvAVl + dal4gddYtmCSRyIscEeVXEbMUwv3W2KSACoPAPDGm679ruX0WwsbJru4/snR5IlVSwDf2NZAggAkkKM4 + VScEnIxmvVVutG0zTJ0s9Fh0q6is7i5sZLi7iY+ZkiLzCrszL5kgLIxxgOBu24rjxeI9nJU4/FL8Drwe + G9qnN/DHVjNB0nTvDdxIzTx2G+NY2llmjdyCc7SxBVFIXdtBYkk7mbaK6G7sNUn0K7udNe3udShRzCbo + DyvOC8BtuAFHUg+wrwPVLq71TULq41C9tNTAKGG7tLdLcJkfPuELlY8N8iAn5l5wvIr2PwD4wb+zbtIN + IS6FpEptrJZg7ONwDIplJZsKSQhJ4G0DGBXE8tk6qqTlfb+vQ75ZjTjh/Z04uL19Ld/X5fMveC9fv9R0 + 82+vT6a17DN5Iv4DFbRMu1AolUSMqyeZ5ijBBKhWKrzno5LRnunF0QjrKI3ikIwDjAPOPu47+teNeJtb + sfFmoyTNaxT6Gl4bnT4LlAIUm8lFdQHR42C9M/MpznBJBHr/AIVto7fw9Y6CG8t4LNUihNyrziKFAoCj + gvs2qMgAYB4GMV5uOo0nUbpO876pLZf1953YWWIpUo+2jywto297/n+mxyXxQ+yXWi/8I3NcadbLckGW + 6v7yO3ggXJUMXcheDzgHcc7QCC2PO7zSNV8L+IP7JYSB5AWjZpljEqrtOQWO1h864IJByCOtafxbsI08 + V6cps5L+2kvopPNMKXjbyFQb4XRg+WLLtAKjIwCSFHW31n9h1PRJpNMn/sqwsUsbdNQDGSOOKPy0/duM + qpAUjGNwyx+Y12ZbWo0KEW38Td/L5enzOfGYStiK75laKSs/68zmPFfj2K4t1trS/wBRF/MYiLabyZoF + Ea7XMJ42yMAT97gb8g7srlaXc2lst0fFMs7w3K7dP1G3USGMuSWVkwHwBwRnONwA7Lh6hpej614kubTU + A94bV5EUwwoWuVkygVcqcNlR8/UY+UZr1m1gt7bSftN7p1nbTXEhlCW8e2JSxJYBVGBhs8Dj+tV8UqdR + Si7u2q/r/K/4HRSwXNg+SpTsr6SfXu0t/J6tdrO9/LPEXxFv9TtriysJ4IVs1DrLGpWGRB8pVFIBUEFc + ZGeo44NT/DuHW4Z9dvdPglTVVtJoU0qWd4mKSqrOykf6xgQG8thtBII+YcVljml1GW+0aXTre9aZrpoL + i2SdmYHMO3cV2DcZA7qdwGDjpXazXvhyLxTp2sQCCyvpi0t1aIymGGfavmNGM5EBkZ1Q4BGzPIIx1y5K + f+0VdUtl2v8A8EjC1nWgsvw8bXvzNbytr8tNbeXfet8PPGen6bpsuiQfboJprtHMNzctOsKGIRs0bMxK + 5dFZhuYfMeMhQLfjJvEF1PDPYSi4swwxEDsdXaURYT+LlZGK9FGzPO4Cr7+E/Dt4XuAht3crPDjcslvI + u5W8vGCFOc4JIOBxgCqmm2lxpAFvayzxRpuEBhI4BAHIVPfk84J45rzXiVUqOVt2vXTszqpUadGEEltz + XvqnfuvT/hzvtFNlHYyW93pNvbXc8ym5e3hMRJBJBLr7Fvmz/Eas3o+y2Mb6bF9rKnP+8CM5DdO55/Sq + WkSxM6rJcvLOzl5NzsxjwM5AyQg5x2ByB6CrM4tmhYLbgJMX3NEuDkHqxAyAefxz6mvMlzOq9L+TNEox + pqzt5o8m+L+ludW07VXgubmWOeZpngcjCGSJIkU7DuwgyyDnc7Hoa9dSILc7ZWLEZ3FcYYA54PPt7jvi + strDR7fUorw2BWNGGBkbUYNuRlXJPBA4Ax7AZraMz3oEjEqsgBXf8hzjvjPoOfyq8TUhKnT5d0tfv6Gt + OU0nCWyb/Gw6f5ZTsUeWRySRnH4jPHb8K8n+Imkv4q8SWjWMRjfSkwZoyHM8klx5SxM3GwDYzc5yGYcc + k+ixWd29wwvrlJY4jkxoeZAMcvxyOnACjk5Bzisufw9ctrV2s1oI9O+0LLMgBUShpGYZwdpAHG4dOOM5 + NY0EqcnOS5kuiaW/qbRxKpTi479L90dMIJ7vSobY/uZUK7vLG/IOM9gBx6+/1FZNJZkLTB18uQHH3jkk + DjPbuSc/kKeNQktI2+0ReZaEkLJCxkIXd/EuBjr2z3z0rQF1FPpr3MLMYXQKjxDceejfTJHTPFQ3JOxj + 0v0Kq/LE0jyqFOFV92MHHQ8deMfjR9nFxMXmkXBkClHYFenDAnAwv65qtp0DCT7UYxDLKuZFWQsDkHhg + eCffr74q/bSrJZyxzKsW9izGIn5euMeuCB6f0M6RZcuZrQWJZobfEcQwuVEu8bTzgg9v8+1UzLE1n5C3 + V9LIZDscONwPGVGRgDHtUIFvayXCqtxcKRvWTjeW75J4PJPsOg9K57VL6cpKHuB+6ym8SMEAYYB2/wAb + YxnsOcDudIR5noQ1Ze8bOoXQskeO3ihTZwdzbMcnHJJyeM/0Fc/OlzqLrd+cqk7fLiPqBzwBxzn86zLY + vPOIr27mkSL/AFSSAsMgcEADn61s3k97p8ataWyWrSR7SS+4seoJHQdSea6VT5HpuRzNosX9/JpwiMbO + 9zKwZH2KgTGBtznk8n/PFZVlZ3P9pRXMlq5DSkeYMHax/hxnjPTPbrUtraW80h3xfaSTv4XJVj3Oe2Tn + jPaty20tHk86JJDIDs2s4CjgcD6/malNQQ2tSSysUgnedwRPvDqFxtjbuR7/AFzWlLZ+ap+zyr5pU5WU + YVl57A8DOORnqeKWK1a58xJJIogBhQckZ4yM9T0NWI4fKkSQSSBAud6+pAOM4PPOMVzSm2aKK6lLSCfs + YIJdtx346MTzxg9Ofy685qe982MJIsfmS7/3nB4yMgg/jSPaTPdG5Fv5MSfKPmXEmSckBeg+6Ocd+OmX + 3NrGqksqB5Bn7mC2MfeyMgdgD7+pqXa9wv2KzIZbmJbRZcfxu4XYx9D3LduOem7gUJCEuQsjFiM52Yww + BHQ8+3uO+KtQ3ytJ5e4BQCrRkdBnAB4PA/WotUnYHe5MqsoYFOMY45x36fr0ovfRDs09TN8czG18K63P + GgZE06d8McEgRsccjPtx7V8++E9W0rWrW503XLbUUWRAHnsRGSgDAgZkyBnGMYPt7eo+O477VNJjtbq6 + m+xXHmRypF8rsRgKXbH1IAwD1IPQcHfeEo/CcEMNjePrLXLsXeO3WAoAFAB3vzyT06c+tepglWo0pypK + 8tAjHL8TWhQxsuWOrvt079L2X/DnoVh43TxH4p0rwZoGkw6bo9tK15IyBTK0xiZACBwPk3ZzknIJIrc8 + aadJH4Tvbh45Ga3eORUALFm8xQAO+O5zn9K4vwR4XsvDPi6DXW19tUS6Vt8EVsQYSyNjJySwUZXAx1z0 + GD6tqdzDfeG9TMM4jR7GQRSA53HY2MEZ74/CssXOtSrKVTfQif1OrT5MHfkV1+L7+TR8l/tKy3E994Kl + ukMcp8NkFSMEAalfAfoBRT/2m2lfUvBbTJsf/hGyCuSf+YjfevNFfZ03eCb7HwdRcs2j6B8F6LpdhaeH + dUSN49Q1fw3pCm5a5VzGV062TCRlRsVhGoJ3MWKjkcAM+I3gKPWNQt7+bU41jEa28cBiMiTTK5IaQr8z + Ltz8uByBk4GK1PDcKr4R8Jus0bRy+G9K+0W7ksGAsIACAM/N0GMc8d+a03tt8NjFbWt0kokRogTh0jB5 + JOfu7QRj3Axkmvi8bUk8TOV9m/zPtsv/AHWHioO11r81r95zPjjwtYtZadpdxd2zwtBOzCVzGJG2ZaXK + jICKCWy3CgcE4B57VPDOn6pYanoWl6RqGnX1iJY9N1YXtlGNYlgYhhHbLtmKyBGEflluWXJxk1ofEnwp + qOo6tJ4k03xJNDHbRl3gVSjqEjO5d+SOeTt24O45riNZeHx3JYQLpcEVvDZRaf8A2pcrlYYYD5rtGyxO + 8ahzJ8yYODtBKggerlWKXKqdSV3okuy2SXm/Lc5M4wM5RjWo6pb97vV/JaL1Os+HegeI10m/lntNNs7e + TyopdOurcXi3mNoAfdnBbklkYMDkDGARmXereHNf8eWN3DZpDqouo4bPVJplkhLQyhomV1iZkIkBY7MB + lB+fnFW/FXxMt9CjbS7XdBdQxeTFJFNHdJFKyk+cJELI5G4EgE8kggHOPONAeyl1PZJJHBLdlY7jyroC + Jhv4kwDyu7B5xgj6muhx9nVjCnHa9/T+v07kUqUsVhKuJxUm27KN3vLa/wAlv31d7o9N1TxOut6Rd6fA + l9Yz2kM5Ooyyxq91CCkTvD5cm6Fj5mUDgq6jIbKYaL4eXM+oQXGhwfbobe3lJgkvYpZFhjZAFQyyF/LB + 2M4Uts3SPjaSFOJqHhXxNfWV5AmrT31qY98UMmVwwB+6Ofm+Yjd1IYgnDEVi+GTq8N29mLW5S6Rd0i7w + m4DnIycEY5GM9KVOnh6kXTpOy3a7/ecUquIoS9pWjzO1k9dPu0+++h6wNAtPCLTarfWNw3my/New25lC + hhhRvUHYCSVA6kt/dyRBrd7c3GpJZ3S3FjmJZVtruFo5WVshSqsNz5Kso25BIOCeK5rxp8RtZtre/g0S + 71NLhYlg1SxuVVoY41RQzRPj5WPBxkj5iRk4xmadd28ngy60W/8AC3ieRftSXMMM9tcTBGEWMRl8lQzx + xFwWKtvPAKqF56tLB0JP217vbVfr5enrubvF11RhOU4RTvbmdrpdrtLe17Xa3as0zTttJ8K+JtSjlE6S + OxVnjW4SNiWzg4cfMSQR8uQcZBPBM+pfDnwxoFrJrl7O0aWmOGIhDMxAAGMZ9OuOTng1S+G0k2iXUP8A + afh+e4sSm3zxp0oltXQArtVlLlTnOQSSQcYwBVj4r3lte2EJuG1G9iuZSlpZ/ZHt4Y5GHBfcMs+N2O/L + HAIxRXjSeJhQpTTi9NJJp62aWit83trd6XxwmcYeUZVYTSnfRbPa6dlJt/JX6WTul1Wj61pjKIo5TdWT + M4WRYWV2KqrEAHBGAQT6Vo39mlyIZbONjICDv2cH2PpyDnFeC+DdQ02LUViv7+58uSViLiIMoUNlRgck + ruck5AAOepPHt9lfPpEv2GaUyW0ibo3yHlYbcZBXA5wDnBz9avHYOOBlCrTu797NfJ79tGkzpoYh41Tp + y0WndNeT6PqrptdO5Vg1A2WpNsX96cJKJJCsRDchSPf1OB1pmseIdeGslNPj0t4EgwkEgKvkj5gCB1yC + BkhcEcA81t6raw3unzW0oEm5PPhdQWYg9Mk981iLb2FvqySXyRySBVEu5h3B4wc8H6965qNWhWlOrWWv + buVWpV6ShTovTv8A10Ld/rM0XhSfUpNNnivdsqx2gUsvmKCQC2MNng8ZJ6DoSJ/hv4pg8VadLceT5U1u + qLLErF8543KM7uzcc9uTmtexktWs3gkhja0CfvISQoWPrt9iNuQR0rhPBfjTwtDqxsBZR6VbC5cwyOqr + JktgPKRyrEYBOe4HTpw06LrwqKEL2s1bouvqdzqOlOPNLpr5+fkeiXc0DbD5rsExsZVxjjP+A5yM/ga8 + x1678X3Hi9NS02KO3W3kCG3kf/W26ElZgclASXlAVsFQRgHLY9Y82clw0kT4JdGEREitwACcnPGfTt0r + lNXYS3zyRLN5gxHwm5SvnGPOThRjGT6AfjWmU8jrcmt3s1/wULHTqwpqain5O+v3Nf1+PUWnmJAsl3Oz + Sydd2Blh3wOmcelXYljiXchWKQMSTu2jk5PbAJP51nabDG8MQFzG9weFlkc7mOSQDnOauqJoom8uEKoZ + gsm4bQM/dP8AkfTivOqL33b/ACOiMvcSe+nn/wAORTbiwuFQGOXJB9SOTnjr0rOmM6O8Sku4Gd6jLZ9x + 6/T/AAqyZYms/IW6vpZDIdjhxuB4yoyMAY9qo6hdCyR47eKFNnB3NsxyccknJ4z/AEFNRu9Coy5dGZer + axBBMbWWGUyROp4ZlEuRz69gOtZUEFxezuYw4V/m8lWzhSeSB6VZnS51F1u/OVSdvlxH1A54A45z+dXr + +/k04RGNne5lYMj7FQJjA25zyeT/AJ4rqXuJJLUzeurGtPBZxPFHEVukXY1sijErY6buvQZJ4/HmqkVl + daji5tXWGNj915CxMnJIBPb2xVeys7n+0ormS1chpSPMGDtY/wAOM8Z6Z7da6WysUgnedwRPvDqFxtjb + uR7/AFzRJqIlcq6PbXggAubd4eQSw6NgDOe/c+nStqWOZw8dpCZVJG9i4AQjrnuRg9P5dadLZ+ap+zyr + 5pU5WUYVl57A8DOORnqeKj0gn7GCCXbcd+OjE88YPTn8uvOa55T1ui0rqw4WFuP3qoEldNjNG7RqRxzg + N14HuBxnFQ3utWWkyrBfazBb70GyFyPMVTkZH+z8p5IPQ8+md4y1m30ezunuLuC2kKZ+0SO5WOUjKqVV + Tktxx1PoeK8p8WWt/bIfEC3bavDcxOIxb3G2WVpEXY7KEffGmMlFxgHAIwwHXhsDOtF1JbGFTGUoVI0m + 7X69j3DR9QtL20eaxuYZ7cS7JCjeZyP7wPYevocg46yO6NBMyQXLvG4YusOF2c52jqRx9a4/4W6p4dXw + /pWjJ4gS71e4iV9gDhFkmYKIxtUgLvkEYLkAtjnJxXSG/VZPsMXmSzblVxE4/crnktyAox07/WsMRham + HaUlv/T+41hiKOIlJ0Xon8/J/MRLdJ5EknHlMqgD5sSL/vehOOhqYPbRsu65Erbtsa5DMTz0A5PAJ9gC + atecDApPyKoCgegxj8aZLFDLGpbcWByCTt2+nT/GuRu7OlXtqYPjeJZ/Cuq2cUUzymymiijjAVixjOFG + Bxk4HI/pXzvY6j4tE8xtre+it1tvsYgmjLqkZGCg3DjOM+vT0FfRviy4nt/BWtPctDOItPuJQRFtckRn + ALc9RnPA+grwz4deJdJuJ5Itc0u6uFMZV2gxtA7AksCPpk9PTp7WUzhT5pzei+45MfRxGIo+xoQ5nJ9t + dNVb8b6ml8Gk8Sv4p0gPp+p2+jWUkwuZnEjW8kxhYAFsbd2TnGen0Fe0+IrK1h0rUroIYpGtpTIyMy/w + kkkKRk8detcno/jqPX/F2k+D9B0m10zR7WR7xmO3zGuDC6IGA6fLu9ScjnpWv49uBBot/DboHZCjuVlP + 7vEinZgDGSuBg4PfniuXExjUrqMJaaa+v/DnTXlWp01KtDllbb0bS29P6Z86/tSxNBq3gmNl2n/hF1OM + 5wDf3pHc9j6miov2mZpLjUPBU0qhXbw1yuMYxqN8AKK+2grRSPz2bbk2z6Y8OQy/8Ih4MvLdt08XhvSg + pdflRfsMGcH6/wAz+HQ+dJdRPs2s8agj5tnI78A4+uD/AErxDXV1exj8Ia8sd+9lb+FdJSJrY/Msn9nx + bRnkY3ZypByC3GSDXomqa3eaV4STWjZM1/LaJKYHhcIrsFyG/u4zn5jnjGa+PzDDNVuaEk+ZvTqnfZ/5 + n1+BxEXR5ZxtyxWvdWOjaBr63aG+Zbg3IKPFPGpEikcq3GGyOxx6VhzrpHh1b7U7jTlt9sKJutk+6qbY + 0VQflxjaMYAHTtXNeE/ias0elx39ssJvppYyfn3CTcpAKkYC/vMDDNnAztFbvivU9Mnllsbmz+0xFkZ4 + 35SfcoJBGCOMd+lc9bB16E+Sas2deGxVCqlJaxXy/rQ8u1nU/BniDQ0tEaK0uZbhXvJobpY5pIkMg+zB + pEYYbMMnmYflCMABQKuh2Olapq+i27CKWWy08Wl3dW0Hlx3RjLlZNoAPCmOMtxuKlurZrsLbwppl1cMb + fTUhRyXEELEBQTyVA6DmuhsrfTdFgmhsbUw3WNsluvzec2Om45bGATxgc8Z5r1HjYxoqnBapWucFTCc+ + IlV2i3fl/r7u/mWI82ksbfYZxJKyiJI5slmx1wPu5z+dcPD4i8W6Br97caC9noetSM008N7p6y3G1vni + QeaV2RsXfey84KnB4q94h1OO/wDtul6Hqd1baiYGSZXglji3PA8rpHeEhFkWIMwVsAgHBJAVtPw14eh8 + R+BTP4pjt7dNOsfKsYRbjz4zHkgF5GLBTzuBLbi7HA+U104HDSpfvJbnBmGLjV/dx26+py/iePwfc69F + rjxXFpqV8xmv4LCdB5U21BLJGCCzQ7ywQHH+qzzlcW7LQEvLqW5tNY8Xy2W2OXyBqMsc8KkZO4PneWzu + CqQQAOmeaPw20Dw7rej3etahoy281hOqMu82626uF2swUhn3MHVf72CMdTXq9rqen+etndpLYXjxRutt + dxNbOysSAVR/vAsrD5SwOOCeK4sVKtim5Qg5Ja2te19/l5q3bY9DEYDAU1TpYi3PG61tq+lr9baNdbJ7 + nN6R4a0fUbiWKPVvEsTRQIHhlvZ42UksQzFnbJOM/KRgcYGa5zxfo4W0Olzf2jcBlc297HeSOqDOF8xZ + Gb5+cbhj7/A4ruYPFGgx61PZXOoWyXUWz92z/LHlQck/dB6/WtWOfTtShuLZZxFP5Iu4Wl+VimA6uM87 + SCOfcZ6ivNoc8J+1Udvlb5r+u9ypZVg6HK404xl0fKvw0/ryPKvD3w40rWobWTStRt7RoIlt5E2E7Vyz + 7stncx3Lg7Qvy4B4Oei8PaPd6XaX39uyQzyQK0glt4Qm3buJIHQsc4yT0A5xwNWOwIW6vYdPhuNU0uST + NuJI4pmkYsp+UvweuS3PJI71zFl8QY9ftJ7OQWOn3MsvkxLdzMIzGdqMHZQSSSxHRQOxPGfRj7bGe0qc + yUE1e7Wj+Wuz7eSu0a/XPZRVFXk7aJRette3S3fRauyZ2Is7SxtIkj1W4klnL+fAziMWuHATaSPnDLkk + DOCO1L4gtbW48xFnijnVTtzHkn3+oqxeaGlyuoy3pMN5p8UbNEkgldmfacqv8SfNgvwBtbpg4wra4Syl + KaghaTYQW5Y84IHB9R17Vwwq4etb2UrtLXtf166a6X/QIVKkm27W6elv68+/cZLfRFkhv90sKA70WJWA + yOT0zjB59alj8JeFb3U4LjyHhngc4ijfPyDnD8HYAcgEbevHzYNa11penahoP297qK2mebyBDOVSRsj5 + Coz82WDDC5I9MdOb0i8uNMvGhb53UiImWY7GU9Fb68YJwK3nQnBdU7ff3/yYQrU6t9nZ/d2/zR217OIr + ea9R4jFGjOxkcqAFznoCc4BPTtXGSafNrMMF3aXIMFyWuYmDAlo2ZiueSO/StnXxaXkKnUtbvNJ002xl + uzbCMokaMvmcBC5xnGF6gYxzzW8CR6XHb32maXLcPa6fI0VvuQgvF1imO9dwLKAcEkAdABtA6sBT9jg6 + uKhG8otJ9kntfrv2tutTlx1SFfE08LUdlLVd21v5Wt89HodJoenyoibPLdFBI2sCSceh47fp2qZ1iW4u + GZDlsAYGGVsDjngjn+lUIJXF5BEVTKciQHJxk8gcH/OelR6hqHk2810si+WD84wXbJ984J5rxpudR8zd + 2z1YRhD3UrJFHXWvoIEa4lxuLoro7bVUggEr/ESMDJOBzgVz9mk93dGGS4uLyGHHlHaWjyB24GcYOauu + 1xeTSSQRvHG6nzGlQFuMDdz04PSte0hbT7PM7FI/4PLHzTA4GR39a6FLkjbqS1zPQo3V1dWUSyWEMcHm + oVLby24jnJHQDk9e/rxUFraW80h3xfaSTv4XJVj3Oe2TnjParM9nealctc28qRxEgKHblmA4/THHSruj + 214IALm3eHkEsOjYAznv3Pp0ocrK4la9ia20tHk86JJDIDs2s4CjgcD6/ma0ZRu3JIVBxgKuSB0zyetP + ljmcPHaQmVSRvYuAEI657kYPT+XWnLHHHA8kke2NWWPzB8qbiMgYPQnrjJ788VzybktCotJ6j7Zo/PXa + 8q7VJJyAfw9f8+9UmuLaXVEEWE+TgA4D8nPAHpt/OqsOr6dE8txc3EKIjbU3P8oIC7iMdcE4JAIHc1et + H07W9PumsbqOXazbJlYHDoc4B/l6g55BFTySh8Stc0bU05LW25578ZXvLm1t7nS0M8kCeZFEBkSMjAnj + qCAwIKkMSMdgK1/BPhu0v9MGoajpl1b6hcKyvJMrQmNVYqqLGcFRhQeRzxkkYxf1DQppRAbxoXdhvVgh + DDJ6cH39xkcg8Ct2A3FwwZ38uUgKwABUgfh/nPTinUm+T2d/d3/r9PmVSUUpNR1fXrZdPS+vyXY8Z8Ka + PfxeO54LazZbeeVZVuvJYhPk3OzOo2Bt6kqG/j5xzz64tnNbyNIwMckmBuALBxkcsM5LgDrkHkZzwBeg + t7dHd4khikkbLlcAsfc+vPb/AAq20hmjeAkiLaVZuhQdMg+vX+lJ4mpOMY1He2g5U4RV6cVHvbd+pR+2 + GGYRzW8km9V8t4YWbOem4DOwe5+XBHPJAsQ28zwpcGWHzM4kUg7hxkrj6dcf1psUaS3Mknm+ciDaqqSm + wcDjHIPv1GTg0kTXcN2YmecJIrASNEcDkkgnAHTpjn271NlsjJye5x/xTjuJLAWDyy/ZZ0lW58s9sYwC + eOhJAPpnBrxnRND1LSmltVsftXmESJLE6KWXsCGI6Yzxnqa+lbiK2ZbjzY98BPlszEbG45Bzwc5/HNZW + m2OkzzSSf2XZlY2AQGFdq5UEkcY656cZq1JxhKNtzqwmLqYeoqtN6nmHwT8NanbeLJtf1g/Zo5EZoIGI + ZmY5A5I6BW4x6r6Yr2GadL2F0tNrYU/K6BhKOMqfb0PbPOcVLJaRXlpcRhtj4LxSN1DLyMZ/L371jLZa + naNG/wBpjWSVN24R89TxjOM8+/I75AqpyVRuWxjUqzqSvN3f9P8ANs+fP2yUCeM/Cqrai1A8Nr+6AA2n + 7deZ6ce/40U79s2SWTxr4UeYASf8I0gbByOL68FFfb4P/d6fovyPg8ZpiKnq/wAz1+9fTNP+G3hvVNXU + m1Tw7pDbfvFn+wQhQqngnP4cEngGptC8Y2PiK3h0rUobeKR7If6OkyPhACvVTlGAGdnUAKR7XYYtPvvh + v4Us9QjZoD4f0lWkIGEY2MHHIORgk8gjk89AMvTNC0nw7Ym206Mu0koYguXU/XjGBnv7+gr5qu6TnVU0 + +bmdnfzZ9Nh1VcaTi1y8qvp5L+v8yO58P6BZmS9s7CCC6tifKYORsJP3iQeg4BHfJ96tKl1qKpeG5jJY + 5jTJOSOSeBxkk/nUsdrPfXs0d6BBFu3TeVyrr1BBzxyBz+FS31y2nIW01oIS6EcymRmPUZGMDGe+DzXO + 5ylo3dnWoxjqlZFi/v5NOERjZ3uZWDI+xUCYwNuc8nk/54rKsrO5/tKK5ktXIaUjzBg7WP8ADjPGeme3 + WnafBbXcc0xge52HfgISxYjqOoIBNdBFpUYcXCFzIABtYkKMgZAGP169azuqdy7OTOM17wdq00t9ci7V + tzO6BRmVFYEtGp2k+oxnocdOK1fAutPH8ILhr43bsbK4Ekv2N2QKpkAYuvygYGc5OPwrqbhBdxT2vmEF + gYyYXyYyQO55zzkfga8j1LV9chup7bQddeNsMz2t4fPyFP7pEUuirGTuDsOQApA6V6mW4ydWp7Keun3W + PGzPCQp0/aQVtfzM74Oa5qVl4Q8U2Phtnurq4gjU6ZK5DsmJd0kTj7zLuBCY5+YjkANr/D/xxoGk6Zc6 + dLZ3ryQ3fmva3UrXBtwItjGJny6EyRozAFs7jxwoEl03hG01fTtSt457G8Zmnura1us+VLsTzWj3bmaE + uWCE4PyZyQwxst4J8B6lcXV01lqUEiL51xbh2Rk3ByWU55LAEn5jxtzjinjsTSo0Xh7O/XbZu99/P+me + pRpRxNeGNrRaUlpr2XL28tl8tDzePStU1bV719KsjdXgDSi4jgfG0sOGCjPbYQ33TnuMjvbG38Vjwzd2 + fiPTbSa0voP3rW0Fuosl2bfkKruXkEtt43biTyxrk5tQXwjqWn39jqt3YabbwoscNoW/endukWTgh/MT + IwzKASTXf/D3xpBr9ve28UccaBsxyo2AoZiEjcnB3dQCeSAT1BJzq4lU6VOtS+Jq23S3Xs/62Iw+FxGJ + hVozf7qLvq9d/s91prYp+BLmaXxBe6ZcTNYQMiJbmNAN48xhEEKKAsbBnPAG3bsBBTFQLDrmjarNNp+t + GCd9SuD9lun8xPKMmUlU4b5+BweoIyQQwPbahNqtpamWxWGOcllEjz/ux0BbcRtV++G+Uk8HOQcfUtEf + S7ZL/V3S7uLVlY7DuMqBsuQexCKSScc84x0eAx1P96qmvMtU7O+uyT/q2t1Y5M1w06vs/Zq3LZK11st3 + r8357p9equJLCOwhjewe6trv/WagmnxJKxgURxBVVPLJUZbIVRhtoCttBybqGNrK5t76WCCQnIXPloVI + JLKDn5QcA/NxjkYYZ6y8u/D91bWS6W5OoQ2YmSObbiWLqJAoxvBZuoI74zxWxLaR/Zp7q7itLrTZVDRw + TxKzbRHtbcu1RzjPI7kfT4GOaKim1za23fRWX8tm7Oz2u0+rd+tOyTa1/r8Oy6I8maS1tX+w6jFDdWLP + 9oiD4kc45BGOMH8cjIPU11/jyy8OXulRPYzJbz3W4iwdt0ySKASV3ckcj6Z+oXj/ABxpKaffPdW9rAun + 6fIJJba8vI7OCVQ7fujI5VVZlD7l6lQxCkriq1jd6FZeO9O8VWGn3M8tvN5b6Lc3DF0OU2tFu+YvFsbE + bEglztJYIkn6Xk+GePwqlVUuXWytdp27Pa9te6T7HjZljYYWqpU7Kel76Jq+z81fTs35h4g0XX4p1hbU + LUhbcx3KvOjbo5C2T8mflYI4DZxuRlHKNXTeBb/QLC1v21S2c3jMkSuJQFKKgAyBtG7lvUgYG44GOB8W + +O28S+O9UvrPQ30iymgjgEt3G3mGVXzJlHG6NGKQ7gqqd0e75j96SaN4bhFaZWeYebG0bBo3BJBORkED + GODwQR1GKnPcgr0cJH3nGnPZ2afz0Sfl6XsPK85pY6c6V/fjvZ/lrf1Oj13VdKk1CWWKREAP7tkiLo6l + vusQcE469fSq9qZryGO3gWKeFUKh0UhW+ZiME89xnPIxWcLKaG3EjLFEMZ2Z5bGc7R/nNa+iGOzsZJJl + 8qWAK2wTFSSc4IXHfPr25rxVBQpqKd7HvRvfU0prW30qFpQQzIB8j8kZxj6gcnFZDXV1eq0kykpGeFUE + NsJGDkcZ+vH5VainGqv5kqrFGoLDf8uB/UnLccVp6bEvl+XGhdJHEiqGBMeRn5yT1yeF7Cs17q13LbDS + dLstP/49Y5UCPuiDADYect9c56561rS2fmqfs8q+aVOVlGFZeewPAzjkZ6niq4Cyb4VbftOzAIOw8cHn + 8cemKy/EnjXw34YuHj1CXVDcQyLE0dvDGWJaGOXcMuMqBKoz6g/iUqNbEt+zV/ml+ZhiMVQwtvaNq+1k + 3t6J9zStbpLPSZLqRiwiDNJggbz1wOcc5wAfXB5zXjmheLfFKa1JrusSm9eWUpJATMLS3QsUeJkjYBxh + yu05OSSTjc1ew6haaT4g0hXS4vbWO4hVtv2ZQVyQ2GG76AjPY+uaTRLDw/oJgh8+eSWbgSSQqMnjgfPw + MkYHOSQOeK0owxFKd6LV/WO3XqJ43BSpyjiKcpJ2+zNWd/8ADv8Ah5M8o8T2Wvav46n0uwsYXvLm3WG3 + WGRrazt5IyzupR97P8gIyHUbuTngV6B4N0R9AtHsmu5LzDAySqmxTJyWKD+6OFyST8vOMVH4msdP1bxU + vkeILvSYFsUhWG1tliEYSZGKmVJlZXOCpC4+UjGCMnqLCPZodvZ3+tXl/dRI8b3kkJMkimV2QMWkZiVR + lj3MzMdmScmu/F0+ehzS5VOXmtr9Nf1OHC5lKMlQi5OlHX4Zb2tquX16BLcPNMCy/NjDA5JI7AdvTn0p + sUYy43CMg4Vio4/P889f505bHSN4aWSV92C5Nsvz8f731P4ms7X9R8OaNpg1C5vLpbd5kgRba3DMWfO3 + aFfPvn/GvIjg6k2lFr71/mek8yoQjdqX/gEv/kSw8UsqBYrlWeOQRyTY5jPXb15Pc5wB6kipLk3ItsI4 + 2LwcnOOvNZjeJ/D9pGLe4vL9og7Rqy2Qbbjjkq53Drg+nXJpsvjDwd9smspdZkQqiyBngGxgw3ZB3844 + z0xmtXgK7ey+9f5mUczw63b/APAZf5GjbRTpIJGvCvB5XGWUdsY6VamnkMYMU7spH3QcjBOScj6dKr6c + NNu7RLi31OeeGVMxstuuCD3+/wA/jVuKOzjYFL26EYGNohH899Zyw809196/zL+vUn0l/wCAT/yHWcrv + CBL5xRgQQxycdePyqq8f2B2AjxHI/wAgRSoT8Ox5AwMfzNR6hr2iWEAk8251AqxAhtkjaQsF3EBTKCSF + 578deoqS51TTL6NbsadqrRBNqpDFEGBzkn/W5zlRkD0Gc8VawVa3vWs/70f8zN5lQv7ql/4BP/5Efdok + U6pHu2DGQAcr/nBpZbh5pgWX5sYYHJJHYDt6c+lU7XUILi2lhFvrAkVcOz20anueCZcE9Rwep96js5tJ + mvY4p7HVYzITuklsowrYHUkOcc5/E+4qfqVVdV/4FH/M1WZUHa6l/wCAT/8AkT54/bQG3xx4XGMAeG1x + /wCB15RTv200hj8deF0twREPDa7QV2/8v152yaK+1with4LyX5Hx2Lkp15yXVv8AM9mKyr4G8HvDLDCG + 8NaT5qlsNMPsUI6dDjPf0Az6Q6HaXl3cvEqDbGB5jEYIXoOnvSNLt8E+E45o4pN3hrS/KQuM4+wQZOD7 + 56EdK19LtYYNPnu4452WWLdlXBIx0HT6/nxXyuKbVSfq/wAz63CL9zC3ZfkQ3cxieSDzHePyysio+QTj + GPfPB7c1g2Nr9okEYhuW28xssmV3ADgrjOMACtzQLe2u9pinihiClyvnAZP+znqc/wA61wq2MUUMzZdp + NqSRoBuHUjoccZ9M47Vz87hdJHS0m9WO0uFhBH/o8u5i+5kYKOeM46gcDjgjvzxWgTbWsTvJ50Sv0Bl7 + ls5ABzu9+3tUVqrzQvLaMIUUj5VUdfTGMk4qshkjuXlvLcyPtwsoHb05+7n/AD6DlbuzblujzT4jPqdr + 49TxBbJqMllHEfs/2UncrhTtAJGNu4NuUhgQX45Bq74v0zTrzwzD4muNP3Xstqkxi8s+X5jhc5DKdvLZ + PPOPXmu18W6ppWkeHf7W1GCOW3hflGjDEkghAueN3OOffsCRh+HPEWmeKGh0ZrWG1nmh8z7L9ojlVowS + AG24KyDHCjBA2scgcepGrWqUoTUPdho35dn/AFoeXKnRp1JQc/enrbz7/wBbnnvhOHwXe3WjSajo6Wkt + zLJFGUkeHEoKYDAAYU7tv3mzzwuePcCwjt/s+AOSoUjoD71iaf4O8O6fqkNzDpyR3EDFkkiYjp6HJ4Oe + R6Hmuin+WU7FHlkckkZx+Izx2/CuPHVqNWpekml5u534RV4w5asubt+Bir4e0s30kt/AtxaN85UbldTg + 5BIOGBxnOM5ycmrJ07S9Phg/sxh9l8z7mCQQxLED3zz3/DipbnElnIsFwybXCkrl8tjowAyBz1OMcHmr + Ygnu9Khtj+5lQru8sb8g4z2AHHr7/Uc0Fp6mlRqMkzmPEdgmq6FcaUJJrSF90iEqSCV+ZUHcLkj16DAw + K828Uf8ACTeGIdDTVNat5luFuo7eJ7ZUWHCoA2I1IyobAxgKAACOcetXX9nWkgh1C9ihnVldbcvulfc2 + wbVPzMCc9Qee3FeN+K7LUbvxRB/bmoXFxbwT7opmbzIwJSNwG0HAwAQMA4wMZ4P0WQScMTHmdo6vVaN2 + 6N9dr26HkZrCVWk/ZfFtvbS/X8bXPUtAu7GHTWttQNtbahbMguolXbDH8wXc2PkLMAB17MFAwwO9cIsd + wFijPy/MpxuUAHAXGTgegxjHHGMVyGqfDa08Q36arqVxHb3jS72ThvJdst8kgK5Ccc98Z4rs9kVtvRQp + cnJ2BckgAZJHXp/TtXgY+nh8UryjvdtbJPtHrb9Duw0ZxXI9bWSb3fm/M4Txhc+JtY1qe3ubC5vrC5RW + WLz5IIZE2OrbniZcZV5EYFgWVyOhFcr4g8L+MdRubnUdS0hJPMlNzI0ckakuW3EhAeOew+mO1ewMjvPE + i/vcN0GDk/4025gkAlt4hHLBN+8CKuXUY+Yj0+vb0Nelhc0qYekqNJJR7f1+Rx18po1anPPV/geOQapD + e61Zx69Zs17BOgnRmaL7aiN80Uj8bJNq4DcY6HIwV0vDGtXEkMqa3rt7c3E9oLWKwlS5ysn2kSC5MUqi + C1CxqYhFblgBJgAKpaut17w0txexX0OnW+JIwjlFTdEyr8u4sM/TrUHhV4NM1SfTNQtra2j2lPOS2QFi + MkbmAx+P0969aedVK2G9lO8klor7X3PMp5JCliPbU2otvXTexn3UjuqBJ9/lJ5cbKBnb1x75ya0Lmx1C + Lbc3KXOWj3AvnAGOBx+g/wAKb4wtbaO5hisoSGl4aXBxIPUDryf51VgnuYbF9NkuCY0bMTfMNgHRcA+/ + 1wMGvG3imj3k7M3/AA7ZTeYkjTKIpAxjZiTx1ORjn16Y61sWYO+4tj5excFJE3AysdwHOOvTPrkdM4rM + 8LxQXtqsCYkfo0S/KVOOTkn6HrnmtNI5BaxXBkG8skQVGwMHGcnhuB/COnOc8EcdR6s2jYtyP5DNEuXb + Gdh5xx0B/wA9a4rxv4Ai1vxLNq6anJZyz28O141yUIhjUOMEHOAw/HIrtJ18iJDbq8xWQhywOR1wc47e + /J9+tT6gT9pG3LL5MXJ6n5FrTD1Z0aUpQdtV+pyYiEK2Jpxmr+7L84HIeM9Um0q30qDTZdk0moRQyKqh + 2aLBLA9cZx165710E1vazPG81vHI0bArvUEqeORnp07elSbBu3+Wu7+9gZp3z5HHzcVg6nupJWa6nX7O + 7d9U+noYanW4oYbi6uLG2khKKzC5keKSM8NuDjIbpg5NaF/qNnZW008zswhKiQRAyOu4gDhcnv8AlmrM + kaTRmOWJJIyBuVgCDyPWsPxAZo9Edbhkt7m6PlzS2c4hk25xlWYEnAwD1OCcV0prEWutdtP69fwM4UlR + i+Uz9U8Z6a0Vu1lHJdWE0phup/LkVYRgAFjt4U5IJ7YOORXF+D9Fa58Yt5cqywyQsb5LebfArLIGj2n/ + AHRj2ORxnFdRqN+1tJe2VpHZRQzNuJtc7QCd2Ac4zzgnHPoKh0u8iieVru51FC2MG3kAJ65zmvoMPlFW + OHc46X+b12+79DlmvaTTm9jqteL6d4amXSrCOZ4gojthEWVhuAPyr6AsfzrI+IemaOdBnvJraOOVVKpI + E4UkdW9q2blbiw1I6g1/GlhMQLkXUpCxEAKgiHRSzEZyeeMc1n+IbbXbjR7V5beC5uoZGa4t7ZiqyqQQ + NpYEggEH618zQnJVE27HqYiEPYcsbP8APVbW8rP7yLwBrmnXVhb6CCY9Q0+1jSaAhvlC4UkEjnBwD7nv + XTyANE4GdxVgPnI7eo6fWvPvAMtpb6xLLqsYs76C1W3R53CGRBtLkj2IXk+9O8ZeItQu7S2n8L6lLE0h + ljy1vmMMg3EOSMKxxgA/lg8dFTDOVa0du72OKNflpXlv5bnVpYaWbuYS+R9sDsiSRvi4QMhzlmJYNs4y + OcdMAcef+PNU1K18V3kFrqN5DCvl7Y45mVRlFJwAfUk1f8NRX/8AYUvjO0gM2p3xju3gRGZZZXVVZioO + 443PgA8ZParnhzWLLV473UPEGlaUoTywsptgC+cg5Zyc42gda58Unz8l7tbnvZC/YQli5QTjta6vra2h + zHh3xPfWetQXOpahqNzapu3x+cz7sqQOCcdSD+FbHhu98V21va3EMyX6akW2famlk8ryyRzg8ZP1zgdK + 29IuPDt/5ki6XpTK+BBAtpH5qnJBD4LDsCOldFpdgtsFYQxRt/DHGgVIwecAdB1/WsPZuPxHdjM3ozTU + KNm7J37K9vxd/M+b/wBrz7d/wlPhI6l5H2o+GwXEGdg/0+9wBnnpiirP7aH/ACPHhbjH/FNJ/wCl15RX + 3uD/AN3p+i/I/Jca+bE1H/ef5ntllELjwN4ZWFonmXwzo6GMwgn/AI8omGWI4x1696vaZIk+mSQLEQVD + bvOAxk568nCnscevSq2nQEeB/C80M+2Y+FtLI3OAq5sYFxyec8cDHr6VY8NZaGVkCt+8G5NuB90nbjpz + g/T3r5PGfxZ2/mf5n1uD/gQ9F+RTsWYwSwrBaXAkUJmT5t3PYkVvafpgupobqSYRSRIAA+doIGCFUccn + k559+BjCkZH1ra8DQOr5UZwAehBHQ4HpXQ2VvbXMH21IfJeYMp2SOoxuxk4IycADPXtXLUlyo6lG5pQR + LHmWTPKZ3R/dzwcfXk5B5/nTnhWVZXaCWNN467SHB642npz3wahikmikEP2wBTHlYyAWVeAcZ7dOoJ56 + 8ipLcmOMgFBGWG8A7/puB7D1rnG00r3M3xDoNhqdibW9jBVyHU7cM23j5sr0wSMHjk49aqeDtH0fw1by + WOkxmKJyTKjncx54zx2JPPfPpjHn3xIXWrPx4NfittTextogsMlsm395tYqFPI27gdykHILccg11Or6x + NbeDotcltAb42cbrGNw2swX75H3Rz91jyBjryO6eGl7OHLO/N0vs+l1+vmclOvBzn7SFuXr3Xl/kdZqk + 7A73JlVlDApxjHHOO/T9elZOnR3F8POu598KE5i6M4Ujl/UcdBgEHkEHFcr4P+IlvqDaXFq0JhlvpHhi + YBsrKNuAUI4B3gAhm5GSFB47m6eBNqHenlnC7VA29+O34Gs8Rh6uFn7OpGzNsPWp14c1OWhpXI+zja2n + xyRKykkYXbnkcY6AjGR+vWqkt99hjAkSSW1YffQFioLHgqO3PUD1z0yX2s0qYUTYI6Z4G4jONp6cEfnT + gdpM0smDKchScAH6de304/GuV6dDaMfM4zxh4Kt/F+pDU7S+FodkckVwsxV96YK7WUE4yqnOcjkitfwX + 4abw7p6wm4kcsN7v5hkjyVGUAwBgHsRu684Nc18QdV13w74gF3ZaMZ7CdPLdog4y7cliEI+YkYz6DtwT + r3Hi6+tfCA1QW00F5NbO1tYSoSy4Hy8kDKn5T0JycZ7jq56kcOo8/uN7X2fp/wAD8znah7WTS1S19Dqx + OsVm0d+iWazz7EZZV+diCVCc8nIGAcE4PHY8f8TbTVrOysn0i+uYIvMJnmeQhmUnncAARyTt+YKTkEfd + I5GLxHL4+bT7BYXgt4Zo5r1EkAAASUNLufO0ABs7jhVBO7jNdXdeJtBbwrfX3h7xRb67Dpk6xT3MZWQR + Rsn3J1k8syb2STa0YJwuSeu728DgJ0nCrTlapGTTVuZLs+qd76rtZq/Tx8VjKdSc6FZfu5RTUk7evmtt + 15p+a6d4k0uw8TW/hm0tNe1ZCsBn1Oe6g8uz85N4bYcSSRouTM4JEO05Bxhukl8yJY3uZPMDp+88sb+C + Rx29vy5FeGvdXniTUYIdGtXtLF+stjcGTa4VAfMJX5i3Hy9MdFCjA9k0vVf9OGmarbXltqCpvQPFmOXp + yhBznkkqR8oGc4IJ6eIsrnQlGpo5tXkl02+X3f8ABOTh/M4VlKld8qdot9d/n95filt7krL5UnknOPMQ + ZAUkZAB7Yzk9vrWHq+kx3VkYUKXMw3KGDKi5PIXvzyK1Z/N+2eVIY4LISLHhguZe5TBGFB5HQk5429ak + NmlmgjOmhBG53eUDkk5KnJIIOAMjHt6V8zSbT0Pp6tjzm7t57K0iFxE4uYlD7TkY5A7dCBnk10gtE1LT + be+ht5BHsZdsYHDDjkEjHQ9PbpWpqOnrNGGuJrySYp+5WJQFJz949TnGAc9sY7gVND1CC2T7Bf8A+jPC + pLM/SYliQO/Izz9DW8p83vIyiraMydGlMF+7JcQxSNgRhwQwx2GMj65ya6O81Cz/ALRt7e5v4rdrzcY4 + mZv3uAACR0XnjGec9zwM/wAS6bEzzy6ZP5UqESbVy2MEcgng8GuAsZbA+Kr298TySx6LpVsbi7EocKqF + o490YQgsxkZE2gYLMuSACR24DDUcVWSrNqNne1r7ab6b2/zW5x4/E1cPQc6MU5ab3S3189r/AOT2Or8e + 6LvvbHXZS82nwFFuUicgypnoQSdvPR16jg8hSOk1nxF4b03Vo9Lgu5FiSKCO1It5SsqeUhUqSDxtI6nj + nPeuS8T+LrBPD32nS2vo5ILmaxayvreS2v4yAGMM1v8AN5kapKvzlw+d2CDuJ6c6amo2mn62+ydLqytJ + FtwgQWoFsMCBAFEQYyZPb5cYHb08XQr0srjCvOThGdop7bS+HfTq+zdvI8fDVsLVzdVKEUqkoPm084Wu + +/8Alch0bxj4a1S4hgg1aKN5wdnnho1zxwWYYzyOM96reHvEl/rWlw6pBpMi2xmeB49ymTcvG9SWG5OR + 2zww64zjpo3h3XEuor3w5LYTRzRxf6Mx5LbTuGzKgAFSc9M11mjWmlaHp5sbWVY4IX3OskoyhbHX0z2r + wJwpQVoxbl56/l6n0UHVm9WreX/BItP1LV570xTaLJDEpP715FBI4K/KCe3B564xSavp8V7LGt9LII1J + 8t0IGCcZB4Pp/ntsjbwD0wMe34Vl+bba9pctust5ZSHHmIHEdxF82Rkc7c479QaijOtBvEUlbl3a6Xv+ + f3GqdNTjSnLV7X8t/W33nFajaSWN49rKyM6YyUJxyAe/1q7YaT5lvDdzyb4ZN22GE5nfBx8qkYODyfat + fTJYlvPsOvWVqLsYxL5WI5eM4Qty20YB9D7VoGwnNvayqtrHeW27y0QEQfMecjGen05r6T/WF1acYrR9 + X30f3a216CqYSVKbUi1q0ccunSrIqSJkEqwyOGBB598Vbx90buM8Gq2pFTZSevrnPcUf2jp32c3P2228 + gYLP5y7QCcAk5x1BH1FfI2fLc2WsrLdmJ4z0OHUES5/s+C6kjxvUh97cjoF6++e1RReF/DuoQTmG3nsh + KQ09tHIYgCDwWjBxk9eR2BrobK+sL3cLS7t7kIBu8qUPtz0zjp0P5Vm6fdWumRmDVbiKC7/iuJpAon7/ + ACsxBbaCAfTpXdCvfDNJ2cdtej7+S/VEyw16nK46vyOO+GX9rXurCU6nMbK0/wBbC87kNvDYwvQ8810m + q+H7SdLtNTngsNLj2fZhby+UFBxu3gjZ97p9fXmtHTm0+LTPtNlpjaa9xwITbiKQkEgbgPx/OqHirVls + fDs63kNpJdbU8uC5AZJfnXJ255wDnj61wx5oxb7nsYmu8ZjoqnHls0klbdfho7u47w94c0jT7Oa40y6k + u1mwVkMiuAVyBtKgDqSD9Kz/AA14g8S3mtWtjqeli3t33b5TbSIRhSRyTjkgdqb4R1vU77TJoYbTS7CY + 4/s9GjaKKX5iZcDPOAP4ehPNZelap4v8u202TTm3ZbZcXsM24nJbls/gOPSoTvZHZLC1Oau6vLKXeT1S + s9V6fmjzD9sieG58ZeFJreaOeJvDS7XjYMpxfXg4I4oqP9r+BLbxV4RgSGOEL4ZX5I1CqCb68J4HFFff + YP8A3en6L8j8lxtvrNTl25n+Z7tazMngLwmVlCn/AIR7SIghQZJawhOQx9s/kafoA8maY2sKFmjLrKxG + 3cBg/wCetUrOOObwl4YH2wLLH4Z0lkjKHqbGAenPAH51q6IpuYp1kcNsTygVB4yD3HPcjOM/1+Vxf8Wf + q/zPrMJpQh6L8ipbPCbiQXUHnhSGQknCZ5IyR1+uM/pW1IyhZLOwTymZS5YzljCc5AxyDkEHBOcHPPFY + FtC1rqdxHFG7XDKCxLFt3zdW/wBkDFdT5zzQsltlZELEBwG3jOcEjPboR68jtXJUsjqV2P0g2j28iy2Y + 88Bd7vgOxGcE9+hxwR37VN5wlnMIjEkyoQGRihxgg5XoQB6H0+lFtLPOBG9q9sQMkuynnPTg1DcWs0e0 + /bZmwBnO3k4x2H8qw59WX7NdTF8WeINH0fR5n1SNrlIGXfHGFdkycAYJwM9B0PPpkit4X8YaR4jt00i5 + t7azla3VorMTrKPKHGMrja4252nkDBHfFvVNDS8tpbe8Rk87ALKu8OOmWBzufbwfqM54w3wto+l+E4pL + KwsZvLnKsHRGkLZzjdjOwZzyeMMOew2SoqlKMo3k7Wd/vv8A09OxjL2rqxlGSUVurf1/XcntfB/hi1uI + b200tILmJtySozIV68cHoRnPqOvpWvA90sWyaWGVhl0dYdsm7gLk5OeM54HPpTobeZ4UuDLD5mcSKQdw + 4yVx9OuP61C+VuXRn3FPvDJOPp7dPpWEnLqzaChskM2QwXTyoxd2x/FkEAccdB6cde9WLdIrhlZZYluG + 4VnYgk8kAjHP0ot7d7pQuEYuMYc4b29/Solt7i1uXV4zMlvwWjUMACM5JPHHfHI4PTmiKuOcla2xS1eK + G5afT0hgkYgsWZtwhYZwNuNvQjg4OMnnipbKDSJtHntL3T4QixhZTJhflXOCfTA7gjGOMCrNxMZwy2hA + dBuAKgiQcHGfT0I6Z74p8Ly3UZgms2gUqd4kKsDnqMA1TlZaEKHNueLX/ivRbkxafY6OmlRJNPFMu8K0 + yy/IwmTuGXKNk5wwGccjJ8deHtdFjHLEZLf7MsUTJG5lKRqAIg7F90SoCwUYyAcY6V6zqPgHRJLq1umM + /mW44JcHzPYkgnrk8EYzxg8jU+wyQs5lTY0oCsdpdZB0+cZ5fHHUdRnPAHq0s0dFxlh/dtq1e+vlfbQ8 + 2tlkK6ca+vT/AIc5Dw5NaaRaWtnaQLPrM8SfZ4WjcRCNgCLpn27hFjBaQgEHIIDAgRNqniix063leHSr + XxBJeCe51SWOK9iuLMoCn2cs6pjeJFMYYKu09GJzoeK9MMFtJYWml3NxZ39q9pcJBG0gMUjK5X+Jogzo + pJHBwDkEnGp4L8HW2k6bG6Wtpbhbk3C2oxiGQxxq2EREQNtijzgNym7cS3HbLNYzpOpJe92et/n/AF5o + 86OVTp1VTXw910+Qmp+KbTTPDtrrmsS21nevDGZbe3cOJpyq7kiySXQt907j8pBLEZJ29A13+1NIj1FU + lKSBiI5QM8EjacHnkE9TXlHxDvfJ8Xx3h0rS7+/ivfs9s2om5e1tV+UGRlt8SMwZVwBkL5h4Y4I9E1LX + W0y+0iSbR1sIb3TotQvYo8FLOV40Z0Z2KjIeRfmJOdw5zmsIYCnicLCdJfvJy76Ja6flr8rHWsZOni5U + qskoRj13b01v066fO5XuvFOhWPiH+yZCsWY1l8yIgxpncgVsHjHl4744zil1WxfUm8024juCyuzqnUew + 9McH/wCvXAeINVntPE0d1pvhmw1Y6wJZcai9xsnhGWjih+z8CR8ttdyUU7T8o5Pp96NE0/S3I1FotNtR + 5dren52ZQwWM/KOp+XoO/QVdfB044ei6UZKrLRre76WW9+ltb6NdiMPjajr1vaOLpx1T2sut3s11vpbW + 5naTq3k2n2C+YQMnAmc/eBPA2n8ufQcGud8a6Dfy3K32mwwGeJfJngO0iVWYncRnByD36HB57a2nTaZ4 + o1R4tMW4n1CKLMrxQSSIiYPL8YXODgtgelWLSS4huvsl1cTb3cbX2qdwB+6T6YrkqU6+Cq+/Bxl2aa/B + ndTqYfG0nySUo900/wAUeN+M/Dd5a6ha3upH/RoZoo5YbaNXWKPceTGdu8nOByOwJ6V77rd3c2Ph23uY + 2trgR2dsFljO+KQ7EBKtxlfmyMY6g1Q8Saat1bSGK3VyQynZEpHPXIJq34lSSTwjDbwWz8WdrtSNflB2 + JkBR7AenA9q76eLeKpwhV1XPH7nc85YKnh8dGdPrGX5xOIu9TvLi8a685oXYYxExUDgDjn2rqrK0hupY + PJuY7hYnkW6WSQSNOOPLJIyGx23YxiuSlsZ0EIwGml3fuFBMiY/vLjjI5HtXcaFpwsLGKOQQ+dzvkQfe + 5yOcAnqK9HO6lChQg6LSeqSXba/y6eep6lJSlLU0AW7DjAzXHeNNP8QNLNf6Y0cMS44tN63E/wB0fPt+ + 9jnHPAzXUtdIL2O1UPJIR8xQAiPjI3+me3HNTjoSGFfP5VmNXKcTHERgpabSV01f9bfqY5pl1PMsO6Ep + OPmtGmc5qPnXmmTxatp939utNmJ9Ph+Y7yP9Qzc9AA340lv4mtrOJzf299ZQgqE+1Q7HYEjO0clgO+On + FdBdTwWts1xczpDAmCZHOAPx+teb6t4r0jWp54tUtroWi7RaNDGnnL3bcScDOB07VpQxOX1KnLiafKpN + XcX8O17R23u/JOy2R0LLc2q0L4OXP7NOykt23peW97W+673Z21zM95ZNfJH50C8wxKN285xnjqahvrq3 + 0DwlfzXRku4ZLi1tVe5tIrmdeLiQlvOVkYEqBnjpjqOQpZ3eoWjkW9xaKJDamDDRgBQH3npjOMY79asa + sba/8PT6Laxxbo9TtYxviDQqfJuW5AOcDaR/vYrOhBpWjs4t37b2+57+oVOWFWE6n2ZLTq9dV8132OC0 + jxxrNraXa24s4LuTZ5ItdEslRsE7t+IsnA6Y713Og65Jq3haxvdastJ1C5cyBPM0u2JJ3kcDy8DoM49B + XET+G4dPtLK8kGpBP3guhGo8yM5ATAwMZyM57dK3tGhsfskWg295cyO24LdWUqnyQCX5Yfd3cjpzyK9T + G5QqOFp18PVlNtPmXRON+bXaytpfdNNDnn9PE4urh61CNK0/cmrXlF6LS19ettE00zrdP8QPd3UNoV0y + 7utQtLw2kkdnB5SPFA7DLqAwAYDpnoaxtW8L2mqal9uu7i7kwPkgZw0KEqF4Uj2B+tdJ4dsLGK9j8q1t + kEFrdeSViA8vdA+7bjpnvjrVOQXP2mHyjF5WD5obO7oMbe31zXz0nKpFOT11/BI7IVY0JtYf3dLPzu3+ + ljNjjtbO3hgNlCk1sCLYpEMgt128cZ7+tcnJaeMNcEE8l3FpkwDYQvLA5GccjB9OPY101rrr3uqRw2Wm + XE9gxkVr7GEVkyMAHk5YYz059Kknu/tOuf2ZE6yiFlaRo8ZgyoYb/mz83GOP8al03s1bQ0oY90XzJKTv + o2r9/wA7ngn7aH/I8eFuMf8AFNJ/6XXlFL+2jn/hOPC2ev8AwjSf+l15RX2+D/3en6L8j89xf+8T9X+Z + 7fpkSP4J8LOZdrf8IzpUewE5cGwh49P61o+HYporO5J2GaRcqCuSuCegA6/Wq2jK0ngXwrynl/8ACNaS + r8dB9hhOT+vNXdPEy+ZHEu5WB5cc/wCPfrnmvkMZL99Neb/M+xwcb4eD8l+RlWglGomW6bErR5DgdMN6 + d+Py4rRMN4skdzbSxwiZMriPnAOMYzj/AOv65ApDsgkcThQR8oZRk9R2rUEg3qiKu3uAjDIJJwAR6nnP + Nc056HVGF3oNgNxcMGd/LlICsAAVIH4f5z04qwMPj51U5x979Oen4f4VFFGMuNwjIOFYqOPz/PPX+dQP + FLKgWK5VnjkEck2OYz129eT3OcAepIrBLmNZvlNNpDNG8BJEW0qzdCg6ZB9ev9KrxRpLcySeb5yINqqp + KbBwOMcg+/UZODUFybkW2EcbF4OTnHXmo7aKdJBI14V4PK4yyjtjHSnG6W5EopstRNdw3ZiZ5wkisBI0 + RwOSSCcAdOmOfbvTL11jlxaSStcAHCoobIB6tnovB5yMnoc0+aeQxgxTuykfdByME5JyPp0qpNcI0BeW + APJEQyPNkhRg56dOPf8Awq9yErasbrupDTtNk1BblY3VcR5UnGcZVRkZwOeo6jrzVPQ72LxTaSNYXAjw + ScqQSso6MwwQSO3Xp+FeTeOPFQ1jU7popt9jpyhYlLECSZyREOBwAQXPqEIr0P4XzunhfTs3Oy7EAjds + KNpD8A9MnBJb1z2wa7KmEdGipPe5zwxKq1HGO1jaltL62uRJDOIzICctudickHknnJJOT+PUCr8BuLhg + zv5cpAVgACpA/D/OenFWb+eWW9JlKs5A34UjIxnA4x1PuahijGXG4RkHCsVHH5/nnr/OuCc7nbTi7XZK + MPj51U5x979Oen4f4VQ8W6W/iHRJNL89oYshmY5A+XOMn0zg9+nQ094pZUCxXKs8cgjkmxzGeu3rye5z + gD1JFQanqcNrttJb+BJTgLC0gaQ7m2ghRycnjgVUac3JKF79LbkucXF823mU/h9p2r2mj+Trt+t/NE7C + LYqxGKI4wMJgKRzgr0BAGKvRa3aw6pdWL6jtMKfvnkQhItzHCs2Aoz0GDkkEYyCa8+tU+ItpraC01KOW + CW4EkksqNIrR88KQCqx/MG7H5VHy4OdvxR4C0vXryPUpWVbgfvRtUv5blizMrgg4z24x1Ht2VIwVS9Sd + 097LZ+lkt97eZxrncLU42a7/ANf1+JU+KdtaaNIuv2sDpKs+6OVSAPtCgMhXPG8nPrkAn1rk/EGu6/8A + E7Vxd7Yrdk2xiSOON4HPyBsgAsXYDdjjtnAxj1zxGtveeFL631JJJLaWEqVkJ5PG3GOR8wWvM/A9zaaD + Yy2libW9u4rqSDy55BGI283Ytwq43eQWyp+XeCgYDDgL9Bw3mSw8anKrzVrN9E01p2enl+Z87xFl7ryp + 8z917pbu2uvl/XY6K7guPDvg+18Nym0uNMvSXuNPjTE0S7lJlRuNoDdm+Qk92+V/Or/xC48YWWtappia + 1DFGxGnoSBOMHCjnIGTuKjO7Dfh6B4evdJ1rw1qM2b17u5jN3JNcqFa5gDyLFIArMIlDROvlnayjacHc + WrBufFV9omoWd2utTaX4Z09LffoNqUWCdFx9ojmTYRMZyGwxPyh9uDjFd2Gx8XiasKsf3mjjK+qvfm9b + 3XpZO+px1sFKOHpTpy/du6lG2j2t6Ws/W9rD7q28KahbyaNpeoXumaJqbPqVsr3khezuJIAqrKykF8cb + N7HyyzbgwJNaF9qS6fqNnoGrsPtLE26SxuT5rqF2k5G3Db8BlZgSM8Dp5rbf8Ta/Mukvb6XOzFfsgUJG + QDtGAxVQOckkjHJOeAPWrCz8OeFtK8Pt498TaBp+oSebPbA3LyKokCYRWj3BkXa4ZuELO6gvgmuvMaGE + xGDVROTqrdPq+6f6O/rffmy7EYrDYxwaSpPZrotNGv8AK33Gzo+ow24+w6g32eSJMs7tkTZJIHfpnn6V + e8QpeDxhpotGcaesccsoViVUrFhFz6MSAfUZ69quvaKz3ctt9k+z3MMoD7QG5BwcY4xwen9a2FR4RbSA + nzDY28b7sgMAiHlT3/Wvi1JQpSfmvykfXVI8+Jh/hl+cDi/CFnfalfak+stf2NxG8b2YyvnMjDa4Zhkn + BAHtk+orTk1u+k1n+zrS3tPndwjy+evCYBz+72k5z/EB6E4rRvdVsrS7aN4p5plQM/2e2eZkGfl3BATz + yR9DV28hNzay2zs6eahQuhAZcjGQexHUH1rOpipTcXNaLbsdkKbimlIx4L2xXW53gu7cRvs+0NLIADhf + l8vHX3zWzFLDNGJInV1OcMrZB/EVyniPw3/Ztr9ogulnjiRQ5YBWI+UKeBjdz83TnPHarHgSWVo7qJnd + kTZsUtkLndnA969TGYCjUwf1ulO/LZNP5L/L5Cp1Xzcr6mtdQQ3t6be9jR7cBSkLLlXPcnPBwa4MeErz + UNHsLWC3htLq18z7TLOjJ5u5spghSWwB36V6AskWoM6Rq/7srsnUAoxI/hIPOMYNc3qOq+MdPgYyQaRM + 6gYjiSVpJMkDIANeHOKinGcddPl1/H8D2ctxFenK1GaWvV7vVfr+RS8IWFlqtvdWr2V5Jo6KPsktx8so + zu37GBx97rjjA5z0r2Dwp8OL3StJfTb5NPuYxLHLDJFMUkUosiDc3lYJ2vjOAevrXD2+o2NnMdMSCO1E + IBKqY40QNgkgEjI55wOp9+fVD8SNB+2PZpbalJOgBZUtwQMjIBbOBn617eX/AMOUMRorK3pb7tmvvR8z + nknVxX1jDx1d7/h16vd+tzz7xdp2mRPe6Nf22qCWPCvHDqcUav8AKsgdXaENt+ZQSOhBGCOvE+Hx4Q0j + XEs5bfX7U7FZZX1iFoXPl7jubyRnGcA5OT6V1njvXLPV9Rl15L/UNHtkVPOSSWKIqGCptlVtw6rxn+9x + 1Nc7b6do/iC3mazXMFw4naXCF0zyBGRnapxmvSyzMKOE9tSqpqlO8Xy9LvSVtnout7972PIx+X4jFKlV + ptOcLSV3u7bd1e71VraaNXOv0HXLGeH7TbaPfwFklib7VdqGXchTJURjPUnGR2qpqpA06UBpVG0ZaJsM + i5GSPQgZP4VFolne2Ae0eUTWyFfs7uxaZs5Lbz06nAx261m+O3eTS47CKQpPdvsjG3cGIUtjHfoOPXFf + O4nkliuSnbkvdW7PX70tH6HvYZzWH5pp89tbvqtPub29R+j6fqj+Hxp+qXgj+RfJubOUpMVBz82RhTwO + nUE1NpTA+JdZGbAgfZ/lhP74fIf9bz/3z7Vzfw4k1ZdUeJZ2m0R7MSxIUAMDlgQueCSQWz6kZrpdI+1/ + 8JPrRls44o824hmEJUzfu+SW6Ng8DHTvWVeHLOe236o2o1HKlBPo3+Xc8C/bQx/wm/hbHT/hGk/9Lryi + j9tD/kePC3GP+KaT/wBLryivscH/ALvT9F+R8Zi/94n6v8z6G8MFR4E8IrNIqxf8I1pbHPGP9Bh5PsME + 1oJPaRxmRZJXVgGBiAO1cc8k45rP8MSj/hAfCkRwc+G9LXBGMA2MGeauWcX2e6zcyCeyUBguz94mM8Ek + 4YHrzz9eK+Kxj/2mp/if5n2uEX+zQ/wr8jOkktry1aUqY1nYKJJl2M2cY4Pc54Dc+orRsNMhlCNKolJY + +YWKE8dFz34/rTb6CyR47m1lDxPINyhflOcnAxxnv/kVBb3McEMsXlyRRlXaMsgbawO4Bcjp2Az6Y4FZ + O70RsnZXL11Z2NuD5lyYRyFVFxxzyDnjn3z6daitWPzWhLSRhVMGFG5lORzz0HHJ9ec4ybAtorkiUIh3 + DneQoIxgjtz09jzzVVrYJaiSd/MkV0UhgQo6ZJQ5BOOMn0OAMnM9BX13Ld/bSxWZm2maM5+VAXYHPPA5 + POBgc81QtrW78pDMGjLquUb/AJZswPDY4J+hIrYgWCG3jSDbE6u3IfGEJ5APIUZA+XgcD6CG7eSRUlMS + +VIxbofmYclicA59D6HihaISlJvUht7co+RdJHGke5k+Y/L26Dnn8q4v4r3bWenSxtcyR2ESNPqEsWA6 + xLxtQjks52qM56+mcdPHdXNrfyLKryJEm4uirnjnBH09M/hxnyr4uauNUddJiWNIoxDPdKg5knZS0cJJ + 7IgLkZ5JAPSuvBUnOqm+hji58lN2e55jqcn75LcQpEU33E0aryksgUlPXCIEUe4b1r2Xweup6Z4VtteW + L7XY6eBbXlsEDyxWpHmPI3HIDOpI/h2YHLNnw/T1nur2MMBuupA0e87S4ZsBj6A9ya+g/hvr7WNxCiMI + mvElRj9mCszbhwhVjxt3sS4yy4wThmHs5hpBLc8jA6ybXyOxt10zUY31KMRtbeZskby9oBUAFeRkbOd3 + ZTuHODipFe6C8as18bMNCJlRl2MIyzICOcj5gee45HBzWboPiGXQbJtDhnRbWz3yBpgsytFPKAJssw8x + NrHDFFB6nIyx1EvtAn1C6tbzR4ru4dDPdmGL97C5OxcbTtMqDZhQRgHsCBXhSpKLe9j11Vk4ou2oUJJC + bhJbVIg8MiLv3AkjaduSTxx689dpNcJ4t8J/8JdcpqmlG6VvMQJKElgnSRPn6FdygYQjgEE9sgFNPuLr + wjrF/wCF/E3iGJ3sIIwLq3UiG4imk3RiTec7txO4H5QuRyCTXf27XWruYbhheAwhHsLsPCxldCCrleBu + DYxyV+71Jq+R0JqcXqtn/XfqRz+2p2ez6HFaHJp3hJE8P6nqNxDcmMzFbiN9ifKS+07cFBtJyMjknJ5r + s9PiSeKK5tL6GW1ZSQYmLBhkjggc8jFR+PHurf4Pa/p0FjqUl5CYLay8wG5b7RI/KQk5ZgNwQFRyeBk5 + rkPhP4rtLjwRpGi6nPeO9tG8UreXlYIovkCSA5aMnBII27hHnIztq6tOVaDrbu+v53IpYhqfs+x2Wq21 + vcWctpdRyXEHV/KyWXHzAqAck5wQOc++a47w58PfD8st+s1//aqbdkERKxLAXLHgopD4BwAxYqoAwFbB + 6TUJoDArW1zHMHKxRLKSEZ2OFQnllJ4JJ3YCk4wKraVrGkJq1xoQkUX2n7ftFsyssq/NyeM70J2/OuSA + cnHfnoupCLcGzprQhOSU7XIfCPgM6DBNcJdPf3UsJjW4KbNkeQ2wAdWzgFmJc4XJ4wMvxdovhK00h9V1 + O3iEJQb1UgFiSqEqjfKCMndle56Hk7HxE1WPSfDF1rthDsvBHI0UjbcKxjYrxyGAIyB7/Wub01pte0wa + pfzJeCwiEmoT2Al8tBIWjUOPLXeytE6iSIMmYyG6Zr08DhqmK5sRKV+V6p3vLrZWW2yfVXvZpO3l43FQ + wyWHjGzktHZWXS7136ro7Wum0Z/hXwd8P9b097TQIJHuGR5o7e+iaRJSqEht+xoQBtkIG4P8rYBAFNsf + CuqanrMVhNpkNvHFb/ZVuJs+StsXyYcIdpTc27YTyzHnGcTeCdc0LTYIdFdRDZXF7K11BC7rIiD7oeMq + UILMSuCwGW4GefToJNLmF3HaeU67pIREtuu1wGYfdydqMNpwx6FelelnGHr4CccRFOKmtLvm09btX/rq + jzspxFHHwlQk1Jw3sra+llpr/WpALePUFAa7YOy7o7pnDM74ByT6k9T3JNZ+r27xa7FMdZmhneGM+Q8g + aHIhjXG3IPU54PX9ZZI2W5MUO5cZYRu7SEf8COe56Dp0HAqv4x8NaTrymO8RxLJFbsJ0c7lIRMEDpzj0 + rwqU17KXO9G10v0kezVhbEw5f5ZfnAoxLc6E+qa5reo2z2RSFWljikHlgFhyCzYyz9vyxjGD8TPGUFp4 + bmsLBZzdXm0QXKPtCpkHejLkHIIIyRwc9arXPw61KOGaz0/xCy2NyNt1BIpAlA24zg4Jyo5wK7e80iw1 + DR49N1G2iniFuLfBQfKu3Hy+ntjpVc1CE41G+b8DTlrTi4Jcv4nI2a6q/gHSp9S1KNpnRjJEZjvcBlRc + juwCgnPbFWvBqyHUwy3SRoPvxmQgyfK2MDvjr7V1N9p9pe2i204JAUBJBjcvI6ccZxUP2Ow0+eS7igHn + SkCNFA+U4x8uBxnvXp083p/UZ4dr3nfppq/616W9C40HGSfYLZG0q3NtuWSAY+zKM+YxOS27tjJ7dqmS + zaYGS5f/AEhsEbR9z2FOtICJPtNyQ8zdh0Qegq0NvAPTAx7fhXhVarnNye73/r8zoS0Ma7tPt0otZo4E + MnM0oUeY2ANpU468c+3Srt5psdzIJFuZ7Zj954G2F+B9445x/jU9xEkyYJKupyrZ5FVmF3Li3cBE/wCW + kg/j7YHpWscVUSjyu1v1/wCGX3IXKch4vXXLzXEtbSwsbrTZiyyFo3McgVUz5xAI4IYLweo44rrtB0yx + 0rTY7bT1Cwqu0E9cA8An2zgDt0q7EscahEGFAGKquptXMsQJiP30B6e4qZ13OCgtP19SY0uWTkV/EV9c + afbJNbvakd1lJ3N937oB5xnmuUudRj1N5ItZh8+0lILImcxkKQCnPB9a6TxVDLd6ar2kEMwHX5SZByv3 + PyOfpXItZXiyJG1pOHfOxTGctjrgd6+pyTDYSrhH7RLm1Xn3/wCCY1pS5tNiJNPvdM33/hvV0gV23tDc + yKXkABUKwz2GSPqDXUfD3VNU1ayvZNWkh8yG7MCCNcDAVTn35P6VxWo6pBZMY4vLWaGykupmuD8jfOI1 + VBj7xLZ5z93pW38FFlbR7u/eSSRLq4LyGUYfzAADyPvL1+hB4FedmuHpU4y5NbO12td9v+CY0Jr2qjF/ + LoeWfto5/wCE48LZ6/8ACNJ/6XXlFJ+2hj/hN/C2On/CNJ/6XXlFe5g/93p+i/I+Vxf+8T9X+Z9EeFNw + 8DeE8Ku0+GdK3HjJ/wBBh/H/AOvj3qe5xJZyLBcMm1wpK5fLY6MAMgc9TjHB5rL8LW0k/hLws00ytbR+ + G9JbyFP3sWNufn7kewwMHnOcV1FyPs42tp8ckSspJGF255HGOgIxkfr1r4vGRtiZvzf5n2eEmlhoLyX5 + FYQT3elQ2x/cyoV3eWN+QcZ7ADj19/qKyaSzIWmDr5cgOPvHJIHGe3ck5/IVPLffYYwJEkltWH30BYqC + x4Kjtz1A9c9Mm7aXMF7bSGGcRo8WI5Ac7uDjBGe/6VhzNGvK15lRfliaR5VCnCq+7GDjoeOvGPxo+zi4 + mLzSLgyBSjsCvThgTgYX9c1HaWTwztcsqRmRcu0bs6PkdCDwSM46Z69quW/nPZTJcWpgVpM74nB6jKlc + HscdcfTsUkkOUm9hsYmtoUEaBTykcquCp5AIbnjPJ6du+KqXfk22mSs9zfO4YlJWmUEMVAI6YUYHp79q + j2W1jcXCiO6uWA3KWQrIxPJyTgN174A6D0rlPFF3OmnzPNdbcZELRNiBkP8AFycM+Ozfd7Lg5OlODlKy + JlZR5pGd4v1ueDwdrt3Yrbx5kFtbhlO0b3VFkJyQwyTn1wa53xxYW8mjanJaTwfbrHUneYMxcTR21kTK + +OpJd2G4nrIB9K3ia6jPhe10JIZMQypqM7xOJiPJzyQQvXkk8/dI61P42R/7XQtMiqmm6pDMwwPNZrJJ + Wx0BBycADouete1RpqDVvP8ACx5Vebmnfy/U8n0+Rkt2lYMdigAFQeMAcjp3Jz9K950nTHf4eT6nqK3F + xBJp05ZoxmQMigqYWHViWJ3DhfmzwGz4ppFrqE2h3txbWUslpIsUUjRxbiku5WQ4HQnJAB+9yBkgV6H8 + DPF9zZXL+Fr9Le+s71ZRZRTZHkXPlmI7QfvLtdwVPGV6bsV142DnByj0Zx4SpyS5X1NXwxq7eJvhRqOq + aoYG1bRbu2tp7hIcMbbAKAngc4kGfl6DnoDg6TaeJfFOha3rEGsXFtLpepWzW5wWNw8so3EMccJ8rf7W + QOBTfDmo2elfDnxL4ftDFDe6xrYtrYMhDLCg8sNyONoLPyRyhOQa2PBPiWLTvhidMZCT5t09xLIT8iLg + sqheTJtCgcYXIJJIArnlF0+ZwXXT06/5G8Xz8qnLp0E+Kv2fxL8Rdc1GORoF0vRBqcfloqrcTrKqAv1O + 0sXbOBkKMcHm78K5Lm68D2FhDql3YTyi6vdbv0IZ7OwaZgvlknPnO4cKeSokdhyUrO0bxLFefCTxHbXl + jpltqVjYGBbmKM+bKs2JJRMx4I3mKNRyRtPYGtLwNeaHpP7N+o3FraQSazf6i+nXMoDCeT7giXf1Chnh + O1TyobHOcTKLVL2dtmkvu3/H7yea0+ZdU/uv/wAA1vB/jSW71PTfFl6RoOg2WpG00y1tkMkdxOytFNcs + gBaWUhvLUJ0LMegYtz+o2WneGfjDqOteNbKfw9puoajKdPeymOFwRI0xCZ3Kdyjb2LEY4rE+Jb6hG2ne + H4I5LSDRIxp2mJEnlsscIy9xtByJJCN7E+g962bi41D4pab4n8ZeJ9NMOn6Vo/kaTfwMy7biLDbQpbDq + +WL/AC4A5U/LmmqSj720WrfK+lvPv8yed3295a/P+tvkb8Gs3v8AZtpqey20+1md4INWtSly7M55MseC + IGdQuDhtuQDt5FWLnQ9E1TxfbeJ5jd6LqtqiS3kdi24PcDIWRtwy7FSAyqMOCdpbk15laSeJ7BPC+raI + qXcmpacbOK2VPNiljjcrLBOpwpLDDHPOCpByOPUdLl0myuLG/utWj07Rb63kl0synzJEljKqbPI3F1Vt + xRVBJIHOQBXPVoulrB732/L+vlbr2U66q/GtjS1yy/4SDw7eaXdxw2DJMEuY42GzeykBkxyYXDf7wbI6 + Lg8pa6J4g06wNtaJK1nK3ziJ0nSdiDtLqmRzuc9APnPrz02r6hPrzWestp0dvocMIgvLDeDcTweZlpZH + UgRKnDGNc/LncxPyiZPC9zoiXWsad4jNqbqaRLPTr5hJDcSFgyLCwGQNmdqEHHHIAFc9OrUpJqErX/P+ + vVGtWnSq2dSN/wA7f18zkvBOk+F/FGrx2uuaq+j6bKrOLiK6jiAcIMfNICAx5OMZxnPv6dY2+oQaYhkg + tYbcBUSKKVpBGgUnmRsb+CB0zwT82eOX+F3hzT9d1T+xPFsG77LekIm7y84EgCllYncW3ZUtnkYxxXVw + xLYWwjklkeOPOBznp0PXd+H5V15hi/a4OnFyv70tOn2bW133v8jkwGF9ljKkoq3uxV+vW9/La3zK2qXN + rbWNzdtLJti2GPyox8+4j7vPTkE56c9ai0nUpNX0zTr2QRi4udLtLqWNeMb4kJxnnGTj8qj1vTJr7TJV + huSkk+5h5oK8hgc4xhemOQc5z1yTNYWP2DStM0/50a10+1hwxDMNsUYwSOO3avPg4/V5p942+6d/0Oqa + n9dptP7M/wA6f/BJ8tjkcc4o+fI4+bikHQkMKiup0t4xk7mb7iDqxxXGlc9IwbnW9Xg8Vf2edOzp7DCy + iJssdmeGzj73HStq1gk3fabnDTMOMdF9gPxrhbh/FHhdRd3LxagJRy8jSTeQBjqTgLnd+OK6m21ma38K + jWNWtpIpFz5sMcZBX59q8Mc+h619jnWSTVOhUwihKEuWmnB6zl1dnqm3o/l3PlMoziDnWp4nnjNXqNTW + kI9FdaNJar59jb+fI4+bigFuw4wM1zNt430GS3lnuJ5bOKJkUtOgGS2cY2k56V0hIVvmcKQduD65xj61 + 8xjMFiMFVdHEQcZLo/S/5M+iwuMoYukqtCSlF9V62/NDstjkcc4o+fI4+bikHQkMKybu4n029mnliu7u + C4MYiWBS/l4AByCQBkkHjrg1lSpOpdJ6pX9f6WvyN5NI0Y7qB/L2TRMJQdgDg78dceuKm+bHI45x+lcx + qviDSrDRPtsP2a2uYkc28Fwixuhzj7jMpGT6YznPrUtnex2t3bWF/eT/AGmDfvk839y+4ZG4k5OBjHvX + VDBOrFyp30vp1sr6/kn5sj2qvY1nR7N/NiXMZ5dB29xU6iKYxziNH2j5GIGVzwcHtUmPujdxng1zFvf/ + AG/V44dOs9RawkeTfeRPsjV0yMBSMkErtz05HauWClLVdCm1EqeLPh/p+uXqXau1uQNrIANjKMfKF7Aj + dkg9/rW/4U0d9C0eHTjdSXciOzNNIOW3MSBjJwADgCrVqJ45ZInLyRdVkYjv29f8/lZx90buM8GqqYip + OKhKV0TGjCM3NLVnzX+2h/yPHhbjH/FNJ/6XXlFL+2jn/hOPC2ev/CNJ/wCl15RX22D/AN3p+i/I+Jxf + +8T9X+Z9EeF7a4m8BeEHS1u3C+HNK2PHEW/5cYTwcY6jHNbdrbakijEFxwCRmJguSOm09BXz94p106DD + 4aXQdQm0i4ufCujSajJbXYje5lFhCqlijbgFjCAI2MHcwX5yz9n470rxL4dt/Et7b+PvEM1npUkSWzy3 + sq/aAGjjnOd2PkllRcDrn6ZyqcOUKkuedRpy1+//AIcwjxPjopwp04uMNNb300/Q9PSyveZnt7g+Yc7f + LOAfpjP9P51BJojec80djOksoIdk3oDk5JIXAJPr1rzXxVBqGlfE638HQ/ELxJEskMu+4udTBCyBZNrE + RyMVTcg3BgHAydvTJFpfiiPXpdGvvHHiK3uoNQ0WynT7e+Y2voi0gI3dUYYHrWa4Zwy2qy+5ehb4pzLZ + 0ob23Z6jDaapH8oFwItu1V8kkqPQH047gnrzzw+CzvYo8G1lEbuAy7GfPpkH09a+UvifqZ1DQvEVhruo + z6xcaeQNOluroPLbyi5jRipdt2GjLhkXOTtYr8gZPFKxrcOUackudv7jpwvEeLrwbcYpp22b/U/RJ7G4 + kJDWdywL5AWFlHXt6fj/AFrgvH2ja7LqElppek6nDARnzLa2chTwTtIUkHbuxgkBucZIx8VVY09o1ud8 + qK4VHZQ2Mbgp25zweccd+mD0p0Mlo053Un+H+RrWzjFTjZ8v3P8A+SPpfxN4S8Tahol/qMPh3Vo7xLWB + IYhYys53yOWQApzgEMxzwT17Vd+IHhnxRN45W5t9A1W7sRo1ysQSzlZIJTE6YGF+8RsA7keleW6poum2 + 3wiGpW2tape62lvBqUl9aO81uiyyLEbKTbJ+5dC24yOgBYbARlS3H+Dku9QvTf6zq2pW3h6xkjbVLmK4 + IcIScQw5yGnkCuEXBGQzNhEkdfQeBpRa1f4dTzqeY4qom1y79n0fqeiw+BPHlvaWt2vhXW1LblXbaSiR + HTHUKNy4yCrYwSMg5U47/wAP+DvEt1PaeM7zTtXh1200q5muY5rJ0a4uYLhEhYgD53kgkOcYZjDuOckn + gfHXhfT9N8Xav4bsrnUoZLmPW9UsbkXshFlDYT3yJbbGJ8wOtlJudm3ZkiIwI3E8M3hGzm1XX9Pj1PWI + F8H6jcWF/Il4xfVVhtb65aQbsi3djYSIMB1VZYsqzRO0zqYWlNbv8PuFHE4uLveP3P8AzNvUPh34usJd + ACaLql5NPZfbr2VLKV1E8pZmRxswrLnDKevXPOaVPCXiw+H7rTpfDXiCF7u9iid/7OncRW6LvYhQDuBO + 0DnOcj1ry3xaFin8PalHJcJp2v2X26TT3uGkjt8XU1u8avI2Sm6BnUsdyhlBZipkbi6HhqbS1f8AWpUc + ViU7Xj9z9O57/ceC/F1mbjTZvDniK6trhkuJJbSwlIdtuYwdyDO0sCynupHHWtK58KeNdCm06xs9A1m9 + tre9i1aKWHTpPvbUK54Oxx/EhJ2kYBPJPzdRS+rU+t/6+RX1nE9Gvuf/AMkfXfxA0rW7x7ay/wCEIvtQ + ubydbzVb1bSYlgR+8iSQAFdx4AABCgFsvyKHie58ean4StfD1t4O11LKCbzJUfTpcSssZVQAqgqgznvu + JOcBQD4D4M0rWvFnjuy8LaDc3Vut3deVGImLLBECSzkKcEKgLE55wTnnNdT8UfBGq/DLSlt/EPidb3Xd + Qk/0S1tJ5mWC2VjundjtwzFQqrhhhnJIIGFHLKCSbu7GU83xPP7LminJbWd7L5na6b4U8b6NFEs3hfWX + njV54BFZPNGrt8h3gLgkhenXBGMV1FjpurXlivn+B/EumzxQCS0a1tmK2c6sxM0KOAVLsQGhyq4yxLEr + jhPiNoyiwtbKO/uoV0nU10qOSBIoZLiNrMXHmTOiAySAkJuPG1em5nZvOYbS4eCKQ6vqmXhtHOLk9ZXK + t+g4/rWOEo4XMaKrUm7fd0T2+Z5+XcSVcXSVWm1Z+T67dfQ+v9NsfEF3pr20elNp8ZTz2huLRnkaZxkc + 48sbThs/Ph1XjAGWSQeJjGP7L0i6N+8VrPc3EtoXkdFVtzKZXiQtu2jydyMokZgHIKD5DmtLhIJZBq+q + ZSG7cZuT1icKv6Hn+lV/EMVzp9rPJHqeoS+XdrBsmm3qymIOcjGDycc8Y7VccjoU3zO7S6aHqSz3F1fd + Tim+tn/mfT/hXwXeeIvHUh8XDXrDS2heeN4NOdCZDLIoUsVIUbIwxG0kl1JIzk7TjxPot3LHpmnXutND + DGsQk0iWK1uGSWSOViC6NDnYHBUudrqVSUV8Q3wiF9OIF2xCRti7g2FzwMgnPHfJqGu+fs5UvYON4dFp + ZemnlqcdNYmM/bKoud2u7O79fe8z9EXWWa2eZNJ1OJ8q4DQklUIzt6AsRxngHOfpUF6T9oVJVZT5EOFc + EMP3aZ3L2Oa/PWvrX4BNdQ/CfQGtbdriSaOdUycRxkXUucn+HIOT64AHNfMY3LadGjam92t/Rn0mBx9a + rX56zVoxeyd9XHzfbbzPTZHVVJAy+Dgbutcz4jvNa057a4sbAX002/zB5LuIgNuANp46n8q1NP1uK8ub + i0lmsVurcb9kcgIZAcM4I4xyBn3+laUMyTKjwyJIG+6yMCDjjivPws1gMSp1qaqJbp7O67r7/keviE8d + hnClUdNvqt1Z9n93zMMamP8AhEf7Q8Qaf2/fW/lf9NML8jn6Hn61fsLmx13SEuTbl7ebP7udQc4bHI5H + UZq5PDFcQtDPBHNEwG5HUMp5B7+9YWt+INN8PxtYwW26WOB5xBBEAsSDGWbHQZYdOea0niKFem1RpuNV + zclZ+7GLWyXdO2vZIiGHrUaidWalTUFF3XvOSe7fZq+ndlLw7HZ/25eRz6HEomKNDPBAWg2bAcE4wjcn + PqfwFb1/o+k6g4a8sYZm2ooZhyApyo9eCa5v4UatqmqafdzXjK6ST74TEB5YHHynAGGGeh7Y4HftAW7D + jAzXLjak/btuTb01u30OnCQiqKsrLXSyXXy0MTVbXX7zSb62R4reZ5c2l1FMRiPfkByOQ+FIOBg5Hvij + /wAJJapqHnRrdNBJ/rkkwduBgbBnAyeua0L5NR/4SbTjC0v2IEyzddoZVIVfoxIyPT17Y3hLTxfQajZ6 + raXNnPujeCOSOPftJ52sATgYHPufUV2YBYWKlKurppbeuunfr8jSdadlT6JvX1t/kWr7xHYyae1vFayT + PtCgXKBlbBH3ucnjNc7qF3JfXkl1KFDvjIXOOAB3+lber6QLu487T7aaBT98PCVUYAxtAXP6d6oroOoM + gYx7c9AUbJ/Tj8a+iy6tlmHhzwfK3vfdeX4GE1OWhT1vxlLY+F3t7UzNeCX7O1zMSI1dvmA35zkKyn0A + I5rsPBcs8vhjT/tof7QsKq7NyGOAdwxwQc5GK8x1nwN4ouLqcQxre25meeOF2aNGkKquCCQQMBec/wAG + Bjv6fb6nYadcRaMcx+UioWIVIx8oPXPAr57GUoV23h1dtuTt0X9MMPOo5v2miWiNfK++eec1HbXFtcwp + PbSxzRMeGjcMpxweR171LlscjjnFUdPlijitILbTprVJC21BAEWPHPzAcLk9PXNeRGnKSuv66/ozvvFL + Xf8Ar/gHzx+2hj/hN/C2On/CNJ/6XXlFH7aH/I8eFuMf8U0n/pdeUV91g/8Ad6fovyPg8X/vE/V/mbU+ + ufDjxRaeG7rWfidpmg3GjaDplhbrHpF9cygw20YkWVRCEDLN5uGR3VlK8KVJfU1jX/h9q9vPb6j+0Jaz + xXCTRyqfCF0AyzXIupOkYxmZQ+RyMYGBxRRXcqr08jy3g43fLJq/TT9UM8S6z8N/EeqDU9X+PmmzXnly + RGWPwXcQllkBV92yJdxIY8nJ5qzo3ifwDpN9dXtr+0FZNPdJAsxm8H3UoLQKBBJhoyA8eAVf7wPOeTRR + R7XyD6prfnf4f5HF+Jovh6mi6nY6B8XtB1C41hFhvbjUdK1O38uNZo5vlCW8hd2eNPmJAADDDFgU4D/h + D/Dn/RWPBv8A4C6t/wDINFFRObk7s2o0Y0k0uuof8If4c/6Kx4N/8BdW/wDkGprLwt4ct7gSH4reDXUq + yMPs2rAlWUqcH7DwcE4/rRRSTtqatXVmeg6pqXgy/wDhvB4Uf4k+DI79UjtrjWU0nUvtFzZxENFbv/oe + dqsqHIIyIox/Dk854ctNN8Nef/wjn7Q9ho32nb5/2CDWLfzdudu7Zbjdjc2M9Mn1ooqpT5t0Y06EaSag + 7X1NfXdQ0jWJ9Wubn4yeDY7jUri5l8+PRdU821iuZHknt4XNsWSF2c5XJODIAQJphJFcXWmSS286fG7w + nBcJIZ7qWPRNSzqMxRozJdA2pE5MbNGwcFWDylgWmmaQopcy7GnK+/5f5GVrWmeFr/VINQ/4W34QUWar + HY2Mem6rHb20asWWNP8ARWIUMSxJyzszMzF3Zzz3/CH+HP8AorHg3/wF1b/5BoopN3Go2D/hD/Dn/RWP + Bv8A4C6t/wDINH/CH+HP+iseDf8AwF1b/wCQaKKRR12g/wBhaNZ6qdM+MfhTTr7ViBc3UOmam00aZJZI + pfsYaNWJy2OoCjnGTS8U6ZoHifVv7V1345eHr28MMcJlk0zVCSsaBF/5deuBknuSSeSaKK0dRtWZiqEF + LnW/y/y8kdH4sv8AwnrOn6fFbfGDQ7W6jf7RqM8tjfyreXQj8sTKgsF8rCfJtBK7Qv8AEGduXTRPDKoq + L8bfDIVVjUD+x9S4EZyn/Lp2P596KKzpKFGPLSiorstF9yMaGAoYeHJSikvRf5fd2B9E8MsjI3xt8MlW + WRSP7H1LkSHL/wDLp3P5dqZe6B4Wu43W5+NHhmdWkEzJ/ZWpJuYLtHzC0OOBjofXBoorR1W/6ZsqMU7/ + AOX+Rl3fhbw9c3UtxJ8V/BYeV2dgtpq2ASc8f6FUX/CH+HP+iseDf/AXVv8A5BoorN6mqVtA/wCEP8Of + 9FY8G/8AgLq3/wAg17r8J/Gvw68KeCdO0LU/iZocktoJATbadqTId0rv1a1B6MO1FFc+Jw1PEw5Kmx04 + bE1MPLmhubV/47+C93Otx/wsKwglCsjNBp+oIZFbGVYi2yw46GorD4m/Du21OV2+IWhvaNtjiUWupjyo + 16YQ2pG4g8884HpRRXOstocnI7teps8xr+09orJ+m/qbP/C2fhPnP/CxdNz/ANg3UP8A5Grz7xz4j8Aa + 3r01/p/xU0O2juLcW0u7T9TVvLyCRxanPKg4yOgznpRRRRyyhRlzQWpVXM69WPLJo6LwH48+FegaAljc + fELQ0n813f7Np2pFMFjt5a2yTjGfyHFbsPxY+FKxlZPiNpjsHYq403UR8pxgEfZznHPP0ooqZ5Th5ycm + nqVHNcRGKimtBf8AhbHwn5/4uLpv/gt1D/5HpP8Ahavwk37/APhYembuAW/szUM4H/btRRWf9i4Xs/vL + /tnFd19w7/hbPwnzn/hYum5/7Buof/I1J/wtj4T8/wDFxdN/8Fuof/I9FFH9i4Xs/vD+2cV3X3Ed18Uv + hPcW7Q/8LLsog2Pnj0/UFYdOh+ze1ZEvjL4NTSGSb4nxSOcZZrG/JP4/ZaKK6qGChh1ak2vmTLNsRLe3 + 3GlYfEz4T2m//i6Fvcb8f6+x1BsY9P8ARuKli+KXwmS4ll/4WVZt5m35W0/UCqYGPlH2bjPf1oorKeV0 + Jtyle73/AK+Q1nGJXVfceEftTeK/Dfi3xjoVz4Y1mPVrWz0NLWaeOCaJRL9quZCoEqIx+WROcY560UUV + 3U4KnBQjstDzqk3Uk5Pd6n//2Q== + + + + False + + + Private + + + Private + + + False + + + Private + + + Private + + + False + + + Private + + + Private + + + False + + + Private + + + Private + + + Private + + + Private + + + False + + + False + + + Private + + + Private + + + False + + + Private + + + Private + + + False + + + Private + + + Private + + + False + + + (Default) + + + False + + + False + + + 8, 8 + + + True + + + 80 + + + True + + + AboutForm + + + Private + + \ No newline at end of file diff --git a/m/Area.cs b/m/Area.cs new file mode 100644 index 0000000..e1d5b0f --- /dev/null +++ b/m/Area.cs @@ -0,0 +1,286 @@ +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Runtime.Serialization; +using System.Text.RegularExpressions; +using SpiffLib; + +namespace m +{ + [Serializable] + public class Area : MapItem, ISerializable, IComparable { + int m_nBonusSortKey = -1; + int m_ctx; + int m_cty; + int m_nHandleDragging; + string m_strName; + int m_xDeltaMouse; + int m_yDeltaMouse; + static int s_nAlpha = 96; + static SolidBrush s_brArea = new SolidBrush(Color.FromArgb(s_nAlpha, 32, 150, 204)); + static SolidBrush s_brAreaSelected = new SolidBrush(Color.FromArgb(s_nAlpha, 152, 212, 240)); + static SolidBrush s_brWhite = new SolidBrush(Color.FromArgb(255, 255, 255)); + static Pen s_penWhite = new Pen(Color.FromArgb(255, 255, 255)); + static Pen s_penBlack = new Pen(Color.FromArgb(0, 0, 0)); + static int s_cpSizeMargin = 5; + + public Area(int ctx, int cty) + { + m_strName = "New Area"; + m_nHandleDragging = -1; + m_ctx = ctx; + m_cty = cty; + } + + // ISerializable implementation + + public Area(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { + m_ctx = info.GetInt32("Ctx"); + m_cty = info.GetInt32("Cty"); + m_strName = info.GetString("Name"); + m_nHandleDragging = -1; + } + + public Area(string strName, string strValue, int txOrigin, int tyOrigin) { + Regex re = new Regex(@"^(?\d+),(?\d+),(?\d+),(?\d+)$"); + Match m = re.Match(strValue); + m_tx = int.Parse(m.Groups["tx"].Value) + txOrigin; + m_ty = int.Parse(m.Groups["ty"].Value) + tyOrigin; + m_ctx = int.Parse(m.Groups["ctx"].Value); + m_cty = int.Parse(m.Groups["cty"].Value); + m_strName = strName; + m_nHandleDragging = -1; + } + + public override Ini.Property GetIniProperty(int txOrigin, int tyOrigin) { + return new Ini.Property(m_strName, (m_tx - txOrigin) + "," + (m_ty - tyOrigin) + "," + m_ctx + "," + m_cty); + } + + public override void GetObjectData(SerializationInfo info, StreamingContext context) { + base.GetObjectData(info, context); + info.AddValue("Ctx", m_ctx); + info.AddValue("Cty", m_cty); + info.AddValue("Name", m_strName); + } + + // IComparable implementation + + public int CompareTo(object ob) { + Area ar = (Area)ob; + int n = String.Compare(m_strName, ar.m_strName); + if (n != 0) { + return n; + } + if (m_nBonusSortKey < ar.m_nBonusSortKey) { + return -1; + } + if (m_nBonusSortKey > ar.m_nBonusSortKey) { + return 1; + } + return 0; + } + + public int BonusSortKey { + get { + return m_nBonusSortKey; + } + set { + m_nBonusSortKey = value; + } + } + + void DrawHandle(Graphics g, Rectangle rc) { + g.FillRectangle(s_brWhite, rc); + } + + Rectangle GetHandleRect(int n, int x, int y, Size sizT) { + // 0 is top left, go clockwise + + int yT = y; + if (n == 2 || n == 3) + yT = y + sizT.Height - s_cpSizeMargin; + int xT = x; + if (n == 1 || n == 2) + xT = x + sizT.Width - s_cpSizeMargin; + return new Rectangle(xT, yT, s_cpSizeMargin, s_cpSizeMargin); + } + + void DrawArea(Graphics g, int x, int y, Size sizTile, bool fSelected) { + Size sizT = new Size(sizTile.Width * m_ctx, sizTile.Height * m_cty); + g.FillRectangle(fSelected ? s_brAreaSelected : s_brArea, x, y, sizT.Width, sizT.Height); + for (int n = 0; n < 4; n++) { + Rectangle rc = GetHandleRect(n, x, y, sizT); + DrawHandle(g, rc); + } + RectangleF rcfClipSav = g.ClipBounds; + g.SetClip(new Rectangle(x, y, sizT.Width, sizT.Height)); + g.DrawString(m_strName, new Font("Arial", 8), s_brWhite, x + s_cpSizeMargin, y + 1); + g.SetClip(rcfClipSav); + Pen pen = fSelected ? s_penWhite : s_penBlack; + g.DrawLine(pen, x, y, x + sizT.Width - 1, y); + g.DrawLine(pen, x + sizT.Width - 1, y, x + sizT.Width - 1, y + sizT.Height - 1); + g.DrawLine(pen, x + sizT.Width - 1, y + sizT.Height - 1, x, y + sizT.Height - 1); + g.DrawLine(pen, x, y + sizT.Height - 1, x, y); + } + + // IMapItem + + public override int ctx { + get { + return m_ctx; + } + } + + public override int cty { + get { + return m_cty; + } + } + + public override bool OnMouseDown(System.Windows.Forms.MouseEventArgs e, Point ptMouse, Size sizTile, TemplateDoc tmpd) { + int nHandle = HittestHandles(ptMouse, sizTile); + if (nHandle != -1) { + m_nHandleDragging = nHandle; + Point ptCorner = GetCornerPoint(m_nHandleDragging, sizTile); + m_xDeltaMouse = ptMouse.X - ptCorner.X; + m_yDeltaMouse = ptMouse.Y - ptCorner.Y; + return true; + } + return false; + } + + public override bool OnMouseMove(System.Windows.Forms.MouseEventArgs e, Point ptMouse, Size sizTile, TemplateDoc tmpd) { + if (m_nHandleDragging == -1) + return false; + Point ptCorner = GetCornerPoint(m_nHandleDragging, sizTile); + int txOld = ptCorner.X / sizTile.Width; + int tyOld = ptCorner.Y / sizTile.Height; + int txNew = (ptMouse.X - m_xDeltaMouse) / sizTile.Width; + int tyNew = (ptMouse.Y - m_yDeltaMouse) / sizTile.Height; + if (txOld == txNew && tyOld == tyNew) + return true; + + switch (m_nHandleDragging) { + case 0: + m_tx = txNew; + m_ty = tyNew; + m_ctx += txOld - txNew; + m_cty += tyOld - tyNew; + break; + + case 1: + m_ty = tyNew; + m_cty += tyOld - tyNew; + m_ctx += txNew - txOld; + break; + + case 2: + m_ctx += txNew - txOld; + m_cty += tyNew - tyOld; + break; + + case 3: + m_tx = txNew; + m_ctx += txOld - txNew; + m_cty += tyNew - tyOld; + break; + } + + m_ctx = Math.Max(1, m_ctx); + m_cty = Math.Max(1, m_cty); + + OnPropertyChanged(this, "Bitmap"); + return true; + } + + Point GetCornerPoint(int nHandleDragging, Size sizTile) { + int x = (int)m_tx * sizTile.Width; + int y = (int)m_ty * sizTile.Height; + int cx = m_ctx * sizTile.Width; + int cy = m_cty * sizTile.Height; + + switch (nHandleDragging) { + case 0: + return new Point(x, y); + + case 1: + return new Point(x + cx, y); + + case 2: + return new Point(x + cx, y + cy); + + case 3: + return new Point(x, y + cy); + } + + return new Point(-1, -1); + } + + public override bool OnMouseUp(System.Windows.Forms.MouseEventArgs e, Point ptMouse, Size sizTile, TemplateDoc tmpd) { + if (m_nHandleDragging != -1) { + m_nHandleDragging = -1; + return true; + } + return false; + } + + int HittestHandles(Point ptMouse, Size sizTile) { + int x = (int)m_tx * sizTile.Width; + int y = (int)m_ty * sizTile.Height; + Size sizT = new Size(sizTile.Width * m_ctx, sizTile.Height * m_cty); + for (int n = 0; n < 4; n++) { + Rectangle rc = GetHandleRect(n, x, y, sizT); + if (rc.Contains(ptMouse)) + return n; + } + return -1; + } + + public override Bitmap GetBitmap(Size sizTile, TemplateDoc tmpd) { + Bitmap bm = new Bitmap(sizTile.Width * m_ctx, sizTile.Height * m_cty); + Graphics g = Graphics.FromImage(bm); + DrawArea(g, 0, 0, sizTile, false); + g.Dispose(); + return bm; + } + + public override Point GetCenterPoint(Size sizTile) { + Size sizT = new Size(sizTile.Width * m_ctx, sizTile.Height * m_cty); + return new Point((int)m_tx * sizTile.Width + sizT.Width / 2, (int)m_ty * sizTile.Height + sizT.Height / 2); + } + + public override Rectangle GetBoundingRectAt(int x, int y, Size sizTile, TemplateDoc tmpd) { + Size sizT = new Size(sizTile.Width * m_ctx, sizTile.Height * m_cty); + return new Rectangle(x, y, sizT.Width, sizT.Height); + } + + public override bool HitTest(int x, int y, Size sizTile, TemplateDoc tmpd) { + int xT = x - (int)m_tx * sizTile.Width; + int yT = y - (int)m_ty * sizTile.Height; + Size sizT = new Size(sizTile.Width * m_ctx, sizTile.Height * m_cty); + if (xT >= 0 && xT < sizT.Width && yT >= 0 && yT < sizT.Height) + return true; + return false; + } + + public override Object Clone() { + return MemberwiseClone(); + } + + public override void Draw(Graphics g, int x, int y, Size sizTile, TemplateDoc tmpd, LayerType layer, bool fSelected) { + if (layer == LayerType.Area) + DrawArea(g, x, y, sizTile, fSelected); + } + + public string Name { + get { + return m_strName; + } + set { + m_strName = value; + OnPropertyChanged(this, "Bitmap"); + } + } + } +} diff --git a/m/AssemblyInfo.cs b/m/AssemblyInfo.cs new file mode 100644 index 0000000..9f89a32 --- /dev/null +++ b/m/AssemblyInfo.cs @@ -0,0 +1,58 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.*")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] diff --git a/m/CaBase.cs b/m/CaBase.cs new file mode 100644 index 0000000..65078a2 --- /dev/null +++ b/m/CaBase.cs @@ -0,0 +1,1461 @@ +using System; +using SpiffLib; +using System.Runtime.Serialization; +using System.Text.RegularExpressions; + +namespace m +{ + public class UnitActionLoader { + public static CaBase LoadIni(string strValue) { + CaBase cab = null; + switch (int.Parse(strValue.Split(',')[0])) { + case 0: // knGuardUnitAction + cab = new GuardUnitAction(); + break; + + case 1: // knGuardVicinityUnitAction + cab = new GuardVicinityUnitAction(); + break; + + case 2: // knGuardAreaUnitAction + cab = new GuardAreaUnitAction(); + break; + + case 3: // knMoveUnitAction + cab = new MoveUnitAction(); + break; + + case 4: // knHuntEnemiesUnitAction + cab = new HuntEnemiesUnitAction(); + break; + + case 5: // knMineUnitAction + cab = new MineUnitAction(); + break; + + default: + return null; + } + if (cab != null) { + cab.FromSaveString(strValue); + } + return cab; + } + } + + public class UnitGroupActionLoader { + public static CaBase LoadIni(string strValue) { + CaBase cab = null; + switch (int.Parse(strValue.Split(',')[0])) { + case 0: // knWaitUnitGroupAction + cab = new WaitUnitGroupAction(); + break; + + case 1: // knSetSwitchUnitGroupAction + cab = new SetSwitchUnitGroupAction(); + break; + + case 2: // knMoveUnitGroupAction + cab = new MoveUnitGroupAction(); + break; + + case 3: // knAttackUnitGroupAction + cab = new AttackUnitGroupAction(); + break; + + case 4: // knGuardUnitGroupAction + cab = new GuardUnitGroupAction(); + break; + + case 5: // knMineUnitGroupAction + cab = new MineUnitGroupAction(); + break; + + case 6: // knGuardVicinityUnitGroupAction + cab = new GuardVicinityUnitGroupAction(); + break; + } + if (cab != null) { + cab.FromSaveString(strValue); + } + return cab; + } + } + + public class TriggerConditionLoader { + public static CaBase LoadIni(string strValue) { + CaBase cab = null; + switch (int.Parse(strValue.Split(',')[0])) { + case 0: // knMissionLoadedCondition + cab = new MissionLoadedCondition(); + break; + + case 1: // knCreditsCondition + cab = new CreditsCondition(); + break; + + case 2: // knAreaContainsUnitsCondition + cab = new AreaContainsUnitsCondition(); + break; + + case 3: // knGalaxiteCapacityReachedCondition + cab = new GalaxiteCapacityReachedCondition(); + break; + + case 4: // knMobileHQDeployedCondition +#if false + cab = new MobileHQDeployedCondition(); +#endif + break; + + case 5: // knPlaceStructureModeCondition + cab = new PlaceStructureModeCondition(); + break; + + case 6: // knElapsedTimeCondition + cab = new ElapsedTimeCondition(); + break; + + case 7: // knOwnsUnitsCondition + cab = new OwnsUnitsCondition(); + break; + + case 8: // knMinerCantFindGalaxiteCondition + cab = new MinerCantFindGalaxiteCondition(); + break; + + case 9: // knUnitSeesUnitCondition +#if false + cab = new UnitSeesUnitCondition(); +#endif + break; + + case 10: // knUnitDestroyedCondition +#if false + cab = new UnitDestroyedCondition(); +#endif + break; + + case 11: // knSwitchCondition + cab = new SwitchCondition(); + break; + + case 12: // knPeriodicTimerCondition + cab = new PeriodicTimerCondition(); + break; + + case 13: // knDiscoversSideCondition + cab = new DiscoversSideCondition(); + break; + + case 14: // knCountdownTimerCondition + cab = new CountdownTimerCondition(); + break; + + case 15: // knCounterCondition +#if false + cab = new CounterCondition(); +#endif + break; + + case 16: // knTestPvarCondition + cab = new TestPvarCondition(); + break; + + case 17: // knHasUpgradesCondition + cab = new HasUpgradesCondition(); + break; + } + if (cab != null) { + cab.FromSaveString(strValue); + } + return cab; + } + } + + public class TriggerActionLoader { + public static CaBase LoadIni(string strValue) { + CaBase cab = null; + switch (int.Parse(strValue.Split(',')[0])) { + + case 0: // knSetResourcesTriggerAction +#if false + cab = new SetResourcesTriggerAction(); +#endif + break; + + case 1: // knSetAllowedUnitsTriggerAction + cab = new SetAllowedUnitsTriggerAction(); + break; + + case 2 : // knEcomTriggerAction + cab = new EcomTriggerAction(); + break; + + case 3: // knSetObjectiveTriggerAction + cab = new SetObjectiveTriggerAction(); + break; + + case 4: // knSetNextMissionTriggerAction + cab = new SetNextMissionTriggerAction(); + break; + + case 5: // knEndMissionTriggerAction + cab = new EndMissionTriggerAction(); + break; + + case 6: // knWaitTriggerAction + cab = new WaitTriggerAction(); + break; + + case 7 : // knSetSwitchTriggerAction + cab = new SetSwitchTriggerAction(); + break; + + case 8: // knSetPlayerControlsTriggerAction +#if false + cab = new SetPlayerControlsTriggerAction(); +#endif + break; + + case 9: // knPreserveTriggerTriggerAction + cab = new PreserveTriggerTriggerAction(); + break; + + case 10: // knCenterViewTriggerAction + cab = new CenterViewTriggerAction(); + break; + + case 11: // knPanViewTriggerAction +#if false + cab = new PanViewTriggerAction(); +#endif + break; + + case 12: // knDefogAreaTriggerAction + cab = new DefogAreaTriggerAction(); + break; + + case 13: // knMoveUnitTriggerAction +#if false + cab = new MoveUnitTriggerAction(); +#endif + break; + + case 14: // knTargetUnitTriggerAction +#if false + cab = new TargetUnitTriggerAction(); +#endif + break; + + // knCreateUnitGroupTriggerAction + // knCreateUnitAtAreaTriggerAction + case 15: + // This can be either action. If the referenced trigger has a + // name prefix of __cuaa, then it is CreateUnitAtAreaTriggerAction. + cab = new CreateUnitGroupTriggerAction(); + cab.FromSaveString(strValue); + if (((CaTypeUnitGroup)cab.GetTypes()[0]).ToString().StartsWith("__cuaa")) { + cab = new CreateUnitAtAreaTriggerAction(); + } else { + cab = new CreateUnitGroupTriggerAction(); + } + break; + + case 16: // knHuntTriggerAction + cab = new HuntTriggerAction(); + break; + + case 17: // knCreateRandomUnitGroupTriggerAction + cab = new CreateRandomUnitGroupTriggerAction(); + break; + + case 18: // knAlliesTriggerAction + cab = new AlliesTriggerAction(); + break; + + case 19: // knStartCountdownTimerTriggerAction + cab = new StartCountdownTimerTriggerAction(); + break; + + case 20: // knModifyCountdownTimerTriggerAction + cab = new ModifyCountdownTimerTriggerAction(); + break; + + case 21: // knRepairTriggerAction + cab = new RepairTriggerAction(); + break; + + case 22: // knEnableReplicatorTriggerAction + cab = new EnableReplicatorTriggerAction(); + break; + + case 23: // knModifyCreditsTriggerAction + cab = new ModifyCreditsTriggerAction(); + break; + + case 24: // knModifyCounterTriggerAction + cab = new ModifyCounterTriggerAction(); + break; + + case 25: // knMoveUnitsInAreaTriggerAction + cab = new MoveUnitsInAreaTriggerAction(); + break; + + case 26: // knSetFormalObjectiveTextTriggerAction + cab = new SetFormalObjectiveTextTriggerAction(); + break; + + case 27: // knSetFormalObjectiveStatusTriggerAction + cab = new SetFormalObjectiveStatusTriggerAction(); + break; + + case 28: // knShowObjectivesTriggerAction + cab = new ShowObjectivesTriggerAction(); + break; + + case 29: // knSetFormalObjectiveInfoTriggerAction + cab = new SetFormalObjectiveInfoTriggerAction(); + break; + + case 30: // knCutSceneTriggerAction + cab = new CutSceneTriggerAction(); + break; + + case 31: // knJumpToMissionTriggerAction + cab = new JumpToMissionTriggerAction(); + break; + + case 32: // knModifyPvarTriggerAction + cab = new ModifyPvarTriggerAction(); + break; + + case 33: // knSetPvarTextTriggerAction + cab = new SetPvarTextTriggerAction(); + break; + + case 34: // knShowAlertTriggerAction + cab = new ShowAlertTriggerAction(); + break; + + case 35: // knSetAllowedUpgradesTriggerAction + cab = new SetAllowedUpgradesTriggerAction(); + break; + + case 36: // knSetUpgradesTriggerAction + cab = new SetUpgradesTriggerAction(); + break; + } + if (cab != null) { + cab.FromSaveString(strValue); + } + return cab; + } + } + + [Serializable] + abstract public class CaBase { + // NOTE: This is legacy. We don't want it anymore but it has been serialized + // into some levels and the effort required to up-convert them is greater + // than the value of doing so. + protected string m_str; + + protected CaType[] m_acat; + protected bool m_fActive; + + public CaBase() { + m_str = null; + m_acat = null; + m_fActive = true; + } + + abstract public string GetString(); + + public CaType[] GetTypes() { + return m_acat; + } + + public virtual CaBase Clone() { + CaBase cab = (CaBase)MemberwiseClone(); + cab.m_acat = new CaType[m_acat.Length]; + for (int n = 0; n < m_acat.Length; n++) + cab.m_acat[n] = m_acat[n].Clone(); + return cab; + } + + public override string ToString() { + string str = GetString(); + for (int j = 0; j < m_acat.Length; j++) + str = str.Replace("$" + (j + 1), m_acat[j].ToString()); + return str; + } + + public virtual string ToSaveString() { + string str = ""; + for (int n = 0; n < m_acat.Length; n++) { + str += m_acat[n].ToSaveString(); + if (n < m_acat.Length - 1) + str += ","; + } + string strType = GetType().ToString(); + int ichDot = strType.IndexOf("."); + strType = strType.Substring(ichDot + 1, strType.Length - ichDot - 1); + return "kn" + strType + "," + str; + } + + public virtual string FromSaveString(string strArgs) { + Regex re = new Regex(@"^\d+,(?.*)$"); + Match m = re.Match(strArgs); + string strT = m.Groups["end"].Value; + for (int i = 0; i < m_acat.Length; i++) { + CaType cat = m_acat[i]; + strT = cat.FromSaveString(strT, i == (m_acat.Length - 1)); + if (strT.Trim().Length != 0) { + re = new Regex(@"^\s*,(?.*)$"); + m = re.Match(strT); + strT = m.Groups["end"].Value; + } + } + return strT; + } + + public bool IsValid() { + if (m_acat == null) + return false; + + foreach (CaType cat in m_acat) { + if (!cat.IsInitialized()) + return false; + } + + return true; + } + + public bool Active { + get { + return m_fActive; + } + set { + m_fActive = value; + } + } + } + + // + // Conditions + // + + [Serializable] + [DisplayName("Mission Loaded")] + [Description("This condition is set immediately after the mission is loaded.")] + public class MissionLoadedCondition : CaBase { + override public string GetString() { + return "Mission loaded"; + } + public MissionLoadedCondition() { + m_acat = new CaType[0]; + } + } + + [Serializable] + [DisplayName("Credits")] + [Description("Use this condition to respond to Credits going above or below a value of interest. " + + "WARNING: it is not a good idea to use the \"Exactly\" comparison against any value other than 0 " + + "because Credits typically change at increments greater than one.")] + public class CreditsCondition : CaBase { + override public string GetString() { + return "$1 has $2 Credits"; + } + public CreditsCondition() { + m_acat = new CaType[] { new CaTypeSide(), new CaTypeQualifiedNumber() }; + } + } + + [Serializable] + [DisplayName("Area Contains Units")] + [Description("All units of all sides and all types specified that are " + + "inside the area are totaled and that total is what is compared against the value.")] + public class AreaContainsUnitsCondition : CaBase { + override public string GetString() { + return "Area '$1' contains $2 $3 owned by $4"; + } + public AreaContainsUnitsCondition() { + m_acat = new CaType[] { new CaTypeArea(), new CaTypeQualifiedNumber(), new CaTypeUnitTypes(), new CaTypeSide() }; + } + } + + [Serializable] + [DisplayName("Place Structure Mode Active")] + [Description("This condition is met when, after ordering a new structure, " + + "the game enters the \"place structure\" mode. It is intended to " + + "be used only by the tutorial.")] + public class PlaceStructureModeCondition : CaBase { + override public string GetString() { + return "Place structure mode is active"; + } + public PlaceStructureModeCondition() { + m_acat = new CaType[0]; + } + } + +#if false + [Serializable] + public class DeathsCondition : CaBase { + override public string GetString() { + return "$1 has suffered $2 deaths of $3"; + } + public DeathsCondition() { + m_acat = new CaType[] { new CaTypeSide(), new CaTypeQualifiedNumber(), new CaTypeUnitTypes() }; + } + } + + [Serializable] + [DisplayName("Mobile Headquarters deployable")] + public class MobileHQDeployableCondition : CaBase { + override public string GetString() { + return "MobileHQ is deployable"; + } + public MobileHQDeployableCondition() { + m_acat = new CaType[0]; + } + } + + [Serializable] + [DisplayName("Mobile Headquarters deployed")] + public class MobileHQDeployedCondition : CaBase { + override public string GetString() { + return "MobileHQ is deployed"; + } + public MobileHQDeployedCondition() { + m_acat = new CaType[0]; + } + } +#endif + + [Serializable] + [DisplayName("Countdown Timer")] + [Description("The countdown timer is compared against the \"qualified quantity\" " + + "to determine if condition is satisfied.")] + public class CountdownTimerCondition : CaBase { + override public string GetString() { + return "Countdown timer is $1"; + } + public CountdownTimerCondition() { + m_acat = new CaType[] { new CaTypeQualifiedNumber() }; + } + } + + [Serializable] + [DisplayName("Owns Units")] + [Description("All units of all sides and types specified are totaled " + + "and that total is what is compared against the \"quantity\" value.")] + public class OwnsUnitsCondition : CaBase { + override public string GetString() { + return "$1 owns $2 $3"; + } + public OwnsUnitsCondition() { + m_acat = new CaType[] { new CaTypeSide(), new CaTypeQualifiedNumber(), new CaTypeUnitTypes() }; + } + } + + [Serializable] + [DisplayName("GalaxMiner Can't Find Galaxite")] + [Description("This condition is met when the GalaxMiner searches the " + + "unfogged area of the map and can't see any Galaxite. It is " + + "intended to be used only by the tutorial.")] + public class MinerCantFindGalaxiteCondition : CaBase { + override public string GetString() { + return "GalaxMiner owned by $1 can't find galaxite"; + } + public MinerCantFindGalaxiteCondition() { + m_acat = new CaType[] { new CaTypeSide() }; + } + } + + [Serializable] + [DisplayName("Galaxite Capacity Reached")] + [Description("This condition is met when all the side's Processors and Warehouses are " + + "full. It is intended to be used only by the tutorial.")] + public class GalaxiteCapacityReachedCondition : CaBase { + override public string GetString() { + return "$1's Galaxite capacity is reached"; + } + public GalaxiteCapacityReachedCondition() { + m_acat = new CaType[] { new CaTypeSide() }; + } + } + + [Serializable] + [DisplayName("Elapsed Time")] + [Description("Time starts at 0 when the mission begins. If you want to set up " + + "an action that recurs every NN seconds do it with an \"Always\" condition " + + "and a \"Wait NN\" action.")] + public class ElapsedTimeCondition : CaBase { + override public string GetString() { + return "Elapsed mission time is $1 seconds"; + } + public ElapsedTimeCondition() { + m_acat = new CaType[] { new CaTypeQualifiedNumber() }; + } + } + + [Serializable] + [DisplayName("Has Upgrades")] + [Description("Use this to determine if the specified side possesses the specified upgrades.")] + public class HasUpgradesCondition : CaBase { + override public string GetString() { + return "$1 has $2 upgrade(s)"; + } + public HasUpgradesCondition() { + m_acat = new CaType[] { new CaTypeSide(), new CaTypeUpgradeTypes() }; + } + } + +#if false + [Serializable] + public class UnitSeesUnitCondition : CaBase { + override public string GetString() { + return "$1 owned by $2 sees $3 owned by $4"; + } + public UnitSeesUnitCondition() { + m_acat = new CaType[] { new CaTypeUnit(), new CaTypeSide(), new CaTypeUnit(), new CaTypeSide() }; + } + } + + [Serializable] + public class UnitDestroyedCondition : CaBase { + override public string GetString() { + return "$1 owned by $2 destroyed"; + } + public UnitDestroyedCondition() { + m_acat = new CaType[] { new CaTypeUnit(), new CaTypeSide() }; + } + } +#endif + + [Serializable] + [DisplayName("Switch On/Off")] + [Description("Switch names can be anything (including spaces, etc) but ARE case sensitive. That is, setting " + + "a switch named \"Whatever\" and testing \"whatever\" won't work.")] + public class SwitchCondition : CaBase { + override public string GetString() { + return "Switch '$1' is $2"; + } + public SwitchCondition() { + m_acat = new CaType[] { new CaTypeSwitch(), new CaTypeOnOff() }; + } + } + + [Serializable] + [DisplayName("Every NN seconds")] + [Description("This condition is satisfied repeatedly at the specified rate. " + + "NOTE: the trigger will only fire once unless the " + + "\"Preserve Trigger\" action is used.")] + public class PeriodicTimerCondition : CaBase { + override public string GetString() { + return "Every $1 seconds"; + } + public PeriodicTimerCondition() { + m_acat = new CaType[] { new CaTypeNumber() }; + } + } + + [Serializable] + [DisplayName("Comment")] + [Description("Use this to comment your conditions. It has no in-game effect.")] + public class CommentCondition : CaBase { + override public string GetString() { + return "Comment: $1"; + } + public CommentCondition() { + m_acat = new CaType[] { new CaTypeRichText() }; + } + } + + [Serializable] + [DisplayName("Discovers Side")] + [Description("This condition is met the first time side A 'discovers' side B. " + + "Discovery means that a unit owned by side B has become visible to " + + "a unit owned by side A (and implicitly, vice-versa). The virtual " + + "area [LastDiscovery] is initialized at discovery time and is " + + "positioned over the unit found.")] + public class DiscoversSideCondition : CaBase { + override public string GetString() { + return "$1 discovers $2"; + } + public DiscoversSideCondition() { + m_acat = new CaType[] { new CaTypeSide(), new CaTypeSide() }; + } + } + +#if false + [Serializable] + [DisplayName("Counter")] + [Description("Use this condition to respond to Counter going above or below a value of interest.")] + public class CounterCondition : CaBase { + override public string GetString() { + return "Counter '$1' is $2"; + } + public CounterCondition() { + m_acat = new CaType[] { new CaTypeCounter(), new CaTypeQualifiedNumber() }; + } + } +#endif + + [Serializable] + [DisplayName("Test Persistent Variable")] + [Description("Use this condition to test whether a persistent variable is above, below, or equal to a value interest.")] + public class TestPvarCondition : CaBase { + override public string GetString() { + return "Persistent variable '$1' is $2"; + } + public TestPvarCondition() { + m_acat = new CaType[] { new CaTypeText(), new CaTypeQualifiedNumber() }; + } + public string GetVariableString() { + CaTypeText cat = (CaTypeText)m_acat[0]; + return cat.Text; + } + } + + // + // Trigger Actions + // + + [Serializable] + [DisplayName("Center View")] + [Description("Centers the player's view over the specified Area. This view change " + + "won't be apparent until the next display refresh. A display refresh can " + + "be forced by adding a subsequent \"Wait 0\" action.")] + public class CenterViewTriggerAction : CaBase { + override public string GetString() { + return "Center view over '$1'"; + } + public CenterViewTriggerAction() { + m_acat = new CaType[] { new CaTypeArea() }; + } + } + + [Serializable] + [DisplayName("Set Next Mission")] + [Description("Sets the mission that will be launched following an \"End Mission\" action. " + + "In theory this could be used to set up a branching mission structure or provide " + + "access to secret missions. The mission naming convention is \"mission.lvl\".")] + public class SetNextMissionTriggerAction : CaBase { + override public string GetString() { + return "Set next mission to \"$1\""; + } + public SetNextMissionTriggerAction() { + m_acat = new CaType[] { new CaTypeText() }; + } + } + + [Serializable] + [DisplayName("End Mission")] + [Description("Ends the mission immediately. If the win/lose parameter is set to \"Win\" " + + "and a next mission has been specified via \"Set Next Mission\" that mission " + + "will be launched. Otherwise gameplay ends.")] + public class EndMissionTriggerAction : CaBase { + override public string GetString() { + return "End Mission: $1"; + } + public EndMissionTriggerAction() { + m_acat = new CaType[] { new CaTypeWinLose() }; + } + } + + [Serializable] + [DisplayName("Set Allowed Units")] + [Description("Use this to specify which Units the player is allowed to build. " + + "Only these units will show in the build panels.")] + public class SetAllowedUnitsTriggerAction : CaBase { + override public string GetString() { + return "Set $1 allowed units to $2"; + } + public SetAllowedUnitsTriggerAction() { + m_acat = new CaType[] { new CaTypeSide(), new CaTypeUnitTypes() }; + } + } + + [Serializable] + [DisplayName("Allies")] + [Description("Use this to specify the sides a side considers as its allies. " + + "A side's units do not (can not) target or attack allies' units. " + + "Players have no control over their allies' units, nor do they share 'sight' " + + "with them. NOTE: just because side A considers side B its ally doesn't mean side B " + + "reciprocates. Use an Allies action for each side if that's what you want. " + + "Also, do not use the virtual sides \"Enemies\" or \"Allies\" for either side.")] + public class AlliesTriggerAction : CaBase { + override public string GetString() { + return "$1 allies with $2"; + } + public AlliesTriggerAction() { + m_acat = new CaType[] { new CaTypeSide(), new CaTypeSide() }; + } + } + + [Serializable] + [DisplayName("Ecom")] + [Description("...")] + public class EcomTriggerAction : CaBase { + override public string GetString() { + return "Ecom ($1 w/ $2) from $3 to $4: \"$5\""; + } + public EcomTriggerAction() { + m_acat = new CaType[] { new CaTypeSmallLarge(), new CaTypeMoreClose(), new CaTypeCharacter(), new CaTypeCharacter(), new CaTypeRichText() }; + } + + // override ToString base to horn our new CaType into older files which don't have it yet + public override string ToString() { + if (m_acat.Length < 4) { + // old file type + + CaType[] acat = new CaType[5]; + acat[0] = new CaTypeSmallLarge(); + acat[1] = new CaTypeMoreClose(); + for (int n = 0; n < m_acat.Length; n++) + acat[n+2] = m_acat[n].Clone(); + m_acat = acat; + } + + string str = GetString(); + for (int j = 0; j < m_acat.Length; j++) + str = str.Replace("$" + (j + 1), m_acat[j].ToString()); + return str; + } + + } + + [Serializable] + [DisplayName("Set Objective")] + [Description("Use this to specify a string to be displayed continuously at the upper-left " + + "of the screen.")] + public class SetObjectiveTriggerAction : CaBase { + override public string GetString() { + return "Set $1's mission objective to \"$2\""; + } + public SetObjectiveTriggerAction() { + m_acat = new CaType[] { new CaTypeSide(), new CaTypeText() }; + } + } + + [Serializable] + [DisplayName("Wait")] + [Description("Delay a number of seconds before advancing to the next action.")] + public class WaitTriggerAction : CaBase { + override public string GetString() { + return "Wait $1 seconds"; + } + public WaitTriggerAction() { + m_acat = new CaType[] { new CaTypeNumber() }; + } + } + + [Serializable] + [DisplayName("Set Switch")] + [Description("Switch names can be anything (including spaces, etc) but ARE case sensitive. That is, setting " + + "a switch named \"Whatever\" and testing \"whatever\" won't work.")] + public class SetSwitchTriggerAction : CaBase { + override public string GetString() { + return "Set switch '$1' $2"; + } + public SetSwitchTriggerAction() { + m_acat = new CaType[] { new CaTypeSwitch(), new CaTypeOnOff() }; + } + } + + [Serializable] + [DisplayName("Start Countdown Timer")] + [Description("Use this to initialize the value of a countdown timer and start a countdown." + + "Put a %s in the string where you want the time displayed. You must use a \"ModifyCountdownTrigger\" to Show the timer. " + + "The timer will stop at 00:00 but you must call \"ModifyCountdownTrigger\" with \"hide\" to make it disappear.")] + public class StartCountdownTimerTriggerAction : CaBase { + override public string GetString() { + return "Start Countdown Timer with $1 seconds and show this string: \"$2\""; + } + public StartCountdownTimerTriggerAction() { + m_acat = new CaType[] { new CaTypeNumber(), new CaTypeText() }; + } + } + + [Serializable] + [DisplayName("Modify Countdown Timer")] + [Description("Use this to stop (or pause), resume, show, or hide the countdown. To resume a countdown " + + "you must create another modify action with \"resume\". Showing/hiding will not affect the countdown. " + + "Stopping will not automatically hide the timer - you must create another action to hide it")] + public class ModifyCountdownTimerTriggerAction : CaBase { + override public string GetString() { + return "$1 the Countdown Timer"; + } + public ModifyCountdownTimerTriggerAction() { + m_acat = new CaType[] { new CaTypeModifyCountdown() }; + } + } + +#if false + [Serializable] + public class SetPlayerControlsTriggerAction : CaBase { + override public string GetString() { + return "Set player controls $1"; + } + public SetPlayerControlsTriggerAction() { + m_acat = new CaType[] { new CaTypeOnOff() }; + } + } +#endif + + [Serializable] + [DisplayName("Preserve Trigger")] + [Description("Use this action if you want a trigger to execute again the " + + "next time its conditions are met. The default behavior is that " + + "triggers only execute the first time their conditions are met.")] + public class PreserveTriggerTriggerAction : CaBase { + override public string GetString() { + return "Preserve trigger"; + } + public PreserveTriggerTriggerAction() { + m_acat = new CaType[] { }; + } + } + +#if false + [Serializable] + public class PanViewTriggerAction : CaBase { + override public string GetString() { + return "Pan view to $1"; + } + public PanViewTriggerAction() { + m_acat = new CaType[] { new CaTypeArea() }; + } + } +#endif + + [Serializable] + [DisplayName("Defog Area")] + [Description("Clear the fog in the area specified. Yes, because Areas are " + + "rectangular the result can look strange. Note, this only affects the " + + "local/human player.")] + public class DefogAreaTriggerAction : CaBase { + override public string GetString() { + return "Defog area over '$1'"; + } + public DefogAreaTriggerAction() { + m_acat = new CaType[] { new CaTypeArea() }; + } + } + +#if false + [Serializable] + public class MoveUnitTriggerAction : CaBase { + override public string GetString() { + return "Move $1 $2 at $3 to $4"; + } + public MoveUnitTriggerAction() { + m_acat = new CaType[] { new CaTypeSide(), new CaTypeUnit(), new CaTypeArea(), new CaTypeArea() }; + } + } + + [Serializable] + public class TargetUnitTriggerAction : CaBase { + override public string GetString() { + return "Set target for $1 $2 at $3 to $4 $5"; + } + public TargetUnitTriggerAction() { + m_acat = new CaType[] { new CaTypeSide(), new CaTypeUnit(), new CaTypeArea(), new CaTypeSide(), new CaTypeUnit() }; + } + } +#endif + + [Serializable] + [DisplayName("Create Unit Group")] + [Description("Create a pre-defined Unit Group and send it on its way according to its intrinsic actions.")] + public class CreateUnitGroupTriggerAction : CaBase { + override public string GetString() { + return "Create Unit Group '$1'"; + } + public CreateUnitGroupTriggerAction() { + m_acat = new CaType[] { new CaTypeUnitGroup() }; + } + } + + [Serializable] + [DisplayName("Comment")] + [Description("Use this to comment your actions. It has no in-game effect.")] + public class CommentTriggerAction : CaBase { + override public string GetString() { + return "Comment: $1"; + } + public CommentTriggerAction() { + m_acat = new CaType[] { new CaTypeRichText() }; + } + } + + [Serializable] + [DisplayName("Hunt Units")] + [Description("Each unit of the first specified side and type will seek " + + "out a unit of the second side and type and attack it and continue " + + "doing so until it is dead or there are no more enemies.")] + public class HuntTriggerAction : CaBase { + override public string GetString() { + return "$1 of $2 hunt $3 of $4"; + } + public HuntTriggerAction() { + m_acat = new CaType[] { new CaTypeUnitTypes(), new CaTypeSide(), new CaTypeUnitTypes(), new CaTypeSide() }; + } + } + + [Serializable] + [DisplayName("Create Random Unit Group")] + [Description("Create a random pre-defined Unit Group and send it on its " + + "way according to its intrinsic actions. The Unit Group is selected from" + + "amongst those with the \"Random\" box checked.")] + public class CreateRandomUnitGroupTriggerAction : CaBase { + override public string GetString() { + return "Create a random Unit Group"; + } + public CreateRandomUnitGroupTriggerAction() { + m_acat = new CaType[0]; + } + } + + [Serializable] + [DisplayName("Repair")] + [Description("Enable or disable repairing of a side's structures. While enabled " + + "repairing will continue as long as there are credits available.")] + public class RepairTriggerAction : CaBase { + override public string GetString() { + return "Set repairing for $1 $2"; + } + public RepairTriggerAction() { + m_acat = new CaType[] { new CaTypeSide(), new CaTypeOnOff() }; + } + } + + [Serializable] + [DisplayName("Replicator")] + [Description("Enable or disable a side's Replicator(s).")] + public class EnableReplicatorTriggerAction : CaBase { + override public string GetString() { + return "Turn $1's Replicator $2"; + } + public EnableReplicatorTriggerAction() { + m_acat = new CaType[] { new CaTypeSide(), new CaTypeOnOff() }; + } + } + + [Serializable] + [DisplayName("Modify Credits")] + [Description("Use this to set, add, or subtract Credits for specified players.")] + public class ModifyCreditsTriggerAction : CaBase { + override public string GetString() { + return "$2's Credits: $1 $3"; + } + public ModifyCreditsTriggerAction() { + m_acat = new CaType[] { new CaTypeModifyNumber(), new CaTypeSide(), new CaTypeNumber() }; + } + } + + [Serializable] + [DisplayName("Modify Counter")] + [Description("Use this to modify the value of a Counter.")] + public class ModifyCounterTriggerAction : CaBase { + override public string GetString() { + return "Counter '$1': $2 $3"; + } + public ModifyCounterTriggerAction() { + m_acat = new CaType[] { new CaTypeCounter(), new CaTypeModifyNumber(), new CaTypeNumber() }; + } + } + + [Serializable] + [DisplayName("Move Units In Area")] + [Description("Use this to units from one area to another.")] + public class MoveUnitsInAreaTriggerAction : CaBase { + override public string GetString() { + return "Move $1 $2 from '$3' to '$4'"; + } + public MoveUnitsInAreaTriggerAction() { + m_acat = new CaType[] { new CaTypeSide(), new CaTypeUnitTypes(), new CaTypeArea(), new CaTypeArea() }; + } + } + + [Serializable] + [DisplayName("Set Formal Objective Text")] + [Description("Use this to set one of the objectives shown on the Objectives screen.")] + public class SetFormalObjectiveTextTriggerAction : CaBase { + override public string GetString() { + return "Set formal objective $1 text to \"$2\""; + } + public SetFormalObjectiveTextTriggerAction() { + m_acat = new CaType[] { new CaTypeNumber(), new CaTypeText() }; + } + } + + [Serializable] + [DisplayName("Set Formal Objective Status")] + [Description("Use this to set one of the status of an objective shown on the Objectives screen.")] + public class SetFormalObjectiveStatusTriggerAction : CaBase { + override public string GetString() { + return "Set formal objective $1 status to \"$2\""; + } + public SetFormalObjectiveStatusTriggerAction() { + m_acat = new CaType[] { new CaTypeNumber(), new CaTypeText() }; + } + } + + [Serializable] + [DisplayName("Set Formal Objective Info")] + [Description("Use this to set the informational text shown on the Objectives screen.")] + public class SetFormalObjectiveInfoTriggerAction : CaBase { + override public string GetString() { + return "Set formal objective info to \"$1\""; + } + public SetFormalObjectiveInfoTriggerAction() { + m_acat = new CaType[] { new CaTypeRichText() }; + } + } + + [Serializable] + [DisplayName("Show Formal Objectives")] + [Description("Use this to invoke the Objectives screen. Make sure you've set all the appropriate " + + "formal objective parameters (text, status, info) first.")] + public class ShowObjectivesTriggerAction : CaBase { + override public string GetString() { + return "Show formal objectives"; + } + public ShowObjectivesTriggerAction() { + m_acat = new CaType[] {}; + } + } + + [Serializable] + [DisplayName("Cut Scene")] + [Description("Use this to display a cut scene. Supported cut scene tags: .")] + public class CutSceneTriggerAction : CaBase { + override public string GetString() { + return "Cut scene \"$1\""; + } + public CutSceneTriggerAction() { + m_acat = new CaType[] { new CaTypeRichText() }; + } + } + + [Serializable] + [DisplayName("Jump To Mission")] + [Description("Jumps immediately to the specified mission. The mission naming convention is \"mission.lvl\".")] + public class JumpToMissionTriggerAction : CaBase { + override public string GetString() { + return "Jump to mission: \"$1\""; + } + public JumpToMissionTriggerAction() { + m_acat = new CaType[] { new CaTypeText() }; + } + } + + [Serializable] + [DisplayName("Create Unit At Area")] + [Description("Builds or spawns a unit/structure at area. 'Built' units follow some game rules (building takes " + + "time and requires credits). 'Spawned' units appear immediately and are free. " + + "Structures are built at the upper-left of the area and the space they require must be unocccupied.")] + public class CreateUnitAtAreaTriggerAction : CaBase { + override public string GetString() { + return "$1 $2 at '$3' for $4 with $5 percent health"; + } + public CreateUnitAtAreaTriggerAction() { + m_acat = new CaType[] { new CaTypeCreate(), new CaTypeUnitType(), new CaTypeArea(), new CaTypeOneSide(), new CaTypeNumber() }; + } + + public override string FromSaveString(string strArgs) { + Regex re = new Regex(@"^\d+,"); + Match m = re.Match(strArgs); + string strT = re.Split(strArgs)[1]; + CaTypeUnitGroup cat = new CaTypeUnitGroup(); + cat.FromSaveString(strT, false); + UnitGroup ug = cat.UnitGroup; + ((CaTypeCreate)m_acat[0]).Result = (ug.Spawn ? CreateType.Spawn : CreateType.Build); + ((CaTypeUnitType)m_acat[1]).UnitType = ((UnitTypeAndCount)(ug.UnitTypeAndCounts[0])).ut; + ((CaTypeArea)m_acat[2]).Area = ug.SpawnArea; + ((CaTypeOneSide)m_acat[3]).Side = ug.Side; + ((CaTypeNumber)m_acat[4]).Value = ug.Health; + return ""; + } + + public override string ToSaveString() { + LevelDoc lvld = (LevelDoc)DocManager.GetActiveDocument(typeof(LevelDoc)); + + // Create a new UnitGroup + + int iug = lvld.UnitGroupManager.Items.Count; + UnitGroup ug = new UnitGroup("__cuaa" + iug); + ug.Spawn = ((CaTypeCreate)m_acat[0]).Result == CreateType.Spawn ? true : false; + ug.SpawnArea = ((CaTypeArea)m_acat[2]).Area; + ug.UnitTypeAndCounts.Add(new UnitTypeAndCount(((CaTypeUnitType)m_acat[1]).UnitType, 1)); + ug.Side = ((CaTypeOneSide)m_acat[3]).Side; + ug.Health = ((CaTypeNumber)m_acat[4]).Value; + ug.LoopForever = false; + ug.ReplaceDestroyedGroup = false; + ug.CreateAtLevelLoad = false; + ug.Aggressiveness = Aggressiveness.Defender; + ug.RandomGroup = false; + lvld.UnitGroupManager.AddUnitGroup(ug); + + string strType = GetType().ToString(); + int ichDot = strType.IndexOf("."); + strType = strType.Substring(ichDot + 1, strType.Length - ichDot - 1); + return "kn" + strType + "," + iug.ToString(); + } + } + + [Serializable] + [DisplayName("Modify Persistent Variable")] + [Description("Use this to set, add to, or subtract from the value of persistent variable.")] + public class ModifyPvarTriggerAction : CaBase { + override public string GetString() { + return "Modify persistent variable '$1': $2 $3"; + } + public ModifyPvarTriggerAction() { + m_acat = new CaType[] { new CaTypeText(), new CaTypeModifyNumber(), new CaTypeNumber() }; + } + } + + [Serializable] + [DisplayName("Set Persistent Variable Text")] + [Description("Use this to set a text string to a persistent variable.")] + public class SetPvarTextTriggerAction : CaBase { + override public string GetString() { + return "Set persistent variable '$1' to \"$2\""; + } + public SetPvarTextTriggerAction() { + m_acat = new CaType[] { new CaTypeText(), new CaTypeText() }; + } + } + + [Serializable] + [DisplayName("Show Alert")] + [Description("Use this to show an alert string at the lower-left of the display.")] + public class ShowAlertTriggerAction : CaBase { + override public string GetString() { + return "Show alert \"$1\""; + } + public ShowAlertTriggerAction() { + m_acat = new CaType[] { new CaTypeText() }; + } + } + + [Serializable] + [DisplayName("Set Allowed Upgrades")] + [Description("Use this to specify which upgrades the side is allowed to research. " + + "Only these Upgrades will show in the R&D panel. " + + "NOTE: all upgrades are allowed by default if no 'Set Allowed Upgrades' action is specified.")] + public class SetAllowedUpgradesTriggerAction : CaBase { + override public string GetString() { + return "Set $1 allowed upgrades to $2"; + } + public SetAllowedUpgradesTriggerAction() { + m_acat = new CaType[] { new CaTypeSide(), new CaTypeUpgradeTypes() }; + } + } + + [Serializable] + [DisplayName("Set Upgrades")] + [Description("Use this to specify which upgrades the side posseses. " + + "NOTE: this can only used as part of a Mission Loaded trigger.")] + public class SetUpgradesTriggerAction : CaBase { + override public string GetString() { + return "Set $1 upgrades to $2"; + } + public SetUpgradesTriggerAction() { + m_acat = new CaType[] { new CaTypeSide(), new CaTypeUpgradeTypes() }; + } + } + + // + // Unit Group Actions + // + + [Serializable] + [DisplayName("Set Switch")] + [Description("Switch names can be anything (including spaces, etc) but ARE case sensitive. That is, setting " + + "a switch named \"Whatever\" and testing \"whatever\" won't work.")] + public class SetSwitchUnitGroupAction : CaBase { + override public string GetString() { + return "Set switch '$1' $2"; + } + public SetSwitchUnitGroupAction() { + m_acat = new CaType[] { new CaTypeSwitch(), new CaTypeOnOff() }; + } + } + + [Serializable] + [DisplayName("Wait")] + [Description("Delay a number of seconds before continuing on with the next action.")] + public class WaitUnitGroupAction : CaBase { + override public string GetString() { + return "Wait $1 seconds"; + } + public WaitUnitGroupAction() { + m_acat = new CaType[] { new CaTypeNumber() }; + } + } + + [Serializable] + [DisplayName("Move")] + [Description("All members of the group are directed to the specified Area. This action " + + "is not considered complete and the next action will not execute until all " + + "the members of the group reach the destination (or as close as they can " + + "reasonably get).")] + public class MoveUnitGroupAction : CaBase { + override public string GetString() { + return "Move to area '$1'"; + } + public MoveUnitGroupAction() { + m_acat = new CaType[] { new CaTypeArea() }; + } + } + + [Serializable] + [DisplayName("Attack")] + [Description("Attack the nearest enemy unit matching the specified side(s) and type(s) for the specified interval. " + + "The entire group will attack the unit together. If the unit is destroyed before the interval " + + "elapses the next nearest matching enemy will be targeted by the group.")] + public class AttackUnitGroupAction : CaBase { + override public string GetString() { + return "Attack nearest $1 owned by $2 for $3 seconds"; + } + public AttackUnitGroupAction() { + m_acat = new CaType[] { new CaTypeUnitTypes(), new CaTypeSide(), new CaTypeNumber() }; + } + } + + [Serializable] + [DisplayName("Comment")] + [Description("Use this to comment your actions. It has no in-game effect.")] + public class CommentUnitGroupAction : CaBase { + override public string GetString() { + return "Comment: $1"; + } + public CommentUnitGroupAction() { + m_acat = new CaType[] { new CaTypeRichText() }; + } + } + + [Serializable] + [DisplayName("Guard")] + [Description("All members of the group are directed guard the area within " + + "their sight range for the specified interval.")] + public class GuardUnitGroupAction : CaBase { + override public string GetString() { + return "Guard for $1 seconds"; + } + public GuardUnitGroupAction() { + m_acat = new CaType[] { new CaTypeNumber() }; + } + } + + [Serializable] + [DisplayName("GuardVicinity")] + [Description("All members of the group are directed guard the area within " + + "an expanded sight range for the specified interval.")] + public class GuardVicinityUnitGroupAction : CaBase { + override public string GetString() { + return "Guard vicinity for $1 seconds"; + } + public GuardVicinityUnitGroupAction() { + m_acat = new CaType[] { new CaTypeNumber() }; + } + } + + [Serializable] + [DisplayName("Mine")] + [Description("Find the nearest Galaxite, mine it, and bring it to the nearest Processor. " + + "Repeat forever or until death. NOTE: This action has no effect on any unit other than the Bullpup.")] + public class MineUnitGroupAction : CaBase { + override public string GetString() { + return "Mine"; + } + public MineUnitGroupAction() { + m_acat = new CaType[] { }; + } + } + + // + // Unit Actions + // + + [Serializable] + [DisplayName("Guard")] + [Description("Guard the area within the unit's immediate range of sight.")] + public class GuardUnitAction : CaBase { + override public string GetString() { + return "Guard sight range"; + } + public GuardUnitAction() { + m_acat = new CaType[0]; + } + } + + [Serializable] + [DisplayName("Guard Vicinity")] + [Description("Guard a largish area surrounding the unit.")] + public class GuardVicinityUnitAction : CaBase { + override public string GetString() { + return "Guard vicinity"; + } + public GuardVicinityUnitAction() { + m_acat = new CaType[0]; + } + } + + [Serializable] + [DisplayName("Guard Area")] + [Description("Guard a specific area. Any enemies entering the area willl be attacked. " + + "If the enemy is destroyed the unit will sit idle until another enemy enters the area.")] + public class GuardAreaUnitAction : CaBase { + override public string GetString() { + return "Guard '$1'"; + } + public GuardAreaUnitAction() { + m_acat = new CaType[] { new CaTypeArea() }; + } + } + + [Serializable] + [DisplayName("Move")] + [Description("Move to the specified area. Depending on the unit's " + + "Aggressiveness setting it may attack enemies along the way.")] + public class MoveUnitAction : CaBase { + override public string GetString() { + return "Move to '$1'"; + } + public MoveUnitAction() { + m_acat = new CaType[] { new CaTypeArea() }; + } + } + + [Serializable] + [DisplayName("Hunt Enemies")] + [Description("Find an enemy unit/structure of the specified type(s) and attack it. " + + "If this unit survives the encounter it will continue the hunt. " + + "The enemy unit is chosen randomly, not based on proximity or any other factors.")] + public class HuntEnemiesUnitAction : CaBase { + override public string GetString() { + return "Hunt $1"; + } + public HuntEnemiesUnitAction() { + m_acat = new CaType[] { new CaTypeUnitTypes() }; + } + } + + [Serializable] + [DisplayName("Mine")] + [Description("Find the nearest Galaxite, mine it, and bring it to the nearest Processor. " + + "Repeat forever or until death. NOTE: This action has no effect on any unit other than the Bullpup.")] + public class MineUnitAction : CaBase { + override public string GetString() { + return "Mine"; + } + public MineUnitAction() { + m_acat = new CaType[] { }; + } + } +} diff --git a/m/CaNew.cs b/m/CaNew.cs new file mode 100644 index 0000000..f3a577e --- /dev/null +++ b/m/CaNew.cs @@ -0,0 +1,323 @@ +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; +using System.Diagnostics; + +namespace m +{ + /// + /// Summary description for CaNew. + /// + public class CaNew : System.Windows.Forms.Form + { + Type[] m_atype; + CaBase m_cab; + string m_strParse; + string m_strKind; + private System.Windows.Forms.Button button1; + private System.Windows.Forms.Label m_labelWhatType; + private System.Windows.Forms.Label m_labelText; + private System.Windows.Forms.RichTextBox m_richTextBox; + private System.Windows.Forms.ComboBox m_comboBoxType; + private System.Windows.Forms.Button buttonCancel; + private System.Windows.Forms.Label m_labelDescription; + private System.Windows.Forms.GroupBox groupBox1; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + public CaNew(CaBase cab, string strTitle, string strKind, Type[] atype, string[] astrName) + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + m_atype = atype; + m_cab = null; + m_strParse = null; + m_strKind = strKind; + + // To do - might want to pretty print these type names + + Text = strTitle; + m_labelWhatType.Text = m_labelWhatType.Text.Replace("$1", strKind); + m_labelText.Text = m_labelText.Text.Replace("$1", strKind); + Array.Sort(astrName, atype); + Array.Sort(astrName); + m_comboBoxType.DataSource = astrName; + m_cab = cab; + if (cab != null) { + m_comboBoxType.SelectedIndex = Array.IndexOf(atype, m_cab.GetType()); + } else { + m_comboBoxType.SelectedIndex = 0; + } + SelectCa(m_comboBoxType.SelectedIndex); + } + + CaBase GetCab() { + return m_cab; + } + + public static CaBase DoModal(CaBase cab, string strTitle, string strKind) { + string strEndsWith = strKind; + System.Reflection.Assembly ass = typeof(CaBase).Module.Assembly; + Type[] atype = ass.GetTypes(); + ArrayList alsType = new ArrayList(); + ArrayList alsName = new ArrayList(); + foreach (Type type in atype) { + string strName = type.ToString(); + if (strName.EndsWith(strEndsWith)) { + alsType.Add(type); + string strDisplayName = Helper.GetDisplayName(type); + if (strDisplayName == null) { + int ichDot = strName.IndexOf('.'); + strDisplayName = strName.Substring(ichDot + 1, strName.Length - (ichDot + 1 + strEndsWith.Length)); + } + alsName.Add(strDisplayName); + } + } + CaNew frmCaNew = new CaNew(cab, strTitle, strKind, (Type[])alsType.ToArray(typeof(Type)), (string[])alsName.ToArray(typeof(string)) ); + frmCaNew.ShowDialog(); + if (frmCaNew.DialogResult == DialogResult.Cancel) + return null; + return frmCaNew.GetCab(); + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.m_labelWhatType = new System.Windows.Forms.Label(); + this.m_comboBoxType = new System.Windows.Forms.ComboBox(); + this.m_labelText = new System.Windows.Forms.Label(); + this.button1 = new System.Windows.Forms.Button(); + this.buttonCancel = new System.Windows.Forms.Button(); + this.m_richTextBox = new System.Windows.Forms.RichTextBox(); + this.m_labelDescription = new System.Windows.Forms.Label(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.groupBox1.SuspendLayout(); + this.SuspendLayout(); + // + // m_labelWhatType + // + this.m_labelWhatType.Location = new System.Drawing.Point(8, 16); + this.m_labelWhatType.Name = "m_labelWhatType"; + this.m_labelWhatType.Size = new System.Drawing.Size(496, 16); + this.m_labelWhatType.TabIndex = 0; + this.m_labelWhatType.Text = "&What type of $1 do you wish to create?"; + // + // m_comboBoxType + // + this.m_comboBoxType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.m_comboBoxType.Location = new System.Drawing.Point(8, 40); + this.m_comboBoxType.MaxDropDownItems = 16; + this.m_comboBoxType.Name = "m_comboBoxType"; + this.m_comboBoxType.Size = new System.Drawing.Size(496, 21); + this.m_comboBoxType.TabIndex = 1; + this.m_comboBoxType.SelectedIndexChanged += new System.EventHandler(this.m_comboBoxType_SelectedIndexChanged); + // + // m_labelText + // + this.m_labelText.Location = new System.Drawing.Point(8, 80); + this.m_labelText.Name = "m_labelText"; + this.m_labelText.Size = new System.Drawing.Size(496, 16); + this.m_labelText.TabIndex = 2; + this.m_labelText.Text = "&$1 Text (click on underlined words to set values):"; + // + // button1 + // + this.button1.Location = new System.Drawing.Point(168, 328); + this.button1.Name = "button1"; + this.button1.TabIndex = 4; + this.button1.Text = "Ok"; + this.button1.Click += new System.EventHandler(this.button1_Click); + // + // buttonCancel + // + this.buttonCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.buttonCancel.Location = new System.Drawing.Point(288, 328); + this.buttonCancel.Name = "buttonCancel"; + this.buttonCancel.TabIndex = 4; + this.buttonCancel.Text = "Cancel"; + this.buttonCancel.Click += new System.EventHandler(this.buttonCancel_Click); + // + // m_richTextBox + // + this.m_richTextBox.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0))); + this.m_richTextBox.Location = new System.Drawing.Point(8, 104); + this.m_richTextBox.Name = "m_richTextBox"; + this.m_richTextBox.ReadOnly = true; + this.m_richTextBox.Size = new System.Drawing.Size(496, 120); + this.m_richTextBox.TabIndex = 5; + this.m_richTextBox.Text = "richTextBox1"; + this.m_richTextBox.MouseDown += new System.Windows.Forms.MouseEventHandler(this.m_richTextBox_MouseDown); + // + // m_labelDescription + // + this.m_labelDescription.Dock = System.Windows.Forms.DockStyle.Fill; + this.m_labelDescription.ForeColor = System.Drawing.Color.Indigo; + this.m_labelDescription.Location = new System.Drawing.Point(3, 16); + this.m_labelDescription.Name = "m_labelDescription"; + this.m_labelDescription.Size = new System.Drawing.Size(490, 61); + this.m_labelDescription.TabIndex = 7; + this.m_labelDescription.Text = "label2"; + this.m_labelDescription.UseMnemonic = false; + // + // groupBox1 + // + this.groupBox1.Controls.AddRange(new System.Windows.Forms.Control[] { + this.m_labelDescription}); + this.groupBox1.Location = new System.Drawing.Point(8, 240); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(496, 80); + this.groupBox1.TabIndex = 8; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "Description"; + // + // CaNew + // + this.AcceptButton = this.button1; + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.CancelButton = this.buttonCancel; + this.ClientSize = new System.Drawing.Size(514, 360); + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.groupBox1, + this.m_richTextBox, + this.button1, + this.m_labelText, + this.m_comboBoxType, + this.m_labelWhatType, + this.buttonCancel}); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "CaNew"; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "CaNew"; + this.groupBox1.ResumeLayout(false); + this.ResumeLayout(false); + + } + #endregion + + void SelectCa(int n) { + if (m_cab == null || m_cab.GetType() != m_atype[n]) + m_cab = (CaBase)System.Activator.CreateInstance(m_atype[n]); + + m_labelDescription.Text = Helper.GetDescription(m_cab.GetType()); + + m_richTextBox.Clear(); + + // String replacement, order independent + + string str = m_cab.GetString(); + CaType[] acat = m_cab.GetTypes(); + for (int j = 0; j < acat.Length; j++) + str = str.Replace("$" + (j + 1), "~" + j + acat[j].ToString() + "~" + j); + + // Save this away for hittesting purposes + + m_strParse = (string)str.Clone(); + + // && delimited pieces are links + + while (str.Length != 0) { + int ichT = str.IndexOf("~"); + if (ichT == -1) { + m_richTextBox.AppendText(str); + break; + } + if (ichT != 0) + m_richTextBox.AppendText(str.Substring(0, ichT)); + str = str.Remove(0, ichT + 2); + + // Now add the underlined text + + int ichStart = m_richTextBox.TextLength; + int cchLink = str.IndexOf("~"); + Debug.Assert(cchLink != -1); + m_richTextBox.AppendText(str.Substring(0, cchLink)); + str = str.Remove(0, cchLink + 2); + m_richTextBox.Select(ichStart, cchLink); + Color clr = m_richTextBox.SelectionColor; + Font fnt = m_richTextBox.SelectionFont; + m_richTextBox.SelectionColor = Color.Blue; + m_richTextBox.SelectionFont = new Font(fnt.FontFamily, fnt.Size, /* FontStyle.Bold | */ FontStyle.Underline); + m_richTextBox.Select(m_richTextBox.TextLength, 0); + m_richTextBox.SelectionFont = fnt; + m_richTextBox.SelectionColor = clr; + } + } + + private void m_comboBoxType_SelectedIndexChanged(object sender, System.EventArgs e) { + SelectCa(m_comboBoxType.SelectedIndex); + } + + int GetCatIndexFromCharIndex(int ich) { + int ichTranslated = 0; + int icat = -1; + for (int ichT = 0; ichT < m_strParse.Length; ichT++) { + if (m_strParse[ichT] == '~') { + if (icat == -1) { + icat = m_strParse[ichT + 1] - '0'; + } else { + icat = -1; + } + ichT++; + continue; + } + if (ich == ichTranslated) + return icat; + ichTranslated++; + } + return -1; + } + + private void m_richTextBox_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e) { + int ich = m_richTextBox.GetCharIndexFromPosition(new Point(e.X, e.Y)); + if (ich == -1) + return; + int icat = GetCatIndexFromCharIndex(ich); + if (icat == -1) + return; + CaType[] acat = m_cab.GetTypes(); + if (acat[icat].EditProperties()) + SelectCa(m_comboBoxType.SelectedIndex); + } + + private void button1_Click(object sender, System.EventArgs e) { + if (m_cab == null || !m_cab.IsValid()) { + MessageBox.Show(this, "Invalid " + m_strKind); + return; + } + DialogResult = DialogResult.OK; + } + + private void buttonCancel_Click(object sender, System.EventArgs e) { + DialogResult = DialogResult.Cancel; + } + } +} diff --git a/m/CaNew.resources b/m/CaNew.resources new file mode 100644 index 0000000..d5b77aa Binary files /dev/null and b/m/CaNew.resources differ diff --git a/m/CaNew.resx b/m/CaNew.resx new file mode 100644 index 0000000..7f932d3 --- /dev/null +++ b/m/CaNew.resx @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + CaNew + + \ No newline at end of file diff --git a/m/CaPropForm.cs b/m/CaPropForm.cs new file mode 100644 index 0000000..65bd765 --- /dev/null +++ b/m/CaPropForm.cs @@ -0,0 +1,118 @@ +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; + +namespace m +{ + /// + /// Summary description for CaPropForm. + /// + public class CaPropForm : System.Windows.Forms.Form + { + private System.Windows.Forms.PropertyGrid propertyGrid1; + private System.Windows.Forms.Button buttonOk; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + public CaPropForm(CaType cat) + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + // + // TODO: Add any constructor code after InitializeComponent call + // + + propertyGrid1.SelectedObject = cat; + propertyGrid1.ExpandAllGridItems(); + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.propertyGrid1 = new System.Windows.Forms.PropertyGrid(); + this.buttonOk = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // propertyGrid1 + // + this.propertyGrid1.CommandsVisibleIfAvailable = true; + this.propertyGrid1.LargeButtons = false; + this.propertyGrid1.LineColor = System.Drawing.SystemColors.ScrollBar; + this.propertyGrid1.Name = "propertyGrid1"; + this.propertyGrid1.Size = new System.Drawing.Size(296, 256); + this.propertyGrid1.TabIndex = 0; + this.propertyGrid1.Text = "propertyGrid1"; + this.propertyGrid1.ToolbarVisible = false; + this.propertyGrid1.ViewBackColor = System.Drawing.SystemColors.Window; + this.propertyGrid1.ViewForeColor = System.Drawing.SystemColors.WindowText; + // + // buttonOk + // + this.buttonOk.Location = new System.Drawing.Point(112, 268); + this.buttonOk.Name = "buttonOk"; + this.buttonOk.TabIndex = 1; + this.buttonOk.Text = "Ok"; + this.buttonOk.Click += new System.EventHandler(this.buttonOk_Click); + // + // CaPropForm + // + this.AcceptButton = this.buttonOk; + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.ClientSize = new System.Drawing.Size(296, 302); + this.ControlBox = false; + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.buttonOk, + this.propertyGrid1}); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "CaPropForm"; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Properties"; + this.ResumeLayout(false); + + } + #endregion + + public static bool DoModal(CaType cat) { + CaPropForm frm = new CaPropForm(cat); + DialogResult res = frm.ShowDialog(); + return res == DialogResult.OK; + } + + private void buttonOk_Click(object sender, System.EventArgs e) { + DialogResult = DialogResult.OK; + } + + private void buttonCancel_Click(object sender, System.EventArgs e) { + DialogResult = DialogResult.Cancel; + } + } +} diff --git a/m/CaPropForm.resources b/m/CaPropForm.resources new file mode 100644 index 0000000..c3f3f5d Binary files /dev/null and b/m/CaPropForm.resources differ diff --git a/m/CaPropForm.resx b/m/CaPropForm.resx new file mode 100644 index 0000000..44f98c0 --- /dev/null +++ b/m/CaPropForm.resx @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + CaPropForm + + \ No newline at end of file diff --git a/m/CaTypeUnitTypesForm.cs b/m/CaTypeUnitTypesForm.cs new file mode 100644 index 0000000..1303321 --- /dev/null +++ b/m/CaTypeUnitTypesForm.cs @@ -0,0 +1,244 @@ +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; + +namespace m +{ + /// + /// Summary description for CaTypeUnitTypesForm. + /// + public class CaTypeUnitTypesForm : System.Windows.Forms.Form + { + private UnitMask m_um; + private System.Windows.Forms.CheckedListBox checkedListBox; + private System.Windows.Forms.Button buttonOK; + private System.Windows.Forms.Button buttonStructures; + private System.Windows.Forms.Button buttonInfantry; + private System.Windows.Forms.Button buttonMobileUnits; + private System.Windows.Forms.Button buttonBuilders; + private System.Windows.Forms.Button buttonVehicles; + private System.Windows.Forms.Button buttonAll; + private System.Windows.Forms.Button buttonNone; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + public CaTypeUnitTypesForm(UnitMask um) + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + // + // TODO: Add any constructor code after InitializeComponent call + // + + m_um = um; + + InitUnitTypesListBox(); + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.checkedListBox = new System.Windows.Forms.CheckedListBox(); + this.buttonOK = new System.Windows.Forms.Button(); + this.buttonStructures = new System.Windows.Forms.Button(); + this.buttonInfantry = new System.Windows.Forms.Button(); + this.buttonMobileUnits = new System.Windows.Forms.Button(); + this.buttonBuilders = new System.Windows.Forms.Button(); + this.buttonVehicles = new System.Windows.Forms.Button(); + this.buttonAll = new System.Windows.Forms.Button(); + this.buttonNone = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // checkedListBox + // + this.checkedListBox.CheckOnClick = true; + this.checkedListBox.Location = new System.Drawing.Point(8, 8); + this.checkedListBox.Name = "checkedListBox"; + this.checkedListBox.Size = new System.Drawing.Size(184, 289); + this.checkedListBox.Sorted = true; + this.checkedListBox.TabIndex = 0; + // + // buttonOK + // + this.buttonOK.DialogResult = System.Windows.Forms.DialogResult.OK; + this.buttonOK.Location = new System.Drawing.Point(200, 272); + this.buttonOK.Name = "buttonOK"; + this.buttonOK.Size = new System.Drawing.Size(80, 23); + this.buttonOK.TabIndex = 1; + this.buttonOK.Text = "OK"; + this.buttonOK.Click += new System.EventHandler(this.buttonOK_Click); + // + // buttonStructures + // + this.buttonStructures.Location = new System.Drawing.Point(200, 104); + this.buttonStructures.Name = "buttonStructures"; + this.buttonStructures.Size = new System.Drawing.Size(80, 23); + this.buttonStructures.TabIndex = 2; + this.buttonStructures.Text = "Structures"; + this.buttonStructures.Click += new System.EventHandler(this.buttonStructures_Click); + // + // buttonInfantry + // + this.buttonInfantry.Location = new System.Drawing.Point(200, 8); + this.buttonInfantry.Name = "buttonInfantry"; + this.buttonInfantry.Size = new System.Drawing.Size(80, 23); + this.buttonInfantry.TabIndex = 3; + this.buttonInfantry.Text = "Infantry"; + this.buttonInfantry.Click += new System.EventHandler(this.buttonInfantry_Click); + // + // buttonMobileUnits + // + this.buttonMobileUnits.Location = new System.Drawing.Point(200, 72); + this.buttonMobileUnits.Name = "buttonMobileUnits"; + this.buttonMobileUnits.Size = new System.Drawing.Size(80, 23); + this.buttonMobileUnits.TabIndex = 4; + this.buttonMobileUnits.Text = "Mobile Units"; + this.buttonMobileUnits.Click += new System.EventHandler(this.buttonMobileUnits_Click); + // + // buttonBuilders + // + this.buttonBuilders.Location = new System.Drawing.Point(200, 136); + this.buttonBuilders.Name = "buttonBuilders"; + this.buttonBuilders.Size = new System.Drawing.Size(80, 23); + this.buttonBuilders.TabIndex = 5; + this.buttonBuilders.Text = "Builders"; + this.buttonBuilders.Click += new System.EventHandler(this.buttonBuilders_Click); + // + // buttonVehicles + // + this.buttonVehicles.Location = new System.Drawing.Point(200, 40); + this.buttonVehicles.Name = "buttonVehicles"; + this.buttonVehicles.Size = new System.Drawing.Size(80, 23); + this.buttonVehicles.TabIndex = 6; + this.buttonVehicles.Text = "Vehicles"; + this.buttonVehicles.Click += new System.EventHandler(this.buttonVehicles_Click); + // + // buttonAll + // + this.buttonAll.Location = new System.Drawing.Point(200, 168); + this.buttonAll.Name = "buttonAll"; + this.buttonAll.Size = new System.Drawing.Size(80, 23); + this.buttonAll.TabIndex = 7; + this.buttonAll.Text = "All"; + this.buttonAll.Click += new System.EventHandler(this.buttonAll_Click); + // + // buttonNone + // + this.buttonNone.Location = new System.Drawing.Point(200, 200); + this.buttonNone.Name = "buttonNone"; + this.buttonNone.Size = new System.Drawing.Size(80, 23); + this.buttonNone.TabIndex = 8; + this.buttonNone.Text = "None"; + this.buttonNone.Click += new System.EventHandler(this.buttonNone_Click); + // + // CaTypeUnitTypesForm + // + this.AcceptButton = this.buttonOK; + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.ClientSize = new System.Drawing.Size(290, 304); + this.ControlBox = false; + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.buttonNone, + this.buttonAll, + this.buttonVehicles, + this.buttonBuilders, + this.buttonMobileUnits, + this.buttonInfantry, + this.buttonStructures, + this.buttonOK, + this.checkedListBox}); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "CaTypeUnitTypesForm"; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Unit Types"; + this.ResumeLayout(false); + + } + #endregion + + + private void InitUnitTypesListBox() { + checkedListBox.Items.Clear(); + for (int i = 0; i < (int)UnitType.kutMax; i++) + checkedListBox.Items.Add(new UnitTypeWrapper((UnitType)i), ((uint)m_um & (1 << i)) != 0); + } + + private void buttonOK_Click(object sender, System.EventArgs e) { + m_um = 0; + for (int i = 0; i < checkedListBox.CheckedItems.Count; i++) { + UnitType ut = ((UnitTypeWrapper)checkedListBox.CheckedItems[i]).ut; + m_um = (UnitMask)(((uint)m_um) | (uint)(1 << (int)ut)); + } + } + + private void buttonInfantry_Click(object sender, System.EventArgs e) { + m_um = UnitMask.kumInfantry; + InitUnitTypesListBox(); + } + + private void buttonVehicles_Click(object sender, System.EventArgs e) { + m_um = UnitMask.kumVehicles; + InitUnitTypesListBox(); + } + + private void buttonMobileUnits_Click(object sender, System.EventArgs e) { + m_um = UnitMask.kumMobileUnits; + InitUnitTypesListBox(); + } + + private void buttonStructures_Click(object sender, System.EventArgs e) { + m_um = UnitMask.kumStructures; + InitUnitTypesListBox(); + } + + private void buttonBuilders_Click(object sender, System.EventArgs e) { + m_um = UnitMask.kumFactories; + InitUnitTypesListBox(); + } + + private void buttonAll_Click(object sender, System.EventArgs e) { + m_um = UnitMask.kumAll; + InitUnitTypesListBox(); + } + + private void buttonNone_Click(object sender, System.EventArgs e) { + m_um = UnitMask.kumNone; + InitUnitTypesListBox(); + } + + public UnitMask UnitMask { + get { + return m_um; + } + } + } +} diff --git a/m/CaTypeUnitTypesForm.resources b/m/CaTypeUnitTypesForm.resources new file mode 100644 index 0000000..32d60f7 Binary files /dev/null and b/m/CaTypeUnitTypesForm.resources differ diff --git a/m/CaTypeUnitTypesForm.resx b/m/CaTypeUnitTypesForm.resx new file mode 100644 index 0000000..977b95e --- /dev/null +++ b/m/CaTypeUnitTypesForm.resx @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + CaTypeUnitTypesForm + + \ No newline at end of file diff --git a/m/CaTypeUpgradeTypesForm.cs b/m/CaTypeUpgradeTypesForm.cs new file mode 100644 index 0000000..27f08fb --- /dev/null +++ b/m/CaTypeUpgradeTypesForm.cs @@ -0,0 +1,165 @@ +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; + +namespace m +{ + /// + /// Summary description for CaTypeUpgradeTypesForm. + /// + public class CaTypeUpgradeTypesForm : System.Windows.Forms.Form + { + private UpgradeMask m_upgm; + private System.Windows.Forms.CheckedListBox checkedListBox; + private System.Windows.Forms.Button buttonOK; + private System.Windows.Forms.Button buttonAll; + private System.Windows.Forms.Button buttonNone; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + public CaTypeUpgradeTypesForm(UpgradeMask upgm) + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + m_upgm = upgm; + + InitUpgradeTypesListBox(); + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.checkedListBox = new System.Windows.Forms.CheckedListBox(); + this.buttonOK = new System.Windows.Forms.Button(); + this.buttonAll = new System.Windows.Forms.Button(); + this.buttonNone = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // checkedListBox + // + this.checkedListBox.CheckOnClick = true; + this.checkedListBox.Location = new System.Drawing.Point(8, 8); + this.checkedListBox.Name = "checkedListBox"; + this.checkedListBox.Size = new System.Drawing.Size(184, 289); + this.checkedListBox.Sorted = true; + this.checkedListBox.TabIndex = 0; + // + // buttonOK + // + this.buttonOK.DialogResult = System.Windows.Forms.DialogResult.OK; + this.buttonOK.Location = new System.Drawing.Point(200, 272); + this.buttonOK.Name = "buttonOK"; + this.buttonOK.Size = new System.Drawing.Size(80, 23); + this.buttonOK.TabIndex = 1; + this.buttonOK.Text = "OK"; + this.buttonOK.Click += new System.EventHandler(this.buttonOK_Click); + // + // buttonAll + // + this.buttonAll.Location = new System.Drawing.Point(200, 8); + this.buttonAll.Name = "buttonAll"; + this.buttonAll.Size = new System.Drawing.Size(80, 23); + this.buttonAll.TabIndex = 7; + this.buttonAll.Text = "All"; + this.buttonAll.Click += new System.EventHandler(this.buttonAll_Click); + // + // buttonNone + // + this.buttonNone.Location = new System.Drawing.Point(200, 40); + this.buttonNone.Name = "buttonNone"; + this.buttonNone.Size = new System.Drawing.Size(80, 23); + this.buttonNone.TabIndex = 8; + this.buttonNone.Text = "None"; + this.buttonNone.Click += new System.EventHandler(this.buttonNone_Click); + // + // CaTypeUpgradeTypesForm + // + this.AcceptButton = this.buttonOK; + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.ClientSize = new System.Drawing.Size(290, 304); + this.ControlBox = false; + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.buttonNone, + this.buttonAll, + this.buttonOK, + this.checkedListBox}); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "CaTypeUpgradeTypesForm"; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Upgrade Types"; + this.ResumeLayout(false); + + } + #endregion + + private void InitUpgradeTypesListBox() { + checkedListBox.Items.Clear(); + for (int i = 0; i < (int)UpgradeType.kupgtMax; i++) + checkedListBox.Items.Add(new UpgradeTypeWrapper((UpgradeType)i), ((uint)m_upgm & (1 << i)) != 0); + } + + private void buttonOK_Click(object sender, System.EventArgs e) { + m_upgm = 0; + for (int i = 0; i < checkedListBox.CheckedItems.Count; i++) { + UpgradeType upgt = ((UpgradeTypeWrapper)checkedListBox.CheckedItems[i]).upgt; + m_upgm = (UpgradeMask)(((uint)m_upgm) | (uint)(1 << (int)upgt)); + } + } + + private void buttonAll_Click(object sender, System.EventArgs e) { + m_upgm = UpgradeMask.kupgmAll; + InitUpgradeTypesListBox(); + } + + private void buttonNone_Click(object sender, System.EventArgs e) { + m_upgm = UpgradeMask.kupgmNone; + InitUpgradeTypesListBox(); + } + + public UpgradeMask UpgradeMask { + get { + return m_upgm; + } + } + } + + public class UpgradeTypeWrapper { + public UpgradeType upgt; + public UpgradeTypeWrapper(UpgradeType upgt) { + this.upgt = upgt; + } + + override public string ToString() { + return Helper.GetDisplayName(typeof(UpgradeType), upgt.ToString()); + } + } +} diff --git a/m/CaTypeUpgradeTypesForm.resources b/m/CaTypeUpgradeTypesForm.resources new file mode 100644 index 0000000..c0b1641 Binary files /dev/null and b/m/CaTypeUpgradeTypesForm.resources differ diff --git a/m/CaTypeUpgradeTypesForm.resx b/m/CaTypeUpgradeTypesForm.resx new file mode 100644 index 0000000..cc7d2ee --- /dev/null +++ b/m/CaTypeUpgradeTypesForm.resx @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + CaTypeUpgradeTypesForm + + \ No newline at end of file diff --git a/m/CaTypes.cs b/m/CaTypes.cs new file mode 100644 index 0000000..cf10818 --- /dev/null +++ b/m/CaTypes.cs @@ -0,0 +1,1511 @@ +using System; +using System.Collections; +using System.Collections.Specialized; +using System.Windows.Forms; +using System.Runtime.Serialization; +using System.Text.RegularExpressions; + +namespace m { +#if false + QualifiedNumber, + Player, + Unit, + Location, + Resources, + Points, + Switch, + Text, + SoundEffect, + Duration, + ScoreType, +#endif + + [Serializable] + public abstract class CaType { + protected bool m_fInit = false; + + public bool IsInitialized() { + return m_fInit; + } + + public new abstract string ToString(); + public abstract string ToSaveString(); + public abstract string FromSaveString(string strArgs, bool fLast); + + public virtual bool EditProperties() { + return CaPropForm.DoModal(this); + } + + public virtual CaType Clone() { + return (CaType)MemberwiseClone(); + } + + protected static string GetSaveString(object obj) { + string str = obj.GetType().ToString(); + int ichDot = str.IndexOf("."); + str = str.Substring(ichDot + 1, str.Length - ichDot - 1); + str = "kn" + str + obj.ToString(); + return str; + } + } + + // Number + + [Serializable] + public class CaTypeNumber : CaType { + protected int m_nValue; + + public CaTypeNumber() { + m_nValue = -1; + } + + public override string ToString() { + if (!m_fInit) { + return "quantity"; + } else { + return m_nValue.ToString(); + } + } + + public override string FromSaveString(string strArgs, bool fLast) { + Regex re = new Regex(@"^(?(-)?\d+)(?.*)$"); + Match m = re.Match(strArgs); + Value = int.Parse(m.Groups["value"].Value); + return m.Groups["end"].Value; + } + + public override string ToSaveString() { + return ToString(); + } + + public int Value { + get { + return m_nValue; + } + set { + m_nValue = value; + m_fInit = true; + } + } + } + + // QualifiedNumber + + public enum Qualifier { AtLeast, AtMost, Exactly }; + + [Serializable] + public class CaTypeQualifiedNumber : CaTypeNumber { + Qualifier m_qfr; + + public CaTypeQualifiedNumber() { + m_qfr = Qualifier.AtLeast; + } + + public override string ToString() { + if (m_fInit) + return m_qfr.ToString() + " " + m_nValue.ToString(); + return base.ToString(); + } + + public override string FromSaveString(string strArgs, bool fLast) { + Regex re = new Regex(@"^(?\d+),(?.*)$"); + Match m = re.Match(strArgs); + Qualifier = (Qualifier)int.Parse(m.Groups["qfr"].Value); + return base.FromSaveString(m.Groups["end"].Value, false); + } + + public override string ToSaveString() { + return GetSaveString(m_qfr) + "," + Value.ToString(); + } + + public Qualifier Qualifier { + get { + return m_qfr; + } + set { + m_qfr = value; + } + } + } + + [Flags] + public enum CaSide { SideNeutral, Side1, Side2, Side3, Side4, Enemies, Allies, AllSides, CurrentSide }; + + [Serializable] + public class CaTypeSide : CaType { + bool[] m_af; + + public CaTypeSide() { + m_af = new bool[Enum.GetNames(typeof(CaSide)).Length]; + } + + public override CaType Clone() { + CaTypeSide cat = (CaTypeSide)MemberwiseClone(); + cat.m_af = (bool[])m_af.Clone(); + return (CaType)cat; + } + + public override string ToString() { + if (!m_fInit) { + return "side"; + } else { + string str = ""; + for (int i = 0; i < m_af.Length; i++) { + if (m_af[i]) { + if (str.Length != 0) + str += ", "; + str += ((CaSide)i).ToString(); + } + } + return str; + } + } + + public override string FromSaveString(string strArgs, bool fLast) { + Regex re = new Regex(@"^(?\d+)(?.*)$"); + Match m = re.Match(strArgs); + uint dwMask = uint.Parse(m.Groups["mask"].Value); + for (int i = 0; i < m_af.Length; i++) { + m_af[i] = ((dwMask & (1 << i)) != 0); + } + m_fInit = true; + return m.Groups["end"].Value; + } + + public override string ToSaveString() { + uint dwMask = 0; + for (int i = 0; i < m_af.Length; i++) { + if (m_af[i]) + dwMask |= (uint)(1 << i); + } + return dwMask.ToString(); + } + + // Is there a better way to expose flags to the property grid? + + public bool CurrentSide { + get { + return m_af[(int)CaSide.CurrentSide]; + } + set { + m_af[(int)CaSide.CurrentSide] = value; + m_fInit = true; + } + } + + public bool Enemies { + get { + return m_af[(int)CaSide.Enemies]; + } + set { + m_af[(int)CaSide.Enemies] = value; + m_fInit = true; + } + } + + public bool Allies { + get { + return m_af[(int)CaSide.Allies]; + } + set { + m_af[(int)CaSide.Allies] = value; + m_fInit = true; + } + } + + public bool Side1 { + get { + return m_af[(int)CaSide.Side1]; + } + set { + m_af[(int)CaSide.Side1] = value; + m_fInit = true; + } + } + + public bool Side2 { + get { + return m_af[(int)CaSide.Side2]; + } + set { + m_af[(int)CaSide.Side2] = value; + m_fInit = true; + } + } + + public bool Side3 { + get { + return m_af[(int)CaSide.Side3]; + } + set { + m_af[(int)CaSide.Side3] = value; + m_fInit = true; + } + } + + public bool Side4 { + get { + return m_af[(int)CaSide.Side4]; + } + set { + m_af[(int)CaSide.Side4] = value; + m_fInit = true; + } + } + + public bool AllSides { + get { + return m_af[(int)CaSide.AllSides]; + } + set { + m_af[(int)CaSide.AllSides] = value; + m_fInit = true; + } + } + + public bool SideNeutral { + get { + return m_af[(int)CaSide.SideNeutral]; + } + set { + m_af[(int)CaSide.SideNeutral] = value; + m_fInit = true; + } + } + } + + // WARNING: This enum must match exactly the one in ht.h + // WARNING: these elements cannot be reordered without breaking existing levels + + public enum UnitType { // ut + kutNone = -1, + + // Infantry Units (NOTE: this is the order they will appear in the build form) + [DisplayName("Security Guard")] + kutShortRangeInfantry, + [DisplayName("Rocket Trooper")] + kutLongRangeInfantry, + [DisplayName("Corporate Raider")] + kutTakeoverSpecialist, + + // Vehicle Units (NOTE: this is the order they will appear in the build form) + [DisplayName("Eagle")] + kutMachineGunVehicle, + [DisplayName("Broadsword")] + kutLightTank, + [DisplayName("Hydra")] + kutRocketVehicle, + [DisplayName("Liberator")] + kutMediumTank, + [DisplayName("Bullpup")] + kutGalaxMiner, + [DisplayName("Dominion")] + kutMobileHeadquarters, + + // Structures (NOTE: this is the order they will appear in the build form) + [DisplayName("Power Generator")] + kutReactor, + [DisplayName("Galaxite Processor")] + kutProcessor, + [DisplayName("Galaxite Warehouse")] + kutWarehouse, + [DisplayName("Human Resource Center")] + kutHumanResourceCenter, + [DisplayName("Vehicle Transport Station")] + kutVehicleTransportStation, + [DisplayName("Surveillance Center")] + kutRadar, + [DisplayName("Research Center")] + kutResearchCenter, + [DisplayName("Headquarters")] + kutHeadquarters, + + // Special structures that like to kill + [DisplayName("Gatling Tower")] + kutMachineGunTower, + [DisplayName("Rocket Tower")] + kutRocketTower, + + [DisplayName("Andy")] + kutAndy, + [DisplayName("Cyclops")] + kutArtillery, + [DisplayName("Replicator")] + kutReplicator, + [DisplayName("Fox")] + kutFox, + + kutMax = kutFox + 1, + } + + // WARNING: This enum must match exactly the one in ht.h + + [Flags] + public enum UnitMask { + kumShortRangeInfantry = 1 << UnitType.kutShortRangeInfantry, + kumLongRangeInfantry = 1 << UnitType.kutLongRangeInfantry, + kumTakeoverSpecialist = 1 << UnitType.kutTakeoverSpecialist, + kumAndy = 1 << UnitType.kutAndy, + kumFox = 1 << UnitType.kutFox, + kumInfantry = kumShortRangeInfantry | kumLongRangeInfantry | kumTakeoverSpecialist, + + kumGalaxMiner = 1 << UnitType.kutGalaxMiner, + kumLightTank = 1 << UnitType.kutLightTank, + kumMediumTank = 1 << UnitType.kutMediumTank, + kumMachineGunVehicle = 1 << UnitType.kutMachineGunVehicle, + kumRocketVehicle = 1 << UnitType.kutRocketVehicle, + kumMobileHeadquarters = 1 << UnitType.kutMobileHeadquarters, + kumArtillery = 1 << UnitType.kutArtillery, + kumVehicles = kumGalaxMiner | kumLightTank | kumMediumTank | kumMachineGunVehicle | + kumRocketVehicle | kumMobileHeadquarters | kumArtillery, + kumMobileUnits = kumInfantry | kumVehicles, + + kumHumanResourceCenter = 1 << UnitType.kutHumanResourceCenter, + kumReactor = 1 << UnitType.kutReactor, + kumProcessor = 1 << UnitType.kutProcessor, + kumHeadquarters = 1 << UnitType.kutHeadquarters, + kumResearchCenter = 1 << UnitType.kutResearchCenter, + kumVehicleTransportStation = 1 << UnitType.kutVehicleTransportStation, + kumRadar = 1 << UnitType.kutRadar, + kumWarehouse = 1 << UnitType.kutWarehouse, + kumReplicator = 1 << UnitType.kutReplicator, + + kumMachineGunTower = 1 << UnitType.kutMachineGunTower, + kumRocketTower = 1 << UnitType.kutRocketTower, + kumTowers = kumMachineGunTower | kumRocketTower, + + kumStructures = kumHumanResourceCenter | kumReactor | kumProcessor | kumHeadquarters | + kumResearchCenter | kumVehicleTransportStation | kumRadar | kumWarehouse | kumMachineGunTower | + kumRocketTower, + kumFactories = kumHeadquarters | kumVehicleTransportStation | kumHumanResourceCenter, + kumAll = kumInfantry | kumVehicles | kumStructures, + kumNewAll = kumAll | kumAndy | kumFox | kumReplicator, + kumNone = 0, + } + +#if false // nobody is using this + [Serializable] + public class CaTypeUnit : CaType { + UnitType m_ut; + string m_strName; + + public CaTypeUnit() { + m_ut = UnitType.kutNone; + m_strName = ""; + m_fInit = true; + } + + public override string ToString() { + if (m_strName != "") + return m_strName; + if (m_ut == UnitType.kutNone) + return "unit"; + return m_ut.ToString(); + } + + public override string ToSaveString() { + if (m_strName != "") + return m_strName; + return GetSaveString(m_ut); + } + + public UnitType Unit { + get { + return m_ut; + } + set { + m_ut = value; + } + } + + public string Name { + get { + return m_strName; + } + set { + m_strName = value; + } + } + } +#endif + + public enum ModifierType { Set, Add, Subtract }; + + [Serializable] + public class CaTypeModifier : CaType { + ModifierType m_mod; + + public CaTypeModifier() { + m_mod = ModifierType.Set; + m_fInit = true; + } + + public override string ToString() { + return m_mod.ToString(); + } + + public override string FromSaveString(string strArgs, bool fLast) { + Regex re = new Regex(@"^(?\d+)(?.*)$"); + Match m = re.Match(strArgs); + Modifier = (ModifierType)int.Parse(m.Groups["mod"].Value); + return m.Groups["end"].Value; + } + + public override string ToSaveString() { + return GetSaveString(m_mod); + } + + public ModifierType Modifier { + get { + return m_mod; + } + set { + m_mod = value; + } + } + } + + [Serializable] + public class CaTypeOneSide : CaType { + Side m_side; + + public CaTypeOneSide() { + m_side = Side.sideNeutral; + m_fInit = true; + } + + public override string ToString() { + return m_side.ToString(); + } + + public override string FromSaveString(string strArgs, bool fLast) { + Regex re = new Regex(@"^(?\d+)(?.*)$"); + Match m = re.Match(strArgs); + Side = (Side)int.Parse(m.Groups["side"].Value); + return m.Groups["end"].Value; + } + + public override string ToSaveString() { + return GetSaveString(m_side); + } + + public Side Side { + get { + return m_side; + } + set { + m_side = value; + } + } + } + +#if false // nobody is using this + public enum ResourceType { Credits }; + + [Serializable] + public class CaTypeResource : CaType { + ResourceType m_res; + + public CaTypeResource() { + m_res = ResourceType.Credits; + m_fInit = true; + } + + public override string ToString() { + return m_res.ToString(); + } + + public override string ToSaveString() { + return GetSaveString(m_res); + } + + public ResourceType Modifier { + get { + return m_res; + } + set { + m_res = value; + } + } + } +#endif + + public enum WinLoseType { None, Win, Lose }; + + [Serializable] + public class CaTypeWinLose : CaType { + WinLoseType m_wlt; + + public CaTypeWinLose() { + m_wlt = WinLoseType.None; + } + + public override string ToString() { + if (!m_fInit) + return "WinOrLose"; + return m_wlt.ToString(); + } + + public override string FromSaveString(string strArgs, bool fLast) { + Regex re = new Regex(@"^(?\d+)(?.*)$"); + Match m = re.Match(strArgs); + Result = (WinLoseType)int.Parse(m.Groups["wlt"].Value); + return m.Groups["end"].Value; + } + + public override string ToSaveString() { + return GetSaveString(m_wlt); + } + + public WinLoseType Result { + get { + return m_wlt; + } + set { + m_wlt = value; + m_fInit = (m_wlt != WinLoseType.None); + } + } + } + + public enum OnOffType { None, On, Off }; + + [Serializable] + public class CaTypeOnOff : CaType { + OnOffType m_oot; + + public CaTypeOnOff() { + m_oot = OnOffType.None; + } + + public override string ToString() { + if (!m_fInit) + return "OnOrOff"; + return m_oot.ToString(); + } + + public override string FromSaveString(string strArgs, bool fLast) { + Regex re = new Regex(@"^(?\d+)(?.*)$"); + Match m = re.Match(strArgs); + Result = (OnOffType)int.Parse(m.Groups["oot"].Value); + return m.Groups["end"].Value; + } + + public override string ToSaveString() { + return GetSaveString(m_oot); + } + + public OnOffType Result { + get { + return m_oot; + } + set { + m_oot = value; + m_fInit = (m_oot != OnOffType.None); + } + } + } + public enum SmallLargeType { None, SmallBottom, Large, SmallTop }; + + [Serializable] + public class CaTypeSmallLarge : CaType { + SmallLargeType m_slt; + + public CaTypeSmallLarge() { + m_slt = SmallLargeType.None; + } + + public override string ToString() { + if (!m_fInit) + return "SmallOrLarge"; + return m_slt.ToString(); + } + + public override string FromSaveString(string strArgs, bool fLast) { + Regex re = new Regex(@"^(?\d+)(?.*)$"); + Match m = re.Match(strArgs); + Result = (SmallLargeType)int.Parse(m.Groups["slt"].Value); + return m.Groups["end"].Value; + } + + public override string ToSaveString() { + return GetSaveString(m_slt); + } + + public SmallLargeType Result { + get { + return m_slt; + } + set { + m_slt = value; + m_fInit = (m_slt != SmallLargeType.None); + } + } + } + + public enum MoreCloseType { None, More, Close }; + + [Serializable] + public class CaTypeMoreClose : CaType { + MoreCloseType m_mct; + + public CaTypeMoreClose() { + m_mct = MoreCloseType.None; + } + + public override string ToString() { + if (!m_fInit) + return "MoreOrCloseButton"; + return m_mct.ToString(); + } + + public override string FromSaveString(string strArgs, bool fLast) { + Regex re = new Regex(@"^(?\d+)(?.*)$"); + Match m = re.Match(strArgs); + Result = (MoreCloseType)int.Parse(m.Groups["mct"].Value); + return m.Groups["end"].Value; + } + + public override string ToSaveString() { + return GetSaveString(m_mct); + } + + public MoreCloseType Result { + get { + return m_mct; + } + set { + m_mct = value; + m_fInit = (m_mct != MoreCloseType.None); + } + } + } + + public enum ModifyCountdownType { None, Stop, Resume, Show, Hide }; + + [Serializable] + public class CaTypeModifyCountdown : CaType { + ModifyCountdownType m_mct; + + public CaTypeModifyCountdown() { + m_mct = ModifyCountdownType.None; + } + + public override string ToString() { + if (!m_fInit) + return "Stop/Resume/Show/Hide"; + return m_mct.ToString(); + } + + public override string FromSaveString(string strArgs, bool fLast) { + Regex re = new Regex(@"^(?\d+)(?.*)$"); + Match m = re.Match(strArgs); + int n = int.Parse(m.Groups["mct"].Value); + // res.h switches around the last two values of the enum + // unfortunately, so when importing it is necessary to switch + // them back. + if (n == 3) { + n = 4; + } else if (n == 4) { + n = 3; + } + Result = (ModifyCountdownType)n; + return m.Groups["end"].Value; + } + + public override string ToSaveString() { + return GetSaveString(m_mct); + } + + public ModifyCountdownType Result { + get { + return m_mct; + } + set { + m_mct = value; + m_fInit = (m_mct != ModifyCountdownType.None); + } + } + } + + public enum ModifyNumberType { None, Set, Add, Subtract }; + + [Serializable] + public class CaTypeModifyNumber : CaType { + ModifyNumberType m_mnt; + + public CaTypeModifyNumber() { + m_mnt = ModifyNumberType.None; + } + + public override string ToString() { + if (!m_fInit) + return "Set/Add/Subtract"; + return m_mnt.ToString(); + } + + public override string FromSaveString(string strArgs, bool fLast) { + Regex re = new Regex(@"^(?\d+)(?.*)$"); + Match m = re.Match(strArgs); + Result = (ModifyNumberType)int.Parse(m.Groups["mnt"].Value); + return m.Groups["end"].Value; + } + + public override string ToSaveString() { + return GetSaveString(m_mnt); + } + + public ModifyNumberType Result { + get { + return m_mnt; + } + set { + m_mnt = value; + m_fInit = (m_mnt != ModifyNumberType.None); + } + } + } + + public enum CreateType { None, Build, Spawn }; + + [Serializable] + public class CaTypeCreate : CaType { + CreateType m_bs; + + public CaTypeCreate() { + m_bs = CreateType.None; + } + + public override string ToString() { + if (!m_fInit) + return "Build/Spawn"; + return m_bs.ToString(); + } + + public override string FromSaveString(string strArgs, bool fLast) { + Regex re = new Regex(@"^(?\d+)(?.*)$"); + Match m = re.Match(strArgs); + Result = (CreateType)int.Parse(m.Groups["bs"].Value); + return m.Groups["end"].Value; + } + + public override string ToSaveString() { + return GetSaveString(m_bs); + } + + public CreateType Result { + get { + return m_bs; + } + set { + m_bs = value; + m_fInit = (m_bs != CreateType.None); + } + } + } + + public enum CharacterType { None, Andy, Jana, Olstrom, Fox, ACME_Security, OMNI_Security, Anonymous, Blank }; + + [Serializable] + public class CaTypeCharacter : CaType { + CharacterType m_cht; + + public CaTypeCharacter() { + m_cht = CharacterType.None; + } + + public override string ToString() { + if (m_cht == CharacterType.None) + return "character"; + return m_cht.ToString(); + } + + public override string FromSaveString(string strArgs, bool fLast) { + Regex re = new Regex(@"^(?\d+)(?.*)$"); + Match m = re.Match(strArgs); + Result = (CharacterType)int.Parse(m.Groups["cht"].Value); + return m.Groups["end"].Value; + } + + public override string ToSaveString() { + return GetSaveString(m_cht); + } + + public CharacterType Result { + get { + return m_cht; + } + set { + m_cht = value; + m_fInit = true; + } + } + } + + [Serializable] + public class CaTypeText : CaType { + string m_str; + + public CaTypeText() { + m_str = ""; + } + + public override string ToString() { + if (m_str == "") + return "text"; + return m_str; + } + + public override string FromSaveString(string strArgs, bool fLast) { + if (fLast) { + Text = strArgs; + return ""; + } else { + Text = strArgs.Split(',')[0]; + return strArgs.Substring(Text.Length); + } + } + + public override string ToSaveString() { + return m_str; + } + + public string Text { + get { + return m_str; + } + set { + m_str = value; + m_fInit = true; + } + } + } + + [Serializable] + public class CaTypeRichText : CaTypeText { + public CaTypeRichText() { + } + + public override bool EditProperties() { + string str = EditRichTextForm.DoModal("Text", Text); + if (str == null) + return false; + m_fInit = true; + Text = str; + return true; + } + } + + [Serializable] + public class CaTypeUnitType : CaType { + UnitType m_ut; + + public CaTypeUnitType() { + m_ut = UnitType.kutNone; + } + + public override string ToString() { + if (!m_fInit) + return "unit type"; + return Helper.GetDisplayName(typeof(UnitType), m_ut.ToString()); + } + + public override string FromSaveString(string strArgs, bool fLast) { + Regex re = new Regex(@"^(?\d+)(?.*)$"); + Match m = re.Match(strArgs); + UnitType = (UnitType)int.Parse(m.Groups["ut"].Value); + return m.Groups["end"].Value; + } + + public override string ToSaveString() { + return m_ut.ToString(); + } + + public UnitType UnitType { + get { + return m_ut; + } + set { + m_ut = value; + m_fInit = m_ut != UnitType.kutNone; + } + } + + StringCollection GetUnitTypeNames() { + StringCollection strc = new StringCollection(); + UnitMask um = UnitMask.kumNewAll; + uint umT = 1; + for (int i = 0; i < 32; i++) { + if (((uint)um & umT) != 0) { + UnitType ut = (UnitType)i; + strc.Add(Helper.GetDisplayName(typeof(UnitType), ut.ToString())); + } + umT <<= 1; + } + + return strc; + } + + public override bool EditProperties() { + StringCollection strc = GetUnitTypeNames(); + string strName = m_ut != UnitType.kutNone ? Helper.GetDisplayName(typeof(UnitType), m_ut.ToString()) : ""; + int n = PickListForm.DoModal("Unit Type", strName, strc); + if (n == -1) + return false; + LevelDoc lvld = (LevelDoc)DocManager.GetActiveDocument(typeof(LevelDoc)); + m_ut = (UnitType)n; + m_fInit = true; + return true; + } + } + + [Serializable] + public class CaTypeUnitTypes : CaType, ISerializable { + private UnitMask m_um; + + public CaTypeUnitTypes() { + m_um = UnitMask.kumNone; + } + + public CaTypeUnitTypes(UnitMask um) { + m_um = um; + } + + public override string FromSaveString(string strArgs, bool fLast) { + Regex re = new Regex(@"^(?(-)?\d+)(?.*)$"); + Match m = re.Match(strArgs); + UnitMask = (UnitMask)int.Parse(m.Groups["um"].Value); + return m.Groups["end"].Value; + } + + public override string ToSaveString() { + return ((uint)m_um).ToString(); + } + + // Begin backward compatibility goo + + // ISerializable implementation + + private enum OldUnitType { + None, + AnyUnit, + Infantry, + Buildings, + Factories, + MachineGunInfantry, + RocketInfantry, + TakeoverSpecialist, + Reactor, + Processor, + HumanResourceCenter, + ResearchCenter, + Headquarters, + VehicleTransportationStation, + Radar, + Warehouse, + MachineGunTower, + RocketTower, + MobileHeadquarters, + LightTank, + MediumTank, + MachineGunVehicle, + RocketVehicle, + GalaxMiner + } + + private static UnitMask[] s_aumFromOldUnitType = { + UnitMask.kumNone, + UnitMask.kumAll, + UnitMask.kumInfantry, + UnitMask.kumStructures, + UnitMask.kumFactories, + UnitMask.kumShortRangeInfantry, + UnitMask.kumLongRangeInfantry, + UnitMask.kumTakeoverSpecialist, + UnitMask.kumReactor, + UnitMask.kumProcessor, + UnitMask.kumHumanResourceCenter, + UnitMask.kumResearchCenter, + UnitMask.kumHeadquarters, + UnitMask.kumVehicleTransportStation, + UnitMask.kumRadar, + UnitMask.kumWarehouse, + UnitMask.kumMachineGunTower, + UnitMask.kumRocketTower, + UnitMask.kumMobileHeadquarters, + UnitMask.kumLightTank, + UnitMask.kumMediumTank, + UnitMask.kumMachineGunVehicle, + UnitMask.kumRocketVehicle, + UnitMask.kumGalaxMiner, + }; + + private CaTypeUnitTypes(SerializationInfo info, StreamingContext context) { + try { + m_um = 0; + OldUnitType[] aut; + aut = (OldUnitType[])info.GetValue("m_aut", typeof(OldUnitType[])); + for (int i = 0; i < aut.Length; i++) { + UnitMask umT = s_aumFromOldUnitType[(int)aut[i]]; + m_um = (UnitMask)(((int)m_um) | (int)umT); + } + } catch (SerializationException) { + m_um = (UnitMask)info.GetValue("m_um", typeof(UnitMask)); + } + m_fInit = true; + } + + public void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { + info.AddValue("m_um", m_um); + } + // End backward compatibility goo + + + public override CaType Clone() { + return (CaType)MemberwiseClone(); + } + + public override string ToString() { + if (!m_fInit) { + return "unit types"; + } else { + switch (m_um) { + case UnitMask.kumAll: + return "Any Unit"; + + case UnitMask.kumInfantry: + return "Any Infantry"; + + case UnitMask.kumFactories: + return "Any Builder"; + + case UnitMask.kumMobileUnits: + return "Any Mobile Unit"; + + case UnitMask.kumVehicles: + return "Any Vehicle"; + + case UnitMask.kumStructures: + return "Any Structure"; + + case UnitMask.kumTowers: + return "Any Tower"; + } + + string str = ""; + for (int i = 0; i < 31; i++) { + if (((uint)m_um & (1 << i)) != 0) { + if (str.Length != 0) + str += ", "; + str += Helper.GetDisplayName(typeof(UnitType), ((UnitType)i).ToString()); + } + } + return str; + } + } + + public override bool EditProperties() { + CaTypeUnitTypesForm frm = new CaTypeUnitTypesForm(m_um); + if (frm.ShowDialog() == DialogResult.OK) { + UnitMask = frm.UnitMask; + } + return true; + } + + public UnitMask UnitMask { + get { + return m_um; + } + set { + m_um = value; + m_fInit = (m_um != UnitMask.kumNone); + } + } + + } + + [Serializable] + public class CaTypeSwitch : CaType, ISerializable { + static ArrayList s_alsFixup; + Switch m_sw; + string m_strName; + + public CaTypeSwitch() { + m_sw = null; + } + + // Begin backward compatibility goo + + // ISerializable implementation + + private CaTypeSwitch(SerializationInfo info, StreamingContext context) { + try { + m_sw = (Switch)info.GetValue("m_sw", typeof(Switch)); + } catch (SerializationException) { + m_strName = info.GetString("m_str"); + AddToFixupList(this); + } + m_fInit = true; + } + + public void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { + info.AddValue("m_sw", m_sw); + } + + // This sucks. There has to be a better way. + + public static void InitFixupList() { + s_alsFixup = new ArrayList(); + } + + public static void AddToFixupList(CaTypeSwitch catSwitch) { + s_alsFixup.Add(catSwitch); + } + + public static void Fixup(LevelDoc lvld) { + foreach (CaTypeSwitch catSwitch in s_alsFixup) { + catSwitch.m_sw = lvld.SwitchManager[catSwitch.m_strName]; + if (catSwitch.m_sw == null) { + catSwitch.m_sw = new Switch(catSwitch.m_strName); + lvld.SwitchManager.AddSwitch(catSwitch.m_sw); + catSwitch.m_strName = null; + } + } + s_alsFixup = null; + } + + // End backward compatibility goo + + public override string ToString() { + if (!m_fInit) + return "switch"; + return m_sw.Name; + } + + public override string FromSaveString(string strArgs, bool fLast) { + Regex re = new Regex(@"^(?(-)?\d+)(?.*)$"); + Match m = re.Match(strArgs); + int index = int.Parse(m.Groups["index"].Value); + StringCollection strc = GetSwitchNames(); + if (index >= 0 && index < strc.Count) { + LevelDoc lvld = (LevelDoc)DocManager.GetActiveDocument(typeof(LevelDoc)); + m_sw = lvld.SwitchManager[strc[index]]; + m_fInit = true; + } + return m.Groups["end"].Value; + } + + public override string ToSaveString() { + LevelDoc lvld = (LevelDoc)DocManager.GetActiveDocument(typeof(LevelDoc)); + return lvld.SwitchManager.Items.IndexOf(m_sw).ToString(); + } + + public static StringCollection GetSwitchNames() { + LevelDoc lvld = (LevelDoc)DocManager.GetActiveDocument(typeof(LevelDoc)); + StringCollection strc = new StringCollection(); + foreach (Switch sw in lvld.SwitchManager.Items) + strc.Add(sw.Name); + return strc; + } + + public override bool EditProperties() { + StringCollection strc = GetSwitchNames(); + string strName = m_sw != null ? m_sw.Name : ""; + Switch sw = SwitchesForm.DoModal("Switch", strName, strc); + if (sw == null) + return false; + m_sw = sw; + m_fInit = true; + return true; + } + } + + [Serializable] + public class CaTypeCounter : CaType { + Counter m_ctr; + + public CaTypeCounter() { + m_ctr = null; + } + + public override string ToString() { + if (!m_fInit) + return "Counter"; + return m_ctr.Name; + } + + public override string FromSaveString(string strArgs, bool fLast) { + Regex re = new Regex(@"^(?(-)?\d+)(?.*)$"); + Match m = re.Match(strArgs); + int index = int.Parse(m.Groups["index"].Value); + StringCollection strc = GetCounterNames(); + if (index >= 0 && index < strc.Count) { + m_ctr.Name = strc[index]; + m_fInit = true; + } + return m.Groups["end"].Value; + } + + public override string ToSaveString() { + LevelDoc lvld = (LevelDoc)DocManager.GetActiveDocument(typeof(LevelDoc)); + return lvld.CounterManager.Items.IndexOf(m_ctr).ToString(); + } + + public static StringCollection GetCounterNames() { + LevelDoc lvld = (LevelDoc)DocManager.GetActiveDocument(typeof(LevelDoc)); + StringCollection strc = new StringCollection(); + foreach (Counter ctr in lvld.CounterManager.Items) + strc.Add(ctr.Name); + return strc; + } + + public override bool EditProperties() { + StringCollection strc = GetCounterNames(); + string strName = m_ctr != null ? m_ctr.Name : ""; + Counter ctr = CountersForm.DoModal("Counter", strName, strc); + if (ctr == null) + return false; + m_ctr = ctr; + m_fInit = true; + return true; + } + } + + [Serializable] + public class CaTypeArea : CaType { + string m_strArea; + + public CaTypeArea() { + m_strArea = ""; + } + + public CaTypeArea(string strArea) { + Area = strArea; + } + + public override string ToString() { + if (!m_fInit) + return "area"; + return m_strArea; + } + + public override string ToSaveString() { + return GetArea(m_strArea).ToString(); + } + + public override string FromSaveString(string strArgs, bool fLast) { + Regex re = new Regex(@"^(?(-)?\d+)(?.*)$"); + Match m = re.Match(strArgs); + int index = int.Parse(m.Groups["index"].Value); + string strName = GetAreaNameFromIndex(index); + if (strName != null) { + Area = strName; + } + return m.Groups["end"].Value; + } + + public string Area { + get { + return m_strArea; + } + set { + m_strArea = value; + m_fInit = (m_strArea != ""); + } + } + + // UNDONE: Hmm.. looks like an AreaManager would be appropriate + + static public int VirtualAreaCount { + get { + return 1; + } + } + + static public string GetAreaNameFromIndex(int index) { + StringCollection strc = GetAreaNames(); + int n = index + VirtualAreaCount; + if (n < 0 || n > strc.Count) { + return null; + } + return strc[n]; + } + + static public int GetArea(string strArea) { + StringCollection strc = GetAreaNames(); + + // Corrects for the 1 virtual area we have so far "[LastDiscovery]" + + return strc.IndexOf(strArea) - VirtualAreaCount; + } + + static public StringCollection GetAreaNames() { + LevelDoc lvld = (LevelDoc)DocManager.GetActiveDocument(typeof(LevelDoc)); + IMapItem[] ami = lvld.MapItems; + ArrayList al = new ArrayList(); + foreach (IMapItem mi in ami) { + if (!(mi is Area)) + continue; + al.Add(((Area)mi).Name); + } + al.Add("[LastDiscovery]"); + al.Sort(); + + StringCollection strc = new StringCollection(); + foreach (string str in al) + strc.Add(str); + return strc; + } + + public override bool EditProperties() { + StringCollection strc = GetAreaNames(); + int n = PickListForm.DoModal("Area", m_strArea, strc); + if (n == -1) + return false; + m_strArea = strc[n]; + if (m_strArea == "") + return false; + m_fInit = true; + return true; + } + } + + [Serializable] + public class CaTypeUnitGroup : CaType { + UnitGroup m_ug; + + public CaTypeUnitGroup() { + m_ug = null; + } + + public override string ToString() { + if (!m_fInit) + return "unit group"; + return m_ug.Name; + } + + public override string FromSaveString(string strArgs, bool fLast) { + Regex re = new Regex(@"^(?(-)?\d+)(?.*)$"); + Match m = re.Match(strArgs); + int index = int.Parse(m.Groups["index"].Value); + LevelDoc lvld = (LevelDoc)DocManager.GetActiveDocument(typeof(LevelDoc)); + if (index >= 0 && index < lvld.UnitGroupManager.Items.Count) { + m_ug = (UnitGroup)lvld.UnitGroupManager.Items[index]; + m_fInit = true; + } + return m.Groups["end"].Value; + } + + public override string ToSaveString() { + LevelDoc lvld = (LevelDoc)DocManager.GetActiveDocument(typeof(LevelDoc)); + return lvld.UnitGroupManager.Items.IndexOf(m_ug).ToString(); + } + + StringCollection GetUnitGroupNames() { + LevelDoc lvld = (LevelDoc)DocManager.GetActiveDocument(typeof(LevelDoc)); + StringCollection strc = new StringCollection(); + foreach (UnitGroup ug in lvld.UnitGroupManager.Items) + strc.Add(ug.Name); + return strc; + } + + public override bool EditProperties() { + StringCollection strc = GetUnitGroupNames(); + string strName = m_ug != null ? m_ug.Name : ""; + int n = PickListForm.DoModal("Unit Group", strName, strc); + if (n == -1) + return false; + LevelDoc lvld = (LevelDoc)DocManager.GetActiveDocument(typeof(LevelDoc)); + m_ug = (UnitGroup)lvld.UnitGroupManager.Items[n]; + m_fInit = true; + return true; + } + + public UnitGroup UnitGroup { + get { + return m_ug; + } + } + } + + // WARNING: This enum must match exactly the one in ht.h + // WARNING: these elements cannot be reordered without breaking existing levels + + public enum UpgradeType { // upgt + kupgtNone = -1, + + [DisplayName("Advanced HRC")] + kupgtAdvancedHRC, + [DisplayName("Advanced VTS")] + kupgtAdvancedVTS, + [DisplayName("Increaed Bullpup Speed")] + kupgtIncreasedBullpupSpeed, + + kupgtMax = kupgtIncreasedBullpupSpeed + 1, + } + + // WARNING: This enum must match exactly the one in ht.h + + [Flags] + public enum UpgradeMask { + kupgmAdvancedHRC = 1 << UpgradeType.kupgtAdvancedHRC, + kupgmAdvancedVTS = 1 << UpgradeType.kupgtAdvancedVTS, + kupgmIncreasedBullpupSpeed = 1 << UpgradeType.kupgtIncreasedBullpupSpeed, + kupgmAll = kupgmAdvancedHRC | kupgmAdvancedVTS | kupgmIncreasedBullpupSpeed, + kupgmNone = 0, + } + + [Serializable] + public class CaTypeUpgradeTypes : CaType { + private UpgradeMask m_upgm; + + public CaTypeUpgradeTypes() { + m_upgm = UpgradeMask.kupgmNone; + } + + public override string ToString() { + if (!m_fInit) { + return "upgrade types"; + } else { + string str = ""; + for (int i = 0; i < 31; i++) { + if (((uint)m_upgm & (1 << i)) != 0) { + if (str.Length != 0) + str += ", "; + str += Helper.GetDisplayName(typeof(UpgradeType), ((UpgradeType)i).ToString()); + } + } + return str; + } + } + + public override string FromSaveString(string strArgs, bool fLast) { + Regex re = new Regex(@"^(?\d+)(?.*)$"); + Match m = re.Match(strArgs); + UpgradeMask = (UpgradeMask)uint.Parse(m.Groups["upgm"].Value); + return m.Groups["end"].Value; + } + + public override string ToSaveString() { + return ((uint)m_upgm).ToString(); + } + + public override bool EditProperties() { + CaTypeUpgradeTypesForm frm = new CaTypeUpgradeTypesForm(m_upgm); + if (frm.ShowDialog() == DialogResult.OK) { + UpgradeMask = frm.UpgradeMask; + } + return true; + } + + public UpgradeMask UpgradeMask { + get { + return m_upgm; + } + set { + m_upgm = value; + m_fInit = (m_upgm != UpgradeMask.kupgmNone); + } + } + } +} diff --git a/m/Counter.cs b/m/Counter.cs new file mode 100644 index 0000000..8ded199 --- /dev/null +++ b/m/Counter.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections; +using SpiffLib; +using System.Runtime.Serialization; + +namespace m { + [Serializable] + public class CounterManager { + ArrayList m_alsCounters; + bool m_fModified; + + public CounterManager() { + m_fModified = false; + m_alsCounters = new ArrayList(); + } + + public ArrayList Items { + get { + return m_alsCounters; + } + } + + public Counter this[string strName] { + get { + foreach (Counter ctr in m_alsCounters) { + if (ctr.Name == strName) + return ctr; + } + return null; + } + } + + void SetModified() { + m_fModified = true; + } + + public void ClearModified() { + m_fModified = false; + } + + public bool IsModified() { + return m_fModified; + } + + public Counter[] GetCounterList() { + return (Counter[])m_alsCounters.ToArray(typeof(Counter)); + } + + public void AddCounter(Counter ctr) { + m_alsCounters.Add(ctr); + SetModified(); + } + + public void RemoveCounter(Counter ctr) { + m_alsCounters.Remove(ctr); + SetModified(); + } + + public void ModifyCounter(Counter ctrModify, Counter ctr) { + int n = m_alsCounters.IndexOf(ctrModify); + if (n >= 0) + m_alsCounters[n] = ctr; + SetModified(); + } + } + + [Serializable] + public class Counter : ISerializable { + string m_strName; + + public Counter(string strName) { + m_strName = strName; + } + + // ISerializable methods for backwards compatibility + + private Counter(SerializationInfo info, StreamingContext context) { + m_strName = info.GetString("m_strName"); + } + + public void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { + info.AddValue("m_strName", m_strName); + } + + public virtual Counter Clone() { + Counter ctr = new Counter(m_strName); + return ctr; + } + + public string Name { + get { + return m_strName; + } + set { + m_strName = value; + } + } + } +} diff --git a/m/CountersForm.cs b/m/CountersForm.cs new file mode 100644 index 0000000..2546cf3 --- /dev/null +++ b/m/CountersForm.cs @@ -0,0 +1,228 @@ +using System; +using System.Drawing; +using System.Collections; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Windows.Forms; + +namespace m +{ + /// + /// Summary description for EditCounterForm. + /// + public class CountersForm : System.Windows.Forms.Form + { + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Button buttonOk; + private System.Windows.Forms.Button buttonCancel; + private System.Windows.Forms.ListBox listBox1; + private System.Windows.Forms.Button buttonNew; + private System.Windows.Forms.Button buttonModify; + private System.Windows.Forms.Button buttonDelete; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + public CountersForm(string strTitle, string strCurrent, StringCollection strc) + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + // + + foreach (string str in strc) + listBox1.Items.Add(str); + listBox1.SelectedIndex = strCurrent == "" ? -1 : listBox1.Items.IndexOf(strCurrent); + Text = strTitle; + label1.Text = label1.Text.Replace("$1", strTitle); + } + + public Counter GetSelectedCounter() { + if (listBox1.SelectedIndex == -1) + return null; + CounterManager ctrm = ((LevelDoc)DocManager.GetActiveDocument(typeof(LevelDoc))).CounterManager; + return ctrm[(string)listBox1.SelectedItem]; + } + + static public Counter DoModal(string strTitle, string strCurrent, StringCollection strc) { + CountersForm frm = new CountersForm(strTitle, strCurrent, strc); + DialogResult res = frm.ShowDialog(); + if (res == DialogResult.Cancel) + return null; + return frm.GetSelectedCounter(); + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.label1 = new System.Windows.Forms.Label(); + this.buttonOk = new System.Windows.Forms.Button(); + this.buttonCancel = new System.Windows.Forms.Button(); + this.listBox1 = new System.Windows.Forms.ListBox(); + this.buttonNew = new System.Windows.Forms.Button(); + this.buttonModify = new System.Windows.Forms.Button(); + this.buttonDelete = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // label1 + // + this.label1.Location = new System.Drawing.Point(8, 8); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(160, 16); + this.label1.TabIndex = 0; + this.label1.Text = "Choose one $1:"; + // + // buttonOk + // + this.buttonOk.DialogResult = System.Windows.Forms.DialogResult.OK; + this.buttonOk.Location = new System.Drawing.Point(184, 8); + this.buttonOk.Name = "buttonOk"; + this.buttonOk.TabIndex = 2; + this.buttonOk.Text = "OK"; + this.buttonOk.Click += new System.EventHandler(this.buttonOk_Click); + // + // buttonCancel + // + this.buttonCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.buttonCancel.Location = new System.Drawing.Point(184, 40); + this.buttonCancel.Name = "buttonCancel"; + this.buttonCancel.TabIndex = 2; + this.buttonCancel.Text = "Cancel"; + this.buttonCancel.Click += new System.EventHandler(this.buttonCancel_Click); + // + // listBox1 + // + this.listBox1.Location = new System.Drawing.Point(8, 24); + this.listBox1.Name = "listBox1"; + this.listBox1.Size = new System.Drawing.Size(168, 173); + this.listBox1.Sorted = true; + this.listBox1.TabIndex = 3; + this.listBox1.DoubleClick += new System.EventHandler(this.listBox1_DoubleClick); + // + // buttonNew + // + this.buttonNew.Location = new System.Drawing.Point(184, 96); + this.buttonNew.Name = "buttonNew"; + this.buttonNew.TabIndex = 4; + this.buttonNew.Text = "New..."; + this.buttonNew.Click += new System.EventHandler(this.buttonNew_Click); + // + // buttonModify + // + this.buttonModify.Location = new System.Drawing.Point(184, 128); + this.buttonModify.Name = "buttonModify"; + this.buttonModify.TabIndex = 5; + this.buttonModify.Text = "Modify..."; + this.buttonModify.Click += new System.EventHandler(this.buttonModify_Click); + // + // buttonDelete + // + this.buttonDelete.Location = new System.Drawing.Point(184, 160); + this.buttonDelete.Name = "buttonDelete"; + this.buttonDelete.TabIndex = 6; + this.buttonDelete.Text = "Delete"; + this.buttonDelete.Click += new System.EventHandler(this.buttonDelete_Click); + // + // CountersForm + // + this.AcceptButton = this.buttonOk; + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.CancelButton = this.buttonCancel; + this.ClientSize = new System.Drawing.Size(266, 208); + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.buttonDelete, + this.buttonModify, + this.buttonNew, + this.listBox1, + this.buttonOk, + this.label1, + this.buttonCancel}); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "CountersForm"; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "EditCounterForm"; + this.ResumeLayout(false); + + } + #endregion + + private void buttonOk_Click(object sender, System.EventArgs e) { + DialogResult = DialogResult.OK; + } + + private void buttonCancel_Click(object sender, System.EventArgs e) { + DialogResult = DialogResult.Cancel; + } + + private void listBox1_DoubleClick(object sender, System.EventArgs e) { + DialogResult = DialogResult.OK; + } + + private void buttonNew_Click(object sender, System.EventArgs e) { + string str = EditStringForm.DoModal("New Counter", "New Counter name:", null); + if (str != null) { + CounterManager ctrm = ((LevelDoc)DocManager.GetActiveDocument(typeof(LevelDoc))).CounterManager; + if (ctrm[str] == null) { + ctrm.AddCounter(new Counter(str)); + int i = listBox1.Items.Add(str); + listBox1.SelectedIndex = i; + // UNDONE: doc is modified + } + } + } + + private void buttonModify_Click(object sender, System.EventArgs e) { + string str = (string)listBox1.SelectedItem; + if (str == null) + return; + CounterManager ctrm = ((LevelDoc)DocManager.GetActiveDocument(typeof(LevelDoc))).CounterManager; + Counter ctr = ctrm[str]; + string strNew = EditStringForm.DoModal("Modify Counter", "New Counter name:", str); + if (strNew == null) + return; + if (strNew != str) { + ctr.Name = strNew; + listBox1.Items.Remove(str); + int i = listBox1.Items.Add(strNew); + listBox1.SelectedIndex = i; + // UNDONE: doc is modified + } + } + + private void buttonDelete_Click(object sender, System.EventArgs e) { + string str = (string)listBox1.SelectedItem; + if (str == null) + return; + CounterManager ctrm = ((LevelDoc)DocManager.GetActiveDocument(typeof(LevelDoc))).CounterManager; + Counter ctr = ctrm[str]; + ctrm.RemoveCounter(ctr); + listBox1.Items.Remove(str); + // UNDONE: doc is modified + } + } +} diff --git a/m/Doc.cs b/m/Doc.cs new file mode 100644 index 0000000..d37e672 --- /dev/null +++ b/m/Doc.cs @@ -0,0 +1,501 @@ +using System; +using System.IO; +using System.Collections; +using System.Drawing; +using System.Windows.Forms; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters; +using System.Runtime.Serialization.Formatters.Binary; + +namespace m +{ + public enum Command { Cut, Copy, Paste, Delete }; + + public interface ICommandTarget { + void DispatchCommand(Command cmd); + } + + public class DocManager { + static ArrayList s_alsTemplates = new ArrayList(); + static Form s_frmParent = null; + static ICommandTarget m_cmdt; + + public static void SetCommandTarget(ICommandTarget cmdt) { + m_cmdt = cmdt; + } + + public static ICommandTarget GetCommandTarget() { + return m_cmdt; + } + + public static void SetFrameParent(Form frmParent) { + s_frmParent = frmParent; + } + + public static Form GetFrameParent() { + return s_frmParent; + } + + public static void AddTemplate(DocTemplate doct) { + s_alsTemplates.Add(doct); + } + + public static void RemoveTemplate(DocTemplate doct) { + s_alsTemplates.Remove(doct); + } + + public static DocTemplate FindDocTemplate(Type type) { + foreach (DocTemplate doct in s_alsTemplates) { + if (doct.GetDocumentType() == type) + return doct; + } + return null; + } + + public static Document NewDocument(Type typeDoc, Object[] aobj) { + DocTemplate doct = FindDocTemplate(typeDoc); + return doct.NewDocument(aobj); + } + + public static Document OpenDocument(Type typeDoc) { + DocTemplate doct = FindDocTemplate(typeDoc); + return doct.OpenDocument(); + } + + public static Document OpenDocument(string strFileName) { + string extFile = Path.GetExtension(strFileName); + foreach (DocTemplate doct in s_alsTemplates) { + string extDoc = doct.GetString(DocTemplate.Strings.FilterExt); + if (extFile == "." + extDoc) + return doct.OpenDocument(strFileName); + } + return null; + } + + public static bool SaveAllModified(Type type) { + foreach (DocTemplate doct in s_alsTemplates) { + if (type == null || doct.GetDocumentType() == type) { + if (!doct.SaveAllModified()) + return false; + } + } + return true; + } + + public static bool CloseAllDocuments() { + foreach (DocTemplate doct in s_alsTemplates) { + if (!doct.CloseAllDocuments()) + return false; + } + return true; + } + + public static Document GetActiveDocument(Type typeDoc) { + DocTemplate doct = FindDocTemplate(typeDoc); + return doct.GetActiveDocument(); + } + + public static void SetActiveDocument(Type typeDoc, Document doc) { + DocTemplate doct = FindDocTemplate(typeDoc); + doct.SetActiveDocument(doc); + } + } + + public class DocTemplate { + string[] m_astr; + protected Type m_typeDoc; + protected Type m_typeFrame; + protected Type m_typeView; + protected ArrayList m_alsDocuments; + protected SerializationBinder m_serBinder; + protected Document m_docActive = null; + + public delegate void DocActiveHandler(Document doc); + public event DocActiveHandler DocActive; + public delegate void DocAddedHandler(Document doc); + public event DocAddedHandler DocAdded; + public delegate void DocRemovedHandler(Document doc); + public event DocRemovedHandler DocRemoved; + + public DocTemplate(string[] astr, Type typeDoc, Type typeFrame, Type typeView, SerializationBinder serBinder) { + m_astr = (string[])astr.Clone(); + m_typeDoc = typeDoc; + m_typeFrame = typeFrame; + m_typeView = typeView; + m_alsDocuments = new ArrayList(); + m_serBinder = serBinder; + } + + public Type GetDocumentType() { + return m_typeDoc; + } + + public virtual Document NewDocument(Object[] aobj) { + // Create a new (empty) document + + Object[] aobjT = { this, null, aobj }; + Document doc = (Document)System.Activator.CreateInstance(m_typeDoc, aobjT); + + // Success? + + if (doc == null) + return null; + + // Add to doc list + + AddDocument(doc); + return OpenFinish(doc); + } + + public virtual Document OpenDocument() { + string strExt = GetString(DocTemplate.Strings.FilterExt); + string strFilterName = GetString(DocTemplate.Strings.FilterName); + string strTitle = GetString(DocTemplate.Strings.WindowTitle); + OpenFileDialog frmOpen = new OpenFileDialog(); + frmOpen.DefaultExt = strExt; + frmOpen.Filter = strFilterName + " (*." + strExt + ")|*." + strExt; + frmOpen.Title = "Open " + strTitle; + if (frmOpen.ShowDialog() == DialogResult.Cancel) + return null; + return OpenDocument(frmOpen.FileName); + } + + public virtual Document OpenDocument(string strFile) { + // Already open? If so, return the already open document after incrementing its open count + + Document doc = null; + string strPath = Path.GetFullPath(strFile); + foreach (Document docT in m_alsDocuments) { + string strPathT = docT.GetPath(); + if (strPathT == null) + continue; + if (strPathT.ToLower() == strPath.ToLower()) { + return OpenFinish(docT); + } + } + + // Not already open - open it + + Stream stm = null; + try { + Hashtable ht = new Hashtable(); + ht.Add("DocTemplate", this); + ht.Add("Filename", strFile); + IFormatter fmtr = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.File, ht)); + if (m_serBinder != null) + fmtr.Binder = m_serBinder; + stm = new FileStream(strFile, FileMode.Open, FileAccess.Read, FileShare.Read); + doc = (Document)fmtr.Deserialize(stm); + stm.Close(); + + } catch (SerializationException ex) { + string strErr = String.Format("Failed to load {0}\n{1}", strFile, ex.ToString()); + MessageBox.Show(strErr, "Deserialization Error"); + + if (stm != null) + stm.Close(); + doc = null; + + } catch (System.Reflection.TargetInvocationException ex) { + string strErr = String.Format("Failed to load {0}\n{1}", strFile, ex.ToString()); + MessageBox.Show(strErr, "Deserialization TargetInvocation Error"); + + if (stm != null) + stm.Close(); + doc = null; + } + + // Success? + + if (doc == null) + return null; + + // Add to doc list + + AddDocument(doc); + return OpenFinish(doc); + } + + + Document OpenFinish(Document doc) { + // This doc is being opened + + doc.IncrementOpenCount(); + + // Doc is not modified + + doc.SetModified(false); + + // Make it active + + SetActiveDocument(doc); + + // Create a frame window of the desired type. It might already know what + // view type to create, but parameterize it just in case + + if (m_typeFrame != null) { + Object[] aobjT = { DocManager.GetFrameParent(), doc, m_typeView }; + System.Activator.CreateInstance(m_typeFrame, aobjT); + } + + // The doc is ready to go + + doc.InitDone(); + return doc; + } + + public virtual void AddDocument(Document doc) { + m_alsDocuments.Add(doc); + if (DocAdded != null) + DocAdded(doc); + } + + public virtual void RemoveDocument(Document doc) { + m_alsDocuments.Remove(doc); + if (doc == m_docActive) { + if (m_alsDocuments.Count != 0) { + SetActiveDocument((Document)m_alsDocuments[0]); + } else { + SetActiveDocument(null); + } + } + if (DocRemoved != null) + DocRemoved(doc); + } + + public virtual Document[] GetDocuments() { + return (Document[])m_alsDocuments.ToArray(typeof(Document)); + } + + public enum Strings { WindowTitle, NewFileName, FilterName, FilterExt }; + + public virtual string GetString(Strings enmStr) { + return m_astr[(int)enmStr]; + } + + public virtual bool SaveAllModified() { + foreach (Document doc in m_alsDocuments) { + if (doc.IsModified()) { + if (!doc.Save()) + return false; + } + } + return true; + } + + public virtual bool CloseAllDocuments() { + while (m_alsDocuments.Count != 0) { + Document doc = (Document)m_alsDocuments[0]; + if (!doc.Close()) + return false; + } + return true; + } + + public virtual Document GetActiveDocument() { + return m_docActive; + } + + public virtual void SetActiveDocument(Document docActive) { + if (Globals.PropertyGrid != null) { + if (Globals.PropertyGrid.SelectedObject != null) { + if (Globals.PropertyGrid.SelectedObject.GetType() == m_typeDoc) { + Globals.PropertyGrid.SelectedObject = (object)docActive; + } else { + Globals.PropertyGrid.SelectedObject = null; + } + } + } + if (m_docActive == docActive) + return; + m_docActive = docActive; + if (DocActive != null) + DocActive(docActive); + } + } + + public class Document { + protected DocTemplate m_doct; + protected string m_strDir; + protected string m_strFileName; + protected bool m_fModified; + protected bool m_fIniting; + protected int m_cOpen; + + public delegate void ModifiedChangedHandler(Document doc, bool fModified); + public event ModifiedChangedHandler ModifiedChanged; + public delegate void PathChangedHandler(Document doc); + public event PathChangedHandler PathChanged; + public delegate void OpenCountChangedHandler(Document doc); + public event OpenCountChangedHandler OpenCountChanged; + + public Document(DocTemplate doct, string strFile) { + m_doct = doct; + SetPath(strFile); + m_fModified = false; + m_fIniting = true; + m_cOpen = 0; + } + + public DocTemplate GetDocTemplate() { + return m_doct; + } + + public virtual void InitDone() { + m_fIniting = false; + } + + public bool IsModified() { + return m_fModified; + } + + public void SetModified(bool fModified) { + if (m_fIniting) + return; + if (fModified == m_fModified) + return; + m_fModified = fModified; + if (ModifiedChanged != null) + ModifiedChanged(this, fModified); + } + + public virtual string GetName() { + return m_doct.GetString(DocTemplate.Strings.NewFileName); + } + + public virtual string GetPath() { + if (m_strFileName == null) + return null; + return m_strDir + Path.DirectorySeparatorChar + m_strFileName; + } + + public virtual void SetPath(string strFile) { + string strDir; + string strFileName; + if (strFile == null) { + strDir = null; + strFileName = null; + } else { + strDir = Path.GetDirectoryName(Path.GetFullPath(strFile)); + if (strDir == "") + strDir = "."; + strFileName = Path.GetFileName(strFile); + } + if (strDir != m_strDir || strFileName != m_strFileName) { + m_strDir = strDir; + m_strFileName = strFileName; + if (PathChanged != null) + PathChanged(this); + } + } + + public virtual int GetOpenCount() { + return m_cOpen; + } + + public virtual void DecrementOpenCount() { + m_cOpen--; + if (OpenCountChanged != null) + OpenCountChanged(this); + } + + public virtual void IncrementOpenCount() { + m_cOpen++; + if (OpenCountChanged != null) + OpenCountChanged(this); + } + + public virtual bool Save() { + if (m_strFileName == null) + return SaveAs(null); + return SaveHelper(m_strDir + Path.DirectorySeparatorChar + m_strFileName); + } + + bool SaveHelper(string strFile) { + Stream stm = null; + try { + IFormatter fmtr = new BinaryFormatter(); + stm = new FileStream(strFile, FileMode.Create, FileAccess.Write, FileShare.None); + fmtr.Serialize(stm, this); + stm.Close(); + } catch (Exception ex) { + if (stm != null) + stm.Close(); + MessageBox.Show("Error saving..." + ex.ToString()); + return false; + } + + SetPath(strFile); + SetModified(false); + return true; + } + + public virtual bool SaveAs(string strFile) { + // Have a filename? use it. + + if (strFile != null) { + if (SaveHelper(strFile)) { + SetPath(strFile); + return true; + } + return false; + } + + // Open dialog + + if (m_strFileName != null) + strFile = m_strDir + Path.DirectorySeparatorChar + m_strFileName; + + SaveFileDialog frmSave = new SaveFileDialog(); + + string strExt = m_doct.GetString(DocTemplate.Strings.FilterExt); + string strFilterName = m_doct.GetString(DocTemplate.Strings.FilterName); + string strTitle = m_doct.GetString(DocTemplate.Strings.WindowTitle); + string strFileNew = m_doct.GetString(DocTemplate.Strings.NewFileName); + + frmSave.DefaultExt = strExt; + frmSave.Filter = strFilterName + " (*." + strExt + ")|*." + strExt; + frmSave.Title = "Save " + GetName() + " As..."; + frmSave.FileName = strFile != null ? strFile : strFileNew; + if (frmSave.ShowDialog() == DialogResult.Cancel) + return false; + + return SaveHelper(frmSave.FileName); + } + + public bool Close() { + // Don't close if the open count is not zero yet + + if (GetOpenCount() > 1) { + DecrementOpenCount(); + return true; + } + + // Ask if modified + + if (m_fModified) { + string strName; + string strPath = GetPath(); + if (strPath == null) { + strName = GetName(); + } else { + strName = Path.GetFileName(strPath); + } + switch (MessageBox.Show("Save changes to " + strName + "?", Application.ProductName, MessageBoxButtons.YesNoCancel)) { + case DialogResult.Cancel: + return false; + + case DialogResult.No: + break; + + case DialogResult.Yes: + if (!Save()) + return false; + break; + } + } + m_doct.RemoveDocument(this); + return true; + } + } +} diff --git a/m/EditCommentsForm.cs b/m/EditCommentsForm.cs new file mode 100644 index 0000000..9cb0941 --- /dev/null +++ b/m/EditCommentsForm.cs @@ -0,0 +1,118 @@ +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; + +namespace m +{ + /// + /// Summary description for EditRichTextForm. + /// + public class EditCommentsForm : System.Windows.Forms.Form + { + private System.Windows.Forms.Button buttonOk; + private System.Windows.Forms.Button buttonCancel; + public System.Windows.Forms.TextBox textBox1; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + public EditCommentsForm(string strCurrent) + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + textBox1.Text = strCurrent; + textBox1.Select(0, 0); + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.buttonOk = new System.Windows.Forms.Button(); + this.buttonCancel = new System.Windows.Forms.Button(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.SuspendLayout(); + // + // buttonOk + // + this.buttonOk.Anchor = (System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left); + this.buttonOk.BackColor = System.Drawing.SystemColors.Control; + this.buttonOk.DialogResult = System.Windows.Forms.DialogResult.OK; + this.buttonOk.Location = new System.Drawing.Point(108, 296); + this.buttonOk.Name = "buttonOk"; + this.buttonOk.TabIndex = 1; + this.buttonOk.Text = "Ok"; + // + // buttonCancel + // + this.buttonCancel.Anchor = (System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left); + this.buttonCancel.BackColor = System.Drawing.SystemColors.Control; + this.buttonCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.buttonCancel.Location = new System.Drawing.Point(236, 296); + this.buttonCancel.Name = "buttonCancel"; + this.buttonCancel.TabIndex = 2; + this.buttonCancel.Text = "Cancel"; + // + // textBox1 + // + this.textBox1.AcceptsReturn = true; + this.textBox1.AcceptsTab = true; + this.textBox1.Anchor = (((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right); + this.textBox1.BackColor = System.Drawing.SystemColors.Info; + this.textBox1.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0))); + this.textBox1.Multiline = true; + this.textBox1.Name = "textBox1"; + this.textBox1.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.textBox1.Size = new System.Drawing.Size(418, 288); + this.textBox1.TabIndex = 0; + this.textBox1.Text = ""; + // + // EditCommentsForm + // + this.AcceptButton = this.buttonOk; + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.BackColor = System.Drawing.SystemColors.Info; + this.CancelButton = this.buttonCancel; + this.ClientSize = new System.Drawing.Size(418, 326); + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.textBox1, + this.buttonOk, + this.buttonCancel}); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "EditCommentsForm"; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Comments"; + this.ResumeLayout(false); + + } + #endregion + } +} diff --git a/m/EditCommentsForm.resources b/m/EditCommentsForm.resources new file mode 100644 index 0000000..abf2599 Binary files /dev/null and b/m/EditCommentsForm.resources differ diff --git a/m/EditCommentsForm.resx b/m/EditCommentsForm.resx new file mode 100644 index 0000000..10b3c93 --- /dev/null +++ b/m/EditCommentsForm.resx @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Public + + + EditCommentsForm + + \ No newline at end of file diff --git a/m/EditLevelTextForm.cs b/m/EditLevelTextForm.cs new file mode 100644 index 0000000..cccc399 --- /dev/null +++ b/m/EditLevelTextForm.cs @@ -0,0 +1,369 @@ +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; +using System.IO; +using System.Text; + +namespace m +{ + /// + /// Summary description for EditTextForm. + /// + public class EditLevelTextForm : System.Windows.Forms.Form + { + private string m_strLevelText; + private LevelDoc m_lvld; + public System.Windows.Forms.RichTextBox richTextBox1; + private System.Windows.Forms.MainMenu mainMenu1; + private System.Windows.Forms.MenuItem menuItem1; + private System.Windows.Forms.MenuItem menuItem2; + private System.Windows.Forms.MenuItem mniExport; + private System.Windows.Forms.MenuItem mniImport; + private System.Windows.Forms.OpenFileDialog ofd; + private System.Windows.Forms.SaveFileDialog sfd; + private System.Windows.Forms.MenuItem mniExit; + private System.Windows.Forms.MenuItem mniCut; + private System.Windows.Forms.MenuItem mniCopy; + private System.Windows.Forms.MenuItem mniPaste; + private System.Windows.Forms.MenuItem mniFind; + private System.Windows.Forms.MenuItem menuItem9; + private System.Windows.Forms.MenuItem mniSave; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + public EditLevelTextForm(LevelDoc lvld) + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + m_lvld = lvld; + m_strLevelText = m_lvld.GetLevelText(); + richTextBox1.Text = m_strLevelText; + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.richTextBox1 = new System.Windows.Forms.RichTextBox(); + this.mainMenu1 = new System.Windows.Forms.MainMenu(); + this.menuItem1 = new System.Windows.Forms.MenuItem(); + this.mniSave = new System.Windows.Forms.MenuItem(); + this.mniImport = new System.Windows.Forms.MenuItem(); + this.mniExport = new System.Windows.Forms.MenuItem(); + this.mniExit = new System.Windows.Forms.MenuItem(); + this.menuItem2 = new System.Windows.Forms.MenuItem(); + this.mniCut = new System.Windows.Forms.MenuItem(); + this.mniCopy = new System.Windows.Forms.MenuItem(); + this.mniPaste = new System.Windows.Forms.MenuItem(); + this.menuItem9 = new System.Windows.Forms.MenuItem(); + this.mniFind = new System.Windows.Forms.MenuItem(); + this.ofd = new System.Windows.Forms.OpenFileDialog(); + this.sfd = new System.Windows.Forms.SaveFileDialog(); + this.SuspendLayout(); + // + // richTextBox1 + // + this.richTextBox1.Dock = System.Windows.Forms.DockStyle.Fill; + this.richTextBox1.Font = new System.Drawing.Font("Microsoft Sans Serif", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0))); + this.richTextBox1.HideSelection = false; + this.richTextBox1.Name = "richTextBox1"; + this.richTextBox1.Size = new System.Drawing.Size(720, 710); + this.richTextBox1.TabIndex = 0; + this.richTextBox1.Text = "richTextBox1"; + // + // mainMenu1 + // + this.mainMenu1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.menuItem1, + this.menuItem2}); + // + // menuItem1 + // + this.menuItem1.Index = 0; + this.menuItem1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.mniSave, + this.mniImport, + this.mniExport, + this.mniExit}); + this.menuItem1.Text = "&File"; + // + // mniSave + // + this.mniSave.Index = 0; + this.mniSave.Shortcut = System.Windows.Forms.Shortcut.CtrlS; + this.mniSave.Text = "&Save"; + this.mniSave.Click += new System.EventHandler(this.mniSave_Click); + // + // mniImport + // + this.mniImport.Index = 1; + this.mniImport.Text = "&Import..."; + this.mniImport.Click += new System.EventHandler(this.mniImport_Click); + // + // mniExport + // + this.mniExport.Index = 2; + this.mniExport.Text = "&Export..."; + this.mniExport.Click += new System.EventHandler(this.mniExport_Click); + // + // mniExit + // + this.mniExit.Index = 3; + this.mniExit.Text = "E&xit"; + this.mniExit.Click += new System.EventHandler(this.mniExit_Click); + // + // menuItem2 + // + this.menuItem2.Index = 1; + this.menuItem2.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.mniCut, + this.mniCopy, + this.mniPaste, + this.menuItem9, + this.mniFind}); + this.menuItem2.Text = "&Edit"; + // + // mniCut + // + this.mniCut.Index = 0; + this.mniCut.Shortcut = System.Windows.Forms.Shortcut.CtrlX; + this.mniCut.Text = "C&ut"; + this.mniCut.Click += new System.EventHandler(this.mniCut_Click); + // + // mniCopy + // + this.mniCopy.Index = 1; + this.mniCopy.Shortcut = System.Windows.Forms.Shortcut.CtrlC; + this.mniCopy.Text = "&Copy"; + this.mniCopy.Click += new System.EventHandler(this.mniCopy_Click); + // + // mniPaste + // + this.mniPaste.Index = 2; + this.mniPaste.Shortcut = System.Windows.Forms.Shortcut.CtrlV; + this.mniPaste.Text = "&Paste"; + this.mniPaste.Click += new System.EventHandler(this.mniPaste_Click); + // + // menuItem9 + // + this.menuItem9.Index = 3; + this.menuItem9.Text = "-"; + // + // mniFind + // + this.mniFind.Index = 4; + this.mniFind.Shortcut = System.Windows.Forms.Shortcut.CtrlF; + this.mniFind.Text = "&Find..."; + this.mniFind.Click += new System.EventHandler(this.mniFind_Click); + // + // ofd + // + this.ofd.DefaultExt = "txt"; + this.ofd.Filter = "Text files|*.txt|All files|*.*"; + this.ofd.Title = "Import"; + // + // sfd + // + this.sfd.DefaultExt = "txt"; + this.sfd.FileName = "doc1"; + this.sfd.Filter = "Text files|*.txt|All files|*.*"; + this.sfd.Title = "Export As"; + // + // EditLevelTextForm + // + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.ClientSize = new System.Drawing.Size(720, 710); + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.richTextBox1}); + this.Menu = this.mainMenu1; + this.Name = "EditLevelTextForm"; + this.Text = "Level Text"; + this.ResumeLayout(false); + + } + #endregion + + protected override void OnClosing(System.ComponentModel.CancelEventArgs e) { + if (richTextBox1.Text == m_strLevelText) + return; + + DialogResult dlgr = MessageBox.Show(this, "Save Changes?", "M", MessageBoxButtons.YesNoCancel, + MessageBoxIcon.Question); + switch (dlgr) { + case DialogResult.Yes: + int ichErrorPos; + if (m_lvld.SetLevelText(richTextBox1.Text, out ichErrorPos)) { + DialogResult = DialogResult.OK; + } else { + richTextBox1.Select(ichErrorPos, 0); + e.Cancel = true; + } + break; + + case DialogResult.No: + DialogResult = DialogResult.Cancel; + break; + + case DialogResult.Cancel: + e.Cancel = true; + break; + } + } + + private void mniImport_Click(object sender, System.EventArgs e) { + if (ofd.FileName == "") + ofd.FileName = Path.GetFileNameWithoutExtension(m_lvld.GetPath()) + ".txt"; + + DialogResult dlgr; + if (richTextBox1.Text != m_strLevelText) { + dlgr = MessageBox.Show(this, "Save changes first?", "M", MessageBoxButtons.YesNoCancel, + MessageBoxIcon.Question); + switch (dlgr) { + case DialogResult.Yes: + int ichErrorPos; + if (m_lvld.SetLevelText(richTextBox1.Text, out ichErrorPos)) { + DialogResult = DialogResult.OK; + } else { + richTextBox1.Select(ichErrorPos, 0); + return; + } + break; + + case DialogResult.No: + break; + + case DialogResult.Cancel: + return; + } + } + + dlgr = ofd.ShowDialog(this); + if (dlgr != DialogResult.OK) + return; + + StreamReader stmr = new StreamReader(ofd.FileName); + string str = stmr.ReadToEnd(); + stmr.Close(); + + richTextBox1.Text = str; + Scrub(); + } + + private void mniExport_Click(object sender, System.EventArgs e) { + sfd.FileName = Path.GetFileNameWithoutExtension(m_lvld.GetPath()) + ".txt"; + DialogResult dlgr = sfd.ShowDialog(this); + if (dlgr != DialogResult.OK) + return; + + StringReader strr = new StringReader(richTextBox1.Text); + StreamWriter stmw = new StreamWriter(sfd.FileName); + + while (true) { + string strT = strr.ReadLine(); + if (strT == null) + break; + stmw.WriteLine(strT); + } + + stmw.Close(); + strr.Close(); + } + + private void mniExit_Click(object sender, System.EventArgs e) { + Close(); + } + + private void mniCut_Click(object sender, System.EventArgs e) { + richTextBox1.Cut(); + } + + private void mniCopy_Click(object sender, System.EventArgs e) { + richTextBox1.Copy(); + } + + private void mniPaste_Click(object sender, System.EventArgs e) { + richTextBox1.Paste(); + Scrub(); + } + + private void Scrub() { + bool fModified = false; + string strT = richTextBox1.Text; + StringBuilder strb = new StringBuilder(); + for (int ich = 0; ich < strT.Length; ich++) { + char ch = strT[ich]; + if (ch == 0x2018 || ch == 0x2019) { // curly-single-quotes + ch = '\''; + fModified = true; + } else if (ch == 0x201c || ch == 0x201d) { // curly-double-quotes + ch = '"'; + fModified = true; + } else if (ch == 0x2013) { // -- + ch = '-'; + strb.Append(ch); + fModified = true; + } else if (ch == 0x2026) { // ... + ch = '.'; + strb.Append(ch); + strb.Append(ch); + fModified = true; + } + + if (ch > 127) + MessageBox.Show(this, String.Format("Unhandled illegal character {0} (0x{1:X})", ch, (Int32)ch), "M"); + + strb.Append(ch); + } + + if (fModified) { + MessageBox.Show(this, "Invalid characters were found and converted to valid ones.", "M"); + richTextBox1.Text = strb.ToString(); + } + } + + private void mniDelete_Click(object sender, System.EventArgs e) { + MessageBox.Show("Coming soon..."); + } + + private void mniFind_Click(object sender, System.EventArgs e) { + FindLevelTextForm frm = new FindLevelTextForm(richTextBox1); + frm.Owner = this; + frm.Show(); + } + + private void mniSave_Click(object sender, System.EventArgs e) { + Scrub(); + + int ichErrorPos; + if (!m_lvld.SetLevelText(richTextBox1.Text, out ichErrorPos)) + richTextBox1.Select(ichErrorPos, 0); + else + m_strLevelText = richTextBox1.Text; + } + } +} diff --git a/m/EditLevelTextForm.resources b/m/EditLevelTextForm.resources new file mode 100644 index 0000000..6d288ac Binary files /dev/null and b/m/EditLevelTextForm.resources differ diff --git a/m/EditLevelTextForm.resx b/m/EditLevelTextForm.resx new file mode 100644 index 0000000..2e1d6bb --- /dev/null +++ b/m/EditLevelTextForm.resx @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Public + + + 17, 17 + + + 126, 17 + + + 193, 17 + + + EditLevelTextForm + + \ No newline at end of file diff --git a/m/EditRichTextForm.cs b/m/EditRichTextForm.cs new file mode 100644 index 0000000..a6a4410 --- /dev/null +++ b/m/EditRichTextForm.cs @@ -0,0 +1,126 @@ +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; + +namespace m +{ + /// + /// Summary description for EditRichTextForm. + /// + public class EditRichTextForm : System.Windows.Forms.Form + { + private System.Windows.Forms.Button buttonOk; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Button buttonCancel; + public System.Windows.Forms.TextBox textBox1; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + public EditRichTextForm(string strTitle, string strCurrent) + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + textBox1.Text = strCurrent; + } + + static public string DoModal(string strTitle, string strCurrent) { + EditRichTextForm frm = new EditRichTextForm(strTitle, strCurrent); + DialogResult res = frm.ShowDialog(); + if (res == DialogResult.Cancel) + return null; + return frm.textBox1.Text; + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.buttonOk = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.buttonCancel = new System.Windows.Forms.Button(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.SuspendLayout(); + // + // buttonOk + // + this.buttonOk.DialogResult = System.Windows.Forms.DialogResult.OK; + this.buttonOk.Location = new System.Drawing.Point(76, 234); + this.buttonOk.Name = "buttonOk"; + this.buttonOk.TabIndex = 1; + this.buttonOk.Text = "Ok"; + // + // label1 + // + this.label1.Location = new System.Drawing.Point(8, 8); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(200, 16); + this.label1.TabIndex = 4; + this.label1.Text = "Edit the text:"; + // + // buttonCancel + // + this.buttonCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.buttonCancel.Location = new System.Drawing.Point(188, 234); + this.buttonCancel.Name = "buttonCancel"; + this.buttonCancel.TabIndex = 2; + this.buttonCancel.Text = "Cancel"; + // + // textBox1 + // + this.textBox1.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0))); + this.textBox1.Location = new System.Drawing.Point(8, 24); + this.textBox1.Multiline = true; + this.textBox1.Name = "textBox1"; + this.textBox1.Size = new System.Drawing.Size(320, 200); + this.textBox1.TabIndex = 0; + this.textBox1.Text = "textBox1"; + // + // EditRichTextForm + // + this.AcceptButton = this.buttonOk; + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.CancelButton = this.buttonCancel; + this.ClientSize = new System.Drawing.Size(338, 266); + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.textBox1, + this.buttonOk, + this.label1, + this.buttonCancel}); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "EditRichTextForm"; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Edit Text"; + this.ResumeLayout(false); + + } + #endregion + } +} diff --git a/m/EditRichTextForm.resources b/m/EditRichTextForm.resources new file mode 100644 index 0000000..8c80d3f Binary files /dev/null and b/m/EditRichTextForm.resources differ diff --git a/m/EditRichTextForm.resx b/m/EditRichTextForm.resx new file mode 100644 index 0000000..cf8cb21 --- /dev/null +++ b/m/EditRichTextForm.resx @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Public + + + EditRichTextForm + + \ No newline at end of file diff --git a/m/EditStringForm.cs b/m/EditStringForm.cs new file mode 100644 index 0000000..09c6139 --- /dev/null +++ b/m/EditStringForm.cs @@ -0,0 +1,132 @@ +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; + +namespace m +{ + /// + /// Summary description for EditStringForm. + /// + public class EditStringForm : System.Windows.Forms.Form + { + private System.Windows.Forms.Button buttonOk; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Button buttonCancel; + private System.Windows.Forms.TextBox textBox1; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + public EditStringForm(string strTitle, string strPrompt, string strCurrent) + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + Text = strTitle; + label1.Text = strPrompt; + textBox1.Text = strCurrent; + } + + static public string DoModal(string strTitle, string strPrompt, string strCurrent) { + EditStringForm frm = new EditStringForm(strTitle, strPrompt, strCurrent); + DialogResult res = frm.ShowDialog(); + if (res == DialogResult.Cancel) + return null; + return frm.textBox1.Text; + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.buttonOk = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.buttonCancel = new System.Windows.Forms.Button(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.SuspendLayout(); + // + // buttonOk + // + this.buttonOk.DialogResult = System.Windows.Forms.DialogResult.OK; + this.buttonOk.Location = new System.Drawing.Point(56, 64); + this.buttonOk.Name = "buttonOk"; + this.buttonOk.TabIndex = 1; + this.buttonOk.Text = "OK"; + // + // label1 + // + this.label1.Location = new System.Drawing.Point(8, 8); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(264, 16); + this.label1.TabIndex = 4; + this.label1.Text = "Edit the text:"; + // + // buttonCancel + // + this.buttonCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.buttonCancel.Location = new System.Drawing.Point(152, 64); + this.buttonCancel.Name = "buttonCancel"; + this.buttonCancel.TabIndex = 2; + this.buttonCancel.Text = "Cancel"; + // + // textBox1 + // + this.textBox1.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0))); + this.textBox1.Location = new System.Drawing.Point(8, 24); + this.textBox1.Name = "textBox1"; + this.textBox1.Size = new System.Drawing.Size(264, 22); + this.textBox1.TabIndex = 0; + this.textBox1.Text = "textBox1"; + this.textBox1.TextChanged += new System.EventHandler(this.textBox1_TextChanged); + // + // EditStringForm + // + this.AcceptButton = this.buttonOk; + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.CancelButton = this.buttonCancel; + this.ClientSize = new System.Drawing.Size(282, 96); + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.textBox1, + this.buttonOk, + this.label1, + this.buttonCancel}); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "EditStringForm"; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Edit Text"; + this.ResumeLayout(false); + + } + #endregion + + private void textBox1_TextChanged(object sender, System.EventArgs e) { + buttonOk.Enabled = textBox1.Text != ""; + } + } +} diff --git a/m/EditStringForm.resources b/m/EditStringForm.resources new file mode 100644 index 0000000..f08c439 Binary files /dev/null and b/m/EditStringForm.resources differ diff --git a/m/EditStringForm.resx b/m/EditStringForm.resx new file mode 100644 index 0000000..f8602ab --- /dev/null +++ b/m/EditStringForm.resx @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + EditStringForm + + \ No newline at end of file diff --git a/m/EditTerrainForm.resources b/m/EditTerrainForm.resources new file mode 100644 index 0000000..4e36c72 Binary files /dev/null and b/m/EditTerrainForm.resources differ diff --git a/m/EmbeddedResources/about.jpg b/m/EmbeddedResources/about.jpg new file mode 100644 index 0000000..53c84e6 Binary files /dev/null and b/m/EmbeddedResources/about.jpg differ diff --git a/m/EmbeddedResources/res.h b/m/EmbeddedResources/res.h new file mode 100755 index 0000000..f4da202 --- /dev/null +++ b/m/EmbeddedResources/res.h @@ -0,0 +1,802 @@ +#define kidfDefault 1000 +#define kidfAreYouSure 1001 +#define kidfEmpty 1002 +#define kidfLoseSummary 1003 +#define kidfGameOptions 1004 +#define kidfTileMapTest 1005 +#define kidfContinueGame 1006 +#define kidfHrcMenu 1008 +#define kidfPickLevel 1009 +#define kidfWinSummary 1010 +#define kidfBuildInfantry 1011 +#define kidfResearchMenu 1012 +#define kidfStructMenu 1013 +#define kidfVtsMenu 1014 +#define kidfBuildVehicle 1015 +#define kidfHqMenu 1016 +#define kidfBuildStructure 1017 +#define kidfPlaceStructure 1018 +#define kidfTestOptions 1019 +#define kidfStartup 1021 +#define kidfPickTransport 1022 +#define kidfHostMultiplayer 1024 +#define kidfObjectives 1025 +#define kidfGameStart 1026 +#define kidfChangeDisplayMode 1028 +#define kidfMessageBox 1029 +#define kidfUnitMenu 1030 +#define kidfMobileHqMenu 1031 +#define kidfMinerMenu 1032 +#define kidfMemoryUse 1033 +#define kidfRoom 1034 +#define kidfCreateGame 1035 +#define kidfSimUI 1036 +#define kidfInputUI 1037 +#define kidfLoadGame 1038 +#define kidfSaveGame 1039 +#define kidfEcomLarge 1040 +#define kidfEcomSmall 1041 +#define kidfHelp 1042 +#define kidfPlay 1043 +#define kidfInGameMenu 1044 +#define kidfUpgrade 1045 +#define kidfMiniMap 1046 +#define kidfCutScene 1047 +#define kidfInGameOptions 1049 +#define kidfSoundOptions 1050 +#define kidfColorOptions 1051 +#define kidfDisplayOptions 1052 +#define kidfPerformanceOptions 1053 +#define kidfGobCount 1054 +#define kidfDrmCode 1055 +#define kidfDrmKey 1056 +#define kidfRegisterNow 1057 +#define kidfMessageBoxQuery 1058 +#define kidfInputPanel 1059 +#define kidfDeleteMissionPack 1060 +#define kidfLoading 1061 +#define kidfWaiting 1062 +#define kidfMultiplayerLoseSummary 1063 +#define kidfMultiplayerWinSummary 1064 +#define kidfMultiplayerObjectives 1065 +#define kidfBluetoothPatchQuery 1066 +#define kidfRoomWide 1067 +#define kidfCreateGameWide 1068 +#define kidfSelectMissionWide 1069 +#define kidfDownloadMissionPackWide 1070 +#define kidfDownloadBox 1071 +#define kidfHelpWide 1072 +#define kidfLobby 1073 +#define kidfLogin 1074 +#define kidfCreateRoom 1075 +#define kidfChooseServer 1076 +#define kidfAddOnSingleMulti 1077 + +#define kifntDefault 0 +#define kifntShadow 1 +#define kifntButton 2 +#define kifntTitle 3 +#define kifntEcom 4 +#define kifntHud 5 +#define kcFonts 6 + +#define kidcOk 2000 +#define kidcCancel 2001 +#define kidcRunTests 2002 +#define kidcMiniMap 2003 +#define kidcHelp 2004 +#define kidcDefault 2005 +#define kidcAlert 2006 + +// Gob types + +// WARNING: the value and order of these constants cannot be +// changed without updating 'M' + +#define kgtNone 0 +#define kgtShortRangeInfantry 1 +#define kgtLongRangeInfantry 2 +#define kgtHumanResourceCenter 3 +#define kgtSurfaceDecal 4 +#define kgtScenery 5 +#define kgtAnimation 6 +#define kgtReactor 7 +#define kgtProcessor 8 +#define kgtStructure 9 +#define kgtUnit 10 +#define kgtGalaxMiner 11 +#define kgtHeadquarters 12 +#define kgtResearchCenter 13 +#define kgtVehicleTransportStation 14 +#define kgtRadar 15 +#define kgtLightTank 16 +#define kgtMediumTank 17 +#define kgtMachineGunVehicle 18 +#define kgtRocketVehicle 19 +#define kgtTakeoverSpecialist 20 +#define kgtWarehouse 21 +#define kgtMobileHeadquarters 22 +#define kgtOvermind 23 +#define kgtTankShot 24 +#define kgtRocket 25 +#define kgtMachineGunTower 26 +#define kgtRocketTower 27 +#define kgtScorch 28 +#define kgtSmoke 29 +#define kgtPuff 30 +#define kgtBullet 31 +#define kgtArtillery 32 +#define kgtArtilleryShot 33 +#define kgtAndy 34 +#define kgtReplicator 35 +#define kgtActivator 36 +#define kgtFox 37 +#define kgtAndyShot 38 + +//#include "../mpshared/side.h" + +// Sides + +#define ksideNeutral 0 +#define kside1 1 +#define kside2 2 +#define kside3 3 +#define kside4 4 +#define kcSides 5 + +#define knIntelligenceHuman 0 +#define knIntelligenceComputer 1 +#define knIntelligenceComputerOvermind 2 +#define knIntelligenceComputerNeutral 3 + +// NOTE: kut values can not be changed without breaking old levels + +#define kutNone -1 + +// Infantry Units (NOTE: this is the order they will appear in the build form) +#define kutShortRangeInfantry 0 +#define kutLongRangeInfantry 1 +#define kutTakeoverSpecialist 2 + +// Vehicle Units (NOTE: this is the order they will appear in the build form) +#define kutMachineGunVehicle 3 +#define kutLightTank 4 +#define kutRocketVehicle 5 +#define kutMediumTank 6 +#define kutGalaxMiner 7 +#define kutMobileHeadquarters 8 + +// Structures (NOTE: this is the order they will appear in the build form) +#define kutReactor 9 +#define kutProcessor 10 +#define kutWarehouse 11 +#define kutHumanResourceCenter 12 +#define kutVehicleTransportStation 13 +#define kutRadar 14 +#define kutResearchCenter 15 +#define kutHeadquarters 16 + +// Special structures that like to kill +#define kutMachineGunTower 17 +#define kutRocketTower 18 + +#define kutAndy 19 +#define kutArtillery 20 +#define kutReplicator 21 +#define kutFox 22 + +#define kutMax 23 + + +// Startup form constants + +#define kidcPlaySinglePlayer 1001 +#define kidcPlayMultiPlayer 1002 +#define kidcPlay 1003 +#define kidcSetupGame 1004 +#define kidcBuyMe 1005 +#define kidcCredits 1006 +#define kidcExitGame 1007 +#define kidcLoadSavedGame 1008 +#define kidcPlayDemo 1500 +#define kidcPlayMission 1010 +#define kidcVersion 1011 +#define kidcDownloadMissions 1501 +#define kidcForums 1502 +#define kidcLeaderboard 1503 + +// Play Solo form constants + +#define kidcBeginNewGame 1012 +#define kidcPlayChallengeLevel 1013 +#define kidcPlayStoryMission 1014 +#define kidcPlaybackGame 1015 +//#define kidcLoadSavedGame 1008 + +// SimUIForm constants + +#define kidcStatusLabel 1001 +#define kidcFps 1004 +#define kidcObjective 1005 +#define kidcCountdown 1006 + +// InputUIForm constants + +#define kidcMenuButton 1002 +#define kidcGraffitiScroll 1012 +#define kidcAppsSilkButton 1013 +#define kidcMenuSilkButton 1014 +#define kidcCalcSilkButton 1015 +#define kidcFindSilkButton 1016 +#define kidcCreditsLabel 1018 +#define kidcPower 1019 + +// InGameMenuForm constants + +#define kidcSaveGame 1001 +#define kidcLoadGame 1002 +#define kidcOptions 1003 +#define kidcRestartMission 1004 +#define kidcAbortMission 1005 +#define kidcObjectives 1006 +//#define kidcHelp 1006 + +// TestOptionsForm constants + +#define kidcDrawPaths 1002 +#define kidcOvermind 1003 +#define kidcClearFog 1004 +#define kidcDrawLines 1005 +#define kidcShowStats 1006 +#define kidcShowFPS 1007 +#define kidcSuspendUpdates 1008 +#define kidcMaxRepaint 1009 +#define kidcAutosave 1010 +#define kidcMemoryUse 1011 +#define kidcLockStep 1012 +#define kidcDrawUpdateRects 1013 +//#define kidcGraphStats 1014 +#define kidcGodMode 1015 +//#define kidcFlashEnemyTargets 1016 +//#define kidcHelp 1017 +#define kidcNextPage 1018 +#define kidcPrevPage 1019 +#define kidcIndex 1020 +#define kidcBack 1021 +#define kidcGobCount 1022 +#define kidcBreak 1023 +#define kidcStylusUI 1024 +#define kidcMoveIndicator 1025 +#define kidcHoldSelect 1026 + +// MemoryUseForm constants + +#define kidcDynDbInitial 1001 +#define kidcMmgrDynDbReserve 1002 +#define kidcDynUse 1003 +#define kidcMmgrUse 1004 +#define kidcCacheUse 1005 +#define kidcClearCache 1006 +#define kidcLimitCache 1007 +#define kidcAdd10KCache 1008 +#define kidcSub10KCache 1009 +#define kidcCacheLimit 1010 + +// Game Over form constants + +#define kidcMessage 1001 + +// Login form constants + +#define kidcAnonymous 1200 +#define kidcLogin 1201 +#define kidcRegister 1202 +#define kidcForgotAccount 1203 + +// Lobby form constants + +#define kidcRoomList 1200 +#define kidcJoinRoom 1201 +#define kidcNewRoom 1202 +#define kidcSignOut 1203 +#define kidcLurkerCount 1204 + +// CreateRoom constants + +#define kidcRoomNameLabel 1200 +#define kidcRoomName 1201 +#define kidcRoomNamePanel 1202 +#define kidcPrivate 1203 + +// PlayMultiplayer form constants + +#define kidcJoinGame 1001 +#define kidcNewGame 1002 +#define kidcGameList 1003 +#define kidcChat 1904 +#define kidcPlayerName 1005 +#define kidcSearching 1006 +#define kidcPlayerNamePanel 1007 +#define kidcPlayerNameLabel 1008 +#define kidcStatus 1009 +#define kidcSpecify 1010 +#define kidcNoGames 1011 + +// CreateNewMultiplayer form constants + +#define kidcPassword 1101 +#define kidcMapList 1102 + +// MeetingForm constants + +#define kidcGameName 1001 +#define kidcMapName 1002 +#define kidcNumPlayers 1003 +#define kidcPlayerList 1004 +#define kidcGameNameLabel 1005 +#define kidcGameNamePanel 1006 +#define kidcPasswordLabel 1107 +#define kidcPasswordPanel 1108 +#define kidcAddress 1009 +#define kidcAddressLabel 1010 + +// PickLevelForm constants + +#define kidcLevelList 1001 +#define kidcCategories 1500 +#define kidcAddOnMessage 1501 +#define kidcStoryList 1502 +#define kidcChallengeList 1503 +#define kidcAddOnList 1504 + +// DownloadMissionPack constants + +#define kidcMissionPackList 1001 +#define kidcMissionPackInfo 1002 +#define kidcNumMissions 1010 +#define kidcDiscuss 1011 + +// PickTransportForm constants + +#define kidcNoTransportsAvailable 1001 +#define kidcTransport1 1101 +#define kidcTransport2 1102 +#define kidcTransport3 1103 +#define kidcTransport4 1104 +#define kidcTransport5 1105 +#define kidcTransport6 1106 + +// UnitMenu constants + +#define kidcTitle 1200 +#define kidcCantTransform 1201 + +// Relocatable UnitMenu buttons + +#define kidcRelocButtonMin 1300 + +#define kidcRepair 1300 +#define kidcSelfDestruct 1301 +#define kidcAbortBuild 1302 +#define kidcAbortRepair 1303 +#define kidcBuild 1304 +#define kidcAbortUpgrade 1304 +#define kidcResearch 1305 +#define kidcTransform 1306 +#define kidcDeliver 1306 + +#define kidcRelocButtonMax 1307 + +// UnitBuildForm constants + +#define kidcName 1001 +#define kidcCost 1002 +#define kidcMoveRate 1003 +#define kidcMoveRateMeter 1103 +#define kidcArmorStrength 1004 +#define kidcArmorStrengthMeter 1104 +#define kidcWeaponStrength 1005 +#define kidcWeaponStrengthMeter 1105 +#define kidcWeaponRange 1006 +#define kidcWeaponRangeMeter 1106 +#define kidcList 1007 +#define kidcOrder 1008 +#define kidcCancelOrder 1009 +#define kidcDescription 1010 +#define kidcCostMeter 1101 +#define kidcLimitReached 1200 + +// BuildStructure form constants (reuses many BuildVehicle form constants) + +#define kidcPowerSupply 1011 +#define kidcPowerSupplyMeter 1111 +#define kidcPowerDemand 1012 +#define kidcPowerDemandMeter 1112 + +// EcomForm constants + +//#define kidcMessage 1001 +#define kidcFrom 1011 +#define kidcTo 1012 +#define kidcFromBitmap 1014 +#define kidcToBitmap 1015 +#define kidcEcomText 1016 + +// CutSceneForm constants + +//#define kidcMessage 1001 +#define kidcBitmap 1002 + +// Upgrade form constants + +#define kidcPrerequisites 1201 +#define kidcPrerequisitesLabel 1202 +#define kidcCostLabel 1203 + +// Objectives form constants + +#define kidcMissionTitle 1001 +#define kidcMissionResult 1002 +#define kidcStatistics 1003 +//#define kidcRestartMission 1004 +//#define kidcAbortMission 1005 +#define kidcObjectiveText1 1101 +#define kidcObjectiveText2 1102 +#define kidcObjectiveText3 1103 +#define kidcObjectiveText4 1104 +#define kidcObjectiveStatus1 1201 +#define kidcObjectiveStatus2 1202 +#define kidcObjectiveStatus3 1203 +#define kidcObjectiveStatus4 1204 +#define kidcPage1 1300 +#define kidcObjectiveInfo 1301 +#define kidcPage2 1400 +#define kidcMobileUnitsKilled 1401 +#define kidcStructuresKilled 1402 +#define kidcMobileUnitsLost 1403 +#define kidcStructuresLost 1404 +#define kidcCreditsAction 1405 +#define kidcGameTime 1406 +#define kidcInfo 1407 +#define kidcRankTitle 1408 +#define kidcPage3 1500 + +// GameOptions constants + +#define kidcInGameOptions 1001 +#define kidcSoundOptions 1002 +#define kidcPerformanceOptions 1003 +#define kidcColorOptions 1004 +#define kidcDisplayOptions 1005 +#define kidcDeleteMissionPack 1007 + +// InGameOptions constants + +#define kidcGameSpeed 1201 +#define kidcGameSpeedLabel 1202 +#define kidcLassoSelection 1003 +#define kidcEasy 1004 +#define kidcNormal 1005 +#define kidcHard 1006 +#define kidc1Select2Scroll 1203 +#define kidcScrollSpeed 1204 +#define kidcScrollSpeedLabel 1205 + +// SoundOptions constants + +#define kidcVol 1001 +#define kidcVolLabel 1002 +#define kidcMute 1003 +#define kidcVolumeString 1004 + +// ColorOptions constants + +#define kidcHueLabelString 1001 +#define kidcHue 1002 +#define kidcHueLabel 1003 +#define kidcSatLabelString 1004 +#define kidcSat 1005 +#define kidcSatLabel 1006 +#define kidcLumLabelString 1007 +#define kidcLum 1008 +#define kidcLumLabel 1009 + +// DisplayOptions constants + +#define kidcModesList 1001 + +// PerformanceOptions constants + +#define kidcRocketShots 1001 +#define kidcRocketTrails 1002 +#define kidcRocketImpacts 1003 +#define kidcShots 1004 +#define kidcShotImpacts 1005 +#define kidcSelectionBrackets 1006 +#define kidcSmoke 1007 +#define kidcEnemyDamageIndicator 1008 +#define kidcScorchMarks 1009 +#define kidcSymbolFlashing 1010 + +// kidfChooseServer +#define kidcServerList 1300 +#define kidcServerName 1301 +#define kidcServerLocation 1302 +#define kidcServerStatus 1303 +#define kidcRefresh 1304 + +// DRM Code + +#define kidcCode 1001 +#define kidcEnterKey 1002 +//#define kidcPlayDemo 1003 + +// DRM Key + +#define kidc0 1010 +#define kidc1 1011 +#define kidc2 1012 +#define kidc3 1013 +#define kidc4 1014 +#define kidc5 1015 +#define kidc6 1016 +#define kidc7 1017 +#define kidc8 1018 +#define kidc9 1019 +#define kidcA 1020 +#define kidcB 1021 +#define kidcC 1022 +#define kidcD 1023 +#define kidcE 1024 +#define kidcF 1025 +#define kidcBackspace 1026 +#define kidcKey 1027 + +// Input Panel + +#define kidcInputLabel 1010 +#define kidcInputEdit 1011 + +// Colors + +#define kiclrBlack 0 +#define kiclrWhite 1 +#define kiclrRed 2 +#define kiclrGreen 3 +#define kiclrYellow 4 +#define kiclrSide1 5 +#define kiclrSide2 6 +#define kiclrSide3 7 +#define kiclrSide4 8 +#define kiclrButtonFill 9 +#define kiclrButtonBorder 10 +#define kiclrMenuBack 11 +#define kiclrFormBackground 12 +#define kiclrMiniMapBorder 13 +#define kiclrGalaxite 14 +#define kiclrButtonFillHighlight 15 +#define kiclrMediumGray 16 +#define kiclrBlueSideFirst 17 +#define kiclr0BlueSide 17 +#define kiclr1BlueSide 18 +#define kiclr2BlueSide 19 +#define kiclr3BlueSide 20 +#define kiclr4BlueSide 21 +#define kiclrBlueSideLast 21 +#define kiclrRedSideFirst 22 +#define kiclr0RedSide 22 +#define kiclr1RedSide 23 +#define kiclr2RedSide 24 +#define kiclr3RedSide 25 +#define kiclr4RedSide 26 +#define kiclrRedSideLast 26 +#define kiclrYellowSideFirst 27 +#define kiclr0YellowSide 27 +#define kiclr1YellowSide 28 +#define kiclr2YellowSide 29 +#define kiclr3YellowSide 30 +#define kiclr4YellowSide 31 +#define kiclrYellowSideLast 31 +#define kiclrCyanSideFirst 32 +#define kiclr0CyanSide 32 +#define kiclr1CyanSide 33 +#define kiclr2CyanSide 34 +#define kiclr3CyanSide 35 +#define kiclr4CyanSide 36 +#define kiclrCyanSideLast 36 +#define kiclrListBackground 37 +#define kiclrListBorder 38 +#define kiclrJana 39 +#define kiclrAndy 40 +#define kiclrOlstrom 41 +#define kiclrFox 42 +#define kiclrSideNeutral 43 +#define kiclrNeutralSideFirst 43 +#define kiclr0NeutralSide 43 +#define kiclr1NeutralSide 44 +#define kiclr2NeutralSide 45 +#define kiclr3NeutralSide 46 +#define kiclr4NeutralSide 47 +#define kiclrNeutralSideLast 47 +#define kiclrFullnessIndicator 48 + +// ARM code resource ids + +#define kidrArmCode 1 + +// Datatypes used for conditions & actions + +#define knCaTypeNumber 0 +#define knCaTypeQualifiedNumber 1 +#define knCaTypeSide 2 +#define knCaTypeCounter 3 +#define knCaTypeUnit 4 +#define knCaTypeModifier 5 +#define knCaTypeResource 6 +#define knCaTypeWinLose 7 +#define knCaTypeOnOff 8 +#define knCaTypeCharacter 9 +#define knCaTypeText 10 +#define knCaTypeRichText 11 +#define knCaTypeUnitTypes 12 +#define knCaTypeSwitch 13 +#define knCaTypeArea 14 + +// Related enumerations + +#define knQualifierAtLeast 0 +#define knQualifierAtMost 1 +#define knQualifierExactly 2 + +#define knCaSideNeutral 0 +#define knCaSideSide1 1 +#define knCaSideSide2 2 +#define knCaSideSide3 3 +#define knCaSideSide4 4 +#define knCaSideEnemies 5 +#define knCaSideAllies 6 +#define knCaSideAllSides 7 +#define knCaSideCurrentSide 8 + +#define knCounterTypeNone 0 +#define knCounterTypeTotal 1 +#define knCounterTypeUnits 2 +#define knCounterTypeBuildings 3 +#define knCounterTypeUnitsAndBuildings 4 +#define knCounterTypeKills 5 +#define knCounterTypeCustom 6 + +#define knModifierTypeSet 0 +#define knModifierTypeAdd 1 +#define knModifierTypeSubtract 2 + +#define knWinLoseTypeNone 0 +#define knWinLoseTypeWin 1 +#define knWinLoseTypeLose 2 + +#define knOnOffTypeNone 0 +#define knOnOffTypeOn 1 +#define knOnOffTypeOff 2 + +#define knSmallLargeTypeNone 0 +#define knSmallLargeTypeSmallBottom 1 +#define knSmallLargeTypeLarge 2 +#define knSmallLargeTypeSmallTop 3 + +#define knMoreCloseTypeNone 0 +#define knMoreCloseTypeMore 1 +#define knMoreCloseTypeClose 2 + +#define knModifyCountdownTypeNone 0 +#define knModifyCountdownTypeStop 1 +#define knModifyCountdownTypeResume 2 +#define knModifyCountdownTypeHide 3 +#define knModifyCountdownTypeShow 4 + +#define knCharacterTypeNone 0 +#define knCharacterTypeAndy 1 +#define knCharacterTypeJana 2 +#define knCharacterTypeOlstrom 3 +#define knCharacterTypeFox 4 +#define knCharacterTypeACME_Security 5 +#define knCharacterTypeOMNI_Security 6 +#define knCharacterTypeAnonymous 7 +#define knCharacterTypeBlank 8 + +#define knAggressivenessCoward 0 +#define knAggressivenessPacifist 1 +#define knAggressivenessSelfDefense 2 +#define knAggressivenessDefender 3 +#define knAggressivenessPitbull 4 + +#define knModifyNumberTypeNone 0 +#define knModifyNumberTypeSet 1 +#define knModifyNumberTypeAdd 2 +#define knModifyNumberTypeSubtract 3 + +// Conditions + +#define knMissionLoadedCondition 0 +#define knCreditsCondition 1 +#define knAreaContainsUnitsCondition 2 +#define knGalaxiteCapacityReachedCondition 3 +#define knMobileHQDeployedCondition 4 +#define knPlaceStructureModeCondition 5 +#define knElapsedTimeCondition 6 +#define knOwnsUnitsCondition 7 +#define knMinerCantFindGalaxiteCondition 8 +#define knUnitSeesUnitCondition 9 +#define knUnitDestroyedCondition 10 +#define knSwitchCondition 11 +#define knPeriodicTimerCondition 12 +#define knDiscoversSideCondition 13 +#define knCountdownTimerCondition 14 +#define knCounterCondition 15 +#define knTestPvarCondition 16 +#define knHasUpgradesCondition 17 +#define knConditionMax 18 + +// TriggerActions + +#define knSetResourcesTriggerAction 0 +#define knSetAllowedUnitsTriggerAction 1 +#define knEcomTriggerAction 2 +#define knSetObjectiveTriggerAction 3 +#define knSetNextMissionTriggerAction 4 +#define knEndMissionTriggerAction 5 +#define knWaitTriggerAction 6 +#define knSetSwitchTriggerAction 7 +#define knSetPlayerControlsTriggerAction 8 +#define knPreserveTriggerTriggerAction 9 +#define knCenterViewTriggerAction 10 +#define knPanViewTriggerAction 11 +#define knDefogAreaTriggerAction 12 +#define knMoveUnitTriggerAction 13 +#define knTargetUnitTriggerAction 14 +#define knCreateUnitGroupTriggerAction 15 +#define knCreateUnitAtAreaTriggerAction 15 // THis is deliberately the same value as knCreateUnitGroupTriggerAction +#define knHuntTriggerAction 16 +#define knCreateRandomUnitGroupTriggerAction 17 +#define knAlliesTriggerAction 18 +#define knStartCountdownTimerTriggerAction 19 +#define knModifyCountdownTimerTriggerAction 20 +#define knRepairTriggerAction 21 +#define knEnableReplicatorTriggerAction 22 +#define knModifyCreditsTriggerAction 23 +#define knModifyCounterTriggerAction 24 +#define knMoveUnitsInAreaTriggerAction 25 +#define knSetFormalObjectiveTextTriggerAction 26 +#define knSetFormalObjectiveStatusTriggerAction 27 +#define knShowObjectivesTriggerAction 28 +#define knSetFormalObjectiveInfoTriggerAction 29 +#define knCutSceneTriggerAction 30 +#define knJumpToMissionTriggerAction 31 +#define knModifyPvarTriggerAction 32 +#define knSetPvarTextTriggerAction 33 +#define knShowAlertTriggerAction 34 +#define knSetAllowedUpgradesTriggerAction 35 +#define knSetUpgradesTriggerAction 36 + +// UnitGroupActions + +#define knWaitUnitGroupAction 0 +#define knSetSwitchUnitGroupAction 1 +#define knMoveUnitGroupAction 2 +#define knAttackUnitGroupAction 3 +#define knGuardUnitGroupAction 4 +#define knMineUnitGroupAction 5 +#define knGuardVicinityUnitGroupAction 6 + +// UnitActions + +#define knGuardUnitAction 0 +#define knGuardVicinityUnitAction 1 +#define knGuardAreaUnitAction 2 +#define knMoveUnitAction 3 +#define knHuntEnemiesUnitAction 4 +#define knMineUnitAction 5 + +// sound effects for triggers + +#define knsfxHappyEnding 0 +#define knsfxExplosion 1 diff --git a/m/FindLevelTextForm.cs b/m/FindLevelTextForm.cs new file mode 100644 index 0000000..867797d --- /dev/null +++ b/m/FindLevelTextForm.cs @@ -0,0 +1,176 @@ +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; + +namespace m +{ + /// + /// Summary description for FindLevelTextForm. + /// + public class FindLevelTextForm : System.Windows.Forms.Form + { + private static string s_strLastFind = ""; + private RichTextBox m_rtb; + private int m_ichLastFind; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Button btnFind; + private System.Windows.Forms.Button btnCancel; + private System.Windows.Forms.TextBox tbcFind; + private System.Windows.Forms.CheckBox chkbWholeWord; + private System.Windows.Forms.CheckBox chkbCase; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + public FindLevelTextForm(RichTextBox rtb) + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + m_rtb = rtb; + tbcFind.Text = s_strLastFind; + btnFind.Enabled = tbcFind.Text != ""; + m_ichLastFind = m_rtb.SelectionStart + m_rtb.SelectionLength; + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.tbcFind = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.btnFind = new System.Windows.Forms.Button(); + this.chkbWholeWord = new System.Windows.Forms.CheckBox(); + this.chkbCase = new System.Windows.Forms.CheckBox(); + this.btnCancel = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // tbcFind + // + this.tbcFind.Location = new System.Drawing.Point(64, 10); + this.tbcFind.Name = "tbcFind"; + this.tbcFind.Size = new System.Drawing.Size(176, 20); + this.tbcFind.TabIndex = 0; + this.tbcFind.Text = ""; + this.tbcFind.TextChanged += new System.EventHandler(this.tbcFind_TextChanged); + // + // label1 + // + this.label1.Location = new System.Drawing.Point(8, 13); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(56, 16); + this.label1.TabIndex = 1; + this.label1.Text = "Fi&nd what:"; + // + // btnFind + // + this.btnFind.Location = new System.Drawing.Point(264, 9); + this.btnFind.Name = "btnFind"; + this.btnFind.TabIndex = 2; + this.btnFind.Text = "&Find Next"; + this.btnFind.Click += new System.EventHandler(this.btnFind_Click); + // + // chkbWholeWord + // + this.chkbWholeWord.Enabled = false; + this.chkbWholeWord.Location = new System.Drawing.Point(8, 40); + this.chkbWholeWord.Name = "chkbWholeWord"; + this.chkbWholeWord.Size = new System.Drawing.Size(160, 16); + this.chkbWholeWord.TabIndex = 3; + this.chkbWholeWord.Text = "Match &whole word only"; + // + // chkbCase + // + this.chkbCase.Location = new System.Drawing.Point(8, 64); + this.chkbCase.Name = "chkbCase"; + this.chkbCase.Size = new System.Drawing.Size(160, 16); + this.chkbCase.TabIndex = 4; + this.chkbCase.Text = "Match &case"; + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(264, 40); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.TabIndex = 5; + this.btnCancel.Text = "Cancel"; + this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click); + // + // FindLevelTextForm + // + this.AcceptButton = this.btnFind; + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(352, 94); + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.btnCancel, + this.chkbCase, + this.chkbWholeWord, + this.btnFind, + this.label1, + this.tbcFind}); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "FindLevelTextForm"; + this.ShowInTaskbar = false; + this.Text = "Find"; + this.ResumeLayout(false); + + } + #endregion + + private void tbcFind_TextChanged(object sender, System.EventArgs e) { + btnFind.Enabled = tbcFind.Text != ""; + } + + private void btnFind_Click(object sender, System.EventArgs e) { + string strSrc = m_rtb.Text; + string strFind = tbcFind.Text; + + if (!chkbCase.Checked) { + strSrc = strSrc.ToUpper(); + strFind = strFind.ToUpper(); + } + + int ich = strSrc.IndexOf(strFind, m_rtb.SelectionStart + m_rtb.SelectionLength); + + if (ich == -1) { + MessageBox.Show(this, "Finished searching the document", "M", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); + } else { + m_rtb.SelectionStart = ich; + m_rtb.SelectionLength = tbcFind.Text.Length; + m_rtb.ScrollToCaret(); + } + } + + private void btnCancel_Click(object sender, System.EventArgs e) { + s_strLastFind = tbcFind.Text; + Close(); + Dispose(); + } + } +} diff --git a/m/FindLevelTextForm.resources b/m/FindLevelTextForm.resources new file mode 100644 index 0000000..6093dc5 Binary files /dev/null and b/m/FindLevelTextForm.resources differ diff --git a/m/FindLevelTextForm.resx b/m/FindLevelTextForm.resx new file mode 100644 index 0000000..16ebadf --- /dev/null +++ b/m/FindLevelTextForm.resx @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + FindLevelTextForm + + \ No newline at end of file diff --git a/m/FlowPanel.resources b/m/FlowPanel.resources new file mode 100644 index 0000000..a33cdb9 Binary files /dev/null and b/m/FlowPanel.resources differ diff --git a/m/Form1.cs b/m/Form1.cs new file mode 100644 index 0000000..cab4490 --- /dev/null +++ b/m/Form1.cs @@ -0,0 +1,1230 @@ +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; +using System.Resources; +using System.IO; +using System.Diagnostics; // for Process +using SpiffLib; + +namespace m +{ + /// + /// Summary description for Form1. + /// + public class MainForm : System.Windows.Forms.Form + { + private System.Windows.Forms.MainMenu mainMenu1; + private System.Windows.Forms.MenuItem menuItemFile; + private System.Windows.Forms.MenuItem menuItemExit; + private System.ComponentModel.IContainer components = null; + private System.Windows.Forms.MenuItem menuItemOpenLevelDoc; + private System.Windows.Forms.MenuItem menuItemSaveLevelDoc; + private System.Windows.Forms.MenuItem menuItemSaveLevelDocAs; + private System.Windows.Forms.MenuItem menuItem2; + private System.Windows.Forms.MenuItem menuItemNewLevelDoc; + private System.Windows.Forms.Panel panel1; + private System.Windows.Forms.Panel panel4; + private System.Windows.Forms.Panel panelLeft; + private System.Windows.Forms.MenuItem menuItem4; + private System.Windows.Forms.MenuItem menuItemLevelDocProperties; + private System.Windows.Forms.MenuItem menuItemSide1Properties; + private System.Windows.Forms.MenuItem menuItemSide2Properties; + private System.Windows.Forms.MenuItem menuItemSide3Properties; + private System.Windows.Forms.MenuItem menuItemSide4Properties; + private System.Windows.Forms.MenuItem menuItemSaveTileMapBitmap; + private System.Windows.Forms.MenuItem menuItemSaveLevelBitmap; + private System.Windows.Forms.Splitter splitterMapRight; + private System.Windows.Forms.Panel panelRight; + private System.Windows.Forms.PropertyGrid propertyGrid1; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Panel panel6; + private System.Windows.Forms.PictureBox pictureBox1; + private System.Windows.Forms.Splitter splitterMapLeft; + private System.Windows.Forms.MenuItem menuItemClose; + private System.Windows.Forms.MenuItem menuItem6; + private System.Windows.Forms.MenuItem menuItemWindow; + private System.Windows.Forms.MenuItem menuItemCascade; + private System.Windows.Forms.MenuItem menuItemTileHorizontal; + private System.Windows.Forms.MenuItem menuItemTileVertical; + private m.GobPanel ctlGobPanel; + private m.TemplatePanel ctlTemplatePanel; + private System.Windows.Forms.MenuItem menuItem1; + private System.Windows.Forms.MenuItem menuItemCut; + private System.Windows.Forms.MenuItem menuItemCopy; + private System.Windows.Forms.MenuItem menuItemPaste; + private System.Windows.Forms.MenuItem menuItemDelete; + private System.Windows.Forms.MenuItem menuItemRenameTemplates; + private System.Windows.Forms.MenuItem menuItemTest; + private System.Windows.Forms.MenuItem menuItemTriggers; + private System.Windows.Forms.MenuItem menuItemUnitGroups; + private System.Windows.Forms.MenuItem menuItem7; + private System.Windows.Forms.MenuItem menuItem3; + private System.Windows.Forms.MenuItem menuItemComments; + private System.Windows.Forms.MenuItem menuItem9; + private System.Windows.Forms.MenuItem menuItemRun; + private System.Windows.Forms.MenuItem menuItem8; + private System.Windows.Forms.MenuItem menuItemSwitches; + private System.Windows.Forms.MenuItem menuItemCounters; + private System.Windows.Forms.MenuItem menuItemText; + private System.Windows.Forms.MenuItem menuItemValidate; + private System.Windows.Forms.MenuItem menuItemMisc; + private System.Windows.Forms.MenuItem menuItem10; + private System.Windows.Forms.StatusBar statusBar1; + private System.Windows.Forms.MenuItem menuItem5; + private System.Windows.Forms.MenuItem menuItemAbout; + private System.Windows.Forms.Splitter splitterGobTiles; + private System.Windows.Forms.MenuItem menuItemHelp; + private System.Windows.Forms.MenuItem menuItem11; + private System.Windows.Forms.MenuItem menuItem12; + string m_strFileSettings = null; + + public MainForm() + { + new Globals(); + new DocManager(); + + DocManager.SetFrameParent(this); + DocManager.AddTemplate(new LevelDocTemplate(typeof(LevelFrame), typeof(LevelView))); + DocManager.AddTemplate(new TemplateDocTemplate()); + + // Load plug-ins. Plugins are .dll assemblies containing a class named "Plugin" + // that implements the IPlugin interface. Plugin dlls must be placed in the same + // directory as the m.exe + +#if STRAFER + try { + // UNDONE: hardwired for now. Could enumerate assemblies with a certain extension + // for maximum extensibility convenience. + + IPlugin plug = (IPlugin)Activator.CreateInstance(@"sidewinder", "m.Plugin").Unwrap(); + Globals.Plugins.Add(plug); + } catch {} +#endif + + // Set Kit early + + Globals.InitKit(); + + // + // Required for Windows Form Designer support + // + InitializeComponent(); + Globals.PropertyGrid = propertyGrid1; + Globals.StatusBar = statusBar1; + + // Load settings + + LoadSettings(); + + if (Globals.IsKit()) { + // Remove stuff we don't want in the kit. + + mainMenu1.MenuItems.Remove(menuItemMisc); + } + + // After the main form and its menus are initialized we give plugins an opportunity + // to modify them. + + foreach (IPlugin plug in Globals.Plugins) { + plug.HackMenus(mainMenu1); + } + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) { + if (disposing) { + if (components != null) + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(MainForm)); + this.mainMenu1 = new System.Windows.Forms.MainMenu(); + this.menuItemFile = new System.Windows.Forms.MenuItem(); + this.menuItemNewLevelDoc = new System.Windows.Forms.MenuItem(); + this.menuItemOpenLevelDoc = new System.Windows.Forms.MenuItem(); + this.menuItemClose = new System.Windows.Forms.MenuItem(); + this.menuItem6 = new System.Windows.Forms.MenuItem(); + this.menuItemSaveLevelDoc = new System.Windows.Forms.MenuItem(); + this.menuItemSaveLevelDocAs = new System.Windows.Forms.MenuItem(); + this.menuItem2 = new System.Windows.Forms.MenuItem(); + this.menuItem10 = new System.Windows.Forms.MenuItem(); + this.menuItemExit = new System.Windows.Forms.MenuItem(); + this.menuItem1 = new System.Windows.Forms.MenuItem(); + this.menuItemCut = new System.Windows.Forms.MenuItem(); + this.menuItemCopy = new System.Windows.Forms.MenuItem(); + this.menuItemPaste = new System.Windows.Forms.MenuItem(); + this.menuItemDelete = new System.Windows.Forms.MenuItem(); + this.menuItem4 = new System.Windows.Forms.MenuItem(); + this.menuItemTriggers = new System.Windows.Forms.MenuItem(); + this.menuItemUnitGroups = new System.Windows.Forms.MenuItem(); + this.menuItemSwitches = new System.Windows.Forms.MenuItem(); + this.menuItemCounters = new System.Windows.Forms.MenuItem(); + this.menuItemText = new System.Windows.Forms.MenuItem(); + this.menuItem7 = new System.Windows.Forms.MenuItem(); + this.menuItemLevelDocProperties = new System.Windows.Forms.MenuItem(); + this.menuItem3 = new System.Windows.Forms.MenuItem(); + this.menuItemSide1Properties = new System.Windows.Forms.MenuItem(); + this.menuItemSide2Properties = new System.Windows.Forms.MenuItem(); + this.menuItemSide3Properties = new System.Windows.Forms.MenuItem(); + this.menuItemSide4Properties = new System.Windows.Forms.MenuItem(); + this.menuItem9 = new System.Windows.Forms.MenuItem(); + this.menuItemComments = new System.Windows.Forms.MenuItem(); + this.menuItemMisc = new System.Windows.Forms.MenuItem(); + this.menuItemValidate = new System.Windows.Forms.MenuItem(); + this.menuItemRun = new System.Windows.Forms.MenuItem(); + this.menuItem8 = new System.Windows.Forms.MenuItem(); + this.menuItemSaveTileMapBitmap = new System.Windows.Forms.MenuItem(); + this.menuItemSaveLevelBitmap = new System.Windows.Forms.MenuItem(); + this.menuItemRenameTemplates = new System.Windows.Forms.MenuItem(); + this.menuItemWindow = new System.Windows.Forms.MenuItem(); + this.menuItemCascade = new System.Windows.Forms.MenuItem(); + this.menuItemTileHorizontal = new System.Windows.Forms.MenuItem(); + this.menuItemTileVertical = new System.Windows.Forms.MenuItem(); + this.menuItem5 = new System.Windows.Forms.MenuItem(); + this.menuItemHelp = new System.Windows.Forms.MenuItem(); + this.menuItemAbout = new System.Windows.Forms.MenuItem(); + this.menuItemTest = new System.Windows.Forms.MenuItem(); + this.splitterMapLeft = new System.Windows.Forms.Splitter(); + this.panelLeft = new System.Windows.Forms.Panel(); + this.panel1 = new System.Windows.Forms.Panel(); + this.panel4 = new System.Windows.Forms.Panel(); + this.ctlTemplatePanel = new m.TemplatePanel(); + this.splitterGobTiles = new System.Windows.Forms.Splitter(); + this.ctlGobPanel = new m.GobPanel(); + this.splitterMapRight = new System.Windows.Forms.Splitter(); + this.panelRight = new System.Windows.Forms.Panel(); + this.statusBar1 = new System.Windows.Forms.StatusBar(); + this.propertyGrid1 = new System.Windows.Forms.PropertyGrid(); + this.label1 = new System.Windows.Forms.Label(); + this.panel6 = new System.Windows.Forms.Panel(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.menuItem11 = new System.Windows.Forms.MenuItem(); + this.menuItem12 = new System.Windows.Forms.MenuItem(); + this.panelLeft.SuspendLayout(); + this.panel1.SuspendLayout(); + this.panel4.SuspendLayout(); + this.panelRight.SuspendLayout(); + this.panel6.SuspendLayout(); + this.SuspendLayout(); + // + // mainMenu1 + // + this.mainMenu1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.menuItemFile, + this.menuItem1, + this.menuItem4, + this.menuItemMisc, + this.menuItemWindow, + this.menuItem5, + this.menuItemTest}); + // + // menuItemFile + // + this.menuItemFile.Index = 0; + this.menuItemFile.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.menuItemNewLevelDoc, + this.menuItemOpenLevelDoc, + this.menuItemClose, + this.menuItem6, + this.menuItemSaveLevelDoc, + this.menuItemSaveLevelDocAs, + this.menuItem2, + this.menuItem12, + this.menuItem11, + this.menuItem10, + this.menuItemExit}); + this.menuItemFile.Text = "&File"; + // + // menuItemNewLevelDoc + // + this.menuItemNewLevelDoc.Index = 0; + this.menuItemNewLevelDoc.Shortcut = System.Windows.Forms.Shortcut.CtrlN; + this.menuItemNewLevelDoc.Text = "&New"; + this.menuItemNewLevelDoc.Click += new System.EventHandler(this.menuItemNewLevelDoc_Click); + // + // menuItemOpenLevelDoc + // + this.menuItemOpenLevelDoc.Index = 1; + this.menuItemOpenLevelDoc.Shortcut = System.Windows.Forms.Shortcut.CtrlO; + this.menuItemOpenLevelDoc.Text = "&Open..."; + this.menuItemOpenLevelDoc.Click += new System.EventHandler(this.menuItemOpenLevelDoc_Click); + // + // menuItemClose + // + this.menuItemClose.Index = 2; + this.menuItemClose.Text = "&Close"; + this.menuItemClose.Click += new System.EventHandler(this.menuItemClose_Click); + // + // menuItem6 + // + this.menuItem6.Index = 3; + this.menuItem6.Text = "-"; + // + // menuItemSaveLevelDoc + // + this.menuItemSaveLevelDoc.Index = 4; + this.menuItemSaveLevelDoc.Shortcut = System.Windows.Forms.Shortcut.CtrlS; + this.menuItemSaveLevelDoc.Text = "&Save"; + this.menuItemSaveLevelDoc.Click += new System.EventHandler(this.menuItemSaveLevelDoc_Click); + // + // menuItemSaveLevelDocAs + // + this.menuItemSaveLevelDocAs.Index = 5; + this.menuItemSaveLevelDocAs.Text = "&Save As..."; + this.menuItemSaveLevelDocAs.Click += new System.EventHandler(this.menuItemSaveLevelDocAs_Click); + // + // menuItem2 + // + this.menuItem2.Index = 6; + this.menuItem2.Text = "-"; + // + // menuItem10 + // + this.menuItem10.Index = 9; + this.menuItem10.Text = "-"; + // + // menuItemExit + // + this.menuItemExit.Index = 10; + this.menuItemExit.Text = "&Exit"; + this.menuItemExit.Click += new System.EventHandler(this.menuItemExit_Click); + // + // menuItem1 + // + this.menuItem1.Index = 1; + this.menuItem1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.menuItemCut, + this.menuItemCopy, + this.menuItemPaste, + this.menuItemDelete}); + this.menuItem1.Text = "&Edit"; + // + // menuItemCut + // + this.menuItemCut.Index = 0; + this.menuItemCut.Shortcut = System.Windows.Forms.Shortcut.CtrlX; + this.menuItemCut.Text = "Cu&t"; + this.menuItemCut.Click += new System.EventHandler(this.menuItemCut_Click); + // + // menuItemCopy + // + this.menuItemCopy.Index = 1; + this.menuItemCopy.Shortcut = System.Windows.Forms.Shortcut.CtrlC; + this.menuItemCopy.Text = "&Copy"; + this.menuItemCopy.Click += new System.EventHandler(this.menuItemCopy_Click); + // + // menuItemPaste + // + this.menuItemPaste.Index = 2; + this.menuItemPaste.Shortcut = System.Windows.Forms.Shortcut.CtrlP; + this.menuItemPaste.Text = "&Paste"; + this.menuItemPaste.Click += new System.EventHandler(this.menuItemPaste_Click); + // + // menuItemDelete + // + this.menuItemDelete.Index = 3; + this.menuItemDelete.Shortcut = System.Windows.Forms.Shortcut.Del; + this.menuItemDelete.Text = "&Delete"; + this.menuItemDelete.Click += new System.EventHandler(this.menuItemDelete_Click); + // + // menuItem4 + // + this.menuItem4.Index = 2; + this.menuItem4.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.menuItemTriggers, + this.menuItemUnitGroups, + this.menuItemSwitches, + this.menuItemCounters, + this.menuItemText, + this.menuItem7, + this.menuItemLevelDocProperties, + this.menuItem3, + this.menuItemSide1Properties, + this.menuItemSide2Properties, + this.menuItemSide3Properties, + this.menuItemSide4Properties, + this.menuItem9, + this.menuItemComments}); + this.menuItem4.Text = "&Settings"; + // + // menuItemTriggers + // + this.menuItemTriggers.Index = 0; + this.menuItemTriggers.Shortcut = System.Windows.Forms.Shortcut.CtrlT; + this.menuItemTriggers.Text = "T&riggers..."; + this.menuItemTriggers.Click += new System.EventHandler(this.menuItemTriggers_Click); + // + // menuItemUnitGroups + // + this.menuItemUnitGroups.Index = 1; + this.menuItemUnitGroups.Shortcut = System.Windows.Forms.Shortcut.CtrlG; + this.menuItemUnitGroups.Text = "Unit &Groups..."; + this.menuItemUnitGroups.Click += new System.EventHandler(this.menuItemUnitGroups_Click); + // + // menuItemSwitches + // + this.menuItemSwitches.Index = 2; + this.menuItemSwitches.Text = "&Switches..."; + this.menuItemSwitches.Click += new System.EventHandler(this.menuItemSwitches_Click); + // + // menuItemCounters + // + this.menuItemCounters.Index = 3; + this.menuItemCounters.Text = "&Counters..."; + this.menuItemCounters.Click += new System.EventHandler(this.menuItemCounters_Click); + // + // menuItemText + // + this.menuItemText.Index = 4; + this.menuItemText.Shortcut = System.Windows.Forms.Shortcut.CtrlL; + this.menuItemText.Text = "Level &Text..."; + this.menuItemText.Click += new System.EventHandler(this.menuItemText_Click); + // + // menuItem7 + // + this.menuItem7.Index = 5; + this.menuItem7.Text = "-"; + // + // menuItemLevelDocProperties + // + this.menuItemLevelDocProperties.Index = 6; + this.menuItemLevelDocProperties.Text = "&Level"; + this.menuItemLevelDocProperties.Click += new System.EventHandler(this.menuItemLevelDocProperties_Click); + // + // menuItem3 + // + this.menuItem3.Index = 7; + this.menuItem3.Text = "-"; + // + // menuItemSide1Properties + // + this.menuItemSide1Properties.Index = 8; + this.menuItemSide1Properties.Text = "Side &1"; + this.menuItemSide1Properties.Click += new System.EventHandler(this.menuItemSide1Properties_Click); + // + // menuItemSide2Properties + // + this.menuItemSide2Properties.Index = 9; + this.menuItemSide2Properties.Text = "Side &2"; + this.menuItemSide2Properties.Click += new System.EventHandler(this.menuItemSide2Properties_Click); + // + // menuItemSide3Properties + // + this.menuItemSide3Properties.Index = 10; + this.menuItemSide3Properties.Text = "Side &3"; + this.menuItemSide3Properties.Click += new System.EventHandler(this.menuItemSide3Properties_Click); + // + // menuItemSide4Properties + // + this.menuItemSide4Properties.Index = 11; + this.menuItemSide4Properties.Text = "Side &4"; + this.menuItemSide4Properties.Click += new System.EventHandler(this.menuItemSide4Properties_Click); + // + // menuItem9 + // + this.menuItem9.Index = 12; + this.menuItem9.Text = "-"; + // + // menuItemComments + // + this.menuItemComments.Index = 13; + this.menuItemComments.Shortcut = System.Windows.Forms.Shortcut.CtrlShiftC; + this.menuItemComments.Text = "Comments..."; + this.menuItemComments.Click += new System.EventHandler(this.menuItemComments_Click); + // + // menuItemMisc + // + this.menuItemMisc.Index = 3; + this.menuItemMisc.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.menuItemValidate, + this.menuItemRun, + this.menuItem8, + this.menuItemSaveTileMapBitmap, + this.menuItemSaveLevelBitmap, + this.menuItemRenameTemplates}); + this.menuItemMisc.Text = "Misc"; + // + // menuItemValidate + // + this.menuItemValidate.Index = 0; + this.menuItemValidate.Shortcut = System.Windows.Forms.Shortcut.F7; + this.menuItemValidate.Text = "&Validate"; + this.menuItemValidate.Click += new System.EventHandler(this.menuItemValidate_Click); + // + // menuItemRun + // + this.menuItemRun.Index = 1; + this.menuItemRun.Shortcut = System.Windows.Forms.Shortcut.F5; + this.menuItemRun.Text = "&Run"; + this.menuItemRun.Click += new System.EventHandler(this.menuItemRun_Click); + // + // menuItem8 + // + this.menuItem8.Index = 2; + this.menuItem8.Text = "-"; + // + // menuItemSaveTileMapBitmap + // + this.menuItemSaveTileMapBitmap.Index = 3; + this.menuItemSaveTileMapBitmap.Text = "Save TileMap Bitmap..."; + this.menuItemSaveTileMapBitmap.Visible = false; + this.menuItemSaveTileMapBitmap.Click += new System.EventHandler(this.menuItemSaveTileMapBitmap_Click); + // + // menuItemSaveLevelBitmap + // + this.menuItemSaveLevelBitmap.Index = 4; + this.menuItemSaveLevelBitmap.Text = "Save Level Bitmap..."; + this.menuItemSaveLevelBitmap.Click += new System.EventHandler(this.menuItemSaveLevelBitmap_Click); + // + // menuItemRenameTemplates + // + this.menuItemRenameTemplates.Index = 5; + this.menuItemRenameTemplates.Text = "Rename Templates..."; + this.menuItemRenameTemplates.Click += new System.EventHandler(this.menuItemRenameTemplates_Click); + // + // menuItemWindow + // + this.menuItemWindow.Index = 4; + this.menuItemWindow.MdiList = true; + this.menuItemWindow.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.menuItemCascade, + this.menuItemTileHorizontal, + this.menuItemTileVertical}); + this.menuItemWindow.Text = "&Window"; + // + // menuItemCascade + // + this.menuItemCascade.Index = 0; + this.menuItemCascade.Text = "&Cascade"; + this.menuItemCascade.Click += new System.EventHandler(this.menuItemCascade_Click); + // + // menuItemTileHorizontal + // + this.menuItemTileHorizontal.Index = 1; + this.menuItemTileHorizontal.Text = "&Tile Horizontal"; + this.menuItemTileHorizontal.Click += new System.EventHandler(this.menuItemTileHorizontal_Click); + // + // menuItemTileVertical + // + this.menuItemTileVertical.Index = 2; + this.menuItemTileVertical.Text = "&Tile Vertical"; + this.menuItemTileVertical.Click += new System.EventHandler(this.menuItemTileVertical_Click); + // + // menuItem5 + // + this.menuItem5.Index = 5; + this.menuItem5.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.menuItemHelp, + this.menuItemAbout}); + this.menuItem5.Text = "&Help"; + // + // menuItemHelp + // + this.menuItemHelp.Index = 0; + this.menuItemHelp.Text = "Help..."; + this.menuItemHelp.Click += new System.EventHandler(this.menuItemHelp_Click); + // + // menuItemAbout + // + this.menuItemAbout.Index = 1; + this.menuItemAbout.Text = "About..."; + this.menuItemAbout.Click += new System.EventHandler(this.menuItemAbout_Click); + // + // menuItemTest + // + this.menuItemTest.Index = 6; + this.menuItemTest.Text = "&Test"; + this.menuItemTest.Visible = false; + this.menuItemTest.Click += new System.EventHandler(this.menuItemTest_Click); + // + // splitterMapLeft + // + this.splitterMapLeft.BackColor = System.Drawing.SystemColors.Control; + this.splitterMapLeft.Location = new System.Drawing.Point(168, 0); + this.splitterMapLeft.Name = "splitterMapLeft"; + this.splitterMapLeft.Size = new System.Drawing.Size(3, 550); + this.splitterMapLeft.TabIndex = 1; + this.splitterMapLeft.TabStop = false; + // + // panelLeft + // + this.panelLeft.BackColor = System.Drawing.SystemColors.Control; + this.panelLeft.Controls.Add(this.panel1); + this.panelLeft.Dock = System.Windows.Forms.DockStyle.Left; + this.panelLeft.ForeColor = System.Drawing.SystemColors.Control; + this.panelLeft.Location = new System.Drawing.Point(0, 0); + this.panelLeft.Name = "panelLeft"; + this.panelLeft.Size = new System.Drawing.Size(168, 550); + this.panelLeft.TabIndex = 1; + // + // panel1 + // + this.panel1.BackColor = System.Drawing.Color.Black; + this.panel1.Controls.Add(this.panel4); + this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel1.Location = new System.Drawing.Point(0, 0); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(168, 550); + this.panel1.TabIndex = 1; + // + // panel4 + // + this.panel4.BackColor = System.Drawing.Color.Black; + this.panel4.Controls.Add(this.ctlTemplatePanel); + this.panel4.Controls.Add(this.splitterGobTiles); + this.panel4.Controls.Add(this.ctlGobPanel); + this.panel4.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel4.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0))); + this.panel4.Location = new System.Drawing.Point(0, 0); + this.panel4.Name = "panel4"; + this.panel4.Size = new System.Drawing.Size(168, 550); + this.panel4.TabIndex = 2; + // + // ctlTemplatePanel + // + this.ctlTemplatePanel.BackColor = System.Drawing.SystemColors.Control; + this.ctlTemplatePanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.ctlTemplatePanel.ForeColor = System.Drawing.SystemColors.Control; + this.ctlTemplatePanel.Location = new System.Drawing.Point(0, 219); + this.ctlTemplatePanel.Name = "ctlTemplatePanel"; + this.ctlTemplatePanel.Size = new System.Drawing.Size(168, 331); + this.ctlTemplatePanel.TabIndex = 3; + this.ctlTemplatePanel.Load += new System.EventHandler(this.ctlTemplatePanel_Load); + // + // splitterGobTiles + // + this.splitterGobTiles.BackColor = System.Drawing.SystemColors.Control; + this.splitterGobTiles.Dock = System.Windows.Forms.DockStyle.Top; + this.splitterGobTiles.Location = new System.Drawing.Point(0, 216); + this.splitterGobTiles.Name = "splitterGobTiles"; + this.splitterGobTiles.Size = new System.Drawing.Size(168, 3); + this.splitterGobTiles.TabIndex = 2; + this.splitterGobTiles.TabStop = false; + // + // ctlGobPanel + // + this.ctlGobPanel.AutoScroll = true; + this.ctlGobPanel.BackColor = System.Drawing.Color.Black; + this.ctlGobPanel.Dock = System.Windows.Forms.DockStyle.Top; + this.ctlGobPanel.Location = new System.Drawing.Point(0, 0); + this.ctlGobPanel.Name = "ctlGobPanel"; + this.ctlGobPanel.Size = new System.Drawing.Size(168, 216); + this.ctlGobPanel.TabIndex = 0; + // + // splitterMapRight + // + this.splitterMapRight.BackColor = System.Drawing.SystemColors.Control; + this.splitterMapRight.Dock = System.Windows.Forms.DockStyle.Right; + this.splitterMapRight.Location = new System.Drawing.Point(769, 0); + this.splitterMapRight.Name = "splitterMapRight"; + this.splitterMapRight.Size = new System.Drawing.Size(3, 550); + this.splitterMapRight.TabIndex = 1; + this.splitterMapRight.TabStop = false; + // + // panelRight + // + this.panelRight.BackColor = System.Drawing.SystemColors.Control; + this.panelRight.Controls.Add(this.statusBar1); + this.panelRight.Controls.Add(this.propertyGrid1); + this.panelRight.Controls.Add(this.label1); + this.panelRight.Controls.Add(this.panel6); + this.panelRight.Dock = System.Windows.Forms.DockStyle.Right; + this.panelRight.Location = new System.Drawing.Point(772, 0); + this.panelRight.Name = "panelRight"; + this.panelRight.Size = new System.Drawing.Size(156, 550); + this.panelRight.TabIndex = 2; + // + // statusBar1 + // + this.statusBar1.Location = new System.Drawing.Point(0, 528); + this.statusBar1.Name = "statusBar1"; + this.statusBar1.Size = new System.Drawing.Size(156, 22); + this.statusBar1.SizingGrip = false; + this.statusBar1.TabIndex = 3; + this.statusBar1.TabStop = true; + // + // propertyGrid1 + // + this.propertyGrid1.BackColor = System.Drawing.SystemColors.Control; + this.propertyGrid1.CommandsVisibleIfAvailable = true; + this.propertyGrid1.Cursor = System.Windows.Forms.Cursors.HSplit; + this.propertyGrid1.Dock = System.Windows.Forms.DockStyle.Fill; + this.propertyGrid1.LargeButtons = false; + this.propertyGrid1.LineColor = System.Drawing.SystemColors.ScrollBar; + this.propertyGrid1.Location = new System.Drawing.Point(0, 123); + this.propertyGrid1.Name = "propertyGrid1"; + this.propertyGrid1.Size = new System.Drawing.Size(156, 427); + this.propertyGrid1.TabIndex = 2; + this.propertyGrid1.Text = "propertyGrid1"; + this.propertyGrid1.ViewBackColor = System.Drawing.SystemColors.Window; + this.propertyGrid1.ViewForeColor = System.Drawing.SystemColors.WindowText; + this.propertyGrid1.SelectedObjectsChanged += new System.EventHandler(this.propertyGrid1_SelectedObjectsChanged); + // + // label1 + // + this.label1.BackColor = System.Drawing.SystemColors.Control; + this.label1.Dock = System.Windows.Forms.DockStyle.Top; + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0))); + this.label1.Location = new System.Drawing.Point(0, 100); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(156, 23); + this.label1.TabIndex = 1; + this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // panel6 + // + this.panel6.BackColor = System.Drawing.SystemColors.Control; + this.panel6.Controls.Add(this.pictureBox1); + this.panel6.Dock = System.Windows.Forms.DockStyle.Top; + this.panel6.Location = new System.Drawing.Point(0, 0); + this.panel6.Name = "panel6"; + this.panel6.Size = new System.Drawing.Size(156, 100); + this.panel6.TabIndex = 0; + // + // pictureBox1 + // + this.pictureBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.pictureBox1.BackColor = System.Drawing.SystemColors.Control; + this.pictureBox1.Location = new System.Drawing.Point(2, 4); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(151, 94); + this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.CenterImage; + this.pictureBox1.TabIndex = 0; + this.pictureBox1.TabStop = false; + // + // menuItem11 + // + this.menuItem11.Index = 8; + this.menuItem11.Text = "Export Mission Pack..."; + this.menuItem11.Click += new System.EventHandler(this.menuItemExportMissionPack_Click); + // + // menuItem12 + // + this.menuItem12.Index = 7; + this.menuItem12.Text = "Import Mission Pack..."; + this.menuItem12.Click += new System.EventHandler(this.menuItemImportMissionPack_Click); + // + // MainForm + // + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.BackColor = System.Drawing.SystemColors.AppWorkspace; + this.ClientSize = new System.Drawing.Size(928, 550); + this.Controls.Add(this.splitterMapLeft); + this.Controls.Add(this.splitterMapRight); + this.Controls.Add(this.panelRight); + this.Controls.Add(this.panelLeft); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.IsMdiContainer = true; + this.Menu = this.mainMenu1; + this.Name = "MainForm"; + this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Show; + this.Text = "m"; + this.Closing += new System.ComponentModel.CancelEventHandler(this.MainForm_Closing); + this.panelLeft.ResumeLayout(false); + this.panel1.ResumeLayout(false); + this.panel4.ResumeLayout(false); + this.panelRight.ResumeLayout(false); + this.panel6.ResumeLayout(false); + this.ResumeLayout(false); + + } + #endregion + + private void menuItemExit_Click(object sender, System.EventArgs e) + { + this.Close(); + } + + private void menuItemNewLevelDoc_Click(object sender, System.EventArgs e) { + DocManager.NewDocument(typeof(LevelDoc), null); + } + + private void menuItemOpenLevelDoc_Click(object sender, System.EventArgs e) { + // Allow both .ld and .tc opening + + OpenFileDialog frmOpen = new OpenFileDialog(); + frmOpen.DefaultExt = "ld"; + frmOpen.Filter = "M files (*.ld;*.tc)|*.ld;*.tc"; + frmOpen.Title = "Open..."; + if (frmOpen.ShowDialog() != DialogResult.Cancel) + DocManager.OpenDocument(frmOpen.FileName); + } + + private void menuItemSaveLevelDoc_Click(object sender, System.EventArgs e) { + Document doc = DocManager.GetActiveDocument(typeof(LevelDoc)); + if (doc != null) + doc.Save(); + } + + private void menuItemSaveLevelDocAs_Click(object sender, System.EventArgs e) { + Document doc = DocManager.GetActiveDocument(typeof(LevelDoc)); + if (doc != null) + doc.SaveAs(null); + } + + private void menuItemSaveTileMapBitmap_Click(object sender, System.EventArgs e) { +#if false + SaveFileDialog frmSave = new SaveFileDialog(); + frmSave.DefaultExt = "png"; + frmSave.Filter = "Png Files (*.png)|*.png"; + frmSave.Title = "Save TileMap Bitmap As"; + if (frmSave.ShowDialog() == DialogResult.Cancel) + return; + LevelDoc lvld = (LevelDoc)DocManager.GetActiveDocument(); + if (lvld != null) + lvld.GetMapBitmap().Save(frmSave.FileName, ImageFormat.Png); +#endif + } + + private void menuItemSaveLevelBitmap_Click(object sender, System.EventArgs e) { + SaveFileDialog frmSave = new SaveFileDialog(); + frmSave.DefaultExt = "png"; + frmSave.Filter = "Png Files (*.png)|*.png"; + frmSave.Title = "Save Level Bitmap As"; + if (frmSave.ShowDialog() == DialogResult.Cancel) + return; + LevelDoc lvld = (LevelDoc)DocManager.GetActiveDocument(typeof(LevelDoc)); + if (lvld != null) { + TemplateDoc tmpd = lvld.GetTemplateDoc(); + lvld.GetMapBitmap(tmpd.TileSize, tmpd, false).Save(frmSave.FileName, ImageFormat.Png); + } + } + + private void menuItemLevelDocProperties_Click(object sender, System.EventArgs e) { + LevelDoc lvld = (LevelDoc)DocManager.GetActiveDocument(typeof(LevelDoc)); + if (lvld != null) + Globals.PropertyGrid.SelectedObject = lvld; + } + + private void propertyGrid1_SelectedObjectsChanged(object sender, System.EventArgs e) { + TemplateDoc tmpd = (TemplateDoc)DocManager.GetActiveDocument(typeof(TemplateDoc)); + Object obj = Globals.PropertyGrid.SelectedObject; + if (obj == null) { + label1.Text = null; + pictureBox1.Image = null; + } else { + string strName = obj.GetType().Name; + if (obj is Unit && !(obj is Activator)) + strName = Helper.GetDisplayName(typeof(UnitType), "kut" + strName); + else if (obj is Tile) + strName = ((Tile)obj).Name; + label1.Text = strName; + IMapItem mi = obj as IMapItem; + if (mi == null) { + pictureBox1.Image = null; + } else { + Tile tile = mi as Tile; + if (tile != null) { + pictureBox1.Image = Misc.TraceEdges(tile.GetBitmap(new Size(tmpd.TileSize.Width, tmpd.TileSize.Height), tmpd), 1, Color.Black); + } else { + pictureBox1.Image = mi.GetBitmap(new Size(tmpd.TileSize.Width, tmpd.TileSize.Height), tmpd); + } + } + } + } + + private void menuItemSide1Properties_Click(object sender, System.EventArgs e) { + LevelDoc lvld = (LevelDoc)DocManager.GetActiveDocument(typeof(LevelDoc)); + if (lvld != null) + Globals.PropertyGrid.SelectedObject = lvld.GetSideInfo(Side.side1); + } + + private void menuItemSide2Properties_Click(object sender, System.EventArgs e) { + LevelDoc lvld = (LevelDoc)DocManager.GetActiveDocument(typeof(LevelDoc)); + if (lvld != null) + Globals.PropertyGrid.SelectedObject = lvld.GetSideInfo(Side.side2); + } + + private void menuItemSide3Properties_Click(object sender, System.EventArgs e) { + LevelDoc lvld = (LevelDoc)DocManager.GetActiveDocument(typeof(LevelDoc)); + if (lvld != null) + Globals.PropertyGrid.SelectedObject = lvld.GetSideInfo(Side.side3); + } + + private void menuItemSide4Properties_Click(object sender, System.EventArgs e) { + LevelDoc lvld = (LevelDoc)DocManager.GetActiveDocument(typeof(LevelDoc)); + if (lvld != null) + Globals.PropertyGrid.SelectedObject = lvld.GetSideInfo(Side.side4); + } + + private void MainForm_Closing(object sender, System.ComponentModel.CancelEventArgs e) { + if (!DocManager.CloseAllDocuments()) { + e.Cancel = true; + return; + } + + // Modifiers to toggle kit mode + + if ((Control.ModifierKeys & Keys.Shift) == Keys.Shift) { + Globals.SetKit(!Globals.IsKit()); + } + + SaveSettings(); + } + + void SaveSettings() { + // Save settings in ini. + + Ini ini = new Ini(); + Ini.Section secGeneral = new Ini.Section("General"); + secGeneral.Add(new Ini.Property("WindowState", WindowState.ToString())); + secGeneral.Add(new Ini.Property("X", Bounds.X.ToString())); + secGeneral.Add(new Ini.Property("Y", Bounds.Y.ToString())); + secGeneral.Add(new Ini.Property("Width", Bounds.Width.ToString())); + secGeneral.Add(new Ini.Property("Height", Bounds.Height.ToString())); + secGeneral.Add(new Ini.Property("AuthorKitPath", AuthorKitPath)); + secGeneral.Add(new Ini.Property("Kit", Globals.IsKit().ToString())); + secGeneral.Add(new Ini.Property("Eula", "1")); + + ini.Add(secGeneral); + + // Place in directory where .exe resides + + try { + if (m_strFileSettings != null) + ini.Save(m_strFileSettings); + } catch { + } + } + + void LoadSettings() { + // Settings + + m_strFileSettings = Application.ExecutablePath.Replace(".exe", ".ini"); + + Ini ini; + try { + ini = new Ini(m_strFileSettings); + } catch { + ini = null; + } + + if (ini == null) + return; + + Ini.Section sec = ini["General"]; + if (sec == null) + return; + Ini.Property prop = sec["WindowState"]; + if (prop != null) { + switch(prop.Value) { + case "Maximized": + WindowState = FormWindowState.Maximized; + break; + + case "Minimized": + WindowState = FormWindowState.Minimized; + break; + + case "Normal": + WindowState = FormWindowState.Normal; + break; + } + + if (WindowState == FormWindowState.Normal) { + Rectangle rc = new Rectangle(); + rc.X = int.Parse(sec["X"].Value); + rc.Y = int.Parse(sec["Y"].Value); + rc.Width = int.Parse(sec["Width"].Value); + rc.Height = int.Parse(sec["Height"].Value); + Bounds = rc; + } + } + + prop = sec["AuthorKitPath"]; + if (prop != null) + AuthorKitPath = prop.Value; + } + + private string m_strAuthorKitPath; + + public string AuthorKitPath { + get { + return m_strAuthorKitPath; + } + set { + m_strAuthorKitPath = value; + } + } + + private void menuItemCascade_Click(object sender, System.EventArgs e) { + LayoutMdi(MdiLayout.Cascade); + } + + private void menuItemTileHorizontal_Click(object sender, System.EventArgs e) { + LayoutMdi(MdiLayout.TileHorizontal); + } + + private void menuItemTileVertical_Click(object sender, System.EventArgs e) { + LayoutMdi(MdiLayout.TileVertical); + } + + private void menuItemCut_Click(object sender, System.EventArgs e) { + ICommandTarget cmdt = DocManager.GetCommandTarget(); + if (cmdt != null) + cmdt.DispatchCommand(Command.Cut); + } + + private void menuItemCopy_Click(object sender, System.EventArgs e) { + ICommandTarget cmdt = DocManager.GetCommandTarget(); + if (cmdt != null) + cmdt.DispatchCommand(Command.Copy); + } + + private void menuItemPaste_Click(object sender, System.EventArgs e) { + ICommandTarget cmdt = DocManager.GetCommandTarget(); + if (cmdt != null) + cmdt.DispatchCommand(Command.Paste); + } + + private void menuItemDelete_Click(object sender, System.EventArgs e) { + ICommandTarget cmdt = DocManager.GetCommandTarget(); + if (cmdt != null) + cmdt.DispatchCommand(Command.Delete); + } + + private void menuItemClose_Click(object sender, System.EventArgs e) { + if (ActiveMdiChild != null) + ActiveMdiChild.Close(); + } + + private void menuItemRenameTemplates_Click(object sender, System.EventArgs e) { + LevelDoc lvld = (LevelDoc)DocManager.GetActiveDocument(typeof(LevelDoc)); + if (lvld == null) + return; + + OpenFileDialog ofd = new OpenFileDialog(); + + ofd.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*" ; + + if (ofd.ShowDialog() != DialogResult.OK) + return; + + StreamReader stmrTemplateNames = new StreamReader(ofd.FileName); + + TemplateDoc tmpd = lvld.GetTemplateDoc(); + Template[] atmpl = tmpd.GetTemplates(); + foreach (Template tmpl in atmpl) { + string strT = stmrTemplateNames.ReadLine(); + if (strT == null) + break; + tmpl.Name = strT; + } + + stmrTemplateNames.Close(); + } + + private void menuItemTest_Click(object sender, System.EventArgs e) { + TriggersForm frm = new TriggersForm(new TriggerManager()); + frm.ShowDialog(); + } + + private void menuItemTriggers_Click(object sender, System.EventArgs e) { + LevelDoc lvld = (LevelDoc)DocManager.GetActiveDocument(typeof(LevelDoc)); + if (lvld != null) + lvld.EditTriggers(); + } + + private void menuItemUnitGroups_Click(object sender, System.EventArgs e) { + LevelDoc lvld = (LevelDoc)DocManager.GetActiveDocument(typeof(LevelDoc)); + if (lvld != null) + lvld.EditUnitGroups(); + } + + private void menuItemComments_Click(object sender, System.EventArgs e) { + LevelDoc lvld = (LevelDoc)DocManager.GetActiveDocument(typeof(LevelDoc)); + if (lvld != null) + lvld.EditComments(); + } + + private void menuItemSwitches_Click(object sender, System.EventArgs e) { + LevelDoc lvld = (LevelDoc)DocManager.GetActiveDocument(typeof(LevelDoc)); + if (lvld != null) + SwitchesForm.DoModal("Switch", "", CaTypeSwitch.GetSwitchNames()); + } + + private void menuItemCounters_Click(object sender, System.EventArgs e) { + LevelDoc lvld = (LevelDoc)DocManager.GetActiveDocument(typeof(LevelDoc)); + if (lvld != null) + CountersForm.DoModal("Counter", "", CaTypeCounter.GetCounterNames()); + } + + private void menuItemRun_Click(object sender, System.EventArgs e) { + LevelDoc lvld = (LevelDoc)DocManager.GetActiveDocument(typeof(LevelDoc)); + if (lvld != null) { + lvld.Save(); + + while (true) { + Process[] apr = Process.GetProcessesByName("Warfare Incorporated"); + if (apr.Length != 0) + MessageBox.Show("Please close Warfare Incorporated now.", "Warfare Incorporated already running"); + else + break; + } + + ProcessStartInfo psi = new ProcessStartInfo("run.bat"); +// psi.WindowStyle = ProcessWindowStyle.Hidden; + psi.Arguments = lvld.GetPath() + " " + Path.GetFileNameWithoutExtension(lvld.GetPath()) + ".lvl"; + string strAuthorKitPath = AuthorKitPath; + if (strAuthorKitPath == null) + strAuthorKitPath = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName); + psi.WorkingDirectory = strAuthorKitPath; + try { + Process ps = Process.Start(psi); + } catch (Win32Exception ex) { + if (ex.NativeErrorCode == 2) + MessageBox.Show("Can't find key AuthorKit files. Launch M.exe from inside the AuthorKit directory or specify the AuthorKitPath in m.ini.", "Error Launching Preview"); + else + throw ex; + } + } + } + + private void menuItemText_Click(object sender, System.EventArgs e) { + LevelDoc lvld = (LevelDoc)DocManager.GetActiveDocument(typeof(LevelDoc)); + if (lvld == null) + return; + lvld.EditLevelText(); + } + + private void menuItemValidate_Click(object sender, System.EventArgs e) { + LevelDoc lvld = (LevelDoc)DocManager.GetActiveDocument(typeof(LevelDoc)); + if (lvld == null) + return; + OutputForm.HideIt(); + OutputForm.Clear(); + lvld.Validate(new LevelDoc.ValidateErrorDelegate(ValidateCallback)); + } + + //c:\code\ht\m\outputform.cs(21,15): error CS0111: + private void ValidateCallback(LevelDoc lvld, LevelDoc.ValidateError ve, int tx, int ty, object ob, string str) { + OutputForm.ShowIt(); +#if false + OutputForm.Error(lvld, ob, "({0},{1},{2}): {3}: {4}\n", tx, ty, ob == null ? "" : ob.GetHashCode().ToString(), ve.ToString(), str); +#else + if (ob is Unit) { + OutputForm.Error(lvld, ob, "({0},{1}): {2}: Side {3} {4}\n", tx, ty, ve.ToString(), ((Unit)ob).Side, str); + } else { + OutputForm.Error(lvld, ob, "({0},{1}): {2}: {3}\n", tx, ty, ve.ToString(), str); + } +#endif + } + + private void ctlTemplatePanel_Load(object sender, System.EventArgs e) { + + } + + private void menuItemImportMissionPack_Click(object sender, System.EventArgs e) { + // Prompt the user to save and close existing missions. This is so if a user + // imports more than once, missions aren't accumulated by mistake. + +#if false // annoying + LevelDocTemplate doctLevel = (LevelDocTemplate)DocManager.FindDocTemplate(typeof(LevelDoc)); + Document[] adoc = doctLevel.GetDocuments(); + if (adoc.Length != 0) { + MessageBox.Show(DocManager.GetFrameParent(), "Close all Level Documents before importing"); + return; + } +#endif + + // Select the pdb to load, and import it + + OpenFileDialog frmOpen = new OpenFileDialog(); + frmOpen.DefaultExt = "pdb"; + frmOpen.Filter = "Pdb files (*.pdb)|*.pdb"; + frmOpen.Title = "Open Mission Pack..."; + if (frmOpen.ShowDialog() != DialogResult.Cancel) + OutputTools.ImportExpansionPdb(frmOpen.FileName); + } + + private void menuItemExportMissionPack_Click(object sender, System.EventArgs e) { + // Any leveldocs to save? + + LevelDocTemplate doctLevel = (LevelDocTemplate)DocManager.FindDocTemplate(typeof(LevelDoc)); + Document[] adoc = doctLevel.GetDocuments(); + if (adoc.Length == 0) { + MessageBox.Show(DocManager.GetFrameParent(), "No Level Descriptions loaded!"); + return; + } + + // First save all level docs + + //annoying + //DocManager.SaveAllModified(typeof(LevelDoc)); + + // Remember this + + LevelDoc lvldActive = (LevelDoc)DocManager.GetActiveDocument(typeof(LevelDoc)); + + // Validate these first + + OutputForm.HideIt(); + OutputForm.Clear(); + foreach (LevelDoc lvld in adoc) { + // Unfortunately it seems we have a number of methods during validation and SaveIni-ing that + // rely on which document is active. + DocManager.SetActiveDocument(typeof(LevelDoc), lvld); + int cErrors = lvld.Validate(new LevelDoc.ValidateErrorDelegate(ValidateCallback)); + if (cErrors != 0) { + MessageBox.Show(DocManager.GetFrameParent(), "Please fix errors in " + lvld.GetName() + " then try again."); + return; + } + } + + // Restore + + if (lvldActive != null) + DocManager.SetActiveDocument(typeof(LevelDoc), lvldActive); + + // First get the filename + + SaveFileDialog frmSave = new SaveFileDialog(); + frmSave.DefaultExt = "pdb"; + frmSave.Filter = "Mission Pack Files (*.pdb)|*.pdb"; + frmSave.Title = "Export Mission Pack"; + if (frmSave.ShowDialog() == DialogResult.Cancel) + return; + + // Export expansion .pdb + + OutputTools.SaveExpansionPdb(frmSave.FileName, adoc, "1.1"); + } + + private void menuItemAbout_Click(object sender, System.EventArgs e) { + AboutForm frm = new AboutForm(); + frm.ShowDialog(); + } + + private void menuItemHelp_Click(object sender, System.EventArgs e) { + if (m_strFileSettings.EndsWith("mgui.ini")) { + System.Diagnostics.Process.Start(m_strFileSettings.Replace("mgui.ini", "m.chm")); + } else if (m_strFileSettings.EndsWith("m.ini")) { + System.Diagnostics.Process.Start(m_strFileSettings.Replace("m.ini", "m.chm")); + } + } + } + + // + // Plug-in interface + // + + public interface IPlugin { + void HackMenus(MainMenu mnu); + IMapItem[] GetMapItems(); + } +} diff --git a/m/Form1.resources b/m/Form1.resources new file mode 100644 index 0000000..80ae8da Binary files /dev/null and b/m/Form1.resources differ diff --git a/m/Form1.resx b/m/Form1.resx new file mode 100644 index 0000000..703512d --- /dev/null +++ b/m/Form1.resx @@ -0,0 +1,652 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Private + + + 17, 17 + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + False + + + Private + + + Private + + + False + + + True + + + Private + + + 8, 8 + + + True + + + Private + + + False + + + True + + + Private + + + 8, 8 + + + True + + + Private + + + False + + + True + + + Private + + + 8, 8 + + + True + + + Private + + + False + + + Private + + + Private + + + False + + + Private + + + Private + + + False + + + Private + + + Private + + + False + + + Private + + + Private + + + False + + + True + + + Private + + + 8, 8 + + + True + + + Private + + + False + + + Private + + + Private + + + False + + + Private + + + Private + + + False + + + Private + + + Private + + + False + + + True + + + Private + + + 8, 8 + + + True + + + Private + + + False + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + False + + + (Default) + + + False + + + False + + + 8, 8 + + + MainForm + + + True + + + 52 + + + True + + + Private + + + + AAABAAMAICAQAAAAAADoAgAANgAAABAQEAAAAAAAKAEAAB4DAAAgIAAAAAAAAKgIAABGBAAAKAAAACAA + AABAAAAAAQAEAAAAAAAAAgAAAAAAAAAAAAAQAAAAEAAAAAAAAAAAAIAAAIAAAACAgACAAAAAgACAAICA + AACAgIAAwMDAAAAA/wAA/wAAAP//AP8AAAD/AP8A//8AAP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + ////////////////////////KAAAABAAAAAgAAAAAQAEAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAIAAAIAAAACAgACAAAAAgACAAICAAACAgIAAwMDAAAAA/wAA/wAAAP//AP8AAAD/AP8A//8AAP// + /wAAAAAAAAAAAAd3d3d3d3d3REREREREREdP///////4R0////////hHT///////+EdP///////4R0// + //////hHT///////+EdP///////4R0////////hHSIiIiIiIiEdMzMzMzMzMR8RERERERETAAAAAAAAA + AAAAAAAAAAAAAP//AACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAQAA//8AAP//AAAoAAAAIAAAAEAAAAABAAgAAAAAAAAEAAAAAAAAAAAAAAABAAAAAQAAAAAAAAAA + gAAAgAAAAICAAIAAAACAAIAAgIAAAMDAwADA3MAA8MqmAAQEBAAICAgADAwMABEREQAWFhYAHBwcACIi + IgApKSkAVVVVAE1NTQBCQkIAOTk5AIB8/wBQUP8AkwDWAP/szADG1u8A1ufnAJCprQAAADMAAABmAAAA + mQAAAMwAADMAAAAzMwAAM2YAADOZAAAzzAAAM/8AAGYAAABmMwAAZmYAAGaZAABmzAAAZv8AAJkAAACZ + MwAAmWYAAJmZAACZzAAAmf8AAMwAAADMMwAAzGYAAMyZAADMzAAAzP8AAP9mAAD/mQAA/8wAMwAAADMA + MwAzAGYAMwCZADMAzAAzAP8AMzMAADMzMwAzM2YAMzOZADMzzAAzM/8AM2YAADNmMwAzZmYAM2aZADNm + zAAzZv8AM5kAADOZMwAzmWYAM5mZADOZzAAzmf8AM8wAADPMMwAzzGYAM8yZADPMzAAzzP8AM/8zADP/ + ZgAz/5kAM//MADP//wBmAAAAZgAzAGYAZgBmAJkAZgDMAGYA/wBmMwAAZjMzAGYzZgBmM5kAZjPMAGYz + /wBmZgAAZmYzAGZmZgBmZpkAZmbMAGaZAABmmTMAZplmAGaZmQBmmcwAZpn/AGbMAABmzDMAZsyZAGbM + zABmzP8AZv8AAGb/MwBm/5kAZv/MAMwA/wD/AMwAmZkAAJkzmQCZAJkAmQDMAJkAAACZMzMAmQBmAJkz + zACZAP8AmWYAAJlmMwCZM2YAmWaZAJlmzACZM/8AmZkzAJmZZgCZmZkAmZnMAJmZ/wCZzAAAmcwzAGbM + ZgCZzJkAmczMAJnM/wCZ/wAAmf8zAJnMZgCZ/5kAmf/MAJn//wDMAAAAmQAzAMwAZgDMAJkAzADMAJkz + AADMMzMAzDNmAMwzmQDMM8wAzDP/AMxmAADMZjMAmWZmAMxmmQDMZswAmWb/AMyZAADMmTMAzJlmAMyZ + mQDMmcwAzJn/AMzMAADMzDMAzMxmAMzMmQDMzMwAzMz/AMz/AADM/zMAmf9mAMz/mQDM/8wAzP//AMwA + MwD/AGYA/wCZAMwzAAD/MzMA/zNmAP8zmQD/M8wA/zP/AP9mAAD/ZjMAzGZmAP9mmQD/ZswAzGb/AP+Z + AAD/mTMA/5lmAP+ZmQD/mcwA/5n/AP/MAAD/zDMA/8xmAP/MmQD/zMwA/8z/AP//MwDM/2YA//+ZAP// + zABmZv8AZv9mAGb//wD/ZmYA/2b/AP//ZgAhAKUAX19fAHd3dwCGhoYAlpaWAMvLywCysrIA19fXAN3d + 3QDj4+MA6urqAPHx8QD4+PgA8Pv/AKSgoACAgIAAAAD/AAD/AAAA//8A/wAAAP8A/wD//wAA////AAoK + CgoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgoKCgoKCgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK + CgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoK + CgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoK + CgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKERERCgoKCgoKCgoKCgoK + CgoKCgoKCgoKEQoKCgoKCgoRCgoKCgoKCgoKEAoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoK + CgoKCgoKCgoKCgwKCgoRCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKEQoKChEKCgoKCgoKCgoKCgoK + CgoKCgoKCgoKCgoKCgoRCgoKEQoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKChEKCgoRCgoKCgoKCgoK + CgoKCgoKCgoKCgwKCgoKCgoKEQoKChEKCgoKCgoKCgoKCgoKCg4KCgoKDAoKCgoKCgoRCgoKEQoKCgoK + CgoKCgoKCgoKCgoKCgoKCgoKCgoKChEKCgoRCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKEQoKChEK + CgoKCgoKCgoKCgoKDAoKChAKCgoKCgoKCgoRCgoKEQoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKChEK + CgoRCgoKCgoKCgoKCgoKQwoKCgoKCgoKCgoKCgoKEQoKChEKCgoKCgoKCgoKCgoKCgoKEQoKCgoKCgoK + CgoRCgoKEQoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKChEKCgoRCgoKCgoKCgoKCgoQCgoKDAoKCgoK + CgoKCgoKEQoKChEKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoRCgoKEQoKCgoKCgoKCgoKCgoKCgoK + CgoKCgoKCgoKChEKCgoRCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKEQoKChEKCgoKCgoKCgoKCgoK + CgoKCgoKCgoKCgoKCgoRCgoKEQoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKChEKCgoRCgoKCgoKEQ4K + DgoKCgoKCgoKCgoKCgoKCgoKCgoKChAKCgoKCgoRERERERERCgoKCgoKCgoKCgoKChEREREREQoKCgoK + CgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoK + Cgr//////////4AAAAGAAAABgAAAAf//////////gfn8AcP4/gPn8P8H5/D/B+fwfwfn4H8H5+A/B+fg + PwfnwL8H58GfB+fB3wfngd8H54PPB+eD7wfnA+cH5wf3B+YH9wfmB/MH5g/7B+QP+QfgD/gHwB/8A8Af + /AP//////////w== + + + \ No newline at end of file diff --git a/m/Galaxite.cs b/m/Galaxite.cs new file mode 100644 index 0000000..6714b4f --- /dev/null +++ b/m/Galaxite.cs @@ -0,0 +1,122 @@ +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Runtime.Serialization; +using System.Text.RegularExpressions; +using SpiffLib; + +namespace m { + [Serializable] + public class Galaxite : MapItem, ISerializable { + private int m_nGx; + private GobImage m_gimg; + + static private string[] s_astrBitmaps = { + "Galaxite1a", + "Galaxite1b", + "Galaxite1c", + "Galaxite2a", + "Galaxite2b", + "Galaxite2c", + "Galaxite3a", + "Galaxite3b", + "Galaxite3c" + }; + + static private int[] s_anGxTranslate = { 5, 3, 1, 10, 8, 6, 15, 13, 11 }; + + public Galaxite(int nGx) { + Init(nGx, 0, 0); + } + + public Galaxite(int nGx, int tx, int ty) { + Init(nGx, tx, ty); + } + + public Galaxite(string strName, string strValue, int txOrigin, int tyOrigin) { + Regex re = new Regex(@"^(?\d+),(?\d+),(?\d+)$"); + Match m = re.Match(strValue); + int xl = int.Parse(m.Groups["xl"].Value); + int nGx = 0; + for (int i = 0; i < s_anGxTranslate.Length; i++) { + if (xl == s_anGxTranslate[i]) { + nGx = i; + break; + } + } + Init(nGx, int.Parse(m.Groups["tx"].Value) + txOrigin, + int.Parse(m.Groups["ty"].Value) + tyOrigin); + } + + public Galaxite(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { + m_nGx = info.GetInt32("nGx"); + m_gimg = Globals.GetGobImage(s_astrBitmaps[m_nGx], true); + } + + public override void GetObjectData(SerializationInfo info, StreamingContext context) { + base.GetObjectData(info, context); + info.AddValue("nGx", m_nGx); + } + + public override Ini.Property GetIniProperty(int txOrigin, int tyOrigin) { + // For example: Gx=1,80,100 + return new Ini.Property("Gx", s_anGxTranslate[m_nGx] + "," + (m_tx - txOrigin).ToString() + "," + (m_ty - tyOrigin).ToString()); + } + + void Init(int nGx, int tx, int ty) { + m_nGx = nGx; + m_tx = tx; + m_ty = ty; + m_gimg = Globals.GetGobImage(s_astrBitmaps[m_nGx], true); + } + + // IMapItem + + public override Bitmap GetBitmap(Size sizTile, TemplateDoc tmpd) { + Bitmap[] abm = m_gimg.GetBitmapSides(sizTile); + return abm[0]; + } + + public override Point GetCenterPoint(Size sizTile) { + Size sizGob = m_gimg.GetSize(sizTile); + return new Point((int)m_tx * sizTile.Width + sizGob.Width / 2, (int)m_ty * sizTile.Height + sizGob.Height / 2); + } + + public override Rectangle GetBoundingRectAt(int x, int y, Size sizTile, TemplateDoc tmpd) { + Size sizGob = m_gimg.GetSize(sizTile); + return new Rectangle(x, y, sizGob.Width, sizGob.Height); + } + + public override bool HitTest(int x, int y, Size sizTile, TemplateDoc tmpd) { + int xT = x - (int)m_tx * sizTile.Width; + int yT = y - (int)m_ty * sizTile.Height; + Size sizGob = m_gimg.GetSize(sizTile); + if (xT > 0 && xT < sizGob.Width && yT > 0 && yT < sizGob.Height) { + Bitmap[] abm = m_gimg.GetBitmapSides(sizTile); + return abm[0].GetPixel(xT, yT) != Color.Transparent; + } + return false; + } + + public override Object Clone() { + Object[] aobj = { m_nGx, (int)m_tx, (int)m_ty }; + return (Object)System.Activator.CreateInstance(GetType(), aobj); + } + + public override void Draw(Graphics g, int x, int y, Size sizTile, TemplateDoc tmpd, LayerType layer, bool fSelected) { + if (layer == LayerType.Galaxite) { + Bitmap[] abm = m_gimg.GetBitmapSides(sizTile); + Bitmap bm = abm[0]; + if (fSelected) { + Rectangle rcDst = new Rectangle(x, y, bm.Width, bm.Height); + ImageAttributes attr = new ImageAttributes(); + attr.SetGamma(0.5f); + g.DrawImage(bm, rcDst, 0, 0, bm.Width, bm.Height, GraphicsUnit.Pixel, attr); + } else { + g.DrawImage(bm, x, y); + } + } + } + } +} diff --git a/m/GobImages/Activator.png b/m/GobImages/Activator.png new file mode 100644 index 0000000..a424ab0 Binary files /dev/null and b/m/GobImages/Activator.png differ diff --git a/m/GobImages/Andy.png b/m/GobImages/Andy.png new file mode 100644 index 0000000..7dd1519 Binary files /dev/null and b/m/GobImages/Andy.png differ diff --git a/m/GobImages/Artillery.png b/m/GobImages/Artillery.png new file mode 100644 index 0000000..07170df Binary files /dev/null and b/m/GobImages/Artillery.png differ diff --git a/m/GobImages/Fox.png b/m/GobImages/Fox.png new file mode 100644 index 0000000..691de94 Binary files /dev/null and b/m/GobImages/Fox.png differ diff --git a/m/GobImages/GalaxMiner.png b/m/GobImages/GalaxMiner.png new file mode 100644 index 0000000..8da06ea Binary files /dev/null and b/m/GobImages/GalaxMiner.png differ diff --git a/m/GobImages/Galaxite1a.png b/m/GobImages/Galaxite1a.png new file mode 100644 index 0000000..8092d31 Binary files /dev/null and b/m/GobImages/Galaxite1a.png differ diff --git a/m/GobImages/Galaxite1b.png b/m/GobImages/Galaxite1b.png new file mode 100644 index 0000000..6730547 Binary files /dev/null and b/m/GobImages/Galaxite1b.png differ diff --git a/m/GobImages/Galaxite1c.png b/m/GobImages/Galaxite1c.png new file mode 100644 index 0000000..3be0236 Binary files /dev/null and b/m/GobImages/Galaxite1c.png differ diff --git a/m/GobImages/Galaxite2a.png b/m/GobImages/Galaxite2a.png new file mode 100644 index 0000000..3061e0d Binary files /dev/null and b/m/GobImages/Galaxite2a.png differ diff --git a/m/GobImages/Galaxite2b.png b/m/GobImages/Galaxite2b.png new file mode 100644 index 0000000..d88dcf7 Binary files /dev/null and b/m/GobImages/Galaxite2b.png differ diff --git a/m/GobImages/Galaxite2c.png b/m/GobImages/Galaxite2c.png new file mode 100644 index 0000000..4dae42e Binary files /dev/null and b/m/GobImages/Galaxite2c.png differ diff --git a/m/GobImages/Galaxite3a.png b/m/GobImages/Galaxite3a.png new file mode 100644 index 0000000..bc73ce5 Binary files /dev/null and b/m/GobImages/Galaxite3a.png differ diff --git a/m/GobImages/Galaxite3b.png b/m/GobImages/Galaxite3b.png new file mode 100644 index 0000000..c3c3c23 Binary files /dev/null and b/m/GobImages/Galaxite3b.png differ diff --git a/m/GobImages/Galaxite3c.png b/m/GobImages/Galaxite3c.png new file mode 100644 index 0000000..7235c54 Binary files /dev/null and b/m/GobImages/Galaxite3c.png differ diff --git a/m/GobImages/Headquarters.png b/m/GobImages/Headquarters.png new file mode 100644 index 0000000..4526bc8 Binary files /dev/null and b/m/GobImages/Headquarters.png differ diff --git a/m/GobImages/HumanResourceCenter.png b/m/GobImages/HumanResourceCenter.png new file mode 100644 index 0000000..0c41a36 Binary files /dev/null and b/m/GobImages/HumanResourceCenter.png differ diff --git a/m/GobImages/LightTank.png b/m/GobImages/LightTank.png new file mode 100644 index 0000000..b4ea0a1 Binary files /dev/null and b/m/GobImages/LightTank.png differ diff --git a/m/GobImages/LongRangeInfantry.png b/m/GobImages/LongRangeInfantry.png new file mode 100644 index 0000000..a96c5f9 Binary files /dev/null and b/m/GobImages/LongRangeInfantry.png differ diff --git a/m/GobImages/MachineGunTower.png b/m/GobImages/MachineGunTower.png new file mode 100644 index 0000000..98b4d85 Binary files /dev/null and b/m/GobImages/MachineGunTower.png differ diff --git a/m/GobImages/MachineGunVehicle.png b/m/GobImages/MachineGunVehicle.png new file mode 100644 index 0000000..960ca3c Binary files /dev/null and b/m/GobImages/MachineGunVehicle.png differ diff --git a/m/GobImages/MediumTank.png b/m/GobImages/MediumTank.png new file mode 100644 index 0000000..605a391 Binary files /dev/null and b/m/GobImages/MediumTank.png differ diff --git a/m/GobImages/MobileHeadquarters.png b/m/GobImages/MobileHeadquarters.png new file mode 100644 index 0000000..8268798 Binary files /dev/null and b/m/GobImages/MobileHeadquarters.png differ diff --git a/m/GobImages/Plant.bmp b/m/GobImages/Plant.bmp new file mode 100644 index 0000000..b3a437e Binary files /dev/null and b/m/GobImages/Plant.bmp differ diff --git a/m/GobImages/Plant1.bmp b/m/GobImages/Plant1.bmp new file mode 100644 index 0000000..f95ae35 Binary files /dev/null and b/m/GobImages/Plant1.bmp differ diff --git a/m/GobImages/Plant2.bmp b/m/GobImages/Plant2.bmp new file mode 100644 index 0000000..07d4975 Binary files /dev/null and b/m/GobImages/Plant2.bmp differ diff --git a/m/GobImages/Plant3.bmp b/m/GobImages/Plant3.bmp new file mode 100644 index 0000000..dc9bbb7 Binary files /dev/null and b/m/GobImages/Plant3.bmp differ diff --git a/m/GobImages/Plant4.bmp b/m/GobImages/Plant4.bmp new file mode 100644 index 0000000..9cf6ed5 Binary files /dev/null and b/m/GobImages/Plant4.bmp differ diff --git a/m/GobImages/Plant5.bmp b/m/GobImages/Plant5.bmp new file mode 100644 index 0000000..97643f8 Binary files /dev/null and b/m/GobImages/Plant5.bmp differ diff --git a/m/GobImages/Processor.png b/m/GobImages/Processor.png new file mode 100644 index 0000000..b319edb Binary files /dev/null and b/m/GobImages/Processor.png differ diff --git a/m/GobImages/Radar.png b/m/GobImages/Radar.png new file mode 100644 index 0000000..9dc4982 Binary files /dev/null and b/m/GobImages/Radar.png differ diff --git a/m/GobImages/Reactor.png b/m/GobImages/Reactor.png new file mode 100644 index 0000000..30321b0 Binary files /dev/null and b/m/GobImages/Reactor.png differ diff --git a/m/GobImages/Replicator.png b/m/GobImages/Replicator.png new file mode 100644 index 0000000..5b11ee1 Binary files /dev/null and b/m/GobImages/Replicator.png differ diff --git a/m/GobImages/ResearchCenter.png b/m/GobImages/ResearchCenter.png new file mode 100644 index 0000000..7e5a3a9 Binary files /dev/null and b/m/GobImages/ResearchCenter.png differ diff --git a/m/GobImages/RocketArtifact.png b/m/GobImages/RocketArtifact.png new file mode 100644 index 0000000..9d7bc05 Binary files /dev/null and b/m/GobImages/RocketArtifact.png differ diff --git a/m/GobImages/RocketTower.png b/m/GobImages/RocketTower.png new file mode 100644 index 0000000..086fb0a Binary files /dev/null and b/m/GobImages/RocketTower.png differ diff --git a/m/GobImages/RocketVehicle.png b/m/GobImages/RocketVehicle.png new file mode 100644 index 0000000..598401b Binary files /dev/null and b/m/GobImages/RocketVehicle.png differ diff --git a/m/GobImages/Rocks.bmp b/m/GobImages/Rocks.bmp new file mode 100644 index 0000000..e662950 Binary files /dev/null and b/m/GobImages/Rocks.bmp differ diff --git a/m/GobImages/ShortRangeInfantry.png b/m/GobImages/ShortRangeInfantry.png new file mode 100644 index 0000000..1e05ae1 Binary files /dev/null and b/m/GobImages/ShortRangeInfantry.png differ diff --git a/m/GobImages/SideWinder.png b/m/GobImages/SideWinder.png new file mode 100644 index 0000000..c3853c9 Binary files /dev/null and b/m/GobImages/SideWinder.png differ diff --git a/m/GobImages/TakeoverSpecialist.png b/m/GobImages/TakeoverSpecialist.png new file mode 100644 index 0000000..cd9d881 Binary files /dev/null and b/m/GobImages/TakeoverSpecialist.png differ diff --git a/m/GobImages/Tree.bmp b/m/GobImages/Tree.bmp new file mode 100644 index 0000000..12bed01 Binary files /dev/null and b/m/GobImages/Tree.bmp differ diff --git a/m/GobImages/Tree1.bmp b/m/GobImages/Tree1.bmp new file mode 100644 index 0000000..99116ac Binary files /dev/null and b/m/GobImages/Tree1.bmp differ diff --git a/m/GobImages/Tree2.bmp b/m/GobImages/Tree2.bmp new file mode 100644 index 0000000..7041998 Binary files /dev/null and b/m/GobImages/Tree2.bmp differ diff --git a/m/GobImages/Tree3.bmp b/m/GobImages/Tree3.bmp new file mode 100644 index 0000000..9d1838a Binary files /dev/null and b/m/GobImages/Tree3.bmp differ diff --git a/m/GobImages/Tree4.bmp b/m/GobImages/Tree4.bmp new file mode 100644 index 0000000..916ee36 Binary files /dev/null and b/m/GobImages/Tree4.bmp differ diff --git a/m/GobImages/Tree5.bmp b/m/GobImages/Tree5.bmp new file mode 100644 index 0000000..50692b5 Binary files /dev/null and b/m/GobImages/Tree5.bmp differ diff --git a/m/GobImages/Tree6.bmp b/m/GobImages/Tree6.bmp new file mode 100644 index 0000000..87b47bf Binary files /dev/null and b/m/GobImages/Tree6.bmp differ diff --git a/m/GobImages/Tree7.bmp b/m/GobImages/Tree7.bmp new file mode 100644 index 0000000..350f661 Binary files /dev/null and b/m/GobImages/Tree7.bmp differ diff --git a/m/GobImages/VehicleTransportStation.png b/m/GobImages/VehicleTransportStation.png new file mode 100644 index 0000000..c9adc9f Binary files /dev/null and b/m/GobImages/VehicleTransportStation.png differ diff --git a/m/GobImages/Wall00.png b/m/GobImages/Wall00.png new file mode 100644 index 0000000..5674e0e Binary files /dev/null and b/m/GobImages/Wall00.png differ diff --git a/m/GobImages/Wall01.png b/m/GobImages/Wall01.png new file mode 100644 index 0000000..ecfe672 Binary files /dev/null and b/m/GobImages/Wall01.png differ diff --git a/m/GobImages/Wall02.png b/m/GobImages/Wall02.png new file mode 100644 index 0000000..314eebf Binary files /dev/null and b/m/GobImages/Wall02.png differ diff --git a/m/GobImages/Wall03.png b/m/GobImages/Wall03.png new file mode 100644 index 0000000..94b6c04 Binary files /dev/null and b/m/GobImages/Wall03.png differ diff --git a/m/GobImages/Wall04.png b/m/GobImages/Wall04.png new file mode 100644 index 0000000..61eaeb4 Binary files /dev/null and b/m/GobImages/Wall04.png differ diff --git a/m/GobImages/Wall05.png b/m/GobImages/Wall05.png new file mode 100644 index 0000000..22860da Binary files /dev/null and b/m/GobImages/Wall05.png differ diff --git a/m/GobImages/Wall06.png b/m/GobImages/Wall06.png new file mode 100644 index 0000000..d3482f3 Binary files /dev/null and b/m/GobImages/Wall06.png differ diff --git a/m/GobImages/Wall07.png b/m/GobImages/Wall07.png new file mode 100644 index 0000000..6b8f9cd Binary files /dev/null and b/m/GobImages/Wall07.png differ diff --git a/m/GobImages/Wall08.png b/m/GobImages/Wall08.png new file mode 100644 index 0000000..22c88b4 Binary files /dev/null and b/m/GobImages/Wall08.png differ diff --git a/m/GobImages/Wall09.png b/m/GobImages/Wall09.png new file mode 100644 index 0000000..21e6ef0 Binary files /dev/null and b/m/GobImages/Wall09.png differ diff --git a/m/GobImages/Wall10.png b/m/GobImages/Wall10.png new file mode 100644 index 0000000..fdfae17 Binary files /dev/null and b/m/GobImages/Wall10.png differ diff --git a/m/GobImages/Wall11.png b/m/GobImages/Wall11.png new file mode 100644 index 0000000..15c3198 Binary files /dev/null and b/m/GobImages/Wall11.png differ diff --git a/m/GobImages/Wall12.png b/m/GobImages/Wall12.png new file mode 100644 index 0000000..40dfb93 Binary files /dev/null and b/m/GobImages/Wall12.png differ diff --git a/m/GobImages/Wall13.png b/m/GobImages/Wall13.png new file mode 100644 index 0000000..a6ebe11 Binary files /dev/null and b/m/GobImages/Wall13.png differ diff --git a/m/GobImages/Wall14.png b/m/GobImages/Wall14.png new file mode 100644 index 0000000..8267c04 Binary files /dev/null and b/m/GobImages/Wall14.png differ diff --git a/m/GobImages/Wall15.png b/m/GobImages/Wall15.png new file mode 100644 index 0000000..24e0ca6 Binary files /dev/null and b/m/GobImages/Wall15.png differ diff --git a/m/GobImages/Warehouse.png b/m/GobImages/Warehouse.png new file mode 100644 index 0000000..1938ea2 Binary files /dev/null and b/m/GobImages/Warehouse.png differ diff --git a/m/GobImages/hires/Andy.png b/m/GobImages/hires/Andy.png new file mode 100644 index 0000000..55beb95 Binary files /dev/null and b/m/GobImages/hires/Andy.png differ diff --git a/m/GobImages/hires/Artillery.png b/m/GobImages/hires/Artillery.png new file mode 100644 index 0000000..28d17a7 Binary files /dev/null and b/m/GobImages/hires/Artillery.png differ diff --git a/m/GobImages/hires/GalaxMiner.png b/m/GobImages/hires/GalaxMiner.png new file mode 100644 index 0000000..8da06ea Binary files /dev/null and b/m/GobImages/hires/GalaxMiner.png differ diff --git a/m/GobImages/hires/Galaxite1a.png b/m/GobImages/hires/Galaxite1a.png new file mode 100644 index 0000000..8092d31 Binary files /dev/null and b/m/GobImages/hires/Galaxite1a.png differ diff --git a/m/GobImages/hires/Galaxite1b.png b/m/GobImages/hires/Galaxite1b.png new file mode 100644 index 0000000..6730547 Binary files /dev/null and b/m/GobImages/hires/Galaxite1b.png differ diff --git a/m/GobImages/hires/Galaxite1c.png b/m/GobImages/hires/Galaxite1c.png new file mode 100644 index 0000000..3be0236 Binary files /dev/null and b/m/GobImages/hires/Galaxite1c.png differ diff --git a/m/GobImages/hires/Galaxite2a.png b/m/GobImages/hires/Galaxite2a.png new file mode 100644 index 0000000..3061e0d Binary files /dev/null and b/m/GobImages/hires/Galaxite2a.png differ diff --git a/m/GobImages/hires/Galaxite2b.png b/m/GobImages/hires/Galaxite2b.png new file mode 100644 index 0000000..d88dcf7 Binary files /dev/null and b/m/GobImages/hires/Galaxite2b.png differ diff --git a/m/GobImages/hires/Galaxite2c.png b/m/GobImages/hires/Galaxite2c.png new file mode 100644 index 0000000..4dae42e Binary files /dev/null and b/m/GobImages/hires/Galaxite2c.png differ diff --git a/m/GobImages/hires/Galaxite3a.png b/m/GobImages/hires/Galaxite3a.png new file mode 100644 index 0000000..bc73ce5 Binary files /dev/null and b/m/GobImages/hires/Galaxite3a.png differ diff --git a/m/GobImages/hires/Galaxite3b.png b/m/GobImages/hires/Galaxite3b.png new file mode 100644 index 0000000..c3c3c23 Binary files /dev/null and b/m/GobImages/hires/Galaxite3b.png differ diff --git a/m/GobImages/hires/Galaxite3c.png b/m/GobImages/hires/Galaxite3c.png new file mode 100644 index 0000000..7235c54 Binary files /dev/null and b/m/GobImages/hires/Galaxite3c.png differ diff --git a/m/GobImages/hires/Headquarters.png b/m/GobImages/hires/Headquarters.png new file mode 100644 index 0000000..6533291 Binary files /dev/null and b/m/GobImages/hires/Headquarters.png differ diff --git a/m/GobImages/hires/HumanResourceCenter.png b/m/GobImages/hires/HumanResourceCenter.png new file mode 100644 index 0000000..6a22d9a Binary files /dev/null and b/m/GobImages/hires/HumanResourceCenter.png differ diff --git a/m/GobImages/hires/LightTank.png b/m/GobImages/hires/LightTank.png new file mode 100644 index 0000000..16bc7b8 Binary files /dev/null and b/m/GobImages/hires/LightTank.png differ diff --git a/m/GobImages/hires/LongRangeInfantry.png b/m/GobImages/hires/LongRangeInfantry.png new file mode 100644 index 0000000..0279ac5 Binary files /dev/null and b/m/GobImages/hires/LongRangeInfantry.png differ diff --git a/m/GobImages/hires/MachineGunTower.png b/m/GobImages/hires/MachineGunTower.png new file mode 100644 index 0000000..98b4d85 Binary files /dev/null and b/m/GobImages/hires/MachineGunTower.png differ diff --git a/m/GobImages/hires/MachineGunVehicle.png b/m/GobImages/hires/MachineGunVehicle.png new file mode 100644 index 0000000..960ca3c Binary files /dev/null and b/m/GobImages/hires/MachineGunVehicle.png differ diff --git a/m/GobImages/hires/MediumTank.png b/m/GobImages/hires/MediumTank.png new file mode 100644 index 0000000..605a391 Binary files /dev/null and b/m/GobImages/hires/MediumTank.png differ diff --git a/m/GobImages/hires/MobileHeadquarters.png b/m/GobImages/hires/MobileHeadquarters.png new file mode 100644 index 0000000..8268798 Binary files /dev/null and b/m/GobImages/hires/MobileHeadquarters.png differ diff --git a/m/GobImages/hires/Processor.png b/m/GobImages/hires/Processor.png new file mode 100644 index 0000000..b319edb Binary files /dev/null and b/m/GobImages/hires/Processor.png differ diff --git a/m/GobImages/hires/Radar.png b/m/GobImages/hires/Radar.png new file mode 100644 index 0000000..9dc4982 Binary files /dev/null and b/m/GobImages/hires/Radar.png differ diff --git a/m/GobImages/hires/Reactor.png b/m/GobImages/hires/Reactor.png new file mode 100644 index 0000000..30321b0 Binary files /dev/null and b/m/GobImages/hires/Reactor.png differ diff --git a/m/GobImages/hires/ResearchCenter.png b/m/GobImages/hires/ResearchCenter.png new file mode 100644 index 0000000..7e5a3a9 Binary files /dev/null and b/m/GobImages/hires/ResearchCenter.png differ diff --git a/m/GobImages/hires/RocketTower.png b/m/GobImages/hires/RocketTower.png new file mode 100644 index 0000000..086fb0a Binary files /dev/null and b/m/GobImages/hires/RocketTower.png differ diff --git a/m/GobImages/hires/RocketVehicle.png b/m/GobImages/hires/RocketVehicle.png new file mode 100644 index 0000000..598401b Binary files /dev/null and b/m/GobImages/hires/RocketVehicle.png differ diff --git a/m/GobImages/hires/ShortRangeInfantry.png b/m/GobImages/hires/ShortRangeInfantry.png new file mode 100644 index 0000000..1e05ae1 Binary files /dev/null and b/m/GobImages/hires/ShortRangeInfantry.png differ diff --git a/m/GobImages/hires/SideWinder.png b/m/GobImages/hires/SideWinder.png new file mode 100644 index 0000000..c3853c9 Binary files /dev/null and b/m/GobImages/hires/SideWinder.png differ diff --git a/m/GobImages/hires/TakeoverSpecialist.png b/m/GobImages/hires/TakeoverSpecialist.png new file mode 100644 index 0000000..cd9d881 Binary files /dev/null and b/m/GobImages/hires/TakeoverSpecialist.png differ diff --git a/m/GobImages/hires/Tree.png b/m/GobImages/hires/Tree.png new file mode 100644 index 0000000..0a22ce3 Binary files /dev/null and b/m/GobImages/hires/Tree.png differ diff --git a/m/GobImages/hires/Tree2.png b/m/GobImages/hires/Tree2.png new file mode 100644 index 0000000..16edcfc Binary files /dev/null and b/m/GobImages/hires/Tree2.png differ diff --git a/m/GobImages/hires/Tree3.png b/m/GobImages/hires/Tree3.png new file mode 100644 index 0000000..78698b6 Binary files /dev/null and b/m/GobImages/hires/Tree3.png differ diff --git a/m/GobImages/hires/VehicleTransportStation.png b/m/GobImages/hires/VehicleTransportStation.png new file mode 100644 index 0000000..c9adc9f Binary files /dev/null and b/m/GobImages/hires/VehicleTransportStation.png differ diff --git a/m/GobImages/hires/Wall00.png b/m/GobImages/hires/Wall00.png new file mode 100644 index 0000000..5674e0e Binary files /dev/null and b/m/GobImages/hires/Wall00.png differ diff --git a/m/GobImages/hires/Wall01.png b/m/GobImages/hires/Wall01.png new file mode 100644 index 0000000..ecfe672 Binary files /dev/null and b/m/GobImages/hires/Wall01.png differ diff --git a/m/GobImages/hires/Wall02.png b/m/GobImages/hires/Wall02.png new file mode 100644 index 0000000..314eebf Binary files /dev/null and b/m/GobImages/hires/Wall02.png differ diff --git a/m/GobImages/hires/Wall03.png b/m/GobImages/hires/Wall03.png new file mode 100644 index 0000000..94b6c04 Binary files /dev/null and b/m/GobImages/hires/Wall03.png differ diff --git a/m/GobImages/hires/Wall04.png b/m/GobImages/hires/Wall04.png new file mode 100644 index 0000000..61eaeb4 Binary files /dev/null and b/m/GobImages/hires/Wall04.png differ diff --git a/m/GobImages/hires/Wall05.png b/m/GobImages/hires/Wall05.png new file mode 100644 index 0000000..22860da Binary files /dev/null and b/m/GobImages/hires/Wall05.png differ diff --git a/m/GobImages/hires/Wall06.png b/m/GobImages/hires/Wall06.png new file mode 100644 index 0000000..d3482f3 Binary files /dev/null and b/m/GobImages/hires/Wall06.png differ diff --git a/m/GobImages/hires/Wall07.png b/m/GobImages/hires/Wall07.png new file mode 100644 index 0000000..6b8f9cd Binary files /dev/null and b/m/GobImages/hires/Wall07.png differ diff --git a/m/GobImages/hires/Wall08.png b/m/GobImages/hires/Wall08.png new file mode 100644 index 0000000..22c88b4 Binary files /dev/null and b/m/GobImages/hires/Wall08.png differ diff --git a/m/GobImages/hires/Wall09.png b/m/GobImages/hires/Wall09.png new file mode 100644 index 0000000..21e6ef0 Binary files /dev/null and b/m/GobImages/hires/Wall09.png differ diff --git a/m/GobImages/hires/Wall10.png b/m/GobImages/hires/Wall10.png new file mode 100644 index 0000000..fdfae17 Binary files /dev/null and b/m/GobImages/hires/Wall10.png differ diff --git a/m/GobImages/hires/Wall11.png b/m/GobImages/hires/Wall11.png new file mode 100644 index 0000000..15c3198 Binary files /dev/null and b/m/GobImages/hires/Wall11.png differ diff --git a/m/GobImages/hires/Wall12.png b/m/GobImages/hires/Wall12.png new file mode 100644 index 0000000..40dfb93 Binary files /dev/null and b/m/GobImages/hires/Wall12.png differ diff --git a/m/GobImages/hires/Wall13.png b/m/GobImages/hires/Wall13.png new file mode 100644 index 0000000..a6ebe11 Binary files /dev/null and b/m/GobImages/hires/Wall13.png differ diff --git a/m/GobImages/hires/Wall14.png b/m/GobImages/hires/Wall14.png new file mode 100644 index 0000000..8267c04 Binary files /dev/null and b/m/GobImages/hires/Wall14.png differ diff --git a/m/GobImages/hires/Wall15.png b/m/GobImages/hires/Wall15.png new file mode 100644 index 0000000..24e0ca6 Binary files /dev/null and b/m/GobImages/hires/Wall15.png differ diff --git a/m/GobImages/hires/Warehouse.png b/m/GobImages/hires/Warehouse.png new file mode 100644 index 0000000..1938ea2 Binary files /dev/null and b/m/GobImages/hires/Warehouse.png differ diff --git a/m/GobPanel.cs b/m/GobPanel.cs new file mode 100644 index 0000000..f182952 --- /dev/null +++ b/m/GobPanel.cs @@ -0,0 +1,224 @@ +using System; +using System.Collections; +using System.ComponentModel; +using System.Drawing; +using System.Data; +using System.Windows.Forms; +using System.Drawing.Imaging; + +namespace m +{ + /// + /// Summary description for GobPanel. + /// + public class GobPanel : System.Windows.Forms.UserControl + { + private System.Windows.Forms.ComboBox comboSide; + private FlowPanel flowPanel; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + public GobPanel() + { + // This call is required by the Windows.Forms Form Designer. + InitializeComponent(); + + // Populate sides combo + + string[] astr = Helper.GetDisplayNames(typeof(Side)); + comboSide.Items.AddRange(astr); + comboSide.SelectedIndex = 0; + +// comboSide.DataSource = Enum.GetNames(typeof(Side)); + + // Fill + + FillGobPanel(); + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Component Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.comboSide = new System.Windows.Forms.ComboBox(); + this.flowPanel = new m.FlowPanel(); + this.SuspendLayout(); + // + // comboSide + // + this.comboSide.Dock = System.Windows.Forms.DockStyle.Top; + this.comboSide.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comboSide.Name = "comboSide"; + this.comboSide.Size = new System.Drawing.Size(208, 21); + this.comboSide.TabIndex = 0; + this.comboSide.SelectedIndexChanged += new System.EventHandler(this.comboSide_SelectedIndexChanged); + // + // flowPanel + // + this.flowPanel.BackColor = System.Drawing.Color.DarkKhaki; + this.flowPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.flowPanel.Location = new System.Drawing.Point(0, 21); + this.flowPanel.Name = "flowPanel"; + this.flowPanel.Size = new System.Drawing.Size(208, 451); + this.flowPanel.TabIndex = 1; + // + // GobPanel + // + this.AutoScroll = true; + this.BackColor = System.Drawing.Color.Black; + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.flowPanel, + this.comboSide}); + this.Name = "GobPanel"; + this.Size = new System.Drawing.Size(208, 472); + this.ResumeLayout(false); + + } + #endregion + + private void ChangeSide(Control ctl, Side side) { + if (!(ctl is PictureBox)) + return; + + PictureBox picb = (PictureBox)ctl; + Unit unit = picb.Tag as Unit; + if (unit == null) + return; + + unit.Side = side; + picb.Image = unit.GetBitmap(new Size(16, 16), null); + } + + void FillGobPanel() { + flowPanel.SuspendLayout(); + flowPanel.Controls.Clear(); + + // If no plugins are enabled then add the default (HT) MapItems + + if (Globals.Plugins.Count == 0) { + flowPanel.Controls.Add(CreatePictureBox(new Headquarters(Side.sideNeutral, 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new Radar(Side.sideNeutral, 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new ResearchCenter(Side.sideNeutral, 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new VehicleTransportStation(Side.sideNeutral, 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new Reactor(Side.sideNeutral, 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new HumanResourceCenter(Side.sideNeutral, 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new Processor(Side.sideNeutral, 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new Warehouse(Side.sideNeutral, 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new MachineGunTower(Side.sideNeutral, 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new RocketTower(Side.sideNeutral, 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new MobileHeadquarters(Side.sideNeutral, 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new GalaxMiner(Side.sideNeutral, 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new LightTank(Side.sideNeutral, 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new MediumTank(Side.sideNeutral, 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new MachineGunVehicle(Side.sideNeutral, 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new RocketVehicle(Side.sideNeutral, 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new Artillery(Side.sideNeutral, 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new ShortRangeInfantry(Side.sideNeutral, 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new LongRangeInfantry(Side.sideNeutral, 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new TakeoverSpecialist(Side.sideNeutral, 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new Andy(Side.sideNeutral, 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new Fox(Side.sideNeutral, 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new Galaxite(0, 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new Galaxite(1, 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new Galaxite(2, 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new Galaxite(3, 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new Galaxite(4, 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new Galaxite(5, 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new Galaxite(6, 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new Galaxite(7, 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new Galaxite(8, 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new Area(2, 2))); + flowPanel.Controls.Add(CreatePictureBox(new Wall(15))); + flowPanel.Controls.Add(CreatePictureBox(new Scenery("RocketArtifact", 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new Scenery("Tree", 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new Scenery("Tree1", 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new Scenery("Tree2", 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new Scenery("Tree3", 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new Scenery("Tree4", 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new Scenery("Tree5", 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new Scenery("Tree6", 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new Scenery("Tree7", 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new Scenery("Plant", 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new Scenery("Plant1", 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new Scenery("Plant2", 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new Scenery("Plant3", 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new Scenery("Plant4", 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new Scenery("Plant5", 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new Scenery("Rocks", 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new Replicator(Side.sideNeutral, 0, 0))); + flowPanel.Controls.Add(CreatePictureBox(new Activator + (Side.sideNeutral, 0, 0))); + + // Otherwise let the plugins add their own MapItems + + } else { + foreach (IPlugin plug in Globals.Plugins) { + IMapItem[] ami = plug.GetMapItems(); + foreach (IMapItem mi in ami) { + flowPanel.Controls.Add(CreatePictureBox(mi)); + } + } + } + flowPanel.ResumeLayout(); + } + + PictureBox CreatePictureBox(IMapItem mi) { + PictureBox picb = new PictureBox(); + picb.Image = mi.GetBitmap(new Size(16, 16), null); + picb.SizeMode = PictureBoxSizeMode.AutoSize; + picb.Tag = (Object)mi; + picb.MouseDown += new MouseEventHandler(PictureBox_MouseDown); + return picb; + } + + private void comboSide_SelectedIndexChanged(object sender, System.EventArgs e) { + Side side = (Side)comboSide.SelectedIndex; + +#if false + if (m_ctlSelected != null) { + ChangeSide(m_ctlSelected, side); + OnMapItemSelectionChanged((IMapItem)m_ctlSelected.Tag); + } +#endif + + foreach (PictureBox picb in flowPanel.Controls) { + ChangeSide(picb, side); + } + } + + private void PictureBox_MouseDown(Object sender, MouseEventArgs e) { + Control ctlSelected = (Control)sender; + + // Start drag drop + + LevelData ldat = new LevelData(); + IMapItem mi = (IMapItem)ctlSelected.Tag; + ldat.ami = new IMapItem[] { mi }; + ldat.txMouse = e.X / 16.0; + ldat.tyMouse = e.Y / 16.0; + ldat.Grid.Width = mi.Grid.Width; + ldat.Grid.Height = mi.Grid.Height; + DoDragDrop(ldat, DragDropEffects.Copy); + } + } +} diff --git a/m/GobPanel.resources b/m/GobPanel.resources new file mode 100644 index 0000000..ca594e6 Binary files /dev/null and b/m/GobPanel.resources differ diff --git a/m/GobPanel.resx b/m/GobPanel.resx new file mode 100644 index 0000000..1cb15a8 --- /dev/null +++ b/m/GobPanel.resx @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + GobPanel + + \ No newline at end of file diff --git a/m/LevelDoc.cs b/m/LevelDoc.cs new file mode 100644 index 0000000..a5ad4bf --- /dev/null +++ b/m/LevelDoc.cs @@ -0,0 +1,2049 @@ +using System; +using System.Collections; +using System.Collections.Specialized; +using System.IO; +using System.Windows.Forms; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters; +using System.Runtime.Serialization.Formatters.Binary; +using System.Runtime.Serialization.Formatters.Soap; +using System.ComponentModel; +using SpiffLib; +using System.Drawing; +using System.Drawing.Imaging; +using System.Text; +using System.Text.RegularExpressions; + +namespace m { + public enum LayerType { Start = 0, TileMap = 0, Galaxite, SurfaceDecal, Scenery, DepthSorted, SmokeFire, Area, Selection, End }; + + [Flags] + public enum LayerFlags { Templates = 1, Gobs = 2, Areas = 4, Default = (Templates | Gobs | Areas) }; + + [Serializable] + public class LevelDoc : Document, ISerializable, IDeserializationCallback { + string m_strOutputFilename = null; + bool m_fSwitchTemplatesEnabled = true; + IMapItem[] m_amiDeserializationTemp; + SideInfo[] m_asidiDeserializationTemp; + TemplateDoc m_tmpd = null; + string m_strTitle = null; + Rectangle m_trcBounds = new Rectangle(1, 1, 62, 62); + int m_nPlayersMin = 1; + int m_nPlayersMax = 1; + ArrayList m_alsmi = new ArrayList(); + ArrayList m_alsidi = new ArrayList(); + bool m_fUpdateDirty = false; + static bool s_fLoading = false; + int m_ctx = 64, m_cty = 64; + TriggerManager m_tgrm = new TriggerManager(); + UnitGroupManager m_ugm = new UnitGroupManager(); + SwitchManager m_swm = new SwitchManager(); + CounterManager m_ctrm = new CounterManager(); + string m_strComment; + int[] m_mpDirToDx = new int[] { 0, 1, 1, 1, 0, -1, -1, -1 }; + int[] m_mpDirToDy = new int[] { -1, -1, 0, 1, 1, 1, 0, -1 }; + ArrayList m_alsmiSelected = new ArrayList(); + + public delegate void ImageChangedHandler(); + public event ImageChangedHandler ImageChanged; + public delegate void ItemsRemovedHandler(IMapItem[] ami); + public event ItemsRemovedHandler ItemsRemoved; + public delegate void NameChangedHandler(LevelDoc lvld); + public event NameChangedHandler NameChanged; + + public LevelDoc(DocTemplate doct, string strFile, Object aobj) : base(doct, strFile) { + m_strTitle = m_doct.GetString(DocTemplate.Strings.NewFileName); + SetTemplateDoc((TemplateDoc)DocManager.GetActiveDocument(typeof(TemplateDoc))); + InitCommon(); + } + + // ISerializable implemented to prevent events from being serialized + public LevelDoc(SerializationInfo info, StreamingContext ctx) : base((DocTemplate)(((Hashtable)ctx.Context)["DocTemplate"]), (string)(((Hashtable)ctx.Context)["Filename"])) { + s_fLoading = true; + CaTypeSwitch.InitFixupList(); + + Title = info.GetString("Title"); + m_trcBounds.X = info.GetInt32("BoundsX"); + m_trcBounds.Y = info.GetInt32("BoundsY"); + m_trcBounds.Width = info.GetInt32("BoundsWidth"); + m_trcBounds.Height = info.GetInt32("BoundsHeight"); + + try { + // These were introduced at the same time so either they're both present or they both aren't + + m_ctx = info.GetInt32("Width"); + m_cty = info.GetInt32("Height"); + } catch (SerializationException) { + } + + // Default values in case they aren't present + + m_nPlayersMin = 1; + m_nPlayersMax = 1; + try { + // These were introduced at the same time so either they're both present or they both aren't + + m_nPlayersMin = info.GetInt32("MinPlayers"); + m_nPlayersMax = info.GetInt32("MaxPlayers"); + } catch (SerializationException) { + } + + // Side info + + try { + m_asidiDeserializationTemp = (SideInfo[])info.GetValue("SideInfos", typeof(SideInfo[])); + } catch (SerializationException) { + } + + // TriggerManager + + try { + m_tgrm = (TriggerManager)info.GetValue("TriggerManager", typeof(TriggerManager)); + } catch (SerializationException) { + m_tgrm = new TriggerManager(); + } + + // UnitGroupManager + + try { + m_ugm = (UnitGroupManager)info.GetValue("UnitGroupManager", typeof(UnitGroupManager)); + } catch (SerializationException) { + m_ugm = new UnitGroupManager(); + } + + // SwitchManager + + try { + m_swm = (SwitchManager)info.GetValue("SwitchManager", typeof(SwitchManager)); + } catch (SerializationException) { + m_swm = new SwitchManager(); + } + + // CounterManager + + try { + m_ctrm = (CounterManager)info.GetValue("CounterManager", typeof(CounterManager)); + } catch (SerializationException) { + m_ctrm = new CounterManager(); + } + + // Comment string + + try { + m_strComment = info.GetString("Comment"); + } catch (SerializationException) { + } + + // Support old .ld's that have separate .map files + + string strTemplatesFileName = null; + try { + string strFile = info.GetString("MapFileName"); + if (strFile != null) { + Stream stm = null; + try { + IFormatter fmtr = new BinaryFormatter(); + stm = new FileStream(m_strDir + Path.DirectorySeparatorChar + strFile, FileMode.Open, FileAccess.Read, FileShare.Read); + strTemplatesFileName = (string)fmtr.Deserialize(stm); + SetTemplateDoc((TemplateDoc)DocManager.OpenDocument(m_strDir + Path.DirectorySeparatorChar + strTemplatesFileName)); + m_alsmi.AddRange((IMapItem[])fmtr.Deserialize(stm)); + stm.Close(); + } catch { + if (stm != null) + stm.Close(); + } + } + } catch(SerializationException) { + } + + // Load the desired template set + + if (m_tmpd == null) { + try { + strTemplatesFileName = info.GetString("TemplatesFileName"); + SetTemplateDoc((TemplateDoc)DocManager.OpenDocument(m_strDir + Path.DirectorySeparatorChar + strTemplatesFileName)); + } catch (SerializationException) { + // If couldn't load it, use the active one + + SetTemplateDoc(null); + string strT = "Couldn't load " + strTemplatesFileName; + if (m_tmpd != null) + strT += " - Using " + m_tmpd.GetName(); + MessageBox.Show(strT); + } + } + + // Load the map items + + m_amiDeserializationTemp = (IMapItem[])info.GetValue("MapItems", typeof(IMapItem[])); + } + + public void OnDeserialization(object obSender) { + + // NEW FOR .NET 1.1 + // Couldn't do this during the deserialization constructor any more because the + // result is null references in the ArrayList. + + m_alsidi.AddRange(m_asidiDeserializationTemp); + m_asidiDeserializationTemp = null; + + // NEW FOR .NET 1.1 + // Couldn't do this during the deserialization constructor any more because the + // result is null references in the ArrayList. + + m_alsmi.AddRange(m_amiDeserializationTemp); + + // Set events + + AddEventHandlers(m_amiDeserializationTemp); + m_amiDeserializationTemp = null; + + // Incorporate old-style switches into the SwitchManager + + CaTypeSwitch.Fixup(this); + + // Common initialization + + InitCommon(); + } + + public void GetObjectData(SerializationInfo info, StreamingContext context) { + info.AddValue("Title", Title); + info.AddValue("MapItems", (IMapItem[])m_alsmi.ToArray(typeof(IMapItem))); + info.AddValue("BoundsX", Bounds.X); + info.AddValue("BoundsY", Bounds.Y); + info.AddValue("BoundsWidth", Bounds.Width); + info.AddValue("BoundsHeight", Bounds.Height); + info.AddValue("MinPlayers", MinPlayers); + info.AddValue("MaxPlayers", MaxPlayers); + info.AddValue("SideInfos", GetSideInfos()); + info.AddValue("Width", Width); + info.AddValue("Height", Height); + info.AddValue("TriggerManager", m_tgrm); + info.AddValue("UnitGroupManager", m_ugm); + info.AddValue("SwitchManager", m_swm); + info.AddValue("CounterManager", m_ctrm); + info.AddValue("Comment", m_strComment); + if (m_tmpd == null) { + info.AddValue("TemplatesFileName", ""); + } else { + info.AddValue("TemplatesFileName", Path.GetFileName(m_tmpd.GetPath())); + } + } + + void InitCommon() { + // We want to know when the application goes idle for batching MapItem notifications + // into single doc notifications + + Application.Idle += new EventHandler(Application_IdleHandler); + + // We want to know when the active template doc changes + + TemplateDocTemplate doct = (TemplateDocTemplate)DocManager.FindDocTemplate(typeof(TemplateDoc)); + doct.DocActive += new DocTemplate.DocActiveHandler(TemplateDocTemplate_DocActiveHandler); + doct.TemplatesRemoved += new TemplateDocTemplate.TemplatesRemovedHandler(TemplateDocTemplate_TemplatesRemovedHandler); + doct.TemplatesAdded += new TemplateDocTemplate.TemplatesAddedHandler(TemplateDocTemplate_TemplatesAddedHandler); + SetModified(false); + } + + public void Dispose() { + m_alsmi.Clear(); + } + + void Application_IdleHandler(object sender, EventArgs e) { + s_fLoading = false; + if (!m_fUpdateDirty) + return; + m_fUpdateDirty = false; + if (ImageChanged != null) + ImageChanged(); + } + + public void Refresh() { + m_fUpdateDirty = true; + } + + void TemplateDocTemplate_DocActiveHandler(Document doc) { + // TemplateDocs get loaded and activated during LevelDoc + // deserialization. We don't want to change the template doc + // of the currently active level doc in this case + + if (!s_fLoading && m_fSwitchTemplatesEnabled) { + if (DocManager.GetActiveDocument(typeof(LevelDoc)) == this) + SetTemplateDoc((TemplateDoc)doc); + } + } + + public bool SwitchTemplatesEnabled { + get { + return m_fSwitchTemplatesEnabled; + } + set { + m_fSwitchTemplatesEnabled = value; + } + } + + public string OutputFilename { + get { + return m_strOutputFilename; + } + set { + m_strOutputFilename = value; + } + } + + void SetTemplateDoc(TemplateDoc tmpd) { + if (m_tmpd == tmpd) + return; + + // Active template doc change. Remove event handlers from old one, add event handlers to new + + if (m_tmpd != null) + m_tmpd.BackgroundChanged -= new TemplateDoc.BackgroundChangedHandler(TemplateDoc_BackgroundChangedHandler); + + // Assign new. If null, use current active + + m_tmpd = tmpd; + if (m_tmpd == null) + m_tmpd = (TemplateDoc)DocManager.GetActiveDocument(typeof(TemplateDoc)); + + // Put in hooks + + if (m_tmpd != null) + m_tmpd.BackgroundChanged += new TemplateDoc.BackgroundChangedHandler(TemplateDoc_BackgroundChangedHandler); + + // Raise changed event + + m_fUpdateDirty = true; + + // Doc modified + + SetModified(true); + } + + public override bool Save() { + // Save the template doc when we save the level doc. For convenience + + if (base.Save()) { + if (m_tmpd != null && m_tmpd.IsModified()) + m_tmpd.Save(); + return true; + } + return false; + } + + public override bool SaveAs(string strFile) { + // Save the template doc when we save the level doc. For convenience + + if (base.SaveAs(strFile)) { + if (m_tmpd != null) + m_tmpd.Save(); + return true; + } + return false; + } + + public TemplateDoc GetTemplateDoc() { + return m_tmpd; + } + + public override string GetName() { + return m_strTitle; + } + + private SideInfo[] GetSideInfos() { + return (SideInfo[])m_alsidi.ToArray(typeof(SideInfo)); + } + + public SideInfo GetSideInfo(Side side) { + foreach (SideInfo sidi in m_alsidi) { + if (sidi.Side == side) + return sidi; + } + + SideInfo sidiNew = new SideInfo(side); + m_alsidi.Add(sidiNew); + SetModified(true); + return sidiNew; + } + + public void Draw(Bitmap bm, IMapItem miExclude, Size sizTile, TemplateDoc tmpd, LayerFlags lyrf) { + ArrayList alsmiSelected = m_alsmiSelected; + + // Draw tile map + DrawTileMap(bm, alsmiSelected, sizTile, tmpd, lyrf); + + // Draw other layers + using (Graphics g = Graphics.FromImage(bm)) { + for (LayerType layer = LayerType.Galaxite; layer < LayerType.End; layer++) { + if (layer == LayerType.Area) { + if ((lyrf & LayerFlags.Areas) == 0) + continue; + } else { + if ((lyrf & LayerFlags.Gobs) == 0) + continue; + } + foreach (IMapItem mi in m_alsmi) { + if (mi != miExclude) { + int x = (int)(mi.tx * sizTile.Width); + int y = (int)(mi.ty * sizTile.Height); + mi.Draw(g, x, y, sizTile, tmpd, layer, alsmiSelected != null ? alsmiSelected.Contains(mi) : false); + } + } + } + + // Draw bounds + Pen pen = new Pen(new SolidBrush(Color.FromArgb(0, 255, 0))); + pen.Width = 2; + g.DrawRectangle(pen, Bounds.X * sizTile.Width - pen.Width + 1, Bounds.Y * sizTile.Height - pen.Width + 1, Bounds.Width * sizTile.Width + pen.Width, Bounds.Height * sizTile.Height + pen.Width); + } + } + + public void DrawTileMap(Bitmap bm, ArrayList alsmiSelected, Size sizTile, TemplateDoc tmpd, LayerFlags lyrf) { + // Draw background + + using (Graphics g = Graphics.FromImage(bm)) { + Template tmplBackground = null; + if (tmpd != null) + tmplBackground = tmpd.GetBackgroundTemplate(); + if (tmplBackground == null) { + g.FillRectangle(new SolidBrush(Color.Black), 0, 0, bm.Width, bm.Height); + for (int x = sizTile.Width; x < bm.Width; x += sizTile.Width) { + for (int y = sizTile.Height; y < bm.Height; y += sizTile.Height) { + bm.SetPixel(x, y, Color.BlanchedAlmond); + } + } + } else { + for (int x = 0; x < bm.Width; x += tmplBackground.Bitmap.Width) { + for (int y = 0; y < bm.Height; y += tmplBackground.Bitmap.Height) + g.DrawImage(tmplBackground.Bitmap, x, y); + } + } + + // Draw templates + + if ((lyrf & LayerFlags.Templates) != 0) { + foreach (IMapItem mi in m_alsmi) { + int x = (int)mi.tx * sizTile.Width; + int y = (int)mi.ty * sizTile.Height; + mi.Draw(g, x, y, sizTile, tmpd, LayerType.TileMap, alsmiSelected != null ? alsmiSelected.Contains(mi) : false); + } + } + } + } + + public void AddMapItems(IMapItem[] ami) { + m_alsmi.AddRange(ami); + AddEventHandlers(ami); + m_fUpdateDirty = true; + SetModified(true); + } + + public void RemoveMapItems(IMapItem[] ami) { + for (int n = 0; n < ami.Length; n++) + m_alsmi.Remove(ami[n]); + RemoveEventHandlers(ami); + if (ItemsRemoved != null) + ItemsRemoved(ami); + m_fUpdateDirty = true; + SetModified(true); + } + + void AddEventHandlers(IMapItem[] ami) { + foreach (IMapItem mi in ami) + mi.PropertyChanged += new PropertyChangedHandler(IMapItem_PropertyChanged); + } + + void RemoveEventHandlers(IMapItem[] ami) { + foreach (IMapItem mi in ami) + mi.PropertyChanged -= new PropertyChangedHandler(IMapItem_PropertyChanged); + } + + void IMapItem_PropertyChanged(IMapItem mi, string strProperty) { + SetModified(true); + m_fUpdateDirty = true; + } + + void TemplateDoc_BackgroundChangedHandler(TemplateDoc tmpd) { + m_fUpdateDirty = true; + } + + private void TemplateDocTemplate_TemplatesAddedHandler(TemplateDoc tmpd, string[] astrNames) { + TemplatesAddedRemoved(astrNames); + } + + + private void TemplateDocTemplate_TemplatesRemovedHandler(TemplateDoc tmpd, string[] astrNames) { + TemplatesAddedRemoved(astrNames); + } + + void TemplatesAddedRemoved(string[] astrNames) { + foreach (string strName in astrNames) { + for (int n = 0; n < m_alsmi.Count; n++) { + Tile tile = m_alsmi[n] as Tile; + if (tile != null && tile.Name == strName) { + m_fUpdateDirty = true; + SetModified(true); + return; + } + } + } + } + + public IMapItem HitTest(int x, int y, Size sizTile, TemplateDoc tmpd, LayerFlags lyrf) { + // MapItems on top are first + for (int i = m_alsmi.Count - 1; i >= 0; i--) { + IMapItem mi = (IMapItem)m_alsmi[i]; + if (mi.HitTest(x, y, sizTile, tmpd)) { + if (mi is Area) { + if ((lyrf & LayerFlags.Areas) != 0) + return mi; + } else if (mi is Tile) { + if ((lyrf & LayerFlags.Templates) != 0) + return mi; + } else { + if ((lyrf & LayerFlags.Gobs) != 0) + return mi; + } + } + } + return null; + } + + public ArrayList HitTest(Rectangle rc, Size sizTile, TemplateDoc tmpd, LayerFlags lyrf) { + ArrayList alsmi = new ArrayList(); + foreach (IMapItem mi in m_alsmi) { + Point ptCenter = mi.GetCenterPoint(sizTile); + if (rc.Contains(ptCenter)) { + if (mi is Area) { + if ((lyrf & LayerFlags.Areas) != 0) + alsmi.Add(mi); + } else if (mi is Tile) { + if ((lyrf & LayerFlags.Templates) != 0) + alsmi.Add(mi); + } else { + if ((lyrf & LayerFlags.Gobs) != 0) + alsmi.Add(mi); + } + } + } + return alsmi; + } + + public Bitmap GetMapBitmap(Size sizTile, TemplateDoc tmpd, bool fTilesOnly) { + Bitmap bm = new Bitmap(Width * sizTile.Width, Height * sizTile.Height, PixelFormat.Format24bppRgb); + + DrawTileMap(bm, null, sizTile, tmpd, LayerFlags.Default); + + if (!fTilesOnly) { + using (Graphics g = Graphics.FromImage(bm)) { + for (LayerType layer = LayerType.Galaxite; layer < LayerType.End; layer++) { + foreach (IMapItem mi in m_alsmi) { + int x = (int)(mi.tx * sizTile.Width); + int y = (int)(mi.ty * sizTile.Height); + mi.Draw(g, x, y, sizTile, tmpd, layer, false); + } + } + } + } + + Bitmap bmT = new Bitmap(Bounds.Width * sizTile.Width, Bounds.Height * sizTile.Height, PixelFormat.Format24bppRgb); + using (Graphics g = Graphics.FromImage(bmT)) { + Rectangle rcSrc = new Rectangle(Bounds.X * sizTile.Width, Bounds.Y * sizTile.Height, bmT.Width, bmT.Height); + g.DrawImage(bm, 0, 0, rcSrc, GraphicsUnit.Pixel); + } + bm.Dispose(); + return bmT; + } + + public TerrainTypes[,] GetTerrainMap(Size sizTile, TemplateDoc tmpd, bool fStructures) { + // Get raw terrain map + + TerrainTypes[,] aterMap = GetRawTerrainMap(sizTile, tmpd); + + // Flood fill to mark areas. The "biggest" area is accessible. + // All the smaller areas are not; mark them as such. + + int[,] anFill = new int[aterMap.GetLength(0), aterMap.GetLength(1)]; + + // Mark all the areas in the fill map that are known to be not accessible. + + for (int ty = 0; ty < aterMap.GetLength(0); ty++) { + for (int tx = 0; tx < aterMap.GetLength(1); tx++) { + if (aterMap[ty, tx] == TerrainTypes.Blocked) { + anFill[ty, tx] = -1; + } + } + } + + // Mark structures if asked, before flood filling + // This is used for terrain diffing to see if structures make terrain inaccessible + + if (fStructures) { + foreach (IMapItem mi in m_alsmi) { + if (mi is Structure) { + for (int ty = (int)mi.ty; ty < mi.ty + mi.cty; ty++) { + for (int tx = (int)mi.tx; tx < mi.tx + mi.ctx; tx++) { + if (Bounds.Contains(tx, ty)) { + int txT = tx - Bounds.Left; + int tyT = ty - Bounds.Top; + anFill[tyT, txT] = -1; + } + } + } + } + } + } + + // Start flood filling areas + + int nFillValue = 1; + ArrayList alsFillCounts = new ArrayList(); + alsFillCounts.Add(0); + for (int ty = 0; ty < anFill.GetLength(0); ty++) { + for (int tx = 0; tx < anFill.GetLength(1); tx++) { + if (anFill[ty, tx] == 0) { + int cFill = FloodFill(anFill, tx, ty, nFillValue); + nFillValue++; + alsFillCounts.Add(cFill); + } + } + } + + // Find the largest count; that is the accessible area + + int nFillValueLargest = -1; + int cLargest = 0; + for (int n = 0; n < alsFillCounts.Count; n++) { + if ((int)alsFillCounts[n] > cLargest) { + cLargest = (int)alsFillCounts[n]; + nFillValueLargest = n; + } + } + + // Now mark the areas that aren't this fill value as inaccessible + + for (int ty = 0; ty < anFill.GetLength(0); ty++) { + for (int tx = 0; tx < anFill.GetLength(1); tx++) { + if (anFill[ty, tx] != nFillValueLargest) { + aterMap[ty, tx] = TerrainTypes.Blocked; + } + } + } + + // Mark where areas are in the terrain + + foreach (IMapItem mi in m_alsmi) { + if (mi is Area) { + for (int ty = (int)mi.ty; ty < mi.ty + mi.cty; ty++) { + for (int tx = (int)mi.tx; tx < mi.tx + mi.ctx; tx++) { + if (Bounds.Contains(tx, ty)) { + int txT = tx - Bounds.Left; + int tyT = ty - Bounds.Top; + if (aterMap[tyT, txT] == TerrainTypes.Open) + aterMap[tyT, txT] = TerrainTypes.Area; + } + } + } + } + } + + // Mark where walls are in the terrain + + foreach (IMapItem mi in m_alsmi) { + if (mi is Wall) { + int tx = (int)mi.tx; + int ty = (int)mi.ty; + if (Bounds.Contains(tx, ty)) { + int txT = tx - Bounds.Left; + int tyT = ty - Bounds.Top; + aterMap[tyT, txT] = TerrainTypes.Wall; + } + } + } + + // Done + + return aterMap; + } + + int FloodFill(int[,] anFill, int tx, int ty, int nFillValue) { + int nFillMatch = anFill[ty, tx]; + if (nFillMatch == nFillValue) + return 0; + + ArrayList alsPoints = new ArrayList(); + anFill[ty, tx] = nFillValue; + alsPoints.Add(new Point(tx, ty)); + int ipt = 0; + int cFill = 1; + + while (true) { + bool fFound = false; + Point ptT = (Point)alsPoints[ipt]; + for (int dir = 0; dir < 8; dir++) { + int txT = ptT.X + m_mpDirToDx[dir]; + int tyT = ptT.Y + m_mpDirToDy[dir]; + if (txT < 0 || txT >= anFill.GetLength(1)) + continue; + if (tyT < 0 || tyT >= anFill.GetLength(0)) + continue; + if (anFill[tyT, txT] == nFillMatch) { + cFill++; + anFill[tyT, txT] = nFillValue; + alsPoints.Add(new Point(txT, tyT)); + ipt++; + fFound = true; + break; + } + } + if (!fFound) { + alsPoints.RemoveAt(ipt); + ipt--; + if (ipt < 0) + break; + } + } + + return cFill; + } + + TerrainTypes[,] GetRawTerrainMap(Size sizTile, TemplateDoc tmpd) { + TerrainTypes[,] aterMap = new TerrainTypes[Bounds.Height, Bounds.Width]; + foreach (IMapItem mi in m_alsmi) { + if (mi is Tile) { + Tile tile = mi as Tile; + int x = (int)mi.tx * sizTile.Width; + int y = (int)mi.ty * sizTile.Height; + Rectangle rc = tile.GetBoundingRectAt(x, y, sizTile, m_tmpd); + rc.Width /= sizTile.Width; + rc.Height /= sizTile.Height; + rc.X /= sizTile.Width; + rc.Y /= sizTile.Height; + rc.Intersect(Bounds); + + for (int ty = rc.Y - (int)tile.ty; ty < rc.Bottom - (int)tile.ty; ty++) { + for (int tx = rc.X - (int)tile.tx; tx < rc.Right - (int)tile.tx; tx++) { + if (tile.Visibility == null || tile.IsVisible(tx, ty)) { + Template tmpl = tile.GetTemplate(m_tmpd); + + // Maybe this template doesn't exist in document's current tile collection + + if (tmpl == null) + continue; + + // Make sure this part of the template is occupied first + + if (tmpl.OccupancyMap[ty, tx]) + aterMap[ty + (int)tile.ty - Bounds.Y, tx + (int)tile.tx - Bounds.X] = tmpl.TerrainMap[ty, tx]; + } + } + } + } + if (mi is Wall) { + int tx = (int)mi.tx - Bounds.X; + int ty = (int)mi.ty - Bounds.Y; + if (tx < 0 || tx >= Bounds.Width) + continue; + if (ty < 0 || ty >= Bounds.Height) + continue; + aterMap[ty, tx] = TerrainTypes.Blocked; + } + } + return aterMap; + } + + public TerrainColors[,] GetTerrainColorsMap(Size sizTile, TemplateDoc tmpd) { + TerrainColors[,] atclrMap = new TerrainColors[Bounds.Height * 2, Bounds.Width * 2]; + foreach (IMapItem mi in m_alsmi) { + Tile tile = mi as Tile; + if (tile == null) + continue; + int x = (int)mi.tx * sizTile.Width; + int y = (int)mi.ty * sizTile.Height; + Rectangle rc = tile.GetBoundingRectAt(x, y, sizTile, m_tmpd); + rc.Width /= sizTile.Width; + rc.Height /= sizTile.Height; + rc.X /= sizTile.Width; + rc.Y /= sizTile.Height; + rc.Intersect(Bounds); + + for (int ty = rc.Y - (int)tile.ty; ty < rc.Bottom - (int)tile.ty; ty++) { + for (int tx = rc.X - (int)tile.tx; tx < rc.Right - (int)tile.tx; tx++) { + if (tile.Visibility == null || tile.Visibility[ty, tx]) { + Template tmpl = tile.GetTemplate(m_tmpd); + + // Maybe this template doesn't exist in document's current tile collection + + if (tmpl == null) + continue; + + // TerrainColors are a 2x2 subgrid inside each tile + + for (int tyT = 0; tyT < 2; tyT++) { + for (int txT = 0; txT < 2; txT++) { + TerrainColors tclr = TerrainColors.Grass; + if (tmpl.TerrainColors != null) + tclr = tmpl.TerrainColors[ty * 2 + tyT, tx * 2 + txT]; + atclrMap[(ty + (int)tile.ty - Bounds.Y) * 2 + tyT, (tx + (int)tile.tx - Bounds.X) * 2 + txT] = tclr; + } + } + } + } + } + } + return atclrMap; + } + + // + // Level validation stuff + // + + public enum ValidateError { + Info, + Warning, + Error + } + + public delegate void ValidateErrorDelegate(LevelDoc lvld, ValidateError ve, int tx, int ty, object ob, string str); + +#if false + public int Validate(ValidateErrorDelegate dgt) { + return 0; + } +#else + enum ItemMask { None = 0, Galaxite = 1, Wall = 2, Unreachable = 4, Scenery = 8, Structure = 16, MobileUnit = 32 }; + + public int Validate(ValidateErrorDelegate dgt) { + int cError = 0; + + // Validate switch count + + int cSwitchesMax = 16; + if (m_swm.Items.Count > cSwitchesMax) { + dgt(this, ValidateError.Error, 0, 0, null, String.Format("Max switches is {0}, this mission has {1}", cSwitchesMax, m_swm.Items.Count)); + cError++; + } + + // Validate area count + + int cAreas = 0; + foreach (IMapItem mi in m_alsmi) { + if (mi is Area) + cAreas++; + } + int cAreasMax = 32; + if (cAreas > cAreasMax) { + dgt(this, ValidateError.Error, 0, 0, null, String.Format("Max areas is {0}, this mission has {1}", cAreasMax, cAreas)); + cError++; + } + + // Validate SideInfo + + int cHuman = 0; + foreach (SideInfo sidi in m_alsidi) { + if (sidi.Intelligence == Intelligence.Human) + cHuman++; + +#if false + if (sidi.InitialCredits == 0) + dgt(this, ValidateError.Warning, 0, 0, sidi, String.Format("Side {0} starts with no credits", sidi.Side)); +#endif + } + + +#if false + // Don't allow multi-player yet + + if (MaxPlayers != 1) { + dgt(this, ValidateError.Error, 0, 0, null, "Multi-player not supported yet: MaxPlayers should be 1"); + cError++; + } + + // Must have 1 human side + + if (cHuman != 1) { + dgt(this, ValidateError.Error, 0, 0, null, "Must have 1 human side!"); + cError++; + } +#endif + + if (cHuman > MaxPlayers) { + dgt(this, ValidateError.Error, 0, 0, null, String.Format("MaxPlayers is {0}, # of human sides is {1}", MaxPlayers, cHuman)); + cError++; + } + + // Validate gob inside/outside of boundaries + + foreach (IMapItem mi in m_alsmi) { + if (mi is Galaxite) + continue; + if (mi is Wall) + continue; + if (mi is Tile) + continue; + + // Gobs can be inside or outside of boundaries. If gobs are intersected by the boundaries + // that is not ok and will send the game into fits + + Rectangle rcT = new Rectangle(Bounds.Location, Bounds.Size); + if (!Bounds.Contains(new Rectangle((int)mi.tx, (int)mi.ty, mi.ctx, mi.cty))) { + if (mi is Area) { + Area area = (Area)mi; + dgt(this, ValidateError.Error, (int)mi.tx, (int)mi.ty, area, String.Format("Area '{0}' out of bounds", area.Name)); + cError++; + } else { + // If we have a gob that is "partially" outside, and gets compiled into the game, + // errors will occur at runtime, so make this an error + + dgt(this, ValidateError.Error, (int)mi.tx, (int)mi.ty, mi, mi.ToString() + " out of bounds"); + cError++; + } + } + } + + // Collect placement information. + + // enum ItemMask { None = 0, Galaxite = 1, Wall = 2, Unreachable = 4, Scenery = 8, Structure = 16, MobileUnit = 32 }; + + // Initialize with all "unreachable areas" appropriately + + TerrainTypes[,] aterMap = GetTerrainMap(m_tmpd.TileSize, m_tmpd, false); + ItemMask[,] aimMap = new ItemMask[Bounds.Height, Bounds.Width]; + for (int ty = 0; ty < Bounds.Height; ty++) { + for (int tx = 0; tx < Bounds.Width; tx++) { + switch (aterMap[ty, tx]) { + case TerrainTypes.Blocked: + case TerrainTypes.Wall: + aimMap[ty, tx] |= ItemMask.Unreachable; + break; + } + } + } + + // Validate placement + + foreach (IMapItem mi in m_alsmi) { + ItemMask im = ItemMask.None; + ItemMask imInvalid = ItemMask.None; + if (mi is Galaxite) { + im = ItemMask.Galaxite; + imInvalid = ItemMask.Wall | ItemMask.Unreachable | ItemMask.Structure; + } else if (mi is Wall) { + im = ItemMask.Wall; + imInvalid = ItemMask.Galaxite | ItemMask.Wall | ItemMask.Structure | ItemMask.MobileUnit; + } else if (mi is Scenery) { + im = ItemMask.Scenery; + imInvalid = ItemMask.None; + } else if (mi is Structure) { + im = ItemMask.Structure; + imInvalid = ItemMask.Galaxite | ItemMask.Wall | ItemMask.Unreachable | ItemMask.Structure | ItemMask.MobileUnit; + } else if (mi is MobileUnit) { + im = ItemMask.MobileUnit; + imInvalid = ItemMask.Wall | ItemMask.Unreachable | ItemMask.Structure | ItemMask.MobileUnit; + } + if (im == ItemMask.None) + continue; + + // Check each tile occupied by this mi + + ItemMask imError = ItemMask.None; + for (int ty = (int)mi.ty; ty < (int)mi.ty + mi.cty; ty++) { + for (int tx = (int)mi.tx; tx < (int)mi.tx + mi.ctx; tx++) { + int txT = tx - Bounds.Left; + int tyT = ty - Bounds.Top; + if (txT < 0 || tyT < 0) + continue; + if (txT >= Bounds.Width || tyT >= Bounds.Height) + continue; + ItemMask imInvalidOverlap = aimMap[tyT, txT] & imInvalid; + aimMap[tyT, txT] |= im; + ItemMask imErrorNew = (ItemMask)(imInvalidOverlap & ~imError); + if (imErrorNew != ItemMask.None) { + imError |= imErrorNew; + + // Build up the error string + + ItemMask[] aimValues = (ItemMask[])Enum.GetValues(typeof(ItemMask)); + string strT = ""; + for (int n = 0; n < aimValues.Length; n++) { + if ((aimValues[n] & imErrorNew) != 0) + strT += aimValues[n].ToString() + ","; + } + if (strT != "") + strT = strT.Substring(0, strT.Length - 1); + + dgt(this, ValidateError.Error, (int)mi.tx, (int)mi.ty, mi, mi.ToString() + " is on top of: " + strT); + cError++; + } + } + } + } + + // Validate that structures don't make terrain inaccessible + // Everywhere there is a blocked section in aterMapStructs that isn't + // blocked in aterMap and isn't a structure is now inaccessible because + // of a structure block + + TerrainTypes[,] aterMapStructs = GetTerrainMap(m_tmpd.TileSize, m_tmpd, true); + for (int ty = 0; ty < aterMap.GetLength(0); ty++) { + for (int tx = 0; tx < aterMap.GetLength(1); tx++) { + // Check + + bool fSrcOpen = (aimMap[ty, tx] & (ItemMask.Unreachable | ItemMask.Structure)) == 0; + if (fSrcOpen && aterMapStructs[ty, tx] == TerrainTypes.Blocked) { + dgt(this, ValidateError.Error, tx + Bounds.Left, ty + Bounds.Top, null, String.Format("Terrain at {0},{1} is inaccessible due to structure blockage", tx + Bounds.Left, ty + Bounds.Top)); + cError++; + } + } + } + + // Validate areas in triggers exist + + StringCollection strc = CaTypeArea.GetAreaNames(); + foreach (Side side in Enum.GetValues(typeof(Side))) { + Trigger[] atgr = m_tgrm.GetTriggerList(side); + foreach (Trigger tgr in atgr) { + foreach (CaBase cab in tgr.Conditions) { + foreach (CaType cat in cab.GetTypes()) { + if (cat is CaTypeArea) { + CaTypeArea catArea = (CaTypeArea)cat; + if (strc.IndexOf(catArea.Area) < 0) { + dgt(this, ValidateError.Error, 0, 0, null, "Area " + catArea.Area + " in trigger " + tgr.ToString() + " doesn't exist!"); + cError++; + } + } + } + } + foreach (CaBase cab in tgr.Actions) { + foreach (CaType cat in cab.GetTypes()) { + if (cat is CaTypeArea) { + CaTypeArea catArea = (CaTypeArea)cat; + if (strc.IndexOf(catArea.Area) < 0) { + dgt(this, ValidateError.Error, 0, 0, null, "Area " + catArea.Area + " in trigger " + tgr.ToString() + " doesn't exist!"); + cError++; + } + } + } + } + } + } + + // Validate unit groups in triggers + + foreach (Side side in Enum.GetValues(typeof(Side))) { + Trigger[] atgr = m_tgrm.GetTriggerList(side); + foreach (Trigger tgr in atgr) { + foreach (CaBase cab in tgr.Conditions) { + foreach (CaType cat in cab.GetTypes()) { + if (cat is CaTypeUnitGroup) { + CaTypeUnitGroup catUg = (CaTypeUnitGroup)cat; + if (Array.IndexOf(atgr, tgr) < 0) { + dgt(this, ValidateError.Error, 0, 0, null, "Orphaned UnitGroup in trigger " + tgr.ToString()); + cError++; + } + } + } + } + foreach (CaBase cab in tgr.Actions) { + foreach (CaType cat in cab.GetTypes()) { + if (cat is CaTypeArea) { + CaTypeArea catArea = (CaTypeArea)cat; + if (strc.IndexOf(catArea.Area) < 0) { + dgt(this, ValidateError.Error, 0, 0, null, "Orphaned UnitGroup in trigger " + tgr.ToString()); + cError++; + } + } + } + } + } + } + + // Validate triggers per side limit + + int cTriggersPerSideMax = 128; + foreach (Side side in Enum.GetValues(typeof(Side))) { + Trigger[] atgr = m_tgrm.GetTriggerList(side); + if (atgr == null) + continue; + if (atgr.Length > cTriggersPerSideMax) { + string strT = String.Format("Triggers per side {0}. Side {1} has {2} triggers", cTriggersPerSideMax, side.ToString(), atgr.Length); + dgt(this, ValidateError.Error, 0, 0, null, strT); + cError++; + } + } + + + // Validate gob limits + + int[] acStructures = new int[Enum.GetNames(typeof(Side)).Length]; + int[] acMunts = new int[Enum.GetNames(typeof(Side)).Length]; + int cScenery = 0; + foreach (IMapItem mi in m_alsmi) { + if (mi is Unit) { + Unit unit = (Unit)mi; + if (unit is Structure) { + acStructures[(int)unit.Side]++; + } else { + acMunts[(int)unit.Side]++; + } + continue; + } + if (mi is Scenery) { + cScenery++; + continue; + } + } + + // Scenery Limit + + int cSceneryMax = 100; + if (cScenery > cSceneryMax) { + string strT = String.Format("{0} scenery; {1} allowed", cScenery, cSceneryMax); + dgt(this, ValidateError.Error, 0, 0, null, strT); + cError++; + } + + // Unit counts + + if (MaxPlayers == 1) { + // Single player - asymmetric: one human count, shared computer counts + // #define kcStructGobsHumanMin 39 + // #define kcStructGobsComputerMin 52 + // #define kcMuntGobsHumanMin 60 + // #define kcMuntGobsComputerMin 80 + + int cStructsHumanMax = 39; + int cMuntsHumanMax = 60; + int cStructsComputerMax = 52; + int cMuntsComputerMax = 80; + + int cStructsHuman = 0; + int cMuntsHuman = 0; + int cStructsComputer = 0; + int cMuntsComputer = 0; + + foreach (SideInfo sidi in m_alsidi) { + if (sidi.Intelligence == Intelligence.Human) { + cStructsHuman += acStructures[(int)sidi.Side]; + cMuntsHuman += acMunts[(int)sidi.Side]; + } else { + cStructsComputer += acStructures[(int)sidi.Side]; + cMuntsComputer += acMunts[(int)sidi.Side]; + } + } + + // HACK ALERT: Sometimes levels have more human structures than the human limit. In this + // case, take some from the computer side if possible. Hack: Reserve 5 structs for computer building + + int cStructuresAvailable = cStructsComputerMax - cStructsComputer - 5; + if (cStructuresAvailable < 0) + cStructuresAvailable = 0; + cStructsHumanMax += cStructuresAvailable; + cStructsComputerMax -= cStructuresAvailable; + + // Check + + if (cStructsHuman > cStructsHumanMax) { + string strT = String.Format("Human Side has {0} structures; {1} allowed", cStructsHuman, cStructsHumanMax); + dgt(this, ValidateError.Error, 0, 0, null, strT); + cError++; + } + if (cMuntsHuman > cMuntsHumanMax) { + string strT = String.Format("Human Side has {0} mobile units; {1} allowed", cMuntsHuman, cMuntsHumanMax); + dgt(this, ValidateError.Error, 0, 0, null, strT); + cError++; + } + if (cStructsComputer > cStructsComputerMax) { + string strT = String.Format("Computer Side has {0} structures; {1} allowed", cStructsComputer, cStructsComputerMax); + dgt(this, ValidateError.Error, 0, 0, null, strT); + cError++; + } + if (cMuntsComputer > cMuntsComputerMax) { + string strT = String.Format("Computer Side has {0} mobile units; {1} allowed", cMuntsComputer, cMuntsComputerMax); + dgt(this, ValidateError.Error, 0, 0, null, strT); + cError++; + } + } else { + // Multi-player - symmetric: same counts for each side + // #define kcStructGobsMax 55 + // #define kcMuntGobsMax 88 + + int cStructsMax = 55; + int cMuntsMax = 88; + + foreach (Side side in Enum.GetValues(typeof(Side))) { + if (acStructures[(int)side] > cStructsMax) { + string strT = String.Format("Side {0} has {1} structures; {2} allowed", side.ToString(), acStructures[(int)side], cStructsMax); + dgt(this, ValidateError.Error, 0, 0, null, strT); + cError++; + } + if (acMunts[(int)side] > cMuntsMax) { + string strT = String.Format("Side {0} has {1} mobile units; {2} allowed", side.ToString(), acMunts[(int)side], cMuntsMax); + dgt(this, ValidateError.Error, 0, 0, null, strT); + cError++; + } + } + } + + // UNDONE: Validate legal multiplayer triggers + + // UNDONE: Validate computer sides have enough power + + // UNDONE: Validate computer sides have a surveillance center if they have towers + + // UNDONE: info -- total credits value of on-map Galaxite + + // UNDONE: info -- power supply/demand for each side + + return cError; + } +#endif + + public void SaveIni(string strFile, int nVersion, string strFileTmap, string strFileTrmap, string strFilePalette) { + FileStream stm = new FileStream(strFile, FileMode.Create); + SaveIni(stm, nVersion, strFileTmap, strFileTrmap, strFilePalette, false); + stm.Close(); + } + + public void SaveIni(Stream stm, int nVersion, string strFileTmap, string strFileTrmap, string strFilePalette, bool fDemoCheckTrigger) { + Ini ini = new Ini(); + Ini.Section sec; + + // [Intro] + sec = new Ini.Section("Intro"); + sec.Add(new Ini.Property("String", "This is a test level!")); + ini.Add(sec); + + // [Side1-n] + int txOrigin = Bounds.X; + int tyOrigin = Bounds.Y; + + // Hack - there should be a real "neutral" side + ArrayList alsidiT = (ArrayList)m_alsidi.Clone(); + SideInfo sidiNeutral = new SideInfo(Side.sideNeutral); + sidiNeutral.Intelligence = Intelligence.ComputerNeutral; + sidiNeutral.InitialCredits = 0; + sidiNeutral.InitialView = new Point(0, 0); + alsidiT.Add(sidiNeutral); + + foreach (SideInfo sidi in alsidiT) { + sec = new Ini.Section(sidi.Side.ToString()); + sec.Add(new Ini.Property("InitialView", String.Format("{0},{1}", + sidi.InitialView.X - txOrigin, sidi.InitialView.Y - tyOrigin))); + sec.Add(new Ini.Property("InitialCredits", sidi.InitialCredits.ToString())); + sec.Add(new Ini.Property("Intelligence", "knIntelligence" + sidi.Intelligence.ToString())); + + // How many units for this side? + + int cStructures = 0; + int cMobileUnits = 0; + foreach (IMapItem mi in m_alsmi) { + if (mi is Unit) { + Unit unt = (Unit)mi; + if (unt.Side == sidi.Side) { + if (mi is MobileUnit) { + cMobileUnits++; + } + if (mi is Structure) { + cStructures++; + } + } + } + } + sec.Add(new Ini.Property("InitialMobileUnitCount", cMobileUnits.ToString())); + sec.Add(new Ini.Property("InitialStructureCount", cStructures.ToString())); + ini.Add(sec); + } + + // [GameObjects] + sec = new Ini.Section("GameObjects"); + foreach (IMapItem mi in m_alsmi) { + if (mi is Galaxite) + continue; + if (mi is Area) + continue; + if (mi is Wall) + continue; + if (mi is Tile) + continue; + + Ini.Property prop = mi.GetIniProperty(txOrigin, tyOrigin); + if (prop == null) + continue; + + // Skip Gobs that are out of bounds + // UNDONE: can't do the right thing to make sure Gob's right/bottom + // edges aren't out of bounds because M doesn't know the true + // width and height of Gobs. + + if (!Bounds.Contains(new Rectangle((int)mi.tx, (int)mi.ty, mi.ctx, mi.cty))) { + Console.WriteLine("{0} out of bounds", mi); + continue; + } + sec.Add(prop); + } + ini.Add(sec); + + // [Galaxite] + sec = new Ini.Section("Galaxite"); + foreach (IMapItem mi in m_alsmi) { + if (!(mi is Galaxite)) + continue; + + Ini.Property prop = mi.GetIniProperty(txOrigin, tyOrigin); + if (prop == null) + continue; + + // Skip Galaxite that is out of bounds + + if (!Bounds.Contains((int)mi.tx, (int)mi.ty)) { + Console.WriteLine("{0} out of bounds", mi); + continue; + } + + sec.Add(prop); + } + ini.Add(sec); + +#if false +// In terrain now + // [Walls] + sec = new Ini.Section("Walls"); + foreach (IMapItem mi in m_alsmi) { + if (!(mi is Wall)) + continue; + + Ini.Property prop = mi.GetIniProperty(txOrigin, tyOrigin); + if (prop == null) + continue; + + // Skip Walls that are out of bounds + + if (!Bounds.Contains((int)mi.tx, (int)mi.ty)) { + Console.WriteLine("{0} out of bounds", mi); + continue; + } + + sec.Add(prop); + } + ini.Add(sec); +#endif + + // [Areas] + ArrayList alT = new ArrayList(); + foreach (IMapItem mi in m_alsmi) { + if (!(mi is Area)) + continue; + alT.Add(mi); + } + alT.Sort(); + + sec = new Ini.Section("Areas"); + foreach (IMapItem mi in alT) { + Ini.Property prop = mi.GetIniProperty(txOrigin, tyOrigin); + if (prop == null) + continue; + + Area area = (Area)mi; + if (!Bounds.Contains(new Rectangle((int)mi.tx, (int)mi.ty, mi.ctx, mi.cty))) { + MessageBox.Show(String.Format("The area \"{0}\" lies outside of the map's bounds", area.Name), "Error Compiling Level"); + } + + sec.Add(prop); + } + ini.Add(sec); + + // [Triggers] + // NOTE: Triggers must be written before UnitGroups because some trigger actions + // e.g., CreateUnitAtArea will dynamically create UnitGroups and add them to the UnitGroup list + ini.Add(m_tgrm.GetIniSection(fDemoCheckTrigger)); + + // [UnitGroup 0-n] + m_ugm.SaveIni(ini); + + // [Switches] + sec = new Ini.Section("Switches"); + foreach (Switch sw in SwitchManager.Items) + sec.Add(new Ini.Property(sw.Name, "")); + ini.Add(sec); + + // [General] + // This section is written last in case any of the values are modified by + // the process of writing out the prior sections (e.g., CreateUnitAtArea actions add UnitGroups) + sec = new Ini.Section("General", null); + sec.Add(new Ini.Property("Title", Title)); + sec.Add(new Ini.Property("TileMap", strFileTmap)); + sec.Add(new Ini.Property("TerrainMap", strFileTrmap)); + sec.Add(new Ini.Property("Palette", strFilePalette)); + sec.Add(new Ini.Property("MinPlayers", m_nPlayersMin.ToString())); + sec.Add(new Ini.Property("MaxPlayers", m_nPlayersMax.ToString())); + sec.Add(new Ini.Property("UnitGroupCount", m_ugm.Items.Count.ToString())); + + // < 0 means use the current version, otherwise use the passed version + // This is the "level file format" version + + if (nVersion < 0) + nVersion = 1; + sec.Add(new Ini.Property("Version", nVersion.ToString())); + + // Add a random number for the revision #. This # is used to determine if saved games are + // based on older versions of a mission. + + if (nVersion > 0) { + Random rand = new Random(); + uint dwRevision = (uint)rand.Next(); + sec.Add(new Ini.Property("Revision", dwRevision.ToString())); + } + + ini.Add(sec); + + // Done + + ini.Save(stm); + + // Mostly Done. + // Clean out the "__cuaa" unit groups created by CreateUnitAtAreaTriggerAction + + ArrayList alsRemove = new ArrayList(); + UnitGroup[] aug = (UnitGroup[])m_ugm.Items.ToArray(typeof(UnitGroup)); + for (int i = 0; i < m_ugm.Items.Count; i++) { + if (((UnitGroup)m_ugm.Items[i]).Name.StartsWith("__cuaa")) { + alsRemove.Add(m_ugm.Items[i]); + } + } + foreach (UnitGroup ug in alsRemove) { + m_ugm.RemoveUnitGroup(ug); + } + } + + IMapItem CreateGameObject(string strSecName, string strName, string strValue) { + if (strSecName == "Galaxite") { + return new Galaxite(strName, strValue, Bounds.Left, Bounds.Top); + } else if (strSecName == "Areas") { + return new Area(strName, strValue, Bounds.Left, Bounds.Top); + } else if (strSecName == "GameObjects") { + int gt = int.Parse(strValue.Split(',')[0]); + switch (gt) { + case 1: // kgtShortRangeInfantry + return new ShortRangeInfantry(strName, strValue, Bounds.Left, Bounds.Top); + + case 2: // kgtLongRangeInfantry + return new LongRangeInfantry(strName, strValue, Bounds.Left, Bounds.Top); + + case 3: // kgtHumanResourceCenter + return new HumanResourceCenter(strName, strValue, Bounds.Left, Bounds.Top); + + case 5: // kgtScenery + return new Scenery(strName, strValue, Bounds.Left, Bounds.Top); + + case 7: // kgtReactor + return new Reactor(strName, strValue, Bounds.Left, Bounds.Top); + + case 8: // kgtProcessor + return new Processor(strName, strValue, Bounds.Left, Bounds.Top); + + case 11: // kgtGalaxMiner + return new GalaxMiner(strName, strValue, Bounds.Left, Bounds.Top); + + case 12: // kgtHeadquarters + return new Headquarters(strName, strValue, Bounds.Left, Bounds.Top); + + case 13: // kgtResearchCenter + return new ResearchCenter(strName, strValue, Bounds.Left, Bounds.Top); + + case 14: // kgtVehicleTransportStation + return new VehicleTransportStation(strName, strValue, Bounds.Left, Bounds.Top); + + case 15: // kgtRadar + return new Radar(strName, strValue, Bounds.Left, Bounds.Top); + + case 16: // kgtLightTank + return new LightTank(strName, strValue, Bounds.Left, Bounds.Top); + + case 17: // kgtMediumTank + return new MediumTank(strName, strValue, Bounds.Left, Bounds.Top); + + case 18: // kgtMachineGunVehicle + return new MachineGunVehicle(strName, strValue, Bounds.Left, Bounds.Top); + + case 19: // kgtRocketVehicle + return new RocketVehicle(strName, strValue, Bounds.Left, Bounds.Top); + + case 20: // kgtTakeoverSpecialist + return new TakeoverSpecialist(strName, strValue, Bounds.Left, Bounds.Top); + + case 21: // kgtWarehouse + return new Warehouse(strName, strValue, Bounds.Left, Bounds.Top); + + case 22: // kgtMobileHeadquarters + return new MobileHeadquarters(strName, strValue, Bounds.Left, Bounds.Top); + + case 26: // kgtMachineGunTower + return new MachineGunTower(strName, strValue, Bounds.Left, Bounds.Top); + + case 27: // kgtRocketTower + return new RocketTower(strName, strValue, Bounds.Left, Bounds.Top); + + case 32: // kgtArtillery + return new Artillery(strName, strValue, Bounds.Left, Bounds.Top); + + case 34: // kgtAndy + return new Andy(strName, strValue, Bounds.Left, Bounds.Top); + + case 35: // kgtReplicator + return new Replicator(strName, strValue, Bounds.Left, Bounds.Top); + + case 36: // kgtActivator + return new Activator(strName, strValue, Bounds.Left, Bounds.Top); + + case 37: // kgtFox + return new Fox(strName, strValue, Bounds.Left, Bounds.Top); + + //case 4: // kgtSurfaceDecal + //case 6: // kgtAnimation: + //case 9: // kgtStructure + //case 10: // kgtUnit + //case 23: // kgtOvermind + //case 24: // kgtTankShot + //case 25: // kgtRocket + //case 28: // kgtScorch + //case 29: // kgtSmoke + //case 30: // kgtPuff + //case 31: // kgtBullet + //case 33: // kgtArtilleryShot + //case 38: // kgtAndyShot + default: + return null; + } + } + return null; + } + + public void LoadIni(Ini ini) { + // General + + Ini.Section secGen = ini["General"]; + m_strTitle = secGen["Title"].Value; + m_nPlayersMin = int.Parse(secGen["MinPlayers"].Value); + m_nPlayersMax = int.Parse(secGen["MaxPlayers"].Value); + + // SideInfo + + for (Side side = Side.side1; side <= Side.side4; side++) { + Ini.Section sec = ini[side.ToString()]; + if (sec == null) { + continue; + } + SideInfo sidi = new SideInfo(side); + string s = sec["InitialView"].Value; + Regex re = new Regex(@"^(?(-)?\d+),(?(-)?\d+)$"); + Match m = re.Match(s); + int tx = int.Parse(m.Groups["tx"].Value); + int ty = int.Parse(m.Groups["ty"].Value); + Point ptInitialView = new Point(tx + Bounds.Left, ty + Bounds.Top); + if (ptInitialView.X < Bounds.Left) { + ptInitialView.X = Bounds.Left; + } + if (ptInitialView.Y < Bounds.Top) { + ptInitialView.Y = Bounds.Top; + } + sidi.InitialView = ptInitialView; + sidi.InitialCredits = int.Parse(sec["InitialCredits"].Value); + sidi.Intelligence = (Intelligence)int.Parse(sec["Intelligence"].Value); + m_alsidi.Add(sidi); + } + + // Misc MapItems. Areas must come before GameObjects because GameObjects + // can refer to Areas by index in UnitActions. + + string[] secNames = { "Galaxite", "Areas", "GameObjects" }; + foreach (string secName in secNames) { + Ini.Section sec = ini[secName]; + ArrayList alsMapItems = new ArrayList(); + foreach (Ini.Property prop in sec) { + IMapItem mi = CreateGameObject(secName, prop.Name, prop.Value); + if (mi != null) { + alsMapItems.Add(mi); + } + } + // Sometimes Areas are given the same name. Then on export the sort order + // doesn't match the original, which is a problem since in the game, area + // creation order is important. + + if (secName == "Areas") { + for (int i = 0; i < alsMapItems.Count; i++) { + Area ar = (Area)alsMapItems[i]; + ar.BonusSortKey = i; + } + } + AddMapItems((IMapItem[])alsMapItems.ToArray(typeof(IMapItem))); + } + + // Switches (must be before triggers). + + foreach (Ini.Property prop in ini["Switches"]) { + m_swm.AddSwitch(new Switch(prop.Name)); + } + + // UnitGroups (must be before triggers). + + m_ugm.LoadIni(ini); + + // Triggers + + m_tgrm.LoadIni(ini); + + // Clear out __cuaa UnitGroups now that triggers have been loaded + // (CreateUnitAtAreaTrigger loads state from __cuaa triggers). + + ArrayList alsRemove = new ArrayList(); + for (int i = 0; i < m_ugm.Items.Count; i++) { + UnitGroup ug = (UnitGroup)m_ugm.Items[i]; + if (ug.Name.StartsWith("__cuaa")) { + alsRemove.Add(ug); + } + } + foreach (UnitGroup ug in alsRemove) { + m_ugm.RemoveUnitGroup(ug); + } + + // TODO: remove the demo check trigger + } + + public void EditTriggers() { + TriggersForm frm = new TriggersForm(m_tgrm); + m_tgrm.ClearModified(); + frm.ShowDialog(); + if (m_tgrm.IsModified()) + SetModified(true); + } + + public void EditUnitGroups() { + UnitGroupsForm frm = new UnitGroupsForm(this, m_ugm); +#if false + frm.Show(); +#else + frm.ShowDialog(); +#endif + } + + public void EditComments() { + EditCommentsForm frm = new EditCommentsForm(m_strComment); + if (frm.ShowDialog() == DialogResult.OK) { + m_strComment = frm.textBox1.Text; + SetModified(true); + } + } + + public void EditLevelText() { + new EditLevelTextForm(this).ShowDialog(); + } + + public string GetLevelText() { + StringBuilder strb = new StringBuilder(); + ArrayList alsTriggers = m_tgrm.Triggers; + foreach (Trigger tgr in alsTriggers) { + ArrayList alsActions = tgr.Actions; + foreach (CaBase cab in alsActions) { + CaType[] acat = cab.GetTypes(); + foreach (CaType cat in acat) { + if (cat is CaTypeText || cat is CaTypeRichText) { + int cch = "TriggerAction".Length; + string strAction = cab.GetType().Name; + strAction = strAction.Remove(strAction.Length - cch, cch); +// string strCaType = cat.GetType().Name; +// strCaType = strCaType.Substring("CaType".Length); + + if (strAction == "Ecom") { + string str = " ($1 w/ $2) from $3 to $4"; + for (int j = 0; j < acat.Length - 1; j++) + str = str.Replace("$" + (j + 1), acat[j].ToString()); + strAction += str; + } + + if (strb.Length != 0) + strb.Append('\n'); + strb.AppendFormat("[{0}]\n", strAction); + strb.Append(cat.ToString()); + } + } + } + } + + return strb.ToString(); + } + + public bool SetLevelText(string strLevelText, out int ichErrorPos) { + ichErrorPos = 0; + + // Validation pass + + StringReader strr; + + strr = new StringReader(strLevelText); + + ArrayList alsTriggers = m_tgrm.Triggers; + foreach (Trigger tgr in alsTriggers) { + ArrayList alsActions = tgr.Actions; + foreach (CaBase cab in alsActions) { + CaType[] acat = cab.GetTypes(); + foreach (CaType cat in acat) { + if (cat is CaTypeText || cat is CaTypeRichText) { + int cch = "TriggerAction".Length; + string strAction = cab.GetType().Name; + strAction = strAction.Remove(strAction.Length - cch, cch); +// string strCaType = cat.GetType().Name; +// strCaType = strCaType.Substring("CaType".Length); + + if (strAction == "Ecom") { + string str = " ($1 w/ $2) from $3 to $4"; + for (int j = 0; j < acat.Length - 1; j++) + str = str.Replace("$" + (j + 1), acat[j].ToString()); + strAction += str; + } + + string strType = String.Format("[{0}]", strAction); + string strT = strr.ReadLine(); + + if (strType != strT) { + MessageBox.Show(String.Format("Expected \"{0}\" but found \"{1}\"", strType, strT), "Error"); + return false; + } + + ichErrorPos += strT.Length + 1; // for \n + + while (strr.Peek() != '[' && strr.Peek() != -1) { + strT = strr.ReadLine(); + ichErrorPos += strT.Length + 1; // for \n + } + } + } + } + } + + strr = new StringReader(strLevelText); + + alsTriggers = m_tgrm.Triggers; + foreach (Trigger tgr in alsTriggers) { + ArrayList alsActions = tgr.Actions; + foreach (CaBase cab in alsActions) { + CaType[] acat = cab.GetTypes(); + foreach (CaType cat in acat) { + if (cat is CaTypeText || cat is CaTypeRichText) { + int cch = "TriggerAction".Length; + string strAction = cab.GetType().Name; + strAction = strAction.Remove(strAction.Length - cch, cch); +// string strCaType = cat.GetType().Name; +// strCaType = strCaType.Substring("CaType".Length); + + string strType = String.Format("[{0}]", strAction); + string strT = strr.ReadLine(); + + StringBuilder strb = new StringBuilder(); + while (strr.Peek() != '[' && strr.Peek() != -1) { + strT = strr.ReadLine(); + strb.Append(strT); + if (strr.Peek() != '[' && strr.Peek() != -1) + strb.Append('\n'); + } + + string strOld = cat.ToString(); + string strNew = strb.ToString(); + + if (strOld != strNew) { + if (cat is CaTypeText) { + ((CaTypeText)cat).Text = strNew; + } else if (cat is CaTypeRichText) { + ((CaTypeRichText)cat).Text = strNew; + } + } + } + } + } + } + + SetModified(true); + return true; + } + + [BrowsableAttribute(false)] + public UnitGroupManager UnitGroupManager { + get { + return m_ugm; + } + } + + [BrowsableAttribute(false)] + public SwitchManager SwitchManager { + get { + return m_swm; + } + } + + [BrowsableAttribute(false)] + public CounterManager CounterManager { + get { + return m_ctrm; + } + } + + [BrowsableAttribute(false)] + public IMapItem[] MapItems { + get { + return (IMapItem[])m_alsmi.ToArray(typeof(IMapItem)); + } + } + + public string Title { + get { + return m_strTitle; + } + set { + if (m_strTitle == value) + return; + m_strTitle = value; + SetModified(true); + if (NameChanged != null) + NameChanged(this); + m_strOutputFilename = null; + } + } + + [BrowsableAttribute(false)] + public int Width { + get { + return m_ctx; + } + set { + m_ctx = value; + if (ImageChanged != null) + ImageChanged(); + } + } + + [BrowsableAttribute(false)] + public int Height { + get { + return m_cty; + } + set { + m_cty = value; + if (ImageChanged != null) + ImageChanged(); + } + } + + public Rectangle Bounds { + get { + return m_trcBounds; + } + set { + if (value.Left < 0 || value.Left >= m_ctx || value.Right < 0 || value.Right >= m_ctx || + value.Top < 0 || value.Top >= m_cty || value.Bottom < 0 || value.Bottom >= m_cty) { + return; + } + m_trcBounds = value; + m_fUpdateDirty = true; + SetModified(true); + } + } + + public int MinPlayers { + get { + return m_nPlayersMin; + } + set { + if (m_nPlayersMin == value) + return; + m_nPlayersMin = value; + SetModified(true); + } + } + + public int MaxPlayers { + get { + return m_nPlayersMax; + } + set { + if (m_nPlayersMax == value) + return; + m_nPlayersMax = value; + SetModified(true); + } + } + + public ArrayList Selection { + get { + return (ArrayList)m_alsmiSelected.Clone(); + } + set { + m_alsmiSelected = value; + m_fUpdateDirty = true; + } + } + } + + public enum Intelligence { + Human, + Computer, + ComputerOvermind, + ComputerNeutral, + } + + [Serializable] + public class SideInfo : ISerializable { + private Side m_side; + private int m_nInitialCredits; + private Point m_ptInitialView; + private Intelligence m_nIntelligence = Intelligence.Computer; + + protected SideInfo(SerializationInfo info, StreamingContext ctx) { + m_side = (Side)info.GetValue("m_side", typeof(Side)); + m_nInitialCredits = info.GetInt32("m_nInitialCredits"); + m_ptInitialView = (Point)info.GetValue("m_ptInitialView", typeof(Point)); + + try { + m_nIntelligence = (Intelligence)info.GetValue("m_nIntelligence", typeof(Intelligence)); + } catch (SerializationException) { + m_nIntelligence = m_side == Side.side1 ? Intelligence.Human : Intelligence.ComputerOvermind; + } + } + + public void GetObjectData(SerializationInfo info, StreamingContext context) { + info.AddValue("m_side", m_side); + info.AddValue("m_nInitialCredits", m_nInitialCredits); + info.AddValue("m_ptInitialView", m_ptInitialView); + info.AddValue("m_nIntelligence", m_nIntelligence); + } + + public SideInfo(Side side) { + m_side = side; + } + + public Side Side { + get { + return m_side; + } + } + + public int InitialCredits { + get { + return m_nInitialCredits; + } + set { + m_nInitialCredits = value; + } + } + + public Point InitialView { + get { + return m_ptInitialView; + } + set { + m_ptInitialView = value; + } + } + + public Intelligence Intelligence { + get { + return m_nIntelligence; + } + set { + m_nIntelligence = value; + } + } + } + + public class LevelDocTemplate : DocTemplate { + static string[] astr = { "Level Document", "UntitledLevel", "Level Docs", "ld" }; + public LevelDocTemplate(Type typeFrame, Type typeView) : base(astr, typeof(LevelDoc), typeFrame, typeView, new LevelDocBinder()) { + } + } + + // Compatibility goo + + public class LevelDocBinder : SerializationBinder { + public override Type BindToType(string strAssembly, string strType) { + if (strType == "m.LevelDescription") + return typeof(LevelDoc); +#if false + // This is how we migrate a serialized class from one Assembly to another. + + if (strType.StartsWith("m.SideWinder")) { + System.Reflection.Assembly ass = Globals.Plugins[0].GetType().Assembly; + Type typeT = ass.GetType(strType); + return typeT; + } +#endif + // Backwards compatibility with old levels + + switch (strType) { + case "m.CenterViewAction": + strType = "m.CenterViewTriggerAction"; + break; + + case "m.SetNextMissionAction": + strType = "m.SetNextMissionTriggerAction"; + break; + + case "m.EndMissionAction": + strType = "m.EndMissionTriggerAction"; + break; + + case "m.SetAllowedUnitsAction": + strType = "m.SetAllowedUnitsTriggerAction"; + break; + + case "m.EcomAction": + strType = "m.EcomTriggerAction"; + break; + + case "m.SetObjectiveAction": + strType = "m.SetObjectiveTriggerAction"; + break; + + case "m.WaitAction": + strType = "m.WaitTriggerAction"; + break; + + case "m.SetSwitchAction": + strType = "m.SetSwitchTriggerAction"; + break; + + case "m.PerserveTriggerAction": + strType = "m.PreserveTriggerTriggerAction"; + break; + + case "m.DefogAreaAction": + strType = "m.DefogAreaTriggerAction"; + break; + + case "m.TakeoverSpecialistInfantry": + strType = "m.TakeoverSpecialist"; + break; + + case "m.BuildUnitGroupTriggerAction": + strType = "m.CreateUnitGroupTriggerAction"; + break; + +#if false + case "m.MoveUnitAction": + strType = "m.MoveUnitGroupAction"; + break; + + case "m.SetSwitchUnitAction": + strType = "m.SetSwitchUnitGroupAction"; + break; + + case "m.WaitUnitAction": + strType = "m.WaitUnitGroupAction"; + break; +#endif + } + + return Type.GetType(strType + ", " + strAssembly); + } + } +} diff --git a/m/LevelFrame.cs b/m/LevelFrame.cs new file mode 100644 index 0000000..64cabee --- /dev/null +++ b/m/LevelFrame.cs @@ -0,0 +1,321 @@ +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; +using System.Diagnostics; + +namespace m +{ + public class LevelFrame : System.Windows.Forms.Form, ICommandTarget + { + private System.ComponentModel.Container components = null; + private System.Windows.Forms.Splitter splitter; + Document m_doc; + LevelViewParent m_viewTop; + LevelViewParent m_viewBottom; + float m_nSplitRatio; + static ArrayList s_alsFrames = new ArrayList(); + bool m_fNoRecurse = false; + + public LevelFrame(Form frmParent, Document doc, Type typeView) { + InitializeComponent(); + + LevelDocTemplate doct = (LevelDocTemplate)DocManager.FindDocTemplate(typeof(LevelDoc)); + doct.DocActive += new LevelDocTemplate.DocActiveHandler(LevelDocTemplate_DocActive); + + m_doc = doc; + + doc.PathChanged += new Document.PathChangedHandler(Document_PathChanged); + doc.ModifiedChanged += new Document.ModifiedChangedHandler(Document_ModifiedChanged); + doc.OpenCountChanged += new Document.OpenCountChangedHandler(Document_OpenCountChanged); + ((LevelDoc)doc).NameChanged += new LevelDoc.NameChangedHandler(LevelDoc_NameChanged); + + // Parent this and create panes + + MdiParent = frmParent; + ChangePanes(2); + + // See if the top most mdi frame is maximized. If so, maximize this too + // If no window around, maximize + + bool fMaximize = true; + if (frmParent.ActiveMdiChild != null) { + if (frmParent.ActiveMdiChild.WindowState != FormWindowState.Maximized) + fMaximize = false; + } + if (fMaximize) + WindowState = FormWindowState.Maximized; + + // Set Title + + s_alsFrames.Add(this); + SetTitle(); + + // If this doc is active, this is the new command target + + if (m_doc == DocManager.GetActiveDocument(typeof(LevelDoc))) + DocManager.SetCommandTarget(this); + + // Show + + Show(); + } + + public void DispatchCommand(Command cmd) { + if (m_viewTop.ContainsFocus) { + m_viewTop.DispatchCommand(cmd); + } + if (m_viewBottom.ContainsFocus) { + m_viewBottom.DispatchCommand(cmd); + } + } + + void Document_ModifiedChanged(Document doc, bool fModified) { + SetTitle(); + } + + void LevelDoc_NameChanged(LevelDoc lvld) { + SetTitle(); + } + + void Document_PathChanged(Document doc) { + SetTitle(); + } + + void Document_OpenCountChanged(Document doc) { + SetTitle(); + } + + void SetTitle() { + // Set the window title + + string strTitle = m_doc.GetName(); + string strPath = m_doc.GetPath(); + if (strPath != null) + strTitle += " (" + strPath + ")"; + if (m_doc.GetOpenCount() > 1) + strTitle += ":" + (s_alsFrames.IndexOf(this) + 1); + if (m_doc.IsModified()) + strTitle += "*"; + Text = strTitle; + } + + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.splitter = new System.Windows.Forms.Splitter(); + this.SuspendLayout(); + // + // splitter + // + this.splitter.BackColor = System.Drawing.Color.PapayaWhip; + this.splitter.Dock = System.Windows.Forms.DockStyle.Top; + this.splitter.MinExtra = 17; + this.splitter.MinSize = 0; + this.splitter.Name = "splitter"; + this.splitter.Size = new System.Drawing.Size(634, 5); + this.splitter.TabIndex = 1; + this.splitter.TabStop = false; + this.splitter.SplitterMoved += new System.Windows.Forms.SplitterEventHandler(this.splitter_SplitterMoved); + this.splitter.DoubleClick += new System.EventHandler(this.splitter_DoubleClick); + // + // LevelFrame + // + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.BackColor = System.Drawing.SystemColors.AppWorkspace; + this.ClientSize = new System.Drawing.Size(634, 360); + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.splitter}); + this.Name = "LevelFrame"; + this.Text = "LevelFrame"; + this.Closing += new System.ComponentModel.CancelEventHandler(this.LevelFrame_Closing); + this.SizeChanged += new System.EventHandler(this.LevelFrame_SizeChanged); + this.ResumeLayout(false); + + } + #endregion + + public Document GetDocument() { + return m_doc; + } + + void ChangePanes(int nPaneStateNew) { + // If starting, create panes and views + + if (m_viewTop == null && m_viewBottom == null) { + // Bottom full size + + m_viewBottom = new LevelViewParent(); + m_viewBottom.SetDocument(m_doc); + m_viewBottom.Dock = DockStyle.Fill; + Controls.Add(m_viewBottom); + + // Top + + m_viewTop = new LevelViewParent(); + m_viewTop.SetDocument(m_doc); + m_viewTop.Dock = DockStyle.Top; + Controls.Add(m_viewTop); + + // Set right order for auto formatting + + Controls.SetChildIndex(m_viewBottom, 0); + Controls.SetChildIndex(splitter, 1); + Controls.SetChildIndex(m_viewTop, 2); + + // If state 2 then top pane is zero height + + if (nPaneStateNew == 2) + splitter.SplitPosition = 0; + return; + } + + // If closing remove panes and views + + if (nPaneStateNew == 0) { + m_viewTop.Dispose(); + Controls.Remove(m_viewTop); + m_viewTop = null; + m_viewBottom.Dispose(); + Controls.Remove(m_viewBottom); + m_viewBottom = null; + return; + } + + // Have both panes. + + switch (nPaneStateNew) { + case -2: + // Expand the top pane: Make top pane the bottom pane, + // then make top pane size 0 + + LevelViewParent viewT = m_viewTop; + m_viewTop = m_viewBottom; + m_viewBottom = viewT; + + // Set right order for auto formatting + + m_viewTop.Dock = DockStyle.Top; + m_viewTop.Height = 0; + m_viewBottom.Dock = DockStyle.Fill; + splitter.SplitPosition = 0; + Controls.SetChildIndex(m_viewBottom, 0); + Controls.SetChildIndex(splitter, 1); + Controls.SetChildIndex(m_viewTop, 2); + break; + + case 2: + // Expand the bottom pane: Make top pane 0 size + + splitter.SplitPosition = 0; + break; + + case 3: + // Expand the top pane + + splitter.SplitPosition = ClientSize.Height / 2; + break; + } + } + + private void splitter_DoubleClick(object sender, System.EventArgs e) { + if (splitter.SplitPosition == 0) { + ChangePanes(3); + } else { + ChangePanes(2); + } + } + + private void splitter_SplitterMoved(object sender, System.Windows.Forms.SplitterEventArgs e) { + // If splitter at bottom, exchange top pane with bottom and close + // out top. + + if (ClientSize.Height - splitter.SplitPosition <= splitter.MinExtra + 10) { + if (!m_fNoRecurse) { + m_fNoRecurse = true; + ChangePanes(-2); + m_fNoRecurse = false; + } + } + + // Save away size ratio + + m_nSplitRatio = (float)splitter.SplitPosition / (float)ClientSize.Height; + } + + private void LevelFrame_Closing(object sender, System.ComponentModel.CancelEventArgs e) { + s_alsFrames.Remove(this); + if (!m_doc.Close()) { + e.Cancel = true; + return; + } + ChangePanes(0); + } + + private void LevelFrame_SizeChanged(object sender, System.EventArgs e) { + splitter.SplitPosition = (int)((float)ClientSize.Height * m_nSplitRatio); + } + + // For zorder control when another doc closes + + void LevelDocTemplate_DocActive(Document doc) { + if (m_doc == doc) + BringToFront(); + } + + // For z order control when this doc gets clicked on. + // Activated event doesn't work consistently + // MdiChildActivate on parent doesn't give details about the activation + // and is before the activation + // This is the resulting hack: + + public struct WINDOWPOS { + public IntPtr hwnd; + public IntPtr hwndInsertAfter; + public int x; + public int y; + public int cx; + public int cy; + public uint flags; + } + + protected unsafe override void WndProc(ref Message m) { + switch (m.Msg) { + // #define WM_WINDOWPOSCHANGED 0x0047 + // #define SWP_NOZORDER 0x0004 + case 0x47: + WINDOWPOS *ppos = (WINDOWPOS *)m.LParam; + if (ppos == null || (ppos->flags & 4) == 0) { + DocManager.SetActiveDocument(typeof(LevelDoc), m_doc); + DocManager.SetCommandTarget(this); + } + break; + + // #define WM_NCACTIVATE 0x0086 + case 0x86: + if (((ushort)m.WParam) != 0) + DocManager.SetCommandTarget(this); + break; + } + + base.WndProc(ref m); + } + } +} diff --git a/m/LevelFrame.resources b/m/LevelFrame.resources new file mode 100644 index 0000000..494c790 Binary files /dev/null and b/m/LevelFrame.resources differ diff --git a/m/LevelFrame.resx b/m/LevelFrame.resx new file mode 100644 index 0000000..df3b179 --- /dev/null +++ b/m/LevelFrame.resx @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + LevelFrame + + \ No newline at end of file diff --git a/m/LevelView.cs b/m/LevelView.cs new file mode 100644 index 0000000..18a684c --- /dev/null +++ b/m/LevelView.cs @@ -0,0 +1,1032 @@ +using System; +using System.Collections; +using System.ComponentModel; +using System.Drawing; +using System.Drawing.Imaging; +using System.Drawing.Drawing2D; +using System.Data; +using System.Windows.Forms; +using System.Runtime.InteropServices; +using System.Diagnostics; + +namespace m +{ + public class LevelView : System.Windows.Forms.UserControl + { + System.ComponentModel.Container components = null; + Bitmap m_bm; + LevelDoc m_lvld; + Rectangle m_rcDragStart = Rectangle.Empty; + System.Windows.Forms.ContextMenu m_contextMenu; + System.Windows.Forms.MenuItem menuItemRemove; + IMapItem m_miContextMenu; + Point m_ptDragSelectAnchor = new Point(0, 0); + Rectangle m_rcDragSelect; + bool m_fDragSelect; + bool m_fJustSelected; + float m_xRatioView = 0.0f; + float m_yRatioView = 0.0f; + float m_flScale = 1.0f; + TemplateDoc m_tmpd = null; + LayerFlags m_lyrf = LayerFlags.Default; + Bitmap m_bmDrag = null; + private System.Windows.Forms.MenuItem menuItemSelectSame; + Rectangle m_rcDragBoundsLast = Rectangle.Empty; + Point m_ptMouseDown; + IMapItem m_miCapturedMouse = null; + + public event EventHandler ScaleChanged; + + public LevelView() { + m_lvld = null; + } + + public void SetDocument(Document doc) { + // This call is required by the Windows.Forms Form Designer. + + InitializeComponent(); + + // Add any initialization after the InitForm call + + m_lvld = (LevelDoc)doc; + + // Create the bitmap + + AutoScroll = true; + VScroll = true; + HScroll = true; + CreateBitmap(); + + // Init view position + + InitPosition(); + + // Draw it initially + + m_lvld.Draw(m_bm, null, GetTileSize(), GetTemplateDoc(), m_lyrf); + + // Need to know these events + + m_lvld.ImageChanged += new LevelDoc.ImageChangedHandler(LevelDoc_ImageChanged); + m_lvld.ItemsRemoved += new LevelDoc.ItemsRemovedHandler(LevelDoc_ItemsRemoved); + + // Check size + + CheckSize(); + } + + public void SetScale(float flScale) { + if (flScale < 0.25f) + flScale = 0.25f; + if (flScale == m_flScale) + return; + m_flScale = flScale; + ResetClientSize(); + DrawLevelImage(null); + if (ScaleChanged != null) + ScaleChanged(this, null); + } + + public float GetScale() { + return m_flScale; + } + + Point WorldToView(Point ptWorld) { + int xView = (int)((float)ptWorld.X * m_flScale); + int yView = (int)((float)ptWorld.Y * m_flScale); + return new Point(xView, yView); + } + + Point ViewToWorld(Point ptView) { + int xWorld = (int)((float)ptView.X / m_flScale); + int yWorld = (int)((float)ptView.Y / m_flScale); + return new Point(xWorld, yWorld); + } + + Size WorldToViewSize(Size sizWorld) { + Point ptWorld = new Point(sizWorld.Width, sizWorld.Height); + Point ptView = WorldToView(ptWorld); + return new Size(ptView.X, ptView.Y); + } + + Size ViewToWorldSize(Size sizView) { + Point ptView = new Point(sizView.Width, sizView.Height); + Point ptWorld = ViewToWorld(ptView); + return new Size(ptWorld.X, ptWorld.Y); + } + + void InitPosition() { + int txWorld = m_lvld.Bounds.X - 1; + if (txWorld < 0) + txWorld = 0; + int tyWorld = m_lvld.Bounds.Y - 1; + if (tyWorld < 0) + tyWorld = 0; + Size sizTile = GetTileSize(); + int xWorld = txWorld * sizTile.Width; + int yWorld = tyWorld * sizTile.Height; + m_xRatioView = (float)xWorld / (float)m_bm.Width; + m_yRatioView = (float)yWorld / (float)m_bm.Height; + AutoScrollPosition = WorldToView(new Point(xWorld, yWorld)); + } + + protected unsafe override void WndProc(ref Message m) { + Point ptWorldScrollPos; + switch (m.Msg) { + // #define WM_HSCROLL 0x0114 + case 0x114: + base.WndProc(ref m); + ptWorldScrollPos = ViewToWorld(AutoScrollPosition); + m_xRatioView = (float)-ptWorldScrollPos.X / (float)m_bm.Width; + break; + + // #define WM_VSCROLL 0x0115 + // #define WM_MOUSEWHEEL 0x020A + case 0x20a: + case 0x115: + base.WndProc(ref m); + ptWorldScrollPos = ViewToWorld(AutoScrollPosition); + m_yRatioView = (float)-ptWorldScrollPos.Y / (float)m_bm.Height; + break; + + default: + base.WndProc(ref m); + break; + } + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + m_lvld.ImageChanged -= new LevelDoc.ImageChangedHandler(LevelDoc_ImageChanged); + m_lvld.ItemsRemoved -= new LevelDoc.ItemsRemovedHandler(LevelDoc_ItemsRemoved); + + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Component Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.m_contextMenu = new System.Windows.Forms.ContextMenu(); + this.menuItemRemove = new System.Windows.Forms.MenuItem(); + this.menuItemSelectSame = new System.Windows.Forms.MenuItem(); + // + // m_contextMenu + // + this.m_contextMenu.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.menuItemRemove, + this.menuItemSelectSame}); + // + // menuItemRemove + // + this.menuItemRemove.Index = 0; + this.menuItemRemove.Text = "Remove"; + this.menuItemRemove.Click += new System.EventHandler(this.menuItemRemove_Click); + // + // menuItemSelectSame + // + this.menuItemSelectSame.Index = 1; + this.menuItemSelectSame.Text = "Select Same"; + this.menuItemSelectSame.Click += new System.EventHandler(this.menuItemSelectSame_Click); + // + // LevelView + // + this.AllowDrop = true; + this.BackColor = System.Drawing.Color.Black; + this.Name = "LevelView"; + this.Size = new System.Drawing.Size(368, 288); + this.Resize += new System.EventHandler(this.LevelView_Resize); + this.DragEnter += new System.Windows.Forms.DragEventHandler(this.LevelView_DragEnter); + this.MouseUp += new System.Windows.Forms.MouseEventHandler(this.LevelView_MouseUp); + this.Paint += new System.Windows.Forms.PaintEventHandler(this.LevelView_Paint); + this.DragLeave += new System.EventHandler(this.LevelView_DragLeave); + this.DragDrop += new System.Windows.Forms.DragEventHandler(this.LevelView_DragDrop); + this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.LevelView_KeyDown); + this.DragOver += new System.Windows.Forms.DragEventHandler(this.LevelView_DragOver); + this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.LevelView_MouseMove); + this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.LevelView_MouseDown); + + } + #endregion + + public void SetTemplateDoc(TemplateDoc tmpd) { + if (tmpd == m_tmpd) + return; + + if (m_tmpd != null) + m_tmpd.BackgroundChanged -= new TemplateDoc.BackgroundChangedHandler(TemplateDoc_BackgroundChangedHandler); + + m_tmpd = tmpd; + + if (m_tmpd != null) + m_tmpd.BackgroundChanged += new TemplateDoc.BackgroundChangedHandler(TemplateDoc_BackgroundChangedHandler); + + UpdateImage(); + } + + void TemplateDoc_BackgroundChangedHandler(TemplateDoc tmpd) { + UpdateImage(); + } + + public void SetLayerFlags(LayerFlags lyrf) { + // If already set to this, return + + if (lyrf == m_lyrf) + return; + + // Remove the appropriate items from the current selection + + ArrayList alsmiSelected = m_lvld.Selection; + + for (int imi = 0; imi < alsmiSelected.Count; ) { + IMapItem mi = (IMapItem)alsmiSelected[imi]; + if (mi is Tile) { + if ((lyrf & LayerFlags.Templates) == 0) { + alsmiSelected.RemoveAt(imi); + continue; + } + } else if (mi is Area) { + if ((lyrf & LayerFlags.Areas) == 0) { + alsmiSelected.RemoveAt(imi); + continue; + } + } else { + if ((lyrf & LayerFlags.Gobs) == 0) { + alsmiSelected.RemoveAt(imi); + continue; + } + } + imi++; + } + m_lvld.Selection = alsmiSelected; + + // Set new flags and redraw + + m_lyrf = lyrf; + Redraw(); + } + + public LayerFlags GetLayerFlags() { + return m_lyrf; + } + + TemplateDoc GetTemplateDoc() { + if (m_tmpd != null) + return m_tmpd; + return m_lvld.GetTemplateDoc(); + } + + Size GetTileSize() { + TemplateDoc tmpd = GetTemplateDoc(); + if (tmpd == null) + return new Size(24, 24); + return tmpd.TileSize; + } + + private void LevelView_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { + if (m_lvld == null) + return; + DrawLevelImage(e.Graphics); + } + + protected override void OnPaintBackground(System.Windows.Forms.PaintEventArgs e) { + if (this.DesignMode) + base.OnPaintBackground(e); + } + + private void Redraw() { + m_lvld.Draw(m_bm, null, GetTileSize(), GetTemplateDoc(), m_lyrf); + using (Graphics gWin = CreateGraphics()) + DrawLevelImage(gWin); + } + + void DrawLevelImage(Graphics g) { + Graphics gT = g; + if (g == null) + gT = CreateGraphics(); + Rectangle rcSrcWorld = new Rectangle(0, 0, m_bm.Width, m_bm.Height); + Rectangle rcDstView = new Rectangle(AutoScrollPosition, WorldToViewSize(m_bm.Size)); + gT.InterpolationMode = InterpolationMode.NearestNeighbor; + gT.PixelOffsetMode = PixelOffsetMode.Half; + gT.DrawImage(m_bm, rcDstView, rcSrcWorld, GraphicsUnit.Pixel); + if (g == null) + gT.Dispose(); + } + + void CreateBitmap() { + Size sizTile = GetTileSize(); + if (m_bm != null) + m_bm.Dispose(); + m_bm = new Bitmap(m_lvld.Width * sizTile.Width, m_lvld.Height * sizTile.Height, PixelFormat.Format24bppRgb); + ResetClientSize(); + } + + void ResetClientSize() { + AutoScrollMinSize = WorldToViewSize(m_bm.Size); + + // Force it to recalc where it thinks the client size should be based on frame decorations, + // and send a Resize event which'll cause us to recalc the client area + + Size sizSav = Size; + Size sizT = new Size(sizSav.Width + 1, sizSav.Height + 1); + Size = sizT; + UpdateBounds(); + Size = sizSav; + UpdateBounds(); + } + + private void LevelDoc_ImageChanged() { + UpdateImage(); + } + + void UpdateImage() { + Size sizTile = GetTileSize(); + if (sizTile.Width * Width != m_bm.Width || sizTile.Height * Height != m_bm.Height) { + CreateBitmap(); + } + Redraw(); + } + + private void LevelDoc_ItemsRemoved(IMapItem[] ami) { + ArrayList alsmiSelected = m_lvld.Selection; + foreach (IMapItem mi in ami) { + alsmiSelected.Remove(mi); + if (m_miContextMenu == mi) + m_miContextMenu = null; + if (m_miCapturedMouse == mi) + m_miCapturedMouse = null; + break; + } + m_lvld.Selection = alsmiSelected; + } + + private void LevelView_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e) { + m_fJustSelected = false; + + m_ptMouseDown = ViewToWorld(new Point(e.X - AutoScrollPosition.X, e.Y - AutoScrollPosition.Y)); + + // Select / clear items + IMapItem mi = m_lvld.HitTest(m_ptMouseDown.X, m_ptMouseDown.Y, GetTileSize(), GetTemplateDoc(), m_lyrf); + if (e.Button == MouseButtons.Left) { + if (mi == null) { + // Clear selection + m_lvld.Selection = new ArrayList(); + Globals.PropertyGrid.SelectedObject = null; + m_ptDragSelectAnchor.X = m_ptMouseDown.X; + m_ptDragSelectAnchor.Y = m_ptMouseDown.Y; + m_rcDragSelect = new Rectangle(m_ptDragSelectAnchor, new Size(0, 0)); + m_fDragSelect = true; + } else { + m_rcDragStart.X = m_ptMouseDown.X - 5; + m_rcDragStart.Y = m_ptMouseDown.Y - 5; + m_rcDragStart.Width = 10; + m_rcDragStart.Height = 10; + + // Add to selection. Extend if control is down + ArrayList alsmiSelected = m_lvld.Selection; + if (!alsmiSelected.Contains(mi)) { + if ((Control.ModifierKeys & Keys.Control) != Keys.Control) + alsmiSelected.Clear(); + alsmiSelected.Add(mi); + Globals.PropertyGrid.SelectedObjects = (IMapItem[])alsmiSelected.ToArray(typeof(IMapItem)); + m_fJustSelected = true; + m_lvld.Selection = alsmiSelected; + } + + if (mi.OnMouseDown(e, m_ptMouseDown, GetTileSize(), GetTemplateDoc())) + m_miCapturedMouse = mi; + } + Redraw(); + } else if (e.Button == MouseButtons.Right) { + if (mi != null) { + // If this is not already selected, then clear selection and add this + if (!m_lvld.Selection.Contains(mi)) { + ArrayList als = new ArrayList(); + als.Add(mi); + m_lvld.Selection = als; + Globals.PropertyGrid.SelectedObject = mi; + Redraw(); + } + + if (mi.OnMouseDown(e, m_ptMouseDown, GetTileSize(), GetTemplateDoc())) { + m_miCapturedMouse = mi; + return; + } + + m_miContextMenu = mi; + m_contextMenu.Show(this, new Point(e.X, e.Y)); + } + } + } + + private Rectangle GetBoundingRect(IMapItem[] ami) { + Rectangle rc = new Rectangle(); + TemplateDoc tmpd = GetTemplateDoc(); + Size sizTile = GetTileSize(); + foreach (IMapItem mi in ami) { + int x = (int)(mi.tx * sizTile.Width); + int y = (int)(mi.ty * sizTile.Height); + rc = UnionRect(rc, mi.GetBoundingRectAt(x, y, sizTile, tmpd)); + } + return rc; + } + + private Rectangle GetSelectRect(int x, int y) { + int cx = x - m_ptDragSelectAnchor.X; + if (cx < 0) + cx = -cx; + int cy = y - m_ptDragSelectAnchor.Y; + if (cy < 0) + cy = -cy; + int xT = x; + if (m_ptDragSelectAnchor.X < x) + xT = m_ptDragSelectAnchor.X; + int yT = y; + if (m_ptDragSelectAnchor.Y < y) + yT = m_ptDragSelectAnchor.Y; + return new Rectangle(xT, yT, cx, cy); + } + + private Rectangle UnionRect(Rectangle rc1, Rectangle rc2) { + if (rc1.IsEmpty) + return new Rectangle(rc2.Location, rc2.Size); + if (rc2.IsEmpty) + return new Rectangle(rc1.Location, rc1.Size); + int xLeft = rc1.Left < rc2.Left ? rc1.Left : rc2.Left; + int yTop = rc1.Top < rc2.Top ? rc1.Top : rc2.Top; + int xRight = rc1.Right > rc2.Right ? rc1.Right : rc2.Right; + int yBottom = rc1.Bottom > rc2.Bottom ? rc1.Bottom : rc2.Bottom; + return new Rectangle(xLeft, yTop, xRight - xLeft, yBottom - yTop); + } + + private void LevelView_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e) { + Point ptMouse = ViewToWorld(new Point(e.X - AutoScrollPosition.X, e.Y - AutoScrollPosition.Y)); + TemplateDoc tmpd = GetTemplateDoc(); + Size sizTile = GetTileSize(); + + // Update status bar + + int tx = ptMouse.X / sizTile.Width; + int ty = ptMouse.Y / sizTile.Height; + Globals.StatusBar.Text = String.Format("Coords: {0}, {1}", tx, ty); + + // If we're dragging a selection, handle it here + + if (m_fDragSelect) { + DragSelectExtend(e); + return; + } + + // Send input to MapItem, see if it wants it + + IMapItem mi = m_miCapturedMouse; + if (mi == null) + mi = m_lvld.HitTest(ptMouse.X, ptMouse.Y, sizTile, tmpd, m_lyrf); + if (mi != null) { + if (mi.OnMouseMove(e, ptMouse, sizTile, tmpd)) + return; + } + + // We're not extending a selection. Perhaps we're initiating a drag drop operation + // Check initiation conditions + + if (m_lvld.Selection.Count == 0) + return; + if (MouseButtons != MouseButtons.Left) + return; + if (m_rcDragStart.Contains(ptMouse.X, ptMouse.Y)) + return; + + PerformDragDrop(e, m_ptMouseDown); + } + + private void LevelView_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e) { + Point ptMouse = ViewToWorld(new Point(e.X - AutoScrollPosition.X, e.Y - AutoScrollPosition.Y)); + + // If a MapItem has captured the mouse, give it a crack at the event + + IMapItem miT = m_miCapturedMouse; + m_miCapturedMouse = null; + if (miT != null) { + if (miT.OnMouseUp(e, ptMouse, GetTileSize(), GetTemplateDoc())) + return; + } + + if (e.Button != MouseButtons.Left) + return; + if (m_fDragSelect) { + m_fDragSelect = false; + Graphics gWin = CreateGraphics(); + Rectangle rcSrcWorld = new Rectangle(m_rcDragSelect.Left, m_rcDragSelect.Top, m_rcDragSelect.Width, m_rcDragSelect.Height); + rcSrcWorld.Inflate(1, 1); + Rectangle rcDstView = new Rectangle(WorldToView(rcSrcWorld.Location), WorldToViewSize(rcSrcWorld.Size)); + rcDstView.Offset(AutoScrollPosition); + gWin.InterpolationMode = InterpolationMode.NearestNeighbor; + gWin.PixelOffsetMode = PixelOffsetMode.Half; + gWin.DrawImage(m_bm, rcDstView, rcSrcWorld, GraphicsUnit.Pixel); + gWin.Dispose(); + } + + // Clear selected placement? + if (m_fJustSelected) { + m_fJustSelected = false; + return; + } + IMapItem mi = m_lvld.HitTest(ptMouse.X, ptMouse.Y, GetTileSize(), GetTemplateDoc(), m_lyrf); + if (mi == null) + return; + if ((Control.ModifierKeys & Keys.Control) != Keys.Control) + return; + ArrayList alsmiSelected = m_lvld.Selection; + if (!alsmiSelected.Contains(mi)) + return; + alsmiSelected.Remove(mi); + m_lvld.Selection = alsmiSelected; + Globals.PropertyGrid.SelectedObject = null; + Redraw(); + } + + private void LevelView_DragOver(object sender, System.Windows.Forms.DragEventArgs e) { + // As the drag occurs, we show what is being dragged. This is done by drawing + // a properly aligned image on top of the view image as the drag occurs. + + // Get the data. It's not what we want then don't allow + + if (!e.Data.GetDataPresent(typeof(LevelData))) { + e.Effect = DragDropEffects.None; + return; + } + LevelData ldat = (LevelData)e.Data.GetData(typeof(LevelData)); + + // Figure out where we want to place these map items + + PointF ptOrigin; + Point ptClient = PointToClient(new Point(e.X, e.Y)); + Point ptMouse = ViewToWorld(new Point(ptClient.X - AutoScrollPosition.X, ptClient.Y - AutoScrollPosition.Y)); + PointF[] aptPlace = GetPlacementPoints2(ptMouse, ldat, out ptOrigin); + + // Calc the bounding rect of the map items + + Size sizTile = GetTileSize(); + Rectangle rcDragBoundsNew = GetBoundingRect(ldat.ami); + rcDragBoundsNew.Offset((int)(ptOrigin.X * sizTile.Width), (int)(ptOrigin.Y * sizTile.Height)); + + // If have an old drag image to erase, the drawing bounds needs to include it too so it + // gets erased during this update + + Rectangle rcUnion = UnionRect(rcDragBoundsNew, m_rcDragBoundsLast); + + // See if our drag compose buffer is suitably sized. If not, recreate it + + if (m_bmDrag == null || m_bmDrag.Width < rcUnion.Width || m_bmDrag.Height < rcUnion.Height) { + if (m_bmDrag != null) + m_bmDrag.Dispose(); + m_bmDrag = new Bitmap(rcUnion.Width + rcUnion.Width / 2, rcUnion.Height + rcUnion.Height / 2, PixelFormat.Format24bppRgb); + } + + // Copy this rectangle from the map bitmap into our drag compose buffer + + Graphics gMem = Graphics.FromImage(m_bmDrag); + gMem.DrawImage(m_bm, 0, 0, rcUnion, GraphicsUnit.Pixel); + + // Now draw in map items + + TemplateDoc tmpd = GetTemplateDoc(); + for (LayerType layer = LayerType.Start; layer < LayerType.End; layer++) { + for (int imi = 0; imi < ldat.ami.Length; imi++) { + int x = (int)(aptPlace[imi].X * sizTile.Width); + int y = (int)(aptPlace[imi].Y * sizTile.Height); + ldat.ami[imi].Draw(gMem, x - rcUnion.X, y - rcUnion.Y, sizTile, tmpd, layer, true); + } + } + + // Put it on screen + + Graphics gWin = CreateGraphics(); + Rectangle rcSrcWorld = new Rectangle(0, 0, rcUnion.Width, rcUnion.Height); + Rectangle rcDstView = new Rectangle(WorldToView(rcUnion.Location), WorldToViewSize(rcUnion.Size)); + rcDstView.Offset(AutoScrollPosition.X, AutoScrollPosition.Y); + gWin.InterpolationMode = InterpolationMode.NearestNeighbor; + gWin.PixelOffsetMode = PixelOffsetMode.Half; + gWin.DrawImage(m_bmDrag, rcDstView, rcSrcWorld, GraphicsUnit.Pixel); + gMem.Dispose(); + gWin.Dispose(); + + // Remember old bounds + + m_rcDragBoundsLast = rcDragBoundsNew; + } + + private void LevelView_DragEnter(object sender, System.Windows.Forms.DragEventArgs e) { + m_rcDragBoundsLast = Rectangle.Empty; + if (!e.Data.GetDataPresent(typeof(LevelData))) { + e.Effect = DragDropEffects.None; + return; + } + e.Effect = DragDropEffects.Copy | DragDropEffects.Move; + } + + private void LevelView_DragLeave(object sender, System.EventArgs e) { + // Update the on-screen drag image so that it gets erased properly + // Copy appropriate part of the backing image + + Graphics gMem = Graphics.FromImage(m_bmDrag); + gMem.DrawImage(m_bm, 0, 0, m_rcDragBoundsLast, GraphicsUnit.Pixel); + + // Now draw in on screen in the appropriate spot + + Graphics gWin = CreateGraphics(); + Rectangle rcSrcWorld = new Rectangle(0, 0, m_rcDragBoundsLast.Width, m_rcDragBoundsLast.Height); + Rectangle rcDstView = new Rectangle(WorldToView(m_rcDragBoundsLast.Location), WorldToViewSize(m_rcDragBoundsLast.Size)); + rcDstView.Offset(AutoScrollPosition); + gWin.InterpolationMode = InterpolationMode.NearestNeighbor; + gWin.PixelOffsetMode = PixelOffsetMode.Half; + gWin.DrawImage(m_bmDrag, rcDstView, rcSrcWorld, GraphicsUnit.Pixel); + gMem.Dispose(); + gWin.Dispose(); + + // Not being used any more + + m_bmDrag.Dispose(); + m_bmDrag = null; + m_rcDragBoundsLast = Rectangle.Empty; + } + + private void LevelView_DragDrop(object sender, System.Windows.Forms.DragEventArgs e) { + // Get the data. It's not what we want then don't allow + + if (!e.Data.GetDataPresent(typeof(LevelData))) { + e.Effect = DragDropEffects.None; + return; + } + LevelData ldat = (LevelData)e.Data.GetData(typeof(LevelData)); + + // Place map items + + Point ptClient = PointToClient(new Point(e.X, e.Y)); + Point ptMouse = ViewToWorld(new Point(ptClient.X - AutoScrollPosition.X, ptClient.Y - AutoScrollPosition.Y)); + PlaceMapItems(ptMouse, ldat); + e.Effect = e.AllowedEffect; + } + + void PlaceMapItems(Point ptMouse, LevelData ldat) { + // Figure out where we want to place these map items + + PointF ptOrigin; + PointF[] aptPlace = GetPlacementPoints2(ptMouse, ldat, out ptOrigin); + + // Set their positions + + IMapItem[] ami = new IMapItem[ldat.ami.Length]; + for (int imi = 0; imi < ldat.ami.Length; imi++) { + ami[imi] = (IMapItem)ldat.ami[imi].Clone(); + ami[imi].tx = aptPlace[imi].X; + ami[imi].ty = aptPlace[imi].Y; + } + + // Add them to the level, make them selected + + m_lvld.AddMapItems(ami); + ArrayList alsmiSelected = new ArrayList(); + alsmiSelected.AddRange(ami); + m_lvld.Selection = alsmiSelected; + Globals.PropertyGrid.SelectedObjects = (Object[])alsmiSelected.ToArray(typeof(Object)); + } + + PointF[] GetPlacementPoints2(Point ptMouse, LevelData ldat, out PointF ptOrigin) { + Size sizTile = GetTileSize(); + double txOrigin = (ptMouse.X / (double)sizTile.Width) - ldat.txMouse; + if (txOrigin < 0) + txOrigin = 0; + double tyOrigin = (ptMouse.Y / (double)sizTile.Height) - ldat.tyMouse; + if (tyOrigin < 0) + tyOrigin = 0; + + // Keep the new positions on the grid + + txOrigin = Math.Floor((txOrigin + ldat.Grid.Width / 2) / ldat.Grid.Width) * ldat.Grid.Width; + tyOrigin = Math.Floor((tyOrigin + ldat.Grid.Height / 2) / ldat.Grid.Height) * ldat.Grid.Height; + + PointF[] aptPlace = new PointF[ldat.ami.Length]; + for (int imi = 0; imi < ldat.ami.Length; imi++) { + double tx = txOrigin + ldat.ami[imi].tx; + if (tx > m_lvld.Width - 1) + tx = m_lvld.Width - 1; + double ty = tyOrigin + ldat.ami[imi].ty; + if (ty > m_lvld.Height - 1) + ty = m_lvld.Height - 1; + aptPlace[imi] = new PointF((float)tx, (float)ty); + } + + ptOrigin = new PointF((float)txOrigin, (float)tyOrigin); + return aptPlace; + } + + LevelData PrepareLevelData(int x, int y, Size sizTile, IMapItem[] ami) { + // Figure out relative spacing + + double txMin = double.MaxValue; + double tyMin = double.MaxValue; + foreach (IMapItem mi in ami) { + if (mi.tx < txMin) + txMin = mi.tx; + if (mi.ty < tyMin) + tyMin = mi.ty; + } + + LevelData ldat = new LevelData(); + ldat.Grid.Width = 0.0000001f; + ldat.Grid.Height = 0.0000001f; + + for (int imi = 0; imi < ami.Length; imi++) { + IMapItem mi = (IMapItem)ami[imi]; + + // Keep track of the maximum gridding required by the various items + // NOTE: all the grids must be evenly divisible into the largest grid + // or final placement of the items will have the indivisible ones + // realigning themselves. + + ldat.Grid.Width = Math.Max(ldat.Grid.Width, mi.Grid.Width); + ldat.Grid.Height = Math.Max(ldat.Grid.Height, mi.Grid.Height); + } + + // Offset tx/yMin so the clones will be grid-aligned + + txMin -= Math.IEEERemainder(txMin, ldat.Grid.Width); + tyMin -= Math.IEEERemainder(tyMin, ldat.Grid.Height); + + // Now clone the map items and readjust tile coordinates + // for origin of 0,0 (or as close as we can get and still be grid-aligned) + + ldat.ami = new IMapItem[ami.Length]; + for (int imi = 0; imi < ami.Length; imi++) { + IMapItem mi = (IMapItem)ami[imi].Clone(); + ldat.ami[imi] = mi; + mi.tx -= txMin; + mi.ty -= tyMin; + } + + // Figure out mouse position relative to this origin + + int xOrigin = (int)(txMin * sizTile.Width); + int yOrigin = (int)(tyMin * sizTile.Height); + ldat.txMouse = (double)(x - xOrigin) / (double)sizTile.Width; + ldat.tyMouse = (double)(y - yOrigin) / (double)sizTile.Height; + + // All done + + return ldat; + } + + void PerformDragDrop(MouseEventArgs e, Point ptMouse) { + // Remember what mi are selected. We may be deleting this if this is a move operation + + ArrayList alsmiSelected = m_lvld.Selection; + IMapItem[] amiMove = (IMapItem[])alsmiSelected.ToArray(typeof(IMapItem)); + + // Prepare a data object for drag drop + + LevelData ldat = PrepareLevelData(ptMouse.X, ptMouse.Y, GetTileSize(), (IMapItem[])alsmiSelected.ToArray(typeof(IMapItem))); + + // Normal operation is a move unless control key is press in which case it is + // a copy + + DragDropEffects eff = DragDropEffects.Move; + if ((Control.ModifierKeys & Keys.Control) == Keys.Control) + eff = DragDropEffects.Copy; + + // Perform the drag drop. If it was cancelled, nothing to do. + + DragDropEffects effActual = DoDragDrop(ldat, eff); + if (effActual == DragDropEffects.None) + return; + + // If a move actually did occur, then remove the originals + + if ((effActual & DragDropEffects.Move) != 0) { + m_lvld.RemoveMapItems(amiMove); + } + } + + void DragSelectExtend(MouseEventArgs e) { + // Select all the mi inside + + Point ptMouse = ViewToWorld(new Point(e.X - AutoScrollPosition.X, e.Y - AutoScrollPosition.Y)); + Size sizTile = GetTileSize(); + TemplateDoc tmpd = GetTemplateDoc(); + Rectangle rcDragSelectNew = GetSelectRect(ptMouse.X, ptMouse.Y); + ArrayList alsmiSelected = m_lvld.HitTest(rcDragSelectNew, GetTileSize(), GetTemplateDoc(), m_lyrf); + Rectangle rcSelect = new Rectangle(); + ArrayList alsmiSelectedOld = m_lvld.Selection; + if (!alsmiSelectedOld.Equals(alsmiSelected)) { + rcSelect = GetBoundingRect((IMapItem[])alsmiSelectedOld.ToArray(typeof(IMapItem))); + m_lvld.Selection = alsmiSelected; + rcSelect = UnionRect(rcSelect, GetBoundingRect((IMapItem[])alsmiSelected.ToArray(typeof(IMapItem)))); + m_lvld.Draw(m_bm, null, sizTile, tmpd, m_lyrf); + Globals.PropertyGrid.SelectedObjects = (Object[])alsmiSelected.ToArray(typeof(Object)); + } + + // Fill a buffer from background + Rectangle rcDragUnion = UnionRect(rcSelect, UnionRect(rcDragSelectNew, m_rcDragSelect)); + + // Expand by 1 so that the boundary is definitely inside. Problems with portions of + // the drag boundary not erasing when scaled due to rounding errors + + rcDragUnion.Inflate(1, 1); +#if false + if (rcDragUnion.IsEmpty) + return; +#else + if (rcDragUnion.Width == 0 || rcDragUnion.Height == 0) + return; +#endif + Bitmap bm = new Bitmap(rcDragUnion.Width, rcDragUnion.Height); + Graphics gMem = Graphics.FromImage(bm); + gMem.DrawImage(m_bm, 0, 0, rcDragUnion, GraphicsUnit.Pixel); + + // Draw in the drag selection + Pen pen = new Pen(new SolidBrush(Color.Red)); + gMem.DrawRectangle(pen, rcDragSelectNew.X - rcDragUnion.X, rcDragSelectNew.Y - rcDragUnion.Y, rcDragSelectNew.Width - 1, rcDragSelectNew.Height - 1); + gMem.Dispose(); + + // Drag onto the screen + Graphics gWinT = CreateGraphics(); + Rectangle rcSrcWorld = new Rectangle(0, 0, bm.Width, bm.Height); + Point ptViewDst = WorldToView(rcDragUnion.Location); + ptViewDst.Offset(AutoScrollPosition.X, AutoScrollPosition.Y); + Rectangle rcDstView = new Rectangle(ptViewDst, WorldToViewSize(rcDragUnion.Size)); + gWinT.InterpolationMode = InterpolationMode.NearestNeighbor; + gWinT.PixelOffsetMode = PixelOffsetMode.Half; + gWinT.DrawImage(bm, rcDstView, rcSrcWorld, GraphicsUnit.Pixel); + gWinT.Dispose(); + bm.Dispose(); + m_rcDragSelect = rcDragSelectNew; + } + + private void LevelView_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e) { + switch (e.KeyCode) { + case Keys.Delete: + Delete(); + break; + + case Keys.C: + if ((Control.ModifierKeys & Keys.Control) != 0) + Copy(); + break; + + case Keys.X: + if ((Control.ModifierKeys & Keys.Control) != 0) + Cut(); + break; + + case Keys.V: + if ((Control.ModifierKeys & Keys.Control) != 0) + Paste(); + break; + + case Keys.OemMinus: + if ((Control.ModifierKeys & Keys.Control) != 0) { + SetScale(1.0f); + } else { + SetScale(m_flScale - 0.25f); + } + break; + + case Keys.Oemplus: + if ((Control.ModifierKeys & Keys.Control) != 0) { + SetScale(1.0f); + } else { + SetScale(m_flScale + 0.25f); + } + break; + } + } + + public void Delete() { + RemoveSelectedItems(); + } + + public void Copy() { + ArrayList alsmiSelected = m_lvld.Selection; + if (alsmiSelected.Count != 0) { + IMapItem[] ami = (IMapItem[])alsmiSelected.ToArray(typeof(IMapItem)); + Rectangle rc = GetBoundingRect(ami); + LevelData ldat = PrepareLevelData(rc.X + rc.Width / 2, rc.Y + rc.Height / 2, GetTileSize(), ami); + //Clipboard.SetDataObject(ldat); doesn't work for some reason + Clipboard.SetDataObject(ldat.ami); + } + } + + public void Cut() { + if (m_lvld.Selection.Count != 0) { + Copy(); + RemoveSelectedItems(); + } + } + + public void Paste() { + // IMapItems present? + + IDataObject data = Clipboard.GetDataObject(); + if (!data.GetDataPresent(typeof(IMapItem[]))) + return; + + // For some reason the clipboard data object won't return a LevelData + + LevelData ldat = new LevelData(); + ldat.ami = (IMapItem[])data.GetData(typeof(IMapItem[])); + Rectangle rc = GetBoundingRect(ldat.ami); + Size sizTile = GetTileSize(); + ldat.txMouse = (double)rc.Width / (double)sizTile.Width; + ldat.tyMouse = (double)rc.Height / (double)sizTile.Height; + + ldat.Grid.Width = 0.0000001f; + ldat.Grid.Height = 0.0000001f; + foreach (IMapItem mi in ldat.ami) { + ldat.Grid.Width = Math.Max(mi.Grid.Width, ldat.Grid.Width); + ldat.Grid.Height = Math.Max(mi.Grid.Height, ldat.Grid.Height); + } + + // Place map items + + Point ptViewCenter = new Point(ClientSize.Width / 2, ClientSize.Height / 2); + Point ptMouse = ViewToWorld(new Point(ptViewCenter.X - AutoScrollPosition.X, ptViewCenter.Y - AutoScrollPosition.Y)); + PlaceMapItems(ptMouse, ldat); + } + + public void RemoveSelectedItems() { + IMapItem[] ami = (IMapItem[])m_lvld.Selection.ToArray(typeof(IMapItem)); + m_lvld.Selection = new ArrayList(); + Globals.PropertyGrid.SelectedObject = null; + m_lvld.RemoveMapItems(ami); + } + + public void CheckSize() { + SuspendLayout(); + Size sizBitmapView = WorldToViewSize(m_bm.Size); + if (ClientSize.Width > sizBitmapView.Width) + Width += sizBitmapView.Width - ClientSize.Width; + if (ClientSize.Height > sizBitmapView.Height) + Height += sizBitmapView.Height - ClientSize.Height; + ResumeLayout(false); + } + + private void LevelView_Resize(object sender, System.EventArgs e) { + // Resize client area around m_bm + + CheckSize(); + + // Reposition scrollbars to last mid point + + Size sizBitmapView = WorldToViewSize(m_bm.Size); + int xViewMap = (int)(m_xRatioView * (float)sizBitmapView.Width); + if (ClientSize.Width >= sizBitmapView.Width) + xViewMap = 0; + int yViewMap = (int)(m_yRatioView * (float)sizBitmapView.Height); + if (ClientSize.Height >= sizBitmapView.Height) + yViewMap = 0; + AutoScrollPosition = new Point(xViewMap, yViewMap); + } + + private void menuItemSelectSame_Click(object sender, System.EventArgs e) { + ArrayList alsmiSelected = new ArrayList(); + foreach (IMapItem mi in m_lvld.MapItems) { + if (mi.GetType() != m_miContextMenu.GetType()) + continue; + if (m_miContextMenu is Tile) { + if (mi is Tile) { + if (((Tile)mi).Name != ((Tile)m_miContextMenu).Name) + continue; + } + } + alsmiSelected.Add(mi); + } + m_lvld.Selection = alsmiSelected; + Redraw(); + } + + private void menuItemRemove_Click(object sender, System.EventArgs e) { + RemoveSelectedItems(); + } + } + + public class LevelData { + public double txMouse; + public double tyMouse; + public IMapItem[] ami; + public SizeF Grid; + } +} diff --git a/m/LevelView.resources b/m/LevelView.resources new file mode 100644 index 0000000..5a8fd36 Binary files /dev/null and b/m/LevelView.resources differ diff --git a/m/LevelView.resx b/m/LevelView.resx new file mode 100644 index 0000000..5409b52 --- /dev/null +++ b/m/LevelView.resx @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + LevelView + + \ No newline at end of file diff --git a/m/LevelViewParent.cs b/m/LevelViewParent.cs new file mode 100644 index 0000000..ccb6938 --- /dev/null +++ b/m/LevelViewParent.cs @@ -0,0 +1,428 @@ +using System; +using System.Collections; +using System.ComponentModel; +using System.Drawing; +using System.Data; +using System.Windows.Forms; +using System.IO; + +namespace m +{ + /// + /// Summary description for LevelViewParent. + /// + public class LevelViewParent : System.Windows.Forms.UserControl, ICommandTarget + { + private m.LevelView view; + private System.Windows.Forms.ToolBar toolBar1; + private System.Windows.Forms.ComboBox comboDocs; + private System.Windows.Forms.ImageList imageList1; + private System.Windows.Forms.ToolBarButton toolBarButtonHideToolbar; + private System.Windows.Forms.Panel panelToolbar; + private System.Windows.Forms.Panel panelShowToolbar; + private System.Windows.Forms.ToolBar toolBarShowToolbar; + private System.Windows.Forms.ToolBarButton toolBarButtonShowToolbar; + private System.Windows.Forms.ToolBarButton toolBarButtonToggleTemplates; + private System.Windows.Forms.ToolBarButton toolBarButtonToggleGobs; + private System.ComponentModel.IContainer components; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.ComboBox comboZoom; + private System.Windows.Forms.ToolBarButton toolBarButtonToggleAreas; + TemplateDoc m_tmpdCurrent = null; + + public LevelViewParent() + { + // This call is required by the Windows.Forms Form Designer. + InitializeComponent(); + + // Default + panelShowToolbar.Hide(); + panelToolbar.Show(); + + // Easier than creating a resource file? + + System.Reflection.Assembly ass = typeof(LevelViewParent).Module.Assembly; + Stream stm = ass.GetManifestResourceStream("m.toolstrip.bmp"); + imageList1.Images.AddStrip(new Bitmap(stm)); + + TemplateDocTemplate doct = (TemplateDocTemplate)DocManager.FindDocTemplate(typeof(TemplateDoc)); + doct.DocAdded += new DocTemplate.DocAddedHandler(TemplateDocTemplate_DocAdded); + doct.DocRemoved += new DocTemplate.DocRemovedHandler(TemplateDocTemplate_DocRemoved); + + // Combo index 0 + + FillCombo(); + comboDocs.SelectedIndex = 0; + UpdateZoomSelection(); + view.ScaleChanged += new EventHandler(View_ScaleChanged); + } + + public void DispatchCommand(Command cmd) { + switch (cmd) { + case Command.Cut: + view.Cut(); + break; + + case Command.Copy: + view.Copy(); + break; + + case Command.Paste: + view.Paste(); + break; + + case Command.Delete: + view.Delete(); + break; + } + } + + void TemplateDocTemplate_DocAdded(Document doc) { + comboDocs.Items.Add(doc); + comboDocs.Invalidate(); + } + + void TemplateDocTemplate_DocRemoved(Document doc) { + // We'll get notified of this after the window has been destroyed. Only + // handle this event if this isn't the case + + if (!Created) + return; + + if (m_tmpdCurrent == (TemplateDoc)doc) { + m_tmpdCurrent = null; + comboDocs.SelectedIndex = 0; + } + + comboDocs.Items.Remove(doc); + } + + void FillCombo() { + comboDocs.Items.Clear(); + TemplateDocTemplate doct = (TemplateDocTemplate)DocManager.FindDocTemplate(typeof(TemplateDoc)); + Document[] adoc = doct.GetDocuments(); + comboDocs.Items.Add(""); + foreach (Document doc in adoc) + comboDocs.Items.Add(doc); + } + + public void SetDocument(Document doc) { + view.SetDocument(doc); + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Component Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.view = new m.LevelView(); + this.panelToolbar = new System.Windows.Forms.Panel(); + this.label2 = new System.Windows.Forms.Label(); + this.comboZoom = new System.Windows.Forms.ComboBox(); + this.label1 = new System.Windows.Forms.Label(); + this.toolBar1 = new System.Windows.Forms.ToolBar(); + this.toolBarButtonToggleTemplates = new System.Windows.Forms.ToolBarButton(); + this.toolBarButtonToggleGobs = new System.Windows.Forms.ToolBarButton(); + this.toolBarButtonToggleAreas = new System.Windows.Forms.ToolBarButton(); + this.toolBarButtonHideToolbar = new System.Windows.Forms.ToolBarButton(); + this.imageList1 = new System.Windows.Forms.ImageList(this.components); + this.comboDocs = new System.Windows.Forms.ComboBox(); + this.panelShowToolbar = new System.Windows.Forms.Panel(); + this.toolBarShowToolbar = new System.Windows.Forms.ToolBar(); + this.toolBarButtonShowToolbar = new System.Windows.Forms.ToolBarButton(); + this.panelToolbar.SuspendLayout(); + this.panelShowToolbar.SuspendLayout(); + this.SuspendLayout(); + // + // view + // + this.view.AllowDrop = true; + this.view.BackColor = System.Drawing.Color.Black; + this.view.Dock = System.Windows.Forms.DockStyle.Fill; + this.view.Name = "view"; + this.view.Size = new System.Drawing.Size(720, 616); + this.view.TabIndex = 0; + // + // panelToolbar + // + this.panelToolbar.BackColor = System.Drawing.Color.White; + this.panelToolbar.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.panelToolbar.Controls.AddRange(new System.Windows.Forms.Control[] { + this.label2, + this.comboZoom, + this.label1, + this.toolBar1, + this.comboDocs}); + this.panelToolbar.Location = new System.Drawing.Point(-1, -1); + this.panelToolbar.Name = "panelToolbar"; + this.panelToolbar.Size = new System.Drawing.Size(452, 25); + this.panelToolbar.TabIndex = 1; + this.panelToolbar.Visible = false; + // + // label2 + // + this.label2.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.label2.Location = new System.Drawing.Point(200, 4); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(80, 23); + this.label2.TabIndex = 5; + this.label2.Text = "Zoom Percent:"; + // + // comboZoom + // + this.comboZoom.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.comboZoom.Items.AddRange(new object[] { + "50", + "75", + "100", + "125", + "150", + "200", + "250", + "300"}); + this.comboZoom.Location = new System.Drawing.Point(280, 1); + this.comboZoom.Name = "comboZoom"; + this.comboZoom.Size = new System.Drawing.Size(72, 21); + this.comboZoom.TabIndex = 4; + this.comboZoom.TabStop = false; + this.comboZoom.KeyDown += new System.Windows.Forms.KeyEventHandler(this.comboZoom_KeyDown); + this.comboZoom.SelectedIndexChanged += new System.EventHandler(this.comboZoom_SelectedIndexChanged); + // + // label1 + // + this.label1.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.label1.Location = new System.Drawing.Point(0, 4); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(56, 23); + this.label1.TabIndex = 3; + this.label1.Text = "View with:"; + // + // toolBar1 + // + this.toolBar1.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.toolBar1.Appearance = System.Windows.Forms.ToolBarAppearance.Flat; + this.toolBar1.Buttons.AddRange(new System.Windows.Forms.ToolBarButton[] { + this.toolBarButtonToggleTemplates, + this.toolBarButtonToggleGobs, + this.toolBarButtonToggleAreas, + this.toolBarButtonHideToolbar}); + this.toolBar1.ButtonSize = new System.Drawing.Size(16, 15); + this.toolBar1.Divider = false; + this.toolBar1.Dock = System.Windows.Forms.DockStyle.None; + this.toolBar1.DropDownArrows = true; + this.toolBar1.ImageList = this.imageList1; + this.toolBar1.Location = new System.Drawing.Point(357, 1); + this.toolBar1.Name = "toolBar1"; + this.toolBar1.ShowToolTips = true; + this.toolBar1.Size = new System.Drawing.Size(99, 22); + this.toolBar1.TabIndex = 2; + this.toolBar1.ButtonClick += new System.Windows.Forms.ToolBarButtonClickEventHandler(this.toolBar1_ButtonClick); + // + // toolBarButtonToggleTemplates + // + this.toolBarButtonToggleTemplates.ImageIndex = 8; + this.toolBarButtonToggleTemplates.Pushed = true; + this.toolBarButtonToggleTemplates.Style = System.Windows.Forms.ToolBarButtonStyle.ToggleButton; + this.toolBarButtonToggleTemplates.ToolTipText = "Toggle Templates"; + // + // toolBarButtonToggleGobs + // + this.toolBarButtonToggleGobs.ImageIndex = 9; + this.toolBarButtonToggleGobs.Pushed = true; + this.toolBarButtonToggleGobs.Style = System.Windows.Forms.ToolBarButtonStyle.ToggleButton; + this.toolBarButtonToggleGobs.ToolTipText = "Toggle Gobs"; + // + // toolBarButtonToggleAreas + // + this.toolBarButtonToggleAreas.ImageIndex = 10; + this.toolBarButtonToggleAreas.Pushed = true; + this.toolBarButtonToggleAreas.Style = System.Windows.Forms.ToolBarButtonStyle.ToggleButton; + this.toolBarButtonToggleAreas.ToolTipText = "Toggle Areas"; + // + // toolBarButtonHideToolbar + // + this.toolBarButtonHideToolbar.ImageIndex = 6; + this.toolBarButtonHideToolbar.ToolTipText = "Hide Toolbar"; + // + // imageList1 + // + this.imageList1.ColorDepth = System.Windows.Forms.ColorDepth.Depth8Bit; + this.imageList1.ImageSize = new System.Drawing.Size(16, 15); + this.imageList1.TransparentColor = System.Drawing.Color.Magenta; + // + // comboDocs + // + this.comboDocs.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.comboDocs.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed; + this.comboDocs.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comboDocs.Location = new System.Drawing.Point(56, 1); + this.comboDocs.Name = "comboDocs"; + this.comboDocs.Size = new System.Drawing.Size(136, 21); + this.comboDocs.TabIndex = 1; + this.comboDocs.TabStop = false; + this.comboDocs.SelectedIndexChanged += new System.EventHandler(this.comboDocs_SelectedIndexChanged); + this.comboDocs.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.comboDocs_DrawItem); + // + // panelShowToolbar + // + this.panelShowToolbar.BackColor = System.Drawing.Color.White; + this.panelShowToolbar.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.panelShowToolbar.Controls.AddRange(new System.Windows.Forms.Control[] { + this.toolBarShowToolbar}); + this.panelShowToolbar.Location = new System.Drawing.Point(-3, -5); + this.panelShowToolbar.Name = "panelShowToolbar"; + this.panelShowToolbar.Size = new System.Drawing.Size(22, 23); + this.panelShowToolbar.TabIndex = 2; + // + // toolBarShowToolbar + // + this.toolBarShowToolbar.Appearance = System.Windows.Forms.ToolBarAppearance.Flat; + this.toolBarShowToolbar.Buttons.AddRange(new System.Windows.Forms.ToolBarButton[] { + this.toolBarButtonShowToolbar}); + this.toolBarShowToolbar.ButtonSize = new System.Drawing.Size(16, 15); + this.toolBarShowToolbar.Dock = System.Windows.Forms.DockStyle.None; + this.toolBarShowToolbar.DropDownArrows = true; + this.toolBarShowToolbar.ImageList = this.imageList1; + this.toolBarShowToolbar.Name = "toolBarShowToolbar"; + this.toolBarShowToolbar.ShowToolTips = true; + this.toolBarShowToolbar.Size = new System.Drawing.Size(25, 24); + this.toolBarShowToolbar.TabIndex = 0; + this.toolBarShowToolbar.ButtonClick += new System.Windows.Forms.ToolBarButtonClickEventHandler(this.toolBarShowToolbar_ButtonClick); + // + // toolBarButtonShowToolbar + // + this.toolBarButtonShowToolbar.ImageIndex = 7; + this.toolBarButtonShowToolbar.ToolTipText = "Show Toolbar"; + // + // LevelViewParent + // + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.panelShowToolbar, + this.panelToolbar, + this.view}); + this.Name = "LevelViewParent"; + this.Size = new System.Drawing.Size(720, 616); + this.panelToolbar.ResumeLayout(false); + this.panelShowToolbar.ResumeLayout(false); + this.ResumeLayout(false); + + } + #endregion + + private void toolBar1_ButtonClick(object sender, System.Windows.Forms.ToolBarButtonClickEventArgs e) { + switch (toolBar1.Buttons.IndexOf(e.Button)) { + case 0: + view.SetLayerFlags(view.GetLayerFlags() ^ LayerFlags.Templates); + break; + + case 1: + view.SetLayerFlags(view.GetLayerFlags() ^ LayerFlags.Gobs); + break; + + case 2: + view.SetLayerFlags(view.GetLayerFlags() ^ LayerFlags.Areas); + break; + + case 3: + panelToolbar.Hide(); + panelShowToolbar.Show(); + break; + } + } + + private void toolBarShowToolbar_ButtonClick(object sender, System.Windows.Forms.ToolBarButtonClickEventArgs e) { + switch (toolBarShowToolbar.Buttons.IndexOf(e.Button)) { + case 0: + panelShowToolbar.Hide(); + panelToolbar.Show(); + break; + } + } + + private void comboDocs_SelectedIndexChanged(object sender, System.EventArgs e) { + int nIndex = comboDocs.SelectedIndex; + if (nIndex == 0) { + view.SetTemplateDoc(null); + return; + } + view.SetTemplateDoc((TemplateDoc)comboDocs.Items[nIndex]); + } + + private void comboDocs_DrawItem(object sender, System.Windows.Forms.DrawItemEventArgs e) { + if (e.Index < 0 || e.Index >= comboDocs.Items.Count) + return; + string strName; + if (e.Index == 0) { + Document docActive = DocManager.GetActiveDocument(typeof(TemplateDoc)); + if (docActive == null) { + strName = "None"; + } else { + strName = "Active (" + docActive.GetName() + ")"; + } + m_tmpdCurrent = null; + } else { + Document doc = (Document)comboDocs.Items[e.Index]; + m_tmpdCurrent = (TemplateDoc)doc; + strName = doc.GetName(); + } + e.DrawBackground(); + e.Graphics.DrawString(strName, e.Font, new SolidBrush(e.ForeColor), e.Bounds.X, e.Bounds.Y); + e.DrawFocusRectangle(); + } + + private void comboZoom_SelectedIndexChanged(object sender, System.EventArgs e) { + SetScale(); + } + + private void comboZoom_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e) { + if (e.KeyCode == Keys.Enter) { + SetScale(); + } + } + + void SetScale() { + bool fReset = false; + try { + view.SetScale(float.Parse(comboZoom.Text) / 100.0f); + } catch { + fReset = true; + } + if (fReset) + UpdateZoomSelection(); + } + + void UpdateZoomSelection() { + string strT = ((float)(view.GetScale() * 100.0f)).ToString(); + for (int i = 0; i < comboZoom.Items.Count; i++) { + if (strT == float.Parse((string)comboZoom.Items[i]).ToString()) { + comboZoom.SelectedIndex = i; + break; + } + } + comboZoom.Text = strT; + } + + void View_ScaleChanged(object sender, EventArgs e) { + UpdateZoomSelection(); + } + } +} diff --git a/m/LevelViewParent.resources b/m/LevelViewParent.resources new file mode 100644 index 0000000..2df3c28 Binary files /dev/null and b/m/LevelViewParent.resources differ diff --git a/m/LevelViewParent.resx b/m/LevelViewParent.resx new file mode 100644 index 0000000..ca07b0c --- /dev/null +++ b/m/LevelViewParent.resx @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + LevelViewParent + + \ No newline at end of file diff --git a/m/OutputForm.cs b/m/OutputForm.cs new file mode 100644 index 0000000..2162f5a --- /dev/null +++ b/m/OutputForm.cs @@ -0,0 +1,175 @@ +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; +using System.Diagnostics; +using System.Text.RegularExpressions; + +namespace m +{ + /// + /// Summary description for OutputForm. + /// + public class OutputForm : System.Windows.Forms.Form + { + static private OutputForm s_frm = null; + private ArrayList m_alLevelErrors = new ArrayList(); + + static public void ShowIt() { + if (s_frm == null) { + s_frm = new OutputForm(); + s_frm.Owner = DocManager.GetFrameParent(); + } + s_frm.Show(); + s_frm.BringToFront(); + } + + static public void HideIt() { + if (s_frm != null) + s_frm.Hide(); + } + + static public void Clear() { + if (s_frm != null) { + s_frm.tbcOutput.Clear(); + s_frm.m_alLevelErrors.Clear(); + } + } + + static public void AppendText(string strFormat, params object[] aob) { + string str = String.Format(strFormat, aob); + s_frm.tbcOutput.AppendText(str); + } + + static public void Error(LevelDoc lvld, object ob, string strFormat, params object[] aob) { + string str = String.Format(strFormat, aob); + s_frm.AddError(lvld, ob, str); + } + + public void AddError(LevelDoc lvld, object ob, string str) { + int i = m_alLevelErrors.Add(new LevelError(lvld, ob)); + tbcOutput.AppendText(i.ToString() + "> " + str); + } + + private System.Windows.Forms.TextBox tbcOutput; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + public OutputForm() + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + Debug.Assert(s_frm == null); + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + s_frm = null; + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.tbcOutput = new System.Windows.Forms.TextBox(); + this.SuspendLayout(); + // + // tbcOutput + // + this.tbcOutput.AcceptsReturn = true; + this.tbcOutput.AcceptsTab = true; + this.tbcOutput.Dock = System.Windows.Forms.DockStyle.Fill; + this.tbcOutput.Multiline = true; + this.tbcOutput.Name = "tbcOutput"; + this.tbcOutput.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.tbcOutput.Size = new System.Drawing.Size(832, 246); + this.tbcOutput.TabIndex = 0; + this.tbcOutput.Text = ""; + this.tbcOutput.DoubleClick += new System.EventHandler(this.tbcOutput_DoubleClick); + // + // OutputForm + // + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.ClientSize = new System.Drawing.Size(832, 246); + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.tbcOutput}); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.SizableToolWindow; + this.Name = "OutputForm"; + this.Text = "Output"; + this.ResumeLayout(false); + + } + #endregion + + private void tbcOutput_DoubleClick(object sender, System.EventArgs e) { + LevelDoc lvld = (LevelDoc)DocManager.GetActiveDocument(typeof(LevelDoc)); + if (lvld == null) + return; + + // scan backwards from the insertion point to the beginning of the line + + string strT = tbcOutput.Text; + int i = tbcOutput.SelectionStart; + while (i > 0 && strT[i] != '\n') + i--; + if (i != 0) + i++; + strT = strT.Substring(i, strT.IndexOf('>', i) - i); + + // Select the item with the error + + i = int.Parse(strT); + LevelError lvle = (LevelError)m_alLevelErrors[i]; + + if (lvle.Object is IMapItem) { + ArrayList al = new ArrayList(); + al.Add(lvle.Object); + lvle.LevelDoc.Selection = al; + } else if (lvle.Object is SideInfo) { + Globals.PropertyGrid.SelectedObject = lvle.Object; + } + } + } + + public class LevelError { + private LevelDoc m_lvld; + private object m_ob; + + public LevelError(LevelDoc lvld, object ob) { + m_lvld = lvld; + m_ob = ob; + } + + public LevelDoc LevelDoc { + get { + return m_lvld; + } + } + + public object Object { + get { + return m_ob; + } + } +} +} diff --git a/m/OutputForm.resources b/m/OutputForm.resources new file mode 100644 index 0000000..bbb0484 Binary files /dev/null and b/m/OutputForm.resources differ diff --git a/m/OutputForm.resx b/m/OutputForm.resx new file mode 100644 index 0000000..8b0d612 --- /dev/null +++ b/m/OutputForm.resx @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + OutputForm + + \ No newline at end of file diff --git a/m/OutputTools.cs b/m/OutputTools.cs new file mode 100644 index 0000000..606548d --- /dev/null +++ b/m/OutputTools.cs @@ -0,0 +1,1186 @@ +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.Collections; +using System.IO; +using SpiffLib; +using System.Diagnostics; +using System.Text; +using m; + +namespace m +{ + public class OutputTools + { + public static void ImportExportPdbs(string[] astr) { + // Expand filespecs + ArrayList alsFiles = new ArrayList(); + for (int n = 1; n < astr.Length; n++) { + string strFileT = Path.GetFileName(astr[n]); + string strDirT = Path.GetDirectoryName(astr[n]); + if (strDirT == "") + strDirT = "."; + string[] astrFiles = Directory.GetFiles(strDirT, strFileT); + foreach (string filename in astrFiles) { + if (filename.EndsWith(".pdb")) { + alsFiles.Add(filename); + } + } + } + + // Import, then save each pdb + foreach (string filename in alsFiles) { + try { + if (File.Exists(filename + "_new")) { + Console.WriteLine("Exists: " + filename + "_new"); + continue; + } + ImportExpansionPdb(filename); + LevelDocTemplate doctLevel = (LevelDocTemplate)DocManager.FindDocTemplate(typeof(LevelDoc)); + Document[] adoc = doctLevel.GetDocuments(); + SaveExpansionPdb(filename + "_new", adoc, "1.1"); + foreach (Document doc in adoc) { + doc.SetModified(false); + } + doctLevel.CloseAllDocuments(); + } catch { + Console.WriteLine("Error loading " + filename + ", skipping"); + } + } + } + + public static void ImportExpansionPdb(string strFile) { + PdbPacker pdbp = new PdbPacker(strFile); + + ArrayList alsTileSets = new ArrayList(); + for (int i = 0; i < pdbp.Count; i++) { + PdbPacker.File file = pdbp[i]; + if (!file.str.EndsWith(".lvl")) { + continue; + } + + // Load up the pieces + + Ini ini = Ini.LoadBinary(new MemoryStream(file.ab)); + string strTileMapFilename = ini["General"]["TileMap"].Value; + TileMap tmap = TileMap.Load(new MemoryStream(pdbp[strTileMapFilename].ab)); + + + // First, tell the active LevelDoc not to switch its templates based on the following + // template load + + LevelDoc lvldActive = (LevelDoc)DocManager.GetActiveDocument(typeof(LevelDoc)); + if (lvldActive != null) { + lvldActive.SwitchTemplatesEnabled = false; + } + + // If the TileSet for this level is not yet available, load it now + + TemplateDoc tmpd = (TemplateDoc)DocManager.OpenDocument(tmap.Filename.Replace(".tset", ".tc")); + TileSet tset = null; + foreach (TileSet tsetT in alsTileSets) { + if (tsetT.FileName == tmap.Filename) { + tset = tsetT; + break; + } + } + if (tset == null) { + tset = new TileSet(tmpd, tmap.Filename); + alsTileSets.Add(tset); + } + + // Re-enable template switching + + if (lvldActive != null) { + lvldActive.SwitchTemplatesEnabled = true; + } + + // Create a new level description, and deduce which templates are in it, with what visibility + + LevelDoc lvld = (LevelDoc)DocManager.NewDocument(typeof(LevelDoc), null); + lvld.OutputFilename = file.str; + ImportTileMap(tmap, tset, tmpd, lvld); + + // Walls are stored in the terrain map. Load them. + string strTrmapFilename = ini["General"]["TerrainMap"].Value; + TerrainMap trmap = TerrainMap.Load(new MemoryStream(pdbp[strTrmapFilename].ab)); + ImportWalls(trmap, lvld); + + // Load everything else + lvld.LoadIni(ini); + } + } + + static void ImportWalls(TerrainMap trmap, LevelDoc lvld) { + ArrayList alsmi = new ArrayList(); + for (int ty = 0; ty < trmap.Map.GetLength(0); ty++) { + for (int tx = 0; tx < trmap.Map.GetLength(1); tx++) { + if (trmap.Map[ty, tx] == TerrainTypes.Wall) { + IMapItem mi = new Wall(100, lvld.Bounds.Left + tx, lvld.Bounds.Top + ty); + alsmi.Add(mi); + } + } + } + lvld.AddMapItems((IMapItem[])alsmi.ToArray(typeof(IMapItem))); + } + + class TemplatePos { + public Template tmpl; + public int txOrigin; + public int tyOrigin; + public bool[,] afMapped; + + public TemplatePos(Template tmpl, int txOrigin, int tyOrigin, bool[,] afMapped) { + this.tmpl = tmpl; + this.txOrigin = txOrigin; + this.tyOrigin = tyOrigin; + this.afMapped = afMapped; + } + } + + static void ImportTileMap(TileMap tmap, TileSet tset, TemplateDoc tmpd, LevelDoc lvld) { + // The TileMap is a list of indexes into a tile set. A Tileset is a list of tiles compiled + // from Templates. Reverse the tilemap into Templates, and set into lvld. + + bool[,] afCellTaken = new bool[64, 64]; + ArrayList alsTemplPos = new ArrayList(); + Template[] atmpl = tmpd.GetTemplates(); + for (int ty = 0; ty < tmap.Height; ty++) { + for (int tx = 0; tx < tmap.Width; tx++) { + // Cell mapped already? + if (afCellTaken[ty, tx]) { + continue; + } + + // Cell not mapped. Create TemplatePos. + int iTile = tmap.GetTileIndex(tx, ty); + TileData tdata = tset.GetTileData(iTile); + Template tmpl = atmpl[tdata.iTemplate]; + + // Don't bother with background tiles + if (tmpl == tmpd.GetBackgroundTemplate()) { + continue; + } + + int txOrigin = tx - tdata.txTemplate; + int tyOrigin = ty - tdata.tyTemplate; + bool[,] afMapped = new bool[tmpl.Cty, tmpl.Ctx]; + + for (int tyTmpl = 0; tyTmpl < tmpl.Cty; tyTmpl++) { + for (int txTmpl = 0; txTmpl < tmpl.Ctx; txTmpl++) { + int txT = txOrigin + txTmpl; + int tyT = tyOrigin + tyTmpl; + if (txT < 0 || txT >= 64 || tyT < 0 || tyT >= 64) { + continue; + } + if (afCellTaken[tyT, txT]) { + continue; + } + int iTileT = tmap.GetTileIndex(txT, tyT); + if (iTileT != -1) { + TileData tdataT = tset.GetTileData(iTileT); + if (tdataT.iTemplate != tdata.iTemplate) { + continue; + } + if (tdataT.txTemplate != txTmpl || tdataT.tyTemplate != tyTmpl) { + continue; + } + } + afMapped[tyTmpl, txTmpl] = true; + afCellTaken[tyT, txT] = true; + } + } + alsTemplPos.Add(new TemplatePos(tmpl, txOrigin, tyOrigin, afMapped)); + } + } + + // Figure out the bounds. + + Rectangle rcBounds = new Rectangle((64 - tmap.Width) / 2, (64 - tmap.Height) / 2, + tmap.Width, tmap.Height); + lvld.Bounds = rcBounds; + + // The list of TemplatePos's has been created. Add to LevelDoc. + + ArrayList alsTiles = new ArrayList(); + foreach (TemplatePos tpos in alsTemplPos) { + Tile tile = new Tile(tpos.tmpl.Name, + tpos.txOrigin + rcBounds.Left, + tpos.tyOrigin + rcBounds.Top, + tpos.afMapped, tpos.tmpl.OccupancyMap); + alsTiles.Add(tile); + } + lvld.AddMapItems((IMapItem[])alsTiles.ToArray(typeof(IMapItem))); + } + + public static void SaveExpansionPdb(string strFile, Document[] adoc, string strVersion) { + // First save all level docs + + //annoying + //DocManager.SaveAllModified(typeof(LevelDoc)); + + // Remember active document + + LevelDoc lvldActive = (LevelDoc)DocManager.GetActiveDocument(typeof(LevelDoc)); + + // Save documents adoc in an expansion .pdb. Expansion .pdbs need: + // - .lvl, .tmap, .trmap, but not .tsets since these come from game .pdbs + // - need a version.txt + // - need an if demo trigger check "inserted" at save time + // - .pdb needs WARI creator, ADD1 type + // - data should be compressed for obfuscation purposes + + PdbPacker pdbp = new PdbPacker(); + + // Add version.txt + + byte[] abT = new byte[strVersion.Length + 1]; + for (int n = 0; n < strVersion.Length; n++) + abT[n] = (byte)strVersion[n]; + abT[abT.Length - 1] = 0; + pdbp.Add(new PdbPacker.File("version.txt", abT)); + + // Load res.h from embedded resource in prep for pre-process + + System.Reflection.Assembly ass = typeof(GobImage).Module.Assembly; + Stream stmResDotH = ass.GetManifestResourceStream("m.EmbeddedResources." + "res.h"); + if (stmResDotH == null) + throw new Exception("Cannot load res.h"); + + // Compile levels + + Random rand = new Random(); + ArrayList alsTileSets = new ArrayList(); + foreach (LevelDoc lvld in adoc) { + // Need to do this unfortunately; some of the "saving" code relies on what the "active" document + // is!! + + DocManager.SetActiveDocument(typeof(LevelDoc), lvld); + + TemplateDoc tmpd = lvld.GetTemplateDoc(); + + // Get appropriate TileSet, or make one if this map + // uses a new tile collection + + TileSet tset = null; + foreach (TileSet tsetT in alsTileSets) { + if (tsetT.TemplateDoc == tmpd) { + tset = tsetT; + break; + } + } + + // Create new tile set if none found + + if (tset == null) { + tset = new TileSet(tmpd, tmpd.GetName() + ".tset"); + alsTileSets.Add(tset); + } + +#if false + // Generate base file name for this level (this is never user-visible, but it should be + // as unique as possible + + char[] achBase = new char[16]; + for (int n = 0; n < achBase.Length; n++) { + int nT = rand.Next() % (26 + 10); + if (nT < 26) { + achBase[n] = (char)(nT + 97); + } else { + achBase[n] = (char)(nT + 48 - 26); + } + } + if (lvld.MaxPlayers > 1) { + achBase[0] = 'm'; + achBase[1] = '_'; + } + string strBase = new String(achBase); +#else + // This isn't unique which can cause problems due to how packfiles work. + // Could change packfile api to accept .pdb parameter. + // Note1: set next mission action requires predictable filename + // Note2: mission sorting is based on filename, not title + // Could put lots of "support" in to fix these problems, or just ship + // it like this. + // + // Hack: filename length 29 + // Maximum extension on filename: 7 + + string strBase = lvld.Title; + if (strBase.Length > 29 - 7) + strBase = strBase.Substring(0, 29 - 7); + + // If multiplayer, add "m_" to the name by losing the last two characters + // so sort order is preserved + + if (lvld.MaxPlayers > 1) + strBase = "m_" + strBase.Substring(0, strBase.Length - 2); +#endif + + // Get tile map file for this level + + MemoryStream stmTmap = new MemoryStream(); + TileMap tmap = TileMap.CreateFromImage(tset, lvld.GetMapBitmap(tmpd.TileSize, tmpd, true), tmpd.TileSize); + tmap.Save(stmTmap); + string strTmap = strBase + ".tmap"; + pdbp.Add(new PdbPacker.File(strTmap, stmTmap.ToArray())); + stmTmap.Close(); + + // Get the terrain map file for this level + + MemoryStream stmTRmap = new MemoryStream(); + TerrainMap trmap = new TerrainMap(lvld.GetTerrainMap(tmpd.TileSize, tmpd, false)); + trmap.Save(stmTRmap); + string strTRmap = strBase + ".trmap"; + pdbp.Add(new PdbPacker.File(strTRmap, stmTRmap.ToArray())); + stmTRmap.Close(); + + // Save .ini file for this level doc + + MemoryStream stmLvld = new MemoryStream(); + lvld.SaveIni(stmLvld, -1, strTmap, strTRmap, tmpd.GetName() + ".palbin", true); + + // Pre-process + + stmLvld.Seek(0, SeekOrigin.Begin); + MemoryStream stmPreprocessed = Misc.PreprocessStream(stmLvld, stmResDotH); + stmPreprocessed.Seek(0, SeekOrigin.Begin); + Ini iniPreProcessed = new Ini(stmPreprocessed); + + MemoryStream stmLvldPreProcessedBinary = new MemoryStream(); + iniPreProcessed.SaveBinary(stmLvldPreProcessedBinary); + stmLvldPreProcessedBinary.Close(); + + string strLvlName = lvld.OutputFilename; + if (strLvlName == null) { + strLvlName = strBase + ".lvl"; + } + pdbp.Add(new PdbPacker.File(strLvlName, stmLvldPreProcessedBinary.ToArray())); + stmLvldPreProcessedBinary.Close(); + } + stmResDotH.Close(); + + // Restore active document + + if (lvldActive != null) + DocManager.SetActiveDocument(typeof(LevelDoc), lvldActive); + + // Now save out pdb + + pdbp.Save(strFile, "WARI", "ADD2"); + } + + public static void MixMapImportSpecial(Theater theater, TemplateDoc tmpdCopyTerrain, string strFileSave) { + TemplateDoc tmpd24 = (TemplateDoc)DocManager.NewDocument(typeof(TemplateDoc), new Object[] { new Size(24, 24) }); + MixTemplate[] amixt = MixSuck.LoadTemplates(theater); + MixSuck.ImportTemplates(amixt, tmpd24); + + Template[] atmpl24 = tmpd24.GetTemplates(); + Template[] atmpl16 = tmpdCopyTerrain.GetTemplates(); + + for (int n = 0; n < atmpl24.Length; n++) { + Template tmpl24 = atmpl24[n]; + Template tmpl16 = atmpl16[n]; + tmpl24.TerrainMap = tmpl16.TerrainMap; + } + + tmpd24.SetBackgroundTemplate(atmpl24[0]); + tmpd24.SaveAs(strFileSave); + } + + public static void MakePalette(string[] astr) { + // -makepal 16 templates.tc palsize fixpalsize backgroundpalsize fixed.pal out.pal + + // tile size + + Size sizTile = new Size(0, 0); + sizTile.Width = int.Parse(astr[1]); + sizTile.Height = sizTile.Width; + + // Load template collection + + TemplateDoc tmpd = (TemplateDoc)DocManager.OpenDocument(astr[2]); + + // palette size + + int cPalEntries = int.Parse(astr[3]); + + // entries fixed + + int cPalEntriesFixed = int.Parse(astr[4]); + + // entries for background + + int cPalEntriesBackground = int.Parse(astr[5]); + + // fixed palette + + Palette palFixed = new Palette(astr[6]); + + // output palette + + string strFilePalOut = astr[7]; + + // If this template collection already has a palette it has already been quantized; we don't + // want that. + + if (tmpd.GetPalette() != null) + new Exception("Template collection has already been quantized!"); + + // Scale templates if needed + + if (sizTile.Width != tmpd.TileSize.Width || sizTile.Height != tmpd.TileSize.Height) + TemplateTools.ScaleTemplates(tmpd, sizTile); + + // Quantize + + TemplateTools.QuantizeTemplates(tmpd, palFixed, cPalEntries, cPalEntriesFixed, cPalEntriesBackground); + + // Save the new palette out + + Palette palNew = tmpd.GetPalette(); + palNew.SaveJasc(strFilePalOut); + } + + public static void ExportMixMaps(string[] astr) { + // Expand filespecs + ArrayList alsFiles = new ArrayList(); + for (int n = 1; n < astr.Length; n++) { + string strFileT = Path.GetFileName(astr[n]); + string strDirT = Path.GetDirectoryName(astr[n]); + if (strDirT == "") + strDirT = "."; + string[] astrFiles = Directory.GetFiles(strDirT, strFileT); + alsFiles.AddRange(astrFiles); + } + + foreach (string strFile in alsFiles) { + LevelDoc lvld = (LevelDoc)DocManager.NewDocument(typeof(LevelDoc), null); + Console.Write("Exporting " + strFile + " as "); + string strFileExport = MixSuck.ImportExportMixMap(strFile, lvld); + if (strFileExport == null) { + Console.Write("Error exporting!\n"); + } else { + Console.Write(strFileExport + "\n"); + } + lvld.Dispose(); + } + } + + public static void ExportImages(string[] astr) { + // Get directory + //string strDir = Path.GetFullPath(astr[1]); + string strDir = "."; + string strPrefix = astr[1]; + + // Expand filespecs + ArrayList alsFiles = new ArrayList(); + for (int n = 2; n < astr.Length; n++) { + string strFileT = Path.GetFileName(astr[n]); + string strDirT = Path.GetDirectoryName(astr[n]); + if (strDirT == "") + strDirT = "."; + string[] astrFiles = Directory.GetFiles(strDirT, strFileT); + alsFiles.AddRange(astrFiles); + } + + // Attempt to process these level docs + foreach (string strFile in alsFiles) { + Console.Write("Map of " + strFile + " -> "); + LevelDoc lvld = (LevelDoc)DocManager.OpenDocument(strFile); + if (lvld == null) + throw new Exception("Could not load level doc " + strFile); + string strPng = strDir + Path.DirectorySeparatorChar + strPrefix + Path.GetFileName(strFile).Replace(".ld", ".png"); + Console.Write(strPng + "..."); + TemplateDoc tmpd = lvld.GetTemplateDoc(); + Bitmap bm = lvld.GetMapBitmap(tmpd.TileSize, tmpd, true); + bm.Save(strPng, ImageFormat.Png); + bm.Dispose(); + lvld.Dispose(); + Console.Write(" Done.\n"); + } + } + + public static void ExportLevels(string[] astr, int nVersion) { + // Get tile size + + Size sizTile = new Size(0, 0); + sizTile.Width = int.Parse(astr[1]); + sizTile.Height = sizTile.Width; + + // Get depth + + int nDepth = Int32.Parse(astr[2]); + + // Get background threshold + + double nAreaBackgroundThreshold = double.Parse(astr[3]); + + // Background luminance multiplier + + double nLuminanceMultBackground = double.Parse(astr[4]); + + // Background saturation multiplier + + double nSaturationMultBackground = double.Parse(astr[5]); + + // Foreground luminance multiplier + + double nLuminanceMultForeground = double.Parse(astr[6]); + + // Foreground saturation multiplier + + double nSaturationMultForeground = double.Parse(astr[7]); + + // Palette directory + + string strPalDir = astr[8]; + + // Get output directory + + string strDir = Path.GetFullPath(astr[9]); + + // Expand filespecs + ArrayList alsFiles = new ArrayList(); + for (int n = 9; n < astr.Length; n++) { + string strFileT = Path.GetFileName(astr[n]); + string strDirT = Path.GetDirectoryName(astr[n]); + if (strDirT == "") + strDirT = "."; + string[] astrFiles = Directory.GetFiles(strDirT, strFileT); + alsFiles.AddRange(astrFiles); + } + + // Attempt to process these level doc + ArrayList alsTileSets = new ArrayList(); + foreach (string strFile in alsFiles) { + Console.Write("Loading " + strFile + "..."); + LevelDoc lvld = (LevelDoc)DocManager.OpenDocument(strFile); + if (lvld == null) + throw new Exception("Could not load level doc " + strFile); + Console.Write(" Done.\n"); + + // Size this template collection if necessary + + TemplateDoc tmpd = lvld.GetTemplateDoc(); + if (sizTile.Width != tmpd.TileSize.Width || sizTile.Height != tmpd.TileSize.Height) + TemplateTools.ScaleTemplates(tmpd, sizTile); + + // Get appropriate TileSet, or make one if this map + // uses a new tile collection + TileSet tset = null; + foreach (TileSet tsetT in alsTileSets) { + if (tsetT.TileCollectionFileName == Path.GetFileName(lvld.GetTemplateDoc().GetPath())) { + tset = tsetT; + break; + } + } + + // Create new tile set if none found + if (tset == null) { + string strTcPath = lvld.GetTemplateDoc().GetPath(); + string strTcName = Path.GetFileNameWithoutExtension(strTcPath); + string strTcDir = Path.GetDirectoryName(strTcPath); + string strT3 = strTcDir + Path.DirectorySeparatorChar + strPalDir + Path.DirectorySeparatorChar + strTcName; + tset = new TileSet(lvld.GetTemplateDoc(), Path.GetFileName(strTcPath), strT3, nDepth, sizTile); + alsTileSets.Add(tset); + } + + // Get and save a tile map for this level + string strTmap = Path.GetFileName(lvld.GetPath().Replace(".ld", ".tmap")); + string strFileTmap = strDir + Path.DirectorySeparatorChar + strTmap; + Console.Write("Creating & writing " + strFileTmap + "..."); + TileMap tmap = TileMap.CreateFromImage(tset, lvld.GetMapBitmap(tmpd.TileSize, tmpd, true), tmpd.TileSize); + tmap.Save(strFileTmap); + Console.Write(" Done.\n"); + + // Get and save terrain map for this level + string strTrmap = Path.GetFileName(lvld.GetPath().Replace(".ld", ".trmap")); + string strFileTrmap = strDir + Path.DirectorySeparatorChar + strTrmap; + Console.Write("Creating & writing " + strFileTrmap + "..."); + TerrainMap trmap = new TerrainMap(lvld.GetTerrainMap(tmpd.TileSize, tmpd, false)); + trmap.Save(strFileTrmap); + Console.Write(" Done.\n"); + + // Save .ini for this level doc + string strFileIni = strDir + Path.DirectorySeparatorChar + Path.GetFileName(strFile).Replace(".ld", ".lvl"); + Console.Write("Writing " + strFileIni + "..."); + lvld.SaveIni(strFileIni, nVersion, strTmap, strTrmap, tset.PalBinFileName); + Console.Write(" Done.\n"); + lvld.Dispose(); + } + + // Now write out tilesets + foreach (TileSet tset in alsTileSets) { + // Save tile set + string strFileTset = strDir + Path.DirectorySeparatorChar + tset.FileName; + Console.Write("Creating & writing " + strFileTset + ", " + tset.Count.ToString() + " tiles..."); + tset.Save(strFileTset); + tset.SaveMini(strFileTset, nAreaBackgroundThreshold, nLuminanceMultBackground, nSaturationMultBackground, nLuminanceMultForeground, nSaturationMultForeground); + Console.Write(" Done.\n"); + } + } + + public static void ExportText(string[] astr) { + // Expand filespecs + ArrayList alsFiles = new ArrayList(); + for (int n = 1; n < astr.Length; n++) { + string strFileT = Path.GetFileName(astr[n]); + string strDirT = Path.GetDirectoryName(astr[n]); + if (strDirT == "") + strDirT = "."; + string[] astrFiles = Directory.GetFiles(strDirT, strFileT); + alsFiles.AddRange(astrFiles); + } + + Console.WriteLine("Exporting text from {0} files", alsFiles.Count); + + // Attempt to process these level docs + + foreach (string strFile in alsFiles) { + Console.Write("Writing text of " + strFile + "..."); + LevelDoc lvld = (LevelDoc)DocManager.OpenDocument(strFile); + if (lvld == null) + throw new Exception("Could not load level doc " + strFile); + string strTextFile = "." + Path.DirectorySeparatorChar + Path.GetFileName(strFile).Replace(".ld", ".txt"); + string str = lvld.GetLevelText(); + str = str.Replace("\n", "\r\n"); + StreamWriter stmw = new StreamWriter(strTextFile); + stmw.Write(str); + stmw.Close(); + lvld.Dispose(); + Console.WriteLine(" done"); + } + } + + public static void ImportText(string[] astr) { + // Expand filespecs + ArrayList alsFiles = new ArrayList(); + for (int n = 1; n < astr.Length; n++) { + string strFileT = Path.GetFileName(astr[n]); + string strDirT = Path.GetDirectoryName(astr[n]); + if (strDirT == "") + strDirT = "."; + string[] astrFiles = Directory.GetFiles(strDirT, strFileT); + alsFiles.AddRange(astrFiles); + } + + Console.WriteLine("Importing text from {0} files", alsFiles.Count); + + // Attempt to process these text files/level docs + + foreach (string strTextFile in alsFiles) { + string strFile = "." + Path.DirectorySeparatorChar + Path.GetFileName(strTextFile).Replace(".txt", ".ld"); + Console.Write("Writing " + strTextFile + " to " + strFile + "..."); + LevelDoc lvld = (LevelDoc)DocManager.OpenDocument(strFile); + if (lvld == null) + throw new Exception("Could not load level doc " + strFile); + + StreamReader stmr = new StreamReader(strTextFile); + string str = stmr.ReadToEnd(); + stmr.Close(); + str = str.Replace("\r\n", "\n"); + + int ichErrorPos; + if (!lvld.SetLevelText(str, out ichErrorPos)) { + Console.WriteLine(" error at char " + ichErrorPos); + } else { + lvld.Save(); + Console.WriteLine(" saved"); + } + lvld.Dispose(); + } + } + } + + class TerrainMap { + private TerrainTypes[,] m_aterMap; + + public TerrainMap(TerrainTypes[,] aterMap) { + m_aterMap = aterMap; + } + + public void Save(string strFileTrmap) { + Stream stm = new FileStream(strFileTrmap, FileMode.Create, FileAccess.Write, FileShare.None); + Save(stm); + } + + public void Save(Stream stm) { + BinaryWriter bwtr = new BinaryWriter(stm); + + //struct TerrainMapHeader { // trmhdr + // word ctx; + // word cty; + //}; + + bwtr.Write(Misc.SwapUShort((ushort)m_aterMap.GetLength(1))); + bwtr.Write(Misc.SwapUShort((ushort)m_aterMap.GetLength(0))); + for (int ty = 0; ty < m_aterMap.GetLength(0); ty++) { + for (int tx = 0; tx < m_aterMap.GetLength(1); tx++) { + byte b = 0; + switch (m_aterMap[ty, tx]) { + default: + case TerrainTypes.Open: + b = 1; + break; + + case TerrainTypes.Area: + b = 0; + break; + + case TerrainTypes.Wall: + b = 2; + break; + + case TerrainTypes.Blocked: + b = 3; + break; + } + bwtr.Write(b); + } + } + bwtr.Close(); + } + + public static TerrainMap Load(MemoryStream stm) { + BinaryReader brdr = new BinaryReader(stm); + int ctx = (int)Misc.SwapUShort(brdr.ReadUInt16()); + int cty = (int)Misc.SwapUShort(brdr.ReadUInt16()); + TerrainTypes[,] map = new TerrainTypes[cty, ctx]; + for (int ty = 0; ty < cty; ty++) { + for (int tx = 0; tx < ctx; tx++) { + byte b = brdr.ReadByte(); + switch (b) { + case 0: // Area + map[ty, tx] = TerrainTypes.Area; + break; + + case 1: // Open + map[ty, tx] = TerrainTypes.Open; + break; + + case 2: // Wall + map[ty, tx] = TerrainTypes.Wall; + break; + + case 3: // Blocked + map[ty, tx] = TerrainTypes.Blocked; + break; + + default: // Default to Open + map[ty, tx] = TerrainTypes.Open; + break; + } + } + } + brdr.Close(); + return new TerrainMap(map); + } + + public TerrainTypes[,] Map { + get { + return m_aterMap; + } + } + } + + class TileMap { + int m_ctx; + int m_cty; + Size m_sizTile; + int[,] m_aiTile; + string m_strTSetFileName; + + public TileMap(string strTSetFileName, int ctx, int cty, Size sizTile) { + m_strTSetFileName = strTSetFileName; + m_ctx = ctx; + m_cty = cty; + m_sizTile = sizTile; + m_aiTile = new int[cty, ctx]; + } + + public TileMap(string strTSetFileName, int ctx, int cty, int[,] aiTile, Size sizTile) { + m_strTSetFileName = strTSetFileName; + m_ctx = ctx; + m_cty = cty; + m_aiTile = aiTile; + m_sizTile = sizTile; + } + + public string Filename { + get { + return m_strTSetFileName; + } + } + + public int Width { + get { + return m_ctx; + } + } + + public int Height { + get { + return m_cty; + } + } + + public static TileMap CreateFromImage(TileSet tset, Bitmap bm, Size sizTile) { + int ctx = bm.Width / sizTile.Width; + int cty = bm.Height / sizTile.Height; + TileMap tmap = new TileMap(tset.FileName, ctx, cty, sizTile); + tmap.InitFromImage(tset, bm); + return tmap; + } + + public void InitFromImage(TileSet tset, Bitmap bm) { + Color[] aclrTile = new Color[m_sizTile.Width * m_sizTile.Height]; + for (int ty = 0; ty < m_cty; ty++) { + for (int tx = 0; tx < m_ctx; tx++) { + TileSet.ExtractTilePixels(bm, tx, ty, m_sizTile, ref aclrTile); + int index = tset.FindTileIndex(aclrTile); + if (index == -1) + throw new Exception("Couldn't find tile index!"); + m_aiTile[ty, tx] = index; + } + } + } + + public void Save(string strFileTmap) { + Stream stm = new FileStream(strFileTmap, FileMode.Create, FileAccess.Write, FileShare.None); + Save(stm); + } + + public void Save(Stream stm) { + BinaryWriter bwtr = new BinaryWriter(stm); + + // #define kcbFilename 28 + // struct TileMapHeader { // thdr + // char szFnTset[kcbFilename]; + // word ctx; + // word cty; + // }; + + char[] szFnTset = new char[28]; + m_strTSetFileName.CopyTo(0, szFnTset, 0, m_strTSetFileName.Length); + bwtr.Write(szFnTset); + bwtr.Write(Misc.SwapUShort((ushort)m_ctx)); + bwtr.Write(Misc.SwapUShort((ushort)m_cty)); + + for (int ty = 0; ty < m_cty; ty++) { + for (int tx = 0; tx < m_ctx; tx++) { + bwtr.Write(Misc.SwapUShort((ushort)(m_aiTile[ty, tx] << 2))); + } + } + bwtr.Close(); + } + + public static TileMap Load(MemoryStream stm) { + BinaryReader brdr = new BinaryReader(stm); + byte[] ab = brdr.ReadBytes(28); + int i = 0; + for (; i < ab.Length; i++) { + if (ab[i] == 0) { + break; + } + } + string strFilename = Encoding.ASCII.GetString(ab, 0, i); + int ctx = Misc.SwapUShort(brdr.ReadUInt16()); + int cty = Misc.SwapUShort(brdr.ReadUInt16()); + + int [,] aiTile = new int[cty, ctx]; + for (int ty = 0; ty < cty; ty++) { + for (int tx = 0; tx < ctx; tx++) { + aiTile[ty, tx] = Misc.SwapUShort(brdr.ReadUInt16()) >> 2; + } + } + brdr.Close(); + + Size sizTile = new Size(24, 24); // hardwired + return new TileMap(strFilename, ctx, cty, aiTile, sizTile); + } + + public int GetTileIndex(int tx, int ty) { + if (tx < 0 || tx >= m_aiTile.GetLength(1)) { + return -1; + } + if (ty < 0 || ty >= m_aiTile.GetLength(0)) { + return -1; + } + return m_aiTile[ty, tx]; + } + } + + struct TileData { + public int hash; + public Color[] aclr; + public int iTemplate; + public int txTemplate; + public int tyTemplate; + } + + class TileSet { + public string FileName; + public string TileCollectionFileName; + public string PalBinFileName; + public TemplateDoc TemplateDoc; + private ArrayList m_alsTileData = new ArrayList(); + private Palette m_pal = null; + private static int s_cbFileMax = 32000; + Size m_sizTile; + + public TileSet(TemplateDoc tmpd, string strFile) { + TemplateDoc = tmpd; + m_pal = tmpd.GetPalette(); + m_sizTile = tmpd.TileSize; + FileName = strFile.Replace(".tc", ".tset"); + SuckTemplates(); + } + + public TileSet(TemplateDoc tmpd, string strFile, string strFilePal, int nDepth, Size sizTile) { + TemplateDoc = tmpd; + m_pal = new Palette(strFilePal + "_" + nDepth.ToString() + "bpp.pal"); + TileCollectionFileName = strFile; + FileName = strFile.Replace(".tc", ".tset"); + PalBinFileName = Path.GetFileName(strFilePal) + ".palbin"; + m_sizTile = sizTile; + SuckTemplates(); + } + + private void SuckTemplates() { + // Suck all the tiles in + + Template[] atmpl = TemplateDoc.GetTemplates(); + int iTemplate = 0; + foreach (Template tmpl in atmpl) { + bool[,] afOccupancy = tmpl.OccupancyMap; + for (int ty = 0; ty < afOccupancy.GetLength(0); ty++) { + for (int tx = 0; tx < afOccupancy.GetLength(1); tx++) { + if (!afOccupancy[ty, tx]) + continue; + TileData tdata = new TileData(); + tdata.iTemplate = iTemplate; + tdata.txTemplate = tx; + tdata.tyTemplate = ty; + tdata.aclr = new Color[m_sizTile.Width * m_sizTile.Height]; + ExtractTilePixels(tmpl.Bitmap, tx, ty, m_sizTile, ref tdata.aclr); + tdata.hash = HashTile(tdata.aclr); + m_alsTileData.Add(tdata); + } + } + iTemplate++; + } + } + + public static unsafe void ExtractTilePixels(Bitmap bm, int tx, int ty, Size sizTile, ref Color[] aclrTile) { + Rectangle rcSrc = new Rectangle(tx * sizTile.Width, ty * sizTile.Height, sizTile.Width, sizTile.Height); + BitmapData bmd = bm.LockBits(rcSrc, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); + IntPtr p = bmd.Scan0; + byte *pbBase = (byte *)p.ToPointer(); + for (int y = 0; y < sizTile.Height; y++) { + for (int x = 0; x < sizTile.Width; x++) { + byte *pb = pbBase + y * bmd.Stride + x * 3; + aclrTile[y * sizTile.Width + x] = Color.FromArgb(pb[2], pb[1], pb[0]); + } + } + bm.UnlockBits(bmd); + } + + public int FindTileIndex(Color[] aclrTile) { + int hash = HashTile(aclrTile); + for (int n = 0; n < m_alsTileData.Count; n++) { + TileData td = (TileData)m_alsTileData[n]; + if (hash == td.hash && CheckTileMatch(aclrTile, td.aclr)) + return n; + } + return -1; + } + + int HashTile(Color[] aclrTile) { + int hash = 0; + for (int n = 0; n < aclrTile.Length; n++) { + Color clr = aclrTile[n]; + hash += (hash << 1) | (clr.R << 16) + (clr.G << 8) + clr.B; + } + return hash; + } + + bool CheckTileMatch(Color[] aclr1, Color[] aclr2) { + for (int n = 0; n < aclr1.Length; n++) { + if (aclr1[n] != aclr2[n]) + return false; + } + return true; + } + + public void Save(string strFileTset) { + // struct TileSetHeader { // tshdr + // ushort cTiles; + // ushort cxTile; + // ushort cyTile; + // }; + + int cbTile = m_sizTile.Width * m_sizTile.Height; + int cTilesFit = (s_cbFileMax - 6) / cbTile; + int cTilesLeft = m_alsTileData.Count; + + int nFile = 0; + int nTile = 0; + while (cTilesLeft != 0) { + // Get filename + string strT = strFileTset; + if (nFile > 0) + strT = strFileTset.Replace(".tset", ".tset." + nFile.ToString()); + nFile++; + + // Open file for writing + Stream stm = new FileStream(strT, FileMode.Create, FileAccess.Write, FileShare.None); + BinaryWriter bwtr = new BinaryWriter(stm); + + // Figure out how many tiles will go in this file + int cTilesWrite = Math.Min(cTilesFit, cTilesLeft); + + // Write out the header + bwtr.Write(Misc.SwapUShort((ushort)cTilesWrite)); + bwtr.Write(Misc.SwapUShort((ushort)m_sizTile.Width)); + bwtr.Write(Misc.SwapUShort((ushort)m_sizTile.Height)); + + // Write out the tiles + for (int n = 0; n < cTilesWrite; n++) { + bwtr.Write(GetTileBytes(nTile)); + nTile++; + } + + // Next file + bwtr.Close(); + cTilesLeft -= cTilesWrite; + } + } + + public void SaveMini(string strFileTset, double nAreaBackgroundThreshold, double nLuminanceMultBackground, double nSaturationMultBackground, double nLuminanceMultForeground, double nSaturationMultForeground) { + // Now write out minimap tiles, 2x2 and 1x1 + + Stream stmT = new FileStream(strFileTset.Replace(".tset", ".tsetmini"), FileMode.Create, FileAccess.Write, FileShare.None); + BinaryWriter bwtrMini = new BinaryWriter(stmT); + WriteMiniTiles(bwtrMini, m_pal, TemplateDoc, 2, true, nAreaBackgroundThreshold, nLuminanceMultBackground, nSaturationMultBackground, nLuminanceMultForeground, nSaturationMultForeground); + WriteMiniTiles(bwtrMini, m_pal, TemplateDoc, 1, false, nAreaBackgroundThreshold, nLuminanceMultBackground, nSaturationMultBackground, nLuminanceMultForeground, nSaturationMultForeground); + bwtrMini.Close(); + } + + void WriteMiniTiles(BinaryWriter bwtr, Palette pal, TemplateDoc tmpd, int cx, bool fNext, double nAreaBackgroundThreshold, double nLuminanceMultBackground, double nSaturationMultBackground, double nLuminanceMultForeground, double nSaturationMultForeground) { + // struct MiniTileSetHeader { // mtshdr + // ushort offNext; + // ushort cTiles; + // ushort cxTile; + // ushort cyTile; + // }; + + ushort offNext = 0; + if (fNext) + offNext = (ushort)(8 + m_alsTileData.Count * cx * cx); + bwtr.Write(Misc.SwapUShort(offNext)); + bwtr.Write(Misc.SwapUShort((ushort)m_alsTileData.Count)); + bwtr.Write(Misc.SwapUShort((ushort)cx)); + bwtr.Write(Misc.SwapUShort((ushort)cx)); + + // If a background template exists, use it to distinguish foreground from background objects for better minimaps + + Size sizTile = tmpd.TileSize; + ArrayList alsColors = new ArrayList(); + Template tmplBackground = tmpd.GetBackgroundTemplate(); + if (tmplBackground != null && nAreaBackgroundThreshold >= 0.0) { + // Get despeckled hue map of background, calc mean and + // std dev for filtering purposes + + Bitmap bmHueBackground = TemplateTools.MakeHueMap(tmplBackground.Bitmap); + TemplateTools.DespeckleGrayscaleBitmap(bmHueBackground, 9, 50); + double nMean = TemplateTools.CalcGrayscaleMean(bmHueBackground); + double nStdDev = TemplateTools.CalcGrayscaleStandardDeviation(bmHueBackground, nMean); + + // Go through each tile, first make a mask that'll delineate foreground from background + + Bitmap bmTile = new Bitmap(sizTile.Width, sizTile.Height); + foreach (TileData td in m_alsTileData) { + // Need to turn data back into a bitmap - doh! + + for (int y = 0; y < sizTile.Height; y++) { + for (int x = 0; x < sizTile.Width; x++) { + Color clr = (Color)td.aclr[y * sizTile.Width + x]; + bmTile.SetPixel(x, y, clr); + } + } + + // Create mask which'll replace background with transparent color (255, 0, 255) + + Bitmap bmMask = TemplateTools.MakeHueMap(bmTile); + TemplateTools.DespeckleGrayscaleBitmap(bmMask, 9, 50); + TemplateTools.SubtractGrayscaleDistribution(bmMask, nMean, nStdDev); + + // Now scale tile down to desired size, using mask as input + + Bitmap bmScaled = TemplateTools.ScaleTemplateBitmap(bmTile, bmMask, cx, cx, nAreaBackgroundThreshold, nLuminanceMultBackground, nSaturationMultBackground, nLuminanceMultForeground, nSaturationMultForeground); + + // Grab the data + + for (int y = 0; y < cx; y++) { + for (int x = 0; x < cx; x++) { + alsColors.Add(bmScaled.GetPixel(x, y)); + } + } + bmScaled.Dispose(); + bmMask.Dispose(); + } + } else { + // No background template; just scale + + Bitmap bmTile = new Bitmap(sizTile.Width, sizTile.Height); + foreach (TileData td in m_alsTileData) { + // Need to turn data back into a bitmap - doh! + + for (int y = 0; y < sizTile.Height; y++) { + for (int x = 0; x < sizTile.Width; x++) { + Color clr = (Color)td.aclr[y * sizTile.Width + x]; + bmTile.SetPixel(x, y, clr); + } + } + + // Now scale tile down to desired size, using mask as input + + Bitmap bmScaled = TemplateTools.ScaleTemplateBitmap(bmTile, null, cx, cx, 1.0, 1.0, 1.0, 1.0, 1.0); + + // Grab the data + + for (int y = 0; y < cx; y++) { + for (int x = 0; x < cx; x++) { + alsColors.Add(bmScaled.GetPixel(x, y)); + } + } + bmScaled.Dispose(); + } + } + + // Palette match and write results + + foreach (Color clr in alsColors) + bwtr.Write((byte)pal.FindClosestEntry(clr)); + } + + byte[] GetTileBytes(int nTile) { + byte[] ab = new byte[m_sizTile.Width * m_sizTile.Height]; + TileData tdata = (TileData)m_alsTileData[nTile]; + for (int n = 0; n < tdata.aclr.Length; n++) + ab[n] = (byte)m_pal.FindClosestEntry(tdata.aclr[n]); + return ab; + } + + public int Count { + get { + return m_alsTileData.Count; + } + } + + public TileData GetTileData(int iTile) { + return (TileData)m_alsTileData[iTile]; + } + } +} diff --git a/m/PickListForm.cs b/m/PickListForm.cs new file mode 100644 index 0000000..a918cd2 --- /dev/null +++ b/m/PickListForm.cs @@ -0,0 +1,151 @@ +using System; +using System.Drawing; +using System.Collections; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Windows.Forms; + +namespace m +{ + /// + /// Summary description for PickListForm. + /// + public class PickListForm : System.Windows.Forms.Form + { + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Button buttonOk; + private System.Windows.Forms.Button buttonCancel; + private System.Windows.Forms.ListBox listBox1; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + public PickListForm(string strTitle, string strCurrent, StringCollection strc) + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + // + // TODO: Add any constructor code after InitializeComponent call + // + listBox1.DataSource = strc; + listBox1.SelectedIndex = strCurrent == "" ? -1 : strc.IndexOf(strCurrent); + Text = strTitle; + label1.Text = label1.Text.Replace("$1", strTitle); + } + + public int GetIndex() { + return listBox1.SelectedIndex; + } + + static public int DoModal(string strTitle, string strCurrent, StringCollection strc) { + PickListForm frm = new PickListForm(strTitle, strCurrent, strc); + DialogResult res = frm.ShowDialog(); + if (res == DialogResult.Cancel) + return -1; + return frm.GetIndex(); + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.label1 = new System.Windows.Forms.Label(); + this.buttonOk = new System.Windows.Forms.Button(); + this.buttonCancel = new System.Windows.Forms.Button(); + this.listBox1 = new System.Windows.Forms.ListBox(); + this.SuspendLayout(); + // + // label1 + // + this.label1.Location = new System.Drawing.Point(8, 8); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(200, 16); + this.label1.TabIndex = 0; + this.label1.Text = "Choose one $1:"; + // + // buttonOk + // + this.buttonOk.DialogResult = System.Windows.Forms.DialogResult.OK; + this.buttonOk.Location = new System.Drawing.Point(32, 344); + this.buttonOk.Name = "buttonOk"; + this.buttonOk.TabIndex = 2; + this.buttonOk.Text = "Ok"; + this.buttonOk.Click += new System.EventHandler(this.buttonOk_Click); + // + // buttonCancel + // + this.buttonCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.buttonCancel.Location = new System.Drawing.Point(144, 344); + this.buttonCancel.Name = "buttonCancel"; + this.buttonCancel.TabIndex = 2; + this.buttonCancel.Text = "Cancel"; + this.buttonCancel.Click += new System.EventHandler(this.buttonCancel_Click); + // + // listBox1 + // + this.listBox1.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0))); + this.listBox1.ItemHeight = 16; + this.listBox1.Location = new System.Drawing.Point(8, 24); + this.listBox1.Name = "listBox1"; + this.listBox1.Size = new System.Drawing.Size(232, 308); + this.listBox1.TabIndex = 3; + this.listBox1.DoubleClick += new System.EventHandler(this.listBox1_DoubleClick); + // + // PickListForm + // + this.AcceptButton = this.buttonOk; + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.CancelButton = this.buttonCancel; + this.ClientSize = new System.Drawing.Size(250, 376); + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.listBox1, + this.buttonOk, + this.label1, + this.buttonCancel}); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "PickListForm"; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "PickListForm"; + this.ResumeLayout(false); + + } + #endregion + + private void buttonOk_Click(object sender, System.EventArgs e) { + DialogResult = DialogResult.OK; + } + + private void buttonCancel_Click(object sender, System.EventArgs e) { + DialogResult = DialogResult.Cancel; + } + + private void listBox1_DoubleClick(object sender, System.EventArgs e) { + DialogResult = DialogResult.OK; + } + } +} diff --git a/m/PickListForm.resources b/m/PickListForm.resources new file mode 100644 index 0000000..8655156 Binary files /dev/null and b/m/PickListForm.resources differ diff --git a/m/PickListForm.resx b/m/PickListForm.resx new file mode 100644 index 0000000..a68bbf2 --- /dev/null +++ b/m/PickListForm.resx @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + PickListForm + + \ No newline at end of file diff --git a/m/Switch.cs b/m/Switch.cs new file mode 100644 index 0000000..6e37aa7 --- /dev/null +++ b/m/Switch.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections; +using SpiffLib; +using System.Runtime.Serialization; + +namespace m { + [Serializable] + public class SwitchManager { + ArrayList m_alsSwitches; + bool m_fModified; + + public SwitchManager() { + m_fModified = false; + m_alsSwitches = new ArrayList(); + } + + public ArrayList Items { + get { + return m_alsSwitches; + } + } + + public Switch this[string strName] { + get { + foreach (Switch sw in m_alsSwitches) { + if (sw.Name == strName) + return sw; + } + return null; + } + } + + void SetModified() { + m_fModified = true; + } + + public void ClearModified() { + m_fModified = false; + } + + public bool IsModified() { + return m_fModified; + } + + public Switch[] GetSwitchList() { + return (Switch[])m_alsSwitches.ToArray(typeof(Switch)); + } + + public void AddSwitch(Switch sw) { + m_alsSwitches.Add(sw); + SetModified(); + } + + public void RemoveSwitch(Switch sw) { + m_alsSwitches.Remove(sw); + SetModified(); + } + + public void ModifySwitch(Switch swModify, Switch sw) { + int n = m_alsSwitches.IndexOf(swModify); + if (n >= 0) + m_alsSwitches[n] = sw; + SetModified(); + } + } + + [Serializable] + public class Switch : ISerializable { + string m_strName; + + public Switch(string strName) { + m_strName = strName; + } + + // ISerializable methods for backwards compatibility + + private Switch(SerializationInfo info, StreamingContext context) { + m_strName = info.GetString("m_strName"); + } + + public void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { + info.AddValue("m_strName", m_strName); + } + + public virtual Switch Clone() { + Switch sw = new Switch(m_strName); + return sw; + } + + public string Name { + get { + return m_strName; + } + set { + m_strName = value; + } + } + } +} diff --git a/m/SwitchesForm.cs b/m/SwitchesForm.cs new file mode 100644 index 0000000..3f02d12 --- /dev/null +++ b/m/SwitchesForm.cs @@ -0,0 +1,228 @@ +using System; +using System.Drawing; +using System.Collections; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Windows.Forms; + +namespace m +{ + /// + /// Summary description for EditSwitchForm. + /// + public class SwitchesForm : System.Windows.Forms.Form + { + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Button buttonOk; + private System.Windows.Forms.Button buttonCancel; + private System.Windows.Forms.ListBox listBox1; + private System.Windows.Forms.Button buttonNew; + private System.Windows.Forms.Button buttonModify; + private System.Windows.Forms.Button buttonDelete; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + public SwitchesForm(string strTitle, string strCurrent, StringCollection strc) + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + // + + foreach (string str in strc) + listBox1.Items.Add(str); + listBox1.SelectedIndex = strCurrent == "" ? -1 : listBox1.Items.IndexOf(strCurrent); + Text = strTitle; + label1.Text = label1.Text.Replace("$1", strTitle); + } + + public Switch GetSelectedSwitch() { + if (listBox1.SelectedIndex == -1) + return null; + SwitchManager swm = ((LevelDoc)DocManager.GetActiveDocument(typeof(LevelDoc))).SwitchManager; + return swm[(string)listBox1.SelectedItem]; + } + + static public Switch DoModal(string strTitle, string strCurrent, StringCollection strc) { + SwitchesForm frm = new SwitchesForm(strTitle, strCurrent, strc); + DialogResult res = frm.ShowDialog(); + if (res == DialogResult.Cancel) + return null; + return frm.GetSelectedSwitch(); + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.label1 = new System.Windows.Forms.Label(); + this.buttonOk = new System.Windows.Forms.Button(); + this.buttonCancel = new System.Windows.Forms.Button(); + this.listBox1 = new System.Windows.Forms.ListBox(); + this.buttonNew = new System.Windows.Forms.Button(); + this.buttonModify = new System.Windows.Forms.Button(); + this.buttonDelete = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // label1 + // + this.label1.Location = new System.Drawing.Point(8, 8); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(160, 16); + this.label1.TabIndex = 0; + this.label1.Text = "Choose one $1:"; + // + // buttonOk + // + this.buttonOk.DialogResult = System.Windows.Forms.DialogResult.OK; + this.buttonOk.Location = new System.Drawing.Point(184, 8); + this.buttonOk.Name = "buttonOk"; + this.buttonOk.TabIndex = 2; + this.buttonOk.Text = "OK"; + this.buttonOk.Click += new System.EventHandler(this.buttonOk_Click); + // + // buttonCancel + // + this.buttonCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.buttonCancel.Location = new System.Drawing.Point(184, 40); + this.buttonCancel.Name = "buttonCancel"; + this.buttonCancel.TabIndex = 2; + this.buttonCancel.Text = "Cancel"; + this.buttonCancel.Click += new System.EventHandler(this.buttonCancel_Click); + // + // listBox1 + // + this.listBox1.Location = new System.Drawing.Point(8, 24); + this.listBox1.Name = "listBox1"; + this.listBox1.Size = new System.Drawing.Size(168, 173); + this.listBox1.Sorted = true; + this.listBox1.TabIndex = 3; + this.listBox1.DoubleClick += new System.EventHandler(this.listBox1_DoubleClick); + // + // buttonNew + // + this.buttonNew.Location = new System.Drawing.Point(184, 96); + this.buttonNew.Name = "buttonNew"; + this.buttonNew.TabIndex = 4; + this.buttonNew.Text = "New..."; + this.buttonNew.Click += new System.EventHandler(this.buttonNew_Click); + // + // buttonModify + // + this.buttonModify.Location = new System.Drawing.Point(184, 128); + this.buttonModify.Name = "buttonModify"; + this.buttonModify.TabIndex = 5; + this.buttonModify.Text = "Modify..."; + this.buttonModify.Click += new System.EventHandler(this.buttonModify_Click); + // + // buttonDelete + // + this.buttonDelete.Location = new System.Drawing.Point(184, 160); + this.buttonDelete.Name = "buttonDelete"; + this.buttonDelete.TabIndex = 6; + this.buttonDelete.Text = "Delete"; + this.buttonDelete.Click += new System.EventHandler(this.buttonDelete_Click); + // + // SwitchesForm + // + this.AcceptButton = this.buttonOk; + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.CancelButton = this.buttonCancel; + this.ClientSize = new System.Drawing.Size(266, 208); + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.buttonDelete, + this.buttonModify, + this.buttonNew, + this.listBox1, + this.buttonOk, + this.label1, + this.buttonCancel}); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "SwitchesForm"; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "EditSwitchForm"; + this.ResumeLayout(false); + + } + #endregion + + private void buttonOk_Click(object sender, System.EventArgs e) { + DialogResult = DialogResult.OK; + } + + private void buttonCancel_Click(object sender, System.EventArgs e) { + DialogResult = DialogResult.Cancel; + } + + private void listBox1_DoubleClick(object sender, System.EventArgs e) { + DialogResult = DialogResult.OK; + } + + private void buttonNew_Click(object sender, System.EventArgs e) { + string str = EditStringForm.DoModal("New Switch", "New switch name:", null); + if (str != null) { + SwitchManager swm = ((LevelDoc)DocManager.GetActiveDocument(typeof(LevelDoc))).SwitchManager; + if (swm[str] == null) { + swm.AddSwitch(new Switch(str)); + int i = listBox1.Items.Add(str); + listBox1.SelectedIndex = i; + // UNDONE: doc is modified + } + } + } + + private void buttonModify_Click(object sender, System.EventArgs e) { + string str = (string)listBox1.SelectedItem; + if (str == null) + return; + SwitchManager swm = ((LevelDoc)DocManager.GetActiveDocument(typeof(LevelDoc))).SwitchManager; + Switch sw = swm[str]; + string strNew = EditStringForm.DoModal("Modify Switch", "New switch name:", str); + if (strNew == null) + return; + if (strNew != str) { + sw.Name = strNew; + listBox1.Items.Remove(str); + int i = listBox1.Items.Add(strNew); + listBox1.SelectedIndex = i; + // UNDONE: doc is modified + } + } + + private void buttonDelete_Click(object sender, System.EventArgs e) { + string str = (string)listBox1.SelectedItem; + if (str == null) + return; + SwitchManager swm = ((LevelDoc)DocManager.GetActiveDocument(typeof(LevelDoc))).SwitchManager; + Switch sw = swm[str]; + swm.RemoveSwitch(sw); + listBox1.Items.Remove(str); + // UNDONE: doc is modified + } + } +} diff --git a/m/SwitchesForm.resources b/m/SwitchesForm.resources new file mode 100644 index 0000000..d53c892 Binary files /dev/null and b/m/SwitchesForm.resources differ diff --git a/m/SwitchesForm.resx b/m/SwitchesForm.resx new file mode 100644 index 0000000..0af6a7e --- /dev/null +++ b/m/SwitchesForm.resx @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + SwitchesForm + + \ No newline at end of file diff --git a/m/TemplateDoc.cs b/m/TemplateDoc.cs new file mode 100644 index 0000000..d3d0e98 --- /dev/null +++ b/m/TemplateDoc.cs @@ -0,0 +1,457 @@ +using System; +using System.Runtime.Serialization; +using System.Collections; +using System.Drawing; +using System.Drawing.Imaging; +using System.Windows.Forms; +using System.IO; +using SpiffLib; + +namespace m +{ + [Serializable] + public class TemplateDoc : Document, ISerializable, IDeserializationCallback { + Template m_tmplBackground = null; + int m_cookie = 5000; + ArrayList m_alsTemplates = new ArrayList(); + string m_strNameBackground = null; + Size m_sizTile; + Palette m_pal; + + public delegate void BackgroundChangedHandler(TemplateDoc tmpd); + public event BackgroundChangedHandler BackgroundChanged; + public delegate void NameChangedHandler(Document doc); + public event NameChangedHandler NameChanged; + + public TemplateDoc(DocTemplate doct, string strFile, Object[] aobj) : base(doct, strFile) { + if (aobj != null) { + m_sizTile = (Size)aobj[0]; + } else { + m_sizTile = AskTileSize(); + } + m_doct = (TemplateDocTemplate)doct; + InitCommon(); + } + + public TemplateDoc(SerializationInfo info, StreamingContext ctx) : base((DocTemplate)(((Hashtable)ctx.Context)["DocTemplate"]), (string)(((Hashtable)ctx.Context)["Filename"])) { + m_cookie = info.GetInt32("Cookie"); + + // Backwards compat + + try { + m_strNameBackground = info.GetInt32("CookieBackground").ToString(); + } catch { + m_strNameBackground = "0"; + try { + m_strNameBackground = info.GetString("NameBackground"); + } catch { + } + } + + // Get tile size. If none, default 16,16 + + try { + m_sizTile = (Size)info.GetValue("TileSize", typeof(Size)); + } catch { + m_sizTile = new Size(16, 16); + } + + // Get palette + + try { + m_pal = (Palette)info.GetValue("Palette", typeof(Palette)); + } catch { + m_pal = null; + } + + m_alsTemplates = (ArrayList)info.GetValue("TileTemplates", typeof(ArrayList)); + } + + public void OnDeserialization(object obSender) { + foreach (Template tmpl in m_alsTemplates) + tmpl.Doc = this; + SetModified(false); + InitCommon(); + } + + void InitCommon() { + TemplateDocTemplate doct = (TemplateDocTemplate)m_doct; + doct.TemplateChanged += new TemplateDocTemplate.TemplateChangedHandler(TemplateDocTemplate_TemplateChanged); + } + + public void GetObjectData(SerializationInfo info, StreamingContext context) { + info.AddValue("Cookie", m_cookie); + info.AddValue("TileTemplates", m_alsTemplates); + string strNameBackground = ""; + if (m_tmplBackground != null) + strNameBackground = m_tmplBackground.Name; + info.AddValue("NameBackground", strNameBackground); + info.AddValue("TileSize", m_sizTile); + if (m_pal != null) + info.AddValue("Palette", m_pal); + } + + Size AskTileSize() { + TileSizeForm form = new TileSizeForm(); + form.ShowDialog(DocManager.GetFrameParent()); + return form.GetTileSize(); + } + + public override void SetPath(string strFile) { + string strDir = m_strDir; + string strFileName = m_strFileName; + base.SetPath(strFile); + if (strDir != m_strDir || strFileName != m_strFileName) { + if (NameChanged != null) + NameChanged(this); + } + } + + public override string GetName() { + if (m_strFileName == null) + return base.GetName(); + return Path.ChangeExtension(m_strFileName, null); + } + + public void AddTemplates(string[] astrFileBitmap) { + ArrayList alsNamesAdded = new ArrayList(); + foreach (string strFileBitmap in astrFileBitmap) { + Template tmpl = new Template(this, "tmpl" + m_cookie); + m_cookie++; + if (tmpl.Import(strFileBitmap)) { + alsNamesAdded.Add(tmpl.Name); + m_alsTemplates.Add(tmpl); + } + } + if (alsNamesAdded.Count != 0) { + TemplateDocTemplate doct = (TemplateDocTemplate)m_doct; + doct.OnTemplatesAdded(this, (string[])alsNamesAdded.ToArray(typeof(string))); + } + SetModified(true); + } + + public void AddTemplates(Template[] atmpl) { + if (atmpl.Length == 0) + return; + ArrayList alsNamesAdded = new ArrayList(); + foreach (Template tmpl in atmpl) { + m_alsTemplates.Add(tmpl); + alsNamesAdded.Add(tmpl.Name); + } + TemplateDocTemplate doct = (TemplateDocTemplate)m_doct; + doct.OnTemplatesAdded(this, (string[])alsNamesAdded.ToArray(typeof(string))); + SetModified(true); + } + + public void RemoveTemplates(Template[] atmpl) { + if (atmpl.Length == 0) + return; + ArrayList alsNames = new ArrayList(); + foreach (Template tmpl in atmpl) { + m_alsTemplates.Remove(tmpl); + alsNames.Add(tmpl.Name); + if (tmpl == m_tmplBackground) + SetBackgroundTemplate(null); + } + TemplateDocTemplate doct = (TemplateDocTemplate)m_doct; + doct.OnTemplatesRemoved(this, (string[])alsNames.ToArray(typeof(string))); + SetModified(true); + } + + public Template[] GetTemplates() { + return (Template[])m_alsTemplates.ToArray(typeof(Template)); + } + + void TemplateDocTemplate_TemplateChanged(TemplateDoc tmpd, string strProperty, string strName, string strParam) { + if (tmpd == this) + SetModified(true); + } + + public Template FindTemplate(string strName) { + foreach (Template tmpl in m_alsTemplates) { + if (tmpl.Name != null && tmpl.Name == strName) + return tmpl; + } + return null; + } + + public void Dispose() { + RemoveTemplates((Template[])m_alsTemplates.ToArray(typeof(Template))); + } + + public Template GetBackgroundTemplate() { + if (m_tmplBackground == null && m_strNameBackground != null) + m_tmplBackground = FindTemplate(m_strNameBackground); + return m_tmplBackground; + } + + public void SetBackgroundTemplate(Template tmpl) { + if (m_tmplBackground == tmpl) + return; + m_tmplBackground = tmpl; + if (BackgroundChanged != null) + BackgroundChanged(this); + SetModified(true); + } + + public void SetPalette(Palette pal, bool fColorMatch) { + m_pal = pal; + SetModified(true); + if (!fColorMatch) + return; + ArrayList alsColors = new ArrayList(); + foreach (Template tmpl in m_alsTemplates) { + Bitmap bm = tmpl.Bitmap; + bool[,] afOccupancy = tmpl.OccupancyMap; + int ctx = afOccupancy.GetLength(1); + int cty = afOccupancy.GetLength(0); + for (int ty = 0; ty < cty; ty++) { + for (int tx = 0; tx < ctx; tx++) { + if (!afOccupancy[ty, tx]) + continue; + int xOrigin = tx * m_sizTile.Width; + int yOrigin = ty * m_sizTile.Height; + for (int y = yOrigin; y < yOrigin + m_sizTile.Height; y++) { + for (int x = xOrigin; x < xOrigin + m_sizTile.Width; x++) { + Color clrOld = bm.GetPixel(x, y); + Color clrNew = pal[pal.FindClosestEntry(clrOld)]; + bm.SetPixel(x, y, clrNew); + } + } + } + } + TemplateDocTemplate doct = (TemplateDocTemplate)m_doct; + doct.OnTemplateChanged(this, "Bitmap", tmpl.Name, null); + } + } + + public Palette GetPalette() { + return m_pal; + } + + public bool IsNameUnique(string strName) { + foreach (Template tmpl in m_alsTemplates) { + if (tmpl.Name == strName) + return false; + } + return true; + } + + public Size TileSize { + get { + return m_sizTile; + } + set { + // This is only done in special circumstances that don't require notification + + m_sizTile = value; + } + } + } + + public enum TerrainTypes { Start = 0, Open = 0, Blocked, Area, Wall, End }; + public enum TerrainColors { Grass, Cliff, Water, Road }; + + [Serializable] + public class Template : ISerializable { + public string ImportPath = null; + private string m_strName = null; + public bool[,] OccupancyMap = null; + public TerrainTypes[,] TerrainMap = null; + public TerrainColors[,] TerrainColors = null; + public Bitmap Bitmap = null; + public TemplateDoc Doc = null; + TemplateDocTemplate m_doct = (TemplateDocTemplate)DocManager.FindDocTemplate(typeof(TemplateDoc)); + + public Template(TemplateDoc doc, string strName) { + Doc = doc; + m_strName = strName; + } + + public Template(TemplateDoc doc, Bitmap bm, string strName) { + m_strName = strName; + Doc = doc; + if (!SetBitmap(bm)) + throw new Exception("Invalid tile template"); + } + + public Template(SerializationInfo info, StreamingContext ctx) { + // Backwards compat + try { + m_strName = info.GetInt32("Cookie").ToString(); + } catch { + m_strName = info.GetString("Name"); + } + try { + TerrainColors = (TerrainColors[,])info.GetValue("TerrainColors", typeof(TerrainColors[,])); + } catch { + TerrainColors = null; + } + OccupancyMap = (bool[,])info.GetValue("OccupancyMap", typeof(bool[,])); + TerrainMap = (TerrainTypes[,])info.GetValue("TerrainMap", typeof(TerrainTypes[,])); + Bitmap = (Bitmap)info.GetValue("Bitmap", typeof(Bitmap)); + } + + public void GetObjectData(SerializationInfo info, StreamingContext context) { + info.AddValue("Name", m_strName); + info.AddValue("OccupancyMap", OccupancyMap); + info.AddValue("TerrainMap", TerrainMap); + info.AddValue("Bitmap", Bitmap); + info.AddValue("TerrainColors", TerrainColors); + } + + public bool Import(string strFileBitmap) { + try { + Bitmap bm = new Bitmap(strFileBitmap); + if (SetBitmap(bm)) { + ImportPath = strFileBitmap; + return true; + } + return false; + } catch { + MessageBox.Show("Error opening tile bitmap"); + return false; + } + } + + private bool SetBitmap(Bitmap bm) { + if ((bm.Width % Doc.TileSize.Width) != 0 && (bm.Height % Doc.TileSize.Height) != 0) { + MessageBox.Show("Tile dimensions not a multiple of tile size"); + return false; + } + bm.MakeTransparent(Color.FromArgb(255, 0, 255)); + Bitmap = bm; + UpdateMaps(); + m_doct.OnTemplateChanged(Doc, "Bitmap", m_strName, null); + return true; + } + + // Update the map sizes to reflect the new bitmap + + private void UpdateMaps() { + int ctx = Bitmap.Width / Doc.TileSize.Width; + int cty = Bitmap.Height / Doc.TileSize.Height; + + // Try to copy what we can of the old terrain map + TerrainTypes[,] ater = new TerrainTypes[cty, ctx]; + if (TerrainMap != null) { + int ctyOld = TerrainMap.GetLength(0); + int ctxOld = TerrainMap.GetLength(1); + for (int tx = 0; tx < ctx && tx < ctxOld; tx++) { + for (int ty = 0; ty < cty && ty < ctyOld; ty++) { + ater[ty, tx] = TerrainMap[ty, tx]; + } + } + } + TerrainMap = ater; + + // Create a new occupancy map. Old one not relevant. + bool[,] afOcc = new bool[cty, ctx]; + for (int tx = 0; tx < ctx; tx++) { + for (int ty = 0; ty < cty; ty++) { + afOcc[ty, tx] = true; + Color clr = Bitmap.GetPixel(tx * Doc.TileSize.Width, ty * Doc.TileSize.Height); + if (clr.A == 0) + afOcc[ty, tx] = false; + } + } + OccupancyMap = afOcc; + } + + // Properties + + public string Name { + get { + return m_strName; + } + + set { + // Only if unique + + if (value == m_strName) + return; + if (!Doc.IsNameUnique(value)) { + MessageBox.Show("The name " + value + " is not unique. Not assigned."); + return; + } + string strNameOld = m_strName; + m_strName = value; + m_doct.OnTemplateChanged(Doc, "Name", strNameOld, m_strName); + } + } + + public int Ctx { + get { + return Bitmap.Width / Doc.TileSize.Width; + } + } + + public int Cty { + get { + return Bitmap.Height / Doc.TileSize.Height; + } + } + } + + // A doc template for (tile) templates. + + public class TemplateDocTemplate : DocTemplate { + static string[] astr = { "Template Collection", "Untitled", "Templates", "tc" }; + + public delegate void TemplatesAddedHandler(TemplateDoc tmpd, string[] astrName); + public event TemplatesAddedHandler TemplatesAdded; + public delegate void TemplatesRemovedHandler(TemplateDoc tmpd, string[] astrName); + public event TemplatesRemovedHandler TemplatesRemoved; + public delegate void TemplateChangedHandler(TemplateDoc tmpd, string strProperty, string strName, string strParam); + public event TemplateChangedHandler TemplateChanged; + + public TemplateDocTemplate() : base(astr, typeof(TemplateDoc), null, null, new TemplateDocBinder()) { + } + + public override Document OpenDocument(string strFile) { + // Don't support loading the same templates twice + + string strPathLower = null; + if (strFile != null) + strPathLower = Path.GetFullPath(strFile).ToLower(); + foreach (Document doc in m_alsDocuments) { + string strPath = doc.GetPath(); + if (strPath == null) + continue; + if (strPath.ToLower() == strPathLower) { + SetActiveDocument(doc); + return doc; + } + } + + return base.OpenDocument(strFile); + } + + public void OnTemplatesAdded(TemplateDoc tmpd, string[] astrName) { + if (TemplatesAdded != null) + TemplatesAdded(tmpd, astrName); + } + + public void OnTemplateChanged(TemplateDoc tmpd, string strProperty, string strName, string strParam) { + if (TemplateChanged != null) + TemplateChanged(tmpd, strProperty, strName, strParam); + } + + public void OnTemplatesRemoved(TemplateDoc tmpd, string[] astrName) { + if (TemplatesRemoved != null) + TemplatesRemoved(tmpd, astrName); + } + } + + // Compatibility goo + + public class TemplateDocBinder : SerializationBinder { + public override Type BindToType(string strAssembly, string strType) { + if (strType == "m.TileTemplateCollection") + return typeof(TemplateDoc); + if (strType == "m.TileTemplate") + return typeof(Template); + return Type.GetType(strType); + } + } +} diff --git a/m/TemplatePanel.cs b/m/TemplatePanel.cs new file mode 100644 index 0000000..00af43a --- /dev/null +++ b/m/TemplatePanel.cs @@ -0,0 +1,720 @@ +using System; +using System.IO; +using System.Collections; +using System.ComponentModel; +using System.Drawing; +using System.Drawing.Imaging; +using System.Data; +using System.Windows.Forms; +using SpiffLib; + +namespace m +{ + /// + /// Summary description for TemplatePanel. + /// + public class TemplatePanel : System.Windows.Forms.UserControl + { + private System.Windows.Forms.ComboBox comboDocs; + private m.FlowPanel flowPanel; + private System.Windows.Forms.ToolBar toolBar; + protected internal System.Windows.Forms.ImageList imageList1; + private System.Windows.Forms.ToolBarButton toolBarButtonNew; + private System.Windows.Forms.ToolBarButton toolBarButtonOpen; + private System.Windows.Forms.ToolBarButton toolBarButtonSave; + private System.Windows.Forms.ContextMenu contextMenuTiles; + private System.Windows.Forms.MenuItem menuItemDeleteTile; + private System.Windows.Forms.MenuItem menuItemTileBackground; + private System.ComponentModel.IContainer components; + private System.Windows.Forms.ToolBarButton toolBarButtonClose; + private System.Windows.Forms.ContextMenu contextMenuToolbar; + private System.Windows.Forms.MenuItem menuItemAddTemplates; + private System.Windows.Forms.MenuItem menuItemEditTerrain; + private System.Windows.Forms.ToolBarButton toolBarButtonMisc; + private System.Windows.Forms.ToolBarButton toolBarButtonSeparator; + private System.Windows.Forms.ContextMenu contextMenuSave; + private System.Windows.Forms.MenuItem menuItemSaveAs; + private System.Windows.Forms.MenuItem menuItemSaveAll; + private System.Windows.Forms.MenuItem menuItemSave; + private System.Windows.Forms.MenuItem menuItemProperties; + private System.Windows.Forms.MenuItem menuItem2; + private System.Windows.Forms.MenuItem menuItemTemplProperties; + private System.Windows.Forms.MenuItem menuItemImportBitmap; + private System.Windows.Forms.MenuItem menuItemExportBitmap; + private System.Windows.Forms.MenuItem menuItemScaleDown; + private System.Windows.Forms.MenuItem menuItemSavePalette; + private System.Windows.Forms.MenuItem menuItemQuantizeOnly; + private System.Windows.Forms.MenuItem menuItem1; + private System.Windows.Forms.ToolTip toolTip; + TemplateDoc m_tmpdActive = null; + + public TemplatePanel() + { + // This call is required by the Windows.Forms Form Designer. + InitializeComponent(); + + if (Globals.IsKit()) { + Controls.Remove(toolBar); + toolBar.Visible = false; + } + + // Easier than creating a resource file? + + System.Reflection.Assembly ass = typeof(TemplatePanel).Module.Assembly; + Stream stm = ass.GetManifestResourceStream("m.toolstrip.bmp"); + imageList1.Images.AddStrip(new Bitmap(stm)); + + // We want to know about changes to template docs + + TemplateDocTemplate doctTemplate = (TemplateDocTemplate)DocManager.FindDocTemplate(typeof(TemplateDoc)); + if (doctTemplate != null) { + doctTemplate.DocActive += new TemplateDocTemplate.DocActiveHandler(TemplateDocTemplate_DocActive); + doctTemplate.DocAdded += new TemplateDocTemplate.DocAddedHandler(TemplateDocTemplate_DocAdded); + doctTemplate.DocRemoved += new TemplateDocTemplate.DocRemovedHandler(TemplateDocTemplate_DocRemoved); + doctTemplate.TemplatesAdded += new TemplateDocTemplate.TemplatesAddedHandler(TemplateDocTemplate_TemplatesAdded); + doctTemplate.TemplatesRemoved += new TemplateDocTemplate.TemplatesRemovedHandler(TemplateDocTemplate_TemplatesRemoved); + doctTemplate.TemplateChanged += new TemplateDocTemplate.TemplateChangedHandler(TemplateDocTemplate_TemplateChanged); + } + + // We want to know when the active level doc changes + + LevelDocTemplate doctLevel = (LevelDocTemplate)DocManager.FindDocTemplate(typeof(LevelDoc)); + if (doctLevel != null) + doctLevel.DocActive += new TemplateDocTemplate.DocActiveHandler(LevelDocTemplate_DocActive); + } + + void LevelDocTemplate_DocActive(Document doc) { + LevelDoc lvld = (LevelDoc)doc; + if (lvld != null) + SetActiveDoc(lvld.GetTemplateDoc()); + } + + void TemplateDocTemplate_DocActive(Document doc) { + SetActiveDoc((TemplateDoc)doc); + } + + void SetActiveDoc(TemplateDoc tmpd) { + if (tmpd == m_tmpdActive) + return; + if (tmpd == null) { + flowPanel.Controls.Clear(); + } else { + ComboItem ciFound = null; + for (int nIndex = 0; nIndex < comboDocs.Items.Count; nIndex++) { + ComboItem ci = (ComboItem)comboDocs.Items[nIndex]; + if (ci.m_tmpd == tmpd) { + ciFound = ci; + comboDocs.SelectedIndex = nIndex; + break; + } + } + if (ciFound != null) + FillPanel(ciFound.m_alsPictureBoxes); + } + m_tmpdActive = tmpd; + } + + void FillPanel(ArrayList alsPictureBoxes) { + flowPanel.SuspendLayout(); + flowPanel.Controls.Clear(); + flowPanel.Controls.AddRange((Control[])alsPictureBoxes.ToArray(typeof(Control))); + flowPanel.ResumeLayout(); + flowPanel.RefreshScrollbar(); + } + + PictureBox CreatePictureBox(TemplateDoc tmpd, Size sizTile, IMapItem mi) { + PictureBox picb = new PictureBox(); + picb.Image = Misc.TraceEdges(mi.GetBitmap(sizTile, tmpd), 1, Color.Azure); + picb.SizeMode = PictureBoxSizeMode.AutoSize; + picb.Tag = (Object)mi; + picb.MouseDown += new MouseEventHandler(PictureBox_MouseDown); + return picb; + } + + public class ComboItem { + public TemplateDoc m_tmpd; + public ArrayList m_alsPictureBoxes; + public ComboItem(TemplateDoc tmpd) { + m_tmpd = tmpd; + m_alsPictureBoxes = new ArrayList(); + } + public override string ToString() { + return m_tmpd.GetName(); + } + } + + void TemplateDocTemplate_DocAdded(Document doc) { + TemplateDoc tmpd = (TemplateDoc)doc; + tmpd.ModifiedChanged += new TemplateDoc.ModifiedChangedHandler(TemplateDoc_ModifiedChanged); + tmpd.NameChanged += new TemplateDoc.NameChangedHandler(TemplateDoc_NameChanged); + ComboItem ci = new ComboItem(tmpd); + comboDocs.SelectedIndex = comboDocs.Items.Add(ci); + Template[] atmpl = tmpd.GetTemplates(); + ArrayList alsNames = new ArrayList(); + foreach (Template tmpl in atmpl) + alsNames.Add(tmpl.Name); + toolTip.RemoveAll(); + TemplateDocTemplate_TemplatesAdded(tmpd, (string[])alsNames.ToArray(typeof(string))); + } + + void TemplateDocTemplate_DocRemoved(Document doc) { + TemplateDoc tmpd = (TemplateDoc)doc; + tmpd.ModifiedChanged -= new TemplateDoc.ModifiedChangedHandler(TemplateDoc_ModifiedChanged); + tmpd.NameChanged -= new TemplateDoc.NameChangedHandler(TemplateDoc_NameChanged); + ComboItem ci = FindComboItem((TemplateDoc)doc); + if (ci != null) + comboDocs.Items.Remove(ci); + toolTip.RemoveAll(); + } + + int FindComboIndex(TemplateDoc tmpd) { + for (int nIndex = 0; nIndex < comboDocs.Items.Count; nIndex++) { + ComboItem ci = (ComboItem)comboDocs.Items[nIndex]; + if (ci.m_tmpd == tmpd) + return nIndex; + } + return -1; + } + + ComboItem FindComboItem(TemplateDoc tmpd) { + int nIndex = FindComboIndex(tmpd); + if (nIndex == -1) + return null; + return (ComboItem)comboDocs.Items[nIndex]; + } + + void TemplateDocTemplate_TemplatesAdded(TemplateDoc tmpd, string[] astrNames) { + ComboItem ci = FindComboItem(tmpd); + foreach (string strName in astrNames) { + PictureBox picb = CreatePictureBox(tmpd, tmpd.TileSize, new Tile(tmpd, strName, 0, 0)); + ci.m_alsPictureBoxes.Add(picb); + toolTip.SetToolTip(picb, strName); + } + if (tmpd == m_tmpdActive) + FillPanel(ci.m_alsPictureBoxes); + } + + void TemplateDocTemplate_TemplatesRemoved(TemplateDoc tmpd, string[] astrNames) { + ComboItem ci = FindComboItem(tmpd); + foreach (string strName in astrNames) { + foreach (PictureBox picb in ci.m_alsPictureBoxes) { + Tile tile = (Tile)picb.Tag; + if (strName == tile.Name) { + ci.m_alsPictureBoxes.Remove(picb); + break; + } + } + } + if (tmpd == m_tmpdActive) + FillPanel(ci.m_alsPictureBoxes); + } + + void TemplateDocTemplate_TemplateChanged(TemplateDoc tmpd, string strProperty, string strName, string strParam) { + ComboItem ci = FindComboItem(tmpd); + foreach (PictureBox picb in ci.m_alsPictureBoxes) { + Tile tile = (Tile)picb.Tag; + if (tile.Name == strName) { + Template tmpl = tmpd.FindTemplate(strProperty == "Name" ? strParam : strName); + picb.Image = Misc.TraceEdges(tmpl.Bitmap, 1, Color.Azure); + break; + } + } + if (tmpd == m_tmpdActive) + FillPanel(ci.m_alsPictureBoxes); + } + + void TemplateDoc_ModifiedChanged(Document doc, bool fModified) { + comboDocs.Invalidate(); + } + + void TemplateDoc_NameChanged(Document doc) { + comboDocs.Invalidate(); + } + + private void comboDocs_DrawItem(object sender, System.Windows.Forms.DrawItemEventArgs e) { + if (e.Index < 0 || e.Index >= comboDocs.Items.Count) + return; + ComboItem ci = (ComboItem)comboDocs.Items[e.Index]; + string strName = ci.ToString() + (ci.m_tmpd.IsModified() ? "*" : ""); + e.DrawBackground(); + e.Graphics.DrawString(strName, e.Font, new SolidBrush(e.ForeColor), e.Bounds.X, e.Bounds.Y); + e.DrawFocusRectangle(); + } + + private void comboDocs_SelectedIndexChanged(object sender, System.EventArgs e) { + ComboItem ci = (ComboItem)comboDocs.Items[comboDocs.SelectedIndex]; + DocManager.SetActiveDocument(typeof(TemplateDoc), ci.m_tmpd); + } + + private void PictureBox_MouseDown(Object sender, MouseEventArgs e) { + Control ctlSelected = (Control)sender; + + if (!Globals.IsKit()) { + if (e.Button == MouseButtons.Right) { + contextMenuTiles.Show(ctlSelected, new Point(e.X, e.Y)); + return; + } + } + + Tile tile = (Tile)((PictureBox)sender).Tag; + Globals.PropertyGrid.SelectedObject = tile.GetTemplate(m_tmpdActive); + + // Start drag drop + + LevelData ldat = new LevelData(); + IMapItem mi = (IMapItem)ctlSelected.Tag; + ldat.ami = new IMapItem[] { mi }; + Size sizTile = m_tmpdActive.TileSize; + ldat.txMouse = e.X / (double)sizTile.Width; + ldat.tyMouse = e.Y / (double)sizTile.Height; + ldat.Grid.Width = mi.Grid.Width; + ldat.Grid.Height = mi.Grid.Height; + DoDragDrop(ldat, DragDropEffects.Copy); + } + + private void AddTemplates() { + // Get tile filename(s) + + OpenFileDialog frmOpen = new OpenFileDialog(); + frmOpen.Multiselect = true; + frmOpen.Filter = "Image Files (*.*)|*.*"; + frmOpen.Title = "Add Templates"; + if (frmOpen.ShowDialog() == DialogResult.Cancel) + return; + + // Load them up. If there is no template doc yet, make a new one + + if (m_tmpdActive == null) + DocManager.NewDocument(typeof(TemplateDoc), null); + m_tmpdActive.AddTemplates(frmOpen.FileNames); + } + + private void toolBar_ButtonClick(object sender, System.Windows.Forms.ToolBarButtonClickEventArgs e) { + switch (toolBar.Buttons.IndexOf(e.Button)) { + case 0: + DocManager.NewDocument(typeof(TemplateDoc), null); + break; + + case 1: + DocManager.OpenDocument(typeof(TemplateDoc)); + break; + + case 2: + if (m_tmpdActive != null) + m_tmpdActive.Save(); + break; + + case 3: + AddTemplates(); + //Rectangle rc = toolBar.Buttons[toolBar.Buttons.IndexOf(e.Button)].Rectangle; + //contextMenuToolbar.Show(toolBar, new Point(rc.X, rc.Y + rc.Height)); + break; + + case 4: + // Separator + break; + + case 5: + if (m_tmpdActive == null) + return; + m_tmpdActive.Close(); + break; + } + } + + private void menuItemSave_Click(object sender, System.EventArgs e) { + if (m_tmpdActive != null) + m_tmpdActive.Save(); + } + + private void menuItemSaveAs_Click(object sender, System.EventArgs e) { + if (m_tmpdActive != null) + m_tmpdActive.SaveAs(null); + } + + private void menuItemSaveAll_Click(object sender, System.EventArgs e) { + DocManager.SaveAllModified(typeof(TemplateDoc)); + } + + private void menuItemAddTemplates_Click(object sender, System.EventArgs e) { + AddTemplates(); + } + + private void menuItemEditTerrain_Click(object sender, System.EventArgs e) { + if (m_tmpdActive == null) + return; + Form frmEditTerrain = new EditTerrainForm(m_tmpdActive); + frmEditTerrain.ShowDialog(); + } + + private IMapItem GetMapItem(Object sender) { + ContextMenu contextMenu = (ContextMenu)((MenuItem)sender).Parent; + return (IMapItem)((PictureBox)contextMenu.SourceControl).Tag; + } + + private void menuItemImportBitmap_Click(object sender, System.EventArgs e) { + Tile tile = GetMapItem(sender) as Tile; + if (tile == null) + return; + Template tmpl = tile.GetTemplate(m_tmpdActive); + OpenFileDialog frmOpen = new OpenFileDialog(); + frmOpen.FileName = tmpl.ImportPath; + if (frmOpen.ShowDialog() == DialogResult.Cancel) + return; + tmpl.Import(frmOpen.FileName); + } + + private void menuItemExportBitmap_Click(object sender, System.EventArgs e) { + Tile tile = GetMapItem(sender) as Tile; + if (tile == null) + return; + Template tmpl = tile.GetTemplate(m_tmpdActive); + SaveFileDialog frmSave = new SaveFileDialog(); + frmSave.DefaultExt = "png"; + frmSave.Filter = "Png Files (*.png)|*.png"; + frmSave.Title = "Save Template Bitmap As"; + if (tmpl.ImportPath != null) { + frmSave.FileName = tmpl.ImportPath; + } else { + frmSave.FileName = tmpl.Name; + } + if (frmSave.ShowDialog() == DialogResult.Cancel) + return; + tmpl.Bitmap.Save(frmSave.FileName, ImageFormat.Png); + } + + private void menuItemProperties_Click(object sender, System.EventArgs e) { + Globals.PropertyGrid.SelectedObject = m_tmpdActive; + } + + private void menuItemDeleteTile_Click(object sender, System.EventArgs e) { + Tile tile = GetMapItem(sender) as Tile; + if (tile == null) + return; + if (MessageBox.Show("Are you sure?", "Delete Tile", MessageBoxButtons.YesNo) == DialogResult.Yes) + m_tmpdActive.RemoveTemplates(new Template[] { tile.GetTemplate(m_tmpdActive) }); + } + + private void menuItemTileBackground_Click(object sender, System.EventArgs e) { + Tile tile = GetMapItem(sender) as Tile; + if (tile == null) + return; + m_tmpdActive.SetBackgroundTemplate(tile.GetTemplate(m_tmpdActive)); + } + + private void menuItemTemplProperties_Click(object sender, System.EventArgs e) { + Tile tile = GetMapItem(sender) as Tile; + if (tile == null) + return; + Globals.PropertyGrid.SelectedObject = tile.GetTemplate(m_tmpdActive); + } + + private void menuItemScaleDown_Click(object sender, System.EventArgs e) { + // If no template doc active, bail + + if (m_tmpdActive == null) + return; + + // Make sure 24 x 24 (could actually allow any sort of conversion...) + + if (m_tmpdActive.TileSize.Width != 24 && m_tmpdActive.TileSize.Height != 24) { + MessageBox.Show(DocManager.GetFrameParent(), "The current template collection must be 24 x 24 tile size"); + return; + } + + // Get busy + + TemplateDoc tmpdDst = TemplateTools.CloneTemplateDoc(m_tmpdActive); + TemplateTools.ScaleTemplates(tmpdDst, new Size(16, 16)); + TemplateTools.QuantizeTemplates(tmpdDst, null, 0, 0, 0); + DocManager.SetActiveDocument(typeof(TemplateDoc), tmpdDst); + } + + private void menuItemQuantizeOnly_Click(object sender, System.EventArgs e) { + if (m_tmpdActive == null) + return; + TemplateDoc tmpdDst = TemplateTools.CloneTemplateDoc(m_tmpdActive); + TemplateTools.QuantizeTemplates(tmpdDst, null, 0, 0, 0); + DocManager.SetActiveDocument(typeof(TemplateDoc), tmpdDst); + } + + private void menuItemSavePalette_Click(object sender, System.EventArgs e) { + if (m_tmpdActive == null) + return; + Palette pal = m_tmpdActive.GetPalette(); + if (pal == null) { + MessageBox.Show(DocManager.GetFrameParent(), "No palette on this template collection. You need to create one!"); + return; + } + pal.SaveDialog(); + } + + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Component Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.comboDocs = new System.Windows.Forms.ComboBox(); + this.flowPanel = new m.FlowPanel(); + this.toolBar = new System.Windows.Forms.ToolBar(); + this.toolBarButtonNew = new System.Windows.Forms.ToolBarButton(); + this.toolBarButtonOpen = new System.Windows.Forms.ToolBarButton(); + this.toolBarButtonSave = new System.Windows.Forms.ToolBarButton(); + this.contextMenuSave = new System.Windows.Forms.ContextMenu(); + this.menuItemSave = new System.Windows.Forms.MenuItem(); + this.menuItemSaveAs = new System.Windows.Forms.MenuItem(); + this.menuItemSaveAll = new System.Windows.Forms.MenuItem(); + this.toolBarButtonMisc = new System.Windows.Forms.ToolBarButton(); + this.contextMenuToolbar = new System.Windows.Forms.ContextMenu(); + this.menuItemAddTemplates = new System.Windows.Forms.MenuItem(); + this.menuItemEditTerrain = new System.Windows.Forms.MenuItem(); + this.menuItemScaleDown = new System.Windows.Forms.MenuItem(); + this.menuItemQuantizeOnly = new System.Windows.Forms.MenuItem(); + this.menuItemSavePalette = new System.Windows.Forms.MenuItem(); + this.menuItem1 = new System.Windows.Forms.MenuItem(); + this.menuItemProperties = new System.Windows.Forms.MenuItem(); + this.toolBarButtonSeparator = new System.Windows.Forms.ToolBarButton(); + this.toolBarButtonClose = new System.Windows.Forms.ToolBarButton(); + this.imageList1 = new System.Windows.Forms.ImageList(this.components); + this.contextMenuTiles = new System.Windows.Forms.ContextMenu(); + this.menuItemImportBitmap = new System.Windows.Forms.MenuItem(); + this.menuItemExportBitmap = new System.Windows.Forms.MenuItem(); + this.menuItemDeleteTile = new System.Windows.Forms.MenuItem(); + this.menuItemTileBackground = new System.Windows.Forms.MenuItem(); + this.menuItem2 = new System.Windows.Forms.MenuItem(); + this.menuItemTemplProperties = new System.Windows.Forms.MenuItem(); + this.toolTip = new System.Windows.Forms.ToolTip(this.components); + this.SuspendLayout(); + // + // comboDocs + // + this.comboDocs.Dock = System.Windows.Forms.DockStyle.Top; + this.comboDocs.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed; + this.comboDocs.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comboDocs.Location = new System.Drawing.Point(0, 24); + this.comboDocs.Name = "comboDocs"; + this.comboDocs.Size = new System.Drawing.Size(168, 21); + this.comboDocs.TabIndex = 0; + this.comboDocs.SelectedIndexChanged += new System.EventHandler(this.comboDocs_SelectedIndexChanged); + this.comboDocs.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.comboDocs_DrawItem); + // + // flowPanel + // + this.flowPanel.AutoScroll = true; + this.flowPanel.BackColor = System.Drawing.Color.Black; + this.flowPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.flowPanel.Location = new System.Drawing.Point(0, 45); + this.flowPanel.Name = "flowPanel"; + this.flowPanel.Size = new System.Drawing.Size(168, 403); + this.flowPanel.TabIndex = 1; + // + // toolBar + // + this.toolBar.Appearance = System.Windows.Forms.ToolBarAppearance.Flat; + this.toolBar.Buttons.AddRange(new System.Windows.Forms.ToolBarButton[] { + this.toolBarButtonNew, + this.toolBarButtonOpen, + this.toolBarButtonSave, + this.toolBarButtonMisc, + this.toolBarButtonSeparator, + this.toolBarButtonClose}); + this.toolBar.ButtonSize = new System.Drawing.Size(16, 16); + this.toolBar.DropDownArrows = true; + this.toolBar.ImageList = this.imageList1; + this.toolBar.Name = "toolBar"; + this.toolBar.ShowToolTips = true; + this.toolBar.Size = new System.Drawing.Size(168, 24); + this.toolBar.TabIndex = 2; + this.toolBar.ButtonClick += new System.Windows.Forms.ToolBarButtonClickEventHandler(this.toolBar_ButtonClick); + // + // toolBarButtonNew + // + this.toolBarButtonNew.ImageIndex = 0; + this.toolBarButtonNew.ToolTipText = "New Template Collection"; + // + // toolBarButtonOpen + // + this.toolBarButtonOpen.ImageIndex = 1; + this.toolBarButtonOpen.ToolTipText = "Open Template Collection"; + // + // toolBarButtonSave + // + this.toolBarButtonSave.DropDownMenu = this.contextMenuSave; + this.toolBarButtonSave.ImageIndex = 2; + this.toolBarButtonSave.Style = System.Windows.Forms.ToolBarButtonStyle.DropDownButton; + this.toolBarButtonSave.ToolTipText = "Save Template Collection"; + // + // contextMenuSave + // + this.contextMenuSave.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.menuItemSave, + this.menuItemSaveAs, + this.menuItemSaveAll}); + // + // menuItemSave + // + this.menuItemSave.Index = 0; + this.menuItemSave.Text = "Save"; + this.menuItemSave.Click += new System.EventHandler(this.menuItemSave_Click); + // + // menuItemSaveAs + // + this.menuItemSaveAs.Index = 1; + this.menuItemSaveAs.Text = "Save As..."; + this.menuItemSaveAs.Click += new System.EventHandler(this.menuItemSaveAs_Click); + // + // menuItemSaveAll + // + this.menuItemSaveAll.Index = 2; + this.menuItemSaveAll.Text = "Save All"; + this.menuItemSaveAll.Click += new System.EventHandler(this.menuItemSaveAll_Click); + // + // toolBarButtonMisc + // + this.toolBarButtonMisc.DropDownMenu = this.contextMenuToolbar; + this.toolBarButtonMisc.ImageIndex = 4; + this.toolBarButtonMisc.Style = System.Windows.Forms.ToolBarButtonStyle.DropDownButton; + this.toolBarButtonMisc.ToolTipText = "Add Templates and other tools"; + // + // contextMenuToolbar + // + this.contextMenuToolbar.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.menuItemAddTemplates, + this.menuItemEditTerrain, + this.menuItemScaleDown, + this.menuItemQuantizeOnly, + this.menuItemSavePalette, + this.menuItem1, + this.menuItemProperties}); + // + // menuItemAddTemplates + // + this.menuItemAddTemplates.Index = 0; + this.menuItemAddTemplates.Text = "Add Templates..."; + this.menuItemAddTemplates.Click += new System.EventHandler(this.menuItemAddTemplates_Click); + // + // menuItemEditTerrain + // + this.menuItemEditTerrain.Index = 1; + this.menuItemEditTerrain.Text = "Edit Terrain..."; + this.menuItemEditTerrain.Click += new System.EventHandler(this.menuItemEditTerrain_Click); + // + // menuItemScaleDown + // + this.menuItemScaleDown.Index = 2; + this.menuItemScaleDown.Text = "Scale && Quantize..."; + this.menuItemScaleDown.Click += new System.EventHandler(this.menuItemScaleDown_Click); + // + // menuItemQuantizeOnly + // + this.menuItemQuantizeOnly.Index = 3; + this.menuItemQuantizeOnly.Text = "Quantize Only..."; + this.menuItemQuantizeOnly.Click += new System.EventHandler(this.menuItemQuantizeOnly_Click); + // + // menuItemSavePalette + // + this.menuItemSavePalette.Index = 4; + this.menuItemSavePalette.Text = "Save Palette..."; + this.menuItemSavePalette.Click += new System.EventHandler(this.menuItemSavePalette_Click); + // + // menuItem1 + // + this.menuItem1.Index = 5; + this.menuItem1.Text = "-"; + // + // menuItemProperties + // + this.menuItemProperties.Index = 6; + this.menuItemProperties.Text = "Properties"; + this.menuItemProperties.Click += new System.EventHandler(this.menuItemProperties_Click); + // + // toolBarButtonSeparator + // + this.toolBarButtonSeparator.Style = System.Windows.Forms.ToolBarButtonStyle.Separator; + // + // toolBarButtonClose + // + this.toolBarButtonClose.ImageIndex = 5; + this.toolBarButtonClose.ToolTipText = "Close Template Collection"; + // + // imageList1 + // + this.imageList1.ColorDepth = System.Windows.Forms.ColorDepth.Depth8Bit; + this.imageList1.ImageSize = new System.Drawing.Size(16, 15); + this.imageList1.TransparentColor = System.Drawing.Color.Magenta; + // + // contextMenuTiles + // + this.contextMenuTiles.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.menuItemImportBitmap, + this.menuItemExportBitmap, + this.menuItemDeleteTile, + this.menuItemTileBackground, + this.menuItem2, + this.menuItemTemplProperties}); + // + // menuItemImportBitmap + // + this.menuItemImportBitmap.Index = 0; + this.menuItemImportBitmap.Text = "Import Bitmap..."; + this.menuItemImportBitmap.Click += new System.EventHandler(this.menuItemImportBitmap_Click); + // + // menuItemExportBitmap + // + this.menuItemExportBitmap.Index = 1; + this.menuItemExportBitmap.Text = "Export Bitmap..."; + this.menuItemExportBitmap.Click += new System.EventHandler(this.menuItemExportBitmap_Click); + // + // menuItemDeleteTile + // + this.menuItemDeleteTile.Index = 2; + this.menuItemDeleteTile.Text = "Remove"; + this.menuItemDeleteTile.Click += new System.EventHandler(this.menuItemDeleteTile_Click); + // + // menuItemTileBackground + // + this.menuItemTileBackground.Index = 3; + this.menuItemTileBackground.Text = "Background"; + this.menuItemTileBackground.Click += new System.EventHandler(this.menuItemTileBackground_Click); + // + // menuItem2 + // + this.menuItem2.Index = 4; + this.menuItem2.Text = "-"; + // + // menuItemTemplProperties + // + this.menuItemTemplProperties.Index = 5; + this.menuItemTemplProperties.Text = "Properties"; + this.menuItemTemplProperties.Click += new System.EventHandler(this.menuItemTemplProperties_Click); + // + // TemplatePanel + // + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.flowPanel, + this.comboDocs, + this.toolBar}); + this.ForeColor = System.Drawing.SystemColors.Control; + this.Name = "TemplatePanel"; + this.Size = new System.Drawing.Size(168, 448); + this.ResumeLayout(false); + + } + #endregion + } +} diff --git a/m/TemplatePanel.resources b/m/TemplatePanel.resources new file mode 100644 index 0000000..c94c64f Binary files /dev/null and b/m/TemplatePanel.resources differ diff --git a/m/TemplatePanel.resx b/m/TemplatePanel.resx new file mode 100644 index 0000000..b3364dc --- /dev/null +++ b/m/TemplatePanel.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 275, 17 + + + 122, 17 + + + FamilyOrAssembly + + + 17, 17 + + + 411, 30 + + + 549, 30 + + + TemplatePanel + + \ No newline at end of file diff --git a/m/TemplateTools.cs b/m/TemplateTools.cs new file mode 100644 index 0000000..061ec64 --- /dev/null +++ b/m/TemplateTools.cs @@ -0,0 +1,671 @@ +using System; +using System.Collections; +using System.Drawing; +using System.Drawing.Imaging; +using System.Drawing.Drawing2D; +using System.Windows.Forms; +using SpiffLib; + +namespace m +{ + /// + /// Summary description for TemplateTools. + /// + public class TemplateTools + { + public TemplateTools() + { + // + // TODO: Add constructor logic here + // + } + + public static void ScaleTemplates(TemplateDoc tmpd, Size sizTile) { + // Scale templates + + Template[] atmpl = tmpd.GetTemplates(); + Template tmplBackground = tmpd.GetBackgroundTemplate(); + tmpd.RemoveTemplates(atmpl); + foreach (Template tmpl in atmpl) + ScaleTemplate(tmpl, tmpd.TileSize, sizTile); + tmpd.TileSize = sizTile; + tmpd.AddTemplates(atmpl); + tmpd.SetBackgroundTemplate(tmplBackground); + } + +#if false + static void ScaleTemplate(Template tmpl, Size sizTileSrc, Size sizTileDst) { + bool [,] afOccupancySrc = tmpl.OccupancyMap; + int ctx = afOccupancySrc.GetLength(1); + int cty = afOccupancySrc.GetLength(0); + Bitmap bmDst = new Bitmap(ctx * sizTileDst.Width, cty * sizTileDst.Height); + Graphics gDst = Graphics.FromImage(bmDst); + gDst.Clear(Color.FromArgb(255, 0, 255)); + for (int tx = 0; tx < ctx; tx++) { + for (int ty = 0; ty < cty; ty++) { + if (!afOccupancySrc[ty, tx]) + continue; + Rectangle rcSrc = new Rectangle(new Point(tx * sizTileSrc.Width, ty * sizTileSrc.Height), sizTileSrc); + Rectangle rcDst = new Rectangle(new Point(tx * sizTileDst.Width, ty * sizTileDst.Height), sizTileDst); + gDst.DrawImage(tmpl.Bitmap, rcDst, rcSrc, GraphicsUnit.Pixel); + } + } + gDst.Dispose(); + bmDst.MakeTransparent(Color.FromArgb(255, 0, 255)); + tmpl.Bitmap = bmDst; + } +#endif + + static void ScaleTemplate(Template tmpl, Size sizTileSrc, Size sizTileDst) { + bool [,] afOccupancySrc = tmpl.OccupancyMap; + int ctx = afOccupancySrc.GetLength(1); + int cty = afOccupancySrc.GetLength(0); + Bitmap bmDst = new Bitmap(ctx * sizTileDst.Width, cty * sizTileDst.Height); + Graphics gDst = Graphics.FromImage(bmDst); + gDst.Clear(Color.FromArgb(255, 0, 255)); + for (int tx = 0; tx < ctx; tx++) { + for (int ty = 0; ty < cty; ty++) { + if (!afOccupancySrc[ty, tx]) + continue; + Rectangle rcSrc = new Rectangle(new Point(tx * sizTileSrc.Width, ty * sizTileSrc.Height), sizTileSrc); + Rectangle rcDst = new Rectangle(new Point(tx * sizTileDst.Width, ty * sizTileDst.Height), sizTileDst); + ScaleTile(tmpl.Bitmap, rcSrc, bmDst, rcDst); + } + } + gDst.Dispose(); + MakeTransparent(bmDst); + + tmpl.Bitmap = bmDst; + } + + // HACK: Mono's MakeTransparent doesn't get the job done so we map the transparent color ourselves. + public static void MakeTransparent(Bitmap bm) { + bm.MakeTransparent(Color.FromArgb(255, 0, 255)); + Color clrTransparentMarker = Color.FromArgb(0, 255, 0, 255); + Color clrTransparent = Color.FromArgb(0, 0, 0, 0); + for (int y = 0; y < bm.Height; y++) { + for (int x = 0; x < bm.Width; x++) { + Color clr = bm.GetPixel(x, y); + if (clr == clrTransparentMarker) + bm.SetPixel(x, y, clrTransparent); + } + } + } + + static void ScaleTile(Bitmap bmSrc, Rectangle rcSrc, Bitmap bmDst, Rectangle rcDst) { + double nWidthRatio = (double)rcSrc.Width / (double)rcDst.Width; + double nHeightRatio = (double)rcSrc.Height / (double)rcDst.Height; + for (int yDst = rcDst.Top; yDst < rcDst.Bottom; yDst++) { + for (int xDst = rcDst.Left; xDst < rcDst.Right; xDst++) { + double xLeftSample = (double)xDst * nWidthRatio; + double yTopSample = (double)yDst * nHeightRatio; + double xRightSample = (double)(xDst + 1) * nWidthRatio; + double yBottomSample = (double)(yDst + 1) * nHeightRatio; + DoubleRect drcSample = new DoubleRect( + xLeftSample, yTopSample, xRightSample, yBottomSample); + Color clrSample = SampleBitmap(bmSrc, drcSample); + bmDst.SetPixel(xDst, yDst, clrSample); + } + } + } + + static Color SampleBitmap(Bitmap bm, DoubleRect drc) { + double r = 0.0; + double g = 0.0; + double b = 0.0; + + double nAreaTotal = drc.Width * drc.Height; + for (int y = (int)Math.Floor(drc.top); y < (int)Math.Ceiling(drc.bottom); y++) { + for (int x = (int)Math.Floor(drc.left); x < (int)Math.Ceiling(drc.right); x++) { + DoubleRect drcPixel = new DoubleRect(x, y, x + 1, y + 1); + drcPixel.Intersect(drc); + double nArea = drcPixel.Width * drcPixel.Height; + double nPercent = nArea / nAreaTotal; + Color clr = bm.GetPixel(x, y); + r += (double)clr.R / 255.0 * nPercent; + g += (double)clr.G / 255.0 * nPercent; + b += (double)clr.B / 255.0 * nPercent; + } + } + return Color.FromArgb((int)(r * 255.0), (int)(g * 255.0), (int)(b * 255.0)); + } + + public static TemplateDoc CloneTemplateDoc(TemplateDoc tmpdSrc) { + // This should probably be on ICloneable::Clone() on TemplateDoc + + TemplateDoc tmpdDst = (TemplateDoc)DocManager.NewDocument(typeof(TemplateDoc), new Object[] { tmpdSrc.TileSize }); + DocManager.SetActiveDocument(typeof(TemplateDoc), tmpdSrc); + + Template[] atmplSrc = tmpdSrc.GetTemplates(); + Template tmplSrcBackground = tmpdSrc.GetBackgroundTemplate(); + Template tmplDstBackground = null; + ArrayList alsTmplDst = new ArrayList(); + foreach (Template tmplSrc in atmplSrc) { + Template tmplDst = new Template(tmpdDst, tmplSrc.Name); + tmplDst.OccupancyMap = tmplSrc.OccupancyMap; + tmplDst.TerrainMap = tmplSrc.TerrainMap; + tmplDst.Bitmap = (Bitmap)tmplSrc.Bitmap.Clone(); + alsTmplDst.Add(tmplDst); + if (tmplSrc == tmplSrcBackground) + tmplDstBackground = tmplDst; + } + if (tmplDstBackground != null) + tmpdDst.SetBackgroundTemplate(tmplDstBackground); + tmpdDst.AddTemplates((Template[])alsTmplDst.ToArray(typeof(Template))); + Palette palSrc = tmpdSrc.GetPalette(); + if (palSrc != null) + tmpdDst.SetPalette(palSrc, false); + + return tmpdDst; + } + + public static void QuantizeTemplates(TemplateDoc tmpd, Palette palFixed, int cPalEntries, int cPalEntriesFixed, int cPalEntriesBackground) { + // Load the fixed palette. The result will be 24 bit templates normalized to a palette of 256 colors, + // the "fixed" palette plus a quantized palette. + + if (palFixed == null) { + palFixed = Palette.OpenDialog(null); + if (palFixed == null) { + MessageBox.Show(DocManager.GetFrameParent(), "Must have the fixed color palette to continue!"); + return; + } + + switch (palFixed.Length) { + case 16: + cPalEntries = 16; + cPalEntriesFixed = 16; + cPalEntriesBackground = 0; + break; + + case 256: + cPalEntries = 256; + cPalEntriesFixed = 128; + cPalEntriesBackground = 32; + break; + } + } + + // Quantize loop. Designed to make optimal use of the lower 128 fixed colors + + // Quantize background separately from foreground + + Template tmplBackground = tmpd.GetBackgroundTemplate(); + if (tmplBackground != null && cPalEntriesBackground != 0) { + // Create a despeckled hue map of the background. We'll use this to + // subtract background from foreground so we can quantize foreground separately + + Bitmap bmHueBackground = MakeHueMap(tmplBackground.Bitmap); + DespeckleGrayscaleBitmap(bmHueBackground, 9, 50); + + // Calc mean and standard deviation for filtering purposes + + double nMean = CalcGrayscaleMean(bmHueBackground); + double nStdDev = CalcGrayscaleStandardDeviation(bmHueBackground, nMean); + + // Add extract & quantize the background pixels + + ArrayList alsColorsBackground = new ArrayList(); + AddTemplateColors(alsColorsBackground, tmplBackground.Bitmap); + palFixed = QuantizeColors(alsColorsBackground, palFixed, cPalEntriesFixed + cPalEntriesBackground, cPalEntriesFixed); + cPalEntriesFixed += cPalEntriesBackground; + + // Now extract foreground pixels by first subtracting background pixels + + ArrayList alsColorsForeground = new ArrayList(); + Template[] atmpl = tmpd.GetTemplates(); + foreach (Template tmpl in atmpl) { + if (tmpl == tmplBackground) + continue; + Bitmap bmT = MakeHueMap(tmpl.Bitmap); + DespeckleGrayscaleBitmap(bmT, 9, 50); + SubtractGrayscaleDistribution(bmT, nMean, nStdDev); + for (int y = 0; y < bmT.Height; y++) { + for (int x = 0; x < bmT.Width; x++) { + Color clr = bmT.GetPixel(x, y); + if (clr != Color.FromArgb(255, 0, 255)) + bmT.SetPixel(x, y, tmpl.Bitmap.GetPixel(x, y)); + } + } + AddTemplateColors(alsColorsForeground, bmT); + } + + // Now quantize foreground pixels + // Set the palette and color match + + Palette palNew = QuantizeColors(alsColorsForeground, palFixed, cPalEntries, cPalEntriesFixed); + tmpd.SetPalette(palNew, true); + } else { + // No background template; just quantize everything together + + Template[] atmpl = tmpd.GetTemplates(); + ArrayList alsColors = new ArrayList(); + foreach (Template tmpl in atmpl) + AddTemplateColors(alsColors, tmpl.Bitmap); + + // Now quantize foreground pixels + // Set the palette and color match + + Palette palNew = QuantizeColors(alsColors, palFixed, cPalEntries, cPalEntriesFixed); + tmpd.SetPalette(palNew, true); + } + } + + static void AddTemplateColors(ArrayList alsColors, Bitmap bm) { + for (int y = 0; y < bm.Height; y++) { + for (int x = 0; x < bm.Width; x++) { + Color clr = bm.GetPixel(x, y); + if (clr == Color.FromArgb(255, 0, 255)) + continue; + alsColors.Add(clr); + } + } + } + + static Palette QuantizeColors(ArrayList alsColors, Palette palFixed, int cPalEntries, int cPalEntriesFixed) { + Palette palNew = null; + while (true) { + palNew = QuantizeColors2(alsColors, palFixed, cPalEntries, cPalEntriesFixed); + ArrayList alsColorsNew = new ArrayList(); + foreach (Color clr in alsColors) { + int iclr = palNew.FindClosestEntry(clr); + if (iclr >= cPalEntriesFixed) + alsColorsNew.Add(clr); + } + if (alsColorsNew.Count == alsColors.Count) + break; + alsColors = alsColorsNew; + } + return palNew; + } + + static Palette QuantizeColors2(ArrayList alsColors, Palette palFixed, int cPalEntries, int cPalEntriesFixed) { + // If no quantization needed (4 bit grayscale), return + + if (cPalEntriesFixed >= cPalEntries) + return palFixed; + + MedianCut mcut = new MedianCut(alsColors); + mcut.convert(cPalEntries - cPalEntriesFixed); + Palette palUpper = mcut.GetPalette(); + palUpper.Pad(cPalEntries, Color.FromArgb(255, 0, 255)); + + Color[] aclr = new Color[cPalEntries]; + for (int iclr = 0; iclr < cPalEntriesFixed; iclr++) + aclr[iclr] = palFixed[iclr]; + for (int iclr = cPalEntriesFixed; iclr < cPalEntries; iclr++) { + Color clr = palUpper[iclr - cPalEntriesFixed]; + Color clrT = Color.FromArgb(clr.R & 0xfc, clr.G & 0xfc, clr.B & 0xfc); + aclr[iclr] = clrT; + } + return new Palette(aclr); + } + + public static Bitmap MakeHueMap(Bitmap bm) { + // Make grayscale image based on input image's hue + + Bitmap bmHue = new Bitmap(bm); + for (int y = 0; y < bm.Height; y++) { + for (int x = 0; x < bm.Width; x++) { + Color clr = bm.GetPixel(x, y); + if (clr == Color.FromArgb(255, 0, 255)) { + bmHue.SetPixel(x, y, clr); + continue; + } + int nHue = (int)(clr.GetHue() / 360.0 * 255.0); + bmHue.SetPixel(x, y, Color.FromArgb(nHue, nHue, nHue)); + } + } + + return bmHue; + } + + public static void DespeckleGrayscaleBitmap(Bitmap bm, int cSamples, int nThreshold) { + int[] anHues = new int[cSamples]; + for (int y = 0; y < bm.Height; y++) { + for (int x = 0; x < bm.Width; x++) { + Color clr = bm.GetPixel(x, y); + if (clr == Color.FromArgb(255, 0, 255)) + continue; + int nHue = PatternSampleGrayscaleBitmap(bm, x, y, nThreshold, anHues); + bm.SetPixel(x, y, Color.FromArgb(nHue, nHue, nHue)); + } + } + } + + public static int PatternSampleGrayscaleBitmap(Bitmap bm, int x, int y, int nThreshold, int[] anSamples) { + // Get the average - only needed for edge situations including edges next + // to transparent color + + int cSamples = 0; + int nTotal = 0; + int nAverage = 0; + + int cSamplesEdge = (int)Math.Sqrt(anSamples.Length); + if (cSamplesEdge < 3) + cSamplesEdge = 3; + int nDelta = cSamplesEdge / 2; + for (int yT = -nDelta ; yT <= nDelta; yT++) { + for (int xT = -nDelta; xT <= nDelta; xT++) { + if (xT == 0 && yT == 0) + continue; + if (x + xT >= 0 && x + xT < bm.Width && y + yT >= 0 && y + yT < bm.Height) { + Color clr = bm.GetPixel(x + xT, y + yT); + if (clr == Color.FromArgb(255, 0, 255)) + continue; + cSamples++; + nTotal += clr.R; + } + } + } + nAverage = nTotal / cSamples; + + // 5 sample filter (cross shape) + + int nSample = 0; + if (anSamples.Length == 5) { + for (int yT = -1 ; yT <= 1; yT++) { + for (int xT = -1; xT <= 1; xT++) { + // If one of the corners, don't collect a sample + + if (xT != 0 && yT != 0) + continue; + + // If off an edge, use the average + + if (x + xT < 0 || x + xT >= bm.Width || y + yT < 0 || y + yT >= bm.Height) { + anSamples[nSample++] = nAverage; + continue; + } + + // If transparent color, use average + + Color clr = bm.GetPixel(x + xT, y + yT); + if (clr == Color.FromArgb(255, 0, 255)) { + anSamples[nSample++] = nAverage; + continue; + } + + // Get pixel + + anSamples[nSample++] = clr.R; + } + } + } else { + // NxN sample filter. Needs to be odd, 3x3, 5x5, etc. + + for (int yT = -nDelta; yT <= nDelta; yT++) { + for (int xT = -nDelta; xT <= nDelta; xT++) { + // If off an edge, use the average + + if (x + xT < 0 || x + xT >= bm.Width || y + yT < 0 || y + yT >= bm.Height) { + anSamples[nSample++] = nAverage; + continue; + } + + // If transparent color, use average + + Color clr = bm.GetPixel(x + xT, y + yT); + if (clr == Color.FromArgb(255, 0, 255)) { + anSamples[nSample++] = nAverage; + continue; + } + + // Get pixel + + anSamples[nSample++] = clr.R; + } + } + } + + // Sort values + + Array.Sort(anSamples); + + // Only accept median if the difference between it and the input pixel is above + // the specified threshold + + int nInput = bm.GetPixel(x, y).R; + int nMedian = anSamples[anSamples.Length / 2]; + if (Math.Abs(nMedian - nInput) < nThreshold) + return nInput; + return nMedian; + } + + public static double CalcGrayscaleStandardDeviation(Bitmap bm, double nMean) { + // 68% within 1 standard deviation from mean + // 95% within 2 standard deviations from mean + // 99.7% within 3 standard deviations from mean + + double nSigma = 0.0; + int cCounted = 0; + for (int y = 0; y < bm.Height; y++) { + for (int x = 0; x < bm.Width; x++) { + Color clr = bm.GetPixel(x, y); + if (clr == Color.FromArgb(255, 0, 255)) + continue; + cCounted++; + double nDiff = clr.R - nMean; + nSigma += nDiff * nDiff; + } + } + + return Math.Sqrt(nSigma / cCounted); + } + + public static double CalcGrayscaleMean(Bitmap bm) { + double nHueTotal = 0.0; + int cCounted = 0; + for (int y = 0; y < bm.Height; y++) { + for (int x = 0; x < bm.Width; x++) { + Color clr = bm.GetPixel(x, y); + if (clr == Color.FromArgb(255, 0, 255)) + continue; + cCounted++; + nHueTotal += clr.R; + } + } + return nHueTotal / cCounted; + } + + public static void SubtractGrayscaleDistribution(Bitmap bm, double nMean, double nStdDev) { + double nMin = nMean - nStdDev * 2.0; + double nMax = nMean + nStdDev * 2.0; + + for (int y = 0; y < bm.Height; y++) { + for (int x = 0; x < bm.Width; x++) { + Color clr = bm.GetPixel(x, y); + if (nMin <= (double)clr.R && (double)clr.R <= nMax) + bm.SetPixel(x, y, Color.FromArgb(255, 0, 255)); + } + } + } + + public static Bitmap ScaleTemplateBitmap(Bitmap bm, Bitmap bmMask, int cxNew, int cyNew, double nAreaBackgroundThreshold, double nLuminanceMultBackground, double nSaturationMultBackground, double nLuminanceMultForeground, double nSaturationMultForeground) { + Bitmap bmNew = new Bitmap(cxNew, cyNew); + for (int y = 0; y < bmNew.Height; y++) { + for (int x = 0; x < bmNew.Width; x++) { + double xLeft = (double)x * (double)bm.Width / (double)bmNew.Width; + double yTop = (double)y * (double)bm.Height / (double)bmNew.Height; + double xRight = (double)(x + 1) * (double)bm.Width / (double)bmNew.Width; + if (xRight > bm.Width) + xRight = bm.Width; + double yBottom = (double)(y + 1) * (double)bm.Height / (double)bmNew.Height; + if (yBottom > bm.Height) + yBottom = bm.Height; + DoubleRect drc = new DoubleRect(xLeft, yTop, xRight, yBottom); + Color clrSample = SampleTemplateBitmap(bm, bmMask, drc, nAreaBackgroundThreshold, nLuminanceMultBackground, nSaturationMultBackground, nLuminanceMultForeground, nSaturationMultForeground); + bmNew.SetPixel(x, y, clrSample); + } + } + + return bmNew; + } + + unsafe static Color SampleTemplateBitmap(Bitmap bm, Bitmap bmMask, DoubleRect drc, double nAreaBackgroundThreshold, double nLuminanceMultBackground, double nSaturationMultBackground, double nLuminanceMultForeground, double nSaturationMultForeground) { + // First classify as foreground or background. Calc percentages + + double nAreaTotal = drc.Width * drc.Height; + double nAreaBackground = 0.0; + if (bmMask != null) { + for (int y = (int)Math.Floor(drc.top); y < (int)Math.Ceiling(drc.bottom); y++) { + for (int x = (int)Math.Floor(drc.left); x < (int)Math.Ceiling(drc.right); x++) { + + // Calc the % of whole taken by this pixel fragment + + DoubleRect drcPixel = new DoubleRect(x, y, x + 1.0, y + 1.0); + drcPixel.Intersect(drc); + double nAreaPixel = drcPixel.Width * drcPixel.Height; + + // Get pixel + + if (bmMask.GetPixel(x, y) == Color.FromArgb(255, 0, 255)) + nAreaBackground += nAreaPixel; + } + } + } + + // If background is above a threshold, this pixel will be background, + // otherwise foreground. + + bool fBackground; + if (nAreaBackground / nAreaTotal >= nAreaBackgroundThreshold) { + fBackground = true; + nAreaTotal = nAreaBackground; + } else { + fBackground = false; + nAreaTotal -= nAreaBackground; + } + + double r = 0; + double g = 0; + double b = 0; + + for (int y = (int)Math.Floor(drc.top); y < (int)Math.Ceiling(drc.bottom); y++) { + for (int x = (int)Math.Floor(drc.left); x < (int)Math.Ceiling(drc.right); x++) { + // Foreground / background? + + Color clr = bm.GetPixel(x, y); + if (bmMask != null) { + if (fBackground) { + if (bmMask.GetPixel(x, y) != Color.FromArgb(255, 0, 255)) + continue; + } else { + if (bmMask.GetPixel(x, y) == Color.FromArgb(255, 0, 255)) + continue; + } + } + + // Calc the % of whole taken by this pixel fragment + + DoubleRect drcPixel = new DoubleRect(x, y, x + 1.0, y + 1.0); + drcPixel.Intersect(drc); + double nAreaPixel = drcPixel.Width * drcPixel.Height; + double nPercentPixel = nAreaPixel / nAreaTotal; + + // Add in the color components + + r += clr.R * nPercentPixel; + g += clr.G * nPercentPixel; + b += clr.B * nPercentPixel; + } + } + + // Tweak luminance & saturation + + Color clrT = Color.FromArgb((int)r, (int)g, (int)b); + double nLuminance = clrT.GetBrightness() * (fBackground ? nLuminanceMultBackground : nLuminanceMultForeground); + if (nLuminance > 1.0) + nLuminance = 1.0; + double nSaturation = clrT.GetSaturation() * (fBackground ? nSaturationMultBackground : nSaturationMultForeground); + if (nSaturation > 1.0) + nSaturation = 1.0; + MyHSLtoRGB(clrT.GetHue(), nSaturation, nLuminance); + return Color.FromArgb((int)(rT * 255.0), (int)(gT * 255.0), (int)(bT * 255.0)); + } + + static double rT, gT, bT; + + /* + * given h,s,l on [0..1], + * return r,g,b on [0..1] + */ + unsafe static void + HSL_to_RGB(double h, double sl, double l) { + double v; + + v = (l <= 0.5) ? (l * (1.0 + sl)) : (l + sl - l * sl); + if (v <= 0) { + rT = gT = bT = 0.0; + } else { + double m; + double sv; + int sextant; + double fract, vsf, mid1, mid2; + + m = l + l - v; + sv = (v - m ) / v; + h *= 6.0; + sextant = (int)h; + fract = h - sextant; + vsf = v * sv * fract; + mid1 = m + vsf; + mid2 = v - vsf; + switch (sextant) { + case 0: rT = v; gT = mid1; bT = m; break; + case 1: rT = mid2; gT = v; bT = m; break; + case 2: rT = m; gT = v; bT = mid1; break; + case 3: rT = m; gT = mid2; bT = v; break; + case 4: rT = mid1; gT = m; bT = v; break; + case 5: rT = v; gT = m; bT = mid2; break; + } + } + } + + unsafe static void MyHSLtoRGB(double h, double s, double l) { + // From Graphics Gems. Convert Foley's 0..360 to 0..1 + + HSL_to_RGB(h / 360.0, s, l); + } + } + + public struct DoubleRect { + public DoubleRect(double leftT, double topT, double rightT, double bottomT) { + left = leftT; + top = topT; + right = rightT; + bottom = bottomT; + } + + public double Width { + get { + return right - left; + } + } + + public double Height { + get { + return bottom - top; + } + } + + public void Intersect(DoubleRect drc) { + left = Math.Max(left, drc.left); + right = Math.Min(right, drc.right); + if (left < right) { + top = Math.Max(top, drc.top); + bottom = Math.Min(bottom, drc.bottom); + if (top < bottom) + return; + } + left = 0.0; + right = 0.0; + top = 0.0; + bottom = 0.0; + } + + public double left; + public double top; + public double right; + public double bottom; + } +} diff --git a/m/TileSizeForm.cs b/m/TileSizeForm.cs new file mode 100644 index 0000000..aec4861 --- /dev/null +++ b/m/TileSizeForm.cs @@ -0,0 +1,226 @@ +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; + +namespace m +{ + /// + /// Summary description for TileSizeForm. + /// + public class TileSizeForm : System.Windows.Forms.Form + { + private System.Windows.Forms.Label label1; + private System.Windows.Forms.RadioButton radioButton24x24; + private System.Windows.Forms.RadioButton radioButton16x16; + private System.Windows.Forms.RadioButton radioButtonCustom; + private System.Windows.Forms.TextBox textBoxWidth; + private System.Windows.Forms.TextBox textBoxHeight; + private System.Windows.Forms.Button buttonOK; + private System.Windows.Forms.Label labelWidth; + private System.Windows.Forms.Label labelHeight; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + Size m_sizTile; + + public TileSizeForm() + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + SelectRadioButton(radioButton24x24); + } + + void SelectRadioButton(RadioButton rbtn) { + bool fEnable = (rbtn == radioButtonCustom); + labelWidth.Enabled = fEnable; + textBoxWidth.Enabled = fEnable; + labelHeight.Enabled = fEnable; + textBoxHeight.Enabled = fEnable; + + if (rbtn == radioButton24x24) + m_sizTile = new Size(24, 24); + + if (rbtn == radioButton16x16) + m_sizTile = new Size(16, 16); + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.label1 = new System.Windows.Forms.Label(); + this.radioButton24x24 = new System.Windows.Forms.RadioButton(); + this.radioButton16x16 = new System.Windows.Forms.RadioButton(); + this.radioButtonCustom = new System.Windows.Forms.RadioButton(); + this.labelWidth = new System.Windows.Forms.Label(); + this.textBoxWidth = new System.Windows.Forms.TextBox(); + this.labelHeight = new System.Windows.Forms.Label(); + this.textBoxHeight = new System.Windows.Forms.TextBox(); + this.buttonOK = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // label1 + // + this.label1.Location = new System.Drawing.Point(24, 16); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(272, 16); + this.label1.TabIndex = 0; + this.label1.Text = "What tile size should this new tile collection have?"; + // + // radioButton24x24 + // + this.radioButton24x24.Location = new System.Drawing.Point(24, 40); + this.radioButton24x24.Name = "radioButton24x24"; + this.radioButton24x24.Size = new System.Drawing.Size(104, 16); + this.radioButton24x24.TabIndex = 1; + this.radioButton24x24.TabStop = true; + this.radioButton24x24.Text = "24 x 24"; + this.radioButton24x24.CheckedChanged += new System.EventHandler(this.radioButton24x24_CheckedChanged); + // + // radioButton16x16 + // + this.radioButton16x16.Location = new System.Drawing.Point(24, 64); + this.radioButton16x16.Name = "radioButton16x16"; + this.radioButton16x16.Size = new System.Drawing.Size(104, 16); + this.radioButton16x16.TabIndex = 1; + this.radioButton16x16.Text = "16 x 16"; + this.radioButton16x16.CheckedChanged += new System.EventHandler(this.radioButton16x16_CheckedChanged); + // + // radioButtonCustom + // + this.radioButtonCustom.Location = new System.Drawing.Point(24, 88); + this.radioButtonCustom.Name = "radioButtonCustom"; + this.radioButtonCustom.Size = new System.Drawing.Size(64, 16); + this.radioButtonCustom.TabIndex = 1; + this.radioButtonCustom.Text = "Custom:"; + this.radioButtonCustom.CheckedChanged += new System.EventHandler(this.radioButtonCustom_CheckedChanged); + // + // labelWidth + // + this.labelWidth.Enabled = false; + this.labelWidth.Location = new System.Drawing.Point(104, 89); + this.labelWidth.Name = "labelWidth"; + this.labelWidth.Size = new System.Drawing.Size(40, 16); + this.labelWidth.TabIndex = 2; + this.labelWidth.Text = "Width:"; + // + // textBoxWidth + // + this.textBoxWidth.Enabled = false; + this.textBoxWidth.Location = new System.Drawing.Point(144, 86); + this.textBoxWidth.Name = "textBoxWidth"; + this.textBoxWidth.Size = new System.Drawing.Size(48, 20); + this.textBoxWidth.TabIndex = 2; + this.textBoxWidth.Text = ""; + // + // labelHeight + // + this.labelHeight.Enabled = false; + this.labelHeight.Location = new System.Drawing.Point(208, 90); + this.labelHeight.Name = "labelHeight"; + this.labelHeight.Size = new System.Drawing.Size(48, 16); + this.labelHeight.TabIndex = 62; + this.labelHeight.Text = "Height:"; + // + // textBoxHeight + // + this.textBoxHeight.Enabled = false; + this.textBoxHeight.Location = new System.Drawing.Point(254, 86); + this.textBoxHeight.Name = "textBoxHeight"; + this.textBoxHeight.Size = new System.Drawing.Size(48, 20); + this.textBoxHeight.TabIndex = 2; + this.textBoxHeight.Text = ""; + // + // buttonOK + // + this.buttonOK.Location = new System.Drawing.Point(136, 128); + this.buttonOK.Name = "buttonOK"; + this.buttonOK.TabIndex = 8; + this.buttonOK.Text = "OK"; + this.buttonOK.Click += new System.EventHandler(this.buttonOK_Click); + // + // TileSizeForm + // + this.AcceptButton = this.buttonOK; + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.ClientSize = new System.Drawing.Size(336, 158); + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.textBoxWidth, + this.textBoxHeight, + this.buttonOK, + this.labelHeight, + this.labelWidth, + this.radioButtonCustom, + this.radioButton16x16, + this.radioButton24x24, + this.label1}); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "TileSizeForm"; + this.ShowInTaskbar = false; + this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; + this.Text = "Tile Size"; + this.ResumeLayout(false); + + } + #endregion + + private void radioButton24x24_CheckedChanged(object sender, System.EventArgs e) { + SelectRadioButton(radioButton24x24); + } + + private void radioButton16x16_CheckedChanged(object sender, System.EventArgs e) { + SelectRadioButton(radioButton16x16); + } + + private void radioButtonCustom_CheckedChanged(object sender, System.EventArgs e) { + SelectRadioButton(radioButtonCustom); + textBoxWidth.Focus(); + } + + private void buttonOK_Click(object sender, System.EventArgs e) { + // If custom, validate + + if (radioButtonCustom.Checked) { + int cx = int.Parse(textBoxWidth.Text); + int cy = int.Parse(textBoxHeight.Text); + if (cx <= 0 || cx > 64 || cy <= 0 || cy > 64) { + MessageBox.Show(DocManager.GetFrameParent(), "Invalid custom tile size. Try again."); + return; + } + m_sizTile = new Size(cx, cy); + } + + DialogResult = DialogResult.OK; + } + + public Size GetTileSize() { + return m_sizTile; + } + } +} diff --git a/m/TileSizeForm.resources b/m/TileSizeForm.resources new file mode 100644 index 0000000..babe694 Binary files /dev/null and b/m/TileSizeForm.resources differ diff --git a/m/TileSizeForm.resx b/m/TileSizeForm.resx new file mode 100644 index 0000000..68f64af --- /dev/null +++ b/m/TileSizeForm.resx @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + TileSizeForm + + \ No newline at end of file diff --git a/m/Trigger.cs b/m/Trigger.cs new file mode 100644 index 0000000..026730f --- /dev/null +++ b/m/Trigger.cs @@ -0,0 +1,108 @@ +using System; +using System.Collections; +using SpiffLib; + +namespace m { + [Serializable] + public class Trigger { + int m_nfSides; + ArrayList m_alsConditions; + ArrayList m_alsActions; + + public Trigger() { + m_nfSides = 0; + m_alsConditions = new ArrayList(); + m_alsActions = new ArrayList(); + } + + public virtual Trigger Clone() { + Trigger tgr = new Trigger(); + tgr.Sides = Sides; + foreach (CaBase cab in Conditions) + tgr.Conditions.Add(cab.Clone()); + foreach (CaBase cab in Actions) + tgr.Actions.Add(cab.Clone()); + return tgr; + } + + public bool IsValid() { + return GetError() == null; + } + + public string GetError() { + // Invalid if 0 sides + + if (m_nfSides == 0) + return "No Sides selected"; + + // Invalid if 0 conditions + + if (m_alsConditions.Count == 0) + return "No Conditions Entered"; + + // All conditions must be valid + + int n = 0; + foreach (CaBase cab in m_alsConditions) { + if (!cab.IsValid()) { + return "Condition " + n + " is invalid"; + } + n++; + } + + // Invalid if 0 actions + + if (m_alsActions.Count == 0) + return "No Actions Entered"; + + // All actions must be valid + + n = 0; + foreach (CaBase cab in m_alsActions) { + if (!cab.IsValid()) { + return "Action " + n + " is invalid"; + } + n++; + } + + // Looks good + + return null; + } + + public ArrayList Conditions { + get { + return m_alsConditions; + } + } + + public ArrayList Actions { + get { + return m_alsActions; + } + } + + public int Sides { + get { + return m_nfSides; + } + set { + m_nfSides = value; + } + } + + public void AddIniProperties(Ini.Section sec) { + // Save conditions & actions + + foreach (CaBase cab in m_alsConditions) { + if (!(cab is CommentCondition)) + sec.Add(new Ini.Property("C", cab.ToSaveString())); + } + + foreach (CaBase cab in m_alsActions) { + if (!(cab is CommentTriggerAction)) + sec.Add(new Ini.Property("A", cab.ToSaveString())); + } + } + } +} diff --git a/m/TriggerManager.cs b/m/TriggerManager.cs new file mode 100644 index 0000000..4f17341 --- /dev/null +++ b/m/TriggerManager.cs @@ -0,0 +1,333 @@ +using System; +using System.Collections; +using SpiffLib; + +namespace m +{ + // Capitalized this way on purpose for ToString() niceness + + public enum Side { + [DisplayName("neutral")] sideNeutral = 0, + [DisplayName("side 1")] side1, + [DisplayName("side 2")] side2, + [DisplayName("side 3")] side3, + [DisplayName("side 4")] side4 + }; + + [AttributeUsage(AttributeTargets.All, Inherited = true, AllowMultiple = false)] + class DisplayNameAttribute : Attribute { + private string m_strDisplayName; + + public DisplayNameAttribute(string strName) { + m_strDisplayName = strName; + } + + public string DisplayName { + get { + return m_strDisplayName; + } + set { + m_strDisplayName = value; + } + } + } + + [AttributeUsage(AttributeTargets.All, Inherited = true, AllowMultiple = false)] + class DescriptionAttribute : Attribute { + private string m_strDescription; + + public DescriptionAttribute(string strDescription) { + m_strDescription = strDescription; + } + + public string Description { + get { + return m_strDescription; + } + set { + m_strDescription = value; + } + } + } + + [Serializable] + public class TriggerManager + { + ArrayList m_alsTriggers; + ArrayList[] m_aalsSideTriggers; + bool m_fModified; + + public TriggerManager() + { + m_fModified = false; + m_alsTriggers = new ArrayList(); + m_aalsSideTriggers = new ArrayList[Enum.GetValues(typeof(Side)).Length]; + for (int n = 0; n < m_aalsSideTriggers.Length; n++) + m_aalsSideTriggers[n] = new ArrayList(); + } + + public ArrayList Triggers { + get { + return m_alsTriggers; + } + } + + void SetModified() { + m_fModified = true; + } + + public void ClearModified() { + m_fModified = false; + } + + public bool IsModified() { + return m_fModified; + } + + public Trigger[] GetTriggerList(Side side) { + ArrayList als = (ArrayList)m_aalsSideTriggers[(int)side]; + return (Trigger[])als.ToArray(typeof(Trigger)); + } + + public void AddTrigger(Trigger tgr) { + m_alsTriggers.Add(tgr); + foreach(Side side in Enum.GetValues(typeof(Side))) { + if ((tgr.Sides & SideToMask(side)) != 0) { + ArrayList als = (ArrayList)m_aalsSideTriggers[(int)side]; + als.Add(tgr); + } + } + SetModified(); + } + + public void RemoveTrigger(Trigger tgr) { + m_alsTriggers.Remove(tgr); + foreach (Side side in Enum.GetValues(typeof(Side))) { + if ((tgr.Sides & SideToMask(side)) != 0) { + ArrayList als = (ArrayList)m_aalsSideTriggers[(int)side]; + als.Remove(tgr); + } + } + SetModified(); + } + + public void ModifyTrigger(Trigger tgrModify, Trigger tgr) { + int n = m_alsTriggers.IndexOf(tgrModify); + if (n >= 0) + m_alsTriggers[n] = tgr; + foreach(Side side in Enum.GetValues(typeof(Side))) { + ArrayList als = (ArrayList)m_aalsSideTriggers[(int)side]; + if ((SideToMask(side) & tgr.Sides) != 0) { + n = als.IndexOf(tgrModify); + if (n >= 0) { + als[n] = tgr; + } else { + als.Add(tgr); + } + } else { + als.Remove(tgrModify); + } + } + SetModified(); + } + + public int MoveUpTrigger(Side side, Trigger tgr) { + ArrayList als = (ArrayList)m_aalsSideTriggers[(int)side]; + int n = als.IndexOf(tgr); + if (n > 0) { + als.Remove(tgr); + als.Insert(n - 1, tgr); + SetModified(); + return n - 1; + } + return -1; + } + + public int MoveDownTrigger(Side side, Trigger tgr) { + ArrayList als = (ArrayList)m_aalsSideTriggers[(int)side]; + int n = als.IndexOf(tgr); + if (n != -1 && n < als.Count - 1) { + als.Remove(tgr); + als.Insert(n + 1, tgr); + SetModified(); + return n + 1; + } + return -1; + } + + public int SideToMask(Side side) { + return (1 << (int)side); + } + + public Side[] GetTriggerSides() { + int nfMask = GetSidesMask(); + ArrayList alsSides = new ArrayList(); + foreach (Side side in Enum.GetValues(typeof(Side))) { + if ((nfMask & SideToMask(side)) != 0) + alsSides.Add(side); + } + return (Side[])alsSides.ToArray(typeof(Side)); + } + + public int GetSidesMask() { + int nfMask = 0; + foreach (Trigger tgr in m_alsTriggers) { + nfMask |= tgr.Sides; + } + return nfMask; + } + + public Ini.Section GetIniSection(bool fDemoCheckTrigger) { + // If asked create a trigger causes mission failure if running on + // demo version side1 + + bool fModifiedSave = m_fModified; + Trigger tgrDemo = new Trigger(); + if (fDemoCheckTrigger) { + // condition: persistent variable $demo is exactly 1 + // action: end mission: lose + + // Condition + + tgrDemo.Sides = SideToMask(Side.side1); + TestPvarCondition cdn = new TestPvarCondition(); + cdn.Active = true; + CaTypeText catText = (CaTypeText)cdn.GetTypes()[0]; + catText.Text = "$demo"; + CaTypeQualifiedNumber catQualNum = (CaTypeQualifiedNumber)cdn.GetTypes()[1]; + catQualNum.Qualifier = Qualifier.Exactly; + catQualNum.Value = 1; + tgrDemo.Conditions.Add(cdn); + + // Action + + EndMissionTriggerAction acn = new EndMissionTriggerAction(); + acn.Active = true; + CaTypeWinLose catWinLose = (CaTypeWinLose)acn.GetTypes()[0]; + catWinLose.Result = WinLoseType.Lose; + tgrDemo.Actions.Add(acn); + + // Add this trigger temporarily + // Move it up to first place + + AddTrigger(tgrDemo); + while (MoveUpTrigger(Side.side1, tgrDemo) != -1) + ; + } + + // Save triggers + + Ini.Section sec = new Ini.Section("Triggers"); + sec.Add(new Ini.Property("Count", m_alsTriggers.Count.ToString())); + foreach (Trigger tgr in m_alsTriggers) { + // Calc per side indexes + + string strT = ""; + for (int n = 0; n < m_aalsSideTriggers.Length; n++) { + ArrayList als = (ArrayList)m_aalsSideTriggers[n]; + int j = als.IndexOf(tgr); + if (j != -1) { + if (strT != "") + strT += ","; + string strType = "k" + ((Side)n).ToString(); + strT += strType + ":" + j.ToString(); + } + } + sec.Add(new Ini.Property("T", strT)); + + // Save trigger contents + + tgr.AddIniProperties(sec); + } + + // Restore order + + if (fDemoCheckTrigger) { + m_fModified = fModifiedSave; + RemoveTrigger(tgrDemo); + } + + return sec; + } + + public void LoadIni(Ini ini) { + Hashtable map = new Hashtable(); + Trigger tgrCurrent = null; + Ini.Section sec = ini["Triggers"]; + foreach (Ini.Property prop in sec.Properties) { + if (prop.Name == "Count") { + continue; + } + if (prop.Name == "T") { + tgrCurrent = new Trigger(); + int nfSides = 0; + foreach (string key in prop.Value.Split(',')) { + Side side = (Side)int.Parse(key.Split(':')[0]); + nfSides |= SideToMask(side); + map.Add(key, tgrCurrent); + } + tgrCurrent.Sides = nfSides; + m_alsTriggers.Add(tgrCurrent); + continue; + } + if (prop.Name == "C") { + tgrCurrent.Conditions.Add(TriggerConditionLoader.LoadIni(prop.Value)); + continue; + } + if (prop.Name == "A") { + tgrCurrent.Actions.Add(TriggerActionLoader.LoadIni(prop.Value)); + continue; + } + } + + // Add the triggers for each side in proper order + + for (int side = 0; side < m_aalsSideTriggers.Length; side++) { + int index = 0; + while (true) { + bool fFound = false; + foreach (string key in map.Keys) { + int sideT = int.Parse(key.Split(':')[0]); + if (sideT != side) { + continue; + } + int indexT = int.Parse(key.Split(':')[1]); + if (indexT != index) { + continue; + } + fFound = true; + m_aalsSideTriggers[side].Add(map[key]); + } + if (!fFound) { + break; + } + index = index + 1; + } + } + + // Go through all the triggers and search for demo check trigger. + // There should be only one, but check them all. + + ArrayList alsRemove = new ArrayList(); + foreach (Trigger tgr in m_alsTriggers) { + foreach (CaBase cab in tgr.Conditions) { + if (cab.GetType() == typeof(TestPvarCondition)) { + TestPvarCondition cdn = (TestPvarCondition)cab; + if (cdn.GetVariableString() == "$demo") { + alsRemove.Add(tgr); + break; + } + } + + } + } + foreach (Trigger tgr in alsRemove) { + RemoveTrigger(tgr); + } + + // Triggers have been modified + + SetModified(); + } + } +} diff --git a/m/TriggerPropForm.cs b/m/TriggerPropForm.cs new file mode 100644 index 0000000..a74e8bf --- /dev/null +++ b/m/TriggerPropForm.cs @@ -0,0 +1,591 @@ +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; + +namespace m +{ + /// + /// Summary description for TriggerPropForm. + /// + public class TriggerPropForm : System.Windows.Forms.Form + { + Trigger m_tgr; + private System.Windows.Forms.TabControl tabControl1; + private System.Windows.Forms.TabPage tabPageConditions; + private System.Windows.Forms.TabPage tabPageActions; + private System.Windows.Forms.Button buttonOk; + private System.Windows.Forms.Button buttonCancel; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.CheckedListBox checkedListBoxConditions; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Button buttonNewCondition; + private System.Windows.Forms.Button buttonModifyCondition; + private System.Windows.Forms.Button buttonCopyCondition; + private System.Windows.Forms.Button buttonDeleteCondition; + private System.Windows.Forms.Button buttonMoveUpCondition; + private System.Windows.Forms.Button buttonMoveDownCondition; + private System.Windows.Forms.Button buttonMoveUpAction; + private System.Windows.Forms.Button buttonDeleteAction; + private System.Windows.Forms.Button buttonCopyAction; + private System.Windows.Forms.Button buttonModifyAction; + private System.Windows.Forms.Button buttonNewAction; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.CheckedListBox checkedListBoxActions; + private System.Windows.Forms.Button buttonMoveDownAction; + private System.Windows.Forms.CheckedListBox checkedListBoxSides; + private System.Windows.Forms.TabPage tabPageSides; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + public TriggerPropForm(Trigger tgr) + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + // + // TODO: Add any constructor code after InitializeComponent call + // + + m_tgr = tgr; + InitConditionsListBox(-1); + InitActionsListBox(-1); + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.tabControl1 = new System.Windows.Forms.TabControl(); + this.tabPageSides = new System.Windows.Forms.TabPage(); + this.label1 = new System.Windows.Forms.Label(); + this.checkedListBoxSides = new System.Windows.Forms.CheckedListBox(); + this.tabPageConditions = new System.Windows.Forms.TabPage(); + this.buttonMoveUpCondition = new System.Windows.Forms.Button(); + this.buttonDeleteCondition = new System.Windows.Forms.Button(); + this.buttonCopyCondition = new System.Windows.Forms.Button(); + this.buttonModifyCondition = new System.Windows.Forms.Button(); + this.buttonNewCondition = new System.Windows.Forms.Button(); + this.label2 = new System.Windows.Forms.Label(); + this.checkedListBoxConditions = new System.Windows.Forms.CheckedListBox(); + this.buttonMoveDownCondition = new System.Windows.Forms.Button(); + this.tabPageActions = new System.Windows.Forms.TabPage(); + this.buttonMoveUpAction = new System.Windows.Forms.Button(); + this.buttonDeleteAction = new System.Windows.Forms.Button(); + this.buttonCopyAction = new System.Windows.Forms.Button(); + this.buttonModifyAction = new System.Windows.Forms.Button(); + this.buttonNewAction = new System.Windows.Forms.Button(); + this.label3 = new System.Windows.Forms.Label(); + this.checkedListBoxActions = new System.Windows.Forms.CheckedListBox(); + this.buttonMoveDownAction = new System.Windows.Forms.Button(); + this.buttonOk = new System.Windows.Forms.Button(); + this.buttonCancel = new System.Windows.Forms.Button(); + this.tabControl1.SuspendLayout(); + this.tabPageSides.SuspendLayout(); + this.tabPageConditions.SuspendLayout(); + this.tabPageActions.SuspendLayout(); + this.SuspendLayout(); + // + // tabControl1 + // + this.tabControl1.Controls.AddRange(new System.Windows.Forms.Control[] { + this.tabPageSides, + this.tabPageConditions, + this.tabPageActions}); + this.tabControl1.Location = new System.Drawing.Point(16, 16); + this.tabControl1.Name = "tabControl1"; + this.tabControl1.SelectedIndex = 0; + this.tabControl1.Size = new System.Drawing.Size(480, 384); + this.tabControl1.TabIndex = 0; + this.tabControl1.SelectedIndexChanged += new System.EventHandler(this.tabControl1_SelectedIndexChanged); + // + // tabPageSides + // + this.tabPageSides.Controls.AddRange(new System.Windows.Forms.Control[] { + this.label1, + this.checkedListBoxSides}); + this.tabPageSides.Location = new System.Drawing.Point(4, 22); + this.tabPageSides.Name = "tabPageSides"; + this.tabPageSides.Size = new System.Drawing.Size(472, 358); + this.tabPageSides.TabIndex = 0; + this.tabPageSides.Text = "Sides"; + // + // label1 + // + this.label1.Location = new System.Drawing.Point(16, 13); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(208, 16); + this.label1.TabIndex = 1; + this.label1.Text = "For which sides will this trigger execute?"; + // + // checkedListBoxSides + // + this.checkedListBoxSides.CheckOnClick = true; + this.checkedListBoxSides.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0))); + this.checkedListBoxSides.Location = new System.Drawing.Point(16, 37); + this.checkedListBoxSides.Name = "checkedListBoxSides"; + this.checkedListBoxSides.Size = new System.Drawing.Size(352, 304); + this.checkedListBoxSides.TabIndex = 0; + this.checkedListBoxSides.ItemCheck += new System.Windows.Forms.ItemCheckEventHandler(this.checkedListBoxSides_ItemCheck); + // + // tabPageConditions + // + this.tabPageConditions.Controls.AddRange(new System.Windows.Forms.Control[] { + this.buttonMoveUpCondition, + this.buttonDeleteCondition, + this.buttonCopyCondition, + this.buttonModifyCondition, + this.buttonNewCondition, + this.label2, + this.checkedListBoxConditions, + this.buttonMoveDownCondition}); + this.tabPageConditions.Location = new System.Drawing.Point(4, 22); + this.tabPageConditions.Name = "tabPageConditions"; + this.tabPageConditions.Size = new System.Drawing.Size(472, 358); + this.tabPageConditions.TabIndex = 1; + this.tabPageConditions.Text = "Conditions"; + // + // buttonMoveUpCondition + // + this.buttonMoveUpCondition.Location = new System.Drawing.Point(16, 323); + this.buttonMoveUpCondition.Name = "buttonMoveUpCondition"; + this.buttonMoveUpCondition.Size = new System.Drawing.Size(168, 23); + this.buttonMoveUpCondition.TabIndex = 6; + this.buttonMoveUpCondition.Text = "Move &Up"; + this.buttonMoveUpCondition.Click += new System.EventHandler(this.buttonMoveUpCondition_Click); + // + // buttonDeleteCondition + // + this.buttonDeleteCondition.Location = new System.Drawing.Point(382, 133); + this.buttonDeleteCondition.Name = "buttonDeleteCondition"; + this.buttonDeleteCondition.TabIndex = 5; + this.buttonDeleteCondition.Text = "&Delete"; + this.buttonDeleteCondition.Click += new System.EventHandler(this.buttonDeleteCondition_Click); + // + // buttonCopyCondition + // + this.buttonCopyCondition.Location = new System.Drawing.Point(382, 101); + this.buttonCopyCondition.Name = "buttonCopyCondition"; + this.buttonCopyCondition.TabIndex = 4; + this.buttonCopyCondition.Text = "&Copy"; + this.buttonCopyCondition.Click += new System.EventHandler(this.buttonCopyCondition_Click); + // + // buttonModifyCondition + // + this.buttonModifyCondition.Location = new System.Drawing.Point(382, 69); + this.buttonModifyCondition.Name = "buttonModifyCondition"; + this.buttonModifyCondition.TabIndex = 3; + this.buttonModifyCondition.Text = "&Modify..."; + this.buttonModifyCondition.Click += new System.EventHandler(this.buttonModifyCondition_Click); + // + // buttonNewCondition + // + this.buttonNewCondition.Location = new System.Drawing.Point(382, 37); + this.buttonNewCondition.Name = "buttonNewCondition"; + this.buttonNewCondition.TabIndex = 2; + this.buttonNewCondition.Text = "&New..."; + this.buttonNewCondition.Click += new System.EventHandler(this.buttonNewCondition_Click); + // + // label2 + // + this.label2.Location = new System.Drawing.Point(16, 13); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(312, 16); + this.label2.TabIndex = 1; + this.label2.Text = "Conditions specified for this trigger:"; + // + // checkedListBoxConditions + // + this.checkedListBoxConditions.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0))); + this.checkedListBoxConditions.Location = new System.Drawing.Point(16, 37); + this.checkedListBoxConditions.Name = "checkedListBoxConditions"; + this.checkedListBoxConditions.Size = new System.Drawing.Size(352, 274); + this.checkedListBoxConditions.TabIndex = 0; + this.checkedListBoxConditions.SelectedIndexChanged += new System.EventHandler(this.checkedListBoxConditions_SelectedIndexChanged); + this.checkedListBoxConditions.ItemCheck += new System.Windows.Forms.ItemCheckEventHandler(this.checkedListBoxConditions_ItemCheck); + // + // buttonMoveDownCondition + // + this.buttonMoveDownCondition.Location = new System.Drawing.Point(200, 323); + this.buttonMoveDownCondition.Name = "buttonMoveDownCondition"; + this.buttonMoveDownCondition.Size = new System.Drawing.Size(168, 23); + this.buttonMoveDownCondition.TabIndex = 6; + this.buttonMoveDownCondition.Text = "Move D&own"; + this.buttonMoveDownCondition.Click += new System.EventHandler(this.buttonMoveDownCondition_Click); + // + // tabPageActions + // + this.tabPageActions.Controls.AddRange(new System.Windows.Forms.Control[] { + this.buttonMoveUpAction, + this.buttonDeleteAction, + this.buttonCopyAction, + this.buttonModifyAction, + this.buttonNewAction, + this.label3, + this.checkedListBoxActions, + this.buttonMoveDownAction}); + this.tabPageActions.Location = new System.Drawing.Point(4, 22); + this.tabPageActions.Name = "tabPageActions"; + this.tabPageActions.Size = new System.Drawing.Size(472, 358); + this.tabPageActions.TabIndex = 2; + this.tabPageActions.Text = "Actions"; + // + // buttonMoveUpAction + // + this.buttonMoveUpAction.Location = new System.Drawing.Point(16, 323); + this.buttonMoveUpAction.Name = "buttonMoveUpAction"; + this.buttonMoveUpAction.Size = new System.Drawing.Size(168, 23); + this.buttonMoveUpAction.TabIndex = 14; + this.buttonMoveUpAction.Text = "Move &Up"; + this.buttonMoveUpAction.Click += new System.EventHandler(this.buttonMoveUpAction_Click); + // + // buttonDeleteAction + // + this.buttonDeleteAction.Location = new System.Drawing.Point(382, 133); + this.buttonDeleteAction.Name = "buttonDeleteAction"; + this.buttonDeleteAction.TabIndex = 12; + this.buttonDeleteAction.Text = "&Delete"; + this.buttonDeleteAction.Click += new System.EventHandler(this.buttonDeleteAction_Click); + // + // buttonCopyAction + // + this.buttonCopyAction.Location = new System.Drawing.Point(382, 101); + this.buttonCopyAction.Name = "buttonCopyAction"; + this.buttonCopyAction.TabIndex = 11; + this.buttonCopyAction.Text = "&Copy"; + this.buttonCopyAction.Click += new System.EventHandler(this.buttonCopyAction_Click); + // + // buttonModifyAction + // + this.buttonModifyAction.Location = new System.Drawing.Point(382, 69); + this.buttonModifyAction.Name = "buttonModifyAction"; + this.buttonModifyAction.TabIndex = 10; + this.buttonModifyAction.Text = "&Modify..."; + this.buttonModifyAction.Click += new System.EventHandler(this.buttonModifyAction_Click); + // + // buttonNewAction + // + this.buttonNewAction.Location = new System.Drawing.Point(382, 37); + this.buttonNewAction.Name = "buttonNewAction"; + this.buttonNewAction.TabIndex = 9; + this.buttonNewAction.Text = "&New..."; + this.buttonNewAction.Click += new System.EventHandler(this.buttonNewAction_Click); + // + // label3 + // + this.label3.Location = new System.Drawing.Point(16, 13); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(312, 16); + this.label3.TabIndex = 8; + this.label3.Text = "Actions specified for this trigger:"; + // + // checkedListBoxActions + // + this.checkedListBoxActions.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0))); + this.checkedListBoxActions.Location = new System.Drawing.Point(16, 37); + this.checkedListBoxActions.Name = "checkedListBoxActions"; + this.checkedListBoxActions.Size = new System.Drawing.Size(352, 274); + this.checkedListBoxActions.TabIndex = 7; + this.checkedListBoxActions.SelectedIndexChanged += new System.EventHandler(this.checkedListBoxActions_SelectedIndexChanged); + // + // buttonMoveDownAction + // + this.buttonMoveDownAction.Location = new System.Drawing.Point(200, 323); + this.buttonMoveDownAction.Name = "buttonMoveDownAction"; + this.buttonMoveDownAction.Size = new System.Drawing.Size(168, 23); + this.buttonMoveDownAction.TabIndex = 13; + this.buttonMoveDownAction.Text = "Move D&own"; + this.buttonMoveDownAction.Click += new System.EventHandler(this.buttonMoveDownAction_Click); + // + // buttonOk + // + this.buttonOk.Location = new System.Drawing.Point(155, 412); + this.buttonOk.Name = "buttonOk"; + this.buttonOk.TabIndex = 1; + this.buttonOk.Text = "Ok"; + this.buttonOk.Click += new System.EventHandler(this.buttonOk_Click); + // + // buttonCancel + // + this.buttonCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.buttonCancel.Location = new System.Drawing.Point(283, 412); + this.buttonCancel.Name = "buttonCancel"; + this.buttonCancel.TabIndex = 1; + this.buttonCancel.Text = "Cancel"; + this.buttonCancel.Click += new System.EventHandler(this.buttonCancel_Click); + // + // TriggerPropForm + // + this.AcceptButton = this.buttonOk; + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.CancelButton = this.buttonCancel; + this.ClientSize = new System.Drawing.Size(512, 446); + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.buttonOk, + this.tabControl1, + this.buttonCancel}); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "TriggerPropForm"; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Trigger Properties"; + this.Load += new System.EventHandler(this.TriggerPropForm_Load); + this.tabControl1.ResumeLayout(false); + this.tabPageSides.ResumeLayout(false); + this.tabPageConditions.ResumeLayout(false); + this.tabPageActions.ResumeLayout(false); + this.ResumeLayout(false); + + } + #endregion + + void InitSidesListBox() { + for (int n = 0; n < checkedListBoxSides.Items.Count; n++) { + checkedListBoxSides.SetItemChecked(n, (m_tgr.Sides & (1 << n)) != 0); + } + } + + void InitConditionsListBox(int nSelectedIndex) { + checkedListBoxConditions.Items.Clear(); + foreach (CaBase cab in m_tgr.Conditions) + checkedListBoxConditions.Items.Add(cab, cab.Active); + EnableConditionButtons(); + checkedListBoxConditions.SelectedIndex = nSelectedIndex; + if (checkedListBoxConditions.SelectedIndex == -1 && checkedListBoxConditions.Items.Count != 0) + checkedListBoxConditions.SelectedIndex = 0; + } + + void InitActionsListBox(int nSelectedIndex) { + checkedListBoxActions.Items.Clear(); + foreach (CaBase cab in m_tgr.Actions) + checkedListBoxActions.Items.Add(cab, cab.Active); + EnableActionButtons(); + checkedListBoxActions.SelectedIndex = nSelectedIndex; + if (checkedListBoxActions.SelectedIndex == -1 && checkedListBoxActions.Items.Count != 0) + checkedListBoxActions.SelectedIndex = 0; + } + + private void buttonNewCondition_Click(object sender, System.EventArgs e) { + CaBase cab = CaNew.DoModal(null, "New Condition", "Condition"); + if (cab != null) { + m_tgr.Conditions.Add(cab); + InitConditionsListBox(m_tgr.Conditions.Count - 1); + } + } + + private void buttonModifyCondition_Click(object sender, System.EventArgs e) { + int n = checkedListBoxConditions.SelectedIndex; + if (n < 0) + return; + CaBase cab = (CaBase)m_tgr.Conditions[n]; + cab = CaNew.DoModal(cab.Clone(), "Modify Condition", "Condition"); + if (cab != null) { + m_tgr.Conditions[n] = cab; + InitConditionsListBox(n); + } + } + + private void buttonNewAction_Click(object sender, System.EventArgs e) { + CaBase cab = CaNew.DoModal(null, "New Action", "TriggerAction"); + if (cab != null) { + m_tgr.Actions.Add(cab); + InitActionsListBox(m_tgr.Actions.Count - 1); + } + } + + private void buttonModifyAction_Click(object sender, System.EventArgs e) { + int n = checkedListBoxActions.SelectedIndex; + if (n < 0) + return; + CaBase cab = (CaBase)m_tgr.Actions[n]; + cab = CaNew.DoModal(cab.Clone(), "Modify Action", "TriggerAction"); + if (cab != null) { + m_tgr.Actions[n] = cab; + InitActionsListBox(n); + } + } + + private void buttonOk_Click(object sender, System.EventArgs e) { + if (!m_tgr.IsValid()) { + MessageBox.Show(this, "Trigger not initialized correctly! (" + m_tgr.GetError() + ")"); + return; + } + DialogResult = DialogResult.OK; + } + + private void buttonCancel_Click(object sender, System.EventArgs e) { + DialogResult = DialogResult.Cancel; + } + + private void checkedListBoxSides_ItemCheck(object sender, System.Windows.Forms.ItemCheckEventArgs e) { + if (e.CurrentValue == CheckState.Unchecked && e.NewValue == CheckState.Checked) { + m_tgr.Sides |= (1 << e.Index); + } + if (e.CurrentValue == CheckState.Checked && e.NewValue == CheckState.Unchecked) { + m_tgr.Sides &= ~(1 << e.Index); + } + } + + private void tabControl1_SelectedIndexChanged(object sender, System.EventArgs e) { + switch (tabControl1.SelectedIndex) { + case 0: + InitSidesListBox(); + break; + + case 1: + InitConditionsListBox(-1); + break; + + case 2: + InitActionsListBox(-1); + break; + } + } + + private void checkedListBoxConditions_ItemCheck(object sender, System.Windows.Forms.ItemCheckEventArgs e) { + if (e.CurrentValue == CheckState.Unchecked && e.NewValue == CheckState.Checked) { + CaBase cab = (CaBase)m_tgr.Conditions[e.Index]; + cab.Active = true; + } + if (e.CurrentValue == CheckState.Checked && e.NewValue == CheckState.Unchecked) { + CaBase cab = (CaBase)m_tgr.Conditions[e.Index]; + cab.Active = false; + } + } + + private void checkedListBoxConditions_SelectedIndexChanged(object sender, System.EventArgs e) { + EnableConditionButtons(); + } + + void EnableConditionButtons() { + int n = checkedListBoxConditions.SelectedIndex; + bool fItemSelected = (n >= 0); + buttonModifyCondition.Enabled = fItemSelected; + buttonCopyCondition.Enabled = fItemSelected; + buttonDeleteCondition.Enabled = fItemSelected; + buttonMoveUpCondition.Enabled = (n > 0); + buttonMoveDownCondition.Enabled = (n < checkedListBoxConditions.Items.Count - 1); + } + + private void checkedListBoxActions_SelectedIndexChanged(object sender, System.EventArgs e) { + EnableActionButtons(); + } + + void EnableActionButtons() { + int n = checkedListBoxActions.SelectedIndex; + bool fItemSelected = (n >= 0); + buttonModifyAction.Enabled = fItemSelected; + buttonCopyAction.Enabled = fItemSelected; + buttonDeleteAction.Enabled = fItemSelected; + buttonMoveUpAction.Enabled = (n > 0); + buttonMoveDownAction.Enabled = (n < checkedListBoxActions.Items.Count - 1); + } + + private void buttonCopyCondition_Click(object sender, System.EventArgs e) { + int n = checkedListBoxConditions.SelectedIndex; + if (n < 0) + return; + CaBase cab = (CaBase)m_tgr.Conditions[n]; + m_tgr.Conditions.Add(cab.Clone()); + InitConditionsListBox(m_tgr.Conditions.Count - 1); + } + + private void buttonDeleteCondition_Click(object sender, System.EventArgs e) { + int n = checkedListBoxConditions.SelectedIndex; + if (n < 0) + return; + m_tgr.Conditions.RemoveAt(n); + InitConditionsListBox(Math.Min(m_tgr.Conditions.Count - 1, n)); + } + + private void buttonCopyAction_Click(object sender, System.EventArgs e) { + int n = checkedListBoxActions.SelectedIndex; + if (n < 0) + return; + CaBase cab = (CaBase)m_tgr.Actions[n]; + m_tgr.Actions.Add(cab.Clone()); + InitActionsListBox(m_tgr.Actions.Count - 1); + } + + private void buttonDeleteAction_Click(object sender, System.EventArgs e) { + int n = checkedListBoxActions.SelectedIndex; + if (n < 0) + return; + m_tgr.Actions.RemoveAt(n); + InitActionsListBox(Math.Min(m_tgr.Actions.Count - 1, n)); + } + + private void buttonMoveUpCondition_Click(object sender, System.EventArgs e) { + int n = checkedListBoxConditions.SelectedIndex; + if (n <= 0) + return; + CaBase cab = (CaBase)m_tgr.Conditions[n]; + m_tgr.Conditions.RemoveAt(n); + m_tgr.Conditions.Insert(n - 1, cab); + InitConditionsListBox(n - 1); + } + + private void buttonMoveDownCondition_Click(object sender, System.EventArgs e) { + int n = checkedListBoxConditions.SelectedIndex; + if (n < 0 || n >= checkedListBoxConditions.Items.Count - 1) + return; + CaBase cab = (CaBase)m_tgr.Conditions[n]; + m_tgr.Conditions.RemoveAt(n); + m_tgr.Conditions.Insert(n + 1, cab); + InitConditionsListBox(n + 1); + } + + private void buttonMoveUpAction_Click(object sender, System.EventArgs e) { + int n = checkedListBoxActions.SelectedIndex; + if (n <= 0) + return; + CaBase cab = (CaBase)m_tgr.Actions[n]; + m_tgr.Actions.RemoveAt(n); + m_tgr.Actions.Insert(n - 1, cab); + InitActionsListBox(n - 1); + } + + private void buttonMoveDownAction_Click(object sender, System.EventArgs e) { + int n = checkedListBoxActions.SelectedIndex; + if (n < 0 || n >= checkedListBoxActions.Items.Count - 1) + return; + CaBase cab = (CaBase)m_tgr.Actions[n]; + m_tgr.Actions.RemoveAt(n); + m_tgr.Actions.Insert(n + 1, cab); + InitActionsListBox(n + 1); + } + + private void TriggerPropForm_Load(object sender, System.EventArgs e) { + // Have to do this initialization here because the sides checked + // list box will get unchecked before display if we don't. + + checkedListBoxSides.DataSource = Enum.GetNames(typeof(Side)); + InitSidesListBox(); + } + } +} diff --git a/m/TriggerPropForm.resources b/m/TriggerPropForm.resources new file mode 100644 index 0000000..7511d80 Binary files /dev/null and b/m/TriggerPropForm.resources differ diff --git a/m/TriggerPropForm.resx b/m/TriggerPropForm.resx new file mode 100644 index 0000000..cc6118d --- /dev/null +++ b/m/TriggerPropForm.resx @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + TriggerPropForm + + \ No newline at end of file diff --git a/m/TriggersForm.cs b/m/TriggersForm.cs new file mode 100644 index 0000000..54e0cab --- /dev/null +++ b/m/TriggersForm.cs @@ -0,0 +1,476 @@ +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; + +namespace m +{ + /// + /// Summary description for TriggersForm. + /// + public class TriggersForm : System.Windows.Forms.Form + { + static Rectangle s_rcBounds = new Rectangle(); + TriggerManager m_tgrm; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.Button buttonClose; + private System.Windows.Forms.Button buttonNewTrigger; + private System.Windows.Forms.Button buttonModifyTrigger; + private System.Windows.Forms.Button buttonLoadTriggers; + private System.Windows.Forms.Button buttonSaveTriggers; + private System.Windows.Forms.Button buttonCopyTrigger; + private System.Windows.Forms.Button buttonDeleteTrigger; + private System.Windows.Forms.Button buttonMoveUpTrigger; + private System.Windows.Forms.Button buttonMoveDownTrigger; + private System.Windows.Forms.ListBox listBoxSides; + private System.Windows.Forms.ListBox listBoxTriggers; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + public TriggersForm(TriggerManager tgrm) + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + if (!s_rcBounds.IsEmpty) + SetBounds(s_rcBounds.Left, s_rcBounds.Top, s_rcBounds.Width, s_rcBounds.Height, BoundsSpecified.All); + else { + Rectangle rcScreen = Screen.GetWorkingArea(this); + SetBounds((rcScreen.Width - Bounds.Width) / 2, (rcScreen.Height - Bounds.Height) / 2, 0, 0, BoundsSpecified.Location); + } + + m_tgrm = tgrm; + InitSidesListBox(null); + InitTriggersListBox(null); + EnableButtons(); + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.listBoxSides = new System.Windows.Forms.ListBox(); + this.label1 = new System.Windows.Forms.Label(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.buttonMoveUpTrigger = new System.Windows.Forms.Button(); + this.buttonMoveDownTrigger = new System.Windows.Forms.Button(); + this.buttonNewTrigger = new System.Windows.Forms.Button(); + this.listBoxTriggers = new System.Windows.Forms.ListBox(); + this.buttonModifyTrigger = new System.Windows.Forms.Button(); + this.buttonCopyTrigger = new System.Windows.Forms.Button(); + this.buttonDeleteTrigger = new System.Windows.Forms.Button(); + this.buttonLoadTriggers = new System.Windows.Forms.Button(); + this.buttonSaveTriggers = new System.Windows.Forms.Button(); + this.buttonClose = new System.Windows.Forms.Button(); + this.groupBox1.SuspendLayout(); + this.SuspendLayout(); + // + // listBoxSides + // + this.listBoxSides.Anchor = ((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right); + this.listBoxSides.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0))); + this.listBoxSides.ItemHeight = 16; + this.listBoxSides.Location = new System.Drawing.Point(16, 24); + this.listBoxSides.Name = "listBoxSides"; + this.listBoxSides.Size = new System.Drawing.Size(528, 68); + this.listBoxSides.TabIndex = 0; + this.listBoxSides.SelectedIndexChanged += new System.EventHandler(this.listBoxSides_SelectedIndexChanged); + // + // label1 + // + this.label1.Location = new System.Drawing.Point(16, 7); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(336, 16); + this.label1.TabIndex = 1; + this.label1.Text = "Sides with triggers:"; + // + // groupBox1 + // + this.groupBox1.Anchor = (((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right); + this.groupBox1.Controls.AddRange(new System.Windows.Forms.Control[] { + this.buttonMoveUpTrigger, + this.buttonMoveDownTrigger, + this.buttonNewTrigger, + this.listBoxTriggers, + this.buttonModifyTrigger, + this.buttonCopyTrigger, + this.buttonDeleteTrigger, + this.buttonLoadTriggers, + this.buttonSaveTriggers}); + this.groupBox1.Location = new System.Drawing.Point(8, 104); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(672, 528); + this.groupBox1.TabIndex = 2; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "Triggers"; + // + // buttonMoveUpTrigger + // + this.buttonMoveUpTrigger.Anchor = (System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left); + this.buttonMoveUpTrigger.Location = new System.Drawing.Point(8, 494); + this.buttonMoveUpTrigger.Name = "buttonMoveUpTrigger"; + this.buttonMoveUpTrigger.Size = new System.Drawing.Size(184, 23); + this.buttonMoveUpTrigger.TabIndex = 8; + this.buttonMoveUpTrigger.Text = "Move &Up"; + this.buttonMoveUpTrigger.Click += new System.EventHandler(this.buttonMoveUpTrigger_Click); + // + // buttonMoveDownTrigger + // + this.buttonMoveDownTrigger.Anchor = (System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left); + this.buttonMoveDownTrigger.Location = new System.Drawing.Point(200, 494); + this.buttonMoveDownTrigger.Name = "buttonMoveDownTrigger"; + this.buttonMoveDownTrigger.Size = new System.Drawing.Size(184, 23); + this.buttonMoveDownTrigger.TabIndex = 7; + this.buttonMoveDownTrigger.Text = "Move D&own"; + this.buttonMoveDownTrigger.Click += new System.EventHandler(this.buttonMoveDownTrigger_Click); + // + // buttonNewTrigger + // + this.buttonNewTrigger.Anchor = (System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right); + this.buttonNewTrigger.Location = new System.Drawing.Point(552, 16); + this.buttonNewTrigger.Name = "buttonNewTrigger"; + this.buttonNewTrigger.Size = new System.Drawing.Size(104, 23); + this.buttonNewTrigger.TabIndex = 1; + this.buttonNewTrigger.Text = "&New..."; + this.buttonNewTrigger.Click += new System.EventHandler(this.buttonNewTrigger_Click); + // + // listBoxTriggers + // + this.listBoxTriggers.Anchor = (((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right); + this.listBoxTriggers.BackColor = System.Drawing.Color.FromArgb(((System.Byte)(113)), ((System.Byte)(111)), ((System.Byte)(100))); + this.listBoxTriggers.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawVariable; + this.listBoxTriggers.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0))); + this.listBoxTriggers.IntegralHeight = false; + this.listBoxTriggers.Location = new System.Drawing.Point(8, 16); + this.listBoxTriggers.Name = "listBoxTriggers"; + this.listBoxTriggers.Size = new System.Drawing.Size(528, 472); + this.listBoxTriggers.TabIndex = 0; + this.listBoxTriggers.DoubleClick += new System.EventHandler(this.listBoxTriggers_DoubleClick); + this.listBoxTriggers.MeasureItem += new System.Windows.Forms.MeasureItemEventHandler(this.listBoxTriggers_MeasureItem); + this.listBoxTriggers.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.listBoxTriggers_DrawItem); + this.listBoxTriggers.SelectedIndexChanged += new System.EventHandler(this.listBoxTriggers_SelectedIndexChanged); + // + // buttonModifyTrigger + // + this.buttonModifyTrigger.Anchor = (System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right); + this.buttonModifyTrigger.Location = new System.Drawing.Point(552, 48); + this.buttonModifyTrigger.Name = "buttonModifyTrigger"; + this.buttonModifyTrigger.Size = new System.Drawing.Size(104, 23); + this.buttonModifyTrigger.TabIndex = 1; + this.buttonModifyTrigger.Text = "&Modify..."; + this.buttonModifyTrigger.Click += new System.EventHandler(this.buttonModifyTrigger_Click); + // + // buttonCopyTrigger + // + this.buttonCopyTrigger.Anchor = (System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right); + this.buttonCopyTrigger.Location = new System.Drawing.Point(552, 80); + this.buttonCopyTrigger.Name = "buttonCopyTrigger"; + this.buttonCopyTrigger.Size = new System.Drawing.Size(104, 23); + this.buttonCopyTrigger.TabIndex = 1; + this.buttonCopyTrigger.Text = "&Copy"; + this.buttonCopyTrigger.Click += new System.EventHandler(this.buttonCopyTrigger_Click); + // + // buttonDeleteTrigger + // + this.buttonDeleteTrigger.Anchor = (System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right); + this.buttonDeleteTrigger.Location = new System.Drawing.Point(552, 112); + this.buttonDeleteTrigger.Name = "buttonDeleteTrigger"; + this.buttonDeleteTrigger.Size = new System.Drawing.Size(104, 23); + this.buttonDeleteTrigger.TabIndex = 1; + this.buttonDeleteTrigger.Text = "&Delete"; + this.buttonDeleteTrigger.Click += new System.EventHandler(this.buttonDeleteTrigger_Click); + // + // buttonLoadTriggers + // + this.buttonLoadTriggers.Anchor = (System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right); + this.buttonLoadTriggers.Location = new System.Drawing.Point(552, 160); + this.buttonLoadTriggers.Name = "buttonLoadTriggers"; + this.buttonLoadTriggers.Size = new System.Drawing.Size(104, 23); + this.buttonLoadTriggers.TabIndex = 1; + this.buttonLoadTriggers.Text = "&Load Triggers"; + this.buttonLoadTriggers.Click += new System.EventHandler(this.buttonLoadTriggers_Click); + // + // buttonSaveTriggers + // + this.buttonSaveTriggers.Anchor = (System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right); + this.buttonSaveTriggers.Location = new System.Drawing.Point(552, 192); + this.buttonSaveTriggers.Name = "buttonSaveTriggers"; + this.buttonSaveTriggers.Size = new System.Drawing.Size(104, 23); + this.buttonSaveTriggers.TabIndex = 1; + this.buttonSaveTriggers.Text = "&Save Triggers"; + this.buttonSaveTriggers.Click += new System.EventHandler(this.buttonSaveTriggers_Click); + // + // buttonClose + // + this.buttonClose.Anchor = (System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right); + this.buttonClose.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.buttonClose.Location = new System.Drawing.Point(560, 24); + this.buttonClose.Name = "buttonClose"; + this.buttonClose.Size = new System.Drawing.Size(104, 23); + this.buttonClose.TabIndex = 3; + this.buttonClose.Text = "Close"; + this.buttonClose.Click += new System.EventHandler(this.buttonClose_Click); + // + // TriggersForm + // + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.CancelButton = this.buttonClose; + this.ClientSize = new System.Drawing.Size(688, 638); + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.buttonClose, + this.groupBox1, + this.label1, + this.listBoxSides}); + this.Name = "TriggersForm"; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.Manual; + this.Text = "Triggers"; + this.groupBox1.ResumeLayout(false); + this.ResumeLayout(false); + + } + #endregion + + private class NameSide { + private string m_strName; + public Side side; + + public NameSide(Side side) { + this.side = side; + m_strName = Helper.GetDisplayName(typeof(Side), side.ToString()); + } + + override public string ToString() { + return m_strName; + } + } + + void InitSidesListBox(Trigger tgr) { + Side[] aside = m_tgrm.GetTriggerSides(); + if (aside.Length == 0) { + listBoxSides.Items.Clear(); + return; + } + int n = listBoxSides.SelectedIndex; + Side sideSelected; + if (n >= 0) { + sideSelected = ((NameSide)listBoxSides.Items[n]).side; +// sideSelected = (Side)Enum.Parse(typeof(Side), (string)listBoxSides.Items[n]); + } else { + sideSelected = aside[0]; + } + listBoxSides.Items.Clear(); + foreach (Side side in aside) + listBoxSides.Items.Add(new NameSide(side)); +// listBoxSides.Items.Add(side.ToString()); + + if (tgr != null) { + if ((tgr.Sides & m_tgrm.SideToMask(sideSelected)) == 0) { + foreach (Side side in aside) { + if ((m_tgrm.SideToMask(side) & tgr.Sides) != 0) { + sideSelected = side; + break; + } + } + } + } + listBoxSides.SelectedIndex = Array.IndexOf(aside, sideSelected); + } + + int GetSideMaskSelected() { + int nSide = listBoxSides.SelectedIndex; + if (nSide < 0) + return 0; + return m_tgrm.SideToMask(GetSideSelected()); + } + + Side GetSideSelected() { + Side[] aside = m_tgrm.GetTriggerSides(); + return aside[listBoxSides.SelectedIndex]; + } + + void InitTriggersListBox(Trigger tgrSelect) { + listBoxTriggers.Items.Clear(); + int nSide = listBoxSides.SelectedIndex; + if (nSide < 0) + return; + Trigger[] atgr = m_tgrm.GetTriggerList(GetSideSelected()); + foreach (Trigger tgr in atgr) + listBoxTriggers.Items.Add(tgr); + if (tgrSelect == null) { + if (listBoxTriggers.SelectedIndex == -1 && listBoxTriggers.Items.Count != 0) + listBoxTriggers.SelectedIndex = 0; + } else { + listBoxTriggers.SelectedIndex = Array.IndexOf(atgr, tgrSelect); + } + } + + private void buttonClose_Click(object sender, System.EventArgs e) { + DialogResult = DialogResult.OK; + } + + private void buttonNewTrigger_Click(object sender, System.EventArgs e) { + Trigger tgr = new Trigger(); + TriggerPropForm frm = new TriggerPropForm(tgr); + if (frm.ShowDialog() == DialogResult.OK) { + m_tgrm.AddTrigger(tgr); + InitSidesListBox(tgr); + InitTriggersListBox(tgr); + } + } + + private void buttonModifyTrigger_Click(object sender, System.EventArgs e) { + int n = listBoxTriggers.SelectedIndex; + if (n < 0) + return; + Trigger[] atgr = m_tgrm.GetTriggerList(GetSideSelected()); + Trigger tgr = atgr[n].Clone(); + TriggerPropForm frm = new TriggerPropForm(tgr); + if (frm.ShowDialog() == DialogResult.OK) { + m_tgrm.ModifyTrigger(atgr[n], tgr); + InitSidesListBox(tgr); + InitTriggersListBox(tgr); + } + } + + private void listBoxTriggers_MeasureItem(object sender, System.Windows.Forms.MeasureItemEventArgs e) { + Trigger[] atgr = m_tgrm.GetTriggerList(GetSideSelected()); + int n = e.Index; + Trigger tgr = atgr[n]; + int cy = (tgr.Actions.Count + tgr.Conditions.Count + 2) * listBoxTriggers.Font.Height + 4; + // BUGBUG: listbox seems to have a problem with item heights > 255 + e.ItemHeight = Math.Min(cy, 255); + } + + private void listBoxTriggers_DrawItem(object sender, System.Windows.Forms.DrawItemEventArgs e) { + int n = e.Index; + if (n == -1) + return; + + Trigger[] atgr = m_tgrm.GetTriggerList(GetSideSelected()); + Trigger trg = atgr[n]; + Color clr = (e.State & DrawItemState.Selected) != 0 ? Color.FromArgb(178, 180, 191) : Color.White; + e.Graphics.FillRectangle(new SolidBrush(clr), e.Bounds.Left + 2, e.Bounds.Top + 2, e.Bounds.Width - 4, e.Bounds.Height - 4); + int y = e.Bounds.Top + 2; + int x = e.Bounds.Left + 2; + int cyFont = e.Font.Height; + Brush br = new SolidBrush(e.ForeColor); + e.Graphics.DrawString("CONDITIONS:", e.Font, br, x, y); + y += cyFont; + foreach (CaBase cab in trg.Conditions) { + e.Graphics.DrawString(" - " + cab.ToString(), e.Font, br, x, y); + y += cyFont; + } + e.Graphics.DrawString("ACTIONS:", e.Font, br, x, y); + y += cyFont; + foreach (CaBase cab in trg.Actions) { + e.Graphics.DrawString(" - " + cab.ToString(), e.Font, br, x, y); + y += cyFont; + } + } + + private void buttonCopyTrigger_Click(object sender, System.EventArgs e) { + int n = listBoxTriggers.SelectedIndex; + if (n < 0) + return; + Trigger[] atgr = m_tgrm.GetTriggerList(GetSideSelected()); + Trigger tgr = atgr[n].Clone(); + m_tgrm.AddTrigger(tgr); + InitTriggersListBox(tgr); + } + + private void buttonDeleteTrigger_Click(object sender, System.EventArgs e) { + int n = listBoxTriggers.SelectedIndex; + if (n < 0) + return; + Trigger[] atgr = m_tgrm.GetTriggerList(GetSideSelected()); + m_tgrm.RemoveTrigger(atgr[n]); + InitSidesListBox(null); + InitTriggersListBox(null); + } + + private void buttonLoadTriggers_Click(object sender, System.EventArgs e) { + + } + + private void buttonSaveTriggers_Click(object sender, System.EventArgs e) { + + } + + private void buttonMoveUpTrigger_Click(object sender, System.EventArgs e) { + int n = listBoxTriggers.SelectedIndex; + if (n <= 0) + return; + Trigger[] atgr = m_tgrm.GetTriggerList(GetSideSelected()); + m_tgrm.MoveUpTrigger(GetSideSelected(), atgr[n]); + InitTriggersListBox(atgr[n]); + } + + private void buttonMoveDownTrigger_Click(object sender, System.EventArgs e) { + int n = listBoxTriggers.SelectedIndex; + if (n < 0 || n >= listBoxTriggers.Items.Count - 1) + return; + Trigger[] atgr = m_tgrm.GetTriggerList(GetSideSelected()); + m_tgrm.MoveDownTrigger(GetSideSelected(), atgr[n]); + InitTriggersListBox(atgr[n]); + } + + void EnableButtons() { + int n = listBoxTriggers.SelectedIndex; + bool fSelected = (n >= 0); + buttonModifyTrigger.Enabled = fSelected; + buttonCopyTrigger.Enabled = fSelected; + buttonDeleteTrigger.Enabled = fSelected; + buttonMoveUpTrigger.Enabled = (n > 0); + buttonMoveDownTrigger.Enabled = (n < listBoxTriggers.Items.Count - 1); + buttonLoadTriggers.Enabled = false; // (listBoxTriggers.Items.Count != 0); + buttonSaveTriggers.Enabled = false; // (listBoxTriggers.Items.Count != 0); + } + + private void listBoxTriggers_SelectedIndexChanged(object sender, System.EventArgs e) { + EnableButtons(); + } + + private void listBoxSides_SelectedIndexChanged(object sender, System.EventArgs e) { + InitTriggersListBox(null); + } + + private void listBoxTriggers_DoubleClick(object sender, System.EventArgs e) { + buttonModifyTrigger_Click(sender, e); + } + + protected override void OnClosed(System.EventArgs e) { + s_rcBounds = Bounds; + base.OnClosed(e); + } + } +} diff --git a/m/TriggersForm.resources b/m/TriggersForm.resources new file mode 100644 index 0000000..e95500f Binary files /dev/null and b/m/TriggersForm.resources differ diff --git a/m/TriggersForm.resx b/m/TriggersForm.resx new file mode 100644 index 0000000..cb249ee --- /dev/null +++ b/m/TriggersForm.resx @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + TriggersForm + + \ No newline at end of file diff --git a/m/UnitGroup.cs b/m/UnitGroup.cs new file mode 100644 index 0000000..eb05ef3 --- /dev/null +++ b/m/UnitGroup.cs @@ -0,0 +1,369 @@ +using System; +using System.Collections; +using SpiffLib; +using System.Runtime.Serialization; +using System.Text.RegularExpressions; + +namespace m { + + [Serializable] + public class UnitTypeAndCount : ICloneable { + public UnitType ut; + public int c; + + public UnitTypeAndCount() { + this.ut = UnitType.kutNone; + this.c = 0; + } + + public UnitTypeAndCount(UnitType ut, int c) { + this.ut = ut; + this.c = c; + } + + override public string ToString() { + string str = Helper.GetDisplayName(typeof(UnitType), ut.ToString()); + return str + ": " + c; + } + + public string ToSaveString() { + return ut.ToString() + "," + c; + } + + public string FromSaveString(string strArg) { + Regex re = new Regex(@"^(?\d+),(?\d+)(?.*)$"); + Match m = re.Match(strArg); + this.ut = (UnitType)int.Parse(m.Groups["ut"].Value); + this.c = int.Parse(m.Groups["c"].Value); + return m.Groups["end"].Value; + } + + public object Clone() { + UnitTypeAndCount utc = new UnitTypeAndCount(ut, c); + return utc; + } + } + + [Serializable] + public class UnitGroup : ISerializable { + bool m_fLoopForever; + bool m_fRandomGroup; + bool m_fSpawn; + bool m_fCreateAtLevelLoad; + bool m_fReplaceDestroyedGroup; + int m_nHealth; + string m_strSpawnArea; + Side m_side; + Aggressiveness m_aggr; + string m_strName; + ArrayList m_alsActions; + ArrayList m_alsUnitTypeAndCounts; + + public UnitGroup(string strName) { + m_strName = strName; + m_side = Side.side2; + m_aggr = Aggressiveness.Defender; + m_alsActions = new ArrayList(); + m_alsUnitTypeAndCounts = new ArrayList(); + m_fLoopForever = false; + m_fRandomGroup = false; + m_fSpawn = false; + m_fCreateAtLevelLoad = false; + m_fReplaceDestroyedGroup = false; + m_strSpawnArea = null; + m_nHealth = 100; + } + + // ISerializable methods for backwards compatibility + + private UnitGroup(SerializationInfo info, StreamingContext context) { + m_strName = info.GetString("m_strName"); + m_side = (Side)info.GetValue("m_side", typeof(Side)); + m_alsActions = (ArrayList)info.GetValue("m_alsActions", typeof(ArrayList)); + m_alsUnitTypeAndCounts = (ArrayList)info.GetValue("m_alsUnitTypeAndCounts", typeof(ArrayList)); + + try { + m_fLoopForever = info.GetBoolean("m_fLoopForever"); + } catch (SerializationException) { + m_fLoopForever = false; + } + + try { + m_aggr = (Aggressiveness)info.GetValue("m_aggr", typeof(Aggressiveness)); + } catch (SerializationException) { + m_aggr = Aggressiveness.Defender; + } + + try { + m_fRandomGroup = info.GetBoolean("m_fRandomGroup"); + m_fSpawn = info.GetBoolean("m_fSpawn"); + m_fCreateAtLevelLoad = info.GetBoolean("m_fCreateAtLevelLoad"); + } catch (SerializationException) { + m_fRandomGroup = false; + m_fSpawn = false; + m_fCreateAtLevelLoad = false; + } + + try { + m_fReplaceDestroyedGroup = info.GetBoolean("m_fReplaceDestroyedGroup"); + } catch (SerializationException) { + try { + m_fReplaceDestroyedGroup = info.GetBoolean("m_fReplaceDestroyedUnits"); + } catch (SerializationException) { + m_fReplaceDestroyedGroup = false; + } + } + + try { + m_strSpawnArea = info.GetString("m_strSpawnArea"); + } catch (SerializationException) { + m_strSpawnArea = null; + } + + try { + m_nHealth = info.GetInt32("m_nHealth"); + } catch (SerializationException) { + m_nHealth = 100; + } + } + + public void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { + info.AddValue("m_strName", m_strName); + info.AddValue("m_side", m_side); + info.AddValue("m_alsActions", m_alsActions); + info.AddValue("m_alsUnitTypeAndCounts", m_alsUnitTypeAndCounts); + info.AddValue("m_fLoopForever", m_fLoopForever); + info.AddValue("m_aggr", m_aggr); + info.AddValue("m_fRandomGroup", m_fRandomGroup); + info.AddValue("m_fSpawn", m_fSpawn); + info.AddValue("m_fCreateAtLevelLoad", m_fCreateAtLevelLoad); + info.AddValue("m_fReplaceDestroyedGroup", m_fReplaceDestroyedGroup); + info.AddValue("m_strSpawnArea", m_strSpawnArea); + info.AddValue("m_nHealth", m_nHealth); + } + + public virtual UnitGroup Clone() { + UnitGroup ug = new UnitGroup(m_strName); + ug.m_side = m_side; + ug.m_aggr = m_aggr; + ug.m_fLoopForever = m_fLoopForever; + ug.m_fRandomGroup = m_fRandomGroup; + ug.m_fSpawn = m_fSpawn; + ug.m_fCreateAtLevelLoad = m_fCreateAtLevelLoad; + ug.m_fReplaceDestroyedGroup = m_fReplaceDestroyedGroup; + ug.m_strSpawnArea = (string)m_strSpawnArea.Clone(); + ug.m_nHealth = m_nHealth; + foreach (UnitTypeAndCount utc in UnitTypeAndCounts) + ug.UnitTypeAndCounts.Add(utc.Clone()); + foreach (CaBase cab in Actions) + ug.Actions.Add(cab.Clone()); + return ug; + } + + public string Name { + get { + return m_strName; + } + set { + m_strName = value; + } + } + + public Side Side { + get { + return m_side; + } + set { + m_side = value; + } + } + + public Aggressiveness Aggressiveness { + get { + return m_aggr; + } + set { + m_aggr = value; + } + } + + public ArrayList Actions { + get { + return m_alsActions; + } + } + + public ArrayList UnitTypeAndCounts { + get { + return m_alsUnitTypeAndCounts; + } + } + + public bool LoopForever { + get { + return m_fLoopForever; + } + set { + m_fLoopForever = value; + } + } + + public bool RandomGroup { + get { + return m_fRandomGroup; + } + set { + m_fRandomGroup = value; + } + } + + public bool Spawn { + get { + return m_fSpawn; + } + set { + m_fSpawn = value; + } + } + + public bool CreateAtLevelLoad { + get { + return m_fCreateAtLevelLoad; + } + set { + m_fCreateAtLevelLoad = value; + } + } + + public bool ReplaceDestroyedGroup { + get { + return m_fReplaceDestroyedGroup; + } + set { + m_fReplaceDestroyedGroup = value; + } + } + + public string SpawnArea { + get { + return m_strSpawnArea; + } + set { + m_strSpawnArea = value; + } + } + + public int Health { + get { + return m_nHealth; + } + set { + m_nHealth = value; + } + } + + public void AddIniProperties(Ini.Section sec) { + // Save Name + + sec.Add(new Ini.Property("Name", Name)); + + // Save Side + + sec.Add(new Ini.Property("Side", "k" + m_side.ToString())); + + // Save Aggressiveness + + sec.Add(new Ini.Property("Aggressiveness", "knAggressiveness" + m_aggr.ToString())); + + // Save flags + + sec.Add(new Ini.Property("LoopForever", m_fLoopForever ? "1" : "0")); + sec.Add(new Ini.Property("CreateAtLevelLoad", m_fCreateAtLevelLoad ? "1" : "0")); + sec.Add(new Ini.Property("RandomGroup", m_fRandomGroup ? "1" : "0")); + sec.Add(new Ini.Property("Spawn", m_fSpawn ? "1" : "0")); + sec.Add(new Ini.Property("ReplaceGroup", m_fReplaceDestroyedGroup ? "1" : "0")); + + // Save SpawnArea + + int nSpawnArea = CaTypeArea.GetArea(m_strSpawnArea); + if (nSpawnArea != -1) + sec.Add(new Ini.Property("SpawnArea", nSpawnArea.ToString())); + + // Save Health + + sec.Add(new Ini.Property("Health", Health.ToString())); + + // Save unit list + + if (m_alsUnitTypeAndCounts.Count > 0) { + + // Write total # of units + + int cTotalUnits = 0; + foreach (UnitTypeAndCount utc in m_alsUnitTypeAndCounts) + cTotalUnits += utc.c; + string str = cTotalUnits.ToString(); + + // Write unit/count pairs + + foreach (UnitTypeAndCount utc in m_alsUnitTypeAndCounts) + str += "," + utc.ToSaveString(); + + sec.Add(new Ini.Property("Units", str)); + } + + // Save actions + + foreach (CaBase cab in m_alsActions) { + if (!(cab is CommentUnitGroupAction)) + sec.Add(new Ini.Property("A", cab.ToSaveString())); + } + } + + public static UnitGroup FromIniSection(Ini.Section sec) { + UnitGroup ug = new UnitGroup(sec["Name"].Value); + ug.Side = (Side)int.Parse(sec["Side"].Value); + ug.Aggressiveness = (Aggressiveness)int.Parse(sec["Aggressiveness"].Value); + ug.LoopForever = (int.Parse(sec["LoopForever"].Value) != 0); + ug.CreateAtLevelLoad = (int.Parse(sec["CreateAtLevelLoad"].Value) != 0); + ug.RandomGroup = (int.Parse(sec["RandomGroup"].Value) != 0); + ug.Spawn = (int.Parse(sec["Spawn"].Value) != 0); + ug.ReplaceDestroyedGroup = (int.Parse(sec["ReplaceGroup"].Value) != 0); + if (sec["SpawnArea"] != null) { + ug.SpawnArea = CaTypeArea.GetAreaNameFromIndex(int.Parse(sec["SpawnArea"].Value)); + } + ug.Health = int.Parse(sec["Health"].Value); + + // Units + + string strUTC = null; + if (sec["Units"] != null) { + strUTC = sec["Units"].Value; + } + if (strUTC != null) { + Regex re = new Regex(@"^(?\d+),(?.*)$"); + Match m = re.Match(strUTC); + string strT = m.Groups["end"].Value; + while (strT.Length != 0) { + UnitTypeAndCount utc = new UnitTypeAndCount(); + strT = utc.FromSaveString(strT); + ug.UnitTypeAndCounts.Add(utc); + re = new Regex(@"^\s*,(?.*)$"); + m = re.Match(strT); + strT = m.Groups["end"].Value; + } + } + + // UnitGroup actions + + foreach (Ini.Property prop in sec.Properties) { + if (prop.Name != "A") { + continue; + } + CaBase cab = UnitGroupActionLoader.LoadIni(prop.Value); + ug.Actions.Add(cab); + } + return ug; + } + } +} diff --git a/m/UnitGroupManager.cs b/m/UnitGroupManager.cs new file mode 100644 index 0000000..8ec3877 --- /dev/null +++ b/m/UnitGroupManager.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections; +using SpiffLib; + +namespace m +{ + [Serializable] + public class UnitGroupManager + { + ArrayList m_alsUnitGroups; + bool m_fModified; + + public UnitGroupManager() + { + m_fModified = false; + m_alsUnitGroups = new ArrayList(); + } + + public ArrayList Items { + get { + return m_alsUnitGroups; + } + } + + public void SetModified() { + m_fModified = true; + } + + public void ClearModified() { + m_fModified = false; + } + + public bool IsModified() { + return m_fModified; + } + + public UnitGroup[] GetUnitGroupList() { + return (UnitGroup[])m_alsUnitGroups.ToArray(typeof(UnitGroup)); + } + + public void AddUnitGroup(UnitGroup ug) { + m_alsUnitGroups.Add(ug); + SetModified(); + } + + public void RemoveUnitGroup(UnitGroup ug) { + m_alsUnitGroups.Remove(ug); + SetModified(); + } + + public void ModifyUnitGroup(UnitGroup ugModify, UnitGroup ug) { + int n = m_alsUnitGroups.IndexOf(ugModify); + if (n >= 0) + m_alsUnitGroups[n] = ug; + SetModified(); + } + + public void SaveIni(Ini ini) { + for (int i = 0; i < m_alsUnitGroups.Count; i++) { + Ini.Section sec = new Ini.Section("UnitGroup " + i); + ((UnitGroup)m_alsUnitGroups[i]).AddIniProperties(sec); + ini.Add(sec); + } + } + + public void LoadIni(Ini ini) { + for (int index = 0; true; index++) { + Ini.Section sec = ini["UnitGroup " + index]; + if (sec == null) { + break; + } + UnitGroup ug = UnitGroup.FromIniSection(sec); + AddUnitGroup(ug); + } + } + } +} diff --git a/m/UnitGroupsForm.cs b/m/UnitGroupsForm.cs new file mode 100644 index 0000000..e4e20f3 --- /dev/null +++ b/m/UnitGroupsForm.cs @@ -0,0 +1,812 @@ +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; +using System.Collections.Specialized; + +namespace m +{ + /// + /// Summary description for UnitGroupsForm. + /// + public class UnitGroupsForm : System.Windows.Forms.Form + { + private UnitGroupManager m_ugm; + private UnitGroup m_ugSelected; + private LevelDoc m_lvld; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.Button buttonMoveUpAction; + private System.Windows.Forms.Button buttonDeleteAction; + private System.Windows.Forms.Button buttonCopyAction; + private System.Windows.Forms.Button buttonModifyAction; + private System.Windows.Forms.Button buttonNewAction; + private System.Windows.Forms.CheckedListBox checkedListBoxActions; + private System.Windows.Forms.Button buttonMoveDownAction; + private System.Windows.Forms.GroupBox groupBox2; + private System.Windows.Forms.ListView listViewUnitGroups; + private System.Windows.Forms.Button buttonNewUnitGroup; + private System.Windows.Forms.Button buttonDeleteUnitGroup; + private System.Windows.Forms.ListBox listBoxUnits; + private System.Windows.Forms.Button buttonDeleteUnits; + private System.Windows.Forms.Button buttonNewUnits; + private System.Windows.Forms.Button buttonClose; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.ComboBox comboBoxSide; + private System.Windows.Forms.CheckBox checkBoxForever; + private System.Windows.Forms.ComboBox comboBoxAggressiveness; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.CheckBox checkBoxRandomGroup; + private System.Windows.Forms.CheckBox checkBoxCreateAtLevelLoad; + private System.Windows.Forms.CheckBox checkBoxReplaceDestroyedGroup; + private System.Windows.Forms.CheckBox checkBoxSpawn; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.ComboBox comboBoxSpawnArea; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.TextBox textBoxHealth; + private System.Windows.Forms.Button buttonAddUnit; + private System.Windows.Forms.Button buttonSubtractUnit; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + public UnitGroupsForm(LevelDoc lvld, UnitGroupManager ugm) + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + // + + m_ugm = ugm; + m_lvld = lvld; + m_ugm.ClearModified(); + InitUnitGroupsListBox(); + InitSideComboBox(); + InitAggressivenessComboBox(); + InitSpawnAreaComboBox(); + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.checkBoxForever = new System.Windows.Forms.CheckBox(); + this.buttonMoveUpAction = new System.Windows.Forms.Button(); + this.buttonDeleteAction = new System.Windows.Forms.Button(); + this.buttonCopyAction = new System.Windows.Forms.Button(); + this.buttonModifyAction = new System.Windows.Forms.Button(); + this.buttonNewAction = new System.Windows.Forms.Button(); + this.checkedListBoxActions = new System.Windows.Forms.CheckedListBox(); + this.buttonMoveDownAction = new System.Windows.Forms.Button(); + this.buttonClose = new System.Windows.Forms.Button(); + this.groupBox2 = new System.Windows.Forms.GroupBox(); + this.buttonDeleteUnits = new System.Windows.Forms.Button(); + this.buttonNewUnits = new System.Windows.Forms.Button(); + this.listBoxUnits = new System.Windows.Forms.ListBox(); + this.buttonNewUnitGroup = new System.Windows.Forms.Button(); + this.buttonDeleteUnitGroup = new System.Windows.Forms.Button(); + this.listViewUnitGroups = new System.Windows.Forms.ListView(); + this.label1 = new System.Windows.Forms.Label(); + this.comboBoxSide = new System.Windows.Forms.ComboBox(); + this.comboBoxAggressiveness = new System.Windows.Forms.ComboBox(); + this.label2 = new System.Windows.Forms.Label(); + this.checkBoxRandomGroup = new System.Windows.Forms.CheckBox(); + this.checkBoxCreateAtLevelLoad = new System.Windows.Forms.CheckBox(); + this.checkBoxReplaceDestroyedGroup = new System.Windows.Forms.CheckBox(); + this.checkBoxSpawn = new System.Windows.Forms.CheckBox(); + this.label3 = new System.Windows.Forms.Label(); + this.comboBoxSpawnArea = new System.Windows.Forms.ComboBox(); + this.label4 = new System.Windows.Forms.Label(); + this.textBoxHealth = new System.Windows.Forms.TextBox(); + this.buttonAddUnit = new System.Windows.Forms.Button(); + this.buttonSubtractUnit = new System.Windows.Forms.Button(); + this.groupBox1.SuspendLayout(); + this.groupBox2.SuspendLayout(); + this.SuspendLayout(); + // + // groupBox1 + // + this.groupBox1.Controls.AddRange(new System.Windows.Forms.Control[] { + this.checkBoxForever, + this.buttonMoveUpAction, + this.buttonDeleteAction, + this.buttonCopyAction, + this.buttonModifyAction, + this.buttonNewAction, + this.checkedListBoxActions, + this.buttonMoveDownAction}); + this.groupBox1.Location = new System.Drawing.Point(152, 240); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(488, 280); + this.groupBox1.TabIndex = 1; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "Actions"; + // + // checkBoxForever + // + this.checkBoxForever.Location = new System.Drawing.Point(400, 160); + this.checkBoxForever.Name = "checkBoxForever"; + this.checkBoxForever.Size = new System.Drawing.Size(64, 32); + this.checkBoxForever.TabIndex = 23; + this.checkBoxForever.Text = "Repeat forever"; + this.checkBoxForever.CheckedChanged += new System.EventHandler(this.checkBoxForever_CheckedChanged); + // + // buttonMoveUpAction + // + this.buttonMoveUpAction.Location = new System.Drawing.Point(8, 248); + this.buttonMoveUpAction.Name = "buttonMoveUpAction"; + this.buttonMoveUpAction.Size = new System.Drawing.Size(152, 23); + this.buttonMoveUpAction.TabIndex = 22; + this.buttonMoveUpAction.Text = "Move Up"; + this.buttonMoveUpAction.Click += new System.EventHandler(this.buttonMoveUpAction_Click); + // + // buttonDeleteAction + // + this.buttonDeleteAction.Location = new System.Drawing.Point(400, 112); + this.buttonDeleteAction.Name = "buttonDeleteAction"; + this.buttonDeleteAction.TabIndex = 20; + this.buttonDeleteAction.Text = "Delete"; + this.buttonDeleteAction.Click += new System.EventHandler(this.buttonDeleteAction_Click); + // + // buttonCopyAction + // + this.buttonCopyAction.Location = new System.Drawing.Point(400, 80); + this.buttonCopyAction.Name = "buttonCopyAction"; + this.buttonCopyAction.TabIndex = 19; + this.buttonCopyAction.Text = "Copy"; + this.buttonCopyAction.Click += new System.EventHandler(this.buttonCopyAction_Click); + // + // buttonModifyAction + // + this.buttonModifyAction.Location = new System.Drawing.Point(400, 48); + this.buttonModifyAction.Name = "buttonModifyAction"; + this.buttonModifyAction.TabIndex = 18; + this.buttonModifyAction.Text = "Modify..."; + this.buttonModifyAction.Click += new System.EventHandler(this.buttonModifyAction_Click); + // + // buttonNewAction + // + this.buttonNewAction.Location = new System.Drawing.Point(400, 16); + this.buttonNewAction.Name = "buttonNewAction"; + this.buttonNewAction.TabIndex = 17; + this.buttonNewAction.Text = "New..."; + this.buttonNewAction.Click += new System.EventHandler(this.buttonNewAction_Click); + // + // checkedListBoxActions + // + this.checkedListBoxActions.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0))); + this.checkedListBoxActions.Location = new System.Drawing.Point(8, 16); + this.checkedListBoxActions.Name = "checkedListBoxActions"; + this.checkedListBoxActions.Size = new System.Drawing.Size(384, 225); + this.checkedListBoxActions.TabIndex = 15; + this.checkedListBoxActions.SelectedIndexChanged += new System.EventHandler(this.checkedListBoxActions_SelectedIndexChanged); + // + // buttonMoveDownAction + // + this.buttonMoveDownAction.Location = new System.Drawing.Point(168, 248); + this.buttonMoveDownAction.Name = "buttonMoveDownAction"; + this.buttonMoveDownAction.Size = new System.Drawing.Size(152, 23); + this.buttonMoveDownAction.TabIndex = 21; + this.buttonMoveDownAction.Text = "Move Down"; + this.buttonMoveDownAction.Click += new System.EventHandler(this.buttonMoveDownAction_Click); + // + // buttonClose + // + this.buttonClose.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.buttonClose.Location = new System.Drawing.Point(552, 8); + this.buttonClose.Name = "buttonClose"; + this.buttonClose.TabIndex = 3; + this.buttonClose.Text = "Close"; + this.buttonClose.Click += new System.EventHandler(this.buttonClose_Click); + // + // groupBox2 + // + this.groupBox2.Controls.AddRange(new System.Windows.Forms.Control[] { + this.buttonAddUnit, + this.buttonDeleteUnits, + this.buttonNewUnits, + this.listBoxUnits, + this.buttonSubtractUnit}); + this.groupBox2.Location = new System.Drawing.Point(152, 104); + this.groupBox2.Name = "groupBox2"; + this.groupBox2.Size = new System.Drawing.Size(488, 128); + this.groupBox2.TabIndex = 4; + this.groupBox2.TabStop = false; + this.groupBox2.Text = "Units"; + // + // buttonDeleteUnits + // + this.buttonDeleteUnits.Location = new System.Drawing.Point(400, 48); + this.buttonDeleteUnits.Name = "buttonDeleteUnits"; + this.buttonDeleteUnits.TabIndex = 2; + this.buttonDeleteUnits.Text = "Delete"; + this.buttonDeleteUnits.Click += new System.EventHandler(this.buttonDeleteUnits_Click); + // + // buttonNewUnits + // + this.buttonNewUnits.Location = new System.Drawing.Point(400, 16); + this.buttonNewUnits.Name = "buttonNewUnits"; + this.buttonNewUnits.TabIndex = 1; + this.buttonNewUnits.Text = "New..."; + this.buttonNewUnits.Click += new System.EventHandler(this.buttonNewUnits_Click); + // + // listBoxUnits + // + this.listBoxUnits.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0))); + this.listBoxUnits.ItemHeight = 16; + this.listBoxUnits.Location = new System.Drawing.Point(8, 16); + this.listBoxUnits.Name = "listBoxUnits"; + this.listBoxUnits.Size = new System.Drawing.Size(384, 100); + this.listBoxUnits.TabIndex = 0; + this.listBoxUnits.SelectedIndexChanged += new System.EventHandler(this.listBoxUnits_SelectedIndexChanged); + // + // buttonNewUnitGroup + // + this.buttonNewUnitGroup.Location = new System.Drawing.Point(16, 456); + this.buttonNewUnitGroup.Name = "buttonNewUnitGroup"; + this.buttonNewUnitGroup.TabIndex = 7; + this.buttonNewUnitGroup.Text = "New..."; + this.buttonNewUnitGroup.Click += new System.EventHandler(this.buttonNewUnitGroup_Click); + // + // buttonDeleteUnitGroup + // + this.buttonDeleteUnitGroup.Location = new System.Drawing.Point(16, 488); + this.buttonDeleteUnitGroup.Name = "buttonDeleteUnitGroup"; + this.buttonDeleteUnitGroup.TabIndex = 8; + this.buttonDeleteUnitGroup.Text = "Delete"; + this.buttonDeleteUnitGroup.Click += new System.EventHandler(this.buttonDeleteUnitGroup_Click); + // + // listViewUnitGroups + // + this.listViewUnitGroups.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0))); + this.listViewUnitGroups.FullRowSelect = true; + this.listViewUnitGroups.HideSelection = false; + this.listViewUnitGroups.LabelEdit = true; + this.listViewUnitGroups.Location = new System.Drawing.Point(8, 8); + this.listViewUnitGroups.MultiSelect = false; + this.listViewUnitGroups.Name = "listViewUnitGroups"; + this.listViewUnitGroups.Size = new System.Drawing.Size(136, 440); + this.listViewUnitGroups.Sorting = System.Windows.Forms.SortOrder.Ascending; + this.listViewUnitGroups.TabIndex = 9; + this.listViewUnitGroups.View = System.Windows.Forms.View.List; + this.listViewUnitGroups.AfterLabelEdit += new System.Windows.Forms.LabelEditEventHandler(this.listViewUnitGroups_AfterLabelEdit); + this.listViewUnitGroups.SelectedIndexChanged += new System.EventHandler(this.listViewUnitGroups_SelectedIndexChanged); + // + // label1 + // + this.label1.Location = new System.Drawing.Point(152, 8); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(32, 16); + this.label1.TabIndex = 10; + this.label1.Text = "Side:"; + // + // comboBoxSide + // + this.comboBoxSide.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comboBoxSide.Location = new System.Drawing.Point(184, 7); + this.comboBoxSide.Name = "comboBoxSide"; + this.comboBoxSide.Size = new System.Drawing.Size(80, 21); + this.comboBoxSide.TabIndex = 11; + this.comboBoxSide.SelectedIndexChanged += new System.EventHandler(this.comboBoxSide_SelectedIndexChanged); + // + // comboBoxAggressiveness + // + this.comboBoxAggressiveness.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comboBoxAggressiveness.Location = new System.Drawing.Point(368, 7); + this.comboBoxAggressiveness.Name = "comboBoxAggressiveness"; + this.comboBoxAggressiveness.Size = new System.Drawing.Size(80, 21); + this.comboBoxAggressiveness.TabIndex = 13; + this.comboBoxAggressiveness.SelectedIndexChanged += new System.EventHandler(this.comboBoxAggressiveness_SelectedIndexChanged); + // + // label2 + // + this.label2.Location = new System.Drawing.Point(280, 8); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(88, 16); + this.label2.TabIndex = 12; + this.label2.Text = "Aggressiveness:"; + // + // checkBoxRandomGroup + // + this.checkBoxRandomGroup.Location = new System.Drawing.Point(152, 32); + this.checkBoxRandomGroup.Name = "checkBoxRandomGroup"; + this.checkBoxRandomGroup.Size = new System.Drawing.Size(104, 16); + this.checkBoxRandomGroup.TabIndex = 14; + this.checkBoxRandomGroup.Text = "Random Group"; + this.checkBoxRandomGroup.CheckedChanged += new System.EventHandler(this.checkBoxRandomGroup_CheckedChanged); + // + // checkBoxCreateAtLevelLoad + // + this.checkBoxCreateAtLevelLoad.Location = new System.Drawing.Point(264, 32); + this.checkBoxCreateAtLevelLoad.Name = "checkBoxCreateAtLevelLoad"; + this.checkBoxCreateAtLevelLoad.Size = new System.Drawing.Size(128, 16); + this.checkBoxCreateAtLevelLoad.TabIndex = 15; + this.checkBoxCreateAtLevelLoad.Text = "Create at level load"; + this.checkBoxCreateAtLevelLoad.CheckedChanged += new System.EventHandler(this.checkBoxCreateAtLevelLoad_CheckedChanged); + // + // checkBoxReplaceDestroyedGroup + // + this.checkBoxReplaceDestroyedGroup.Location = new System.Drawing.Point(392, 32); + this.checkBoxReplaceDestroyedGroup.Name = "checkBoxReplaceDestroyedGroup"; + this.checkBoxReplaceDestroyedGroup.Size = new System.Drawing.Size(152, 16); + this.checkBoxReplaceDestroyedGroup.TabIndex = 16; + this.checkBoxReplaceDestroyedGroup.Text = "Recreate if destroyed"; + this.checkBoxReplaceDestroyedGroup.CheckedChanged += new System.EventHandler(this.checkBoxReplaceDestroyedGroup_CheckedChanged); + // + // checkBoxSpawn + // + this.checkBoxSpawn.Location = new System.Drawing.Point(152, 56); + this.checkBoxSpawn.Name = "checkBoxSpawn"; + this.checkBoxSpawn.Size = new System.Drawing.Size(120, 16); + this.checkBoxSpawn.TabIndex = 17; + this.checkBoxSpawn.Text = "Spawn, don\'t build"; + this.checkBoxSpawn.CheckedChanged += new System.EventHandler(this.checkBoxSpawn_CheckedChanged); + // + // label3 + // + this.label3.Location = new System.Drawing.Point(272, 56); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(72, 15); + this.label3.TabIndex = 18; + this.label3.Text = "Spawn Area:"; + // + // comboBoxSpawnArea + // + this.comboBoxSpawnArea.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comboBoxSpawnArea.Location = new System.Drawing.Point(344, 54); + this.comboBoxSpawnArea.Name = "comboBoxSpawnArea"; + this.comboBoxSpawnArea.Size = new System.Drawing.Size(160, 21); + this.comboBoxSpawnArea.TabIndex = 19; + this.comboBoxSpawnArea.SelectedIndexChanged += new System.EventHandler(this.comboBoxSpawnArea_SelectedIndexChanged); + // + // label4 + // + this.label4.Location = new System.Drawing.Point(152, 80); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(64, 16); + this.label4.TabIndex = 20; + this.label4.Text = "Health (%):"; + // + // textBoxHealth + // + this.textBoxHealth.Location = new System.Drawing.Point(216, 78); + this.textBoxHealth.Name = "textBoxHealth"; + this.textBoxHealth.Size = new System.Drawing.Size(40, 20); + this.textBoxHealth.TabIndex = 21; + this.textBoxHealth.Text = "100"; + this.textBoxHealth.TextChanged += new System.EventHandler(this.textBoxHealth_TextChanged); + // + // buttonAddUnit + // + this.buttonAddUnit.Location = new System.Drawing.Point(408, 80); + this.buttonAddUnit.Name = "buttonAddUnit"; + this.buttonAddUnit.Size = new System.Drawing.Size(24, 23); + this.buttonAddUnit.TabIndex = 3; + this.buttonAddUnit.Text = "+"; + this.buttonAddUnit.Click += new System.EventHandler(this.buttonAddUnit_Click); + // + // buttonSubtractUnit + // + this.buttonSubtractUnit.Location = new System.Drawing.Point(440, 80); + this.buttonSubtractUnit.Name = "buttonSubtractUnit"; + this.buttonSubtractUnit.Size = new System.Drawing.Size(24, 23); + this.buttonSubtractUnit.TabIndex = 3; + this.buttonSubtractUnit.Text = "–"; + this.buttonSubtractUnit.Click += new System.EventHandler(this.buttonSubtractUnit_Click); + // + // UnitGroupsForm + // + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.CancelButton = this.buttonClose; + this.ClientSize = new System.Drawing.Size(650, 528); + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.textBoxHealth, + this.label4, + this.comboBoxSpawnArea, + this.label3, + this.checkBoxSpawn, + this.checkBoxReplaceDestroyedGroup, + this.checkBoxCreateAtLevelLoad, + this.checkBoxRandomGroup, + this.comboBoxAggressiveness, + this.label2, + this.comboBoxSide, + this.label1, + this.listViewUnitGroups, + this.buttonDeleteUnitGroup, + this.buttonNewUnitGroup, + this.groupBox2, + this.buttonClose, + this.groupBox1}); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "UnitGroupsForm"; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Unit Groups"; + this.Closed += new System.EventHandler(this.UnitGroupsForm_Closed); + this.groupBox1.ResumeLayout(false); + this.groupBox2.ResumeLayout(false); + this.ResumeLayout(false); + + } + #endregion + + // Action list management + + void InitActionsListBox(int nSelectedIndex) { + checkedListBoxActions.Items.Clear(); + if (m_ugSelected != null) { + foreach (CaBase cab in m_ugSelected.Actions) + checkedListBoxActions.Items.Add(cab, cab.Active); + checkedListBoxActions.SelectedIndex = nSelectedIndex; + if (checkedListBoxActions.SelectedIndex == -1 && checkedListBoxActions.Items.Count != 0) + checkedListBoxActions.SelectedIndex = 0; + } + EnableActionButtons(); + } + + private void buttonNewAction_Click(object sender, System.EventArgs e) { + CaBase cab = CaNew.DoModal(null, "New Action", "UnitGroupAction"); + if (cab != null) { + m_ugSelected.Actions.Add(cab); + InitActionsListBox(m_ugSelected.Actions.Count - 1); + } + } + + private void buttonModifyAction_Click(object sender, System.EventArgs e) { + int n = checkedListBoxActions.SelectedIndex; + if (n < 0) + return; + CaBase cab = (CaBase)m_ugSelected.Actions[n]; + cab = CaNew.DoModal(cab.Clone(), "Modify Action", "UnitGroupAction"); + if (cab != null) { + m_ugSelected.Actions[n] = cab; + InitActionsListBox(n); + } + } + + private void checkedListBoxActions_SelectedIndexChanged(object sender, System.EventArgs e) { + EnableActionButtons(); + } + + void EnableActionButtons() { + int n = checkedListBoxActions.SelectedIndex; + bool fItemSelected = (n >= 0); + buttonNewAction.Enabled = m_ugSelected != null; + buttonModifyAction.Enabled = fItemSelected; + buttonCopyAction.Enabled = fItemSelected; + buttonDeleteAction.Enabled = fItemSelected; + buttonMoveUpAction.Enabled = (n > 0); + buttonMoveDownAction.Enabled = (n < checkedListBoxActions.Items.Count - 1); + } + + private void buttonCopyAction_Click(object sender, System.EventArgs e) { + int n = checkedListBoxActions.SelectedIndex; + if (n < 0) + return; + CaBase cab = (CaBase)m_ugSelected.Actions[n]; + m_ugSelected.Actions.Add(cab.Clone()); + InitActionsListBox(m_ugSelected.Actions.Count - 1); + } + + private void buttonDeleteAction_Click(object sender, System.EventArgs e) { + int n = checkedListBoxActions.SelectedIndex; + if (n < 0) + return; + m_ugSelected.Actions.RemoveAt(n); + InitActionsListBox(Math.Min(m_ugSelected.Actions.Count - 1, n)); + } + + private void buttonMoveUpAction_Click(object sender, System.EventArgs e) { + int n = checkedListBoxActions.SelectedIndex; + if (n <= 0) + return; + CaBase cab = (CaBase)m_ugSelected.Actions[n]; + m_ugSelected.Actions.RemoveAt(n); + m_ugSelected.Actions.Insert(n - 1, cab); + InitActionsListBox(n - 1); + } + + private void buttonMoveDownAction_Click(object sender, System.EventArgs e) { + int n = checkedListBoxActions.SelectedIndex; + if (n < 0 || n >= checkedListBoxActions.Items.Count - 1) + return; + CaBase cab = (CaBase)m_ugSelected.Actions[n]; + m_ugSelected.Actions.RemoveAt(n); + m_ugSelected.Actions.Insert(n + 1, cab); + InitActionsListBox(n + 1); + } + + // UnitGroup management + + void InitUnitGroupsListBox() { + listViewUnitGroups.Items.Clear(); + UnitGroup[] aug = m_ugm.GetUnitGroupList(); + bool fFirst = true; + foreach (UnitGroup ug in aug) { + ListViewItem lvi = new ListViewItem(ug.Name); + lvi.Tag = ug; + if (fFirst) { + lvi.Selected = true; + fFirst = false; + } + listViewUnitGroups.Items.Add(lvi); + } + EnableActionButtons(); + EnableUnitsButtons(); + } + + private void listViewUnitGroups_AfterLabelEdit(object sender, System.Windows.Forms.LabelEditEventArgs e) { + if (e.Label != null && e.Label != "") + ((UnitGroup)listViewUnitGroups.Items[e.Item].Tag).Name = e.Label; + } + + private void buttonNewUnitGroup_Click(object sender, System.EventArgs e) { + int i = 1; + for (; i < 100; i++) { + bool fFound = false; + for (int j = 0; j < listViewUnitGroups.Items.Count; j++) { + if (listViewUnitGroups.Items[j].Text == "group" + i) { + fFound = true; + break; + } + } + if (!fFound) + break; + } + m_ugm.AddUnitGroup(new UnitGroup("group" + i)); + InitUnitGroupsListBox(); + SelectUnitGroup("group" + i); + } + + private void SelectUnitGroup(string str) { + for (int j = 0; j < listViewUnitGroups.Items.Count; j++) { + if (listViewUnitGroups.Items[j].Text == str ) { + listViewUnitGroups.Items[j].Selected = true; + break; + } + } + } + + private void buttonDeleteUnitGroup_Click(object sender, System.EventArgs e) { + if (listViewUnitGroups.SelectedItems.Count == 0) + return; + ListViewItem lvi = listViewUnitGroups.SelectedItems[0]; + listViewUnitGroups.Items.Remove(lvi); + m_ugm.RemoveUnitGroup((UnitGroup)lvi.Tag); + } + + private void listViewUnitGroups_SelectedIndexChanged(object sender, System.EventArgs e) { + if (listViewUnitGroups.SelectedItems.Count == 0) + m_ugSelected = null; + else + m_ugSelected = (UnitGroup)listViewUnitGroups.SelectedItems[0].Tag; + InitUnitGroupPanel(); + } + + private void InitUnitGroupPanel() { + InitUnitsListBox(-1); + InitActionsListBox(-1); + InitSideComboBox(); + InitAggressivenessComboBox(); + InitSpawnAreaComboBox(); + checkBoxForever.Checked = m_ugSelected == null ? false : m_ugSelected.LoopForever; + checkBoxRandomGroup.Checked = m_ugSelected == null ? false : m_ugSelected.RandomGroup; + checkBoxSpawn.Checked = m_ugSelected == null ? false : m_ugSelected.Spawn; + comboBoxSpawnArea.Enabled = m_ugSelected == null ? false : m_ugSelected.Spawn; + checkBoxCreateAtLevelLoad.Checked = m_ugSelected == null ? false : m_ugSelected.CreateAtLevelLoad; + checkBoxReplaceDestroyedGroup.Checked = m_ugSelected == null ? false : m_ugSelected.ReplaceDestroyedGroup; + textBoxHealth.Text = m_ugSelected == null ? "" : m_ugSelected.Health.ToString(); + } + + // Units list management + + void InitUnitsListBox(int nSelected) { + listBoxUnits.Items.Clear(); + if (m_ugSelected != null) { + + foreach (UnitTypeAndCount utc in m_ugSelected.UnitTypeAndCounts) + listBoxUnits.Items.Add(utc); + #if false + checkedListBoxUnits.SelectedIndex = nSelectedIndex; + if (checkedListBoxUnits.SelectedIndex == -1 && checkedListBoxUnits.Items.Count != 0) + checkedListBoxUnits.SelectedIndex = 0; + #endif + EnableUnitsButtons(); + if (nSelected != -1) + listBoxUnits.SelectedIndex = nSelected; + } + } + + void EnableUnitsButtons() { + int n = listBoxUnits.SelectedIndex; + bool fItemSelected = (n >= 0); + buttonNewUnits.Enabled = m_ugSelected != null; + buttonDeleteUnits.Enabled = fItemSelected; + } + + private void buttonNewUnits_Click(object sender, System.EventArgs e) { + UnitTypeAndCountForm frm = new UnitTypeAndCountForm(); + if (frm.ShowDialog(this) == DialogResult.Cancel) + return; + UnitTypeAndCount[] autc = (UnitTypeAndCount[])m_ugSelected.UnitTypeAndCounts.ToArray(typeof(UnitTypeAndCount)); + bool fDuplicate = false; + foreach (UnitTypeAndCount utc in autc) { + if (utc.ut == frm.UnitType) { + utc.c += frm.Count; + fDuplicate = true; + break; + } + } + int n = -1; + if (!fDuplicate) + n = m_ugSelected.UnitTypeAndCounts.Add(new UnitTypeAndCount(frm.UnitType, frm.Count)); + m_ugm.SetModified(); + InitUnitsListBox(n); + } + + private void buttonDeleteUnits_Click(object sender, System.EventArgs e) { + int n = listBoxUnits.SelectedIndex; + if (n >= 0) { + m_ugSelected.UnitTypeAndCounts.RemoveAt(n); + m_ugm.SetModified(); + InitUnitsListBox(-1); + } + } + + private void buttonAddUnit_Click(object sender, System.EventArgs e) { + int n = listBoxUnits.SelectedIndex; + if (n >= 0) { + UnitTypeAndCount uct = (UnitTypeAndCount)m_ugSelected.UnitTypeAndCounts[n]; + uct.c++; + m_ugm.SetModified(); + InitUnitsListBox(n); + } + } + + private void buttonSubtractUnit_Click(object sender, System.EventArgs e) { + int n = listBoxUnits.SelectedIndex; + if (n >= 0) { + UnitTypeAndCount uct = (UnitTypeAndCount)m_ugSelected.UnitTypeAndCounts[n]; + if (uct.c > 1) { + uct.c--; + m_ugm.SetModified(); + InitUnitsListBox(n); + } + } + } + + private void listBoxUnits_SelectedIndexChanged(object sender, System.EventArgs e) { + EnableUnitsButtons(); + } + + private void checkBoxForever_CheckedChanged(object sender, System.EventArgs e) { + if (m_ugSelected != null) + m_ugSelected.LoopForever = checkBoxForever.Checked; + } + + private void checkBoxCreateAtLevelLoad_CheckedChanged(object sender, System.EventArgs e) { + if (m_ugSelected != null) + m_ugSelected.CreateAtLevelLoad = checkBoxCreateAtLevelLoad.Checked; + } + + private void checkBoxReplaceDestroyedGroup_CheckedChanged(object sender, System.EventArgs e) { + if (m_ugSelected != null) + m_ugSelected.ReplaceDestroyedGroup = checkBoxReplaceDestroyedGroup.Checked; + } + + private void checkBoxRandomGroup_CheckedChanged(object sender, System.EventArgs e) { + if (m_ugSelected != null) + m_ugSelected.RandomGroup = checkBoxRandomGroup.Checked; + } + + private void checkBoxSpawn_CheckedChanged(object sender, System.EventArgs e) { + if (m_ugSelected != null) + m_ugSelected.Spawn = checkBoxSpawn.Checked; + comboBoxSpawnArea.Enabled = checkBoxSpawn.Checked; + } + + private void textBoxHealth_TextChanged(object sender, System.EventArgs e) { + if (m_ugSelected != null) { + try { + m_ugSelected.Health = int.Parse(textBoxHealth.Text); + } catch { + MessageBox.Show("Invalid Health value. Must be in the range from 0 to 100.", "M"); + textBoxHealth.Text = m_ugSelected.Health.ToString(); + } + } + } + + // Side combobox management + + void InitSideComboBox() { + comboBoxSide.Items.Clear(); + comboBoxSide.Items.Add(Helper.GetDisplayName(typeof(Side), "side1")); + comboBoxSide.Items.Add(Helper.GetDisplayName(typeof(Side), "side2")); + comboBoxSide.Items.Add(Helper.GetDisplayName(typeof(Side), "side3")); + comboBoxSide.Items.Add(Helper.GetDisplayName(typeof(Side), "side4")); + + if (m_ugSelected != null) + comboBoxSide.SelectedIndex = (int)m_ugSelected.Side - 1; + } + + private void comboBoxSide_SelectedIndexChanged(object sender, System.EventArgs e) { + if (m_ugSelected != null) + m_ugSelected.Side = (Side)(comboBoxSide.SelectedIndex + 1); + } + + // Aggressiveness combobox management + + void InitAggressivenessComboBox() { + comboBoxAggressiveness.Items.Clear(); + string[] astr = Enum.GetNames(typeof(Aggressiveness)); + foreach (string str in astr) + comboBoxAggressiveness.Items.Add(str); + + if (m_ugSelected != null) + comboBoxAggressiveness.SelectedIndex = (int)m_ugSelected.Aggressiveness; + } + + private void comboBoxAggressiveness_SelectedIndexChanged(object sender, System.EventArgs e) { + if (m_ugSelected != null) + m_ugSelected.Aggressiveness = (Aggressiveness)comboBoxAggressiveness.SelectedIndex; + } + + // Spawn Area combobox management + + void InitSpawnAreaComboBox() { + comboBoxSpawnArea.Items.Clear(); + StringCollection strc = CaTypeArea.GetAreaNames(); + for (int i = CaTypeArea.VirtualAreaCount; i < strc.Count; i++) + comboBoxSpawnArea.Items.Add(strc[i]); + + if (m_ugSelected != null) { + int i = -1; + if (m_ugSelected.SpawnArea != null) + i = comboBoxSpawnArea.Items.IndexOf(m_ugSelected.SpawnArea); + if (i == -1) + comboBoxSpawnArea.ResetText(); + else + comboBoxSpawnArea.SelectedIndex = i; + } + } + + private void comboBoxSpawnArea_SelectedIndexChanged(object sender, System.EventArgs e) { + if (m_ugSelected != null) + m_ugSelected.SpawnArea = (string)comboBoxSpawnArea.SelectedItem; + } + + private void buttonClose_Click(object sender, System.EventArgs e) { + Close(); + } + + private void UnitGroupsForm_Closed(object sender, System.EventArgs e) { + if (m_ugm.IsModified()) + m_lvld.SetModified(true); + } + } +} diff --git a/m/UnitGroupsForm.resources b/m/UnitGroupsForm.resources new file mode 100644 index 0000000..73af0f8 Binary files /dev/null and b/m/UnitGroupsForm.resources differ diff --git a/m/UnitGroupsForm.resx b/m/UnitGroupsForm.resx new file mode 100644 index 0000000..9c4609f --- /dev/null +++ b/m/UnitGroupsForm.resx @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + UnitGroupsForm + + \ No newline at end of file diff --git a/m/UnitTypeAndCountForm.cs b/m/UnitTypeAndCountForm.cs new file mode 100644 index 0000000..ba97090 --- /dev/null +++ b/m/UnitTypeAndCountForm.cs @@ -0,0 +1,185 @@ +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; + +namespace m +{ + /// + /// Summary description for UnitTypeAndCountForm. + /// + public class UnitTypeAndCountForm : System.Windows.Forms.Form + { + public UnitType m_ut; + public int m_c; + private System.Windows.Forms.Button buttonOK; + private System.Windows.Forms.Button buttonCancel; + private System.Windows.Forms.ListBox listBoxUnitTypes; + private System.Windows.Forms.NumericUpDown nudCount; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + public UnitTypeAndCountForm() + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + // + // TODO: Add any constructor code after InitializeComponent call + // + + InitUnitTypesListBox(); + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.listBoxUnitTypes = new System.Windows.Forms.ListBox(); + this.buttonOK = new System.Windows.Forms.Button(); + this.buttonCancel = new System.Windows.Forms.Button(); + this.nudCount = new System.Windows.Forms.NumericUpDown(); + ((System.ComponentModel.ISupportInitialize)(this.nudCount)).BeginInit(); + this.SuspendLayout(); + // + // listBoxUnitTypes + // + this.listBoxUnitTypes.Location = new System.Drawing.Point(8, 8); + this.listBoxUnitTypes.Name = "listBoxUnitTypes"; + this.listBoxUnitTypes.Size = new System.Drawing.Size(152, 160); + this.listBoxUnitTypes.TabIndex = 0; + this.listBoxUnitTypes.DoubleClick += new System.EventHandler(this.listBoxUnitTypes_DoubleClick); + this.listBoxUnitTypes.SelectedIndexChanged += new System.EventHandler(this.listBoxUnitTypes_SelectedIndexChanged); + // + // buttonOK + // + this.buttonOK.DialogResult = System.Windows.Forms.DialogResult.OK; + this.buttonOK.Enabled = false; + this.buttonOK.Location = new System.Drawing.Point(8, 208); + this.buttonOK.Name = "buttonOK"; + this.buttonOK.TabIndex = 1; + this.buttonOK.Text = "OK"; + this.buttonOK.Click += new System.EventHandler(this.buttonOK_Click); + // + // buttonCancel + // + this.buttonCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.buttonCancel.Location = new System.Drawing.Point(88, 208); + this.buttonCancel.Name = "buttonCancel"; + this.buttonCancel.TabIndex = 2; + this.buttonCancel.Text = "Cancel"; + // + // nudCount + // + this.nudCount.Location = new System.Drawing.Point(8, 176); + this.nudCount.Minimum = new System.Decimal(new int[] { + 1, + 0, + 0, + 0}); + this.nudCount.Name = "nudCount"; + this.nudCount.Size = new System.Drawing.Size(152, 20); + this.nudCount.TabIndex = 4; + this.nudCount.Value = new System.Decimal(new int[] { + 1, + 0, + 0, + 0}); + // + // UnitTypeAndCountForm + // + this.AcceptButton = this.buttonOK; + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.CancelButton = this.buttonCancel; + this.ClientSize = new System.Drawing.Size(170, 240); + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.nudCount, + this.buttonCancel, + this.buttonOK, + this.listBoxUnitTypes}); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "UnitTypeAndCountForm"; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Unit Type And Count"; + ((System.ComponentModel.ISupportInitialize)(this.nudCount)).EndInit(); + this.ResumeLayout(false); + + } + #endregion + + private void InitUnitTypesListBox() { + UnitMask um = UnitMask.kumMobileUnits | UnitMask.kumAndy | UnitMask.kumFox; + uint umT = 1; + for (int i = 0; i < 32; i++) { + if (((uint)um & umT) != 0) { + UnitType ut = (UnitType)i; + listBoxUnitTypes.Items.Add(new UnitTypeWrapper(ut)); + } + umT <<= 1; + } + } + + private void buttonOK_Click(object sender, System.EventArgs e) { + m_ut = ((UnitTypeWrapper)listBoxUnitTypes.SelectedItem).ut; + m_c = (int)nudCount.Value; + } + + private void listBoxUnitTypes_DoubleClick(object sender, System.EventArgs e) { + buttonOK_Click(sender, e); + DialogResult = DialogResult.OK; + } + + private void listBoxUnitTypes_SelectedIndexChanged(object sender, System.EventArgs e) { + buttonOK.Enabled = listBoxUnitTypes.SelectedIndex >= 0; + } + + public UnitType UnitType { + get { + return m_ut; + } + } + + public int Count { + get { + return m_c; + } + } + } + + public class UnitTypeWrapper { + public UnitType ut; + public UnitTypeWrapper(UnitType ut) { + this.ut = ut; + } + + override public string ToString() { + return Helper.GetDisplayName(typeof(UnitType), ut.ToString()); + } + } +} diff --git a/m/UnitTypeAndCountForm.resources b/m/UnitTypeAndCountForm.resources new file mode 100644 index 0000000..b93c0b3 Binary files /dev/null and b/m/UnitTypeAndCountForm.resources differ diff --git a/m/UnitTypeAndCountForm.resx b/m/UnitTypeAndCountForm.resx new file mode 100644 index 0000000..e22efef --- /dev/null +++ b/m/UnitTypeAndCountForm.resx @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + UnitTypeAndCountForm + + \ No newline at end of file diff --git a/m/Wall.cs b/m/Wall.cs new file mode 100644 index 0000000..0f719a5 --- /dev/null +++ b/m/Wall.cs @@ -0,0 +1,108 @@ +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Runtime.Serialization; +using SpiffLib; + +namespace m { + [Serializable] + public class Wall : MapItem, ISerializable { + private int m_nHealth; + private GobImage m_gimg; + + static private string[] s_astrBitmaps = { + "Wall00", + "Wall01", + "Wall02", + "Wall03", + "Wall04", + "Wall05", + "Wall06", + "Wall07", + "Wall08", + "Wall09", + "Wall10", + "Wall11", + "Wall12", + "Wall13", + "Wall14", + "Wall15", + }; + + public Wall(int nHealth) { + m_nHealth = nHealth; + m_gimg = Globals.GetGobImage(s_astrBitmaps[0], false); + } + + public Wall(int nHealth, int tx, int ty) { + m_nHealth = nHealth; + m_tx = tx; + m_ty = ty; + m_gimg = Globals.GetGobImage(s_astrBitmaps[0], false); + } + + public Wall(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { + m_nHealth = info.GetInt32("nHealth"); + m_gimg = Globals.GetGobImage(s_astrBitmaps[0], false); + } + + public override void GetObjectData(SerializationInfo info, StreamingContext context) { + base.GetObjectData(info, context); + info.AddValue("nHealth", m_nHealth); + } + + // IMapItem + + public override Bitmap GetBitmap(Size sizTile, TemplateDoc tmpd) { + Bitmap[] abm = m_gimg.GetBitmapSides(sizTile); + return abm[0]; + } + + public override Point GetCenterPoint(Size sizTile) { + Size sizGob = m_gimg.GetSize(sizTile); + return new Point((int)m_tx * sizTile.Width + sizGob.Width / 2, (int)m_ty * sizTile.Height + sizGob.Height / 2); + } + + public override Rectangle GetBoundingRectAt(int x, int y, Size sizTile, TemplateDoc tmpd) { + Size sizGob = m_gimg.GetSize(sizTile); + return new Rectangle(x, y, sizGob.Width, sizGob.Height); + } + + public override bool HitTest(int x, int y, Size sizTile, TemplateDoc tmpd) { + int xT = x - (int)m_tx * sizTile.Width; + int yT = y - (int)m_ty * sizTile.Height; + Size sizGob = m_gimg.GetSize(sizTile); + if (xT > 0 && xT < sizGob.Width && yT > 0 && yT < sizGob.Height) { + Bitmap[] abm = m_gimg.GetBitmapSides(sizTile); + return abm[0].GetPixel(xT, yT) != Color.Transparent; + } + return false; + } + + public override Object Clone() { + Object[] aobj = { m_nHealth, (int)m_tx, (int)m_ty }; + return (Object)System.Activator.CreateInstance(GetType(), aobj); + } + + public override void Draw(Graphics g, int x, int y, Size sizTile, TemplateDoc tmpd, LayerType layer, bool fSelected) { + if (layer == LayerType.Galaxite) { + Bitmap[] abm = m_gimg.GetBitmapSides(sizTile); + Bitmap bm = abm[0]; + if (fSelected) { + Rectangle rcDst = new Rectangle(x, y, bm.Width, bm.Height); + ImageAttributes attr = new ImageAttributes(); + attr.SetGamma(0.5f); + g.DrawImage(bm, rcDst, 0, 0, bm.Width, bm.Height, GraphicsUnit.Pixel, attr); + } else { + g.DrawImage(bm, x, y); + } + } + } + + public override Ini.Property GetIniProperty(int txOrigin, int tyOrigin) { + // For example: W=15,80,100 + return new Ini.Property("W", m_nHealth + "," + (m_tx - txOrigin).ToString() + "," + (m_ty - tyOrigin).ToString()); + } + } +} diff --git a/m/deploy.bat b/m/deploy.bat new file mode 100644 index 0000000..a106d66 --- /dev/null +++ b/m/deploy.bat @@ -0,0 +1,4 @@ +copy mcl\bin\debug\mcl.exe ..\bin +copy mgui\bin\debug\mgui.exe ..\bin\m.exe +copy bin\debug\m.dll ..\bin + diff --git a/m/deploy.sh b/m/deploy.sh new file mode 100755 index 0000000..30c8f5e --- /dev/null +++ b/m/deploy.sh @@ -0,0 +1,3 @@ +cp mcl/bin/debug/mcl.exe ../bin +cp mgui/bin/debug/mgui.exe ../bin/m.exe +cp bin/debug/m.dll ../bin diff --git a/m/editterrainform.cs b/m/editterrainform.cs new file mode 100644 index 0000000..239bb77 --- /dev/null +++ b/m/editterrainform.cs @@ -0,0 +1,192 @@ +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; +using SpiffLib; + +namespace m +{ + /// + /// Summary description for Terrain. + /// + public class EditTerrainForm : System.Windows.Forms.Form { + + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + private Brush[] m_abr = new Brush[s_aclrTerrain.Length]; + private System.Windows.Forms.Panel panel9; + private System.Windows.Forms.Button button1; + private m.FlowPanel panel1; + Size m_sizTile; + + static Color[] s_aclrTerrain = { + Color.FromArgb(0, 0, 0, 0), // Open + Color.FromArgb(100, 255, 0, 0), // Blocked + Color.FromArgb(100, 255, 255, 0), // Road + Color.FromArgb(100, 0, 255, 0) // Scrabble + }; + + public EditTerrainForm(TemplateDoc tmpd) { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + m_sizTile = tmpd.TileSize; + for (int n = 0; n < m_abr.Length; n++) + m_abr[n] = new SolidBrush(s_aclrTerrain[n]); + + Template[] atmpl = tmpd.GetTemplates(); + panel1.SuspendLayout(); + foreach (Template tmpl in atmpl) { + PictureBox picb = new PictureBox(); + picb.Image = ConstructTerrainBitmap(tmpl); + picb.SizeMode = PictureBoxSizeMode.AutoSize; + picb.Tag = (Object)tmpl; + picb.MouseDown += new MouseEventHandler(PictureBox_MouseDown); + panel1.Controls.Add(picb); + } + panel1.ResumeLayout(); + } + + private Bitmap ConstructTerrainBitmap(Template tmpl) { + Bitmap bm = new Bitmap(tmpl.Bitmap); + Graphics g = Graphics.FromImage(bm); + int ctx = tmpl.Bitmap.Width / m_sizTile.Width; + int cty = tmpl.Bitmap.Height / m_sizTile.Height; + for (int ty = 0; ty < cty; ty++) { + for (int tx = 0; tx < ctx; tx++) { + if (!tmpl.OccupancyMap[ty, tx]) + continue; + int n = (int)tmpl.TerrainMap[ty, tx]; + g.FillRectangle(m_abr[n], tx * m_sizTile.Width, ty * m_sizTile.Height, m_sizTile.Width, m_sizTile.Height); + bm.SetPixel(tx * m_sizTile.Width + m_sizTile.Width / 2, ty * m_sizTile.Height + m_sizTile.Height / 2, Color.White); + } + } + + return Misc.TraceEdges(bm, 1, Color.Azure); + } + + private void PictureBox_MouseDown(Object obj, MouseEventArgs args) { + PictureBox picb = (PictureBox)obj; + Rectangle rcBounds = picb.ClientRectangle; + rcBounds.Inflate(-1, -1); + if (!rcBounds.Contains(args.X, args.Y)) + return; + Template tmpl = (Template)picb.Tag; + if ((Control.ModifierKeys & Keys.Control) != Keys.Control) { + int tx = (args.X - 1) / m_sizTile.Width; + int ty = (args.Y - 1) / m_sizTile.Height; + if (!tmpl.OccupancyMap[ty, tx]) + return; + TerrainTypes ter = tmpl.TerrainMap[ty, tx]; + ter += 1; + if (ter == TerrainTypes.End) + ter = TerrainTypes.Start; + tmpl.TerrainMap[ty, tx] = ter; + } else { + TerrainTypes ter = TerrainTypes.Open; + for (int ty = 0; ty < tmpl.OccupancyMap.GetLength(0); ty++) { + for (int tx = 0; tx < tmpl.OccupancyMap.GetLength(1); tx++) { + if (tmpl.OccupancyMap[ty, tx]) { + ter = tmpl.TerrainMap[ty, tx]; + break; + } + } + } + + ter += 1; + if (ter == TerrainTypes.End) + ter = TerrainTypes.Start; + + for (int ty = 0; ty < tmpl.OccupancyMap.GetLength(0); ty++) { + for (int tx = 0; tx < tmpl.OccupancyMap.GetLength(1); tx++) { + if (tmpl.OccupancyMap[ty, tx]) { + tmpl.TerrainMap[ty, tx] = ter; + } + } + } + } + picb.Image = ConstructTerrainBitmap(tmpl); + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.panel1 = new m.FlowPanel(); + this.panel9 = new System.Windows.Forms.Panel(); + this.button1 = new System.Windows.Forms.Button(); + this.panel9.SuspendLayout(); + this.SuspendLayout(); + // + // panel1 + // + this.panel1.AutoScroll = true; + this.panel1.BackColor = System.Drawing.Color.Black; + this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(854, 564); + this.panel1.TabIndex = 1; + // + // panel9 + // + this.panel9.Controls.AddRange(new System.Windows.Forms.Control[] { + this.button1}); + this.panel9.Dock = System.Windows.Forms.DockStyle.Bottom; + this.panel9.Location = new System.Drawing.Point(0, 564); + this.panel9.Name = "panel9"; + this.panel9.Size = new System.Drawing.Size(854, 48); + this.panel9.TabIndex = 0; + // + // button1 + // + this.button1.Location = new System.Drawing.Point(8, 16); + this.button1.Name = "button1"; + this.button1.TabIndex = 0; + this.button1.Text = "Ok"; + this.button1.Click += new System.EventHandler(this.button1_Click); + // + // EditTerrainForm + // + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.ClientSize = new System.Drawing.Size(854, 612); + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.panel1, + this.panel9}); + this.Name = "EditTerrainForm"; + this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Show; + this.Text = "Edit Terrain"; + this.panel9.ResumeLayout(false); + this.ResumeLayout(false); + + } + #endregion + + private void button1_Click(object sender, System.EventArgs e) { + DialogResult = DialogResult.OK; + } + } +} diff --git a/m/editterrainform.resx b/m/editterrainform.resx new file mode 100644 index 0000000..35ccd1f --- /dev/null +++ b/m/editterrainform.resx @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + EditTerrainForm + + + text/microsoft-resx + + + 1.0.0.0 + + + System.Resources.ResXResourceReader + + + System.Resources.ResXResourceWriter + + \ No newline at end of file diff --git a/m/flowpanel.cs b/m/flowpanel.cs new file mode 100644 index 0000000..5876e0a --- /dev/null +++ b/m/flowpanel.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections; +using System.ComponentModel; +using System.Drawing; + +using System.Windows.Forms; + + +namespace m { + public class FlowPanel : Panel { + private System.ComponentModel.IContainer components = null; + public Size Spacing; + + public FlowPanel() { + // This call is required by the Windows Form Designer. + InitializeComponent(); + + Spacing.Width = 1; + Spacing.Height = 1; + Layout += new LayoutEventHandler(FlowPanel_Layout); + } + + public void RefreshScrollbar() { + Size siz = Size; + Size = new Size(siz.Width + 1, siz.Height); + UpdateBounds(); + Size = siz; + UpdateBounds(); + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) { + if( disposing ) { + if (components != null) { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + // + // FlowPanel + // + + } + #endregion + + void FlowPanel_Layout(object sender, LayoutEventArgs e) { + PerformLayout(true); + } + + int PerformLayout(bool fSetPosition) { + int xItem = Spacing.Width + AutoScrollPosition.X; + int yItem = Spacing.Height + AutoScrollPosition.Y; + int cyTallest = 0; + bool fAdded = false; + foreach (Control ctl in Controls) { + if (fAdded) { + if (xItem + ctl.Width + Spacing.Width >= ClientSize.Width) { + xItem = Spacing.Width; + yItem += cyTallest + Spacing.Height; + cyTallest = 0; + fAdded = false; + } + } + if (fSetPosition) + ctl.Location = new Point(xItem, yItem); + fAdded = true; + if (ctl.Height > cyTallest) + cyTallest = ctl.Height; + xItem += ctl.Width + Spacing.Width; + } + return yItem + cyTallest; + } + } +} + diff --git a/m/flowpanel.resx b/m/flowpanel.resx new file mode 100644 index 0000000..438a4d5 --- /dev/null +++ b/m/flowpanel.resx @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + FlowPanel + + + False + + \ No newline at end of file diff --git a/m/globals.cs b/m/globals.cs new file mode 100644 index 0000000..66a1da3 --- /dev/null +++ b/m/globals.cs @@ -0,0 +1,330 @@ +using System; +using System.IO; +using System.Drawing; +using System.Drawing.Imaging; +using System.Drawing.Drawing2D; +using System.Collections; +using System.Collections.Specialized; +using System.Windows.Forms; +using System.Reflection; +using SpiffLib; + +namespace m { + public class Globals { + public static Ini GobTemplatesIni = null; + public static PropertyGrid PropertyGrid; + public static StatusBar StatusBar; + private static ArrayList m_alsGobImages = new ArrayList(); + public static Font LabelFont; + public static ArrayList Plugins = new ArrayList(); + private static bool m_fKit = true; + + public Globals() { +#if false + // For the time being, we can execute without gob templates. + // Fix this approach. + OpenFileDialog frmOpen = new OpenFileDialog(); + frmOpen.Filter = "pp (*.*)|*.*"; + frmOpen.Title = "Templates File"; + if (frmOpen.ShowDialog() != DialogResult.Cancel) + GobTemplatesIni = new Ini(frmOpen.FileName); +#endif + LabelFont = new Font("Arial", 8); + } + + public static void InitKit() { + try { + Ini ini = new Ini(Application.ExecutablePath.Replace(".exe", ".ini")); + Globals.SetKit(ini["General"]["Kit"].Value == bool.TrueString); + } catch { + Globals.SetKit(true); + } + } + + public static bool IsKit() { + return m_fKit; + } + + public static void SetKit(bool fKit) { + m_fKit = fKit; + } + + public static GobImage GetGobImage(String strName, bool fTight) { + // See if it already exists + foreach (GobImage gimg in m_alsGobImages) { + if (strName == gimg.Name) + return gimg; + } + + // See if it can be loaded + GobImage gimgT = GobImage.Load(strName, fTight); + if (gimgT != null) + m_alsGobImages.Add(gimgT); + return gimgT; + } + } + + public class GobImage { + public String Name; + ArrayList m_alsBitmapSides = new ArrayList(); + Point m_ptOriginUnscaled; + Bitmap m_bmUnscaled; + + public static GobImage Load(String strName, bool fTight) { + // Load in from embedded resource + System.Reflection.Assembly ass = typeof(GobImage).Module.Assembly; + Stream stm = ass.GetManifestResourceStream("m.GobImages." + strName + ".png"); + if (stm == null) + stm = ass.GetManifestResourceStream("m.GobImages." + strName + ".bmp"); + if (stm == null) + throw new Exception("Cannot load image for " + strName); + Bitmap bm = new Bitmap(stm); + + // Extract image and calc origin (this code is from aed) + + // 2. 'Normalize' the bitmap. Normalized bitmaps are 24-bit, 'tight', + // have the proper transparent color, and an origin. + + // All pixels the same color as the upper-left pixel get mapped to the + // transparent color + + bm.MakeTransparent(bm.GetPixel(0, 0)); + + Color clrTransparent = Color.FromArgb(0xff, 0, 0xff); + SolidBrush brTransparent = new SolidBrush(clrTransparent); + + Bitmap bmT = new Bitmap(bm.Width, bm.Height, PixelFormat.Format24bppRgb); + using (Graphics g = Graphics.FromImage(bmT)) { + + // Prep the new image by filling with the transparent color + + g.FillRectangle(brTransparent, 0, 0, bm.Width, bm.Height); + + // Convert the Bitmap to 24-bpp while leaving transparent pixels behind + + g.DrawImageUnscaled(bm, 0, 0); + } + + bm.Dispose(); + bm = bmT; + int xOrigin = 0; + int yOrigin = 0; + + if (fTight) { + // UNDONE: any color mapping + + // Find the tight boundary of the image and create a new Bitmap with just + // that portion of the Bitmap + // OPT: this could be made faster by doing four independent edge scans + + int xL = bm.Width; + int xR = 0; + int yT = bm.Height; + int yB = 0; + for (int y = 0; y < bm.Height; y++) { + for (int x = 0; x < bm.Width; x++) { + Color clr = bm.GetPixel(x, y); + if (clr != clrTransparent) { + xL = Math.Min(xL, x); + xR = Math.Max(xR, x); + yT = Math.Min(yT, y); + yB = Math.Max(yB, y); + } + } + } + int cx = xR - xL + 1; + int cy = yB - yT + 1; + xOrigin = bm.Width / 2 - xL; + yOrigin = bm.Height / 2 - yT; + + bmT = new Bitmap(cx, cy); + using (Graphics g = Graphics.FromImage(bmT)) { + Rectangle rcT = new Rectangle(xL, yT, cx, cy); + g.DrawImage(bm, 0, 0, rcT, GraphicsUnit.Pixel); + } + + bm.Dispose(); + bm = bmT; + } + + return new GobImage(strName, bm, new Point(xOrigin, yOrigin)); + } + + public GobImage(String strName, Bitmap bmUnscaled, Point ptOriginUnscaled) { + Name = strName; + m_bmUnscaled = bmUnscaled; + m_ptOriginUnscaled = ptOriginUnscaled; + } + + BitmapSides FindBitmapSides(Size sizTile) { + BitmapSides bmsFound = null; + foreach(BitmapSides bms in m_alsBitmapSides) { + if (bms.m_sizTile == sizTile) { + bmsFound = bms; + break; + } + } + if (bmsFound == null) { + bmsFound = new BitmapSides(m_bmUnscaled, m_ptOriginUnscaled, sizTile); + m_alsBitmapSides.Add(bmsFound); + } + return bmsFound; + } + + public Bitmap[] GetBitmapSides(Size sizTile) { + BitmapSides bms = FindBitmapSides(sizTile); + return bms.m_abmSides; + } + + public Size GetSize(Size sizTile) { + Bitmap[] abmSides = GetBitmapSides(sizTile); + return abmSides[0].Size; + } + + public Point GetOrigin(Size sizTile) { + BitmapSides bms = FindBitmapSides(sizTile); + return bms.m_ptOrigin; + } + } + + public class BitmapSides { + public Size m_sizTile; + public Bitmap[] m_abmSides; + public Point m_ptOrigin; + + public BitmapSides(Bitmap bmUnscaled, Point ptOriginUnscaled, Size sizTile) { + m_sizTile = sizTile; + m_abmSides = GetBitmapSides(bmUnscaled, sizTile); + float xScale = (float)sizTile.Width / 16.0f; + float yScale = (float)sizTile.Height / 16.0f; + int xNew = (int)((float)ptOriginUnscaled.X * xScale + 0.5f); + int yNew = (int)((float)ptOriginUnscaled.Y * yScale + 0.5f); + m_ptOrigin = new Point(xNew, yNew); + } + + Bitmap[] GetBitmapSides(Bitmap bmUnscaled, Size sizTile) { + // Maps the blues into a new hue + + Color[][] aaclr = { + new Color[] { + // gray + Color.FromArgb(116, 116, 116), + Color.FromArgb(96, 96, 96), + Color.FromArgb(64, 64, 64), + Color.FromArgb(48, 48, 48), + Color.FromArgb(32, 32, 32) + }, + new Color[] { + // blue + Color.FromArgb(0, 116, 232), // (h: 149, s: 255, l: 116) + Color.FromArgb(0, 96, 196), // (h: 149, s: 255, l: 98) + Color.FromArgb(0, 64, 120), // (h: 147, s: 255, l: 60) + Color.FromArgb(0, 48, 92), // (h: 148, s: 255, l: 46) + Color.FromArgb(0, 32, 64) // (h: 149, s: 255, l: 32) + }, + new Color[] { + // red (h: 6) + Color.FromArgb(232, 32, 0), + Color.FromArgb(196, 28, 0), + Color.FromArgb(128, 8, 0), + Color.FromArgb(92, 8, 0), + Color.FromArgb(64, 8, 0) + }, + new Color[] { + // yellow (h: 42) + Color.FromArgb(232, 229, 0), + Color.FromArgb(196, 194, 0), + Color.FromArgb(120, 119, 0), + Color.FromArgb(92, 91, 0), + Color.FromArgb(64, 63, 0) + }, + new Color[] { + // cyan + Color.FromArgb(0, 229, 232), // (h: 128, s: 255, l: 116) + Color.FromArgb(0, 194, 196), // (h: 128, s: 255, l: 98) + Color.FromArgb(0, 119, 120), // (h: 128, s: 255, l: 60) + Color.FromArgb(0, 91, 92), // (h: 128, s: 255, l: 46) + Color.FromArgb(0, 63, 64) // (h: 128, s: 255, l: 32) + }, + new Color[] { + // green (h: 104) + Color.FromArgb(0, 232, 104), + Color.FromArgb(0, 196, 88), + Color.FromArgb(0, 120, 54), + Color.FromArgb(0, 92, 41), + Color.FromArgb(0, 64, 29) + }, + }; + + // Make the side bitmaps + m_abmSides = new Bitmap[6]; + double nScale = (double)sizTile.Width / 16.0; + Bitmap bmScaled = TBitmapTools.ScaleBitmap(bmUnscaled, nScale, null); + for (int i = 0; i < 6; i++) { + Bitmap bmSide = new Bitmap(bmScaled); + MapColors(bmSide, aaclr[1], aaclr[i]); + MapColors(bmSide, new Color[] { Color.FromArgb(156, 212, 248) }, new Color[] { Color.FromArgb(75, 0, 0, 0) }); + + // HACK: Map the transparent color manually because Mono's MakeTransparent doesn't get the job done. + MapColors(bmSide, new Color[] { Color.FromArgb(255, 0, 255) }, new Color[] { Color.FromArgb(0, 0, 0, 0) }); + // bmSide.MakeTransparent(Color.FromArgb(255, 255, 0, 255)); + m_abmSides[i] = bmSide; + } + + return m_abmSides; + } + + void MapColors(Bitmap bm, Color[] aclrFrom, Color[] aclrTo) { + for (int y = 0; y < bm.Height; y++) { + for (int x = 0; x < bm.Width; x++) { + Color clr = bm.GetPixel(x, y); + for (int n = 0; n < aclrFrom.Length; n++) { + if (clr == aclrFrom[n]) { + bm.SetPixel(x, y, aclrTo[n]); + break; + } + } + } + } + } + } + + public class Helper { + static public string[] GetDisplayNames(Type typ) { + ArrayList al = new ArrayList(); + FieldInfo[] afldi = typ.GetFields(); + foreach (FieldInfo fldi in afldi) { + DisplayNameAttribute dna = (DisplayNameAttribute)Attribute.GetCustomAttribute(fldi, typeof(DisplayNameAttribute)); + if (dna != null) + al.Add(dna.DisplayName); + } + + return (string[])al.ToArray(typeof(string)); + } + + static public string GetDisplayName(Type typ, string strField) { + FieldInfo fldi = typ.GetField(strField); + DisplayNameAttribute dna = (DisplayNameAttribute)Attribute.GetCustomAttribute(fldi, typeof(DisplayNameAttribute)); + if (dna != null) + return dna.DisplayName; + else + return strField; + } + + static public string GetDisplayName(Type typ) { + DisplayNameAttribute dna = (DisplayNameAttribute)Attribute.GetCustomAttribute(typ, typeof(DisplayNameAttribute)); + if (dna == null) + return null; + return dna.DisplayName; + } + + static public string GetDescription(Type typ) { + DescriptionAttribute desa = (DescriptionAttribute)Attribute.GetCustomAttribute(typ, typeof(DescriptionAttribute)); + if (desa == null) + return null; + return desa.Description; + } + } +} + + diff --git a/m/licenses.licx b/m/licenses.licx new file mode 100644 index 0000000..96683f5 --- /dev/null +++ b/m/licenses.licx @@ -0,0 +1,22 @@ +System.Windows.Forms.ContextMenu, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.MainMenu, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.UserControl, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +m.MapControl, m, Version=1.0.667.19884, Culture=neutral, PublicKeyToken=null +System.Data.DataSet, System.Data, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.PropertyGrid, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.ToolBar, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.Label, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.Form, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.Button, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.Splitter, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.ToolBarButton, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.MenuItem, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.PictureBox, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.CheckBox, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Data.DataColumn, System.Data, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +System.Windows.Forms.Panel, System.Windows.Forms, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +m.FlowPanel, m, Version=1.0.667.19884, Culture=neutral, PublicKeyToken=null +System.Data.DataTable, System.Data, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +m.FlowPanel, m, Version=1.0.667.19468, Culture=neutral, PublicKeyToken=null +m.FlowPanel, m, Version=1.0.667.19468, Culture=neutral, PublicKeyToken=null +m.FlowPanel, m, Version=1.0.667.19793, Culture=neutral, PublicKeyToken=null diff --git a/m/m.chm b/m/m.chm new file mode 100644 index 0000000..e02f0c9 Binary files /dev/null and b/m/m.chm differ diff --git a/m/m.csproj b/m/m.csproj new file mode 100644 index 0000000..8875b18 --- /dev/null +++ b/m/m.csproj @@ -0,0 +1,283 @@ + + + + Debug + AnyCPU + 9.0.21022 + 2.0 + {D60E8B53-2A68-40DE-8182-C730A49E9371} + Library + m + m + v3.5 + + + true + full + false + bin\Debug\ + prompt + 4 + false + -unsafe + true + + + none + false + bin\Release\ + prompt + 4 + false + -unsafe + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + compressor.cs + + + AudioFormats.cs + + + bitmapraw.cs + + + doublerect.cs + + + ini.cs + + + misc.cs + + + palette.cs + + + palmdatabase.cs + + + PdbPacker.cs + + + tbitmap.cs + + + tbitmaptools.cs + + + + + AboutForm.cs + + + CaNew.cs + + + CaPropForm.cs + + + CaTypeUnitTypesForm.cs + + + CaTypeUpgradeTypesForm.cs + + + EditCommentsForm.cs + + + EditLevelTextForm.cs + + + EditRichTextForm.cs + + + EditStringForm.cs + + + EditTerrainForm.cs + + + FindLevelTextForm.cs + + + FlowPanel.cs + + + Form1.cs + + + GobPanel.cs + + + LevelFrame.cs + + + LevelView.cs + + + LevelViewParent.cs + + + OutputForm.cs + + + PickListForm.cs + + + SwitchesForm.cs + + + TemplatePanel.cs + + + TileSizeForm.cs + + + + TriggerPropForm.cs + + + TriggersForm.cs + + + UnitGroupsForm.cs + + + UnitTypeAndCountForm.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/m/m.exe.config b/m/m.exe.config new file mode 100644 index 0000000..0e2bf2d --- /dev/null +++ b/m/m.exe.config @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/m/m.sln b/m/m.sln new file mode 100644 index 0000000..1c9672c --- /dev/null +++ b/m/m.sln @@ -0,0 +1,56 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mgui", "mgui\mgui.csproj", "{9D1A44EF-D2D0-4EF7-BE3B-60E0189DCBF2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mcl", "mcl\mcl.csproj", "{81F2F902-EBE3-40C7-9E56-F9A688D820C0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "m", "m.csproj", "{D60E8B53-2A68-40DE-8182-C730A49E9371}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {81F2F902-EBE3-40C7-9E56-F9A688D820C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {81F2F902-EBE3-40C7-9E56-F9A688D820C0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {81F2F902-EBE3-40C7-9E56-F9A688D820C0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {81F2F902-EBE3-40C7-9E56-F9A688D820C0}.Release|Any CPU.Build.0 = Release|Any CPU + {9D1A44EF-D2D0-4EF7-BE3B-60E0189DCBF2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9D1A44EF-D2D0-4EF7-BE3B-60E0189DCBF2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9D1A44EF-D2D0-4EF7-BE3B-60E0189DCBF2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9D1A44EF-D2D0-4EF7-BE3B-60E0189DCBF2}.Release|Any CPU.Build.0 = Release|Any CPU + {D60E8B53-2A68-40DE-8182-C730A49E9371}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D60E8B53-2A68-40DE-8182-C730A49E9371}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D60E8B53-2A68-40DE-8182-C730A49E9371}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D60E8B53-2A68-40DE-8182-C730A49E9371}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(MonoDevelopProperties) = preSolution + StartupItem = mgui\mgui.csproj + Policies = $0 + $0.TextStylePolicy = $1 + $1.FileWidth = 120 + $1.inheritsSet = VisualStudio + $1.inheritsScope = text/plain + $1.scope = text/plain + $0.DotNetNamingPolicy = $2 + $2.DirectoryNamespaceAssociation = None + $2.ResourceNamePolicy = FileFormatDefault + $0.TextStylePolicy = $3 + $3.inheritsSet = null + $3.scope = text/x-csharp + $0.CSharpFormattingPolicy = $4 + $4.BeforeMethodDeclarationParentheses = False + $4.BeforeMethodCallParentheses = False + $4.BeforeConstructorDeclarationParentheses = False + $4.BeforeIndexerDeclarationBracket = False + $4.BeforeDelegateDeclarationParentheses = False + $4.AfterDelegateDeclarationParameterComma = True + $4.NewParentheses = False + $4.SpacesBeforeBrackets = False + $4.inheritsSet = Mono + $4.inheritsScope = text/x-csharp + $4.scope = text/x-csharp + EndGlobalSection +EndGlobal diff --git a/m/mapitem.cs b/m/mapitem.cs new file mode 100644 index 0000000..1238e92 --- /dev/null +++ b/m/mapitem.cs @@ -0,0 +1,148 @@ +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.Runtime.Serialization; +using System.ComponentModel; +using SpiffLib; + +namespace m { + + public delegate void PropertyChangedHandler(IMapItem mi, string strProperty); + + public interface IMapItem : ICloneable { + double tx { + get; + set; + } + double ty { + get; + set; + } + SizeF Grid { + get; + } + int ctx { + get; + } + int cty { + get; + } + + bool OnMouseMove(System.Windows.Forms.MouseEventArgs e, Point ptMouse, Size sizTile, TemplateDoc tmpd); + bool OnMouseDown(System.Windows.Forms.MouseEventArgs e, Point ptMouse, Size sizTile, TemplateDoc tmpd); + bool OnMouseUp(System.Windows.Forms.MouseEventArgs e, Point ptMouse, Size sizTile, TemplateDoc tmpd); + Bitmap GetBitmap(Size sizTile, TemplateDoc tmpd); + Point GetCenterPoint(Size sizTile); + Rectangle GetBoundingRectAt(int x, int y, Size sizTile, TemplateDoc tmpd); + bool HitTest(int x, int y, Size sizTile, TemplateDoc tmpd); + void Draw(Graphics g, int x, int y, Size sizTile, TemplateDoc tmpd, LayerType layer, bool fSelected); + Ini.Property GetIniProperty(int txOrigin, int tyOrigin); + + event PropertyChangedHandler PropertyChanged; + } + + [Serializable] + public abstract class MapItem : IMapItem, ISerializable { + protected double m_tx; + protected double m_ty; + + public event PropertyChangedHandler PropertyChanged; + + public MapItem() { + m_tx = 0; + m_ty = 0; + } + + public MapItem(SerializationInfo info, StreamingContext ctx) { + try { + m_tx = info.GetDouble("TileX"); + m_ty = info.GetDouble("TileY"); + } catch (SerializationException) { + try { + m_tx = info.GetInt32("tx"); + m_ty = info.GetInt32("ty"); + } catch (SerializationException) { + m_tx = info.GetInt32("X") / 16; + m_ty = info.GetInt32("Y") / 16; + } + } + } + + public virtual void GetObjectData(SerializationInfo info, StreamingContext context) { + info.AddValue("TileX", m_tx); + info.AddValue("TileY", m_ty); + } + + public virtual void OnPropertyChanged(IMapItem mi, string strProperty) { + if (PropertyChanged != null) + PropertyChanged(mi, strProperty); + } + + [Description("X Position (in tile coords)"), Category("Appearance")] + public virtual double tx { + get { + return m_tx; + } + set { + if (value == m_tx) + return; + m_tx = value; + if (PropertyChanged != null) + PropertyChanged(this, "tx"); + } + } + + [Description("Y Position (in tile coords)"), Category("Appearance")] + public virtual double ty { + get { + return m_ty; + } + set { + if (value == m_ty) + return; + m_ty = value; + if (PropertyChanged != null) + PropertyChanged(this, "ty"); + } + } + + public virtual int ctx { + get { + return 1; + } + } + + public virtual int cty { + get { + return 1; + } + } + + [Browsable(false)] + public virtual SizeF Grid { + get { + return new SizeF(1.0f, 1.0f); + } + } + + public virtual bool OnMouseMove(System.Windows.Forms.MouseEventArgs e, Point ptMouse, Size sizTile, TemplateDoc tmpd) { + return false; + } + + public virtual bool OnMouseDown(System.Windows.Forms.MouseEventArgs e, Point ptMouse, Size sizTile, TemplateDoc tmpd) { + return false; + } + + public virtual bool OnMouseUp(System.Windows.Forms.MouseEventArgs e, Point ptMouse, Size sizTile, TemplateDoc tmpd) { + return false; + } + + public abstract Bitmap GetBitmap(Size sizTile, TemplateDoc tmpd); + public abstract Point GetCenterPoint(Size sizTile); + public abstract Rectangle GetBoundingRectAt(int x, int y, Size sizTile, TemplateDoc tmpd); + public abstract bool HitTest(int x, int y, Size sizTile, TemplateDoc tmpd); + public abstract void Draw(Graphics g, int x, int y, Size sizTile, TemplateDoc tmpd, LayerType layer, bool fSelected); + public abstract Ini.Property GetIniProperty(int txOrigin, int tyOrigin); + public abstract Object Clone(); + } +} diff --git a/m/mapitemcontrol.resx b/m/mapitemcontrol.resx new file mode 100644 index 0000000..8677d72 --- /dev/null +++ b/m/mapitemcontrol.resx @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MapItemControl + + + text/microsoft-resx + + + 1.0.0.0 + + + System.Resources.ResXResourceReader + + + System.Resources.ResXResourceWriter + + \ No newline at end of file diff --git a/m/mcl/.cvsignore b/m/mcl/.cvsignore new file mode 100644 index 0000000..ba077a4 --- /dev/null +++ b/m/mcl/.cvsignore @@ -0,0 +1 @@ +bin diff --git a/m/mcl/AssemblyInfo.cs b/m/mcl/AssemblyInfo.cs new file mode 100644 index 0000000..9f89a32 --- /dev/null +++ b/m/mcl/AssemblyInfo.cs @@ -0,0 +1,58 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.*")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] diff --git a/m/mcl/CommandLine.cs b/m/mcl/CommandLine.cs new file mode 100644 index 0000000..a428c34 --- /dev/null +++ b/m/mcl/CommandLine.cs @@ -0,0 +1,60 @@ +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.Collections; +using System.IO; +using SpiffLib; +using System.Diagnostics; +using m; + +namespace m +{ + class CommandLine + { + public static void Main(string[] astr) + { + new Globals(); + new DocManager(); + + DocManager.AddTemplate(new LevelDocTemplate(null, null)); + DocManager.AddTemplate(new TemplateDocTemplate()); + + switch (astr[0]) { + case "-mixmaps": + OutputTools.ExportMixMaps(astr); + break; + + case "-levels": + OutputTools.ExportLevels(astr, 0); + break; + + case "-images": + OutputTools.ExportImages(astr); + break; + + case "-makepal": + OutputTools.MakePalette(astr); + break; + + case "-special": + char sep = Path.DirectorySeparatorChar; + string root = sep + "ht" + sep + "data" + sep; + OutputTools.MixMapImportSpecial(Theater.Desert, (TemplateDoc)DocManager.OpenDocument(root + "desert.tc"), root + "desert24.tc"); + OutputTools.MixMapImportSpecial(Theater.Temperate, (TemplateDoc)DocManager.OpenDocument(root + "temperate.tc"), root + "temperate24.tc"); + break; + + case "-exporttext": + OutputTools.ExportText(astr); + break; + + case "-importtext": + OutputTools.ImportText(astr); + break; + + case "-testimport": + OutputTools.ImportExportPdbs(astr); + break; + } + } + } +} diff --git a/m/mcl/mcl.csproj b/m/mcl/mcl.csproj new file mode 100644 index 0000000..3ea9f28 --- /dev/null +++ b/m/mcl/mcl.csproj @@ -0,0 +1,54 @@ + + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {81F2F902-EBE3-40C7-9E56-F9A688D820C0} + Exe + mcl + mcl + v3.5 + + + true + full + false + bin\Debug\ + prompt + 4 + false + -levels 32 8 -1.0 1.0 1.0 1.0 1.0 art824 832 S_07.ld S_08.ld S_09.ld S_10.ld S_11.ld S_12.ld S_13.ld S_14.ld C_01.ld C_04.ld C_05.ld D_03.ld M_02.ld M_03.ld M_04.ld M_06.ld M_07.ld M_08.ld M_09.ld M_10.ld M_12.ld M_15.ld M_18.ld M_20.ld M_21.ld + -unsafe + + + none + false + bin\Release\ + prompt + 4 + false + -unsafe + + + + + + + + + + + + + + + + + + {D60E8B53-2A68-40DE-8182-C730A49E9371} + m + + + \ No newline at end of file diff --git a/m/mediancut.cs b/m/mediancut.cs new file mode 100644 index 0000000..ad1d123 --- /dev/null +++ b/m/mediancut.cs @@ -0,0 +1,511 @@ +using System; +using System.Drawing; +using SpiffLib; +using System.Collections; + +/** Converts an RGB image to 8-bit index color using Heckbert's median-cut + color quantization algorithm. Based on median.c by Anton Kruger from the + September, 1994 issue of Dr. Dobbs Journal. +*/ +namespace m { + public class MedianCut { + + static int MAXCOLORS = 256; // maximum # of output colors +#if false + // Histogram size for 5 bit color + static int HSIZE = 32768; // size of image histogram +#else + // Histogram size for 6 bit color + static int HSIZE = 256 * 1024; // size of image histogram +#endif + private int[] hist; // RGB histogram and reverse color lookup table + private int[] histPtr; // points to colors in "hist" + private Cube[] list; // list of cubes +#if false + private int[] pixels32; + private int width, height; +#endif + private Palette m_pal; + + public MedianCut() { + } + +#if false + public MedianCut(int[] pixels, int width, int height) { + int color16; + + pixels32 = pixels; + this.width = width; + this.height = height; + + //build 32x32x32 RGB histogram + hist = new int[HSIZE]; + for (int i=0; i0) count++; + return count; + } + + + Color getModalColor() { + int max=0; + int c = 0; + for (int i=0; imax) { + max = hist[i]; + c = i; + } + return Color.FromArgb(red(c), green(c), blue(c)); + } + + +#if false + // Coverters for 5 bit color + // Convert from 24-bit to 15-bit color + private int rgb(int c) { + int r = (c&0xf80000)>>19; + int g = (c&0xf800)>>6; + int b = (c&0xf8)<<7; + return b | g | r; + } + + // Get red component of a 15-bit color + private int red(int x) { + return (x&31)<<3; + } + + // Get green component of a 15-bit color + private int green(int x) { + return (x>>2)&0xf8; + } + + // Get blue component of a 15-bit color + private int blue(int x) { + return (x>>7)&0xf8; + } +#else + // Converters for 6 bit color + // Convert from 24-bit to 18-bit color + private int rgb(int c) { + int r = (((c & 0x00fc0000) >> 16) >> 2) << 0; + int g = (((c & 0x0000fc00) >> 8) >> 2) << 6; + int b = (((c & 0x000000fc) >> 0) >> 2) << 12; + return b | g | r; + } + + // Get red component of a 18-bit color + private int red(int x) { + return ((x >> 0) << 2) & 0xfc; + } + + // Get green component of a 18-bit color + private int green(int x) { + return ((x >> 6) << 2) & 0xfc; + } + + // Get blue component of a 18-bit color + private int blue(int x) { + return ((x >> 12) << 2) & 0xfc; + } +#endif + + public Palette GetPalette() { + return m_pal; + } + + /** Uses Heckbert's median-cut algorithm to divide the color space defined by + "hist" into "maxcubes" cubes. The centroids (average value) of each cube + are are used to create a color table. "hist" is then updated to function + as an inverse color map that is used to generate an 8-bit image. */ + public void convert(int maxcubes) { + + int lr, lg, lb; + int i, median, color; + int count; + int k, level, ncubes, splitpos; + int longdim=0; //longest dimension of cube + Cube cube, cubeA, cubeB; + + // Create initial cube + list = new Cube[MAXCOLORS]; + histPtr = new int[HSIZE]; + ncubes = 0; + cube = new Cube(); + for (i=0,color=0; i<=HSIZE-1; i++) { + if (hist[i] != 0) { + histPtr[color++] = i; + cube.count = cube.count + hist[i]; + } + } + cube.lower = 0; cube.upper = color-1; + cube.level = 0; + Shrink(cube); + list[ncubes++] = cube; + + //Main loop + while (ncubes < maxcubes) { + + // Search the list of cubes for next cube to split, the lowest level cube + level = 255; splitpos = -1; + for (k=0; k<=ncubes-1; k++) { + if (list[k].lower == list[k].upper) { + // single color; cannot be split + } else if (list[k].level < level) { + level = list[k].level; + splitpos = k; + } + } + if (splitpos == -1) // no more cubes to split + break; + + // Find longest dimension of this cube + cube = list[splitpos]; + lr = cube.rmax - cube.rmin; + lg = cube.gmax - cube.gmin; + lb = cube.bmax - cube.bmin; + if (lr >= lg && lr >= lb) longdim = 0; + if (lg >= lr && lg >= lb) longdim = 1; + if (lb >= lr && lb >= lg) longdim = 2; + + // Sort along "longdim" + reorderColors(histPtr, cube.lower, cube.upper, longdim); + quickSort(histPtr, cube.lower, cube.upper); + restoreColorOrder(histPtr, cube.lower, cube.upper, longdim); + + // Find median + count = 0; + for (i=cube.lower;i<=cube.upper-1;i++) { + if (count >= cube.count/2) break; + color = histPtr[i]; + count = count + hist[color]; + } + median = i; + + // Now split "cube" at the median and add the two new + // cubes to the list of cubes. + cubeA = new Cube(); + cubeA.lower = cube.lower; + cubeA.upper = median-1; + cubeA.count = count; + cubeA.level = cube.level + 1; + Shrink(cubeA); + list[splitpos] = cubeA; // add in old slot + + cubeB = new Cube(); + cubeB.lower = median; + cubeB.upper = cube.upper; + cubeB.count = cube.count - count; + cubeB.level = cube.level + 1; + Shrink(cubeB); + list[ncubes++] = cubeB; // add in new slot */ + } + + // We have enough cubes, or we have split all we can. Now + // compute the color map, the inverse color map, and return + // an 8-bit image. + makeInverseMap(hist, ncubes); + //return makeImage(); + } + + + void Shrink(Cube cube) { + // Encloses "cube" with a tight-fitting cube by updating the + // (rmin,gmin,bmin) and (rmax,gmax,bmax) members of "cube". + + int r, g, b; + int color; + int rmin, rmax, gmin, gmax, bmin, bmax; + + rmin = 255; rmax = 0; + gmin = 255; gmax = 0; + bmin = 255; bmax = 0; + for (int i=cube.lower; i<=cube.upper; i++) { + color = histPtr[i]; + r = red(color); + g = green(color); + b = blue(color); + if (r > rmax) rmax = r; + if (r < rmin) rmin = r; + if (g > gmax) gmax = g; + if (g < gmin) gmin = g; + if (b > bmax) bmax = b; + if (b < bmin) bmin = b; + } + cube.rmin = rmin; cube.rmax = rmax; + cube.gmin = gmin; cube.gmax = gmax; + cube.gmin = gmin; cube.gmax = gmax; + } + + + void makeInverseMap(int[] hist, int ncubes) { + // For each cube in the list of cubes, computes the centroid + // (average value) of the colors enclosed by that cube, and + // then loads the centroids in the color map. Next loads + // "hist" with indices into the color map + + int r, g, b; + int color; + float rsum, gsum, bsum; + Cube cube; + byte[] rLUT = new byte[256]; + byte[] gLUT = new byte[256]; + byte[] bLUT = new byte[256]; + + for (int k=0; k<=ncubes-1; k++) { + cube = list[k]; + rsum = gsum = bsum = (float)0.0; + for (int i=cube.lower; i<=cube.upper; i++) { + color = histPtr[i]; + r = red(color); + rsum += (float)r*(float)hist[color]; + g = green(color); + gsum += (float)g*(float)hist[color]; + b = blue(color); + bsum += (float)b*(float)hist[color]; + } + + // Update the color map + r = (int)(rsum/(float)cube.count); + g = (int)(gsum/(float)cube.count); + b = (int)(bsum/(float)cube.count); +#if false + if (r==248 && g==248 && b==248) + r=g=b=255; // Restore white (255,255,255) +#endif + rLUT[k] = (byte)r; + gLUT[k] = (byte)g; + bLUT[k] = (byte)b; + } +#if false + cm = new IndexColorModel(8, ncubes, rLUT, gLUT, bLUT); +#else + Color[] aclr = new Color[ncubes]; + for (int iclr = 0; iclr < ncubes; iclr++) + aclr[iclr] = Color.FromArgb(rLUT[iclr], gLUT[iclr], bLUT[iclr]); + m_pal = new Palette(aclr); +#endif + + // For each color in each cube, load the corre- + // sponding slot in "hist" with the centroid of the cube. + for (int k=0; k<=ncubes-1; k++) { + cube = list[k]; + for (int i=cube.lower; i<=cube.upper; i++) { + color = histPtr[i]; + hist[color] = k; + } + } + } + + + void reorderColors(int[] a, int lo, int hi, int longDim) { +#if false + // Change the ordering of the 5-bit colors in each word of int[] + // so we can sort on the 'longDim' color + + int c, r, g, b; + switch (longDim) { + case 0: //red + for (int i=lo; i<=hi; i++) { + c = a[i]; + r = c & 31; + a[i] = (r<<10) | (c>>5); + } + break; + case 1: //green + for (int i=lo; i<=hi; i++) { + c = a[i]; + r = c & 31; + g = (c>>5) & 31; + b = c>>10; + a[i] = (g<<10) | (b<<5) | r; + } + break; + case 2: //blue; already in the needed order + break; + } +#else + // Change the ordering of the 6-bit colors in each word of int[] + // so we can sort on the 'longDim' color + + int c, r, g, b; + switch (longDim) { + case 0: //red + for (int i=lo; i<=hi; i++) { + c = a[i]; + r = c & 0x3f; + a[i] = (r<<12) | (c>>6); + } + break; + case 1: //green + for (int i=lo; i<=hi; i++) { + c = a[i]; + r = c & 0x3f; + g = (c>>6) & 0x3f; + b = c>>12; + a[i] = (g<<12) | (b<<6) | r; + } + break; + case 2: //blue; already in the needed order + break; + } +#endif + } + + + void restoreColorOrder(int[] a, int lo, int hi, int longDim) { +#if false + // Restore the 5-bit colors to the original order + + int c, r, g, b; + switch (longDim){ + case 0: //red + for (int i=lo; i<=hi; i++) { + c = a[i]; + r = c >> 10; + a[i] = ((c&1023)<<5) | r; + } + break; + case 1: //green + for (int i=lo; i<=hi; i++) { + c = a[i]; + r = c & 31; + g = c>>10; + b = (c>>5) & 31; + a[i] = (b<<10) | (g<<5) | r; + } + break; + case 2: //blue + break; + } +#else + // Restore the 6-bit colors to the original order + + int c, r, g, b; + switch (longDim){ + case 0: //red + for (int i=lo; i<=hi; i++) { + c = a[i]; + r = c >> 12; + a[i] = ((c&0xfff)<<6) | r; + } + break; + case 1: //green + for (int i=lo; i<=hi; i++) { + c = a[i]; + r = c & 0x3f; + g = c>>12; + b = (c>>6) & 0x3f; + a[i] = (b<<12) | (g<<6) | r; + } + break; + case 2: //blue + break; + } +#endif + } + + + void quickSort(int[] a, int lo0, int hi0) { + // Based on the QuickSort method by James Gosling from Sun's SortDemo applet + + int lo = lo0; + int hi = hi0; + int mid, t; + + if ( hi0 > lo0) { + mid = a[ ( lo0 + hi0 ) / 2 ]; + while( lo <= hi ) { + while( ( lo < hi0 ) && ( a[lo] < mid ) ) + ++lo; + while( ( hi > lo0 ) && ( a[hi] > mid ) ) + --hi; + if( lo <= hi ) { + t = a[lo]; + a[lo] = a[hi]; + a[hi] = t; + ++lo; + --hi; + } + } + if( lo0 < hi ) + quickSort( a, lo0, hi ); + if( lo < hi0 ) + quickSort( a, lo, hi0 ); + + } + } + +#if false + public Image makeImage() { + // Generate 8-bit image + +#if false + Image img8; +#endif + byte[] pixels8; + int color16; + + pixels8 = new byte[width*height]; + for (int i=0; i. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] diff --git a/m/mgui/app.ico b/m/mgui/app.ico new file mode 100644 index 0000000..603c410 Binary files /dev/null and b/m/mgui/app.ico differ diff --git a/m/mgui/mgui.cs b/m/mgui/mgui.cs new file mode 100644 index 0000000..eb92c97 --- /dev/null +++ b/m/mgui/mgui.cs @@ -0,0 +1,22 @@ +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; +using System.Data; +using SpiffLib; +using m; + +namespace MapEdit +{ + public class Wrapper + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() { + Application.Run(new MainForm()); + } + } +} diff --git a/m/mgui/mgui.csproj b/m/mgui/mgui.csproj new file mode 100644 index 0000000..788a1cf --- /dev/null +++ b/m/mgui/mgui.csproj @@ -0,0 +1,52 @@ + + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {9D1A44EF-D2D0-4EF7-BE3B-60E0189DCBF2} + WinExe + mgui + mgui + v3.5 + + + true + full + false + bin\Debug\ + prompt + 4 + false + + + none + false + bin\Release\ + prompt + 4 + false + + + + + + + + + + + + + + + + + + + {D60E8B53-2A68-40DE-8182-C730A49E9371} + m + + + \ No newline at end of file diff --git a/m/mixsuck.cs b/m/mixsuck.cs new file mode 100644 index 0000000..5b2d1a2 --- /dev/null +++ b/m/mixsuck.cs @@ -0,0 +1,720 @@ +using System; +using System.Globalization; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; +using System.Data; +using System.IO; +using System.Text.RegularExpressions; +using SpiffLib; + +namespace m +{ + struct MagicEntry { + public int index; + public byte[] aiTheater; + public short ctx, cty; + public string strName; + public string strComment; + } + + public enum Theater { Desert, Temperate, Winter }; + + public class MixSuck { + public static MixMap LoadMap(Stream stm, MixTemplate[] amixt) { + MixReader mixr = new MixReader("general.mix"); + MixMap map = new MixMap(stm, amixt); + mixr.Dispose(); + return map; + } + + public static MixTemplate[] LoadTemplates(Theater theater) { + ArrayList alMagicTable = GetTemplateList(); + MixReader mixr = new MixReader(s_astrTheaterFileNames[(int)theater]); + + // Load palette + Stream stm = mixr.GetFileStream(s_idePalette[(int)theater] - 1); + Color[] aclrPalette = LoadPalette(stm); + stm.Close(); + + // Load templates + ArrayList alsTemplates = new ArrayList(); + foreach (MagicEntry me in alMagicTable) { + if (me.aiTheater[(int)theater] == 0xff) { + alsTemplates.Add(null); + continue; + } + stm = mixr.GetFileStream(me.aiTheater[(int)theater] - 1); + alsTemplates.Add(new MixTemplate(stm, me.ctx, me.cty, aclrPalette, me.index)); + stm.Close(); + } + mixr.Dispose(); + return (MixTemplate[])alsTemplates.ToArray(typeof(MixTemplate)); + } + + public static int ImportTemplates(MixTemplate[] amixt, TemplateDoc tmpd) { + Size sizTile = tmpd.TileSize; + ArrayList alsTmpl = new ArrayList(); + int cImages = 0; + foreach (MixTemplate mixt in amixt) { + if (mixt == null) + continue; + cImages += mixt.ImageCount; + alsTmpl.Add(ConstructTemplate(tmpd, mixt, sizTile)); + } + tmpd.AddTemplates((Template[])alsTmpl.ToArray(typeof(Template))); + return cImages; + } + + public static string ImportExportMixMap(string strFile, LevelDoc lvld) { + // Load ini + Ini ini = new Ini(strFile); + + // Get name of level + String strName = strFile; + Ini.Section secBasic = ini["Basic"]; + if (secBasic != null) { + Ini.Property propName = secBasic["Name"]; + if (propName != null) { + strName = propName.Value; + } else { + Ini.Property propBrief = secBasic["Brief"]; + if (propBrief != null) + strName = propBrief.Value; + } + } + + // Get theater + Ini.Section secMap = ini["MAP"]; + if (secMap == null) { + MessageBox.Show("Could not load " + strFile); + return null; + } + Theater theater; + switch (secMap["Theater"].Value) { + case "DESERT": + theater = Theater.Desert; + break; + + case "TEMPERATE": + theater = Theater.Temperate; + break; + + case "WINTER": + theater = Theater.Winter; + break; + + default: + MessageBox.Show("Could not load " + strFile); + return null; + } + + // Get bounds & invert + int xLeft = Int32.Parse(secMap["X"].Value); + int yTop = Int32.Parse(secMap["Y"].Value); + int cxWidth = Int32.Parse(secMap["Width"].Value); + int cyHeight = Int32.Parse(secMap["Height"].Value); + Rectangle rcBounds = new Rectangle(64 - (xLeft + cxWidth), yTop, cxWidth, cyHeight); + + // We're ready to go + lvld.Title = strName; + //fixme + //lvld.TileCollectionFileName = theater.ToString() + ".tc"; + lvld.Bounds = rcBounds; + + // Load up + MixTemplate[] amixt = MixSuck.LoadTemplates(theater); + MixSuck.ImportTemplates(amixt, lvld.GetTemplateDoc()); + Stream stm = (Stream)new FileStream(strFile.ToLower().Replace(".ini", ".bin"), FileMode.Open, FileAccess.Read, FileShare.Read); + MixMap map = MixSuck.LoadMap(stm, amixt); + stm.Close(); + map.ImportMap(lvld); + //fixme + //lvld.SetBackgroundTemplate(lvld.GetTemplateDoc()FindTemplate(0)); + + // Save + + string strFileLvld = strName + "_" + Path.GetFileName(strFile).Replace(".ini", ".ld"); + lvld.SaveAs(strFileLvld); + + // Also save a scaled image for reference + TemplateDoc tmpd = lvld.GetTemplateDoc(); + Bitmap bm = lvld.GetMapBitmap(tmpd.TileSize, tmpd, true); + int cx; + int cy; + if (bm.Width > bm.Height) { + cx = 128; + cy = bm.Height * 128 / bm.Width; + } else { + cx = bm.Width * 128 / bm.Height; + cy = 128; + } + bm = (Bitmap)bm.GetThumbnailImage(cx, cy, null, IntPtr.Zero); + bm.Save(strFileLvld.Replace(".ld", ".png"), ImageFormat.Png); + bm.Dispose(); + + // Save a full 24x24 original map image for reference + map.SaveMapBitmap("cc_" + strFileLvld.Replace(".ld", ".png")); + + return strFileLvld; + } + + private static Color[] LoadPalette(Stream stm) { + BinaryReader brdr = new BinaryReader(stm); + int cColors = (int)stm.Length / 3; + Color[] aclr = new Color[cColors]; + for (int n = 0; n < cColors; n++) + aclr[n] = Color.FromArgb(brdr.ReadByte() << 2, brdr.ReadByte() << 2, brdr.ReadByte() << 2); + brdr.Close(); + return aclr; + } + + public static Template ConstructTemplate(TemplateDoc tmpd, MixTemplate mixt, Size sizTile) { + Bitmap bm = new Bitmap(mixt.XTileCount * sizTile.Width, mixt.YTileCount * sizTile.Height); + Graphics g = Graphics.FromImage(bm); + g.Clear(Color.FromArgb(255, 0, 255)); + Bitmap bmNew = new Bitmap(sizTile.Width, sizTile.Height, PixelFormat.Format24bppRgb); + Graphics gNew = Graphics.FromImage(bmNew); + for (int tx = 0; tx < mixt.XTileCount; tx++) { + for (int ty = 0; ty < mixt.YTileCount; ty++) { + Bitmap bmT = mixt.TileBitmaps[tx + ty * mixt.XTileCount]; + if (bmT == null) + continue; + gNew.DrawImage(bmT, 0, 0, sizTile.Width, sizTile.Height); + g.DrawImageUnscaled(bmNew, tx * sizTile.Width, ty * sizTile.Height, sizTile.Width, sizTile.Height); + } + } + gNew.Dispose(); + bmNew.Dispose(); + g.Dispose(); + bm.RotateFlip(RotateFlipType.RotateNoneFlipX); + return new Template(tmpd, bm, mixt.Index.ToString()); + } + + private static ArrayList GetTemplateList() { + ArrayList alMagicTable = new ArrayList(); + Regex re = new Regex(@"\s*(\w+)h\W*(\w+)\W*(\w+)\W*(\w+)\W*(\d+)x(\d+)]\W*(\w+)\s*\W*(.*)"); + foreach (string strToMatch in s_astrMagicTable) { + Match mat = re.Match(strToMatch); + MagicEntry me = new MagicEntry(); + me.index = Int32.Parse(mat.Groups[1].Value, NumberStyles.HexNumber); + me.aiTheater = new byte[3]; + string strT = mat.Groups[2].Value; + me.aiTheater[0] = strT == "x" ? (byte)0xff : byte.Parse(strT); + strT = mat.Groups[3].Value; + me.aiTheater[1] = strT == "x" ? (byte)0xff : byte.Parse(strT); + strT = mat.Groups[4].Value; + me.aiTheater[2] = strT == "x" ? (byte)0xff : byte.Parse(strT); + me.ctx = short.Parse(mat.Groups[5].Value); + me.cty = short.Parse(mat.Groups[6].Value); + me.strName = mat.Groups[7].Value; + me.strComment = mat.Groups[8].Value; + alMagicTable.Add(me); + } + return alMagicTable; + } + + // Theater file names + static string[] s_astrTheaterFileNames = { + "desert.mix", "temperat.mix", "winter.mix" + }; + + // From cncmap1f.txt: + // In each MIX there's also a palette, the entries are: + // DESERT.MIX entry n. 26 + // TEMPERAT.MIX entry n. 62 + // WINTER.MIX entry n. 62 + static int[] s_idePalette = { 26, 62, 62 }; + + // From cncmap1f.txt: + static string[] s_astrMagicTable = { + "00h | 007 | 011 | 028 | [4x4] | CLEAR1 | Default terrain", + "01h | 002 | 007 | 007 | [1x1] | W1 | Water (not animated)", + "02h | x | 009 | 009 | [2x2] | W2 | Water", + "03h | x | 087 | 087 | [3x3] | SH1 | Coast WD (1)", + "04h | x | 106 | 105 | [3x3] | SH2 | Coast WD", + "05h | x | 126 | 124 | [1x1] | SH3 | Rock in water", + "06h | x | 143 | 140 | [2x1] | SH4 | Rock in water", + "07h | x | 159 | 157 | [3x3] | SH5 | Coast WD", + "08h | x | 018 | 017 | [3x3] | SH11 | Fjord WD", + "09h | x | 024 | 023 | [3x3] | SH12 | Coast WU", + "0Ah | x | 031 | 031 | [3x3] | SH13 | Coast WU", + "0Bh | x | 037 | 037 | [3x3] | SH14 | Coast WU", + "0Ch | x | 042 | 042 | [3x3] | SH15 | Coast WU", + "0Dh | 106 | 074 | 074 | [2x2] | S01 | Cliff Left Edge", + "0Eh | 122 | 093 | 092 | [2x3] | S02 | Cliff Wu-Wd (2)", + "0Fh | 138 | 112 | 110 | [2x2] | S03 | Cliff W-E", + "10h | 154 | 131 | 128 | [2x2] | S04 | Cliff W-E", + "11h | 170 | 147 | 144 | [2x2] | S05 | Cliff W-E", + "12h | 185 | 163 | 161 | [2x3] | S06 | Cliff Wd-Eu", + "13h | 200 | 180 | 179 | [2x2] | S07 | Cliff Right Edge", + "14h | 212 | 195 | 195 | [2x2] | S08 | Cliff Top Edge", + "15h | 225 | 208 | 209 | [3x2] | S09 | Cliff N-S", + "16h | 096 | 064 | 064 | [2x2] | S10 | Cliff N-S", + "17h | 108 | 078 | 078 | [2x2] | S11 | Cliff N-S", + "18h | 124 | 097 | 096 | [2x2] | S12 | Cliff N-S", + "19h | 140 | 117 | 115 | [3x2] | S13 | Cliff N-S", + "1Ah | 157 | 135 | 132 | [2x2] | S14 | Cliff Bottom Edge", + "1Bh | 172 | 151 | 149 | [2x2] | S15 | Cliff Left Edge", + "1Ch | 187 | 167 | 166 | [2x3] | S16 | Cliff Wu-Ed", + "1Dh | 202 | 184 | 184 | [2x2] | S17 | Cliff W-E", + "1Eh | 215 | 199 | 200 | [2x2] | S18 | Cliff W-E", + "1Fh | 228 | 211 | 213 | [2x2] | S19 | Cliff W-E", + "20h | 098 | 068 | 069 | [2x3] | S20 | Cliff Wu-Ed", + "21h | 110 | 082 | 082 | [1x2] | S21 | Cliff Right Edge", + "22h | 126 | 101 | 100 | [2x1] | S22 | Cliff Corner S-E Internal", + "23h | 142 | 121 | 119 | [3x2] | S23 | Cliff Sl-Nr", + "24h | 159 | 139 | 136 | [2x2] | S24 | Cliff N-S", + "25h | 174 | 155 | 153 | [2x2] | S25 | Cliff N-S", + "26h | 189 | 171 | 170 | [2x2] | S26 | Cliff N-S", + "27h | 204 | 188 | 188 | [3x2] | S27 | Cliff Nl-Sr", + "28h | 218 | 202 | 203 | [2x2] | S28 | Cliff Bottom Edge", + "29h | 230 | 213 | 215 | [2x2] | S29 | Cliff Corner N-E External", + "2Ah | 101 | 070 | 071 | [2x2] | S30 | Cliff Corner S-E Ext", + "2Bh | 113 | 084 | 084 | [2x2] | S31 | Cliff Corner W-S Ext", + "2Ch | 129 | 103 | 102 | [2x2] | S32 | Cliff Corner N-W Ext", + "2Dh | 145 | 123 | 121 | [2x2] | S33 | Cliff Corner N-E Internal", + "2Eh | 162 | 141 | 138 | [2x2] | S34 | Cliff Corner S-E Int", + "2Fh | 177 | 157 | 155 | [2x2] | S35 | Cliff Corner W-S Int", + "30h | 192 | 173 | 172 | [2x2] | S36 | Cliff Corner W-N Int", + "31h | 207 | 190 | 190 | [2x2] | S37 | Cliff Junction NW-SE", + "32h | 221 | 204 | 205 | [2x2] | S38 | Cliff Junction SW-NE", + "33h | x | 027 | 026 | [3x3] | SH32 | Coast Corner N-W Int", + "34h | x | 033 | 033 | [3x3] | SH33 | Coast Corner N-E Int", + "35h | 017 | x | x | [4x1] | SH20 | Coast WD", + "36h | 024 | x | x | [3x1] | SH21 | Coast WD", + "37h | 041 | x | x | [6x2] | SH22 | Coast WD", + "38h | 049 | x | x | [2x2] | SH23 | Coast WD", + "39h | 118 | x | x | [1x1] | BR1 | Bush", + "3Ah | 134 | x | x | [1x1] | BR2 | Bush", + "3Bh | 150 | x | x | [1x1] | BR3 | Cactus", + "3Ch | 166 | x | x | [1x1] | BR4 | Cactus", + "3Dh | 181 | x | x | [1x1] | BR5 | ??? Purple square (bug ?)", + "3Eh | 196 | x | x | [2x2] | BR6 | Bushes", + "3Fh | 210 | x | x | [2x2] | BR7 | Bushes", + "40h | 223 | x | x | [3x2] | BR8 | Bushes", + "41h | 234 | x | x | [3x2] | BR9 | Bushes", + "42h | 016 | x | x | [2x1] | BR10 | ??? Purple squares (bug ?)", + "43h | 105 | 073 | x | [1x1] | P01 | Bones / Wall (3)", + "44h | 121 | 092 | x | [1x1] | P02 | Bones / Wall (3)", + "45h | 137 | 111 | x | [1x1] | P03 | Mud / UFO (3) (6)", + "46h | 153 | 130 | x | [1x1] | P04 | Rock / UFO (3) (6)", + "47h | 169 | x | x | [2x2] | P05 | Gray Sand", + "48h | 184 | x | x | [6x4] | P06 | Gray Sand", + "49h | 199 | 179 | 178 | [4x2] | P07 | Mud", + "4Ah | x | 194 | 194 | [3x2] | P08 | Mud", + "4Bh | x | 045 | 045 | [3x2] | SH16 | Fjord WU", + "4Ch | 072 | 047 | 047 | [2x2] | SH17 | Water (anim.)", + "4Dh | 078 | 049 | 049 | [2x2] | SH18 | Water (anim.)", + "4Eh | 084 | x | x | [3x2] | SH19 | Coast WD", + "4Fh | x | 116 | 114 | [3x2] | P13 | Destroyed House", + "50h | x | 134 | 131 | [2x1] | P14 | Walls", + "51h | x | x | 148 | [4x2] | P15 | Snow", + "52h | 001 | 006 | 006 | [1x1] | B1 | Rock", + "53h | 003 | 008 | 008 | [2x1] | B2 | Rock", + "54h | x | 010 | 010 | [3x1] | B3 | Rock", + "55h | 004 | x | x | [1x1] | B4 | ?? Rock (7)", + "56h | 005 | x | x | [1x1] | B5 | ?? Rock (7)", + "57h | 006 | x | x | [1x1] | B6 | ?? Rock (7)", + "58h | x | 175 | 174 | [3x3] | SH6 | Coast WD", + "59h | x | 191 | 191 | [2x2] | SH7 | Coast Corner W-N External", + "5Ah | x | 205 | 206 | [3x3] | SH8 | Coast Corner S-E Ext", + "5Bh | x | 215 | 217 | [3x3] | SH9 | Coast Corner W-S Ext", + "5Ch | x | 012 | 011 | [2x2] | SH10 | Coast Corner N-E Ext", + "5Dh | 104 | 072 | 073 | [2x2] | D01 | Road Bottom End", + "5Eh | 120 | 091 | 091 | [2x2] | D02 | Road Left End", + "5Fh | 136 | 110 | 109 | [1x2] | D03 | Road Top End", + "60h | 152 | 129 | 127 | [2x2] | D04 | Road Right End", + "61h | 168 | 146 | 143 | [3x4] | D05 | Road S-N", + "62h | 183 | 162 | 160 | [2x3] | D06 | Road S-N", + "63h | 198 | 178 | 177 | [3x2] | D07 | Road S-N", + "64h | 211 | 193 | 193 | [3x2] | D08 | Road S-N", + "65h | 224 | 207 | 208 | [4x3] | D09 | Road W-E", + "66h | 095 | 063 | 063 | [4x2] | D10 | Road W-E", + "67h | 107 | 077 | 077 | [2x3] | D11 | Road W-E", + "68h | 123 | 096 | 095 | [2x2] | D12 | Road W-E", + "69h | 139 | 115 | 113 | [4x3] | D13 | Road Wu-Ed", + "6Ah | 156 | 133 | 130 | [3x3] | D14 | Road T N--W+E (4)", + "6Bh | 171 | 150 | 147 | [3x3] | D15 | Road Y S--N+E (4)", + "6Ch | 186 | 166 | 164 | [3x3] | D16 | Road Y S--N+E", + "6Dh | 201 | 183 | 182 | [3x2] | D17 | Road T S--W+E", + "6Eh | 214 | 198 | 198 | [3x3] | D18 | Road T W--N+S", + "6Fh | 227 | 210 | 211 | [3x3] | D19 | Road + W-N-E-S", + "70h | 097 | 067 | 067 | [3x3] | D20 | Road Corner N-E", + "71h | 109 | 081 | 081 | [3x2] | D21 | Road Corner S-E", + "72h | 125 | 100 | 099 | [3x3] | D22 | Road Corner W-S", + "73h | 141 | 120 | 118 | [3x3] | D23 | Road Corner W-N", + "74h | 158 | 138 | 135 | [3x3] | D24 | Road Diagonal NW-SE (5)", + "75h | 173 | 154 | 152 | [3x3] | D25 | Road Diag NW-SE", + "76h | 188 | 170 | 169 | [2x2] | D26 | Road Diag NW-SE (Conn.) (5)", + "77h | 203 | 187 | 187 | [2x2] | D27 | Road Diag NW-SE (Conn.)", + "78h | 217 | 201 | 202 | [2x2] | D28 | Road Corner W-SE (Conn.)", + "79h | 229 | 212 | 214 | [2x2] | D29 | Road Corner N-SE (Conn.)", + "7Ah | 100 | 069 | 070 | [2x2] | D30 | Road Y SE--N+W (Conn.)", + "7Bh | 112 | 083 | 083 | [2x2] | D31 | Road Corner E-NW (Conn.)", + "7Ch | 128 | 102 | 101 | [2x2] | D32 | Road Corner S-NW (Conn.)", + "7Dh | 144 | 122 | 120 | [2x2] | D33 | Road Y NW--S+E (Conn.)", + "7Eh | 161 | 140 | 137 | [3x3] | D34 | Road Diag SW-NE", + "7Fh | 176 | 156 | 154 | [3x3] | D35 | Road Diag SW-NE", + "80h | 191 | 172 | 171 | [2x2] | D36 | Road Diag SW-NE (Conn.)", + "81h | 206 | 189 | 189 | [2x2] | D37 | Road Diag SW-NE (Conn.)", + "82h | 220 | 203 | 204 | [2x2] | D38 | Road Corner E-SW (Conn.)", + "83h | 232 | 214 | 216 | [2x2] | D39 | Road Corner N-SW (Conn.)", + "84h | 103 | 071 | 072 | [2x2] | D40 | Road Y SW--N+E (Conn.)", + "85h | 115 | 085 | 085 | [2x2] | D41 | Road Corner W-NE (Conn.)", + "86h | 131 | 104 | 103 | [2x2] | D42 | Road Corner S-NE (Conn.)", + "87h | 147 | 124 | 122 | [2x2] | D43 | Road Y NE--W+S (Conn.)", + "88h | x | 017 | 016 | [5x4] | RV01 | River W-E", + "89h | x | 023 | 022 | [5x3] | RV02 | River W-E", + "8Ah | x | 030 | 030 | [4x4] | RV03 | River Wu-Ed", + "8Bh | x | 036 | 036 | [4x4] | RV04 | River Wd-Eu", + "8Ch | x | 041 | 041 | [3x3] | RV05 | River N-S", + "8Dh | x | 044 | 044 | [3x2] | RV06 | River N-S", + "8Eh | x | 046 | 046 | [3x2] | RV07 | River N-S", + "8Fh | x | 048 | 048 | [2x2] | RV08 | River Corner S-E", + "90h | x | 052 | 052 | [2x2] | RV09 | River Corner W-S", + "91h | x | 014 | 013 | [2x2] | RV10 | River Corner N-E", + "92h | x | 020 | 019 | [2x2] | RV11 | River Corner W-N", + "93h | x | 026 | 025 | [3x4] | RV12 | River Y N--W+S", + "94h | x | 032 | 032 | [4x4] | RV13 | River Y Eu--W+S", + "95h | 055 | x | x | [4x3] | RV14 | River W-E", + "96h | 060 | x | x | [4x3] | RV15 | River W-E", + "97h | 067 | x | x | [6x4] | RV16 | River Wd-Eu", + "98h | 073 | x | x | [6x5] | RV17 | River Wu-Ed", + "99h | 079 | x | x | [4x4] | RV18 | River N-S", + "9Ah | 085 | x | x | [4x4] | RV19 | River N-S", + "9Bh | 018 | x | x | [6x8] | RV20 | River Nr-Sl", + "9Ch | 025 | x | x | [5x8] | RV21 | River Nl-Sr", + "9Dh | 042 | x | x | [3x3] | RV22 | River Corner E-S", + "9Eh | 050 | x | x | [3x3] | RV23 | River Corner W-S", + "9Fh | 057 | x | x | [3x3] | RV24 | River Corner N-E", + "A0h | 062 | x | x | [3x3] | RV25 | River Corner N-W", + "A1h | 009 | 002 | 004 | [3x3] | FORD1 | River Crossing (Road W-E)", + "A2h | 010 | 003 | 005 | [3x3] | FORD2 | River Crossing (Road N-S)", + "A3h | 047 | 057 | 057 | [3x3] | FALLS1 | Falls W-E", + "A4h | 048 | 058 | 058 | [3x2] | FALLS2 | Falls N-S", + "A5h | x | 218 | 220 | [4x4] | BRIDGE1 | Bridge SW-NE", + "A6h | x | 059 | 059 | [4x4] | BRIDGE1D | Fallen Bridge SW-NE", + "A7h | x | 219 | 221 | [5x5] | BRIDGE2 | Bridge NW-SE", + "A8h | x | 060 | 060 | [5x5] | BRIDGE2D | Fallen Bridge NW-SE", + "A9h | 236 | x | x | [6x5] | BRIDGE3 | Bridge SW-NE", + "AAh | 092 | x | x | [6x5] | BRIDGE3D | Fallen Bridge SW-NE", + "ABh | 237 | x | x | [6x4] | BRIDGE4 | Bridge NW-SE", + "ACh | 093 | x | x | [6x4] | BRIDGE4D | Fallen Bridge NW-SE", + "ADh | 056 | x | x | [3x3] | SH24 | Fjord WD", + "AEh | 061 | x | x | [3x2] | SH25 | Coast WU", + "AFh | 068 | x | x | [3x2] | SH26 | Coast WU", + "B0h | 074 | x | x | [4x1] | SH27 | Coast WU", + "B1h | 080 | x | x | [3x1] | SH28 | Coast WU", + "B2h | 086 | x | x | [6x2] | SH29 | Coast WU", + "B3h | 019 | x | x | [2x2] | SH30 | Coast WU", + "B4h | 027 | x | x | [3x3] | SH31 | Fjord WU", + "B5h | x | x | 165 | [2x2] | P16 | Snow", + "B6h | x | x | 183 | [4x2] | P17 | Snow", + "B7h | x | x | 199 | [4x3] | P18 | Snow", + "B8h | x | x | 212 | [4x3] | P19 | Snow", + "B9h | x | x | 068 | [4x3] | P20 | Snow", + "BAh | x | 038 | 038 | [3x3] | SH34 | Coast WR", + "BBh | x | 043 | 043 | [3x3] | SH35 | Coast WL", + "BCh | 069 | x | x | [1x1] | SH36 | Coast Corner S-E Int", + "BDh | 075 | x | x | [1x1] | SH37 | Coast Corner W-S Int", + "BEh | 081 | x | x | [1x1] | SH38 | Coast Corner N-E Int", + "BFh | 087 | x | x | [1x1] | SH39 | Coast Corner N-W Int", + "C0h | 020 | x | x | [3x3] | SH40 | Coast Corner S-E Int", + "C1h | 028 | x | x | [3x3] | SH41 | Coast Corner N-W Int", + "C2h | 043 | x | x | [1x2] | SH42 | Coast WL", + "C3h | 051 | x | x | [1x3] | SH43 | Coast WL", + "C4h | 058 | x | x | [1x3] | SH44 | Coast WR", + "C5h | 063 | x | x | [1x2] | SH45 | Coast WR", + "C6h | 070 | x | x | [3x3] | SH46 | Coast Corner S-E Int", + "C7h | 076 | x | x | [3x3] | SH47 | Coast Corner S-E Int", + "C8h | 082 | x | x | [3x3] | SH48 | Coast Corner N-E Int", + "C9h | 088 | x | x | [3x3] | SH49 | Coast Corner N-W Int", + "CAh | 021 | x | x | [4x3] | SH50 | Coast Corner S-E Ext", + "CBh | 029 | x | x | [4x3] | SH51 | Coast Corner W-S Ext", + "CCh | 044 | x | x | [4x3] | SH52 | Coast Corner N-E Ext", + "CDh | 052 | x | x | [4x3] | SH53 | Coast Corner N-W Ext", + "CEh | 059 | x | x | [3x2] | SH54 | Coast WD", + "CFh | 064 | x | x | [3x2] | SH55 | Coast WD", + "D0h | 071 | x | x | [3x2] | SH56 | Coast WU", + "D1h | 077 | x | x | [3x2] | SH57 | Coast WU", + "D2h | 083 | x | x | [2x3] | SH58 | Coast WR", + "D3h | 089 | x | x | [2x3] | SH59 | Coast WR", + "D4h | 022 | x | x | [2x3] | SH60 | Coast WL", + "D5h | 030 | x | x | [2x3] | SH61 | Coast WL", + "D6h | 045 | x | x | [6x1] | SH62 | Coast WD", + "D7h | 053 | x | x | [4x1] | SH63 | Coast WD", + }; + } + + public class MixMap { + struct Cell { + public uint iTemplate; + public uint iTile; + } + + Cell[,] m_acell; + MixTemplate[] m_amixt; + + public MixMap(Stream stm, MixTemplate[] amixt) { + m_amixt = amixt; + BinaryReader brdr = new BinaryReader(stm); + m_acell = new Cell[64,64]; + for (int iCell = 0; iCell < 64 * 64; iCell++) { + m_acell[iCell / 64, iCell % 64].iTemplate = (uint)brdr.ReadByte(); + m_acell[iCell / 64, iCell % 64].iTile = (uint)brdr.ReadByte(); + } + brdr.Close(); + } + + public void SaveMapBitmap(String strFile) { + Bitmap bm = new Bitmap(64 * 24, 64 * 24, PixelFormat.Format16bppRgb565); + using (Graphics g = Graphics.FromImage(bm)) { + int ctx = m_amixt[0].XTileCount; + int cty = m_amixt[0].YTileCount; + for (int ty = 0; ty < 64; ty += cty) { + for (int tx = 0; tx < 64; tx += ctx) { + for (int tyT = 0; tyT < cty; tyT++) { + for (int txT = 0; txT < ctx; txT++) { + if (tx + txT >= 64 || ty + tyT >= 64) + continue; + g.DrawImageUnscaled(m_amixt[0].TileBitmaps[tyT * ctx + txT], (tx + txT) * 24, (ty + tyT) * 24); + } + } + } + } + TemplatePos[] atempl = GetTemplatePositions(); + foreach (TemplatePos tpos in atempl) { + int x = tpos.m_txOrigin * 24; + int y = tpos.m_tyOrigin * 24; + for (int ty = 0; ty < tpos.m_mixt.YTileCount; ty++) { + for (int tx = 0; tx < tpos.m_mixt.XTileCount; tx++) { + if (!tpos.m_afMapped[ty, tx]) + continue; + g.DrawImageUnscaled(tpos.m_mixt.TileBitmaps[ty * tpos.m_mixt.XTileCount + tx], x + tx * 24, y + ty * 24); + } + } + } + } + bm.Save(strFile, ImageFormat.Png); + } + + class TemplatePos { + public MixTemplate m_mixt; + public int m_txOrigin; + public int m_tyOrigin; + public bool[,] m_afMapped; + + public TemplatePos(MixTemplate mixt, int txOrigin, int tyOrigin, bool[,] afMapped) { + m_mixt = mixt; + m_txOrigin = txOrigin; + m_tyOrigin = tyOrigin; + m_afMapped = afMapped; + } + } + + public void ImportMap(LevelDoc lvld) { + TemplatePos[] atpos = GetTemplatePositions(); + ArrayList alsTiles = new ArrayList(); + foreach (TemplatePos tpos in atpos) { + int txOrigin = 64 - tpos.m_mixt.XTileCount - tpos.m_txOrigin; + int tyOrigin = tpos.m_tyOrigin; + bool[,] afDraw = new bool[tpos.m_mixt.YTileCount, tpos.m_mixt.XTileCount]; + for (int ty = 0; ty < tpos.m_mixt.YTileCount; ty++) { + for (int tx = 0; tx < tpos.m_mixt.XTileCount; tx++) { + afDraw[ty, tx] = tpos.m_afMapped[ty, tpos.m_mixt.XTileCount - tx - 1]; + } + } + Tile tile = new Tile(tpos.m_mixt.Index.ToString(), txOrigin, tyOrigin, afDraw, null); + alsTiles.Add(tile); + } + lvld.AddMapItems((IMapItem[])alsTiles.ToArray(typeof(IMapItem))); + } + + TemplatePos[] GetTemplatePositions() { + bool[,] afCellTaken = new bool[64,64]; + ArrayList alsTemplPos = new ArrayList(); + for (int ty = 0; ty < 64; ty++) { + for (int tx = 0; tx < 64; tx++) { + if (afCellTaken[ty, tx]) + continue; + if (m_acell[ty, tx].iTemplate == 255) + continue; + + // Find out what part of this template matches + MixTemplate mixt = m_amixt[m_acell[ty, tx].iTemplate]; + + // scg03ea has an invalid iTemplate outside the map bounds + if (mixt == null) + continue; + + int ctxTmpl = mixt.XTileCount; + int ctyTmpl = mixt.YTileCount; + bool[,]afMapped = new bool[ctyTmpl, ctxTmpl]; + int txTmpl = (int)m_acell[ty, tx].iTile % ctxTmpl; + int tyTmpl = (int)m_acell[ty, tx].iTile / ctxTmpl; + int txOrigin = tx - txTmpl; + int tyOrigin = ty - tyTmpl; + + for (tyTmpl = 0; tyTmpl < ctyTmpl; tyTmpl++) { + for (txTmpl = 0; txTmpl < ctxTmpl; txTmpl++) { + int txT = txOrigin + txTmpl; + int tyT = tyOrigin + tyTmpl; + if (txT < 0 || tyT < 0 || txT >= 64 || tyT >= 64) + continue; + if (afCellTaken[tyT, txT]) + continue; + Cell cell = m_acell[tyT, txT]; + if (cell.iTemplate == 255) + continue; + if (cell.iTemplate != m_acell[ty, tx].iTemplate) + continue; + if (cell.iTile != tyTmpl * ctxTmpl + txTmpl) + continue; + afMapped[tyTmpl, txTmpl] = true; + afCellTaken[tyT, txT] = true; + } + } + alsTemplPos.Add(new TemplatePos(mixt, txOrigin, tyOrigin, afMapped)); + } + } + return (TemplatePos[])alsTemplPos.ToArray(typeof(TemplatePos)); + } + } + + public class MixTemplate { + public int XTileCount; + public int YTileCount; + public int ImageCount; + public int TileCount; + public int Index; + public Bitmap[] TileBitmaps; + private byte[] m_mpiTileiTile; + + public MixTemplate(Stream stm, int ctx, int cty, Color[] aclrPalette, int index) { + XTileCount = ctx; + YTileCount = cty; + Index = index; + + BinaryReader brdr = new BinaryReader(stm); + + // From cncmap1f.txt: + // Header = record + // Width : word; {Width of images, always 24 (18h)} + // Heigth : word; {Heigth of images, always 24} + // NumTil : word; {Number of Tiles (may differ from num. of Images} + // Zero1 : word; {Seems to be always 0000h} + // Size : longint; {size of file} + // ImgStart : longint; {Offset of first image} + // Zero2 : longint; {Seems to be always 00000000h} + // ID1 : word; {Always FFFFh} + // ID2 : word; {Always 1A0Dh (or 0D1Ah I can't remeber} + // Index2 : longint; {Offset of Index2} + // Index1 : longint; {Offset of Index1} {I will explain these later} + // end; + + // Skip width and height word[2] + brdr.ReadUInt16(); + brdr.ReadUInt16(); + + // Count of tiles + TileCount = brdr.ReadInt16(); + + // Zero1 + brdr.ReadUInt16(); + + // Size of file + brdr.ReadInt32(); + + // ImgStart + int ibImageStart = brdr.ReadInt32(); + + // Zero2 + brdr.ReadInt32(); + + // ID1 & ID2 + brdr.ReadUInt16(); + brdr.ReadUInt16(); + + // Index2 & Index1 + int ibIndex2 = brdr.ReadInt32(); + int ibIndex1 = brdr.ReadInt32(); + brdr.BaseStream.Seek(ibIndex1, SeekOrigin.Begin); + m_mpiTileiTile = brdr.ReadBytes(TileCount); + + // Derive useful information + ImageCount = (ibIndex1 - ibImageStart) / (24 * 24); + + // Load images + TileBitmaps = new Bitmap[TileCount]; + for (int n = 0; n < TileCount; n++) { + int iTile = m_mpiTileiTile[n]; + if (iTile == 0xff) + continue; + brdr.BaseStream.Seek(ibImageStart + iTile * 24 * 24, SeekOrigin.Begin); + byte[] abImage = brdr.ReadBytes(24 * 24); + TileBitmaps[n] = LoadTileImage(abImage, aclrPalette); + } + + brdr.Close(); + } + + Bitmap LoadTileImage(byte[] abImage, Color[] aclrPalette) { + Bitmap bm = new Bitmap(24, 24, PixelFormat.Format24bppRgb); + for (int n = 0; n < abImage.Length; n++) + bm.SetPixel(n % 24, n / 24, aclrPalette[abImage[n]]); + return bm; + } + } + + class MixReader { + FileStream m_fstm; + BinaryReader m_brdr; + int m_cFiles; + int m_cbBodyLength; + ArrayList m_alDir; + + public MixReader(string strFile) { + m_fstm = new FileStream(strFile, FileMode.Open, FileAccess.Read); + m_brdr = new BinaryReader(m_fstm); + ReadDirectory(); + } + + public void Dispose() { + m_brdr.Close(); + } + + struct DirectoryEntry { + public uint ID; + public int ibStart; + public int cbLength; + } + + private bool ReadDirectory() { + m_brdr.BaseStream.Seek(0, SeekOrigin.Begin); + + m_cFiles = m_brdr.ReadInt16(); + m_cbBodyLength = m_brdr.ReadInt32(); + + m_alDir = new ArrayList(m_cFiles); + + for (int i = 0; i < m_cFiles; i++) { + DirectoryEntry de = new DirectoryEntry(); + de.ID = m_brdr.ReadUInt32(); + de.ibStart = m_brdr.ReadInt32() + m_cFiles * 12 + 6; + de.cbLength = m_brdr.ReadInt32(); + m_alDir.Add(de); + } + return true; + } + + public Stream GetFileStream(int index) { + DirectoryEntry de = (DirectoryEntry)m_alDir[index]; + m_brdr.BaseStream.Seek(de.ibStart, SeekOrigin.Begin); + return (Stream)new MemoryStream(m_brdr.ReadBytes(de.cbLength)); + } + } +} \ No newline at end of file diff --git a/m/scenery.cs b/m/scenery.cs new file mode 100644 index 0000000..cc4ddf0 --- /dev/null +++ b/m/scenery.cs @@ -0,0 +1,100 @@ +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Runtime.Serialization; +using System.Text.RegularExpressions; +using SpiffLib; + +namespace m { + [Serializable] + public class Scenery : MapItem, ISerializable { + private String m_strName; + private GobImage m_gimg; + + public Scenery(String strName) { + m_strName = strName; + m_gimg = Globals.GetGobImage(strName, false); + } + + public Scenery(String strName, int tx, int ty) { + m_strName = strName; + m_tx = tx; + m_ty = ty; + m_gimg = Globals.GetGobImage(strName, false); + } + + public Scenery(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { + m_strName = info.GetString("Name"); + m_gimg = Globals.GetGobImage(m_strName, false); + } + + public Scenery(string strName, string strValue, int txOrigin, int tyOrigin) { + Regex re = new Regex(@"^(?\d+),(?\w+).tbm,(?\d+),(?\d+)$"); + Match m = re.Match(strValue); + m_strName = m.Groups["name"].Value; + m_tx = int.Parse(m.Groups["tx"].Value) + txOrigin; + m_ty = int.Parse(m.Groups["ty"].Value) + tyOrigin; + m_gimg = Globals.GetGobImage(m_strName, false); + } + + public override Ini.Property GetIniProperty(int txOrigin, int tyOrigin) { + // For example: nil=scenery,image.tbm,80,100 + return new Ini.Property("nil", "kgt" + GetType().Name + "," + m_strName + ".tbm," + (m_tx - txOrigin).ToString() + "," + (m_ty - tyOrigin).ToString()); + } + + public override void GetObjectData(SerializationInfo info, StreamingContext context) { + base.GetObjectData(info, context); + info.AddValue("Name", m_strName); + } + + // IMapItem + + public override Bitmap GetBitmap(Size sizTile, TemplateDoc tmpd) { + Bitmap[] abm = m_gimg.GetBitmapSides(sizTile); + return abm[0]; + } + + public override Point GetCenterPoint(Size sizTile) { + Size sizGob = m_gimg.GetSize(sizTile); + return new Point((int)m_tx * sizTile.Width + sizGob.Width / 2, + (int)m_ty * sizTile.Height + sizGob.Height / 2); + } + + public override Rectangle GetBoundingRectAt(int x, int y, Size sizTile, TemplateDoc tmpd) { + Size sizGob = m_gimg.GetSize(sizTile); + return new Rectangle(x, y, sizGob.Width, sizGob.Height); + } + + public override bool HitTest(int x, int y, Size sizTile, TemplateDoc tmpd) { + int xT = x - ((int)m_tx * sizTile.Width); + int yT = y - ((int)m_ty * sizTile.Height); + Size sizGob = m_gimg.GetSize(sizTile); + if (xT > 0 && xT < sizGob.Width && yT > 0 && yT < sizGob.Height) { + Bitmap[] abm = m_gimg.GetBitmapSides(sizTile); + return abm[0].GetPixel(xT, yT) != Color.Transparent; + } + return false; + } + + public override Object Clone() { + Object[] aobj = { m_strName, (int)m_tx, (int)m_ty }; + return (Object)System.Activator.CreateInstance(GetType(), aobj); + } + + public override void Draw(Graphics g, int x, int y, Size sizTile, TemplateDoc tmpd, LayerType layer, bool fSelected) { + if (layer == LayerType.Scenery) { + Bitmap[] abm = m_gimg.GetBitmapSides(sizTile); + Bitmap bm = abm[0]; + if (fSelected) { + Rectangle rcDst = new Rectangle(x, y, bm.Width, bm.Height); + ImageAttributes attr = new ImageAttributes(); + attr.SetGamma(0.5f); + g.DrawImage(bm, rcDst, 0, 0, bm.Width, bm.Height, GraphicsUnit.Pixel, attr); + } else { + g.DrawImage(bm, x, y); + } + } + } + } +} diff --git a/m/structure.cs b/m/structure.cs new file mode 100644 index 0000000..b0f7f33 --- /dev/null +++ b/m/structure.cs @@ -0,0 +1,299 @@ +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Runtime.Serialization; +using SpiffLib; +using System.Text.RegularExpressions; + +namespace m { + // Wrappers for graceful future versioning + [Serializable] + public class RocketTower : Structure { + public RocketTower(Side side, int tx, int ty) : base (side, tx, ty) { + } + + public RocketTower(string strName, string strValue, int txOrigin, int tyOrigin) : + base(strName, strValue, txOrigin, tyOrigin) { + } + + public RocketTower(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { + } + } + + [Serializable] + public class MachineGunTower : Structure { + public MachineGunTower(Side side, int tx, int ty) : base (side, tx, ty) { + } + + public MachineGunTower(string strName, string strValue, int txOrigin, int tyOrigin) : + base(strName, strValue, txOrigin, tyOrigin) { + } + + public MachineGunTower(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { + } + } + + [Serializable] + public class Warehouse : Structure { + public Warehouse(Side side, int tx, int ty) : base (side, tx, ty) { + } + + public Warehouse(string strName, string strValue, int txOrigin, int tyOrigin) : + base(strName, strValue, txOrigin, tyOrigin) { + } + + public Warehouse(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { + } + + public override int ctx { + get { + return 2; + } + } + + public override int cty { + get { + return 2; + } + } + } + + [Serializable] + public class VehicleTransportStation : Structure { + public VehicleTransportStation(Side side, int tx, int ty) : base (side, tx, ty) { + } + + public VehicleTransportStation(string strName, string strValue, int txOrigin, int tyOrigin) : + base(strName, strValue, txOrigin, tyOrigin) { + } + + public VehicleTransportStation(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { + } + + public override int ctx { + get { + return 3; + } + } + + public override int cty { + get { + return 2; + } + } + } + + [Serializable] + public class Radar : Structure { + public Radar(Side side, int tx, int ty) : base (side, tx, ty) { + } + + public Radar(string strName, string strValue, int txOrigin, int tyOrigin) : + base(strName, strValue, txOrigin, tyOrigin) { + } + + public Radar(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { + } + + public override int ctx { + get { + return 2; + } + } + + public override int cty { + get { + return 2; + } + } + } + + [Serializable] + public class ResearchCenter : Structure { + public ResearchCenter(Side side, int tx, int ty) : base (side, tx, ty) { + } + + public ResearchCenter(string strName, string strValue, int txOrigin, int tyOrigin) : + base(strName, strValue, txOrigin, tyOrigin) { + } + + public ResearchCenter(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { + } + + public override int ctx { + get { + return 2; + } + } + + public override int cty { + get { + return 2; + } + } + } + + [Serializable] + public class Headquarters : Structure { + public Headquarters(Side side, int tx, int ty) : base (side, tx, ty) { + } + + public Headquarters(string strName, string strValue, int txOrigin, int tyOrigin) : + base(strName, strValue, txOrigin, tyOrigin) { + } + + public Headquarters(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { + } + + public override int ctx { + get { + return 3; + } + } + + public override int cty { + get { + return 2; + } + } + } + + [Serializable] + public class Reactor : Structure { + public Reactor(Side side, int tx, int ty) : base (side, tx, ty) { + } + + public Reactor(string strName, string strValue, int txOrigin, int tyOrigin) : + base(strName, strValue, txOrigin, tyOrigin) { + } + + public Reactor(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { + } + + public override int ctx { + get { + return 2; + } + } + + public override int cty { + get { + return 2; + } + } + } + + [Serializable] + public class HumanResourceCenter : Structure { + public HumanResourceCenter(Side side, int tx, int ty) : base (side, tx, ty) { + } + + public HumanResourceCenter(string strName, string strValue, int txOrigin, int tyOrigin) : + base(strName, strValue, txOrigin, tyOrigin) { + } + + public HumanResourceCenter(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { + } + + public override int ctx { + get { + return 3; + } + } + + public override int cty { + get { + return 2; + } + } + } + + [Serializable] + public class Processor : Structure { + public Processor(Side side, int tx, int ty) : base (side, tx, ty) { + } + + public Processor(string strName, string strValue, int txOrigin, int tyOrigin) : + base(strName, strValue, txOrigin, tyOrigin) { + } + + public Processor(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { + } + + public override int ctx { + get { + return 3; + } + } + + public override int cty { + get { + return 2; + } + } + } + + [Serializable] + public class Replicator : Structure { + public Replicator(Side side, int tx, int ty) : base (side, tx, ty) { + } + + public Replicator(string strName, string strValue, int txOrigin, int tyOrigin) : + base(strName, strValue, txOrigin, tyOrigin) { + } + + public Replicator(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { + } + + public override int ctx { + get { + return 5; + } + } + + public override int cty { + get { + return 4; + } + } + } + + [Serializable] + public class Activator : Unit { + public Activator(Side side, int tx, int ty) : base (side, tx, ty) { + } + + public Activator(string strName, string strValue, int txOrigin, int tyOrigin) : + base(strName, strValue, txOrigin, tyOrigin) { + } + + public Activator(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { + } + + public override Point GetTileOrigin(Size sizTile) { + // Activators have an origin at the top left of a tile + + return new Point(0, 0); + } + } + + [Serializable] + public class Structure : Unit { + public Structure(Side side, int tx, int ty) : base(side, tx, ty) { + } + + public Structure(string strName, string strValue, int txOrigin, int tyOrigin) : + base(strName, strValue, txOrigin, tyOrigin) { + } + + public Structure(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { + } + + public override Point GetTileOrigin(Size sizTile) { + // Structs have an origin at the top left of a tile + + return new Point(0, 0); + } + } +} diff --git a/m/tile.cs b/m/tile.cs new file mode 100644 index 0000000..2fd8085 --- /dev/null +++ b/m/tile.cs @@ -0,0 +1,278 @@ +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; +using System.Runtime.Serialization; +using SpiffLib; + +namespace m { + [Serializable] + public class Tile : MapItem, ISerializable { + string m_strName = null; + bool[,] m_afVisible = null; + bool[,] m_afOccupancy = null; + Bitmap m_bmCache = null; + TemplateDoc m_tmpdCache = null; + Template m_tmplCache = null; + + public Tile(TemplateDoc tmpd, string strName, int tx, int ty) { + m_strName = strName; + m_tx = tx; + m_ty = ty; + Template tmpl = GetTemplate(tmpd); + m_afOccupancy = tmpl.OccupancyMap; + m_afVisible = null; + InitCommon(); + } + + public Tile(string strName, int tx, int ty, bool[,] afVisible, bool[,] afOccupancy) { + m_tx = tx; + m_ty = ty; + m_afVisible = afVisible; + m_strName = strName; + m_afOccupancy = afOccupancy; + InitCommon(); + } + + public Tile(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { + m_strName = null; + try { + m_strName = info.GetString("Name"); + } catch { + m_strName = info.GetInt32("Cookie").ToString(); + } + + m_afVisible = (bool[,])info.GetValue("Visibility", typeof(bool[,])); + + try { + m_afOccupancy = (bool[,])info.GetValue("Occupancy", typeof(bool[,])); + } catch { + TemplateDoc tmpd = (TemplateDoc)DocManager.GetActiveDocument(typeof(TemplateDoc)); + Template tmpl = tmpd.FindTemplate(m_strName); + if (tmpl != null) { + m_afOccupancy = tmpl.OccupancyMap; + } else { + m_afOccupancy = new bool[1, 1]; + } + } + + InitCommon(); + } + + public override void GetObjectData(SerializationInfo info, StreamingContext context) { + base.GetObjectData(info, context); + info.AddValue("Name", m_strName); + info.AddValue("Visibility", m_afVisible); + info.AddValue("Occupancy", m_afOccupancy); + } + + void InitCommon() { + TemplateDocTemplate doct = (TemplateDocTemplate)DocManager.FindDocTemplate(typeof(TemplateDoc)); + doct.TemplateChanged += new TemplateDocTemplate.TemplateChangedHandler(TemplateDocTemplate_TemplateChanged); + } + + void TemplateDocTemplate_TemplateChanged(TemplateDoc tmpd, string strProperty, string strName, string strParam) { + if (m_strName != strName) + return; + + // Flush cached items + + GetTemplate(null); + + // Handle changes + + switch (strProperty) { + case "Name": + m_strName = strParam; + OnPropertyChanged(this, "Name"); + break; + + case "Bitmap": + OnPropertyChanged(this, "Bitmap"); + break; + } + } + + public string Name { + get { + return m_strName; + } + } + + public Template GetTemplate(TemplateDoc tmpd) { + if (tmpd == m_tmpdCache) + return m_tmplCache; + m_tmpdCache = null; + m_tmplCache = null; + m_bmCache = null; + if (tmpd == null) + return null; + Template tmpl = tmpd.FindTemplate(m_strName); + if (tmpl != null) { + m_tmpdCache = tmpd; + m_tmplCache = tmpl; + m_afOccupancy = tmpl.OccupancyMap; + return tmpl; + } + return null; + } + + [BrowsableAttribute(false)] + public bool[,] Visibility { + get { + return m_afVisible; + } + set { + // Setting to null? + + m_bmCache = null; + if (value == null) { + m_afVisible = null; + return; + } + + // Same as occupancy map? Then no visibility map + + bool fEqual = true; + for (int ty = 0; ty < value.GetLength(0); ty++) { + for (int tx = 0; tx < value.GetLength(1); tx++) { + if (value[ty, tx] != m_afOccupancy[ty, tx]) + fEqual = false; + } + } + + if (fEqual) { + m_afVisible = null; + } else { + m_afVisible = value; + } + } + } + + + public bool IsVisible(int tx, int ty) { + if (m_afVisible == null) { + return true; + } + if (tx < m_afVisible.GetLength(1) && ty < m_afVisible.GetLength(0)) { + return m_afVisible[ty, tx]; + } + return false; + } + + // IMapItem + + public override Bitmap GetBitmap(Size sizTile, TemplateDoc tmpd) { + // Get the template. This'll invalidate m_bmCache if necessary + + Template tmpl = GetTemplate(tmpd); + + // If already cached, use it + + if (m_bmCache != null) + return m_bmCache; + + // If we haven't mapped to a template, create a correctly sized bitmap as a placeholder + + bool fDispose = false; + Bitmap bm; + if (tmpl != null) { + bm = tmpl.Bitmap; + + // Dont use the bitmap directly, it might have knobbies on that'll create + // problems at compile time. + +#if false + if (m_afVisible == null) + return bm; +#endif + } else { + fDispose = true; + bm = new Bitmap(m_afOccupancy.GetLength(1) * sizTile.Width, m_afOccupancy.GetLength(0) * sizTile.Height); + using (Graphics gT = Graphics.FromImage(bm)) + gT.Clear(Color.Firebrick); + } + + // Fill in the chunks that are visible + + int cxT = m_afOccupancy.GetLength(1) * sizTile.Width; + int cyT = m_afOccupancy.GetLength(0) * sizTile.Height; + m_bmCache = new Bitmap(cxT, cyT); + using (Graphics g = Graphics.FromImage(m_bmCache)) { + g.Clear(Color.FromArgb(255, 0, 255)); + for (int ty = 0; ty < m_afOccupancy.GetLength(0); ty++) { + for (int tx = 0; tx < m_afOccupancy.GetLength(1); tx++) { + if (!m_afOccupancy[ty, tx]) + continue; + if (!IsVisible(tx, ty)) { + continue; + } + Rectangle rcDst = new Rectangle(tx * sizTile.Width, ty * sizTile.Height, sizTile.Width, sizTile.Height); + g.DrawImage(bm, rcDst, tx * sizTile.Width, ty * sizTile.Height, sizTile.Width, sizTile.Height, GraphicsUnit.Pixel); + } + } + } + if (fDispose) + bm.Dispose(); + + // Hide the areas that are invisible + + TemplateTools.MakeTransparent(m_bmCache); + return m_bmCache; + } + + + public override Point GetCenterPoint(Size sizTile) { + int x = (int)m_tx * sizTile.Width + m_afOccupancy.GetLength(1) * sizTile.Width / 2; + int y = (int)m_ty * sizTile.Height + m_afOccupancy.GetLength(0) * sizTile.Height / 2; + return new Point(x, y); + } + + public override Rectangle GetBoundingRectAt(int x, int y, Size sizTile, TemplateDoc tmpd) { + Bitmap bm = GetBitmap(sizTile, tmpd); + return new Rectangle(x, y, bm.Width, bm.Height); + } + + public override bool HitTest(int x, int y, Size sizTile, TemplateDoc tmpd) { + int xOffset = x - (int)m_tx * sizTile.Width; + int yOffset = y - (int)m_ty * sizTile.Height; + Rectangle rc = new Rectangle(new Point(0, 0), GetBitmap(sizTile, tmpd).Size); + if (!rc.Contains(xOffset, yOffset)) + return false; + int tx = xOffset / sizTile.Width; + int ty = yOffset / sizTile.Height; + try { + if (m_afVisible != null) + return IsVisible(tx, ty); + return m_afOccupancy[ty, tx]; + } catch { + return false; + } + } + + public override Object Clone() { + Tile tile = new Tile(m_strName, (int)m_tx, (int)m_ty, m_afVisible, m_afOccupancy); + return (Object)tile; + } + + public override void Draw(Graphics g, int x, int y, Size sizTile, TemplateDoc tmpd, LayerType layer, bool fSelected) { + if (layer == LayerType.TileMap) { + Bitmap bm = GetBitmap(sizTile, tmpd); + if (fSelected) { + Rectangle rcDst = new Rectangle(x, y, bm.Width, bm.Height); + ImageAttributes attr = new ImageAttributes(); + attr.SetGamma(0.5f); + g.DrawImage(bm, rcDst, 0, 0, bm.Width, bm.Height, GraphicsUnit.Pixel, attr); + } else { + g.DrawImage(bm, x, y); + } + } + } + + public override Ini.Property GetIniProperty(int xOrigin, int yOrigin) { + return null; + } + } +} diff --git a/m/tilecollection.cs b/m/tilecollection.cs new file mode 100644 index 0000000..f31f148 --- /dev/null +++ b/m/tilecollection.cs @@ -0,0 +1,146 @@ +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.Collections; +using System.Data; + +namespace m +{ + public delegate void TileAddedEventHandler(Tile tile); + public delegate void TileRemovedEventHandler(Tile tile); + + /// + /// Summary description for TileCollection. + /// + public class TileCollection + { + private TilesDataSet m_dsTiles = new TilesDataSet(); + private ArrayList m_alsTiles = new ArrayList(); + public event TileAddedEventHandler TileAdded; + public event TileRemovedEventHandler TileRemoved; + + public TileCollection() { + } + + public bool Load(String strFile) { + try { + // At least make sure the file is valid before anything + TilesDataSet dsTiles = new TilesDataSet(); + dsTiles.ReadXml(strFile); + + // Remove current tiles + while (Count != 0) + RemoveTile(this[0]); + + // Use this TileDataSet and load tiles + m_dsTiles = dsTiles; + foreach (TilesDataSet.TilesRow row in m_dsTiles.Tiles) { + Tile tile = new Tile(row); + m_alsTiles.Add(tile); + OnTileAdded(tile); + } + return true; + } catch { + return false; + } + } + + public bool Save(String strFile) { + try { + foreach (Tile tile in m_alsTiles) + tile.Save(); + m_dsTiles.WriteXml(strFile); + return true; + } catch { + return false; + } + } + + public Tile NewTile(String strFileBitmap) { + TilesDataSet.TilesRow row = m_dsTiles.Tiles.NewTilesRow(); + Tile tile = new Tile(row); + if (tile.Import(strFileBitmap)) { + m_dsTiles.Tiles.AddTilesRow(row); + m_alsTiles.Add(tile); + OnTileAdded(tile); + return tile; + } + return null; + } + + public void RemoveTile(Tile tile) { + if (!m_alsTiles.Contains(tile)) + return; + m_dsTiles.Tiles.RemoveTilesRow(tile.Row); + m_alsTiles.Remove(tile); + OnTileRemoved(tile); + } + + public Tile FindTile(int cookie) { + foreach (Tile tile in m_alsTiles) { + if (tile.Cookie == cookie) + return tile; + } + return null; + } + + // Event firing + + private void OnTileRemoved(Tile tile) { + if (TileRemoved != null) + TileRemoved(tile); + } + + private void OnTileAdded(Tile tile) { + if (TileAdded != null) + TileAdded(tile); + } + + // Properties + + public Tile this[int index] { + get { + return (Tile)m_alsTiles[index]; + } + } + + public int Count { + get { + return m_alsTiles.Count; + } + } + + // Enumeration support + + public TileEnumerator GetEnumerator() { + return new TileEnumerator(this); + } + + public class TileEnumerator { + private TileCollection m_tcol; + private int m_pos = -1; + + public TileEnumerator(TileCollection tcol) { + m_tcol = tcol; + } + + public bool MoveNext() { + if (m_pos < m_tcol.m_alsTiles.Count - 1) { + m_pos++; + return true; + } + return false; + } + + public void Reset() { + m_pos = -1; + } + + public Tile Current { + get { + return (Tile)m_tcol.m_alsTiles[m_pos]; + } + } + } + } +} diff --git a/m/tiles.zip b/m/tiles.zip new file mode 100644 index 0000000..6fbff15 Binary files /dev/null and b/m/tiles.zip differ diff --git a/m/toolstrip.bmp b/m/toolstrip.bmp new file mode 100644 index 0000000..00f64e2 Binary files /dev/null and b/m/toolstrip.bmp differ diff --git a/m/unit.cs b/m/unit.cs new file mode 100644 index 0000000..b08e685 --- /dev/null +++ b/m/unit.cs @@ -0,0 +1,492 @@ +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Runtime.Serialization; +using SpiffLib; +using System.Text.RegularExpressions; +using System.Collections.Specialized; +using System.Drawing.Design; +using System.ComponentModel; + +namespace m { + // Wrappers for graceful future versioning + [Serializable] + public class GalaxMiner : MobileUnit { + public GalaxMiner(Side side, int tx, int ty) : base(side, tx, ty) { + m_aggr = Aggressiveness.Coward; + } + + public GalaxMiner(Side side, int tx, int ty, Aggressiveness aggr, CaBase cab) : base(side, tx, ty, aggr, cab) { + m_aggr = Aggressiveness.Coward; + } + + public GalaxMiner(string strName, string strValue, int txOrigin, int tyOrigin) : + base(strName, strValue, txOrigin, tyOrigin) { + } + + public GalaxMiner(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { + try { + m_aggr = (Aggressiveness)info.GetInt32("Aggressiveness"); + } catch (SerializationException) { + m_aggr = Aggressiveness.Coward; + } + } + } + + [Serializable] + public class ShortRangeInfantry : MobileUnit { + public ShortRangeInfantry(Side side, int tx, int ty) : base(side, tx, ty) { + } + + public ShortRangeInfantry(Side side, int tx, int ty, Aggressiveness aggr, CaBase cab) : base(side, tx, ty, aggr, cab) { + } + + public ShortRangeInfantry(string strName, string strValue, int txOrigin, int tyOrigin) : + base(strName, strValue, txOrigin, tyOrigin) { + } + + public ShortRangeInfantry(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { + } + } + + [Serializable] + public class LongRangeInfantry : MobileUnit { + public LongRangeInfantry(Side side, int tx, int ty) : base(side, tx, ty) { + } + + public LongRangeInfantry(Side side, int tx, int ty, Aggressiveness aggr, CaBase cab) : base(side, tx, ty, aggr, cab) { + } + + public LongRangeInfantry(string strName, string strValue, int txOrigin, int tyOrigin) : + base(strName, strValue, txOrigin, tyOrigin) { + } + + public LongRangeInfantry(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { + } + } + + [Serializable] + public class TakeoverSpecialist : MobileUnit { + public TakeoverSpecialist(Side side, int tx, int ty) : base(side, tx, ty) { + m_aggr = Aggressiveness.Coward; + } + + public TakeoverSpecialist(Side side, int tx, int ty, Aggressiveness aggr, CaBase cab) : base(side, tx, ty, aggr, cab) { + m_aggr = Aggressiveness.Coward; + } + + public TakeoverSpecialist(string strName, string strValue, int txOrigin, int tyOrigin) : + base(strName, strValue, txOrigin, tyOrigin) { + } + + public TakeoverSpecialist(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { + try { + m_aggr = (Aggressiveness)info.GetInt32("Aggressiveness"); + } catch (SerializationException) { + m_aggr = Aggressiveness.Coward; + } + } + } + + [Serializable] + public class LightTank : MobileUnit { + public LightTank(Side side, int tx, int ty) : base(side, tx, ty) { + } + + public LightTank(Side side, int tx, int ty, Aggressiveness aggr, CaBase cab) : base(side, tx, ty, aggr, cab) { + } + + public LightTank(string strName, string strValue, int txOrigin, int tyOrigin) : + base(strName, strValue, txOrigin, tyOrigin) { + } + + public LightTank(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { + } + } + + [Serializable] + public class MediumTank : MobileUnit { + public MediumTank(Side side, int tx, int ty) : base(side, tx, ty) { + } + + public MediumTank(Side side, int tx, int ty, Aggressiveness aggr, CaBase cab) : base(side, tx, ty, aggr, cab) { + } + + public MediumTank(string strName, string strValue, int txOrigin, int tyOrigin) : + base(strName, strValue, txOrigin, tyOrigin) { + } + + public MediumTank(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { + } + } + + [Serializable] + public class MachineGunVehicle : MobileUnit { + public MachineGunVehicle(Side side, int tx, int ty) : base(side, tx, ty) { + } + + public MachineGunVehicle(Side side, int tx, int ty, Aggressiveness aggr, CaBase cab) : base(side, tx, ty, aggr, cab) { + } + + public MachineGunVehicle(string strName, string strValue, int txOrigin, int tyOrigin) : + base(strName, strValue, txOrigin, tyOrigin) { + } + + public MachineGunVehicle(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { + } + } + + [Serializable] + public class RocketVehicle : MobileUnit { + public RocketVehicle(Side side, int tx, int ty) : base(side, tx, ty) { + } + + public RocketVehicle(Side side, int tx, int ty, Aggressiveness aggr, CaBase cab) : base(side, tx, ty, aggr, cab) { + } + + public RocketVehicle(string strName, string strValue, int txOrigin, int tyOrigin) : + base(strName, strValue, txOrigin, tyOrigin) { + } + + public RocketVehicle(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { + } + } + + [Serializable] + public class MobileHeadquarters : MobileUnit { + public MobileHeadquarters(Side side, int tx, int ty) : base(side, tx, ty) { + m_aggr = Aggressiveness.Coward; + } + + public MobileHeadquarters(Side side, int tx, int ty, Aggressiveness aggr, CaBase cab) : base(side, tx, ty, aggr, cab) { + m_aggr = Aggressiveness.Coward; + } + + public MobileHeadquarters(string strName, string strValue, int txOrigin, int tyOrigin) : + base(strName, strValue, txOrigin, tyOrigin) { + } + + public MobileHeadquarters(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { + try { + m_aggr = (Aggressiveness)info.GetInt32("Aggressiveness"); + } catch (SerializationException) { + m_aggr = Aggressiveness.Coward; + } + } + } + + [Serializable] + public class Artillery : MobileUnit { + public Artillery(Side side, int tx, int ty) : base(side, tx, ty) { + } + + public Artillery(Side side, int tx, int ty, Aggressiveness aggr, CaBase cab) : base(side, tx, ty, aggr, cab) { + } + + public Artillery(string strName, string strValue, int txOrigin, int tyOrigin) : + base(strName, strValue, txOrigin, tyOrigin) { + } + + public Artillery(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { + } + } + + [Serializable] + public class Andy : MobileUnit { + public Andy(Side side, int tx, int ty) : base(side, tx, ty) { + } + + public Andy(Side side, int tx, int ty, Aggressiveness aggr, CaBase cab) : base(side, tx, ty, aggr, cab) { + } + + public Andy(string strName, string strValue, int txOrigin, int tyOrigin) : + base(strName, strValue, txOrigin, tyOrigin) { + } + + public Andy(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { + } + } + + [Serializable] + public class Fox : MobileUnit { + public Fox(Side side, int tx, int ty) : base(side, tx, ty) { + } + + public Fox(Side side, int tx, int ty, Aggressiveness aggr, CaBase cab) : base(side, tx, ty, aggr, cab) { + } + + public Fox(string strName, string strValue, int txOrigin, int tyOrigin) : + base(strName, strValue, txOrigin, tyOrigin) { + } + + public Fox(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { + } + } + + public enum Aggressiveness { + Coward, + Pacifist, + SelfDefense, + Defender, + Pitbull + } + + [Serializable] + public class MobileUnit : Unit { + protected Aggressiveness m_aggr; + + public MobileUnit(Side side, int tx, int ty) : base(side, tx, ty) { + m_aggr = Aggressiveness.Defender; + m_cab = new GuardUnitAction(); + } + + public MobileUnit(Side side, int tx, int ty, Aggressiveness aggr, CaBase cab) : base(side, tx, ty) { + m_aggr = aggr; + m_cab = cab; + } + + public MobileUnit(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { + try { + m_aggr = (Aggressiveness)info.GetInt32("Aggressiveness"); + } catch (SerializationException) { + m_aggr = Aggressiveness.Defender; + } + + try { + m_cab = (CaBase)info.GetValue("Action", typeof(CaBase)); + } catch (SerializationException) { + m_cab = new GuardUnitAction(); + } + Init(); + } + + public MobileUnit(string strName, string strValue, int txOrigin, + int tyOrigin) : base(strName, strValue, txOrigin, tyOrigin) { + Regex re = new Regex(@"^(?\d+),(?\d+),(?\d+),(?\d+),(?\d+),(?\d+),(?\d+)(,\{(?.*)\})?$"); + Match m = re.Match(strValue); + m_aggr = (Aggressiveness)int.Parse(m.Groups["aggr"].Value); + if (m.Groups["cab"] != null) { + m_cab = UnitActionLoader.LoadIni(m.Groups["cab"].Value); + } + } + + public override Ini.Property GetIniProperty(int txOrigin, int tyOrigin) { + // For example: nil=kgtRocketVehicle,side,38,120,0,100,knAggressivenessPitbull,{knGuardAreaAction,2} + Ini.Property prp = base.GetIniProperty(txOrigin, tyOrigin); + prp.Value += ",knAggressiveness" + m_aggr.ToString(); + if (m_cab != null) + prp.Value += ",{" + m_cab.ToSaveString() + "}"; + return prp; + } + + public override void GetObjectData(SerializationInfo info, StreamingContext context) { + base.GetObjectData(info, context); + info.AddValue("Aggressiveness", m_aggr); + info.AddValue("Action", m_cab); + } + + public override Object Clone() { + Object[] aobj = { m_side, (int)m_tx, (int)m_ty, m_aggr, m_cab.Clone() }; + return (Object)System.Activator.CreateInstance(GetType(), aobj); + } + + [Category("Behavior")] + public Aggressiveness Aggressiveness { + get { + return m_aggr; + } + set { + if (m_aggr != value) { + m_aggr = value; + OnPropertyChanged(this, "Aggressiveness"); + } + } + } + + [EditorAttribute(typeof(ActionEditor), typeof(UITypeEditor)), Category("Behavior")] + public CaBase Action { + get { + return m_cab; + } + set { + if (m_cab != value) { + m_cab = value; + OnPropertyChanged(this, "Action"); + } + } + } + } + + public class ActionEditor : UITypeEditor { + // Indicates that the UITypeEditor provides a form-based (modal) dialog. + + public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) { + return UITypeEditorEditStyle.Modal; + } + + // Displays the UI for value selection. + + public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) { + CaBase cab = (CaBase)value; + cab = CaNew.DoModal(cab.Clone(), "Modify Action", "UnitAction"); + return cab == null ? value : cab; + } + } + + [Serializable] + public class Unit : MapItem, ISerializable { + protected Side m_side; + protected GobImage m_gimg; + protected CaBase m_cab; + protected int m_nHealth; + + public Unit(Side side) { + m_side = side; + Init(); + } + + public Unit(Side side, int tx, int ty) { + m_tx = tx; + m_ty = ty; + m_side = side; + Init(); + } + + public Unit(SerializationInfo info, StreamingContext ctx) : base (info, ctx) { + m_side = (Side)info.GetInt32("Side"); + try { + m_nHealth = info.GetInt32("Health"); + } catch (SerializationException) { + m_nHealth = 100; + } + Init(); + } + + public Unit(string strName, string strValue, int txOrigin, int tyOrigin) { + Regex re = new Regex(@"^(?\d+),(?\d+),(?\d+),(?\d+),(?\d+),(?\d+).*$"); + Match m = re.Match(strValue); + m_side = (Side)int.Parse(m.Groups["side"].Value); + m_tx = int.Parse(m.Groups["tx"].Value) + txOrigin; + m_ty = int.Parse(m.Groups["ty"].Value) + tyOrigin; + m_nHealth = int.Parse(m.Groups["health"].Value); + Init(); + } + + public override Ini.Property GetIniProperty(int txOrigin, int tyOrigin) { + // For example: nil=gobtype,side,38,120,0,100 + string strValue = "kgt" + GetType().Name + ","; + strValue += "k" + m_side.ToString() + ","; + strValue += (m_tx - txOrigin).ToString() + "," + (m_ty - tyOrigin).ToString(); + strValue += ",0," + m_nHealth; + return new Ini.Property("nil", strValue); + } + + public override void GetObjectData(SerializationInfo info, StreamingContext context) { + base.GetObjectData(info, context); + info.AddValue("Side", m_side); + info.AddValue("Health", m_nHealth); + } + + protected virtual void Init() { + m_gimg = Globals.GetGobImage(GetType().Name, true); + if (m_gimg == null) + throw new Exception("Cannot find image for gob " + GetType().Name); + } + + public Side Side { + get { + return m_side; + } + set { + if (m_side != value) { + m_side = value; + OnPropertyChanged(this, "Side"); + } + } + } + + public int Health { + get { + return m_nHealth; + } + set { + m_nHealth = value; + OnPropertyChanged(this, "Health"); + } + } + + public virtual Point GetTileOrigin(Size sizTile) { + // Units have an offset of mid-tile (to place their origin in the tile center, as per game code) + + return new Point(sizTile.Width / 2, sizTile.Height / 2); + } + + public virtual Size GetSize(Size sizTile) { + return m_gimg.GetSize(sizTile); + } + + // IMapItem + + public override Bitmap GetBitmap(Size sizTile, TemplateDoc tmpd) { + Bitmap[] abm = m_gimg.GetBitmapSides(sizTile); + return abm[(int)m_side]; + } + + public override Point GetCenterPoint(Size sizTile) { + Point ptTOrigin = GetTileOrigin(sizTile); + Point ptGobOrigin = m_gimg.GetOrigin(sizTile); + Size sizGob = m_gimg.GetSize(sizTile); + int x = (int)m_tx * sizTile.Width + ptTOrigin.X - ptGobOrigin.X; + int y = (int)m_ty * sizTile.Height + ptTOrigin.Y - ptGobOrigin.Y; + return new Point(x + sizGob.Width / 2, y + sizGob.Height / 2); + } + + public override Rectangle GetBoundingRectAt(int x, int y, Size sizTile, TemplateDoc tmpd) { + Point ptTOrigin = GetTileOrigin(sizTile); + Point ptGobOrigin = m_gimg.GetOrigin(sizTile); + int xT = x + ptTOrigin.X - ptGobOrigin.X; + int yT = y + ptTOrigin.Y - ptGobOrigin.Y; + Size siz = GetSize(sizTile); + return new Rectangle(xT, yT, siz.Width, siz.Height); + } + + public override bool HitTest(int x, int y, Size sizTile, TemplateDoc tmpd) { + Point ptTOrigin = GetTileOrigin(sizTile); + Point ptGobOrigin = m_gimg.GetOrigin(sizTile); + Size sizGob = m_gimg.GetSize(sizTile); + int xT = x - ((int)m_tx * sizTile.Width + ptTOrigin.X - ptGobOrigin.X); + int yT = y - ((int)m_ty * sizTile.Height + ptTOrigin.Y - ptGobOrigin.Y); + if (xT > 0 && xT < sizGob.Width && yT > 0 && yT < sizGob.Height) { + Bitmap[] abmGob = m_gimg.GetBitmapSides(sizTile); + return abmGob[(int)m_side].GetPixel(xT, yT) != Color.Transparent; + } + return false; + } + + public override Object Clone() { + Object[] aobj = { m_side, (int)m_tx, (int)m_ty }; + return (Object)System.Activator.CreateInstance(GetType(), aobj); + } + + public override void Draw(Graphics g, int x, int y, Size sizTile, TemplateDoc tmpd, LayerType layer, bool fSelected) { + Point ptTOrigin = GetTileOrigin(sizTile); + Point ptGobOrigin = m_gimg.GetOrigin(sizTile); + x += ptTOrigin.X - ptGobOrigin.X; + y += ptTOrigin.Y - ptGobOrigin.Y; + + if (layer == LayerType.DepthSorted) { + Bitmap bm = m_gimg.GetBitmapSides(sizTile)[(int)m_side]; + if (fSelected) { + Rectangle rcDst = new Rectangle(x, y, bm.Width, bm.Height); + ImageAttributes attr = new ImageAttributes(); + attr.SetGamma(0.5f); + g.DrawImage(bm, rcDst, 0, 0, bm.Width, bm.Height, GraphicsUnit.Pixel, attr); + } else { + g.DrawImage(bm, x, y); + } + } + } + } +} + diff --git a/m/worklist.txt b/m/worklist.txt new file mode 100644 index 0000000..5c6bc28 --- /dev/null +++ b/m/worklist.txt @@ -0,0 +1,24 @@ +- shouldn't be able to delete a referenced area, switch, or group without being shown the consequences +- areas, switches, groups should all be renameable w/o worries [switches are, and so are groups, I think] +- undo/redo +- change history [infinite undo?] +- VS.NET forms designer-like model (gui generates code and vice-versa) +- only need one resolution template collection +- show orphaned areas, switches, groups +- property editor should be cleared when + - level closed + - level switch (or updated) + - +- less clicks trigger editing +- illustrate area/group/trigger condition/action relationships +- turbo, robust F5 +- M as level debugger? +- validation +- hires units/scenery +- bug: can't click on units under areas even when areas are toggled off +- show unit counts and limits +- show power supply and demand +- show value of Galaxite (and selected Galaxite) +- show unit groups and their actions on-map (paths) +- show build distance limit +- diffable diff --git a/mpshared/constants.h b/mpshared/constants.h new file mode 100644 index 0000000..573c2ee --- /dev/null +++ b/mpshared/constants.h @@ -0,0 +1,115 @@ +#include "inc/basictypes.h" +#include "base/misc.h" + +#ifndef __CONSTANTS_H__ +#define __CONSTANTS_H__ + +namespace wi { + +const dword kdwProtocolCurrent = 0x485407d3; + +const byte XMSG_NONE = 0x00; +const byte XMSG_HANDSHAKE = 0x81; +const byte XMSG_HANDSHAKERESULT = 0x82; +const byte XMSG_SHOWMESSAGE = 0x83; +const byte XMSG_ECHO = 0x84; +const byte XMSG_PROTOCOLERROR = 0x85; +const byte XMSG_LOGIN = 0x92; +const byte XMSG_LOGINRESULT = 0x93; +const byte XMSG_SIGNOUT = 0x94; +const byte XMSG_SIGNOUTRESULT = 0x95; +const byte XMSG_LOBBYJOIN = 0xa0; +const byte XMSG_LOBBYJOINRESULT = 0xa1; +const byte XMSG_LOBBYCREATEROOM = 0xa2; +const byte XMSG_LOBBYCREATEROOMRESULT = 0xa3; +const byte XMSG_LOBBYCANJOINROOM = 0xa4; +const byte XMSG_LOBBYCANJOINROOMRESULT = 0xa5; +const byte XMSG_LOBBYLURKERCOUNT = 0xa6; +const byte XMSG_LOBBYADDROOM = 0xa7; +const byte XMSG_LOBBYREMOVEROOM = 0xa8; +const byte XMSG_LOBBYUPDATEROOM = 0xa9; +const byte XMSG_LOBBYLEAVE = 0xaa; +const byte XMSG_LOBBYLEAVERESULT = 0xab; +const byte XMSG_ROOMJOIN = 0xb0; +const byte XMSG_ROOMJOINRESULT = 0xb1; +const byte XMSG_ROOMADDPLAYER = 0xb2; +const byte XMSG_ROOMREMOVEPLAYER = 0xb3; +const byte XMSG_ROOMSENDCHAT = 0xb4; +const byte XMSG_ROOMRECEIVECHAT = 0xb5; +const byte XMSG_ROOMADDGAME = 0xb6; +const byte XMSG_ROOMREMOVEGAME = 0xb7; +const byte XMSG_ROOMGAMEINPROGRESS = 0xb8; +const byte XMSG_ROOMGAMEPLAYERNAMES = 0xb9; +const byte XMSG_ROOMSTATUSCOMPLETE = 0xba; +const byte XMSG_ROOMCREATEGAME = 0xbb; +const byte XMSG_ROOMCREATEGAMERESULT = 0xbc; +const byte XMSG_ROOMCANJOINGAME = 0xbd; +const byte XMSG_ROOMCANJOINGAMERESULT = 0xbe; +const byte XMSG_ROOMLEAVE = 0xbf; +const byte XMSG_ROOMLEAVERESULT = 0xc0; +const byte XMSG_GAMEJOIN = 0xd0; +const byte XMSG_GAMEJOINRESULT = 0xd1; +const byte XMSG_GAMESENDCHAT = 0xd2; +const byte XMSG_GAMERECEIVECHAT = 0xd3; +const byte XMSG_GAMENETMESSAGE = 0xd4; +const byte XMSG_GAMEUPDATEEMPTY = 0xd5; +const byte XMSG_GAMEUPDATERESULT = 0xd6; +const byte XMSG_GAMEKILLED = 0xd7; +const byte XMSG_GAMELEAVE = 0xd8; +const byte XMSG_GAMELEAVERESULT = 0xd9; + +STARTLABEL(XMsgLabels) + LABEL(XMSG_NONE) + LABEL(XMSG_HANDSHAKE) + LABEL(XMSG_HANDSHAKERESULT) + LABEL(XMSG_SHOWMESSAGE) + LABEL(XMSG_ECHO) + LABEL(XMSG_PROTOCOLERROR) + LABEL(XMSG_LOGIN) + LABEL(XMSG_LOGINRESULT) + LABEL(XMSG_SIGNOUT) + LABEL(XMSG_SIGNOUTRESULT) + LABEL(XMSG_LOBBYJOIN) + LABEL(XMSG_LOBBYJOINRESULT) + LABEL(XMSG_LOBBYCREATEROOM) + LABEL(XMSG_LOBBYCREATEROOMRESULT) + LABEL(XMSG_LOBBYCANJOINROOM) + LABEL(XMSG_LOBBYCANJOINROOMRESULT) + LABEL(XMSG_LOBBYLURKERCOUNT) + LABEL(XMSG_LOBBYADDROOM) + LABEL(XMSG_LOBBYREMOVEROOM) + LABEL(XMSG_LOBBYUPDATEROOM) + LABEL(XMSG_LOBBYLEAVE) + LABEL(XMSG_LOBBYLEAVERESULT) + LABEL(XMSG_ROOMJOIN) + LABEL(XMSG_ROOMJOINRESULT) + LABEL(XMSG_ROOMADDPLAYER) + LABEL(XMSG_ROOMREMOVEPLAYER) + LABEL(XMSG_ROOMSENDCHAT) + LABEL(XMSG_ROOMRECEIVECHAT) + LABEL(XMSG_ROOMADDGAME) + LABEL(XMSG_ROOMREMOVEGAME) + LABEL(XMSG_ROOMGAMEINPROGRESS) + LABEL(XMSG_ROOMGAMEPLAYERNAMES) + LABEL(XMSG_ROOMSTATUSCOMPLETE) + LABEL(XMSG_ROOMCREATEGAME) + LABEL(XMSG_ROOMCREATEGAMERESULT) + LABEL(XMSG_ROOMCANJOINGAME) + LABEL(XMSG_ROOMCANJOINGAMERESULT) + LABEL(XMSG_ROOMLEAVE) + LABEL(XMSG_ROOMLEAVERESULT) + LABEL(XMSG_GAMEJOIN) + LABEL(XMSG_GAMEJOINRESULT) + LABEL(XMSG_GAMESENDCHAT) + LABEL(XMSG_GAMERECEIVECHAT) + LABEL(XMSG_GAMENETMESSAGE) + LABEL(XMSG_GAMEUPDATEEMPTY) + LABEL(XMSG_GAMEUPDATERESULT) + LABEL(XMSG_GAMEKILLED) + LABEL(XMSG_GAMELEAVE) + LABEL(XMSG_GAMELEAVERESULT) +ENDLABEL(XMsgLabels) + +} // namespace wi + +#endif // __CONSTANTS_H__ diff --git a/mpshared/decompress.cpp b/mpshared/decompress.cpp new file mode 100644 index 0000000..58751f2 --- /dev/null +++ b/mpshared/decompress.cpp @@ -0,0 +1,107 @@ +#include "mpshared/decompress.h" +#include "mpshared/misc.h" +#include "inc/rip.h" +#include + +namespace wi { + +word DecompressChunk(byte **ppbCompressed, byte *pbDecompressed, + byte *pbCacheEnd, word cbMax) +{ + byte *pbSrc = *ppbCompressed; + byte *pbDst = pbDecompressed; + + bool fChunk = true; + if (pbCacheEnd == NULL) { + pbCacheEnd = pbDecompressed; + fChunk = false; + } + + bool fDone = false; + while (!fDone) { + if (fChunk) { + // If we're almost to the max size, break out (max copy size is 129) + + if (pbDst - pbDecompressed > cbMax - (8 * 129)) + break; + } + + // Grab the next flag byte + + byte bT = *pbSrc++; + for (int ibit = 7; ibit >= 0; ibit--) { + // Literal or code? + + if ((bT & (1 << ibit)) != 0) { + *pbDst++ = *pbSrc++; + continue; + } + + // Not a literal. Get code. + + word code = ((word)(*pbSrc++)) << 8; + code |= *pbSrc++; + + // Extended code? Check for count == 0 + + word offBackwards = (code & 0x1fff); + word cb; + if (code & 0xe000) { + // Count != 0, so not an extended code + + cb = ((code & 0xe000) >> 13) + 1; + } else { + // Extended code. End? + + if (offBackwards == 0) { + fDone = true; + break; + } + + // Extended match + + offBackwards = (code << 1) | (*pbSrc >> 7); + cb = (*pbSrc++ & 0x7f) + 2; + } + + // Copy this chunk into the output + + int offStart = (pbDst - pbDecompressed) - offBackwards; + if (offStart >= 0) { + // Block is in local memory + memcpy(pbDst, pbDecompressed + offStart, cb); + pbDst += cb; + } else { + // Block is in cache memory or half in cache, half local + + if ((int)cb + offStart <= 0) { + // Block is totally in cache memory + + memcpy(pbDst, pbCacheEnd + offStart, cb); + pbDst += cb; + } else { + // Block is partly in both + + memcpy(pbDst, pbCacheEnd + offStart, -offStart); + pbDst += -offStart; + memcpy(pbDst, pbDecompressed, (int)cb + offStart); + pbDst += (int)cb + offStart; + } + } + } + } + + // Pass back the current point in the compressed source and the # bytes decompressed + + *ppbCompressed = pbSrc; + return pbDst - pbDecompressed; +} + +void DecompressToMemory(byte *pbCompressed, byte *pbOut, + CompressionHeader *pcoh) +{ + byte *pbCompressedT = pbCompressed; + DecompressChunk(&pbCompressedT, pbOut, NULL, 0); +} + +} // namespace wi diff --git a/mpshared/decompress.h b/mpshared/decompress.h new file mode 100644 index 0000000..33efa3f --- /dev/null +++ b/mpshared/decompress.h @@ -0,0 +1,23 @@ +#ifndef __DECOMPRESS_H__ +#define __DECOMPRESS_H__ + +#include "inc/basictypes.h" + +namespace wi { + +struct CompressionHeader // coh +{ + word fCompressed; + word cbUncompressed; + word cbCompressed; +}; +#define kcbCompressionHeader 6 // sizeof returns 8 on some platforms + +extern "C" word DecompressChunk(byte **ppbCompressed, byte *pbDecompressed, + byte *pbCacheEnd, word cbMax); +extern "C" void DecompressToMemory(byte *pbCompressed, byte *pbOut, + CompressionHeader *pcoh); + +} // namespace wi + +#endif // __DECOMPRESS_H__ diff --git a/mpshared/enum.h b/mpshared/enum.h new file mode 100644 index 0000000..b37057b --- /dev/null +++ b/mpshared/enum.h @@ -0,0 +1,33 @@ +#ifndef __ENUM_H__ +#define __ENUM_H__ + +#include "inc/basictypes.h" + +namespace wi { + +// Enum class, used for enumeration + +const dword kEnmFirst = (dword)-1; + +class Enum // enm +{ +public: + Enum() + { + Reset(); + } + void Reset() + { + m_pvNext = (void *)kEnmFirst; + m_dwUser = kEnmFirst; + m_wUser = (word)kEnmFirst; + } + + void *m_pvNext; + dword m_dwUser; + word m_wUser; +}; + +} // namespace wi + +#endif // __ENUM_H__ diff --git a/mpshared/indexloader.cpp b/mpshared/indexloader.cpp new file mode 100644 index 0000000..dd9ef85 --- /dev/null +++ b/mpshared/indexloader.cpp @@ -0,0 +1,147 @@ +#include "mpshared/indexloader.h" +#include "mpshared/mpht.h" +#include "base/format.h" +#include "yajl/wrapper/jsonbuilder.h" + +namespace wi { + +IndexLoader::IndexLoader() { +} + +bool IndexLoader::InitFromFile(const char *indexfile) { + FILE *file = fopen(indexfile, "r"); + if (file == NULL) { + return false; + } + + index_.clear(); + json::JsonBuilder builder; + builder.Start(this); + while (true) { + char ach[512]; + size_t cb = fread(ach, 1, sizeof(ach), file); + if (cb == 0) { + if (ferror(file)) { + fclose(file); + return false; + } + break; + } + if (!builder.Update(ach, cb)) { + fclose(file); + return false; + } + } + builder.End(); + fclose(file); + return true; +} + +int IndexLoader::GetCount() { + return index_.size(); +} + +const IndexEntry *IndexLoader::GetEntry(int i) { + if (i < 0 || i >= (int)index_.size()) { + return NULL; + } + return &index_[i]; +} + +void IndexLoader::OnObject(json::JsonObject *obj) { + // Array order is: + // 1. pack id + // 2. pack hash + // 3. title + // 4. min player count + // 5. max player count + // 6. mission count + + int cExpected = 6; + + // Must be array of strings. + bool error = false; + if (obj->type() != json::JSONTYPE_ARRAY) { + error = true; + } + const json::JsonArray *a = (const json::JsonArray *)obj; + if (!error) { + if (a->GetCount() != cExpected) { + error = true; + } + } + if (!error) { + for (int i = 0; i < a->GetCount(); i++) { + if (a->GetObject(i)->type() != json::JSONTYPE_STRING) { + error = true; + } + } + } + + IndexEntry entry; + const json::JsonString *s; + + // id + if (!error) { + s = (const json::JsonString *)a->GetObject(0); + if (!base::Format::ToDword(s->GetString(), 10, &entry.packid.id)) { + error = true; + } + } + + // hash + if (!error) { + s = (const json::JsonString *)a->GetObject(1); + if (s->GetLength() != 32) { + error = true; + } + if (!base::Format::FromHex(s->GetString(), entry.packid.hash, + sizeof(entry.packid.hash))) { + error = true; + } + } + + // title + if (!error) { + s = (const json::JsonString *)a->GetObject(2); + entry.title = s->GetString(); + } + + // min players + if (!error) { + s = (const json::JsonString *)a->GetObject(3); + if (!base::Format::ToInteger(s->GetString(), 10, &entry.cPlayersMin)) { + error = true; + } + } + + // max players + if (!error) { + s = (const json::JsonString *)a->GetObject(4); + if (!base::Format::ToInteger(s->GetString(), 10, &entry.cPlayersMax)) { + error = true; + } + } + + // mission count + if (!error) { + s = (const json::JsonString *)a->GetObject(5); + if (!base::Format::ToInteger(s->GetString(), 10, &entry.cMissions)) { + error = true; + } + } + delete obj; + + if (error) { + OnParseError(); + return; + } + + entry.inIndex = true; + index_.push_back(entry); +} + +void IndexLoader::OnParseError() { +} + +} // namespace wi diff --git a/mpshared/indexloader.h b/mpshared/indexloader.h new file mode 100644 index 0000000..0c01100 --- /dev/null +++ b/mpshared/indexloader.h @@ -0,0 +1,40 @@ +#ifndef __INDEXLOADER_H__ +#define __INDEXLOADER_H__ + +#include "inc/basictypes.h" +#include "mpshared/mpht.h" +#include "yajl/wrapper/jsonbuilder.h" + +namespace wi { + +struct IndexEntry { + PackId packid; + std::string title; + int cPlayersMin; + int cPlayersMax; + int cMissions; + bool inIndex; +}; + +class IndexLoader : protected json::JsonBuilder::ArrayItemsCallback { // idxl +public: + IndexLoader(); + virtual ~IndexLoader() {} + + bool InitFromFile(const char *indexfile); + int GetCount(); + const IndexEntry *GetEntry(int i); + +protected: + virtual void OnParseError(); + + // ArrayItemsCallback + virtual void OnObject(json::JsonObject *obj); + + typedef std::vector Index; + Index index_; +}; + +} // namespace wi + +#endif // __INDEXLOADER_H__ diff --git a/mpshared/ini.cpp b/mpshared/ini.cpp new file mode 100644 index 0000000..afca2c6 --- /dev/null +++ b/mpshared/ini.cpp @@ -0,0 +1,390 @@ +#include "mpshared/ini.h" +#include "mpshared/misc.h" +#include "inc/rip.h" +#include +#include + +namespace wi { + +#undef BigWord +#define BigWord(x) ((((x)&0xFF)<<8) | (((x)&0xFF00)>>8)) + +// IniScanf / VIniScanf +// +// Supports: +// %s : "trimmed string", matches a string with interleaving spaces. Removes +// leading and trailing white space, with ini-friendly delimiters +// %d : signed integer in the range of +32,767 / -32,768. Leading '.' & trailing +// '%' ignored for convenience of percentages like .xx and xx%. +// %ld : signed integer in the range of +2,147,483,647 / -2,147,483,648. Leading '.' +// & trailing '%' ignored for convenience of percentages like .xx and xx%. +// %a : "argument group", matches a curly-brace enclosed string that can include +// any character (e.g., commas) +// %s+: means %s plus store index of matching string +// %+ : means store current buffer index +// '*' after % is supported and means match but don't store +// ' ', '\t', '\n' : match for optional white space (space, tab, newline) + +#define IsWhiteSpace(ch) ((ch) == ' ' || (ch) == '\t' || (ch) == '\n' || (ch) == '\r') +#ifdef IsDigit +#undef IsDigit +#endif +#define IsDigit(ch) ((ch) >= '0' && (ch) <= '9') + +int IniScanf(char *pszBuff, char *pszFmt, ...) +{ + va_list va; + va_start(va, pszFmt); + int c = VIniScanf(pszBuff, pszFmt, va); + va_end(va); + return c; +} + +int VIniScanf(char *pszBuff, char *pszFmt, va_list va) +{ + bool fLong; + char *pszBuffT = pszBuff; + char *pszFmtT = pszFmt; + int cArgs = 0; + + while (*pszFmtT != 0) { + // Make this special check before checking for end + // of buffer + + if (pszFmtT[0] == '%' && pszFmtT[1] == '+') { + pszFmtT += 2; + *va_arg(va, int *) = pszBuffT - pszBuff; + cArgs++; + continue; + } + + switch (*pszFmtT) { + case ' ': + case '\n': + case '\t': + // Eat white space (space, tab, newline) + + while (IsWhiteSpace(*pszBuffT)) + pszBuffT++; + pszFmtT++; + continue; + } + + // End of buffer? Bail. + + if (*pszBuffT == 0) + break; + + bool fSuppress; + switch (*pszFmtT) { + case '%': + // Assume parsed ints are not longs + + fLong = false; + + // Suppress? + + pszFmtT++; + fSuppress = false; + if (*pszFmtT == '*') { + fSuppress = true; + pszFmtT++; + } + + // Format specification + + switch (*pszFmtT) { + case 's': + // Trimmed string. + { + pszFmtT++; + + // If %s+, it means save the location of substring + + bool fSaveLocation = false; + if (*pszFmtT == '+') { + fSaveLocation = true; + pszFmtT++; + } + + // Go past initial whitespace + + int nchStart = pszBuffT - pszBuff; + while (IsWhiteSpace(*pszBuffT)) { + nchStart++; + pszBuffT++; + } + + // Copy the string into pszT. Figure out when to stop + // and remember trimming information. + + char chStop = *pszFmtT; + char *pszT = va_arg(va, char *); + char *pszWhiteLast = NULL; + while (*pszBuffT != chStop) { + if (*pszBuffT == 0) + break; + if (!IsWhiteSpace(*pszBuffT)) { + pszWhiteLast = NULL; + } else if (pszWhiteLast == NULL) { + pszWhiteLast = pszT; + } + if (!fSuppress) + *pszT++ = *pszBuffT++; + } + if (!fSuppress) { + *pszT = 0; + if (pszWhiteLast != NULL) + *pszWhiteLast = 0; + cArgs++; + } + + // %s+ means save location string started + + if (!fSuppress && fSaveLocation) { + *va_arg(va, int *) = nchStart; + cArgs++; + } + } + continue; + + case 'a': + // Argument group. + { + pszFmtT++; + + // Go past opening curly brace + + Assert(*pszBuffT == '{'); + pszBuffT++; + + // Copy the string into pszT. Figure out when to stop + // and remember trimming information. + + char *pszT = va_arg(va, char *); + while (*pszBuffT != '}') { + if (*pszBuffT == 0) + break; + if (!fSuppress) + *pszT++ = *pszBuffT++; + } + if (!fSuppress) { + *pszT = 0; + cArgs++; + } + + // Go past closing curly brace + + pszBuffT++; + } + continue; + + case 'l': + fLong = true; + pszFmtT++; + Assert(*pszFmtT == 'd'); + + // ... FALL THROUGH ... + + case 'd': + // Integer between 32767 / -32768. Leading . and trailing % + // ignored. NOTE: this precision limitation is not enforced. + // Parsing a number greater than 32767 will succeed on Win32 + // but fail on Palm due to differing int sizes. + { + pszFmtT++; + + while (IsWhiteSpace(*pszBuffT)) + pszBuffT++; + if (*pszBuffT == '.') + pszBuffT++; + char szT[11]; + int n = 0; + if (*pszBuffT == '-') { + szT[0] = '-'; + n = 1; + pszBuffT++; + } + bool fHaveInt = false; + for (; n < sizeof(szT) - 1; n++) { + if (!IsDigit(*pszBuffT)) + break; + szT[n] = *pszBuffT++; + fHaveInt = true; + } + if (fHaveInt) { + szT[n] = 0; + if (*pszBuffT == '%') + pszBuffT++; + if (!fSuppress) { + if (fLong) + *va_arg(va, long *) = atol(szT); + else + *va_arg(va, int *) = atoi(szT); + cArgs++; + } + } + } + continue; + + default: + // Non-recognized % command + + return cArgs; + } + break; + + default: + // Match character + + if (*pszBuffT == *pszFmtT) { + pszBuffT++; + pszFmtT++; + continue; + } + break; + } + + // If we're here it means stop + + break; + } + + return cArgs; +} + +IniReader *LoadIniFile(PackFileReader& pak, const char *pszFn) +{ + IniReader *pini = new IniReader(pak); + if (pini == NULL) + return NULL; + if (!pini->Init(pszFn)) { + delete pini; + return NULL; + } + return pini; +} + +// IniReader + +IniReader::IniReader(PackFileReader& pak) : m_pak(pak), m_psecFirst(NULL) +{ +} + +IniReader::~IniReader() +{ + if (m_psecFirst != NULL) + m_pak.UnmapFile(&m_fmap); +} + +bool IniReader::Init(const char *psz) +{ + m_psecFirst = (IniSection *)m_pak.MapFile(psz, &m_fmap); + return m_psecFirst != NULL; +} + +bool IniReader::GetPropertyValue(char *pszSec, char *pszProp, char *pszValue, int cbValue) +{ + IniSection *psec = FindSection(pszSec); + if (psec == NULL) + return false; + IniProperty prop; + if (!FindProperty(psec, pszProp, &prop)) + return false; + strncpyz(pszValue, prop.pszValue, cbValue); + return true; +} + +int IniReader::GetPropertyValue(char *pszSec, char *pszProp, char *pszFmt, ...) +{ + IniSection *psec = FindSection(pszSec); + if (psec == NULL) + return 0; + IniProperty prop; + if (!FindProperty(psec, pszProp, &prop)) + return 0; + va_list va; + va_start(va, pszFmt); + int c = VIniScanf(prop.pszValue, pszFmt, va); + va_end(va); + return c; +} + +bool IniReader::FindNextProperty(FindProp *pfind, char *pszSec, char *pszProp, int cbProp) +{ + if (pfind->m_psecFind == NULL) { + pfind->m_psecFind = FindSection(pszSec); + if (pfind->m_psecFind == NULL) + return false; + pfind->m_ipropFind = 0; + } else { + pfind->m_ipropFind++; + if (pfind->m_ipropFind >= BigWord(pfind->m_psecFind->cprops)) + return false; + } + + IniProperty prop; + if (!FindProperty(pfind->m_psecFind, pfind->m_ipropFind, &prop)) + return false; + strncpyz(pszProp, prop.pszProp, cbProp); + return true; +} + +int IniReader::GetPropertyValue(FindProp *pfind, char *pszFmt, ...) +{ + IniProperty prop; + if (!FindProperty(pfind->m_psecFind, pfind->m_ipropFind, &prop)) + return 0; + va_list va; + va_start(va, pszFmt); + int c = VIniScanf(prop.pszValue, pszFmt, va); + va_end(va); + return c; +} + +IniSection *IniReader::FindSection(char *pszSec) +{ + IniSection *psec = m_psecFirst; + while (psec != NULL) { + if (strcmp(pszSec, (char *)(psec + 1)) == 0) + return psec; + if (psec->offNext == 0) + return NULL; + psec = (IniSection *)((byte *)psec + BigWord(psec->offNext)); + } + return NULL; +} + +bool IniReader::FindProperty(IniSection *psec, char *pszProp, IniProperty *pprop) +{ + char *pszPropT = (char *)(psec + 1) + strlen((char *)(psec + 1)) + 1; + for (int iprop = 0; iprop < BigWord(psec->cprops); iprop++) { + if (strcmp(pszPropT, pszProp) == 0) { + pprop->pszProp = pszPropT; + pprop->pszValue = pszPropT + strlen(pszPropT) + 1; + return true; + } + + pszPropT += strlen(pszPropT) + 1; + pszPropT += strlen(pszPropT) + 1; + } + return false; +} + +bool IniReader::FindProperty(IniSection *psec, int iprop, IniProperty *pprop) +{ + char *pszPropT = (char *)(psec + 1) + strlen((char *)(psec + 1)) + 1; + for (int ipropT = 0; ipropT < BigWord(psec->cprops); ipropT++) { + if (ipropT == iprop) { + pprop->pszProp = pszPropT; + pprop->pszValue = pszPropT + strlen(pszPropT) + 1; + return true; + } + + pszPropT += strlen(pszPropT) + 1; + pszPropT += strlen(pszPropT) + 1; + } + return false; +} + +} // namespace wi diff --git a/mpshared/ini.h b/mpshared/ini.h new file mode 100644 index 0000000..f36ef52 --- /dev/null +++ b/mpshared/ini.h @@ -0,0 +1,67 @@ +#ifndef __INI_H__ +#define __INI_H__ + +#include "inc/basictypes.h" +#include "mpshared/packfile.h" +#include + +namespace wi { + +struct IniSection // sec +{ + word offNext; + word cprops; + // char szName[]; +}; + +struct IniProperty // prop +{ + char *pszProp; + char *pszValue; +}; + +class FindProp // find +{ +public: + FindProp() + { + m_psecFind = NULL; + m_ipropFind = 0; + } + IniSection *m_psecFind; + int m_ipropFind; + + void Assign(FindProp *pfind) { + m_psecFind = pfind->m_psecFind; + m_ipropFind = pfind->m_ipropFind; + } +}; + +class IniReader // ini +{ +public: + IniReader(PackFileReader& pak); + ~IniReader(); + + bool Init(const char *psz); + bool GetPropertyValue(char *pszSec, char *pszProp, char *pszValue, int cbValue); + int GetPropertyValue(char *pszSec, char *pszProp, char *pszFmt, ...); + bool FindNextProperty(FindProp *pfind, char *pszSec, char *pszProp, int cbProp); + int GetPropertyValue(FindProp *pfind, char *pszFmt, ...); + +private: + IniSection *FindSection(char *pszSec); + bool FindProperty(IniSection *psec, char *pszProp, IniProperty *pprop); + bool FindProperty(IniSection *psec, int iprop, IniProperty *pprop); + + PackFileReader& m_pak; + IniSection *m_psecFirst; + FileMap m_fmap; +}; +IniReader *LoadIniFile(PackFileReader& pak, const char *pszFn); +int IniScanf(char *pszBuff, char *pszFmt, ...); +int VIniScanf(char *pszBuff, char *pszFmt, va_list va); + +} // namespace wi; + +#endif // __INI_H__ diff --git a/mpshared/messages.cpp b/mpshared/messages.cpp new file mode 100644 index 0000000..59d9e0d --- /dev/null +++ b/mpshared/messages.cpp @@ -0,0 +1,595 @@ +#include "mpshared/messages.h" +#include "mpshared/netmessage.h" +#include "base/socketaddress.h" +#include + +namespace wi { + +#ifdef LOGGING +std::string XMsgShowMessage::ToString() { + std::ostringstream ss; + ss << XMsgLabels.Find(id_) << " message: " << message_ << + " address: " << base::SocketAddress(ipRedirect_, 0).ToString() << + " disconnect: " << (disconnect_ ? "true" : "false"); + return ss.str(); +} +#endif + +base::ByteBuffer *XMsgShowMessage::ToBuffer(const char *message, + dword ipRedirect, bool disconnect) { + int cbMessage = strlen(message) + 1; + char messageT[kcbShowMessageMax]; + if (cbMessage > sizeof(messageT)) { + strncpyz(messageT, message, sizeof(messageT)); + message = messageT; + cbMessage = sizeof(messageT); + } + dword cb = XMSGSIZE_SHOWMESSAGE_FIXED + cbMessage; + base::ByteBuffer *bb = new base::ByteBuffer(cb); + bb->WriteWord(cb); + bb->WriteByte(XMSG_SHOWMESSAGE); + bb->WriteDword(ipRedirect); + bb->WriteDword(disconnect ? 1 : 0); + bb->WriteDword(cbMessage); + bb->WriteBytes((const byte *)message, cbMessage); + if (bb->Length() != cb) { + delete bb; + return NULL; + } + return bb; +} + +XMsgShowMessage *XMsgShowMessage::FromBuffer(base::ByteBuffer& bb, + dword cb) { + dword cbSav = bb.Length(); + word w; + if (!bb.ReadWord(&w)) { + return NULL; + } + if (cb != (dword)w || w < XMSGSIZE_SHOWMESSAGE_FIXED) { + Assert(); + return NULL; + } + if (w - sizeof(word) > bb.Length()) { + return NULL; + } + byte id = 0; + bb.ReadByte(&id); + if (id != XMSG_SHOWMESSAGE) { + return NULL; + } + dword ipRedirect = 0; + bb.ReadDword(&ipRedirect); + dword disconnect = 0; + bb.ReadDword(&disconnect); + dword cbMessage = 0; + bb.ReadDword(&cbMessage); + if (cbMessage > kcbShowMessageMax) { + Assert(); + return NULL; + } + char message[kcbShowMessageMax]; + bb.ReadBytes((byte *)message, cbMessage); + message[cbMessage - 1] = 0; + if (cbSav - bb.Length() != w) { + Assert(); + return NULL; + } + + return new XMsgShowMessage(message, ipRedirect, disconnect != 0); +} + +#ifdef LOGGING +std::string XMsgRoomCreateGame::ToString() { + std::ostringstream ss; + ss << XMsgLabels.Find(id_) << " GameParams: " << + GameParamsToString(params_); + return ss.str(); +} +#endif + +base::ByteBuffer *XMsgRoomCreateGame::ToBuffer(GameParams *params) { + base::ByteBuffer *bb = new base::ByteBuffer(XMSGSIZE_ROOMCREATEGAME); + bb->WriteWord(XMSGSIZE_ROOMCREATEGAME); + bb->WriteByte(XMSG_ROOMCREATEGAME); + GameParams paramsT = *params; + SwapGameParams(¶msT); + bb->WriteBytes((const byte *)¶msT, sizeof(paramsT)); + Assert(bb->Length() == XMSGSIZE_ROOMCREATEGAME); + if (bb->Length() != XMSGSIZE_ROOMCREATEGAME) { + delete bb; + return NULL; + } + return bb; +} + +XMsgRoomCreateGame *XMsgRoomCreateGame::FromBuffer(base::ByteBuffer& bb, + dword cb) { + dword cbSav = bb.Length(); + word w; + if (!bb.ReadWord(&w)) { + return NULL; + } + if (cb != (dword)w) { + Assert(); + return NULL; + } + if (w - sizeof(word) > bb.Length()) { + return NULL; + } + byte id = 0; + bb.ReadByte(&id); + if (id != XMSG_ROOMCREATEGAME) { + return NULL; + } + GameParams params; + bb.ReadBytes((byte *)¶ms, sizeof(GameParams)); + SwapGameParams(¶ms); + if (!ValidateGameParams(params)) { + return NULL; + } + Assert(cbSav - bb.Length() == XMSGSIZE_ROOMCREATEGAME); + if (cbSav - bb.Length() != XMSGSIZE_ROOMCREATEGAME) { + return NULL; + } + + return new XMsgRoomCreateGame(¶ms); +} + +base::ByteBuffer *XMsgRoomCreateGameResult::ToBuffer(dword gameid, + dword result, const PackId *ppackid) { + base::ByteBuffer *bb = new base::ByteBuffer( + XMSGSIZE_ROOMCREATEGAMERESULT); + bb->WriteWord(XMSGSIZE_ROOMCREATEGAMERESULT); + bb->WriteByte(XMSG_ROOMCREATEGAMERESULT); + bb->WriteDword(gameid); + bb->WriteDword(result); + PackId packidT; + if (ppackid == NULL) { + memset(&packidT, 0, sizeof(packidT)); + ppackid = &packidT; + } + bb->WriteDword(ppackid->id); + bb->WriteBytes(ppackid->hash, sizeof(ppackid->hash)); + Assert(bb->Length() == XMSGSIZE_ROOMCREATEGAMERESULT); + if (bb->Length() != XMSGSIZE_ROOMCREATEGAMERESULT) { + delete bb; + return NULL; + } + return bb; +} + +XMsgRoomCreateGameResult *XMsgRoomCreateGameResult::FromBuffer( + base::ByteBuffer& bb, dword cb) { + dword cbSav = bb.Length(); + word w; + if (!bb.ReadWord(&w)) { + return NULL; + } + if (cb != (dword)w) { + Assert(); + return NULL; + } + if (w - sizeof(word) > bb.Length()) { + return NULL; + } + byte id = 0; + bb.ReadByte(&id); + if (id != XMSG_ROOMCREATEGAMERESULT) { + return NULL; + } + dword gameid; + bb.ReadDword(&gameid); + dword result; + bb.ReadDword(&result); + PackId packid; + bb.ReadDword(&packid.id); + bb.ReadBytes(packid.hash, sizeof(packid.hash)); + + Assert(cbSav - bb.Length() == XMSGSIZE_ROOMCREATEGAMERESULT); + if (cbSav - bb.Length() != XMSGSIZE_ROOMCREATEGAMERESULT) { + return NULL; + } + + return new XMsgRoomCreateGameResult(gameid, result, &packid); +} + +#ifdef LOGGING +std::string XMsgGameNetMessage::ToString() { + std::ostringstream ss; + ss << XMsgLabels.Find(id_) << ", " << PszFromNetMessage(pnm_); + return ss.str(); +} +#endif + +base::ByteBuffer *XMsgGameNetMessage::ToBuffer(NetMessage *pnm) { + // Special case very common messages, to make them as small as possible + if (pnm->nmid == knmidScUpdate) { + UpdateNetMessage *punm = (UpdateNetMessage *)pnm; + if (punm->cmsgCommands == 0) { + return XMsgGameUpdateEmpty::ToBuffer(punm); + } + } + if (pnm->nmid == knmidCsUpdateResult) { + return XMsgGameUpdateResult::ToBuffer((UpdateResultNetMessage *)pnm); + } + + // Slim the generic NetMessage to essential elements. + + word cb = XMSGSIZE_GAMENETMESSAGE_FIXED; // fixed overhead of this xmsg + cb += 1; // nmid + word cbInside = pnm->cb - sizeof(NetMessage); + cb += cbInside; + byte *pbInside = (byte *)pnm + sizeof(NetMessage); + + base::ByteBuffer *bb = new base::ByteBuffer(cb); + bb->WriteWord(cb); + bb->WriteByte(XMSG_GAMENETMESSAGE); + Assert(bb->Length() == XMSGSIZE_GAMENETMESSAGE_FIXED); + if (bb->Length() != XMSGSIZE_GAMENETMESSAGE_FIXED) { + delete bb; + return NULL; + } + + bb->WriteByte(pnm->nmid); + NetMessageByteOrderSwap(pnm, true); + bb->WriteBytes(pbInside, cbInside); + NetMessageByteOrderSwap(pnm, false); + + Assert(bb->Length() == cb); + if (bb->Length() != cb) { + delete bb; + return NULL; + } + return bb; +} + +XMsgGameNetMessage *XMsgGameNetMessage::FromBuffer(base::ByteBuffer& bb, + dword cb) { + dword cbSav = bb.Length(); + word cbMsg; + if (!bb.ReadWord(&cbMsg)) { + return NULL; + } + if (cb != (dword)cbMsg) { + Assert(); + return NULL; + } + if (cbMsg - sizeof(word) > bb.Length()) { + return NULL; + } + byte id = 0; + bb.ReadByte(&id); + if (id != XMSG_GAMENETMESSAGE) { + return NULL; + } + byte nmid = 0; + bb.ReadByte(&nmid); + + word cbInside = cbMsg - XMSGSIZE_GAMENETMESSAGE_FIXED - 1; + word cbNmsg = cbInside + sizeof(NetMessage); + + byte *pb = new byte[cbNmsg]; + memset(pb, 0, cbNmsg); + NetMessage *pnm = (NetMessage *)pb; + pnm->cb = base::ByteBuffer::HostToNetWord(cbNmsg); + pnm->nmid = base::ByteBuffer::HostToNetWord(nmid); + bb.ReadBytes(pb + sizeof(NetMessage), cbInside); + NetMessageByteOrderSwap(pnm, false); + + Assert(cbSav - bb.Length() == cbMsg); + if (cbSav - bb.Length() != cbMsg) { + delete pb; + return NULL; + } + + // TODO: Validate size and content of NetMessage + + return new XMsgGameNetMessage(pnm); +} + +base::ByteBuffer *XMsgGameUpdateEmpty::ToBuffer(UpdateNetMessage *punm) { + base::ByteBuffer *bb = new base::ByteBuffer(XMSGSIZE_GAMEUPDATEEMPTY); + bb->WriteWord(XMSGSIZE_GAMEUPDATEEMPTY); + bb->WriteByte(XMSG_GAMEUPDATEEMPTY); + bb->WriteDword(punm->cUpdatesBlock); + bb->WriteDword(punm->cUpdatesSync); + if (bb->Length() != XMSGSIZE_GAMEUPDATEEMPTY) { + delete bb; + return NULL; + } + return bb; +} + +XMsgGameNetMessage *XMsgGameUpdateEmpty::FromBuffer(base::ByteBuffer& bb, + dword cb) { + dword cbSav = bb.Length(); + word cbMsg; + if (!bb.ReadWord(&cbMsg)) { + return NULL; + } + if (cb != (dword)cbMsg || cbMsg != XMSGSIZE_GAMEUPDATEEMPTY) { + Assert(); + return NULL; + } + if (cbMsg - sizeof(word) > bb.Length()) { + return NULL; + } + byte id = 0; + bb.ReadByte(&id); + if (id != XMSG_GAMEUPDATEEMPTY) { + return NULL; + } + dword cUpdatesBlock = 0; + bb.ReadDword(&cUpdatesBlock); + dword cUpdatesSync = 0; + bb.ReadDword(&cUpdatesSync); + Assert(cbSav - bb.Length() == XMSGSIZE_GAMEUPDATEEMPTY); + if (cbSav - bb.Length() != XMSGSIZE_GAMEUPDATEEMPTY) { + return NULL; + } + + int cbUnm = sizeof(UpdateNetMessage) - sizeof(Message); + UpdateNetMessage *punm = (UpdateNetMessage *)new byte[cbUnm]; + punm->nmid = knmidScUpdate; + punm->cb = cbUnm; + punm->cUpdatesBlock = cUpdatesBlock; + punm->cUpdatesSync = cUpdatesSync; + punm->cmsgCommands = 0; + return new XMsgGameNetMessage(punm); +} + +base::ByteBuffer *XMsgGameUpdateResult::ToBuffer( + UpdateResultNetMessage *purnm) { + base::ByteBuffer *bb = new base::ByteBuffer(XMSGSIZE_GAMEUPDATERESULT); + bb->WriteWord(XMSGSIZE_GAMEUPDATERESULT); + bb->WriteByte(XMSG_GAMEUPDATERESULT); + bb->WriteDword(purnm->ur.cUpdatesBlock); + bb->WriteDword(purnm->ur.hash); + if (purnm->ur.cmsLatency > SHRT_MAX) { + bb->WriteWord(SHRT_MAX); + } else if (purnm->ur.cmsLatency < SHRT_MIN) { + bb->WriteWord((word)SHRT_MIN); + } else { + bb->WriteWord((short)purnm->ur.cmsLatency); + } + if (bb->Length() != XMSGSIZE_GAMEUPDATERESULT) { + delete bb; + return NULL; + } + return bb; +} + +XMsgGameNetMessage *XMsgGameUpdateResult::FromBuffer(base::ByteBuffer& bb, + dword cb) { + dword cbSav = bb.Length(); + word cbMsg; + if (!bb.ReadWord(&cbMsg)) { + return NULL; + } + if (cb != (dword)cbMsg || cbMsg != XMSGSIZE_GAMEUPDATERESULT) { + Assert(); + return NULL; + } + if (cbMsg - sizeof(word) > bb.Length()) { + return NULL; + } + byte id = 0; + bb.ReadByte(&id); + if (id != XMSG_GAMEUPDATERESULT) { + return NULL; + } + dword cUpdatesBlock = 0; + bb.ReadDword(&cUpdatesBlock); + dword hash = 0; + bb.ReadDword(&hash); + short cmsLatency = 0; + bb.ReadWord((word *)&cmsLatency); + + Assert(cbSav - bb.Length() == XMSGSIZE_GAMEUPDATERESULT); + if (cbSav - bb.Length() != XMSGSIZE_GAMEUPDATERESULT) { + return NULL; + } + + UpdateResultNetMessage *purnm = new UpdateResultNetMessage; + purnm->ur.cUpdatesBlock = cUpdatesBlock; + purnm->ur.hash = hash; + purnm->ur.cmsLatency = cmsLatency; + return new XMsgGameNetMessage(purnm); +} + +base::ByteBuffer *XMsgRoomAddGame::ToBuffer(const char *player, dword gameid, + const GameParams& params, dword minplayers, dword maxplayers, + const char *title, dword ctotal) { + base::ByteBuffer *bb = new base::ByteBuffer(XMSGSIZE_ROOMADDGAME); + bb->WriteWord(XMSGSIZE_ROOMADDGAME); + bb->WriteByte(XMSG_ROOMADDGAME); + bb->WriteDword(ctotal); + bb->WriteDword(gameid); + bb->WriteDword(minplayers); + bb->WriteDword(maxplayers); + char playerT[kcbPlayerName]; + memset(playerT, 0, sizeof(playerT)); + strncpyz(playerT, player, sizeof(playerT)); + bb->WriteBytes((const byte *)playerT, sizeof(playerT)); + char titleT[kcbLevelTitle]; + memset(titleT, 0, sizeof(titleT)); + strncpyz(titleT, title, sizeof(titleT)); + bb->WriteBytes((const byte *)titleT, sizeof(titleT)); + GameParams paramsT = params; + SwapGameParams(¶msT); + bb->WriteBytes((const byte *)¶msT, sizeof(paramsT)); + if (bb->Length() != XMSGSIZE_ROOMADDGAME) { + delete bb; + return NULL; + } + return bb; +} + +XMsgRoomAddGame *XMsgRoomAddGame::FromBuffer(base::ByteBuffer& bb, dword cb) { + dword cbSav = bb.Length(); + word w; + if (!bb.ReadWord(&w)) { + return NULL; + } + if (cb != (dword)w || w != XMSGSIZE_ROOMADDGAME) { + Assert(); + return NULL; + } + if (w - sizeof(word) > bb.Length()) { + return NULL; + } + byte id = 0; + bb.ReadByte(&id); + if (id != XMSG_ROOMADDGAME) { + return NULL; + } + dword ctotal; + bb.ReadDword(&ctotal); + dword gameid; + bb.ReadDword(&gameid); + dword minplayers; + bb.ReadDword(&minplayers); + dword maxplayers; + bb.ReadDword(&maxplayers); + char player[kcbPlayerName]; + bb.ReadBytes((byte *)player, sizeof(player)); + player[sizeof(player) - 1] = 0; + char title[kcbLevelTitle]; + bb.ReadBytes((byte *)title, sizeof(title)); + title[sizeof(title) - 1] = 0; + GameParams params; + bb.ReadBytes((byte *)¶ms, sizeof(params)); + SwapGameParams(¶ms); + if (!ValidateGameParams(params)) { + return NULL; + } + Assert(cbSav - bb.Length() == XMSGSIZE_ROOMADDGAME); + if (cbSav - bb.Length() != XMSGSIZE_ROOMADDGAME) { + return NULL; + } + return new XMsgRoomAddGame(player, gameid, params, minplayers, maxplayers, + title, ctotal); +} + +base::ByteBuffer *XMsgRoomGamePlayerNames::ToBuffer(dword gameid, dword cnames, + const PlayerName *anames) { + dword cb = XMSGSIZE_ROOMGAMEPLAYERNAMES_FIXED + cnames * sizeof(PlayerName); + base::ByteBuffer *bb = new base::ByteBuffer(cb); + bb->WriteWord(cb); + bb->WriteByte(XMSG_ROOMGAMEPLAYERNAMES); + bb->WriteDword(gameid); + bb->WriteDword(cnames); + bb->WriteBytes((const byte *)anames, cnames * sizeof(PlayerName)); + if (bb->Length() != cb) { + delete bb; + return NULL; + } + return bb; +} + +XMsgRoomGamePlayerNames *XMsgRoomGamePlayerNames::FromBuffer( + base::ByteBuffer& bb, dword cb) { + dword cbSav = bb.Length(); + word w; + if (!bb.ReadWord(&w)) { + return NULL; + } + if (cb != (dword)w) { + Assert(); + return NULL; + } + if (w - sizeof(word) > bb.Length()) { + return NULL; + } + byte id = 0; + bb.ReadByte(&id); + if (id != XMSG_ROOMGAMEPLAYERNAMES) { + return NULL; + } + dword gameid; + bb.ReadDword(&gameid); + dword cnames; + bb.ReadDword(&cnames); + PlayerName *anames = new PlayerName[cnames]; + bb.ReadBytes((byte *)anames, cnames * sizeof(PlayerName)); + + if (cbSav - bb.Length() != w) { + Assert(); + delete anames; + return NULL; + } + + return new XMsgRoomGamePlayerNames(gameid, cnames, anames); +} + +#ifdef LOGGING +std::string XMsgLobbyAddRoom::ToString() { + std::ostringstream ss; + ss << XMsgLabels.Find(id_) << " room: " << room_ << " roomid: " << + roomid_ << " priv: " << priv_ << " cPlayers: " << cPlayers_ << + " cGames: " << cGames_; + return ss.str(); +} +#endif + +base::ByteBuffer *XMsgLobbyAddRoom::ToBuffer(const char *room, dword roomid, + bool priv, dword cPlayers, dword cGames) { + dword cb = XMSGSIZE_LOBBYADDROOM; + base::ByteBuffer *bb = new base::ByteBuffer(cb); + bb->WriteWord(cb); + bb->WriteByte(XMSG_LOBBYADDROOM); + char roomT[kcbRoomname]; + memset(roomT, 0, sizeof(roomT)); + strncpyz(roomT, room, sizeof(roomT)); + bb->WriteBytes((const byte *)roomT, sizeof(roomT)); + bb->WriteDword(roomid); + bb->WriteDword(priv ? 1 : 0); + bb->WriteDword(cPlayers); + bb->WriteDword(cGames); + if (bb->Length() != cb) { + delete bb; + return NULL; + } + return bb; +} + +XMsgLobbyAddRoom *XMsgLobbyAddRoom::FromBuffer(base::ByteBuffer& bb, dword cb) { + dword cbSav = bb.Length(); + word w; + if (!bb.ReadWord(&w)) { + return NULL; + } + if (cb != (dword)w) { + Assert(); + return NULL; + } + if (w - sizeof(word) > bb.Length()) { + return NULL; + } + byte id = 0; + bb.ReadByte(&id); + if (id != XMSG_LOBBYADDROOM) { + return NULL; + } + char room[kcbRoomname]; + bb.ReadBytes((byte *)room, sizeof(room)); + dword roomid; + bb.ReadDword(&roomid); + dword v; + bb.ReadDword(&v); + bool priv = (v != 0); + dword cPlayers; + bb.ReadDword(&cPlayers); + dword cGames; + bb.ReadDword(&cGames); + + if (cbSav - bb.Length() != w) { + Assert(); + return NULL; + } + + return new XMsgLobbyAddRoom(room, roomid, priv, cPlayers, cGames); +} + +} // namespace wi diff --git a/mpshared/messages.h b/mpshared/messages.h new file mode 100644 index 0000000..a52f0a2 --- /dev/null +++ b/mpshared/messages.h @@ -0,0 +1,344 @@ +#ifndef __MESSAGES_H__ +#define __MESSAGES_H__ + +#include "base/bytebuffer.h" +#include "mpshared/xmsg.h" +#include "mpshared/netmessage.h" +#include "mpshared/constants.h" +#include "mpshared/misc.h" +#include "base/misc.h" + +namespace wi { + +const dword knLobbyCreateRoomResultSuccess = 0; +const dword knLobbyCreateRoomResultFail = 1; +const dword knLobbyCreateRoomResultFull = 2; +const dword knLobbyCreateRoomResultExists = 3; + +STARTLABEL(LobbyCreateRoomResults) + LABEL(knLobbyCreateRoomResultSuccess) + LABEL(knLobbyCreateRoomResultFail) + LABEL(knLobbyCreateRoomResultFull) + LABEL(knLobbyCreateRoomResultExists) +ENDLABEL(LobbyCreateRoomResults) + +const dword knHandshakeResultSuccess = 0; +const dword knHandshakeResultFail = 1; +const dword knHandshakeResultServerFull = 2; + +STARTLABEL(HandshakeResults) + LABEL(knHandshakeResultSuccess) + LABEL(knHandshakeResultFail) + LABEL(knHandshakeResultServerFull) +ENDLABEL(HandshakeResults) + +const dword knGameJoinResultSuccess = 0; +const dword knGameJoinResultFail = 1; +const dword knGameJoinResultRoomNotFound = 2; +const dword knGameJoinResultGameNotFound = 3; +const dword knGameJoinResultGameFull = 4; +const dword knGameJoinResultInProgress = 5; + +STARTLABEL(GameJoinResults) + LABEL(knGameJoinResultSuccess) + LABEL(knGameJoinResultFail) + LABEL(knGameJoinResultGameNotFound) + LABEL(knGameJoinResultRoomNotFound) + LABEL(knGameJoinResultGameFull) + LABEL(knGameJoinResultInProgress) +ENDLABEL(GameJoinResults) + +const dword knGameLeaveResultSuccess = 0; +const dword knGameLeaveResultFail = 1; +const dword knGameLeaveResultNotFound = 2; + +STARTLABEL(GameLeaveResults) + LABEL(knGameLeaveResultSuccess) + LABEL(knGameLeaveResultFail) + LABEL(knGameLeaveResultNotFound) +ENDLABEL(GameLeaveResults) + +const dword knRoomCreateGameResultSuccess = 0; +const dword knRoomCreateGameResultFail = 1; +const dword knRoomCreateGameResultUnknownMissionPack = 2; +const dword knRoomCreateGameResultUpgradeMissionPack = 3; +const dword knRoomCreateGameResultRoomFull = 4; + +STARTLABEL(CreateGameResults) + LABEL(knRoomCreateGameResultSuccess) + LABEL(knRoomCreateGameResultFail) + LABEL(knRoomCreateGameResultUnknownMissionPack) + LABEL(knRoomCreateGameResultUpgradeMissionPack) + LABEL(knRoomCreateGameResultRoomFull) +ENDLABEL(CreateGameResults) + +const dword knLoginResultSuccess = 0; +const dword knLoginResultAnonymousSuccess = 1; +const dword knLoginResultFail = 2; +const dword knLoginResultStaleToken = 3; +const dword knLoginResultAuthDown = 4; +const dword knLoginResultNoPassword = 5; + +STARTLABEL(LoginResults) + LABEL(knLoginResultSuccess) + LABEL(knLoginResultAnonymousSuccess) + LABEL(knLoginResultFail) + LABEL(knLoginResultStaleToken) + LABEL(knLoginResultAuthDown) + LABEL(knLoginResultNoPassword) +ENDLABEL(LoginResults) + +const dword knSignOutResultSuccess = 0; +const dword knSignOutResultFail = 1; + +STARTLABEL(SignOutResults) + LABEL(knSignOutResultSuccess) + LABEL(knSignOutResultFail) +ENDLABEL(SignOutResults) + +const dword knRoomJoinResultSuccess = 0; +const dword knRoomJoinResultFail = 1; +const dword knRoomJoinResultFull = 2; +const dword knRoomJoinResultNotFound = 3; +const dword knRoomJoinResultWrongPassword = 4; + +STARTLABEL(RoomJoinResults) + LABEL(knRoomJoinResultSuccess) + LABEL(knRoomJoinResultFail) + LABEL(knRoomJoinResultFull) + LABEL(knRoomJoinResultNotFound) + LABEL(knRoomJoinResultWrongPassword) +ENDLABEL(RoomJoinResults) + +const dword knRoomLeaveResultSuccess = 0; +const dword knRoomLeaveResultFail = 1; + +STARTLABEL(RoomLeaveResults) + LABEL(knRoomLeaveResultSuccess) + LABEL(knRoomLeaveResultFail) +ENDLABEL(RoomLeaveResults) + +const dword knLobbyJoinResultSuccess = 0; +const dword knLobbyJoinResultFail = 1; +const dword knLobbyJoinResultNotLoggedIn = 2; +const dword knLobbyJoinResultFull = 3; + +STARTLABEL(LobbyJoinResults) + LABEL(knLobbyJoinResultSuccess) + LABEL(knLobbyJoinResultFail) + LABEL(knLobbyJoinResultNotLoggedIn) + LABEL(knLobbyJoinResultFull) +ENDLABEL(LobbyJoinResults) + +const dword knLobbyLeaveResultSuccess = 0; +const dword knLobbyLeaveResultFail = 1; + +STARTLABEL(LobbyLeaveResults) + LABEL(knLobbyLeaveResultSuccess) + LABEL(knLobbyLeaveResultFail) +ENDLABEL(LobbyLeaveResults) + +typedef XMsg2 XMsgHandshake; +typedef XMsg2 XMsgHandshakeResult; +typedef XMsg0 XMsgEcho; +typedef XMsg1 XMsgProtocolError; +typedef XMsgS3 XMsgLogin; +typedef XMsg1 XMsgLoginResult; +typedef XMsg0 XMsgSignOut; +typedef XMsg1 XMsgSignOutResult; +typedef XMsg0 XMsgLobbyJoin; +typedef XMsg1 XMsgLobbyJoinResult; +typedef XMsgS2 XMsgLobbyCreateRoom; +typedef XMsg2 XMsgLobbyCreateRoomResult; +typedef XMsgDS XMsgLobbyCanJoinRoom; +typedef XMsg1 XMsgLobbyCanJoinRoomResult; +typedef XMsg1 XMsgLobbyLurkerCount; +typedef XMsg1 XMsgLobbyRemoveRoom; +typedef XMsg3 XMsgLobbyUpdateRoom; +typedef XMsg0 XMsgLobbyLeave; +typedef XMsg1 XMsgLobbyLeaveResult; +typedef XMsgDS XMsgRoomJoin; +typedef XMsg1 XMsgRoomJoinResult; +typedef XMsgS1 XMsgRoomAddPlayer; +typedef XMsgDS XMsgRoomRemovePlayer; +typedef XMsgS1 XMsgRoomSendChat; +typedef XMsgS2 XMsgRoomReceiveChat; +typedef XMsg2 XMsgRoomRemoveGame; +typedef XMsg1 XMsgRoomGameInProgress; +typedef XMsg0 XMsgRoomStatusComplete; +typedef XMsg1 XMsgRoomCanJoinGame; +typedef XMsg1 XMsgRoomCanJoinGameResult; +typedef XMsg1 XMsgRoomLeave; +typedef XMsg1 XMsgRoomLeaveResult; +typedef XMsg2 XMsgGameJoin; +typedef XMsg1 XMsgGameJoinResult; +typedef XMsgS1 XMsgGameSendChat; +typedef XMsgS2 XMsgGameReceiveChat; +typedef XMsg1 XMsgGameKilled; +typedef XMsg0 XMsgGameLeave; +typedef XMsg1 XMsgGameLeaveResult; + +struct XMsgShowMessage : public XMsg { + XMsgShowMessage(const char *message, dword ipRedirect, bool disconnect) : + XMsg(XMSG_SHOWMESSAGE) { + message_ = AllocString(message); + ipRedirect_ = ipRedirect; + disconnect_ = disconnect; + } + ~XMsgShowMessage() { + delete message_; + } + const char *message_; + dword ipRedirect_; + bool disconnect_; +#ifdef LOGGING + virtual std::string ToString(); +#endif + static base::ByteBuffer *ToBuffer(const char *message, dword ipRedirect, + bool disconnect); + static XMsgShowMessage *FromBuffer(base::ByteBuffer& bb, dword cb); +}; +const dword XMSGSIZE_SHOWMESSAGE_FIXED = XMSGSIZE_FIXED + sizeof(dword) * 3; + +struct XMsgLobbyAddRoom : public XMsg { + XMsgLobbyAddRoom(const char *room, dword roomid, bool priv, + dword cPlayers, dword cGames) : XMsg(XMSG_LOBBYADDROOM) { + room_ = AllocString(room); + roomid_ = roomid; + priv_ = priv; + cPlayers_ = cPlayers; + cGames_ = cGames; + } + ~XMsgLobbyAddRoom() { + delete room_; + } + const char *room_; + dword roomid_; + bool priv_; + dword cPlayers_; + dword cGames_; +#ifdef LOGGING + virtual std::string ToString(); +#endif + static base::ByteBuffer *ToBuffer(const char *room, dword roomid, + bool priv, dword cPlayers, dword cGames); + static XMsgLobbyAddRoom *FromBuffer(base::ByteBuffer& bb, dword cb); +}; +const dword XMSGSIZE_LOBBYADDROOM = XMSGSIZE_FIXED + kcbRoomname + + sizeof(dword) * 4; + +struct XMsgRoomAddGame : public XMsg +{ + XMsgRoomAddGame(const char *player, dword gameid, const GameParams& params, + int minplayers, int maxplayers, const char *title, dword ctotal) : + XMsg(XMSG_ROOMADDGAME), gameid_(gameid), params_(params), + minplayers_(minplayers), maxplayers_(maxplayers), ctotal_(ctotal) { + strncpyz(player_, player, sizeof(player_)); + strncpyz(title_, title, sizeof(title_)); + } + + char player_[kcbPlayerName]; + dword ctotal_; + dword gameid_; + dword minplayers_; + dword maxplayers_; + char title_[kcbLevelTitle]; + GameParams params_; + + static base::ByteBuffer *ToBuffer(const char *player, dword gameid, + const GameParams& params, dword minplayers, dword maxplayers, + const char *title, dword ctotal); + static XMsgRoomAddGame *FromBuffer(base::ByteBuffer& bb, dword cb); +}; +const dword XMSGSIZE_ROOMADDGAME = XMSGSIZE_FIXED + kcbPlayerName + + sizeof(dword) * 2 + sizeof(int) * 2 + kcbLevelTitle + + sizeof(GameParams); + +struct XMsgRoomGamePlayerNames : public XMsg +{ + XMsgRoomGamePlayerNames(dword gameid, dword cnames, + PlayerName *anames) : XMsg(XMSG_ROOMGAMEPLAYERNAMES), + gameid_(gameid), cnames_(cnames), anames_(anames) { } + virtual ~XMsgRoomGamePlayerNames() { delete anames_; } + dword gameid_; + dword cnames_; + PlayerName *anames_; + + static base::ByteBuffer *ToBuffer(dword gameid, dword cnames, + const PlayerName *anames); + static XMsgRoomGamePlayerNames *FromBuffer(base::ByteBuffer& bb, dword cb); +}; +const dword XMSGSIZE_ROOMGAMEPLAYERNAMES_FIXED = XMSGSIZE_FIXED + + sizeof(dword) * 2; + +struct XMsgRoomCreateGame : public XMsg { + XMsgRoomCreateGame(GameParams *params) : XMsg(XMSG_ROOMCREATEGAME) { + params_ = *params; + } + GameParams params_; +#ifdef LOGGING + virtual std::string ToString(); +#endif + static base::ByteBuffer *ToBuffer(GameParams *params); + static XMsgRoomCreateGame *FromBuffer(base::ByteBuffer& bb, dword cb); +}; +const dword XMSGSIZE_ROOMCREATEGAME = XMSGSIZE_FIXED + sizeof(GameParams); + +struct XMsgRoomCreateGameResult : public XMsg { + XMsgRoomCreateGameResult(dword gameid, dword result, + const PackId *ppackid) : XMsg(XMSG_ROOMCREATEGAMERESULT) { + gameid_ = gameid; + result_= result; + packid_ = *ppackid; + } + dword gameid_; + dword result_; + PackId packid_; + static base::ByteBuffer *ToBuffer(dword gameid, dword result, + const PackId *ppackid); + static XMsgRoomCreateGameResult *FromBuffer(base::ByteBuffer& bb, + dword cb); +}; +const dword XMSGSIZE_ROOMCREATEGAMERESULT = XMSGSIZE_FIXED + + sizeof(dword) * 2 + sizeof(PackId); + +struct XMsgGameNetMessage : public XMsg +{ + XMsgGameNetMessage(NetMessage *pnm) : XMsg(XMSG_GAMENETMESSAGE) { + pnm_ = pnm; + } + virtual ~XMsgGameNetMessage() { + delete pnm_; + } + NetMessage *pnm_; +#ifdef LOGGING + virtual std::string ToString(); +#endif + static base::ByteBuffer *ToBuffer(NetMessage *pnm); + static XMsgGameNetMessage *FromBuffer(base::ByteBuffer& bb, dword cb); +}; +const dword XMSGSIZE_GAMENETMESSAGE_FIXED = XMSGSIZE_FIXED; + +struct XMsgGameUpdateEmpty : public XMsg { + //long cUpdatesBlock; + //long cUpdatesSync; + + static base::ByteBuffer *ToBuffer(UpdateNetMessage *punm); + static XMsgGameNetMessage *FromBuffer(base::ByteBuffer& bb, dword cb); +}; +const dword XMSGSIZE_GAMEUPDATEEMPTY = XMSGSIZE_FIXED + sizeof(dword) * 2; + +struct XMsgGameUpdateResult : public XMsg { + //long cUpdatesBlock; + //dword hash; + //short cmsLatency; + + static base::ByteBuffer *ToBuffer(UpdateResultNetMessage *purnm); + static XMsgGameNetMessage *FromBuffer(base::ByteBuffer& bb, dword cb); +}; +const dword XMSGSIZE_GAMEUPDATERESULT = XMSGSIZE_FIXED + sizeof(dword) + + sizeof(dword) + sizeof(short); + +} // namespace wi + +#endif // __MESSAGES_H__ diff --git a/mpshared/misc.cpp b/mpshared/misc.cpp new file mode 100644 index 0000000..92f70f8 --- /dev/null +++ b/mpshared/misc.cpp @@ -0,0 +1,74 @@ +#include "mpshared/misc.h" +#include + +namespace wi { + +// Zero terminating counted strcpy + +void strncpyz(char *pszDst, const char *pszSrc, int cb) +{ + if (cb <= 0) + return; + cb--; + while (cb-- != 0) { + if ((*pszDst++ = *pszSrc++) == 0) + return; + } + *pszDst = 0; +} + +int stricmp(const char *psz1, const char *psz2) +{ + while (true) { + byte b1 = (byte)*psz1++; + if (b1 >= 'A' && b1 <= 'Z') + b1 += 'a' - 'A'; + byte b2 = (byte)*psz2++; + if (b2 >= 'A' && b2 <= 'Z') + b2 += 'a' - 'A'; + if (b1 != b2) + return b1 - b2; + if (b1 == 0) + return 0; + } +} + +dword HashString(const char *s) { + dword h = 0; + for (int i = 0; *s != 0; s++, i++) { + h = 5 * (i + 1) * h + *s; + } + return h; +} + +char *AllocString(const char *psz) { + int cb = strlen(psz) + 1; + char *pszNew = new char[cb]; + if (pszNew == NULL) { + return NULL; + } + strncpyz(pszNew, psz, cb); + return pszNew; +} + +const char *StripWhitespace(const char *sz, int *pcch) { + int cch = strlen(sz); + int start = 0; + for (int i = 0; i < cch; i++, start++) { + if (!isspace(sz[i])) { + break; + } + } + + int end = cch; + for (int i = cch - 1; i >= start; i--, end--) { + if (!isspace(sz[i])) { + break; + } + } + + *pcch = end - start; + return &sz[start]; +} + +} // namespace wi diff --git a/mpshared/misc.h b/mpshared/misc.h new file mode 100644 index 0000000..0221bfe --- /dev/null +++ b/mpshared/misc.h @@ -0,0 +1,25 @@ +#ifndef __MISC_H__ +#define __MISC_H__ + +#include "inc/basictypes.h" +#include + +namespace wi { + +#if defined(__CPU_68K) +#define BigWord(x) (x) +#define BigDword(x) (x) +#else +#define BigWord(x) ((((x)&0xFF)<<8) | (((x)&0xFF00)>>8)) +#define BigDword(x) ((((x)&0xFF)<<24) | (((x)&0xFF00)<<8) | (((x)&0xFF0000)>>8) | (((x)&0xFF000000)>>24)) +#endif + +extern void strncpyz(char *pszDst, const char *pszSrc, int cb); +extern int stricmp(const char *psz1, const char *psz2); +extern dword HashString(const char *s); +extern char *AllocString(const char *psz); +extern const char *StripWhitespace(const char *sz, int *pcch); + +} // namespace wi + +#endif // __MISC_H__ diff --git a/mpshared/mpht.h b/mpshared/mpht.h new file mode 100644 index 0000000..811b76d --- /dev/null +++ b/mpshared/mpht.h @@ -0,0 +1,321 @@ +#ifndef __MPHT_H__ +#define __MPHT_H__ + +#include "inc/basictypes.h" +#include "mpshared/side.h" +#include "base/misc.h" + +namespace wi { + +const long kcUpdatesBlockInitial = 1; + +const dword kdwClientID = 0x47414d45; + +#define kdwTypeAddon 'ADD2' +#define kszTypeAddon "ADD2" + +#define knVersionLevelSupported 1 + +// NOTE: Gob ids (gid) and StateMachine ids (smid) are interchangeable + +typedef word Gid; // gid +typedef word StateMachineId; // smid +typedef word Side; // side +typedef short UnitType; // ut +typedef unsigned long UnitMask; // um +typedef signed short TCoord; +typedef signed short WCoord; +typedef word Pid; // pid + +const int kcPlayersMax = 4 + 1; // + 1 computer controlled neutral player + +const Pid kpidNeutral = 0xffff; + +#define knLagNone 0 +#define knLagGrace 1 +#define knLagGuilty 2 +#define knLagKill 3 + +const int kcupdAllowedLag = 7; // TUNE: + +const int kcbLevelTitle = 40; +#define kcbFilename 29 +const int kcbPlayerName = 32; +const int kcbRoomname = 32; +const int kcbPassword = 32; +const int kcbUsernameMax = 64; +const int kcbTokenMax = 256; +const int kcbDidMax = 34; +const int kcbChatMax = 1024; +const int kcbShowMessageMax = 512; + +// Side masks + +typedef word SideMask; // sidm + +const SideMask ksidmNeutral = 1 << ksideNeutral; +const SideMask ksidmSide1 = 1 << kside1; +const SideMask ksidmSide2 = 1 << kside2; +const SideMask ksidmSide3 = 1 << kside3; +const SideMask ksidmSide4 = 1 << kside4; +const SideMask ksidmAll = ksidmNeutral | ksidmSide1 | ksidmSide2 | ksidmSide3 | ksidmSide4; + +#define GetSideMask(side) (1 << side) + +const word kfPlrInUse = 0x0001; +const word kfPlrReady = 0x0002; +const word kfPlrComputer = 0x0004; +const word kfPlrUnfulfilled = 0x0008; +const word kfPlrStructureAttacked = 0x0010; +const word kfPlrComputerOvermind = 0x0020; +const word kfPlrAutoRepair = 0x0040; +const word kfPlrDisconnectBroadcasted = 0x0080; +const word kfPlrObserver = 0x0100; +const word kfPlrSummaryShown = 0x0200; +const word kfPlrCreator = 0x0400; +const word kfPlrDisconnectScheduled = 0x0800; +const word kfPlrWinner = 0x1000; +const word kfPlrLoser = 0x2000; +const word kfPlrRemovedAtGameStart = 0x4000; +const word kfPlrHumanJoined = 0x8000; + +// Size + +struct Size // siz +{ + int cx; + int cy; +}; + +struct WPoint // pt +{ + WCoord wx, wy; +}; + +// Network address + +struct NetAddress // nad +{ + byte ab[16]; +}; + +// Simulation version, use in multiplayer game params + +#define knVersionSimulation 2 + +#define PACKID_MAIN 0xffffffff + +struct PackId { + dword id; + byte hash[16]; +}; + +struct GameParams // rams +{ + PackId packid; // 20 + dword dwVersionSimulation; // 4 + long tGameSpeed; // 4 + char szLvlFilename[kcbFilename]; // 29 + byte filler[3]; // 3 +}; // 60 + +struct PlayerName { + char szPlayerName[kcbPlayerName]; +}; + +// Message is sent multiplayer and dispatched as is (without going through +// reformatting). This means: fixed sized data types, "platform independent" +// packing (meaning data types must be aligned on natural boundaries, like +// dword on a 4 byte boundary etc). Message structure arrays are memcpy'ed to +// produce NetMessages. This gives the additional requirement of packed Message +// structures beginning on 4 byte boundaries. To achieve this ensure that all +// union'ed structs are 4 bytes length multiples + +struct Message // msg +{ + word mid; + // UNDONE: smidSender isn't used except by DEBUG_HELPERS + StateMachineId smidSender; + StateMachineId smidReceiver; + word wDummy; // for long alignment + long tDelivery; + + // MessageId-specific arguments + + union { + struct { + Gid gidAssailant; + Side sideAssailant; + // UNDONE: maybe a damage type (e.g., small-arms, area weapon) + short nDamage; + word wDummy; + } Hit; + + struct { + word sfx; + word wDummy; + } PlaySfx; + + struct { + Gid gidEnemy; + word wDummy; + } EnemyNearby; + + struct { + Gid gidTarget; + WPoint wptTarget; + WPoint wptTargetCenter; + TCoord tcTargetRadius; + WCoord wcMoveDistPerUpdate; + word wDummy; + } MoveCommand; + + // NOTE: both the gidTarget and wptTarget must be filled out so the + // attacker can continue on to the target's location even if it no + // longer exists. + + struct { + Gid gidTarget; + WPoint wptTarget; + WPoint wptTargetCenter; + TCoord tcTargetRadius; + WCoord wcMoveDistPerUpdate; + word wDummy; + } AttackCommand; + + struct { + word wfUpgrade; + word wDummy; + } UpgradeCommand; + + struct { + word ut; + WPoint wpt; + word wDummy; + } BuildOtherCommand; + + struct { + UnitType ut; + word wDummy; + } AbortBuildOtherCommand; + + struct { + Gid gidTarget; + word wDummy; + } DeliverCommand; + + struct { + Gid gidTarget; + WPoint wptTarget; + word wDummy; + } MineCommand; + + struct { + word nArea; + word wDummy; + } GuardAreaCommand; + + struct { + UnitMask um; + } HuntEnemiesCommand; + + struct { + Pid pid; + word nReason; + } PlayerDisconnect; + }; +}; + +// NOTE: be sure to update the corresponding string table in StateMachine.cpp + +enum MessageId { // mid + kmidReservedNull, + kmidReservedEnter, + kmidReservedExit, + kmidReservedUpdate, + kmidHit, + kmidNearbyAllyHit, + kmidDelete, + kmidEnemyNearby, + kmidPowerLowHigh, + kmidMoveWaitingNearby, + kmidBeingUpgraded, + kmidUpgradeComplete, + kmidMoveCommand, + kmidAttackCommand, + kmidAnimationComplete, + kmidBuildOtherCommand, + kmidAbortBuildOtherCommand, + kmidBuildComplete, + kmidFireComplete, + kmidSpawnSmoke, + kmidSelfDestructCommand, + kmidRepairCommand, + kmidUpgradeCommand, + kmidAbortUpgradeCommand, + kmidTransformCommand, + kmidDeliverCommand, + kmidGalaxiteDelivery, + kmidMineCommand, + kmidMoveAction, + kmidAttackAction, + kmidGuardAction, + kmidGuardVicinityAction, + kmidGuardAreaAction, + kmidHuntEnemiesAction, + kmidFire, + kmidHeal, + kmidPlaySfx, + kmidPlayerDisconnect, +}; + +STARTLABEL(MessageNames) + LABEL(kmidReservedNull) + LABEL(kmidReservedEnter) + LABEL(kmidReservedExit) + LABEL(kmidReservedUpdate) + LABEL(kmidHit) + LABEL(kmidNearbyAllyHit) + LABEL(kmidDelete) + LABEL(kmidEnemyNearby) + LABEL(kmidPowerLowHigh) + LABEL(kmidMoveWaitingNearby) + LABEL(kmidBeingUpgraded) + LABEL(kmidUpgradeComplete) + LABEL(kmidMoveCommand) + LABEL(kmidAttackCommand) + LABEL(kmidAnimationComplete) + LABEL(kmidBuildOtherCommand) + LABEL(kmidAbortBuildOtherCommand) + LABEL(kmidBuildComplete) + LABEL(kmidFireComplete) + LABEL(kmidSpawnSmoke) + LABEL(kmidSelfDestructCommand) + LABEL(kmidRepairCommand) + LABEL(kmidUpgradeCommand) + LABEL(kmidAbortUpgradeCommand) + LABEL(kmidTransformCommand) + LABEL(kmidDeliverCommand) + LABEL(kmidGalaxiteDelivery) + LABEL(kmidMineCommand) + LABEL(kmidMoveAction) + LABEL(kmidAttackAction) + LABEL(kmidGuardAction) + LABEL(kmidGuardVicinityAction) + LABEL(kmidGuardAreaAction) + LABEL(kmidHuntEnemiesAction) + LABEL(kmidFire) + LABEL(kmidHeal) + LABEL(kmidPlaySfx) +ENDLABEL(MessageNames) + +struct UpdateResult // ur +{ + long cUpdatesBlock; + dword hash; + long cmsLatency; +}; + +} // namespace wi + +#endif // __MPHT_H__ diff --git a/mpshared/netmessage.cpp b/mpshared/netmessage.cpp new file mode 100644 index 0000000..6e14566 --- /dev/null +++ b/mpshared/netmessage.cpp @@ -0,0 +1,444 @@ +#include "inc/basictypes.h" +#include "inc/rip.h" +#include "mpshared/netmessage.h" +#include "mpshared/mpht.h" +#include "base/bytebuffer.h" +#include "base/format.h" + +namespace wi { + +long gatGameSpeeds[15] = { + 64, 48, 32, 24, 16, 12, 10, 8, 7, 6, 5, 4, 3, 2, 1 +}; + +#ifndef __CPU_68K + +#define SwapWord(x) ((((x)&0xFF)<<8) | (((x)&0xFF00)>>8)) +#define SwapDword(x) ((((x)&0xFF)<<24) | (((x)&0xFF00)<<8) | (((x)&0xFF0000)>>8) | (((x)&0xFF000000)>>24)) + +void MessageByteOrderSwap(word cmsgs, Message *pmsg, bool fNative) +{ + for (; cmsgs != 0; cmsgs--, pmsg++) { + word mid = pmsg->mid; + if (!fNative) { + mid = base::ByteBuffer::HostToNetWord(pmsg->mid); + } + + pmsg->mid = SwapWord(pmsg->mid); + pmsg->smidSender = SwapWord(pmsg->smidSender); + pmsg->smidReceiver = SwapWord(pmsg->smidReceiver); + pmsg->tDelivery = SwapDword(pmsg->tDelivery); + + switch (mid) { + case kmidHit: + case kmidNearbyAllyHit: + case kmidDelete: + case kmidEnemyNearby: + case kmidPowerLowHigh: + case kmidMoveWaitingNearby: + case kmidBeingUpgraded: + case kmidUpgradeComplete: + case kmidAnimationComplete: + case kmidBuildComplete: + case kmidFireComplete: + case kmidSpawnSmoke: + case kmidGalaxiteDelivery: + case kmidMoveAction: + case kmidAttackAction: + case kmidGuardAction: + case kmidGuardVicinityAction: + case kmidGuardAreaAction: + case kmidHuntEnemiesAction: + case kmidFire: + case kmidHeal: + case kmidPlaySfx: + // These are only sent. If they arrive here, it's a mistake + + Assert(false, "Message %d is classified as sent only", pmsg->mid); + break; + + default: + // Message unknown, not classified yet + + Assert(false, "Message %d unknown, not classified yet", pmsg->mid); + break; + + // Posted + + case kmidMineCommand: + pmsg->MineCommand.gidTarget = SwapWord(pmsg->MineCommand.gidTarget); + pmsg->MineCommand.wptTarget.wx = SwapWord(pmsg->MineCommand.wptTarget.wx); + pmsg->MineCommand.wptTarget.wy = SwapWord(pmsg->MineCommand.wptTarget.wy); + break; + + case kmidAttackCommand: + pmsg->AttackCommand.gidTarget = SwapWord(pmsg->AttackCommand.gidTarget); + pmsg->AttackCommand.wptTarget.wx = SwapWord(pmsg->AttackCommand.wptTarget.wx); + pmsg->AttackCommand.wptTarget.wy = SwapWord(pmsg->AttackCommand.wptTarget.wy); + pmsg->AttackCommand.wptTargetCenter.wx = SwapWord(pmsg->AttackCommand.wptTargetCenter.wx); + pmsg->AttackCommand.wptTargetCenter.wy = SwapWord(pmsg->AttackCommand.wptTargetCenter.wy); + pmsg->AttackCommand.tcTargetRadius = SwapWord(pmsg->AttackCommand.tcTargetRadius); + pmsg->AttackCommand.wcMoveDistPerUpdate = SwapWord(pmsg->AttackCommand.wcMoveDistPerUpdate); + break; + + case kmidMoveCommand: + pmsg->MoveCommand.gidTarget = SwapWord(pmsg->MoveCommand.gidTarget); + pmsg->MoveCommand.wptTarget.wx = SwapWord(pmsg->MoveCommand.wptTarget.wx); + pmsg->MoveCommand.wptTarget.wy = SwapWord(pmsg->MoveCommand.wptTarget.wy); + pmsg->MoveCommand.wptTargetCenter.wx = SwapWord(pmsg->MoveCommand.wptTargetCenter.wx); + pmsg->MoveCommand.wptTargetCenter.wy = SwapWord(pmsg->MoveCommand.wptTargetCenter.wy); + pmsg->MoveCommand.tcTargetRadius = SwapWord(pmsg->MoveCommand.tcTargetRadius); + pmsg->MoveCommand.wcMoveDistPerUpdate = SwapWord(pmsg->MoveCommand.wcMoveDistPerUpdate); + break; + + case kmidBuildOtherCommand: + pmsg->BuildOtherCommand.ut = SwapWord(pmsg->BuildOtherCommand.ut); + pmsg->BuildOtherCommand.wpt.wx = SwapWord(pmsg->BuildOtherCommand.wpt.wx); + pmsg->BuildOtherCommand.wpt.wy = SwapWord(pmsg->BuildOtherCommand.wpt.wy); + break; + + case kmidAbortBuildOtherCommand: + pmsg->AbortBuildOtherCommand.ut = SwapWord(pmsg->AbortBuildOtherCommand.ut); + break; + + case kmidUpgradeCommand: + pmsg->UpgradeCommand.wfUpgrade = SwapWord(pmsg->UpgradeCommand.wfUpgrade); + break; + + case kmidDeliverCommand: + pmsg->DeliverCommand.gidTarget = SwapWord(pmsg->DeliverCommand.gidTarget); + break; + + case kmidTransformCommand: + case kmidSelfDestructCommand: + case kmidRepairCommand: + case kmidAbortUpgradeCommand: + // No parameters + break; + + case kmidPlayerDisconnect: + pmsg->PlayerDisconnect.pid = SwapWord(pmsg->PlayerDisconnect.pid); + pmsg->PlayerDisconnect.nReason = + SwapWord(pmsg->PlayerDisconnect.nReason); + break; + } + } +} + +void SwapGameParams(GameParams *prams) +{ + prams->packid.id = SwapDword(prams->packid.id); + prams->dwVersionSimulation = SwapDword(prams->dwVersionSimulation); + prams->tGameSpeed = SwapDword(prams->tGameSpeed); +} + +void NetMessageByteOrderSwap(NetMessage *pnm, bool fNative) +{ + // Swap endian order + + word nmid; + if (fNative) { + nmid = pnm->nmid; + } else { + nmid = SwapWord(pnm->nmid); + } + + pnm->nmid = SwapWord(pnm->nmid); + pnm->cb = SwapWord(pnm->cb); + + switch (nmid) { + case knmidCsPlayerJoin: + case knmidCsClientReady: + case knmidScCantAcceptMoreConnections: + case knmidScServerInfo: + case knmidCsConnect: + case knmidCsRequestBeginGame: + case knmidScBeginGameFail: + case knmidCsLagAcknowledge: + case knmidCsChallengeWin: + // Nothing to do for these + + break; + + case knmidCsWinStats: + { + WinStatsNetMessage *pwsnm = (WinStatsNetMessage *)pnm; + pwsnm->ws.cCreditsAcquired = SwapDword(pwsnm->ws.cCreditsAcquired); + pwsnm->ws.cCreditsConsumed = SwapDword(pwsnm->ws.cCreditsConsumed); + pwsnm->ws.sidm = SwapWord(pwsnm->ws.sidm); + pwsnm->ws.sidmAllies = SwapWord(pwsnm->ws.sidmAllies); + pwsnm->ws.cEnemyMobileUnitsKilled = + SwapWord(pwsnm->ws.cEnemyMobileUnitsKilled); + pwsnm->ws.cEnemyStructuresKilled = + SwapWord(pwsnm->ws.cEnemyStructuresKilled); + pwsnm->ws.cMobileUnitsLost = SwapWord(pwsnm->ws.cMobileUnitsLost); + pwsnm->ws.cStructuresLost = SwapWord(pwsnm->ws.cStructuresLost); + pwsnm->ws.ff = SwapWord(pwsnm->ws.ff); + for (int i = 0; i < ARRAYSIZE(pwsnm->ws.acut); i++) { + pwsnm->ws.acut[i] = SwapWord(pwsnm->ws.acut[i]); + } + for (int i = 0; i < ARRAYSIZE(pwsnm->ws.acutBuilt); i++) { + pwsnm->ws.acutBuilt[i] = SwapWord(pwsnm->ws.acutBuilt[i]); + } + } + break; + + case knmidScCheckWin: + { + CheckWinNetMessage *pcwnm = (CheckWinNetMessage *)pnm; + pcwnm->pid = SwapWord(pcwnm->pid); + } + break; + + case knmidScGameParams: + { + GameParamsNetMessage *pgpnm = (GameParamsNetMessage *)pnm; + SwapGameParams(&pgpnm->rams); + } + break; + + case knmidScBeginGame: + { + BeginGameNetMessage *pbgnm = (BeginGameNetMessage *)pnm; + pbgnm->ulRandomSeed = SwapDword(pbgnm->ulRandomSeed); + } + break; + + case knmidScPlayersUpdate: + { + PlayersUpdateNetMessage *punm = (PlayersUpdateNetMessage *)pnm; + int cplrr = fNative ? punm->cplrr : SwapWord(punm->cplrr); + punm->cplrr = SwapWord(punm->cplrr); + for (int iplrr = 0; iplrr < cplrr; iplrr++) { + PlayerRecord *pplrr = &punm->aplrr[iplrr]; + pplrr->pid = SwapWord(pplrr->pid); + pplrr->side = SwapWord(pplrr->side); + pplrr->wf = SwapWord(pplrr->wf); + } + } + break; + + case knmidCsClientCommands: + { + ClientCommandsNetMessage *pccnm = (ClientCommandsNetMessage *)pnm; + MessageByteOrderSwap(fNative ? pccnm->cmsgCommands : SwapWord(pccnm->cmsgCommands), pccnm->amsgCommands, fNative); + pccnm->cmsgCommands = SwapWord(pccnm->cmsgCommands); + } + break; + + case knmidScUpdate: + { + UpdateNetMessage *punm = (UpdateNetMessage *)pnm; + MessageByteOrderSwap(fNative ? punm->cmsgCommands : SwapWord(punm->cmsgCommands), punm->amsgCommands, fNative); + punm->cmsgCommands = SwapWord(punm->cmsgCommands); + punm->cUpdatesBlock = SwapDword(punm->cUpdatesBlock); + punm->cUpdatesSync = SwapDword(punm->cUpdatesSync); + } + break; + + case knmidCsUpdateResult: + { + UpdateResultNetMessage *purnm = (UpdateResultNetMessage *)pnm; + purnm->ur.cUpdatesBlock = SwapDword(purnm->ur.cUpdatesBlock); + purnm->ur.hash = SwapDword(purnm->ur.hash); + purnm->ur.cmsLatency = SwapDword(purnm->ur.cmsLatency); + } + break; + + case knmidScLagNotify: + { + LagNotifyNetMessage *plnnm = (LagNotifyNetMessage *)pnm; + plnnm->pidLagging = SwapWord(plnnm->pidLagging); + plnnm->cSeconds = SwapWord(plnnm->cSeconds); + } + break; + + case knmidScPlayerDisconnect: + { + PlayerDisconnectNetMessage *ppdnm = (PlayerDisconnectNetMessage *)pnm; + ppdnm->pid = SwapWord(ppdnm->pid); + ppdnm->nReason = SwapWord(ppdnm->nReason); + } + break; + + case knmidCsKillLaggingPlayer: + { + KillLaggingPlayerNetMessage *pklpnm = + (KillLaggingPlayerNetMessage *)pnm; + pklpnm->pid = SwapWord(pklpnm->pid); + pklpnm->fYes = SwapWord(pklpnm->fYes); + } + break; + + case knmidCsPlayerResign: + { + PlayerResignNetMessage *pprnm = (PlayerResignNetMessage *)pnm; + pprnm->pid = SwapWord(pprnm->pid); + } + break; + + case knmidScSyncError: + { + SyncErrorNetMessage *psenm = (SyncErrorNetMessage *)pnm; + for (int i = 0; i < kcPlayersMax; i++) { + psenm->aur[i].cUpdatesBlock = + SwapDword(psenm->aur[i].cUpdatesBlock); + psenm->aur[i].hash = SwapDword(psenm->aur[i].hash); + psenm->aur[i].cmsLatency = + SwapDword(psenm->aur[i].cmsLatency); + } + psenm->urLastStraw.cUpdatesBlock = + SwapDword(psenm->urLastStraw.cUpdatesBlock); + psenm->urLastStraw.hash = SwapDword(psenm->urLastStraw.hash); + psenm->urLastStraw.cmsLatency = + SwapDword(psenm->urLastStraw.cmsLatency); + } + break; + + default: + Assert("Unhandled knmid! (%d)", nmid); + break; + } +} +#endif // ifndef __CPU_68K + +bool FindZero(const char *psz, int cb) +{ + for (const char *pszT = psz; pszT < &psz[cb]; pszT++) { + if (*pszT == 0) { + return true; + } + } + return false; +} + +bool ValidateGameParams(const GameParams& params) +{ + bool fFound = false; + for (int i = 0; i < ARRAYSIZE(gatGameSpeeds); i++) { + if (params.tGameSpeed == gatGameSpeeds[i]) { + fFound = true; + break; + } + } + if (!fFound) { + return false; + } + if (!FindZero(params.szLvlFilename, sizeof(params.szLvlFilename))) { + return false; + } + return true; +} + +#ifdef RELEASE_LOGGING +const char *GameParamsToString(const GameParams& params) { + char szHash[33]; + strncpyz(szHash, base::Format::ToHex(params.packid.hash, + sizeof(params.packid.hash)), sizeof(szHash)); + return base::Format::ToString( +"id=%d hash=%s dwVersionSimulation=%d tGameSpeed=%ld szLvlFilename=\"%s\'", + params.packid.id, szHash, params.dwVersionSimulation, + params.tGameSpeed, params.szLvlFilename); +} +#endif + +#ifdef RELEASE_LOGGING +void LogGameParams(const GameParams& params) { + RLOG() << GameParamsToString(params); +} +#endif + +#ifdef DEBUG_LOGGING +const char *PszFromNetMessage(NetMessage *pnm) +{ + static char s_szT[256]; + + switch (pnm->nmid) { + case knmidScCantAcceptMoreConnections: + return "ScCantAcceptMoreConnections"; + + case knmidScServerInfo: + ((ServerInfoNetMessage *)pnm)->ToString(s_szT, sizeof(s_szT)); + break; + + case knmidCsClientCommands: + ((ClientCommandsNetMessage *)pnm)->ToString(s_szT, sizeof(s_szT)); + break; + + case knmidScUpdate: + ((UpdateNetMessage *)pnm)->ToString(s_szT, sizeof(s_szT)); + break; + + case knmidScGameParams: + ((GameParamsNetMessage *)pnm)->ToString(s_szT, sizeof(s_szT)); + break; + + case knmidScBeginGame: + ((BeginGameNetMessage *)pnm)->ToString(s_szT, sizeof(s_szT)); + break; + + case knmidCsClientReady: + return "CsClientReady"; + + case knmidCsPlayerJoin: + ((PlayerJoinNetMessage *)pnm)->ToString(s_szT, sizeof(s_szT)); + break; + + case knmidScPlayersUpdate: + ((PlayersUpdateNetMessage *)pnm)->ToString(s_szT, sizeof(s_szT)); + break; + + case knmidCsUpdateResult: + ((UpdateResultNetMessage *)pnm)->ToString(s_szT, sizeof(s_szT)); + break; + + case knmidCsPlayerResign: + ((PlayerResignNetMessage *)pnm)->ToString(s_szT, sizeof(s_szT)); + break; + + case knmidCsRequestBeginGame: + return "CsRequestBeginGame"; + + case knmidScBeginGameFail: + return "ScBeginGameFail"; + + case knmidScPlayerDisconnect: + ((PlayerDisconnectNetMessage *)pnm)->ToString(s_szT, sizeof(s_szT)); + break; + + case knmidCsKillLaggingPlayer: + ((KillLaggingPlayerNetMessage *)pnm)->ToString(s_szT, sizeof(s_szT)); + break; + + case knmidScLagNotify: + ((LagNotifyNetMessage *)pnm)->ToString(s_szT, sizeof(s_szT)); + break; + + case knmidCsLagAcknowledge: + ((LagAcknowledgeNetMessage *)pnm)->ToString(s_szT, sizeof(s_szT)); + break; + + case knmidScSyncError: + return "ScSyncError"; + + case knmidCsWinStats: + ((WinStatsNetMessage *)pnm)->ToString(s_szT, sizeof(s_szT)); + break; + + case knmidScCheckWin: + ((CheckWinNetMessage *)pnm)->ToString(s_szT, sizeof(s_szT)); + break; + + case knmidCsChallengeWin: + return "CsChallengeWin"; + + default: + sprintf(s_szT, "NetMessage(nmid: %d)", pnm->nmid); + break; + } + + return s_szT; +} +#endif + +} // namespace wi diff --git a/mpshared/netmessage.h b/mpshared/netmessage.h new file mode 100644 index 0000000..b03c00f --- /dev/null +++ b/mpshared/netmessage.h @@ -0,0 +1,568 @@ +#ifndef __NETMESSAGE_H__ +#define __NETMESSAGE_H__ + +#include "inc/basictypes.h" +#include "inc/rip.h" +#include "mpshared/mpht.h" +#include "mpshared/misc.h" +#include "mpshared/side.h" +#include "base/format.h" +#include "base/log.h" +#include "base/misc.h" +#include +#include + +//--------------------------------------------------------------------------- +// Network and multiplayer stuff + +// NOTE: be sure to update PszFromNetMessage in Comm.cpp if you change/add/remove NetMessageIds + +namespace wi { + +class NetMessage; +#ifdef DEBUG_LOGGING +const char *PszFromNetMessage(NetMessage *pnm); +#endif + +#ifdef RELEASE_LOGGING +void LogGameParams(const GameParams& params); +const char *GameParamsToString(const GameParams& params); +#endif + +const int kcbGameName = 80; + +typedef word NetMessageId; // nmid + +const NetMessageId knmidScCantAcceptMoreConnections = 1; +const NetMessageId knmidScServerInfo = 2; +const NetMessageId knmidCsClientCommands = 3; +const NetMessageId knmidScUpdate = 4; +const NetMessageId knmidScGameParams = 5; +const NetMessageId knmidScBeginGame = 6; +const NetMessageId knmidCsClientReady = 7; +const NetMessageId knmidCsPlayerJoin = 8; +const NetMessageId knmidScPlayersUpdate = 9; +const NetMessageId knmidCsUpdateResult = 10; +//const NetMessageId knmidGameHostFound = 11; +const NetMessageId knmidCsConnect = 12; +const NetMessageId knmidScLagNotify = 13; +const NetMessageId knmidScPlayerDisconnect = 14; +const NetMessageId knmidCsPlayerResign = 15; +const NetMessageId knmidCsRequestBeginGame = 16; +const NetMessageId knmidScBeginGameFail = 17; +const NetMessageId knmidCsKillLaggingPlayer = 18; +const NetMessageId knmidScSyncError = 19; +const NetMessageId knmidCsLagAcknowledge = 20; +const NetMessageId knmidCsWinStats = 21; +const NetMessageId knmidScCheckWin = 22; +const NetMessageId knmidCsChallengeWin = 23; +const NetMessageId knmidMax = 24; + +class NetMessage // nm +{ +public: + NetMessage() {} + NetMessage(NetMessageId nmid); + + word cb; // size of whole message, including these fields + NetMessageId nmid; + + // This (and the pad below) are DEBUG only information. + // We want to play DEBUG vs. Release so it's not conditional + // OPT: ditch it to shorten each message by 4 bytes + byte nSeq; + byte abPad[3]; + + // OPT: Could move this out of the message to shorten each by 4 bytes + NetMessage *pnmNext; + // After this is dword aligned +}; + +inline NetMessage::NetMessage(NetMessageId nmid) +{ + this->nmid = nmid; + this->cb = sizeof(NetMessage); + this->pnmNext = NULL; + CompileAssert((sizeof(this) % 4) == 0); +} + +class ServerInfoNetMessage : public NetMessage { +public: + ServerInfoNetMessage(); + ServerInfoNetMessage(char *pszGameName); + char m_szGameName[kcbGameName]; + +#ifdef LOGGING + void ToString(char *psz, int cb) { + snprintf(psz, cb, "ScServerInfo(gameName: \"%s\")", m_szGameName); + } +#endif +}; + +inline ServerInfoNetMessage::ServerInfoNetMessage() +{ + nmid = knmidScServerInfo; + cb = sizeof(ServerInfoNetMessage); + CompileAssert((sizeof(this) % 4) == 0); +} + +inline ServerInfoNetMessage::ServerInfoNetMessage(char *pszGameName) +{ + nmid = knmidScServerInfo; + cb = sizeof(ServerInfoNetMessage); + strncpyz(m_szGameName, pszGameName, sizeof(m_szGameName)); + CompileAssert((sizeof(this) % 4) == 0); +} + +class ConnectNetMessage : public NetMessage { +public: + ConnectNetMessage(); + +#ifdef LOGGING + void ToString(char *psz, int cb) { + strncpyz(psz, "CsConnect", cb); + } +#endif +}; + +inline ConnectNetMessage::ConnectNetMessage() +{ + nmid = knmidCsConnect; + cb = sizeof(ConnectNetMessage); + CompileAssert((sizeof(this) % 4) == 0); +} + +class BeginGameNetMessage : public NetMessage { +public: + BeginGameNetMessage(); + unsigned long ulRandomSeed; + +#ifdef LOGGING + void ToString(char *psz, int cb) { + snprintf(psz, cb, "ScBeginGame(seed: %ld)", ulRandomSeed); + } +#endif +}; + +inline BeginGameNetMessage::BeginGameNetMessage() +{ + nmid = knmidScBeginGame; + cb = sizeof(BeginGameNetMessage); + CompileAssert((sizeof(this) % 4) == 0); +} + +class ClientCommandsNetMessage : public NetMessage { +public: + ClientCommandsNetMessage(); + word cmsgCommands; + word wDummy; // for long aligning the Messages on 68K + Message amsgCommands[1]; + +#ifdef LOGGING + void ToString(char *psz, int cb) { + snprintf(psz, cb, "CsClientCommands(cmsg: %d)", cmsgCommands); + for (int i = 0; i < cmsgCommands; i++) { + int cch = strlen(psz); + if ((cb - (cch + 1)) <= 0) + break; + snprintf(&psz[cch], cb - cch, ", %d", amsgCommands[i].mid); + } + } +#endif +}; + +inline ClientCommandsNetMessage::ClientCommandsNetMessage() +{ + Assert((sizeof(this) % 4) == 0); +} + +class UpdateNetMessage : public NetMessage { +public: + UpdateNetMessage(); + long cUpdatesBlock; + long cUpdatesSync; + word cmsgCommands; + word wDummy; // for long aligning the Messages on 68K + Message amsgCommands[1]; + +#ifdef LOGGING + void ToString(char *psz, int cb) { + snprintf(psz, cb, "ScUpdate(cmsg: %d, cUpdatesBlock: %ld, cUpdatesSync: %ld)", + cmsgCommands, cUpdatesBlock, cUpdatesSync); + } +#endif +}; + +inline UpdateNetMessage::UpdateNetMessage() +{ + Assert((sizeof(this) % 4) == 0); +} + +class GameParamsNetMessage : public NetMessage { +public: + GameParamsNetMessage(); + GameParams rams; + +#ifdef LOGGING + void ToString(char *psz, int cb) { + strncpyz(psz, GameParamsToString(rams), cb); + } +#endif +}; + +inline GameParamsNetMessage::GameParamsNetMessage() +{ + nmid = knmidScGameParams; + cb = sizeof(GameParamsNetMessage); + CompileAssert((sizeof(this) % 4) == 0); +} + +class PlayerJoinNetMessage : public NetMessage { +public: + PlayerJoinNetMessage(); + char szPlayerName[kcbPlayerName]; + +#ifdef LOGGING + void ToString(char *psz, int cb) { + snprintf(psz, cb, "CsPlayerJoin(%s)", szPlayerName); + } +#endif +}; + +inline PlayerJoinNetMessage::PlayerJoinNetMessage() +{ + nmid = knmidCsPlayerJoin; + cb = sizeof(PlayerJoinNetMessage); + CompileAssert((sizeof(this) % 4) == 0); +} + +const byte kfPlrrReady = 0x01; +const byte kfPlrrLocal = 0x02; +const byte kfPlrrComputer = 0x04; +const byte kfPlrrComputerOvermind = 0x08; +const byte kfPlrrUnfulfilled = 0x10; +const byte kfPlrrCreator = 0x20; + +struct PlayerRecord // plrr +{ + Pid pid; // 4 + Side side; // 2 + word wf; // 2 + char szName[kcbPlayerName]; // 32 +}; + +class PlayersUpdateNetMessage : public NetMessage { +public: + PlayersUpdateNetMessage(); + word cplrr; + word wDummy; // for long alignment + PlayerRecord aplrr[1]; + +#ifdef LOGGING + void ToString(char *psz, int cb) { + int cHuman = 0; + for (int i = 0; i < cplrr; i++) { + if ((aplrr[i].wf & (kfPlrrComputer | kfPlrrUnfulfilled)) == 0) { + cHuman++; + } + } + snprintf(psz, cb, "ScPlayersUpdate(cplrr human: %d)", cHuman); + } +#endif +}; + +inline PlayersUpdateNetMessage::PlayersUpdateNetMessage() +{ + CompileAssert((sizeof(this) % 4) == 0); +} + +class UpdateResultNetMessage : public NetMessage { // urnm +public: + UpdateResultNetMessage(); + UpdateResult ur; + +#ifdef LOGGING + void ToString(char *psz, int cb) { + snprintf(psz, cb, + "CsUpdateResult(cUpdatesBlock: %ld, hash %08lx, cmsLatency: %ld)", + ur.cUpdatesBlock, ur.hash, ur.cmsLatency); + } +#endif +}; + +inline UpdateResultNetMessage::UpdateResultNetMessage() +{ + nmid = knmidCsUpdateResult; + cb = sizeof(UpdateResultNetMessage); + CompileAssert((sizeof(this) % 4) == 0); +} + +struct GameHost { // gh + GameHost *pghNext; + NetAddress nad; + unsigned long msLastContact; + unsigned long id; + char szGameName[kcbGameName]; +}; + +class LagNotifyNetMessage : public NetMessage { // lnnm +public: + LagNotifyNetMessage(); + Pid pidLagging; + word cSeconds; + +#ifdef LOGGING + void ToString(char *psz, int cb) { + snprintf(psz, cb, "ScLagNotify(pidLagging: %d)", pidLagging); + } +#endif +}; + +inline LagNotifyNetMessage::LagNotifyNetMessage() +{ + nmid = knmidScLagNotify; + cb = sizeof(LagNotifyNetMessage); + CompileAssert((sizeof(this) % 4) == 0); +} + +class LagAcknowledgeNetMessage : public NetMessage { // lanm +public: + LagAcknowledgeNetMessage(); + Pid pidLagging; + word cSeconds; + +#ifdef LOGGING + void ToString(char *psz, int cb) { + snprintf(psz, cb, "ScLagAcknowledge(pidLagging: %d)", pidLagging); + } +#endif +}; + +inline LagAcknowledgeNetMessage::LagAcknowledgeNetMessage() +{ + nmid = knmidCsLagAcknowledge; + cb = sizeof(LagAcknowledgeNetMessage); + CompileAssert((sizeof(this) % 4) == 0); +} + +#define kfwsReceivedStats 0x0001 +#define kfwsWinner 0x0002 +#define kfwsLoser 0x0004 +#define kfwsWinChallenger 0x0008 +#define kfwsComputer 0x0010 +#define kfwsHuman 0x0020 +#define kfwsAnonymous 0x0040 +#define kfwsLocked 0x0080 +#define kfwsRemovedAtGameStart 0x0100 +#define kfwsMask 0x1ff + +struct WinStats { + word sidm; + word sidmAllies; + word cEnemyMobileUnitsKilled; + word cEnemyStructuresKilled; + word cMobileUnitsLost; + word cStructuresLost; + word ff; + word acut[kutMax]; + word acutBuilt[kutMax]; + word dummy; // to ensure alignment on all platforms + dword cCreditsAcquired; + dword cCreditsConsumed; +}; + +class WinStatsNetMessage : public NetMessage { // wnm +public: + WinStatsNetMessage(); + + Pid pid; + word filler; + byte hash[16]; + WinStats ws; + +#ifdef LOGGING + void ToString(char *psz, int cb) { + snprintf(psz, cb, "WinStatsNetMessage(" + "sidm: %08x, sidmAllies: %08x, " + "cCreditsAcquired: %ld, cCreditsConsumed: %ld, " + "cEnemyMobileUnitsKilled: %d, cEnemyStructuresKilled: %d, " + "cMobileUnitsLost: %d, cStructuresLost: %d, ff: %08x)", + ws.sidm, ws.sidmAllies, + ws.cCreditsAcquired, ws.cCreditsConsumed, + ws.cEnemyMobileUnitsKilled, ws.cEnemyStructuresKilled, + ws.cMobileUnitsLost, ws.cStructuresLost, ws.ff); + } +#endif +}; + +inline WinStatsNetMessage::WinStatsNetMessage() +{ + memset(this, 0, sizeof(*this)); + nmid = knmidCsWinStats; + cb = sizeof(WinStatsNetMessage); + CompileAssert((sizeof(this) % 4) == 0); +} + +class CheckWinNetMessage : public NetMessage { // cwnm +public: + CheckWinNetMessage(); + Pid pid; + +#ifdef LOGGING + void ToString(char *psz, int cb) { + snprintf(psz, cb, "ScCheckWin(pid: %d)", pid); + } +#endif +}; + +inline CheckWinNetMessage::CheckWinNetMessage() +{ + nmid = knmidScCheckWin; + cb = sizeof(CheckWinNetMessage); + CompileAssert((sizeof(this) % 4) == 0); +} + +class ChallengeWinNetMessage : public NetMessage { // cwnm +public: + ChallengeWinNetMessage(); + +#ifdef LOGGING + void ToString(char *psz, int cb) { + strncpyz(psz, "CsChallengeWin", cb); + } +#endif +}; + +inline ChallengeWinNetMessage::ChallengeWinNetMessage() +{ + nmid = knmidCsChallengeWin; + cb = sizeof(ChallengeWinNetMessage); + CompileAssert((sizeof(this) % 4) == 0); +} + +#define knDisconnectReasonResign 0 +#define knDisconnectReasonKilled 1 +#define knDisconnectReasonAbnormal 2 +#define knDisconnectReasonNotResponding 3 +#define knDisconnectReasonLeftGame 4 + +STARTLABEL(DisconnectReasons) + LABEL(knDisconnectReasonResign) + LABEL(knDisconnectReasonKilled) + LABEL(knDisconnectReasonAbnormal) + LABEL(knDisconnectReasonNotResponding) + LABEL(knDisconnectReasonLeftGame) +ENDLABEL(DisconnectReasons) + +class KillLaggingPlayerNetMessage : public NetMessage { +public: + KillLaggingPlayerNetMessage(); + Pid pid; + word fYes; + +#ifdef LOGGING + void ToString(char *psz, int cb) { + snprintf(psz, cb, "CsKillLaggingPlayer(pid: %d, fYes: %d)", + pid, fYes); + } +#endif +}; + +inline KillLaggingPlayerNetMessage::KillLaggingPlayerNetMessage() +{ + nmid = knmidCsKillLaggingPlayer; + cb = sizeof(KillLaggingPlayerNetMessage); + CompileAssert((sizeof(this) % 4) == 0); +} + +class SyncErrorNetMessage : public NetMessage { +public: + SyncErrorNetMessage(); + UpdateResult urLastStraw; + UpdateResult aur[kcPlayersMax]; + +#ifdef LOGGING + void ToString(char *psz, int cb) { + CompileAssert(kcPlayersMax == 5); + const char *pszT = base::Format::ToString("ScSyncErrorNetMessage " + "pid 0: cUpdatesBlock=%d, hash=%08x " + "pid 1: cUpdatesBlock=%d, hash=%08x " + "pid 2: cUpdatesBlock=%d, hash=%08x " + "pid 3: cUpdatesBlock=%d, hash=%08x " + "pid 4: cUpdatesBlock=%d, hash=%08x " + "urLastStraw: cUpdatesBlock=%d, hash=%08x", + aur[0].cUpdatesBlock, aur[0].hash, + aur[1].cUpdatesBlock, aur[1].hash, + aur[2].cUpdatesBlock, aur[2].hash, + aur[3].cUpdatesBlock, aur[3].hash, + aur[4].cUpdatesBlock, aur[4].hash, + urLastStraw.cUpdatesBlock, urLastStraw.hash); + strncpyz(psz, pszT, cb); + } +#endif +}; + +inline SyncErrorNetMessage::SyncErrorNetMessage() +{ + nmid = knmidScSyncError; + cb = sizeof(*this); + CompileAssert((sizeof(this) % 4) == 0); +} + +class PlayerDisconnectNetMessage : public NetMessage { // pdnm +public: + PlayerDisconnectNetMessage(); + Pid pid; + word nReason; + +#ifdef LOGGING + void ToString(char *psz, int cb) { + snprintf(psz, cb, "ScPlayerDisconnect(pid: %d, nReason: %s)", + pid, DisconnectReasons.Find(nReason)); + } +#endif +}; + +inline PlayerDisconnectNetMessage::PlayerDisconnectNetMessage() +{ + nmid = knmidScPlayerDisconnect; + cb = sizeof(PlayerDisconnectNetMessage); + CompileAssert((sizeof(this) % 4) == 0); +} + +class PlayerResignNetMessage : public NetMessage { // prnm +public: + PlayerResignNetMessage(); + Pid pid; + +#ifdef LOGGING + void ToString(char *psz, int cb) { + snprintf(psz, cb, "CsPlayerResign(pid: %d)", pid); + } +#endif +}; + +inline PlayerResignNetMessage::PlayerResignNetMessage() +{ + nmid = knmidCsPlayerResign; + cb = sizeof(PlayerResignNetMessage); + CompileAssert((sizeof(this) % 4) == 0); +} + +#ifdef __CPU_68K // 68K byte order == our chosen network byte order +#define NetMessageByteOrderSwap(a, b) +#define MessageByteOrderSwap(a, b, c) +#define SwapGameParams(a) +#else +void NetMessageByteOrderSwap(NetMessage *pnm, bool fNative); +void MessageByteOrderSwap(word cmsgs, Message *pmsg, bool fNative); +void SwapGameParams(GameParams *prams); +#endif + +bool ValidateGameParams(const GameParams& params); + +extern long gatGameSpeeds[15]; + +} // namespace wi + +#endif // __NETMESSAGE_H__ diff --git a/mpshared/packfile.cpp b/mpshared/packfile.cpp new file mode 100644 index 0000000..4a1a89a --- /dev/null +++ b/mpshared/packfile.cpp @@ -0,0 +1,586 @@ +#include "base/md5.h" +#include "mpshared/packfile.h" +#include "mpshared/misc.h" +#include "base/log.h" +#include +#include +#include +#include + +namespace wi { + +//#define INCL_RECORDOPENS + +#if defined(INCL_RECORDOPENS) +void RecordOpen(char *pszApi, char *pszFile, dword dw) +{ + FILE *pfil = fopen("openlog.txt", "a+"); + fseek(pfil, 0, SEEK_END); + fprintf(pfil, "0x%08lx: %s %s\n", dw, pszApi, pszFile); + fclose(pfil); +} + +void RecordClose(dword dw) +{ + FILE *pfil = fopen("openlog.txt", "r+"); + char sz[128]; + while (true) { + long pos = ftell(pfil); + if (fgets(sz, sizeof(sz) - 1, pfil) == NULL) + break; + char szCompare[32]; + sprintf(szCompare, "0x%08lx", dw); + if (strncmp(sz, szCompare, 10) == 0) { + fseek(pfil, pos, SEEK_SET); + char ch = 'x'; + fwrite(&ch, 1, 1, pfil); + fclose(pfil); + return; + } + } + fclose(pfil); +} +#endif + +// +// Reads packed files +// + +PackFileReader::PackFileReader() +{ + m_crnfo = 0; + m_crnfoAlloc = 0; + m_arnfo = NULL; +} + +PackFileReader::~PackFileReader() +{ + while (m_crnfo != 0) + Pop(); + delete[] m_arnfo; +} + +File *PackFileReader::fopen(const char *pszFn, const char *pszMode) +{ + // Only support read binary mode + + if (strcmp(pszMode, "rb") != 0) + return NULL; + + // Find this directory entry + + ReaderInfo *prnfo; + DirEntry dir; + if (!FindDirEntry(pszFn, &dir, &prnfo)) { + LOG() << "can't find dir entry " << pszFn; + return NULL; + } + + // Alloc the file stream info for this file, fill it in + + File *pfil = (File *)new byte[sizeof(File) + sizeof(word) * + (dir.cRecs - 1)]; + if (pfil == NULL) + return NULL; + pfil->prnfo = prnfo; + pfil->offRecStart = 0; + pfil->offStream = 0; + pfil->nRecOffStream = 0; + pfil->cbTotal = 0; + pfil->cRecs = dir.cRecs; + pfil->nRecFirst = dir.nRecFirst; + + // Remember the size of each record + + word *pcb = pfil->acb; + word nRecFirst = BigWord(dir.nRecFirst); + for (word nRec = nRecFirst; nRec < nRecFirst + dir.cRecs; nRec++) { + word cbT; + prnfo->ppdbReader->GetRecordSize(nRec, &cbT); + pfil->cbTotal += cbT; + *pcb++ = cbT; + } + + // Remember we have one open + + prnfo->cOpen++; + +#if defined(INCL_RECORDOPENS) + RecordOpen("fopen", pszFn, (dword)pfil); +#endif + + return pfil; +} + +int PackFileReader::fclose(File *pfil) +{ +#if defined(INCL_RECORDOPENS) + RecordClose((dword)pfil); +#endif + + Assert(pfil->prnfo->cOpen > 0); + pfil->prnfo->cOpen--; + delete[] (byte *)pfil; + return 0; +} + +dword PackFileReader::fread(void *pv, dword cb, int c, File *pfil) +{ + dword cbT = cb * c; + byte *pb = (byte *)pv; + + word nRecFirst = BigWord(pfil->nRecFirst); + while (cbT != 0 && pfil->nRecOffStream < pfil->cRecs) { + dword offRecEnd = pfil->offRecStart + pfil->acb[pfil->nRecOffStream]; + word cbLeft = (word)(offRecEnd - pfil->offStream); + word cbReadNext = cbT < (dword)cbLeft ? (word)cbT : cbLeft; + pfil->prnfo->ppdbReader->ReadRecord(nRecFirst + pfil->nRecOffStream, (word)(pfil->offStream - pfil->offRecStart), cbReadNext, pb); + cbT -= cbReadNext; + pb += cbReadNext; + pfil->offStream += cbReadNext; + if (pfil->offStream == offRecEnd && pfil->nRecOffStream < pfil->cRecs) { + pfil->offRecStart = offRecEnd; + pfil->nRecOffStream++; + } + } + + // If it was all read, return the count + + if (cbT == 0) + return c; + + // It wasn't all read, return the count that was read. Note we may have a partial + // item read which according to c-runtime docs is ok. + + dword cbRead = cb * c - cbT; + return cbRead / cb; +} + +int PackFileReader::fseek(File *pfil, long delta, int nOrigin) +{ + dword offNew; + switch (nOrigin) { + case SEEK_CUR: + // Current file position + delta + + offNew = pfil->offStream + delta; + break; + + case SEEK_END: + // End of file + delta + + offNew = pfil->cbTotal + delta; + break; + + case SEEK_SET: + // Start of file + delta + + offNew = delta; + break; + + default: + // Error + + return 1; + } + + // Within file bounds? Error for now even though c-runtime allows this + + if ((long)offNew < 0 || offNew > pfil->cbTotal) + return 1; + + // Special supported case for seeking to the end + + if (offNew == pfil->cbTotal) { + if (pfil->cbTotal == 0) + return 0; + while (pfil->nRecOffStream < pfil->cRecs - 1) { + pfil->offRecStart += pfil->acb[pfil->nRecOffStream]; + pfil->nRecOffStream++; + } + pfil->offStream = offNew; + return 0; + } + + // Special case for seeking from the end backwards + + if (pfil->offStream == pfil->cbTotal) { + if (pfil->nRecOffStream == pfil->cRecs) { + pfil->nRecOffStream--; + pfil->offRecStart -= pfil->acb[pfil->nRecOffStream]; + } + } + + // Need to update File * members. + + if (offNew < pfil->offRecStart + pfil->acb[pfil->nRecOffStream]) { + // The new pos is in the current record or behind + + if (offNew < pfil->offRecStart) { + // The new pos is in a previous record + + while (true) { + pfil->nRecOffStream--; + Assert((char)pfil->nRecOffStream >= 0); + pfil->offRecStart -= pfil->acb[pfil->nRecOffStream]; + if (offNew >= pfil->offRecStart) { + pfil->offStream = offNew; + break; + } + } + } else { + // The new pos is in the current record + + pfil->offStream = offNew; + } + } else { + // The new pos is in a record ahead of the current record + + while (true) { + pfil->offRecStart += pfil->acb[pfil->nRecOffStream]; + pfil->nRecOffStream++; + Assert(pfil->nRecOffStream < pfil->cRecs); + if (offNew < pfil->offRecStart + pfil->acb[pfil->nRecOffStream]) { + pfil->offStream = offNew; + break; + } + } + } + + return 0; +} + +dword PackFileReader::ftell(File *pfil) +{ + return pfil->offStream; +} + +bool PackFileReader::EnumFiles(Enum *penm, int key, char *pszFn, int cb) +{ + if (penm->m_pvNext == (void *)kEnmFirst) { + if (m_crnfo == 0) { + return false; + } + ReaderInfo *prnfo = NULL; + if (key == PACKENUM_FIRST) { + prnfo = &m_arnfo[0]; + } + if (key == PACKENUM_LAST) { + prnfo = &m_arnfo[m_crnfo - 1]; + } + if (prnfo == NULL) { + return false; + } + penm->m_pvNext = (void *)prnfo; + penm->m_dwUser = 0; + } + ReaderInfo *prnfo = (ReaderInfo *)penm->m_pvNext; + if ((int)penm->m_dwUser < prnfo->cEntries) { + DirEntry *pdirT = &prnfo->pdir[penm->m_dwUser]; + strncpyz(pszFn, pdirT->szFn, cb); + penm->m_dwUser++; + return true; + } + return false; +} + +void *PackFileReader::MapFile(const char *pszFn, FileMap *pfmap, dword *pcb) +{ + memset(pfmap, 0, sizeof(*pfmap)); + + // Find the file + + ReaderInfo *prnfo; + DirEntry dir; + if (!FindDirEntry(pszFn, &dir, &prnfo)) + return NULL; + word nRecFirst = BigWord(dir.nRecFirst); + + // For now don't allow mapping of multi-record files. If this is needed + // later it can be done. + + byte *pb = NULL; + if (dir.cRecs > 1) { + File *pfil = this->fopen(pszFn, "rb"); + if (pfil == NULL) { + return NULL; + } + this->fseek(pfil, 0, SEEK_END); + dword cb = ftell(pfil); + this->fseek(pfil, 0, SEEK_SET); + pb = new byte[cb]; + if (pb == NULL) { + this->fclose(pfil); + return NULL; + } + if (this->fread(pb, cb, 1, pfil) != 1) { + this->fclose(pfil); + return NULL; + } + pfmap->prnfo = prnfo; + pfmap->dwCookie = 0; + pfmap->nRec = 0; + pfmap->pbAlloced = pb; + if (pcb != NULL) { + *pcb = cb; + } + this->fclose(pfil); + } else { + // See if pdbReader will map this entry + + word cbRec; + dword dwCookie; + pb = prnfo->ppdbReader->MapRecord(nRecFirst, &dwCookie, &cbRec); + if (pb == NULL) + return NULL; + + // All is ok. + + pfmap->prnfo = prnfo; + pfmap->nRec = nRecFirst; + pfmap->dwCookie = dwCookie; + pfmap->pbAlloced = NULL; + if (pcb != NULL) + *pcb = (dword)cbRec; + } + prnfo->cOpen++; + +#if defined(INCL_RECORDOPENS) + RecordOpen("MapFile", pszFn, (dword)pfmap); +#endif + + return pb; +} + +void PackFileReader::UnmapFile(FileMap *pfmap) +{ +#if defined(INCL_RECORDOPENS) + RecordClose((dword)pfmap); +#endif + + Assert(pfmap->prnfo->cOpen > 0); + pfmap->prnfo->cOpen--; + if (pfmap->pbAlloced != NULL) { + delete[] pfmap->pbAlloced; + } else { + pfmap->prnfo->ppdbReader->UnmapRecord(pfmap->nRec, pfmap->dwCookie); + } +} + +bool PackFileReader::HashFile(const char *pszFn, byte *hash) +{ + dword cb; + FileMap fmap; + byte *pb = (byte *)MapFile(pszFn, &fmap, &cb); + if (pb == NULL) { + return false; + } + MD5_CTX ctx; + MD5Init(&ctx); + MD5Update(&ctx, pb, cb); + MD5Final(hash, &ctx); + UnmapFile(&fmap); + return true; +} + +bool PackFileReader::GetPdbName(const char *pszFn, char *pszPdb, int cbPdb, + char *pszDir, int cbDir) +{ + ReaderInfo *prnfo; + DirEntry dir; + if (!FindDirEntry(pszFn, &dir, &prnfo)) + return false; + strncpyz(pszPdb, prnfo->pszFn, cbPdb); + if (pszDir != NULL) { + strncpyz(pszDir, prnfo->pszDir, cbDir); + } + return true; +} + +bool PackFileReader::FindDirEntry(const char *psz, DirEntry *pdir, + ReaderInfo **pprnfo) { + // Convert to lower case + + char szT[kcbFilename]; + + // Lower case apis differ platform to platform, so fudge it + + strcpy(szT, psz); + char *pszT = szT; + while (*pszT != 0) { + if (*pszT >= 'A' && *pszT <= 'Z') + (*pszT) += 'a' - 'A'; + pszT++; + } + + // The most recently pushed PdbReader has priority + + for (int n = m_crnfo - 1; n >= 0; n--) { + ReaderInfo *prnfo = &m_arnfo[n]; + + // Look through the directory entries for this file. The entries + // are sorted in ascending order, so use a binary search. + + int nMin = 0; + int nMax = prnfo->cEntries - 1; + while (true) { + if (nMax < nMin) + break; + int nCurrent = nMin + (nMax - nMin) / 2; + DirEntry *pdirT = &prnfo->pdir[nCurrent]; + + int n = strcmp(szT, pdirT->szFn); + if (n == 0) { + *pdir = *pdirT; + *pprnfo = prnfo; + return true; + } + if (n > 0) { + nMin = nCurrent + 1; + continue; + } + if (n < 0) { + nMax = nCurrent - 1; + continue; + } + } + } + + return false; +} + +#define kcrnfoAlloc 25 + +bool PackFileReader::Push(const char *pszDir, const char *pszFn, + PdbReader *ppdbReader) +{ + // Grow as necessary + + if (m_crnfo == m_crnfoAlloc) { + int crnfoAllocNew = m_crnfoAlloc + kcrnfoAlloc; + ReaderInfo *prnfo = new ReaderInfo[crnfoAllocNew]; + if (prnfo == NULL) + return false; + if (m_arnfo != NULL) { + memcpy(prnfo, m_arnfo, m_crnfo * sizeof(ReaderInfo)); + delete m_arnfo; + } + m_arnfo = prnfo; + m_crnfoAlloc = crnfoAllocNew; + } + + // First try to map the directory record + + word cb; + dword dwCookie; + DirEntry *pdir = (DirEntry *)ppdbReader->MapRecord(0, &dwCookie, &cb); + + if (pdir == NULL) + return false; + + // That worked. Add this pdb to the list + + ReaderInfo *prnfo = &m_arnfo[m_crnfo]; + m_crnfo++; + prnfo->ppdbReader = ppdbReader; + prnfo->dwCookie = dwCookie; + prnfo->pdir = pdir; + prnfo->cEntries = cb / sizeof(DirEntry); + prnfo->cOpen = 0; + + // If pszDir is NULL, split pszFn into directory and filename. + // prnfo->pszFn must just point to the basename. + if (pszDir == NULL) { + char *pszBasename = basename((char *)pszFn); + int cchBase = strlen(pszBasename); + int cchFn = strlen(pszFn); + prnfo->pszDir = new char[cchFn - cchBase + 1]; + strncpyz(prnfo->pszDir, pszFn, cchFn - cchBase + 1); + int cch = strlen(prnfo->pszDir); + if (cch >= 1 && prnfo->pszDir[cch - 1] == '/') { + prnfo->pszDir[cch - 1] = 0; + } + pszFn = pszBasename; + } else { + prnfo->pszDir = new char[strlen(pszDir) + 1]; + strcpy(prnfo->pszDir, pszDir); + } + + prnfo->pszFn = new char[strlen(pszFn) + 1]; + strcpy(prnfo->pszFn, pszFn); + + return true; +} + +bool PackFileReader::Pop() +{ + // if the sound data file is missing, we can encounter this case + + if (m_crnfo == 0) + return false; + RemoveReader(m_crnfo - 1); + return true; +} + +void PackFileReader::RemoveReader(int rnfo) +{ + ReaderInfo *prnfo = &m_arnfo[rnfo]; + Assert(prnfo->cOpen == 0); + Assert(m_crnfo != 0); + + // Free the directory record and close the pdb reader + + delete[] prnfo->pszDir; + delete[] prnfo->pszFn; + prnfo->ppdbReader->UnmapRecord(0, prnfo->dwCookie); + prnfo->ppdbReader->Close(); + delete prnfo->ppdbReader; + + // Move readers down + + memcpy(&m_arnfo[rnfo], &m_arnfo[rnfo + 1], + (m_crnfo - 1 - rnfo) * ELEMENTSIZE(m_arnfo)); + m_crnfo--; +} + +bool PackFileReader::Delete(const char *pszDir, const char *pszFn) +{ + // See if pszFn is pushed. If so is it in use? + // If pushed and in use, can't delete + + for (int n = 0; n < m_crnfo; n++) { + ReaderInfo *prnfo = &m_arnfo[n]; + if (pszDir != NULL) { + if (stricmp(pszDir, prnfo->pszDir) != 0) + continue; + } + if (stricmp(pszFn, prnfo->pszFn) != 0) + continue; + if (prnfo->cOpen != 0) + return false; + RemoveReader(n); + break; + } + + // Reader removed. Now the file needs to be deleted + + return DeletePdb(pszDir, pszFn); +} + +bool PackFileReader::Push(const char *pszDir, const char *pszFn) +{ + PdbReader *ppdbReader = OpenPdb(pszDir, pszFn); + if (ppdbReader == NULL) { + return false; + } + + if (!Push(pszDir, pszFn, ppdbReader)) { + ppdbReader->Close(); + delete ppdbReader; + return false; + } + + return true; +} + +} // namespace wi diff --git a/mpshared/packfile.h b/mpshared/packfile.h new file mode 100644 index 0000000..4e44907 --- /dev/null +++ b/mpshared/packfile.h @@ -0,0 +1,162 @@ +#ifndef __PACKFILE_H__ +#define __PACKFILE_H__ + +#include "inc/basictypes.h" +#include "mpshared/pdbreader.h" +#include "mpshared/enum.h" + +namespace wi { + +#if !defined(PIL) + +// Pdb structs + +#define dmDBNameLength 32 + +typedef unsigned char UInt8; +typedef unsigned short UInt16; +typedef unsigned long UInt32; +typedef unsigned long LocalID; +typedef char Char; + +struct RecordEntryType { + LocalID localChunkID; // local chunkID of a record + UInt8 attributes; // record attributes; + UInt8 uniqueID[3]; // unique ID of record; should + // not be 0 for a legal record. +}; + +struct RecordListType { + LocalID nextRecordListID; // local chunkID of next list + UInt16 numRecords; // number of records in this list + UInt16 firstEntry; // array of Record/Rsrc entries + // starts here +}; + +struct DatabaseHdrType { + char name[dmDBNameLength]; // 0 name of database + UInt16 attributes; // 32 database attributes + UInt16 version; // 34 version of database + UInt32 creationDate; // 36 creation date of database + UInt32 modificationDate; // 40 latest modification date + UInt32 lastBackupDate; // 44 latest backup date + UInt32 modificationNumber; // 48 modification number of database + LocalID appInfoID; // 52 application specific info + LocalID sortInfoID; // 56 app specific sorting info + UInt32 type; // 60 database type + UInt32 creator; // 64 database creator + UInt32 uniqueIDSeed; // 68 used to generate unique IDs. + // Note that only the low order + // 3 bytes of this is used (in + // RecordEntryType.uniqueID). + // We are keeping 4 bytes for + // alignment purposes. + RecordListType recordList; // 72 first record list + // 80 +}; +#endif // !defined(PIL) + +// PackFileReader. Exposes c-runtime prototype apis, calls PdbReader for data +// access to pdbs created with packpdb. + +#ifndef kcbFilename +#define kcbFilename 29 +#endif + +#if kcbFilename != 29 +#error kcbFilename must be 29 +#endif + +// DirEntry is fixed at 32 + +struct DirEntry // dir +{ + char szFn[kcbFilename]; + byte cRecs; + word nRecFirst; +}; + +struct ReaderInfo // rnfo +{ + PdbReader *ppdbReader; + DirEntry *pdir; + char *pszDir; + char *pszFn; + dword dwCookie; + int cEntries; + int cOpen; +}; + +// Passed to fopen, etc. + +struct File +{ + ReaderInfo *prnfo; + dword cbTotal; + dword offRecStart; + dword offStream; + byte nRecOffStream; + byte cRecs; + word nRecFirst; + word acb[1]; +}; + +// Used for MapFile / UnmapFile + +struct FileMap // fmap +{ + ReaderInfo *prnfo; + dword dwCookie; + dword nRec; + byte *pbAlloced; +}; + +#define PACKENUM_FIRST 0 +#define PACKENUM_LAST 1 + +#ifndef USE_PALM_UNIX_HEADERS +class PackFileReader // pakr +{ +public: + PackFileReader(); + ~PackFileReader(); + + File *fopen(const char *pszFn, const char *pszMode); + int fclose(File *pfil); + dword fread(void *pv, dword cb, int c, File *pfil); + int fseek(File *pfil, long off, int nOrigin); + dword ftell(File *pfil); + bool EnumFiles(Enum *penm, int key, char *pszFn, int cbFn); + void *MapFile(const char *pszFn, FileMap *pfmap, dword *pcb = NULL); + void UnmapFile(FileMap *pfmap); + bool HashFile(const char *pszFn, byte *hash); + bool GetPdbName(const char *pszFn, char *pszPdb, int cbPdb, + char *pszDir = NULL, int cbDir = 0); + + bool Push(const char *pszDir, const char *pszFn); + bool Pop(); + bool Delete(const char *pszDir, const char *pszFn); + +private: + virtual PdbReader *OpenPdb(const char *pszDir, const char *pszFn) = 0; + virtual bool DeletePdb(const char *pszDir, const char *pszFn) = 0; + + bool Push(const char *pszDir, const char *pszFn, PdbReader *ppdbReader); + bool FindDirEntry(const char *psz, DirEntry *pdir, ReaderInfo **pprnfo); + void RemoveReader(int rnfo); + + int m_crnfo; + int m_crnfoAlloc; + ReaderInfo *m_arnfo; +}; +#endif + +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#define SEEK_END 2 +#define SEEK_SET 0 +#endif + +} // namespace wi + +#endif // __PACKFILE_H__ diff --git a/mpshared/packinfomanager.cpp b/mpshared/packinfomanager.cpp new file mode 100644 index 0000000..7ae0cbc --- /dev/null +++ b/mpshared/packinfomanager.cpp @@ -0,0 +1,79 @@ +#include "mpshared/packinfomanager.h" +#include "mpshared/misc.h" +#include "base/format.h" +#include "yajl/wrapper/jsonbuilder.h" + +namespace wi { + +PackInfoManager::PackInfoManager(const char *cachedir) { + cachedir_ = cachedir; +} + +PackInfoManager::~PackInfoManager() { +} + +bool PackInfoManager::GetPackId(json::JsonMap *map, PackId *ppackid) { + const char *id = GetString(map, "id"); + if (id == NULL) { + return false; + } + const char *hash = GetString(map, "h"); + if (hash == NULL) { + return false; + } + if (!base::Format::FromHex(hash, ppackid->hash, sizeof(ppackid->hash))) { + return false; + } + if (!base::Format::ToDword(id, 10, &ppackid->id)) { + return false; + } + return true; +} + +const char *PackInfoManager::GetString(const json::JsonMap *map, + const char *key) { + const json::JsonObject *obj = map->GetObject(key); + if (obj != NULL && obj->type() == json::JSONTYPE_STRING) { + return ((const json::JsonString *)obj)->GetString(); + } + return NULL; +} + +json::JsonMap *PackInfoManager::GetInfoMap(const PackId *ppackid) { + FILE *file = fopen(GetInfoFilename(ppackid), "rb"); + if (file == NULL) { + return NULL; + } + json::JsonMap *map = LoadInfoMap(file); + fclose(file); + return map; +} + +json::JsonMap *PackInfoManager::LoadInfoMap(FILE *file) { + fseek(file, 0, SEEK_SET); + json::JsonBuilder builder; + builder.Start(NULL); + byte ab[256]; + while (true) { + size_t cb = fread(ab, 1, sizeof(ab), file); + if (cb == 0) { + break; + } + builder.Update((const char *)ab, cb); + } + json::JsonObject *obj = builder.End(); + if (obj == NULL || obj->type() != json::JSONTYPE_MAP) { + delete obj; + return false; + } + return (json::JsonMap *)obj; +} + +const char *PackInfoManager::GetInfoFilename(const PackId *ppackid) { + char szHash[33]; + strncpyz(szHash, base::Format::ToHex(ppackid->hash, 16), sizeof(szHash)); + return base::Format::ToString("%s/%08lx-%s", cachedir_.c_str(), + ppackid->id, szHash); +} + +} // namespace wi diff --git a/mpshared/packinfomanager.h b/mpshared/packinfomanager.h new file mode 100644 index 0000000..44396f7 --- /dev/null +++ b/mpshared/packinfomanager.h @@ -0,0 +1,29 @@ +#ifndef __PACKINFOMANAGER_H__ +#define __PACKINFOMANAGER_H__ + +#include "inc/basictypes.h" +#include "mpshared/mpht.h" +#include "yajl/wrapper/jsonbuilder.h" +#include + +namespace wi { + +class PackInfoManager { +public: + PackInfoManager(const char *cachedir); + ~PackInfoManager(); + + json::JsonMap *GetInfoMap(const PackId *ppackid); + +protected: + bool GetPackId(json::JsonMap *map, PackId *ppackid); + const char *GetString(const json::JsonMap *map, const char *key); + json::JsonMap *LoadInfoMap(FILE *file); + const char *GetInfoFilename(const PackId *ppackid); + + std::string cachedir_; +}; + +} // namespace wi + +#endif // __PACKINFOMANAGER_H__ diff --git a/mpshared/packmanager.cpp b/mpshared/packmanager.cpp new file mode 100644 index 0000000..5c46b28 --- /dev/null +++ b/mpshared/packmanager.cpp @@ -0,0 +1,207 @@ +#include "mpshared/packmanager.h" +#include "mpshared/indexloader.h" +#include "mpshared/mpht.h" +#include "mpshared/misc.h" +#include "inc/rip.h" +#include "base/md5.h" +#include "base/format.h" +#include +#include +#include +#include +#include + +namespace wi { + +PackManager::PackManager(const char *cachedir) { + cachedir_ = cachedir; +} + +PackManager::~PackManager() { +} + +bool PackManager::EnumPacks(Enum *penm, PackId *ppackid) { + if (penm->m_wUser == (word)kEnmFirst) { + penm->m_wUser = 0; + penm->m_pvNext = (void *)new PackMap::iterator(map_.begin()); + } + + PackMap::iterator *pit = (PackMap::iterator *)penm->m_pvNext; + if (*pit == map_.end()) { + delete pit; + return false; + } + *ppackid = (*pit)->second; + (*pit)++; + return true; +} + +int PackManager::IsInstalled(const PackId *ppackid, PackId *ppackidUpgrade) { + // This is temporary. Ideally, the "main" packs use the same + // versioning system. + + if (ppackid->id == PACKID_MAIN) { + // Exact pack found + return 1; + } + + // This needs to be fast, so a map is used. + PackMap::const_iterator it = map_.find(ppackid->id); + if (it == map_.end()) { + // Not found + return 0; + } + if (memcmp(ppackid, &it->second, sizeof(*ppackid)) == 0) { + // Exact pack found + return 1; + } + + // Pack found, but needs upgrade (hash mismatch) + if (ppackidUpgrade != NULL) { + *ppackidUpgrade = it->second; + } + return 2; +} + +bool PackManager::Remove(const PackId *ppackid, bool fCheckHash) { + int result = IsInstalled(ppackid); + if (result == 0) { + return false; + } + if (fCheckHash && result == 2) { + return false; + } + + PackMap::iterator it = map_.find(ppackid->id); + if (it == map_.end()) { + // It should always be found at this point + Assert(); + return false; + } + + // Remove the file. + if (unlink(GetPackFilepath(&it->second)) < 0) { + // This shouldn't fail + Assert(); + return false; + } + + map_.erase(it); + return true; +} + +bool PackManager::Mount(PackFileReader& pakr, const PackId *ppackid) { + if (ppackid->id == PACKID_MAIN) { + return true; + } + return pakr.Push(NULL, GetPackFilepath(ppackid)); +} + +bool PackManager::Unmount(PackFileReader& pakr, const PackId *ppackid) { + if (ppackid->id == PACKID_MAIN) { + return true; + } + return pakr.Pop(); +} + +void PackManager::InitFromInstalled() { + map_.clear(); + DIR *pdir = opendir(cachedir_.c_str()); + dirent *pdent; + while ((pdent = readdir(pdir)) != NULL) { + PackId packid; + if (!ParseFilename(pdent->d_name, &packid)) { + continue; + } + map_.insert(PackMap::value_type(packid.id, packid)); + } + closedir(pdir); +} + +const char *PackManager::GetPackFilename(const PackId *ppackid) { + char szHash[33]; + strncpyz(szHash, base::Format::ToHex(ppackid->hash, 16), sizeof(szHash)); + return base::Format::ToString("%08x-%s", ppackid->id, szHash); +} + +const char *PackManager::GetPackFilepath(const PackId *ppackid) { + char szFilename[8 + 1 + 32 + 1]; + strncpyz(szFilename, GetPackFilename(ppackid), sizeof(szFilename)); + return base::Format::ToString("%s/%s", cachedir_.c_str(), szFilename); +} + +bool PackManager::ParseFilename(const char *filename, PackId *ppackid) { + // id -hash + // iiiiiiii-hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh + if (strlen(filename) != 8 + 1 + 32) { + return false; + } + char szId[9]; + strncpyz(szId, filename, 9); + if (!base::Format::ToDword(szId, 16, &ppackid->id)) { + return false; + } + return base::Format::FromHex(filename + 8 + 1, ppackid->hash, 16); +} + +void PackManager::WatchIndex(const char *indexfile, int watch_interval_ticks) { + // Load on a separate thread, so the game thread doesn't have to do this + indexfile_ = indexfile; + + // Remember the last modification time of the file + struct stat s; + memset(&s, 0, sizeof(s)); + stat(indexfile_.c_str(), &s); + mtime_ = s.st_mtime; + + // Load the index a first time synchronously + map_ = *LoadIndex(); + + // Now update asynchronously + watch_interval_ticks_ = watch_interval_ticks; + watcher_.Start(this, &PackManager::WatcherStart); +} + +void PackManager::WatcherStart(void *pv) { + while (!watcher_.IsStopping()) { + // Only reload if the file has been modified + struct stat s; + memset(&s, 0, sizeof(s)); + stat(indexfile_.c_str(), &s); + if (s.st_mtime != mtime_) { + mtime_ = s.st_mtime; + PackMap *map = LoadIndex(); + if (map != NULL) { + thread_.Post(1, this, new WatcherData(map)); + } + } + watcher_.RunLoop(watch_interval_ticks_); + } +} + +void PackManager::OnMessage(base::Message *msg) { + // Executing on game thread. Accept new map. + if (msg->id == 1) { + RLOG() << "Importing new index..."; + WatcherData *data = (WatcherData *)msg->data; + map_ = *(data->map_); + delete data; + } +} + +std::map *PackManager::LoadIndex() { + // Load the index and return it as a map + IndexLoader index; + if (!index.InitFromFile(indexfile_.c_str())) { + return NULL; + } + PackMap *map = new PackMap; + int c = index.GetCount(); + for (int i = 0; i < c; i++) { + const IndexEntry *entry = index.GetEntry(i); + map->insert(PackMap::value_type(entry->packid.id, entry->packid)); + } + return map; +} + +} // namespace wi diff --git a/mpshared/packmanager.h b/mpshared/packmanager.h new file mode 100644 index 0000000..2dab802 --- /dev/null +++ b/mpshared/packmanager.h @@ -0,0 +1,60 @@ +#ifndef __PACKMANAGER_H__ +#define __PACKMANAGER_H__ + +#include "inc/basictypes.h" +#include "mpshared/enum.h" +#include "mpshared/mpht.h" +#include "mpshared/packfile.h" +#include "base/messagehandler.h" +#include "base/messagequeue.h" +#include "base/thread.h" +#include +#include +#include + +namespace wi { + +class PackManager : base::MessageHandler { // packm +public: + PackManager(const char *cachedir); + ~PackManager(); + + void InitFromInstalled(); + void WatchIndex(const char *indexfile, + int watch_interval_ticks = 100 * 60 * 5); + + bool EnumPacks(Enum *penm, PackId *ppackid); + int IsInstalled(const PackId *ppackid, PackId *ppackidUpgrade = NULL); + bool Remove(const PackId *ppackid, bool fCheckHash = true); + bool Mount(PackFileReader& pakr, const PackId *ppackid); + bool Unmount(PackFileReader& pakr, const PackId *ppackid); + static const char *GetPackFilename(const PackId *ppackid); + +protected: + const char *GetPackFilepath(const PackId *ppackid); + bool ParseFilename(const char *filename, PackId *ppackid); + void WatcherStart(void *pv); + std::map *LoadIndex(); + + // MessageHandler + void OnMessage(base::Message *msg); + + std::string cachedir_; + std::string indexfile_; + time_t mtime_; + + typedef std::map PackMap; + PackMap map_; + + struct WatcherData : base::MessageData { + WatcherData(PackMap *map) : map_(map) {} + ~WatcherData() { delete map_; } + PackMap *map_; + }; + base::Thread watcher_; + int watch_interval_ticks_; +}; + +} // namespace wi + +#endif // __PACKMANAGER_H__ diff --git a/mpshared/pdbreader.h b/mpshared/pdbreader.h new file mode 100644 index 0000000..fafc9ca --- /dev/null +++ b/mpshared/pdbreader.h @@ -0,0 +1,26 @@ +#ifndef __PDBREADER_H__ +#define __PDBREADER_H__ + +#include "inc/basictypes.h" + +namespace wi { + +// Used to read palm databases. They can be stored in various ways for +// example in the Palm database manager (includes Springboard modules), +// in the Palm DS card (streamed FAT filesystem), or in the Win32 +// filesystem (streamed or mapped). + +class PdbReader +{ +public: + virtual ~PdbReader() {} + virtual void Close() = 0; + virtual bool GetRecordSize(word nRec, word *pcb) = 0; + virtual bool ReadRecord(word nRec, word n, word cb, void *pv) = 0; + virtual byte *MapRecord(word nRec, dword *pdwCookie, word *pcb = NULL) = 0; + virtual void UnmapRecord(word nRec, dword dwCookie) = 0; +}; + +} // namespace wi + +#endif // __PDBREADER_H__ diff --git a/mpshared/side.h b/mpshared/side.h new file mode 100644 index 0000000..92688ce --- /dev/null +++ b/mpshared/side.h @@ -0,0 +1,56 @@ +#ifndef __SIDE_H__ +#define __SIDE_H__ + +// Sides + +#define ksideNeutral 0 +#define kside1 1 +#define kside2 2 +#define kside3 3 +#define kside4 4 +#define kcSides 5 + +#define knIntelligenceHuman 0 +#define knIntelligenceComputer 1 +#define knIntelligenceComputerOvermind 2 +#define knIntelligenceComputerNeutral 3 + +// NOTE: kut values can not be changed without breaking old levels + +#define kutNone -1 + +// Infantry Units (NOTE: this is the order they will appear in the build form) +#define kutShortRangeInfantry 0 +#define kutLongRangeInfantry 1 +#define kutTakeoverSpecialist 2 + +// Vehicle Units (NOTE: this is the order they will appear in the build form) +#define kutMachineGunVehicle 3 +#define kutLightTank 4 +#define kutRocketVehicle 5 +#define kutMediumTank 6 +#define kutGalaxMiner 7 +#define kutMobileHeadquarters 8 + +// Structures (NOTE: this is the order they will appear in the build form) +#define kutReactor 9 +#define kutProcessor 10 +#define kutWarehouse 11 +#define kutHumanResourceCenter 12 +#define kutVehicleTransportStation 13 +#define kutRadar 14 +#define kutResearchCenter 15 +#define kutHeadquarters 16 + +// Special structures that like to kill +#define kutMachineGunTower 17 +#define kutRocketTower 18 + +#define kutAndy 19 +#define kutArtillery 20 +#define kutReplicator 21 +#define kutFox 22 + +#define kutMax 23 + +#endif // __SIDE_H__ diff --git a/mpshared/xmsg.h b/mpshared/xmsg.h new file mode 100644 index 0000000..a1c8ca5 --- /dev/null +++ b/mpshared/xmsg.h @@ -0,0 +1,697 @@ +#ifndef __XMSG_H__ +#define __XMSG_H__ + +#include "inc/basictypes.h" +#include "inc/rip.h" +#include "base/bytebuffer.h" +#include "base/misc.h" +#include "base/log.h" +#include "mpshared/constants.h" +#include "mpshared/misc.h" +#include + +namespace wi { + +// byte count, id +const dword XMSGSIZE_SIZE = sizeof(word); +const dword XMSGSIZE_ID = sizeof(byte); +const dword XMSGSIZE_FIXED = XMSGSIZE_SIZE + XMSGSIZE_ID; + +struct XMsg +{ + XMsg(byte id) { + id_ = id; + } + virtual ~XMsg() {} + byte id_; + + static bool ExtractSize(base::ByteBuffer& bb, dword *pcb) { + if (bb.Length() < XMSGSIZE_SIZE) { + return false; + } + if (pcb != NULL) { + word cb; + memcpy(&cb, bb.Data(), sizeof(cb)); + *pcb = (dword)base::ByteBuffer::NetToHostWord(cb); + } + return true; + } + + static bool ExtractId(base::ByteBuffer& bb, dword *pid) { + if (bb.Length() < XMSGSIZE_SIZE + XMSGSIZE_ID) { + return false; + } + byte idT; + memcpy(&idT, bb.Data() + XMSGSIZE_SIZE, sizeof(idT)); + *pid = (dword)idT; + return true; + } + + static base::ByteBuffer *ToBuffer(dword id, dword cb) { + base::ByteBuffer *bb = new base::ByteBuffer(cb); + bb->WriteWord((word)cb); + bb->WriteByte((byte)id); + Assert(bb->Length() == cb); + if (bb->Length() != cb) { + delete bb; + return NULL; + } + return bb; + } + + static XMsg *FromBuffer(dword id, base::ByteBuffer& bb, dword cb) { + if (bb.Length() < XMSGSIZE_FIXED) { + return NULL; + } + dword cbSav = bb.Length(); + word w = 0; + bb.ReadWord(&w); + if (cb != (dword)w) { + Assert(); + return NULL; + } + byte b; + bb.ReadByte(&b); + if (id != (dword)b) { + return NULL; + } + Assert(cbSav - bb.Length() == XMSGSIZE_FIXED); + if (cbSav - bb.Length() != XMSGSIZE_FIXED) { + return NULL; + } + return new XMsg(id); + } + +#ifdef LOGGING + virtual std::string ToString() { + std::ostringstream ss; + ss << XMsgLabels.Find(id_); + return ss.str(); + } +#endif +}; + +const dword XMSGSIZE_ZERODWORD = XMSGSIZE_FIXED + 0 * sizeof(dword); + +template +struct XMsg0 : public XMsg +{ + XMsg0() : XMsg(ID) { + } + static base::ByteBuffer *ToBuffer() { + return XMsg::ToBuffer(ID, XMSGSIZE_ZERODWORD); + } + static XMsg0 *FromBuffer(base::ByteBuffer& bb, dword cb) { + return (XMsg0 *)XMsg::FromBuffer(ID, bb, cb); + } +}; + +const dword XMSGSIZE_ONEDWORD = XMSGSIZE_FIXED + 1 * sizeof(dword); + +template +struct XMsg1 : public XMsg +{ + XMsg1(dword dw0) : XMsg(ID) { + dw0_ = dw0; + } + dword dw0_; + +#ifdef LOGGING + virtual std::string ToString() { + std::ostringstream ss; + ss << XMsgLabels.Find(id_) << ", dw0: " << dw0_; + return ss.str(); + } +#endif + + static base::ByteBuffer *ToBuffer(dword dw0) { + base::ByteBuffer *bb = new base::ByteBuffer(XMSGSIZE_ONEDWORD); + bb->WriteWord(XMSGSIZE_ONEDWORD); + bb->WriteByte((byte)ID); + bb->WriteDword(dw0); + Assert(bb->Length() == XMSGSIZE_ONEDWORD); + if (bb->Length() != XMSGSIZE_ONEDWORD) { + delete bb; + return NULL; + } + return bb; + } + + static XMsg1 *FromBuffer(base::ByteBuffer& bb, dword cb) { + Assert(cb == XMSGSIZE_ONEDWORD); + if (cb != XMSGSIZE_ONEDWORD || bb.Length() < XMSGSIZE_ONEDWORD) { + return NULL; + } + word cbSav = bb.Length(); + + word w = 0; + bb.ReadWord(&w); + if (cb != (dword)w) { + Assert(); + return NULL; + } + byte b = 0; + bb.ReadByte(&b); + if (ID != (dword)b) { + return NULL; + } + dword dw; + if (!bb.ReadDword(&dw)) { + return NULL; + } + Assert(cbSav - bb.Length() == XMSGSIZE_ONEDWORD); + if (cbSav - bb.Length() != XMSGSIZE_ONEDWORD) { + return NULL; + } + return new XMsg1(dw); + } +}; + +const dword XMSGSIZE_TWODWORD = XMSGSIZE_FIXED + 2 * sizeof(dword); + +template +struct XMsg2 : public XMsg +{ + XMsg2(dword dw0, dword dw1) : XMsg(ID) { + dw0_ = dw0; + dw1_ = dw1; + } + dword dw0_; + dword dw1_; + +#ifdef LOGGING + virtual std::string ToString() { + std::ostringstream ss; + ss << XMsgLabels.Find(id_) << ", dw0: " << dw0_ << ", dw1: " << dw1_ + << std::endl; + return ss.str(); + } +#endif + + static base::ByteBuffer *ToBuffer(dword dw0, dword dw1) { + base::ByteBuffer *bb = new base::ByteBuffer(XMSGSIZE_TWODWORD); + bb->WriteWord(XMSGSIZE_TWODWORD); + bb->WriteByte((byte)ID); + bb->WriteDword(dw0); + bb->WriteDword(dw1); + Assert(bb->Length() == XMSGSIZE_TWODWORD); + if (bb->Length() != XMSGSIZE_TWODWORD) { + delete bb; + return NULL; + } + return bb; + } + + static XMsg2 *FromBuffer(base::ByteBuffer& bb, dword cb) { + Assert(cb == XMSGSIZE_TWODWORD); + if (cb != XMSGSIZE_TWODWORD || bb.Length() < XMSGSIZE_TWODWORD) { + return NULL; + } + word cbSav = bb.Length(); + + word w = 0; + bb.ReadWord(&w); + if (cb != (dword)w) { + Assert(); + return NULL; + } + byte b = 0; + bb.ReadByte(&b); + if (ID != (dword)b) { + return NULL; + } + dword dw0; + if (!bb.ReadDword(&dw0)) { + return NULL; + } + dword dw1; + if (!bb.ReadDword(&dw1)) { + return NULL; + } + Assert(cbSav - bb.Length() == XMSGSIZE_TWODWORD); + if (cbSav - bb.Length() != XMSGSIZE_TWODWORD) { + return NULL; + } + return new XMsg2(dw0, dw1); + } +}; + +const dword XMSGSIZE_THREEDWORD = XMSGSIZE_FIXED + 3 * sizeof(dword); + +template +struct XMsg3 : public XMsg +{ + XMsg3(dword dw0, dword dw1, dword dw2) : XMsg(ID) { + dw0_ = dw0; + dw1_ = dw1; + dw2_ = dw2; + } + dword dw0_; + dword dw1_; + dword dw2_; + +#ifdef LOGGING + virtual std::string ToString() { + std::ostringstream ss; + ss << XMsgLabels.Find(id_) << ", dw0: " << dw0_ << ", dw1: " << dw1_ + << ", dw2: " << dw2_ << std::endl; + return ss.str(); + } +#endif + + static base::ByteBuffer *ToBuffer(dword dw0, dword dw1, dword dw2) { + base::ByteBuffer *bb = new base::ByteBuffer(XMSGSIZE_THREEDWORD); + bb->WriteWord(XMSGSIZE_THREEDWORD); + bb->WriteByte((byte)ID); + bb->WriteDword(dw0); + bb->WriteDword(dw1); + bb->WriteDword(dw2); + Assert(bb->Length() == XMSGSIZE_THREEDWORD); + if (bb->Length() != XMSGSIZE_THREEDWORD) { + delete bb; + return NULL; + } + return bb; + } + + static XMsg3 *FromBuffer(base::ByteBuffer& bb, dword cb) { + Assert(cb == XMSGSIZE_THREEDWORD); + if (cb != XMSGSIZE_THREEDWORD || bb.Length() < XMSGSIZE_THREEDWORD) { + return NULL; + } + word cbSav = bb.Length(); + + word w = 0; + bb.ReadWord(&w); + if (cb != (dword)w) { + Assert(); + return NULL; + } + byte b = 0; + bb.ReadByte(&b); + if (ID != (dword)b) { + return NULL; + } + dword dw0; + if (!bb.ReadDword(&dw0)) { + return NULL; + } + dword dw1; + if (!bb.ReadDword(&dw1)) { + return NULL; + } + dword dw2; + if (!bb.ReadDword(&dw2)) { + return NULL; + } + Assert(cbSav - bb.Length() == XMSGSIZE_THREEDWORD); + if (cbSav - bb.Length() != XMSGSIZE_THREEDWORD) { + return NULL; + } + return new XMsg3(dw0, dw1, dw2); + } +}; + +const dword XMSGSIZE_STRING1_FIXED = XMSGSIZE_FIXED + sizeof(word); + +template +struct XMsgS1 : public XMsg +{ + XMsgS1(const char *s0) : XMsg(ID) { + strncpyz(s0_, s0, sizeof(s0_)); + } + char s0_[SIZE0]; + +#ifdef LOGGING + virtual std::string ToString() { + std::ostringstream ss; + ss << XMsgLabels.Find(id_) << ", s0: " << s0_ << std::endl; + return ss.str(); + } +#endif + + static base::ByteBuffer *ToBuffer(const char *s0) { + word cbS0 = strlen(s0) + 1; + char s0T[SIZE0]; + if (cbS0 > sizeof(s0T)) { + strncpyz(s0T, s0, sizeof(s0T)); + s0 = s0T; + cbS0 = sizeof(s0T); + } + dword cb = XMSGSIZE_STRING1_FIXED + cbS0; + base::ByteBuffer *bb = new base::ByteBuffer(cb); + bb->WriteWord(cb); + bb->WriteByte(ID); + bb->WriteWord(cbS0); + bb->WriteBytes((const byte *)s0, cbS0); + if (bb->Length() != cb) { + delete bb; + return NULL; + } + return bb; + } + + static XMsgS1 *FromBuffer(base::ByteBuffer& bb, dword cb) { + dword cbSav = bb.Length(); + word w; + if (!bb.ReadWord(&w)) { + return NULL; + } + if (cb != (dword)w || w < XMSGSIZE_STRING1_FIXED || + w > XMSGSIZE_STRING1_FIXED + SIZE0) { + Assert(); + return NULL; + } + if (w - sizeof(word) > bb.Length()) { + return NULL; + } + byte id = 0; + bb.ReadByte(&id); + if (id != ID) { + return NULL; + } + word cbS0 = 0; + bb.ReadWord(&cbS0); + if (cbS0 > SIZE0) { + Assert(); + return NULL; + } + char s0[SIZE0]; + bb.ReadBytes((byte *)s0, cbS0); + s0[cbS0 - 1] = 0; + if (cbSav - bb.Length() != w) { + Assert(); + return NULL; + } + + return new XMsgS1(s0); + } +}; + +const dword XMSGSIZE_STRING2_FIXED = XMSGSIZE_FIXED + sizeof(word) * 2; + +template +struct XMsgS2 : public XMsg +{ + XMsgS2(const char *s0, const char *s1) : XMsg(ID) { + strncpyz(s0_, s0, sizeof(s0_)); + strncpyz(s1_, s1, sizeof(s1_)); + } + char s0_[SIZE0]; + char s1_[SIZE1]; + +#ifdef LOGGING + virtual std::string ToString() { + std::ostringstream ss; + ss << XMsgLabels.Find(id_) << ", s0: " << s0_ << ", s1: " << s1_ << + std::endl; + return ss.str(); + } +#endif + + static base::ByteBuffer *ToBuffer(const char *s0, const char *s1) { + word cbS0 = strlen(s0) + 1; + char s0T[SIZE0]; + if (cbS0 > sizeof(s0T)) { + strncpyz(s0T, s0, sizeof(s0T)); + s0 = s0T; + cbS0 = sizeof(s0T); + } + word cbS1 = strlen(s1) + 1; + char s1T[SIZE1]; + if (cbS1 > sizeof(s1T)) { + strncpyz(s1T, s1, sizeof(s1T)); + s1 = s1T; + cbS1 = sizeof(s1T); + } + dword cb = XMSGSIZE_STRING2_FIXED + cbS0 + cbS1; + base::ByteBuffer *bb = new base::ByteBuffer(cb); + bb->WriteWord(cb); + bb->WriteByte(ID); + bb->WriteWord(cbS0); + bb->WriteBytes((const byte *)s0, cbS0); + bb->WriteWord(cbS1); + bb->WriteBytes((const byte *)s1, cbS1); + if (bb->Length() != cb) { + delete bb; + return NULL; + } + return bb; + } + + static XMsgS2 *FromBuffer(base::ByteBuffer& bb, + dword cb) { + dword cbSav = bb.Length(); + word w; + if (!bb.ReadWord(&w)) { + return NULL; + } + if (cb != (dword)w || w < XMSGSIZE_STRING2_FIXED || + w > XMSGSIZE_STRING2_FIXED + SIZE0 + SIZE1) { + Assert(); + return NULL; + } + if (w - sizeof(word) > bb.Length()) { + return NULL; + } + byte id = 0; + bb.ReadByte(&id); + if (id != ID) { + return NULL; + } + + word cbS0 = 0; + bb.ReadWord(&cbS0); + if (cbS0 > SIZE0) { + Assert(); + return NULL; + } + char s0[SIZE0]; + bb.ReadBytes((byte *)s0, cbS0); + s0[cbS0 - 1] = 0; + word cbS1 = 0; + bb.ReadWord(&cbS1); + if (cbS1 > SIZE1) { + Assert(); + return NULL; + } + char s1[SIZE1]; + bb.ReadBytes((byte *)s1, cbS1); + s1[cbS1 - 1] = 0; + if (cbSav - bb.Length() != w) { + Assert(); + return NULL; + } + + return new XMsgS2(s0, s1); + } +}; + +const dword XMSGSIZE_STRING3_FIXED = XMSGSIZE_FIXED + sizeof(word) * 3; + +template +struct XMsgS3 : public XMsg +{ + XMsgS3(const char *s0, const char *s1, const char *s2) : XMsg(ID) { + strncpyz(s0_, s0, sizeof(s0_)); + strncpyz(s1_, s1, sizeof(s1_)); + strncpyz(s2_, s2, sizeof(s2_)); + } + char s0_[SIZE0]; + char s1_[SIZE1]; + char s2_[SIZE2]; + +#ifdef LOGGING + virtual std::string ToString() { + std::ostringstream ss; + ss << XMsgLabels.Find(id_) << ", s0: " << s0_ << ", s1: " << s1_ << + ", s2: " << s2_ << std::endl; + return ss.str(); + } +#endif + + static base::ByteBuffer *ToBuffer(const char *s0, const char *s1, + const char *s2) { + word cbS0 = strlen(s0) + 1; + char s0T[SIZE0]; + if (cbS0 > sizeof(s0T)) { + strncpyz(s0T, s0, sizeof(s0T)); + s0 = s0T; + cbS0 = sizeof(s0T); + } + word cbS1 = strlen(s1) + 1; + char s1T[SIZE1]; + if (cbS1 > sizeof(s1T)) { + strncpyz(s1T, s1, sizeof(s1T)); + s1 = s1T; + cbS1 = sizeof(s1T); + } + word cbS2 = strlen(s2) + 1; + char s2T[SIZE2]; + if (cbS2 > sizeof(s2T)) { + strncpyz(s2T, s2, sizeof(s2T)); + s2 = s2T; + cbS2 = sizeof(s2T); + } + dword cb = XMSGSIZE_STRING3_FIXED + cbS0 + cbS1 + cbS2; + base::ByteBuffer *bb = new base::ByteBuffer(cb); + bb->WriteWord(cb); + bb->WriteByte(ID); + bb->WriteWord(cbS0); + bb->WriteBytes((const byte *)s0, cbS0); + bb->WriteWord(cbS1); + bb->WriteBytes((const byte *)s1, cbS1); + bb->WriteWord(cbS2); + bb->WriteBytes((const byte *)s2, cbS2); + if (bb->Length() != cb) { + delete bb; + return NULL; + } + return bb; + } + + static XMsgS3 *FromBuffer(base::ByteBuffer& bb, + dword cb) { + dword cbSav = bb.Length(); + word w; + if (!bb.ReadWord(&w)) { + return NULL; + } + if (cb != (dword)w || w < XMSGSIZE_STRING3_FIXED || + w > XMSGSIZE_STRING3_FIXED + SIZE0 + SIZE1 + SIZE2) { + Assert(); + return NULL; + } + if (w - sizeof(word) > bb.Length()) { + return NULL; + } + byte id = 0; + bb.ReadByte(&id); + if (id != ID) { + return NULL; + } + + word cbS0 = 0; + bb.ReadWord(&cbS0); + if (cbS0 > SIZE0) { + Assert(); + return NULL; + } + char s0[SIZE0]; + bb.ReadBytes((byte *)s0, cbS0); + s0[cbS0 - 1] = 0; + + word cbS1 = 0; + bb.ReadWord(&cbS1); + if (cbS1 > SIZE1) { + Assert(); + return NULL; + } + char s1[SIZE1]; + bb.ReadBytes((byte *)s1, cbS1); + s1[cbS1 - 1] = 0; + + word cbS2 = 0; + bb.ReadWord(&cbS2); + if (cbS2 > SIZE2) { + Assert(); + return NULL; + } + char s2[SIZE2]; + bb.ReadBytes((byte *)s2, cbS2); + s2[cbS2 - 1] = 0; + + if (cbSav - bb.Length() != w) { + Assert(); + return NULL; + } + + return new XMsgS3(s0, s1, s2); + } +}; + +const dword XMSGSIZE_DWORDSTRING_FIXED = XMSGSIZE_FIXED + sizeof(dword) + + sizeof(word); + +template +struct XMsgDS : public XMsg +{ + XMsgDS(dword dw0, const char *s0) : XMsg(ID) { + dw0_ = dw0; + strncpyz(s0_, s0, sizeof(s0_)); + } + dword dw0_; + char s0_[SIZE0]; + +#ifdef LOGGING + virtual std::string ToString() { + std::ostringstream ss; + ss << XMsgLabels.Find(id_) << ", dw0: " << dw0_ << ", s0: " << s0_ << + std::endl; + return ss.str(); + } +#endif + + static base::ByteBuffer *ToBuffer(dword dw0, const char *s0) { + word cbS0 = strlen(s0) + 1; + char s0T[SIZE0]; + if (cbS0 > sizeof(s0T)) { + strncpyz(s0T, s0, sizeof(s0T)); + s0 = s0T; + cbS0 = sizeof(s0T); + } + dword cb = XMSGSIZE_DWORDSTRING_FIXED + cbS0; + base::ByteBuffer *bb = new base::ByteBuffer(cb); + bb->WriteWord(cb); + bb->WriteByte(ID); + bb->WriteDword(dw0); + bb->WriteWord(cbS0); + bb->WriteBytes((const byte *)s0, cbS0); + if (bb->Length() != cb) { + delete bb; + return NULL; + } + return bb; + } + + static XMsgDS *FromBuffer(base::ByteBuffer& bb, dword cb) { + dword cbSav = bb.Length(); + word w; + if (!bb.ReadWord(&w)) { + return NULL; + } + if (cb != (dword)w || w < XMSGSIZE_DWORDSTRING_FIXED || + w > XMSGSIZE_DWORDSTRING_FIXED + SIZE0) { + Assert(); + return NULL; + } + if (w - sizeof(word) > bb.Length()) { + return NULL; + } + byte id = 0; + bb.ReadByte(&id); + if (id != ID) { + return NULL; + } + dword dw0; + bb.ReadDword(&dw0); + word cbS0 = 0; + bb.ReadWord(&cbS0); + if (cbS0 > SIZE0) { + Assert(); + return NULL; + } + char s0[SIZE0]; + bb.ReadBytes((byte *)s0, cbS0); + s0[cbS0 - 1] = 0; + if (cbSav - bb.Length() != w) { + Assert(); + return NULL; + } + return new XMsgDS(dw0, s0); + } +}; + +} // namespace wi + +#endif // __XMSG_H__ diff --git a/mpshared/xmsglog.cpp b/mpshared/xmsglog.cpp new file mode 100644 index 0000000..14b04b4 --- /dev/null +++ b/mpshared/xmsglog.cpp @@ -0,0 +1,145 @@ +#include "mpshared/xmsglog.h" +#include "mpshared/xpump.h" +#include "base/tick.h" +#include "base/log.h" +#include + +namespace wi { + +XMsgLog::XMsgLog(const std::string& filepath) { + file_ = NULL; + + file_ = fopen(filepath.c_str(), "w"); + if (file_ == NULL) { + return; + } + + time_t t = time(NULL); + struct tm *tm = localtime(&t); + + LogHeader header; + header.marker0 = 0xffffffff; + header.marker1 = 0xffffffff; + header.month = tm->tm_mon; + header.dayofmonth = tm->tm_mday; + header.hour = tm->tm_hour; + header.minute = tm->tm_min; + header.second = tm->tm_sec; + + size_t c = fwrite(&header, sizeof(header), 1, file_); + if (c != 1) { + Close(); + } +} + +void XMsgLog::Log(const base::ByteBuffer& bb, int cb, dword from, dword to) { + Log(bb, from, to, base::GetMillisecondCount(), cb); +} + + +void XMsgLog::Log(const base::ByteBuffer& bb, dword from, dword to, long64 ts, + int cb) { + if (file_ == NULL) { + return; + } + LogItem item; + item.marker = 0xeaeaeaea; + item.ts = ts; + item.from = from; + item.to = to; + + size_t c = fwrite(&item, sizeof(item), 1, file_); + if (c != 1) { + Close(); + return; + } + + if (cb == -1) { + cb = bb.Length(); + } + + c = fwrite(bb.Data(), cb, 1, file_); + if (c != 1) { + Close(); + return; + } +} + +void XMsgLog::Flush() { + if (file_ != NULL) { + fflush(file_); + } +} + +void XMsgLog::Close() { + if (file_ != NULL) { + fclose(file_); + file_ = NULL; + } +} + +bool XMsgLog::ReadHeader(FILE *file, LogHeader *plh) { + size_t c = fread(plh, sizeof(*plh), 1, file); + if (c != 1) { + LOG() << "error reading logfile"; + return false; + } + + if (plh->marker0 != 0xffffffff || plh->marker1 != 0xffffffff) { + LOG() << "something wrong with log header"; + fclose(file); + return false; + } + return true; +} + +XMsg *XMsgLog::GetNextXMsg(FILE *file, LogItem *pli) { + base::ByteBuffer *pbb = GetNextByteBuffer(file, pli); + if (pbb != NULL) { + XMsg *pxmsg = XPump::XMsgFromBuffer(*pbb, pbb->Length()); + delete pbb; + return pxmsg; + } + return NULL; +} + +base::ByteBuffer *XMsgLog::GetNextByteBuffer(FILE *file, LogItem *pli) { + size_t c = fread(pli, sizeof(*pli), 1, file); + if (c != 1) { + return NULL; + } + + dword cb; + c = fread(&cb, sizeof(cb), 1, file); + if (c != 1) { + LOG() << "size read error!"; + return NULL; + } + cb = base::ByteBuffer::NetToHostDword(cb); + base::ByteBuffer *pbb = new base::ByteBuffer(cb); + if (pbb == NULL) { + return NULL; + } + pbb->WriteDword(cb); + cb -= sizeof(cb); + + while (cb != 0) { + byte ab[4 * 1024]; + int cbRead = sizeof(ab); + if (cb < cbRead) { + cbRead = cb; + } + c = fread(ab, 1, cbRead, file); + if (c != cbRead) { + LOG() << "read error!"; + delete pbb; + return NULL; + } + pbb->WriteBytes(ab, c); + cb -= c; + } + + return pbb; +} + +} // namespace wi diff --git a/mpshared/xmsglog.h b/mpshared/xmsglog.h new file mode 100644 index 0000000..4091cfe --- /dev/null +++ b/mpshared/xmsglog.h @@ -0,0 +1,51 @@ +#ifndef __XMSGLOG_H__ +#define __XMSGLOG_H__ + +#include "inc/basictypes.h" +#include "base/bytebuffer.h" +#include "mpshared/xmsg.h" +#include +#include + +namespace wi { + +struct LogHeader { + dword marker0; + dword marker1; + dword month; + dword dayofmonth; + dword hour; + dword minute; + dword second; +}; + +struct LogItem { + dword marker; + dword from; + dword to; + long64 ts; +}; + +class XMsgLog +{ +public: + XMsgLog(const std::string& filepath); + ~XMsgLog() { Close(); } + + void Log(const base::ByteBuffer& bb, int cb, dword from, dword to); + void Log(const base::ByteBuffer& bb, dword from, dword to, long64 ts, + int cb = -1); + void Flush(); + void Close(); + + static bool ReadHeader(FILE *file, LogHeader *plh); + static XMsg *GetNextXMsg(FILE *file, LogItem *pli); + static base::ByteBuffer *GetNextByteBuffer(FILE *file, LogItem *pli); + +private: + FILE *file_; +}; + +} // namespace wi + +#endif // __XMSGLOG_H__ diff --git a/mpshared/xpump.cpp b/mpshared/xpump.cpp new file mode 100644 index 0000000..0d39bd5 --- /dev/null +++ b/mpshared/xpump.cpp @@ -0,0 +1,734 @@ +#include "mpshared/xpump.h" +#include "mpshared/messages.h" +#include "base/socketaddress.h" +#include "base/socket.h" +#include "base/log.h" +#include "inc/rip.h" + +namespace wi { + +//--------------------------------------------------------------------------- +// XPump implementation + +XPump::XPump() : sock_(NULL), notify_(NULL), pbbSendFirst_(NULL), log_(NULL), + notifycloseok_(false) { +} + +XPump::~XPump() { + // Free send buffers + base::ByteBuffer *pbbT = pbbSendFirst_; + while (pbbT != NULL) { + base::ByteBuffer *pbbNext = pbbT->pbbNext_; + delete pbbT; + pbbT = pbbNext; + } + + // Close socket + Close(); +} + +bool XPump::Attach(base::Socket *sock, XPumpNotify *notify, XMsgLog *log) { + LOG() << "XPump attached to " << sock->GetRemoteAddress().ToString(); + + // XPump will receive socket notifications. This puts the socket into + // async mode. + + log_ = log; + logid_ = 0; + notify_ = notify; + sock_ = sock; + sock_->SetNotify(this); + + return true; +} + +void XPump::Close() { + if (sock_ != NULL) { + sock_->Close(); + delete sock_; + sock_ = NULL; + } +} + +void XPump::NotifyCloseOk() { + // Notify when all data has been sent. This is needed for asynchronous + // closing. + notifycloseok_ = true; + if (pbbSendFirst_ == NULL) { + notify_->OnCloseOk(); + } +} + +bool XPump::Send(base::ByteBuffer *pbb) { + if (pbb == NULL) + return false; + +#ifdef LOGGING + base::ByteBuffer *pbbT = pbb->Clone(); + XMsg *pmsg = XMsgFromBuffer(*pbbT, pbbT->Length()); + LOG() << base::Log::Format("0x%08lx", notify_) << ": " << pmsg->ToString() + << ", " << pbb->Length() << " bytes"; + delete pmsg; + delete pbbT; +#endif + + if (log_ != NULL) { + log_->Log(*pbb, -1, logid_, (dword)notify_); + } + + base::ByteBuffer **ppbbSend = &pbbSendFirst_; + while ((*ppbbSend) != NULL) { + ppbbSend = &(*ppbbSend)->pbbNext_; + } + *ppbbSend = pbb; + pbb->pbbNext_ = NULL; + return ProcessWrite(); +} + +bool XPump::ProcessWrite() { + // Start sending bytes from queue + + while (pbbSendFirst_ != NULL) { + base::ByteBuffer *pbb = pbbSendFirst_; + int cb = sock_->Send(pbb->Data(), pbb->Length()); + if (cb < 0) { + if (sock_->IsBlocking()) { + return true; + } + notify_->OnError(sock_->GetError()); + return false; + } + //LOG() << "Sent " << cb << " bytes out of " << pbb->Length(); + if (cb != pbb->Length()) { + pbb->Shift(cb); + break; + } + pbbSendFirst_ = pbb->pbbNext_; + delete pbb; + } + if (notifycloseok_ && pbbSendFirst_ == NULL) { + notify_->OnCloseOk(); + } + return true; +} + +bool XPump::ProcessRead() { + // Read data out of the socket + while (true) { + static byte s_abT[4096]; + int cb = sock_->Recv(s_abT, sizeof(s_abT)); + if (cb < 0) { + if (sock_->IsBlocking()) { + return true; + } + notify_->OnError(sock_->GetError()); + return false; + } + //LOG() << "Read " << cb << " bytes."; + bbRead_.WriteBytes(s_abT, cb); + } +} + +bool XPump::Peek(dword *pcb) { + dword cbT; + if (pcb == NULL) { + pcb = &cbT; + } + return XMsg::ExtractSize(bbRead_, pcb) && bbRead_.Length() >= *pcb; +} + +bool XPump::Dispatch() { + dword cbMsg; + if (!Peek(&cbMsg)) { + return false; + } + + if (log_ != NULL) { + log_->Log(bbRead_, (int)cbMsg, (dword)notify_, logid_); + } + + // Instantiate and dispatch this message. + XMsg *pmsg = XMsgFromBuffer(bbRead_, cbMsg); + if (pmsg == NULL) { + notify_->OnError(-1); + return false; + } + +#ifdef LOGGING + LOG() << base::Log::Format("0x%08lx", notify_) << ": " + << pmsg->ToString() << ", " << cbMsg << " bytes."; +#endif + + DispatchXMsg(pmsg); + delete pmsg; + return Peek(); +} + +XMsg *XPump::XMsgFromBuffer(base::ByteBuffer& bb, dword cb) { + dword cbT; + if (!XMsg::ExtractSize(bb, &cbT)) { + return NULL; + } + Assert(cb == cbT); + if (cb != cbT) { + return NULL; + } + dword id; + if (!XMsg::ExtractId(bb, &id)) { + return NULL; + } + + switch (id) { + case XMSG_HANDSHAKE: + return XMsgHandshake::FromBuffer(bb, cb); + + case XMSG_HANDSHAKERESULT: + return XMsgHandshakeResult::FromBuffer(bb, cb); + + case XMSG_SHOWMESSAGE: + return XMsgShowMessage::FromBuffer(bb, cb); + + case XMSG_ECHO: + return XMsgEcho::FromBuffer(bb, cb); + + case XMSG_PROTOCOLERROR: + return XMsgProtocolError::FromBuffer(bb, cb); + + case XMSG_LOGIN: + return XMsgLogin::FromBuffer(bb, cb); + + case XMSG_LOGINRESULT: + return XMsgLoginResult::FromBuffer(bb, cb); + + case XMSG_SIGNOUT: + return XMsgSignOut::FromBuffer(bb, cb); + + case XMSG_SIGNOUTRESULT: + return XMsgSignOutResult::FromBuffer(bb, cb); + + case XMSG_LOBBYJOIN: + return XMsgLobbyJoin::FromBuffer(bb, cb); + + case XMSG_LOBBYJOINRESULT: + return XMsgLobbyJoinResult::FromBuffer(bb, cb); + + case XMSG_LOBBYCREATEROOM: + return XMsgLobbyCreateRoom::FromBuffer(bb, cb); + + case XMSG_LOBBYCREATEROOMRESULT: + return XMsgLobbyCreateRoomResult::FromBuffer(bb, cb); + + case XMSG_LOBBYCANJOINROOM: + return XMsgLobbyCanJoinRoom::FromBuffer(bb, cb); + + case XMSG_LOBBYCANJOINROOMRESULT: + return XMsgLobbyCanJoinRoomResult::FromBuffer(bb, cb); + + case XMSG_LOBBYLURKERCOUNT: + return XMsgLobbyLurkerCount::FromBuffer(bb, cb); + + case XMSG_LOBBYADDROOM: + return XMsgLobbyAddRoom::FromBuffer(bb, cb); + + case XMSG_LOBBYREMOVEROOM: + return XMsgLobbyRemoveRoom::FromBuffer(bb, cb); + + case XMSG_LOBBYUPDATEROOM: + return XMsgLobbyUpdateRoom::FromBuffer(bb, cb); + + case XMSG_LOBBYLEAVE: + return XMsgLobbyLeave::FromBuffer(bb, cb); + + case XMSG_LOBBYLEAVERESULT: + return XMsgLobbyLeaveResult::FromBuffer(bb, cb); + + case XMSG_ROOMJOIN: + return XMsgRoomJoin::FromBuffer(bb, cb); + + case XMSG_ROOMJOINRESULT: + return XMsgRoomJoinResult::FromBuffer(bb, cb); + + case XMSG_ROOMADDPLAYER: + return XMsgRoomAddPlayer::FromBuffer(bb, cb); + + case XMSG_ROOMREMOVEPLAYER: + return XMsgRoomRemovePlayer::FromBuffer(bb, cb); + + case XMSG_ROOMSENDCHAT: + return XMsgRoomSendChat::FromBuffer(bb, cb); + + case XMSG_ROOMRECEIVECHAT: + return XMsgRoomReceiveChat::FromBuffer(bb, cb); + + case XMSG_ROOMADDGAME: + return XMsgRoomAddGame::FromBuffer(bb, cb); + + case XMSG_ROOMREMOVEGAME: + return XMsgRoomRemoveGame::FromBuffer(bb, cb); + + case XMSG_ROOMGAMEINPROGRESS: + return XMsgRoomGameInProgress::FromBuffer(bb, cb); + + case XMSG_ROOMGAMEPLAYERNAMES: + return XMsgRoomGamePlayerNames::FromBuffer(bb, cb); + + case XMSG_ROOMSTATUSCOMPLETE: + return XMsgRoomStatusComplete::FromBuffer(bb, cb); + + case XMSG_ROOMCREATEGAME: + return XMsgRoomCreateGame::FromBuffer(bb, cb); + + case XMSG_ROOMCREATEGAMERESULT: + return XMsgRoomCreateGameResult::FromBuffer(bb, cb); + + case XMSG_ROOMCANJOINGAME: + return XMsgRoomCanJoinGame::FromBuffer(bb, cb); + + case XMSG_ROOMCANJOINGAMERESULT: + return XMsgRoomCanJoinGameResult::FromBuffer(bb, cb); + + case XMSG_ROOMLEAVE: + return XMsgRoomLeave::FromBuffer(bb, cb); + + case XMSG_ROOMLEAVERESULT: + return XMsgRoomLeaveResult::FromBuffer(bb, cb); + + case XMSG_GAMEJOIN: + return XMsgGameJoin::FromBuffer(bb, cb); + + case XMSG_GAMEJOINRESULT: + return XMsgGameJoinResult::FromBuffer(bb, cb); + + case XMSG_GAMESENDCHAT: + return XMsgGameSendChat::FromBuffer(bb, cb); + + case XMSG_GAMERECEIVECHAT: + return XMsgGameReceiveChat::FromBuffer(bb, cb); + + case XMSG_GAMENETMESSAGE: + return XMsgGameNetMessage::FromBuffer(bb, cb); + + case XMSG_GAMEUPDATEEMPTY: + return XMsgGameUpdateEmpty::FromBuffer(bb, cb); + + case XMSG_GAMEUPDATERESULT: + return XMsgGameUpdateResult::FromBuffer(bb, cb); + + case XMSG_GAMEKILLED: + return XMsgGameKilled::FromBuffer(bb, cb); + + case XMSG_GAMELEAVE: + return XMsgGameLeave::FromBuffer(bb, cb); + + case XMSG_GAMELEAVERESULT: + return XMsgGameLeaveResult::FromBuffer(bb, cb); + + default: + return NULL; + } +} + +void XPump::DispatchXMsg(XMsg *pmsg) { + switch (pmsg->id_) { + case XMSG_HANDSHAKE: + { + XMsgHandshake *pmsgT = (XMsgHandshake *)pmsg; + // clientid, protocolid + notify_->OnHandshake(pmsgT->dw0_, pmsgT->dw1_); + } + break; + + case XMSG_HANDSHAKERESULT: + { + XMsgHandshakeResult *pmsgT = (XMsgHandshakeResult *)pmsg; + // result, id_ + notify_->OnHandshakeResult(pmsgT->dw0_, pmsgT->dw1_); + } + break; + + case XMSG_SHOWMESSAGE: + { + XMsgShowMessage *pmsgT = (XMsgShowMessage *)pmsg; + notify_->OnShowMessage(pmsgT->message_, pmsgT->ipRedirect_, + pmsgT->disconnect_); + } + break; + + case XMSG_ECHO: + notify_->OnEcho(); + break; + + case XMSG_PROTOCOLERROR: + { + XMsgProtocolError *pmsgT = (XMsgProtocolError *)pmsg; + // error + notify_->OnProtocolError(pmsgT->dw0_); + } + break; + + case XMSG_LOGIN: + { + XMsgLogin *pmsgT = (XMsgLogin *)pmsg; + // username, token, did + notify_->OnLogin(pmsgT->s0_, pmsgT->s1_, pmsgT->s2_); + } + break; + + case XMSG_LOGINRESULT: + { + XMsgLoginResult *pmsgT = (XMsgLoginResult *)pmsg; + // result + notify_->OnLoginResult(pmsgT->dw0_); + } + break; + + case XMSG_SIGNOUT: + { + XMsgSignOut *pmsgT = (XMsgSignOut *)pmsg; + notify_->OnSignOut(); + } + break; + + case XMSG_SIGNOUTRESULT: + { + XMsgSignOutResult *pmsgT = (XMsgSignOutResult *)pmsg; + // result + notify_->OnSignOutResult(pmsgT->dw0_); + } + break; + + case XMSG_LOBBYJOIN: + { + XMsgLobbyJoin *pmsgT = (XMsgLobbyJoin *)pmsg; + notify_->OnLobbyJoin(); + } + break; + + case XMSG_LOBBYJOINRESULT: + { + XMsgLobbyJoinResult *pmsgT = (XMsgLobbyJoinResult *)pmsg; + // result + notify_->OnLobbyJoinResult(pmsgT->dw0_); + } + break; + + case XMSG_LOBBYCREATEROOM: + { + XMsgLobbyCreateRoom *pmsgT = (XMsgLobbyCreateRoom *)pmsg; + // username, password + notify_->OnLobbyCreateRoom(pmsgT->s0_, pmsgT->s1_); + } + break; + + case XMSG_LOBBYCREATEROOMRESULT: + { + XMsgLobbyCreateRoomResult *pmsgT = + (XMsgLobbyCreateRoomResult *)pmsg; + // result, roomid + notify_->OnLobbyCreateRoomResult(pmsgT->dw0_, pmsgT->dw1_); + } + break; + + case XMSG_LOBBYCANJOINROOM: + { + XMsgLobbyCanJoinRoom *pmsgT = (XMsgLobbyCanJoinRoom *)pmsg; + // roomid, password + notify_->OnLobbyCanJoinRoom(pmsgT->dw0_, pmsgT->s0_); + } + break; + + case XMSG_LOBBYCANJOINROOMRESULT: + { + XMsgLobbyCanJoinRoomResult *pmsgT = + (XMsgLobbyCanJoinRoomResult *)pmsg; + // result + notify_->OnLobbyCanJoinRoomResult(pmsgT->dw0_); + } + break; + + case XMSG_LOBBYLURKERCOUNT: + { + XMsgLobbyLurkerCount *pmsgT = (XMsgLobbyLurkerCount *)pmsg; + // result + notify_->OnLobbyLurkerCount(pmsgT->dw0_); + } + break; + + case XMSG_LOBBYADDROOM: + { + XMsgLobbyAddRoom *pmsgT = (XMsgLobbyAddRoom *)pmsg; + notify_->OnLobbyAddRoom(pmsgT->room_, pmsgT->roomid_, pmsgT->priv_, + pmsgT->cPlayers_, pmsgT->cGames_); + } + break; + + case XMSG_LOBBYREMOVEROOM: + { + XMsgLobbyRemoveRoom *pmsgT = (XMsgLobbyRemoveRoom *)pmsg; + // roomid + notify_->OnLobbyRemoveRoom(pmsgT->dw0_); + } + break; + + case XMSG_LOBBYUPDATEROOM: + { + XMsgLobbyUpdateRoom *pmsgT = (XMsgLobbyUpdateRoom *)pmsg; + // roomid, cplayers, cgames + notify_->OnLobbyUpdateRoom(pmsgT->dw0_, pmsgT->dw1_, pmsgT->dw2_); + } + break; + + case XMSG_LOBBYLEAVE: + { + notify_->OnLobbyLeave(); + } + break; + + case XMSG_LOBBYLEAVERESULT: + { + XMsgLobbyLeaveResult *pmsgT = (XMsgLobbyLeaveResult *)pmsg; + // result + notify_->OnLobbyLeaveResult(pmsgT->dw0_); + } + break; + + case XMSG_ROOMJOIN: + { + XMsgRoomJoin *pmsgT = (XMsgRoomJoin *)pmsg; + // roomid, password + notify_->OnRoomJoin(pmsgT->dw0_, pmsgT->s0_); + } + break; + + case XMSG_ROOMJOINRESULT: + { + XMsgRoomJoinResult *pmsgT = (XMsgRoomJoinResult *)pmsg; + // result + notify_->OnRoomJoinResult(pmsgT->dw0_); + } + break; + + case XMSG_ROOMADDPLAYER: + { + XMsgRoomAddPlayer *pmsgT = (XMsgRoomAddPlayer *)pmsg; + // player name + notify_->OnRoomAddPlayer(pmsgT->s0_); + } + break; + + case XMSG_ROOMREMOVEPLAYER: + { + XMsgRoomRemovePlayer *pmsgT = (XMsgRoomRemovePlayer *)pmsg; + // hint, player name + notify_->OnRoomRemovePlayer(pmsgT->dw0_, pmsgT->s0_); + } + break; + + case XMSG_ROOMSENDCHAT: + { + XMsgRoomSendChat *pmsgT = (XMsgRoomSendChat *)pmsg; + // chat + notify_->OnRoomSendChat(pmsgT->s0_); + } + break; + + case XMSG_ROOMRECEIVECHAT: + { + XMsgRoomReceiveChat *pmsgT = (XMsgRoomReceiveChat *)pmsg; + // player, chat + notify_->OnRoomReceiveChat(pmsgT->s0_, pmsgT->s1_); + } + break; + + case XMSG_ROOMADDGAME: + { + XMsgRoomAddGame *pmsgT = (XMsgRoomAddGame *)pmsg; + notify_->OnRoomAddGame(pmsgT->player_, pmsgT->gameid_, + pmsgT->params_, pmsgT->minplayers_, pmsgT->maxplayers_, + pmsgT->title_, pmsgT->ctotal_); + } + break; + + case XMSG_ROOMREMOVEGAME: + { + XMsgRoomRemoveGame *pmsgT = (XMsgRoomRemoveGame *)pmsg; + // gameid, ctotal + notify_->OnRoomRemoveGame(pmsgT->dw0_, pmsgT->dw1_); + } + break; + + case XMSG_ROOMGAMEINPROGRESS: + { + XMsgRoomGameInProgress *pmsgT = (XMsgRoomGameInProgress *)pmsg; + // gameid + notify_->OnRoomGameInProgress(pmsgT->dw0_); + } + break; + + case XMSG_ROOMGAMEPLAYERNAMES: + { + XMsgRoomGamePlayerNames *pmsgT = (XMsgRoomGamePlayerNames *)pmsg; + notify_->OnRoomGamePlayerNames(pmsgT->gameid_, pmsgT->cnames_, + pmsgT->anames_); + } + break; + + case XMSG_ROOMSTATUSCOMPLETE: + notify_->OnRoomStatusComplete(); + break; + + case XMSG_ROOMCREATEGAME: + { + XMsgRoomCreateGame *pmsgT = (XMsgRoomCreateGame *)pmsg; + notify_->OnRoomCreateGame(pmsgT->params_); + } + break; + + case XMSG_ROOMCREATEGAMERESULT: + { + XMsgRoomCreateGameResult *pmsgT = + (XMsgRoomCreateGameResult *)pmsg; + notify_->OnRoomCreateGameResult(pmsgT->gameid_, pmsgT->result_, + &pmsgT->packid_); + } + break; + + case XMSG_ROOMCANJOINGAME: + { + XMsgRoomCanJoinGame *pmsgT = (XMsgRoomCanJoinGame *)pmsg; + // gameid + notify_->OnRoomCanJoinGame(pmsgT->dw0_); + } + break; + + case XMSG_ROOMCANJOINGAMERESULT: + { + XMsgRoomCanJoinGameResult *pmsgT = + (XMsgRoomCanJoinGameResult *)pmsg; + // result + notify_->OnRoomCanJoinGameResult(pmsgT->dw0_); + } + break; + + case XMSG_ROOMLEAVE: + { + XMsgRoomLeave *pmsgT = (XMsgRoomLeave *)pmsg; + // hint + notify_->OnRoomLeave(pmsgT->dw0_); + } + break; + + case XMSG_ROOMLEAVERESULT: + notify_->OnRoomLeaveResult(); + break; + + case XMSG_GAMEJOIN: + { + XMsgGameJoin *pmsgT = (XMsgGameJoin *)pmsg; + // gameid, roomid + notify_->OnGameJoin(pmsgT->dw0_, pmsgT->dw1_); + } + break; + + case XMSG_GAMEJOINRESULT: + { + XMsgGameJoinResult *pmsgT = (XMsgGameJoinResult *)pmsg; + // result + notify_->OnGameJoinResult(pmsgT->dw0_); + } + break; + + case XMSG_GAMESENDCHAT: + { + XMsgGameSendChat *pmsgT = (XMsgGameSendChat *)pmsg; + // chat + notify_->OnGameSendChat(pmsgT->s0_); + } + break; + + case XMSG_GAMERECEIVECHAT: + { + XMsgGameReceiveChat *pmsgT = (XMsgGameReceiveChat *)pmsg; + // player, chat + notify_->OnGameReceiveChat(pmsgT->s0_, pmsgT->s1_); + } + break; + + case XMSG_GAMENETMESSAGE: + { + XMsgGameNetMessage *pmsgT = (XMsgGameNetMessage *)pmsg; + notify_->OnGameNetMessage(&pmsgT->pnm_); + } + break; + + case XMSG_GAMEKILLED: + { + XMsgGameKilled *pmsgT = (XMsgGameKilled *)pmsg; + // gameid + notify_->OnGameKilled(pmsgT->dw0_); + } + break; + + case XMSG_GAMELEAVE: + notify_->OnGameLeave(); + break; + + case XMSG_GAMELEAVERESULT: + { + XMsgGameLeaveResult *pmsgT = (XMsgGameLeaveResult *)pmsg; + // result + notify_->OnGameLeaveResult(pmsgT->dw0_); + } + break; + + default: + notify_->OnError(-1); + Assert(false); + break; + } +} + +void XPump::OnConnectEvent(base::Socket *sock) { + // This will never get called. + Assert(false); +} + +void XPump::OnReadEvent(base::Socket *sock) { + // Read the socket + Assert(sock == sock_); + ProcessRead(); + + // If there are messages, notify that there are messages. This allows the + // notifyee to do per-message procesing. + + if (Peek()) { + if (!notify_->OnMessages()) { + // OnMessages returning false means execute the default, + // which is to dispatch them all. + while (Dispatch()) { + ; + } + } + } +} + +void XPump::OnWriteEvent(base::Socket *sock) { + Assert(sock == sock_); + ProcessWrite(); +} + +void XPump::OnCloseEvent(base::Socket *sock) { + Assert(sock == sock_); + LOG() << "Socket closed from " << sock->GetRemoteAddress().ToString(); + sock_->SetNotify(NULL); + notify_->OnClose(sock_->GetError()); +} + +bool XPump::IsClosed() { + if (sock_ == NULL) { + return true; + } + return sock_->GetState() == base::Socket::CS_CLOSED; +} + +} // namespace wi diff --git a/mpshared/xpump.h b/mpshared/xpump.h new file mode 100644 index 0000000..77b6c3f --- /dev/null +++ b/mpshared/xpump.h @@ -0,0 +1,121 @@ +#ifndef __XPUMP_H__ +#define __XPUMP_H__ + +#include "mpshared/xmsg.h" +#include "mpshared/messages.h" +#include "mpshared/xmsglog.h" +#include "base/socket.h" +#include "inc/rip.h" + +namespace wi { + +class XMsg; + +class XPumpNotify +{ +public: + virtual void OnHandshake(dword clientid, dword protocolid) { Assert(); } + virtual void OnHandshakeResult(dword result, dword id) { Assert(); } + virtual void OnShowMessage(const char *message, dword ipRedirect, + bool disconnect) { Assert(); } + virtual void OnEcho() { Assert(); } + virtual void OnProtocolError(dword error) { Assert(); } + virtual void OnLogin(const char *username, const char *token, const char *did) { Assert(); } + virtual void OnLoginResult(dword loginResult) { Assert(); } + virtual void OnSignOut() { Assert(); } + virtual void OnSignOutResult(dword result) { Assert(); } + virtual void OnLobbyJoin() { Assert(); } + virtual void OnLobbyJoinResult(dword result) { Assert(); } + virtual void OnLobbyCreateRoom(const char *name, const char *password) + { Assert(); } + virtual void OnLobbyCreateRoomResult(dword result, dword roomid) + { Assert(); } + virtual void OnLobbyCanJoinRoom(dword roomid, const char *password) + { Assert(); } + virtual void OnLobbyCanJoinRoomResult(dword result) { Assert(); } + virtual void OnLobbyLurkerCount(dword count) { Assert(); } + virtual void OnLobbyAddRoom(const char *room, dword roomid, bool priv, + dword cPlayers, dword cGames) { Assert(); } + virtual void OnLobbyRemoveRoom(dword roomid) { Assert(); } + virtual void OnLobbyUpdateRoom(dword roomid, dword cPlayers, dword cGames) + { Assert(); } + virtual void OnLobbyLeave() { Assert(); } + virtual void OnLobbyLeaveResult(dword result) { Assert(); } + virtual void OnRoomJoin(dword roomid, const char *password) { Assert(); } + virtual void OnRoomJoinResult(dword result) { Assert(); } + virtual void OnRoomAddPlayer(const char *player) { Assert(); } + virtual void OnRoomRemovePlayer(dword hint, const char *player) + { Assert(); } + virtual void OnRoomSendChat(const char *chat) { Assert(); } + virtual void OnRoomReceiveChat(const char *player, const char *chat) + { Assert(); } + virtual void OnRoomAddGame(const char *player, dword gameid, + const GameParams& params, dword minplayers, dword maxplayers, + const char *title, dword ctotal) { Assert(); } + virtual void OnRoomRemoveGame(dword gameid, dword ctotal) { Assert(); } + virtual void OnRoomGameInProgress(dword gameid) { Assert(); } + virtual void OnRoomGamePlayerNames(dword gameid, dword cnames, + const PlayerName *anames) { Assert(); } + virtual void OnRoomStatusComplete() { Assert(); } + virtual void OnRoomCreateGame(const GameParams& params) { Assert(); } + virtual void OnRoomCreateGameResult(dword gameid, dword result, + const PackId *ppackid) { Assert(); } + virtual void OnRoomCanJoinGame(dword gameid) { Assert(); } + virtual void OnRoomCanJoinGameResult(dword result) { Assert(); } + virtual void OnRoomLeave(dword hint) { Assert(); } + virtual void OnRoomLeaveResult() { Assert(); } + virtual void OnGameJoin(dword gameid, dword roomid) { Assert(); } + virtual void OnGameJoinResult(dword gameid) { Assert(); } + virtual void OnGameSendChat(const char *chat) { Assert(); } + virtual void OnGameReceiveChat(const char *player, const char *chat) + { Assert(); } + virtual void OnGameNetMessage(NetMessage **ppnm) { Assert(); } + virtual void OnGameKilled(dword gameid) { Assert(); } + virtual void OnGameLeave() { Assert(); } + virtual void OnGameLeaveResult(dword result) { Assert(); } + virtual bool OnMessages() { return false; } + virtual void OnCloseOk() { return; } + virtual void OnError(int error) { Assert(); } + virtual void OnClose(int error) { Assert(); } +}; + +class XPump : base::SocketNotify { +public: + XPump(); + ~XPump(); + + bool Attach(base::Socket *socket, XPumpNotify *notify, XMsgLog *log = NULL); + void Close(); + bool Send(base::ByteBuffer *pbb); + bool Flush() { return ProcessWrite(); } + bool Dispatch(); + bool IsClosed(); + void NotifyCloseOk(); + void SetLogId(dword id) { logid_ = id; } + static XMsg *XMsgFromBuffer(base::ByteBuffer& bb, dword cb); + base::Socket *socket() { return sock_; } + +private: + bool Peek(dword *pcb = NULL); + bool ProcessRead(); + bool ProcessWrite(); + void DispatchXMsg(XMsg *pmsg); + + // SocketNotify + virtual void OnConnectEvent(base::Socket *socket); + virtual void OnReadEvent(base::Socket *socket); + virtual void OnWriteEvent(base::Socket *socket); + virtual void OnCloseEvent(base::Socket *socket); + + XMsgLog *log_; + dword logid_; + base::Socket *sock_; + XPumpNotify *notify_; + base::ByteBuffer *pbbSendFirst_; + base::ByteBuffer bbRead_; + bool notifycloseok_; +}; + +} // namespace wi + +#endif // __XPUMP_H__ diff --git a/packpdb2/.cvsignore b/packpdb2/.cvsignore new file mode 100644 index 0000000..0ca40fa --- /dev/null +++ b/packpdb2/.cvsignore @@ -0,0 +1,2 @@ +bin +SpiffLib diff --git a/packpdb2/Class1.cs b/packpdb2/Class1.cs new file mode 100644 index 0000000..a3e575e --- /dev/null +++ b/packpdb2/Class1.cs @@ -0,0 +1,210 @@ +using System; +using System.IO; +using System.Collections; +using SpiffLib; + +namespace packpdb2 { + class Class1 { + static int s_cbFilenameMax = 29; + + static void Usage() { + Console.WriteLine("Usage:"); + Console.WriteLine(" packpdb2 -p [filespec]"); + Console.WriteLine(" Pack [filespec] into with creator id ."); + Console.WriteLine(" Optional [filespec] defaults to .\\*.*"); + Console.WriteLine(""); + Console.WriteLine(" packpdb2 -u "); + Console.WriteLine(" Unpacks files from ."); + Console.WriteLine(""); + Console.WriteLine(" packpdb2 -v "); + Console.WriteLine(" View contents of ."); + Console.WriteLine(""); + Console.WriteLine(" packpdb2 -m ..."); + Console.WriteLine(" Merge a prc with one or more pdb files."); + Console.WriteLine(""); + } + + [STAThread] + static void Main(string[] args) { + bool fSuccess = false; + if (args.Length > 0) { + switch (args[0]) { + case "-p": + fSuccess = PackFiles(args); + break; + + case "-u": + fSuccess = UnpackFiles(args[1]); + break; + + case "-v": + fSuccess = ViewFiles(args[1]); + break; + + case "-m": + fSuccess = MergeFiles(args); + break; + } + } + if (!fSuccess) + Usage(); + } + + static bool ViewFiles(string strFilePdb) { + // Open pack pdb + + PdbPacker pdbp = new PdbPacker(strFilePdb); + + // For each file... + + for (int iFile = 0; iFile < pdbp.Count; iFile++) { + PdbPacker.File file = pdbp[iFile]; + int cbCompressed = file.GetCompressedSize(); + int cbUncompressed = (int)file.ab.Length; + int nPercentSavings = ((cbUncompressed - cbCompressed) * 100 + cbCompressed / 2) / cbCompressed; + Console.WriteLine(file.str + ", " + cbUncompressed + " bytes, " + cbCompressed + " bytes compressed, " + nPercentSavings + "% savings"); + } + + return true; + } + + static bool UnpackFiles(string strFilePdb) { + // Open pack pdb + + PdbPacker pdbp = new PdbPacker(strFilePdb); + + // For each file... + + for (int iFile = 0; iFile < pdbp.Count; iFile++) { + PdbPacker.File file = pdbp[iFile]; + BinaryWriter bwtr = new BinaryWriter(new FileStream(file.str, FileMode.Create, FileAccess.Write, FileShare.None)); + bwtr.Write(file.ab); + bwtr.Close(); + Console.WriteLine("Wrote " + file.str + ", " + file.ab.Length + " bytes."); + } + + return true; + } + + static bool PackFiles(string[] args) { + // Parse parameters, validate. + + if (args[1].Length > 4) { + Console.WriteLine("Creator id " + args[1] + " must be 4 chars or less."); + return false; + } + string strCreatorId = args[1]; + string strFullPathPdb = Path.GetFullPath(args[2]); + string strFilePdb = Path.GetFileName(strFullPathPdb); + string strDirPdb = Path.GetDirectoryName(strFullPathPdb); + if (strDirPdb == "") + strDirPdb = "."; + if (strFilePdb.Length > 31) { + Console.WriteLine("Pdb filename " + strFilePdb + " must be 31 characters or less."); + return false; + } + string strFileSpec; + if (args.Length == 3) { + strFileSpec = ".\\*.*"; + } else { + strFileSpec = args[3]; + } + + // Get list of files to add + + string strFileFileSpecAdd = Path.GetFileName(strFileSpec); + string strDirFileSpecAdd = Path.GetDirectoryName(strFileSpec); + if (strDirFileSpecAdd == "") + strDirFileSpecAdd = "."; + string[] astrFilesAdd = Directory.GetFiles(strDirFileSpecAdd, strFileFileSpecAdd); + + // File types not to compress + + ArrayList alsStrFilesNoCompress = new ArrayList(); + if (args.Length > 4) { + if (args[4] == "-nocompress") { + for (int i = 5; i < args.Length; i++) { + string[] astrFiles = Directory.GetFiles(strDirFileSpecAdd, args[i]); + foreach (string str in astrFiles) + alsStrFilesNoCompress.Add(Path.GetFullPath(str)); + } + } + } + + // Print status + + Console.Write("Packing files... "); + PdbPacker pdbp = new PdbPacker(); + foreach (string strFileAdd in astrFilesAdd) { + // Don't add the .pdb we're building + + string strFullPathAdd = Path.GetFullPath(strFileAdd); + if (strFullPathPdb.ToLower() == strFullPathAdd.ToLower()) + continue; + + // Get filename only, check length + + string strFile = Path.GetFileName(strFullPathAdd).ToLower(); + if (strFile.Length >= s_cbFilenameMax) { + Console.WriteLine("The file " + strFile + " is too long. Must be " + (s_cbFilenameMax - 1) + "chars max."); + return false; + } + + // Compress or not? + + bool fCompress = true; + foreach (string strFileNoCompress in alsStrFilesNoCompress) { + if (strFullPathAdd.ToLower() == strFileNoCompress.ToLower()) { + fCompress = false; + break; + } + } + + // Read the file + + Stream stm = new FileStream(strFullPathAdd, FileMode.Open, FileAccess.Read); + BinaryReader brdr = new BinaryReader(stm); + PdbPacker.File file = new PdbPacker.File(strFile, brdr.ReadBytes((int)brdr.BaseStream.Length), fCompress); + brdr.Close(); + pdbp.Add(file); + } + + // Save out + + pdbp.Save(strFullPathPdb, strCreatorId); + return true; + } + + static bool MergeFiles(string [] args) + { + if (args.Length < 4) { + Console.WriteLine("Must specify input prc, output prc and at least one pdb."); + return false; + } + + // Read input prc. + + PalmDatabase pdb = new PalmDatabase(); + pdb.Load(args[2]); + + // Read pdbs. + + PalmDatabase [] apdb = new PalmDatabase[args.Length - 3]; + + for (int iarg = 3; iarg < args.Length; iarg++) { + apdb[iarg - 3] = new PalmDatabase(); + apdb[iarg - 3].Load(args[iarg]); + } + + // Merge. + + PdbPacker.MergePdbs(pdb, apdb); + + // Write the output. + + pdb.Save(args[1]); + + return true; + } + } +} diff --git a/packpdb2/app.ico b/packpdb2/app.ico new file mode 100644 index 0000000..3a5525f Binary files /dev/null and b/packpdb2/app.ico differ diff --git a/packpdb2/assemblyinfo.cs b/packpdb2/assemblyinfo.cs new file mode 100644 index 0000000..9f89a32 --- /dev/null +++ b/packpdb2/assemblyinfo.cs @@ -0,0 +1,58 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.*")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] diff --git a/packpdb2/deploy.bat b/packpdb2/deploy.bat new file mode 100644 index 0000000..225b96a --- /dev/null +++ b/packpdb2/deploy.bat @@ -0,0 +1 @@ +copy bin\debug\packpdb2.exe ..\bin diff --git a/packpdb2/packpdb2.csproj b/packpdb2/packpdb2.csproj new file mode 100644 index 0000000..3a79797 --- /dev/null +++ b/packpdb2/packpdb2.csproj @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packpdb2/packpdb2.sln b/packpdb2/packpdb2.sln new file mode 100644 index 0000000..86c2e6d --- /dev/null +++ b/packpdb2/packpdb2.sln @@ -0,0 +1,21 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "packpdb2", "packpdb2.csproj", "{E6B8BCF4-4450-4A1C-A948-5A97D4C9E7DC}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Release = Release + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {E6B8BCF4-4450-4A1C-A948-5A97D4C9E7DC}.Debug.ActiveCfg = Debug|.NET + {E6B8BCF4-4450-4A1C-A948-5A97D4C9E7DC}.Debug.Build.0 = Debug|.NET + {E6B8BCF4-4450-4A1C-A948-5A97D4C9E7DC}.Release.ActiveCfg = Release|.NET + {E6B8BCF4-4450-4A1C-A948-5A97D4C9E7DC}.Release.Build.0 = Release|.NET + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/pal2act/.cvsignore b/pal2act/.cvsignore new file mode 100644 index 0000000..ba077a4 --- /dev/null +++ b/pal2act/.cvsignore @@ -0,0 +1 @@ +bin diff --git a/pal2act/AssemblyInfo.cs b/pal2act/AssemblyInfo.cs new file mode 100644 index 0000000..9f89a32 --- /dev/null +++ b/pal2act/AssemblyInfo.cs @@ -0,0 +1,58 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.*")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] diff --git a/pal2act/app.ico b/pal2act/app.ico new file mode 100644 index 0000000..3a5525f Binary files /dev/null and b/pal2act/app.ico differ diff --git a/pal2act/deploy.bat b/pal2act/deploy.bat new file mode 100644 index 0000000..1911dea --- /dev/null +++ b/pal2act/deploy.bat @@ -0,0 +1 @@ +copy bin\debug\pal2act.exe ..\bin diff --git a/pal2act/pal2act.cs b/pal2act/pal2act.cs new file mode 100644 index 0000000..da06c9f --- /dev/null +++ b/pal2act/pal2act.cs @@ -0,0 +1,37 @@ +using System; +using System.IO; +using SpiffLib; + +namespace pal2act +{ + /// + /// Summary description for App. + /// + class App + { + /// + /// The main entry point for the application. + /// + [STAThread] + static int Main(string[] astrArgs) + { + if (astrArgs.Length < 1) { + Console.WriteLine("pal2act usage:\npal2act [out.act]"); + return -1; + } + string strIn = astrArgs[0]; + + string strOut; + if (astrArgs.Length < 2) { + strOut = Path.ChangeExtension(strIn, ".act"); + } else { + strOut = astrArgs[1]; + } + + Palette pal = new Palette(strIn); + pal.SavePhotoshopAct(strOut); + + return 0; + } + } +} diff --git a/pal2act/pal2act.csproj b/pal2act/pal2act.csproj new file mode 100644 index 0000000..142e684 --- /dev/null +++ b/pal2act/pal2act.csproj @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pal2act/pal2act.sln b/pal2act/pal2act.sln new file mode 100644 index 0000000..e68ed23 --- /dev/null +++ b/pal2act/pal2act.sln @@ -0,0 +1,21 @@ +Microsoft Visual Studio Solution File, Format Version 7.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "pal2act", "pal2act.csproj", "{7E05C5A8-FCBA-42CC-A2B6-EE8DB9E02722}" +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + ConfigName.0 = Debug + ConfigName.1 = Release + EndGlobalSection + GlobalSection(ProjectDependencies) = postSolution + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {7E05C5A8-FCBA-42CC-A2B6-EE8DB9E02722}.Debug.ActiveCfg = Debug|.NET + {7E05C5A8-FCBA-42CC-A2B6-EE8DB9E02722}.Debug.Build.0 = Debug|.NET + {7E05C5A8-FCBA-42CC-A2B6-EE8DB9E02722}.Release.ActiveCfg = Release|.NET + {7E05C5A8-FCBA-42CC-A2B6-EE8DB9E02722}.Release.Build.0 = Release|.NET + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/palbin/App.ico b/palbin/App.ico new file mode 100644 index 0000000..3a5525f Binary files /dev/null and b/palbin/App.ico differ diff --git a/palbin/AssemblyInfo.cs b/palbin/AssemblyInfo.cs new file mode 100644 index 0000000..9f89a32 --- /dev/null +++ b/palbin/AssemblyInfo.cs @@ -0,0 +1,58 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.*")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] diff --git a/palbin/Class1.cs b/palbin/Class1.cs new file mode 100644 index 0000000..2478bdc --- /dev/null +++ b/palbin/Class1.cs @@ -0,0 +1,50 @@ +using System; +using System.IO; +using System.Drawing; +using SpiffLib; + +namespace palbin +{ + /// + /// Summary description for Class1. + /// + class Class1 { + /// + /// The main entry point for the application. + /// + [STAThread] + static int Main(string[] astrArgs) { + if (astrArgs.Length != 2) { + Usage(); + return 1; + } + + Palette pal; + try { + pal = new Palette(astrArgs[0]); + } catch (Exception ex) { + Console.WriteLine(ex); + return 1; + } + + BinaryWriter binw = new BinaryWriter(new FileStream(astrArgs[1], FileMode.Create)); + binw.Write((byte)((pal.Length & 0xff00) >> 8)); + binw.Write((byte)(pal.Length & 0xff)); + + for (int i = 0; i < pal.Length; i++) { + binw.Write(pal[i].R); + binw.Write(pal[i].G); + binw.Write(pal[i].B); + } + + binw.Close(); + + return 0; + } + + static void Usage() { + Console.WriteLine("Usage:"); + Console.WriteLine("palbin palette.palbin"); + } + } +} diff --git a/palbin/deploy.bat b/palbin/deploy.bat new file mode 100644 index 0000000..82155a9 --- /dev/null +++ b/palbin/deploy.bat @@ -0,0 +1 @@ +copy bin\debug\palbin.exe ..\bin diff --git a/palbin/palbin.csproj b/palbin/palbin.csproj new file mode 100644 index 0000000..26e04be --- /dev/null +++ b/palbin/palbin.csproj @@ -0,0 +1,58 @@ + + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {84152DF1-6B4E-4FD7-B152-C0506CFBAE49} + Exe + palbin + palbin + + + true + full + false + bin\Debug\ + prompt + 4 + false + -unsafe + + + none + false + bin\Release\ + prompt + 4 + false + -unsafe + + + + + + + + + misc.cs + + + palette.cs + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/palbin/palbin.sln b/palbin/palbin.sln new file mode 100644 index 0000000..df39dc5 --- /dev/null +++ b/palbin/palbin.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "palbin", "palbin.csproj", "{84152DF1-6B4E-4FD7-B152-C0506CFBAE49}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {84152DF1-6B4E-4FD7-B152-C0506CFBAE49}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {84152DF1-6B4E-4FD7-B152-C0506CFBAE49}.Debug|Any CPU.Build.0 = Debug|Any CPU + {84152DF1-6B4E-4FD7-B152-C0506CFBAE49}.Release|Any CPU.ActiveCfg = Release|Any CPU + {84152DF1-6B4E-4FD7-B152-C0506CFBAE49}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(MonoDevelopProperties) = preSolution + StartupItem = palbin.csproj + EndGlobalSection +EndGlobal diff --git a/paltool/.cvsignore b/paltool/.cvsignore new file mode 100644 index 0000000..ba077a4 --- /dev/null +++ b/paltool/.cvsignore @@ -0,0 +1 @@ +bin diff --git a/paltool/AssemblyInfo.cs b/paltool/AssemblyInfo.cs new file mode 100644 index 0000000..9f89a32 --- /dev/null +++ b/paltool/AssemblyInfo.cs @@ -0,0 +1,58 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.*")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] diff --git a/paltool/PalTool.cs b/paltool/PalTool.cs new file mode 100644 index 0000000..ada8b86 --- /dev/null +++ b/paltool/PalTool.cs @@ -0,0 +1,449 @@ +using System; +using System.IO; +using System.Drawing; +using System.Drawing.Imaging; +using SpiffLib; +using System.Collections; +using System.Collections.Specialized; + +// need limit argument (so fixed pal doesn't go above a certain limit. 128 on 8 bit, 16 on 4 +// need overall palette size argument (can use -p) + +namespace PalTool +{ + /// + /// Summary description for PalToolApp. + /// + class PalToolApp + { + static StringCollection gstrcFileNames = new StringCollection(); + static bool gfPrintHistogram = false; + static bool gf6bitRGB = false; + static string gstrOutputFileName = null; + static bool gfVerbose = false; + static bool gfEliminateShadowColor = false; + static bool gfEliminateTransparentColor = false; + static bool gfPrintColorUsers = false; + static bool gfAnalyse = false; + static int giclrInsert = -1; + static int gcPaletteEntries = 256; + static int gcColorEntries = 256; + static bool gfPhotoshopPad = false; + + struct BitmapColorInfo { + public string strFileName; + public Hashtable htColorCount; + } + + class ColorCounter : IComparable { + public int cclr; + public ArrayList alBitmaps = new ArrayList(); + + // IComparable implementation + + public int CompareTo(object ob) { + return cclr - ((ColorCounter)ob).cclr; + } + } + + static void AddFilesFromFile(string strFile) { + char[] achDelimiter = new char[1]; + achDelimiter[0] = ' '; + StreamReader sr = new StreamReader(strFile); + String strLine; + while ((strLine = sr.ReadLine()) != null) { + if (strLine.Trim() != "") { + string[] astrFiles = strLine.Split(achDelimiter); + for (int i = 0; i < astrFiles.Length; i++) { + AddFiles(astrFiles[i]); + } + } + } + } + + static void AddFiles(string strFileArg) { + string strDir = Path.GetDirectoryName(strFileArg); + if (strDir == "") + strDir = "."; + string[] astrFileNames = Directory.GetFiles(strDir, Path.GetFileName(strFileArg)); + + if (astrFileNames.Length == 0) { + gstrcFileNames.Add(strFileArg); + } else { + foreach (string strFileName in astrFileNames) { + if (strFileName.ToLower().EndsWith(".ani") || strFileName.ToLower().EndsWith(".amx")) { + string strT = Path.GetDirectoryName(strFileName) + @"\" + Path.GetFileNameWithoutExtension(strFileName) + @"\*.png"; + string[] astrT = Directory.GetFiles(Path.GetDirectoryName(strT), Path.GetFileName(strT)); + gstrcFileNames.AddRange(astrT); + } else { + gstrcFileNames.Add(strFileName); + } + } + } + } + + static unsafe int Main(string[] astrArgs) { + // Command-line argument processing + + if (astrArgs.Length == 0) { + PrintHelp(); + return 0; + } + + for (int i = 0; i < astrArgs.Length; i++) { + switch (astrArgs[i]) { + case "-?": + PrintHelp(); + return 0; + + case "-i": + giclrInsert = Int32.Parse(astrArgs[++i]); + break; + + case "-p": + gcPaletteEntries = Int32.Parse(astrArgs[++i]); + break; + + case "-c": + gcColorEntries = Int32.Parse(astrArgs[++i]); + break; + + case "-t": + gfEliminateTransparentColor = true; + break; + + case "-s": + gfEliminateShadowColor = true; + break; + + case "-v": + gfVerbose = true; + break; + + case "-u": + gfPrintColorUsers = true; + break; + + case "-6": + gf6bitRGB = true; + break; + + case "-h": + gfPrintHistogram = true; + break; + + case "-a": + gfAnalyse = true; + break; + + case "-n": + gfPhotoshopPad = true; + break; + + case "-f": + AddFilesFromFile(astrArgs[++i]); + break; + + case "-o": + if (i + 1 >= astrArgs.Length) { + Console.WriteLine("Error: -o command requires a filename argument"); + return -1; + } + gstrOutputFileName = astrArgs[++i]; + break; + + default: + if (astrArgs[i][0] == '-') { + Console.WriteLine("Error: invalid flag '{0}'", astrArgs[i]); + return -1; + } + + // Assume all 'unassociated' arguments are input filenames (potentially wildcarded) + + AddFiles(astrArgs[i]); + break; + } + } + + if (gstrcFileNames.Count == 0) { + Console.WriteLine("Error: no files specified"); + return -1; + } + + // Build a list of the colors used and count of uses for each bitmap + + ArrayList alstBitmapColorInfos = new ArrayList(); + + foreach (string strFileName in gstrcFileNames) { + + BitmapColorInfo bci = new BitmapColorInfo(); + bci.strFileName = strFileName; + bci.htColorCount = new Hashtable(); + + // Handle .PALs + + if (strFileName.ToLower().EndsWith(".pal")) { + Palette pal = new Palette(strFileName); + + int i = 0; + foreach (Color clr in pal.Colors) { + Color clrT = clr; + if (gf6bitRGB) + clrT = Color.FromArgb(clr.R & 0xfc, clr.G & 0xfc, clr.B & 0xfc); + + // This hack causes the .PAL colors to be sorted at the head of the + // combined palette while retaining the order they were found in the .PAL. + + if (!bci.htColorCount.Contains(clrT)) + bci.htColorCount[clrT] = (Int32.MaxValue / 2) - i++; + } + + // Handle everything else (bitmaps) + + } else { + Bitmap bm = null; + try { + bm = new Bitmap(strFileName); + } catch { + Console.WriteLine("Error: {0} is not a recognized bitmap or palette file", strFileName); + continue; + } + + // Prep to filter out the transparent color + + Color clrTransparent = Color.GhostWhite; + if (gfEliminateTransparentColor) + clrTransparent = bm.GetPixel(0, 0); + + // Prep to filter out the shadow color + + Color clrShadow = Color.GhostWhite; + if (gfEliminateShadowColor) + clrShadow = Color.FromArgb(156, 212, 248); + + // Keep a per-bitmap list of unique colors and how many times they're used + + Hashtable ht = bci.htColorCount; + + // Lock down bits for speed + + Rectangle rc = new Rectangle(0, 0, bm.Width, bm.Height); + BitmapData bmd = bm.LockBits(rc, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); + byte *pbBase = (byte *)bmd.Scan0.ToPointer(); + + for (int y = 0; y < bm.Height; y++) { + for (int x = 0; x < bm.Width; x++) { + byte *pb = pbBase + y * bmd.Stride + x * 3; + Color clr = Color.FromArgb(pb[2], pb[1], pb[0]); + if (gfEliminateTransparentColor && clr == clrTransparent) + continue; + + if (gfEliminateShadowColor && clr == clrShadow) + continue; + + if (gf6bitRGB) + clr = Color.FromArgb(clr.R & 0xfc, clr.G & 0xfc, clr.B & 0xfc); + + object obT = ht[clr]; + if (obT == null) + ht[clr] = 1; + else + ht[clr] = 1 + (int)obT; + } + } + bm.UnlockBits(bmd); + } + + if (gfVerbose) + Console.WriteLine("{0} uses {1} colors", strFileName, bci.htColorCount.Count); + + if (gfPrintHistogram && gfVerbose) { + foreach (DictionaryEntry de in bci.htColorCount) { + Color clr = (Color)de.Key; + Console.WriteLine("{0},{1},{2} : {3} occurances", clr.R, clr.G, clr.B, (int)de.Value); + } + Console.WriteLine(); + } + + alstBitmapColorInfos.Add(bci); + } + + if (alstBitmapColorInfos.Count == 0) { + Console.WriteLine("Error: no valid bitmap files to process, terminating"); + return -1; + } + + // Combine all the color tables and count data + + Hashtable htCombined = new Hashtable(); + + foreach (BitmapColorInfo bci in alstBitmapColorInfos) { + foreach (DictionaryEntry de in bci.htColorCount) { + Color clr = (Color)de.Key; + ColorCounter clrc = (ColorCounter)htCombined[clr]; + if (clrc == null) { + clrc = new ColorCounter(); + clrc.cclr = (int)de.Value; + htCombined[clr] = clrc; + } else { + int nAdd = (int)de.Value; + if (nAdd > Int32.MaxValue / 3) + clrc.cclr = (int)de.Value; + else if (clrc.cclr < Int32.MaxValue / 3) + clrc.cclr += nAdd; + } + clrc.alBitmaps.Add(bci.strFileName); + } + } + + int cclrCombined = htCombined.Count; + Console.WriteLine("Combined palette has {0} unique colors", cclrCombined); + + // Sort everything by # colors used + + ColorCounter[] aclrcSorted = new ColorCounter[cclrCombined]; +// int i = 0; +// foreach (ColorCounter clrc in htCombined.Values) +// acOccurancesSorted[i++] = clrc.cclr; + htCombined.Values.CopyTo(aclrcSorted, 0); + Color[] aclrSorted = new Color[cclrCombined]; + htCombined.Keys.CopyTo(aclrSorted, 0); + + Array.Sort(aclrcSorted, aclrSorted); + + // Reverse so most-used colors come first + // OPT: could do this inside the Sort above by specifying a custom IComparer + + Array.Reverse(aclrcSorted); + Array.Reverse(aclrSorted); + + if (gfPrintHistogram || gfPrintColorUsers) { + for (int i = 0; i < cclrCombined; i++) { + Color clr = aclrSorted[i]; + int cOccurances = aclrcSorted[i].cclr; + if (cOccurances >= Int32.MaxValue / 3) + Console.WriteLine("{0},{1},{2} : preloaded", clr.R, clr.G, clr.B); + else + Console.WriteLine("{0},{1},{2} : {3} occurances", clr.R, clr.G, clr.B, cOccurances); + + if (gfPrintColorUsers) { + foreach (string strFileName in aclrcSorted[i].alBitmaps) { + Console.WriteLine(" {0}", strFileName); + } + } + } + } + + // Print warning if # of unique colors is greater than the desired palette size + // Truncate to match the requested size since other tools depend on this + + if (cclrCombined > gcColorEntries) { + Console.WriteLine("Warning! {0} unique colors, {1} palette entries reserved. Truncating...", cclrCombined, gcColorEntries); + Color[] aclrSortedT = new Color[gcColorEntries]; + Array.Copy(aclrSorted, 0, aclrSortedT, 0, gcColorEntries); + aclrSorted = aclrSortedT; + ColorCounter[] aclrcSortedT = new ColorCounter[gcColorEntries]; + Array.Copy(aclrcSorted, 0, aclrcSortedT, 0, gcColorEntries); + aclrcSorted = aclrcSortedT; + cclrCombined = gcColorEntries; + } + + // Create the palette. Presorted colors start at 0. New colors start at giclrInsert. + // giclrInsert == -1 means new colors are simply appended to the presorted colors. + + Color[] aclrPalette = aclrSorted; + + if (giclrInsert != -1) { + // Init to transparent + + aclrPalette = new Color[gcColorEntries]; + for (int i = 0; i < aclrPalette.Length; i++) + aclrPalette[i] = Color.FromArgb(255, 0, 255); + + // Insert new colors appropriately + + int iclrBase = -1; + for (int i = 0; i < cclrCombined; i++) { + if (aclrcSorted[i].cclr >= Int32.MaxValue / 3) { + aclrPalette[i] = aclrSorted[i]; + continue; + } + if (iclrBase == -1) + iclrBase = i; + int iclrNew = giclrInsert + (i - iclrBase); + if (iclrNew < aclrPalette.Length) + aclrPalette[iclrNew] = aclrSorted[i]; + } + } + + // Write the output palette file, if requested + + if (gstrOutputFileName != null) { + Palette pal = new Palette(aclrPalette); + if (gfPhotoshopPad) + pal.Pad(gcPaletteEntries, pal[pal.Length - 1]); + else + pal.Pad(gcPaletteEntries, Color.FromArgb(255, 0, 255)); + pal.SaveJasc(gstrOutputFileName); + } + + if (gfAnalyse) { + Palette pal = new Palette(aclrPalette); + + // For each color find the nearest color in RGB space and print + // the pair as well as the distance between them. + + for (int iclrA = 0; iclrA < aclrPalette.Length; iclrA++) { + Color clrA = aclrPalette[iclrA]; + + // Find the entry, the long way + + int nLowest = 256 * 256 * 3; + int iLowest = 0; + for (int iclr = 0; iclr < aclrPalette.Length; iclr++) { + if (iclr == iclrA) + continue; + + Color clrPal = aclrPalette[iclr]; + int dR = clrPal.R - clrA.R; + int dG = clrPal.G - clrA.G; + int dB = clrPal.B - clrA.B; + int nD = dR * dR + dG * dG + dB * dB; + if (nD < nLowest) { + nLowest = nD; + iLowest = iclr; + } + } + + Color clrB = aclrPalette[iLowest]; + double n = Math.Sqrt(nLowest); + Console.WriteLine("{8:#.##}\t[{3}] {0},{1},{2} \t[{7}] {4},{5},{6}", + clrA.R, clrA.G, clrA.B, iclrA, clrB.R, clrB.G, clrB.B, iLowest, n); + } + } + + return 0; + } + + // + + static void PrintHelp() { + Console.WriteLine( + "PalTool usage:\n" + + "PalTool [-v] [-t] [-s] [-h] [-6] [-o ] file[s]\n" + + "-v: verbose\n" + + "-c : color entries that aren't padding\n" + + "-p : total palette entry count\n" + + "-i : color index to insert new colors, can be -1\n" + + "-t: remove transparent color(s) from combined palette\n" + + "-s: remove shadow color from combined palette\n" + + "-h: print histogram for combined palette\n" + + "-u: list which bitmaps use each color\n" + + "-6: reduce colors to 6-bit RGB before using\n" + + "-a: analyse the resulting palette and print the results\n" + + "-n: repeat last entry to pad out to total (good for Photoshop import)\n" + + "-o : output combined palette to filename\n" + + "files[s]: palette, bitmap, ani, and amx files to be processed. Wildcards allowed."); + } + } +} diff --git a/paltool/PalTool.csproj b/paltool/PalTool.csproj new file mode 100644 index 0000000..8080ee7 --- /dev/null +++ b/paltool/PalTool.csproj @@ -0,0 +1,55 @@ + + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {319BCFB0-BDA6-4402-86E3-B16AA464BD1F} + Exe + PalTool + PalTool + + + true + full + false + bin\Debug\ + prompt + 4 + false + -unsafe + + + none + false + bin\Release\ + prompt + 4 + false + -unsafe + + + + + + misc.cs + + + palette.cs + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/paltool/PalTool.sln b/paltool/PalTool.sln new file mode 100644 index 0000000..03dfcfd --- /dev/null +++ b/paltool/PalTool.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PalTool", "PalTool.csproj", "{319BCFB0-BDA6-4402-86E3-B16AA464BD1F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {319BCFB0-BDA6-4402-86E3-B16AA464BD1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {319BCFB0-BDA6-4402-86E3-B16AA464BD1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {319BCFB0-BDA6-4402-86E3-B16AA464BD1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {319BCFB0-BDA6-4402-86E3-B16AA464BD1F}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(MonoDevelopProperties) = preSolution + StartupItem = PalTool.csproj + EndGlobalSection +EndGlobal diff --git a/paltool/deploy.bat b/paltool/deploy.bat new file mode 100644 index 0000000..ef152b9 --- /dev/null +++ b/paltool/deploy.bat @@ -0,0 +1 @@ +copy bin\debug\paltool.exe ..\bin diff --git a/server/.gitignore b/server/.gitignore new file mode 100644 index 0000000..8f0b3b1 --- /dev/null +++ b/server/.gitignore @@ -0,0 +1,5 @@ +/testdata/ +/release/ +/debug/ +/log/ +log.txt diff --git a/server/Makefile b/server/Makefile new file mode 100644 index 0000000..a5c7eb9 --- /dev/null +++ b/server/Makefile @@ -0,0 +1,87 @@ +# Compiler command line +# Add warning check for implicit data type resolution loss, otherwise +# gcc is silent. +PRE_CC=g++ -m32 +UNAME := $(shell uname) +ifeq ($(UNAME), Darwin) +CC=$(PRE_CC) -Wshorten-64-to-32 +endif +ifeq ($(UNAME), Linux) +CC=$(PRE_CC) -Wconversion +endif + +LD=$(CC) +CPP_INCS=-I.. +OUTDIR= +BINARY=hts +LDFLAGS=-lpthread + +# Choose socketserver type + +ifeq ($(shell uname -s),Darwin) +SOCKETSERVER=selectserver +endif + +ifeq ($(shell uname -s),Linux) +SOCKETSERVER=epollserver +endif + +# Debug is default + +ifdef REL +CPP_FLAGS=-DRELEASE_LOGGING -g -Wno-write-strings +OUTDIR=release +OPTFLAGS=-O2 +else +CPP_FLAGS=-DDEBUG_LOGGING -DDEBUG -DDEV_BUILD -g -Wno-write-strings +OUTDIR=debug +OPTFLAGS=-O0 +endif + +ifdef DEV_BUILD +CPP_FLAGS=-DDEV_BUILD $(CPP_FLAGS) +endif + +CPPFLAGS=$(OPTFLAGS) $(CPP_FLAGS) $(CPP_INCS) -fsigned-char + +MAINFILES=main server endpoint room lobby game levelinfo levelinfocache \ +ncpackfile ncpdbreader playermgr player tokenauth statsposter httppost \ +serverinfoupdater chatlimiter filewatcher tracker badwords tokenbucket \ +logger secrets +BASEFILES=deletetracker socket socketaddress socketserver $(SOCKETSERVER) \ +bytebuffer eventer md5c messagequeue messagehandler tick format base64 thread +YAJLFILESCPP=jsonbuilder jsontypes +YAJLFILESC=yajl yajl_buf yajl_encode yajl_lex yajl_parser yajl_gen +MPSHAREDFILES=messages netmessage misc xpump ini packfile decompress \ +packmanager indexloader xmsglog +INCFILES=rip + +ALLFILES=$(INCFILES) $(MAINFILES) $(BASEFILES) $(MPSHAREDFILES) $(YAJLFILESC) \ +$(YAJLFILESCPP) + +all: $(OUTDIR)/$(BINARY) + +clean: + rm -f $(OUTDIR)/* + +$(OUTDIR)/$(BINARY): $(ALLFILES:%=$(OUTDIR)/%.o) + $(LD) $(LDFLAGS) -o $@ $^ $(MAPFLAGS) + +$(MAINFILES:%=$(OUTDIR)/%.o): $(MAINFILES:%=%.cpp) + $(CC) $(CPPFLAGS) -c $( Multiplayer. + +Please continue reading for how to deploy on a public server. + +How to run in production +------------------------ + +1. Create a Google App Engine application name here: + + https://appengine.google.com/ + + It doesn't matter what the name is, the user will never see it. + +2. Change the application: line in ../stats/app.yaml with your app name: + + application: + +3. Deploy this application to Google App Engine using the + GoogleAppEngineLauncher app that comes with the SDK. + +4. Modify ../game/serviceurls.cpp, and replace where you see + with your GAE app name. These service urls are now permanent. + +5. Rebuild the server on a Linux system + + $ cd server + $ mkdir release + $ make REL=1 clean all + +6. Copy start.sh, release/hts, and htdata832.pdb to a publicly reachable + Linux system + + $ scp start.sh you@your_server.com: + $ scp release/hts you@your_server.com: + $ scp ../game/htdata832.pdb you@your_server.com: + +7. Download a copy of the 5000+ single and multiplayer maps, and copy this + to your server as well + + $ wget http://www.warfareincorporated.com/~wicontent/wicontent.tar.gz + $ scp wicontent.tar.gz you@your_server.com: + +8. ssh to you@your_server.com and perform the following: + + $ + $ cd + $ tar xvf wicontent.tar.gz (probably need to be root to do this) + + (you'll now have a wi subdirectory in your apache document root) + +9. Back on your local system, modify ../game/serviceurls.cpp, kszIndexUrl, + kszPackInfoUrl, and kszPackUrl as follows: + +const char *kszIndexUrl = "http:///wi/index"; +const char *kszPackInfoUrl = "http:///wi/info"; +const char *kszPackUrl = "http:///wi/pack"; + + This tells the client how to download mission packs. It will also be needed + by the server. + +10. Rebuild your client with the changes that were made to serviceurls.cpp. + +11. Back on your_server.com machine, cd into the directory containing + start.sh, hts, and htdata832.pdb + +12. Edit start.sh: + + a. Change --listen_address to be the ip of your server and the port you + wish to use, in ip:port format. + b. Change --missionpack_dir to the directory that holds the + wicontent.tar.gz content that was untarred into the apache document + root. For example: --missionpack_dir /var/www/wi + c. Change --htdata to ./htdata832.pdb, since it is in the current directory. + d. Change --stats_address to .appsport.com:80, using the Google + App Engine name you choose previously. + e. Change --server_name to the server name you wish to show up in-game. + f. Change --server_location to the server location you want to show up + in-game. + g. Change --server_type to production. + h. --server_info_extra is for adding additional json to that shows up + in the /api/serverinfo response from the leaderboard. It is optional but + can be useful for deployment information such as instance ids, zones, + versions, etc. Note the client looks for sort_key to indicate how this + server will be sorted related to others in the server list. + +13. Go ahead and run start.sh to start the server. Ultimately this can be + done from a cronjob at system startup using the @reboot directive (just + run start.sh from the @reboot crontab directive). + +14. Wait 30 seconds for the game server to announce itself to the leaderboard. + Now start the game client (with the serviceurls.cpp changes), and select + Multiplayer. You will get an error if your client can't see the server. + +15. You can start as many servers as you want. The client will present a list + of all game servers to the user, who will then choose which server to + connect to. + +16. If this is all working, distribute your game client. + + +How to deploy a new game server +------------------------------- + +Occasionally you'll want to deploy a new game server (with changes perhaps), +yet you want to minimally disrupt game play of players on the existing server. +Here are steps to do that: + +1. Build the new server. Deploy it with a lower sort_key so it is first in + the client's server list. + +2. Go to the admin page of the leaderboard with your browser: + + https://.appspot.com/private/admin + +3. Select Drain / Undrain. Select the checkbox next to the old server you will + be stopping, select Drain. In approximately a minute, this will put the + server into a mode where it will stop accepting users. + +4. From the admin page, select Send Chat to Server. Select the checkbox next + to the old server, and type a message that the server will be shutting down + in 15 minutes, and all users should join the new server. Press submit. + Within 30 seconds, all users on that server will see this message. Send the + message every 2 minutes or so to remind users. + +5. After 15 minutes or so, stop the old server. + +This is the way to do it manually. There is a script that performs these steps +automatically, however if the player count less than 10, just stop the +old server without going through these steps. + +Operational overview +-------------------- + +When a user presses the multiplayer button, the client queries the leaderboard +for a list of current game servers (game servers publish information about +themselves to the leaderboard on a regular interval). This is an example of +what the leaderboard returns to the client for Warfare Incorporated: + +{ + "expires_utc": 1402207827, + "updated_utc": 1402207767, + "infos": [ + { + "status": "ok", + "expires_utc": 1402207827, + "player_count": 3, + "protocol": 21, + "name": "Edge of Tomorrow", + "zone": "us-east-1b", + "sort_key": 60, + "start_utc": 1346624757, + "instance": "i-ee916831", + "version": "wiuser-64", + "location": "USA", + "address": "50.18.179.243:15618", + "type": "production" + } + ] +} + +This lists just one server, although the leaderboard will report all game +servers that are reporting to it. If more than one is listed, the client +will bring up UI letting the user choose which game server to connect to. + +After selecting the server, the user will then be able to login with a +user name / password. To authenticate the user, the client sends the +credentials to the leaderboard over HTTPS and gets a token back. The client +then uses this token to login to the game server. The game server knows how +to check to know that the token is valid. + +From here, the user will be able to join existing rooms on this server, or +create new rooms, chat with other players, and play games with other users. +Once a game is complete, the game server posts the results to the leaderboard, +which saves them away, and adjusts player scores and stats as appropriate. + +Note you can see the serverinfo from your own leaderboard this way: + +$ curl http://.appspot.com/api/serverinfo + + +Mission Packs and other content +------------------------------- + +The server expects to find mission pack content (and other content) at the +location pointed to by --missionpack_dir. This is a directory tree with +a required layout: + + + info directory containing mission pack descriptions + pack directory containing mission packs themselves + index an index of the mission packs available for + download. + index-all an index of all the mission packs, including ones + removed from the public list. + modlist a list of the game server admins and moderators + badwords a description of the swear words to filter out of chat. + + +Mission Packs +------------- + +It's important that the client and the server see the same mission packs. +Warfare Incorporated supports user submitted missions packs submitted to +the forums. A cronjob pulls the mission packs out of the forums, checks them +for validity, and builds the new missionpack_dir content, and pushes these +changes out to all game servers. This happens every 15 minutes or so. Game +servers watch when the index file changes and reload. + +Game servers could use the content from warfareincorporated.com directly by +updating from this url: + +http://www.warfareincorporated.com/~wicontent/wicontent.tar.gz + +NOTE: This file is currently a static snapshot. It can be made to +dynamically update in the future. Contact scottlu. + + +Modlist +------- + +The game server supports administrator and moderator roles. Users that have +these roles have certain powers / abilities that regular users don't have. +This is present to help manage the community and game play environment for the +benefit of the game and players. Admins and mods can issue commands through +in-game or in-room chat. For an overview of commands, grant yourself +admin and/or mod powers, and type /help (commands to be documented). + +The structure of the modlist file is very simple: + +admin, +mod, + +List additional admins and mods as needed, 1 per line. + +Some of the commands available to admins aren't available to mods. The game +server will watch for this file to change every 5 minutes, and reload it. + + +Badwords +-------- + +Badwords is a description format for words that will get filtered. It isn't +perfect, but it can be made more perfect for anyone interested :). By default, +the server will filter words. Admins / mods can turn filtering off with the +/filter command. + + + + +Chat logging +------------ + +Chatting is logged into the log subdirectory of the game directory, using +a binary format. This can be useful to resolve disputes when mods / admins +aren't present, or when there is a dispute involving mods or admins. Only +the game administrator has access to this data. + + diff --git a/server/badwords.cpp b/server/badwords.cpp new file mode 100644 index 0000000..8d92614 --- /dev/null +++ b/server/badwords.cpp @@ -0,0 +1,598 @@ +#include "server/badwords.h" +#include "mpshared/misc.h" + +namespace boost { +template +T next(T x) { return ++x; } +template +T prev(T x) { return --x; } +} // namespace boost + +namespace wi { + +// Number of wildcard chars in an entry, for example s**t has 2. +const int kcWildcardsEntryMax = 2; + +BadWords::BadWords(const std::string& filename) : watcher_(filename), + on_(true) { + watcher_.SignalOnFileUpdated.connect(this, &BadWords::OnFileUpdated); +} + +std::map > BadWords::ParsePairs( + const std::string& s) { + // Example: @pairs/f:ph/o:a,e,i,u/o:y + std::vector parts = Split(s, '/'); + std::map > pairs; + if (parts.size() == 0) { + return pairs; + } + std::vector::const_iterator it = parts.begin(); + for (; it != parts.end(); it++) { + std::vector key_values = Split(*it, ':'); + if (key_values.size() != 2) { + continue; + } + // Get the values for this key. Include the key itself, since + // the value list will be enumerated for word permutation. + std::vector values = Split(key_values[1], ','); + values.push_back(key_values[0]); + + if (values.size() == 0) { + continue; + } + std::map >::iterator it = + pairs.find(key_values[0]); + if (it == pairs.end()) { + pairs.insert(std::map > + ::value_type(key_values[0], values)); + } else { + it->second.insert(it->second.end(), values.begin(), values.end()); + } + } + return pairs; +} + +void BadWords::OnFileUpdated(ThreadedFileWatcher *watcher) { + RLOG() << watcher_.filename() << " has changed!"; + FILE *f = fopen(watcher_.filename().c_str(), "r"); + if (f == NULL) { + return; + } + + std::map > pairs; + nodes_.clear(); + nodes_.push_back(MatchNode()); + bool standalone = false; + bool wildcards = true; + + char sz[256]; + while (fgets(sz, sizeof(sz), f) != NULL) { + int cch; + const char *start = StripWhitespace(sz, &cch); + if (cch == 0) { + continue; + } + std::string s(start, cch); + if (s[0] == '#') { + continue; + } + if (s.find("@pairs") == 0) { + pairs = ParsePairs(s); + continue; + } + if (s.find("@standalone") == 0) { + std::vector parts = Split(s.c_str(), '/'); + if (parts.size() == 2) { + if (parts[1].compare("true") == 0) { + standalone = true; + } + if (parts[1].compare("false") == 0) { + standalone = false; + } + } + continue; + } + if (s.find("@wildcards") == 0) { + std::vector parts = Split(s.c_str(), '/'); + if (parts.size() == 2) { + if (parts[1].compare("true") == 0) { + wildcards = true; + } + if (parts[1].compare("false") == 0) { + wildcards = false; + } + } + continue; + } + AddPermutations(s, pairs, standalone, wildcards); + } + fclose(f); +} + +void BadWords::AddPermutations(const std::string& word, + const std::map >& pairs, + bool standalone, bool wildcards) { + // Split the word into pieces. Remember the order of the split values. + std::vector parts; + std::vector *> values; + const std::vector *splitter_values; + const char *next = word.c_str(); + while (true) { + const char *last = next; + next = NULL; + const char *splitter = NULL; + std::map >::const_iterator it = + pairs.begin(); + for (; it != pairs.end(); it++) { + const char *pos = strstr(last, it->first.c_str()); + if (pos != NULL && (next == NULL || pos < next)) { + next = pos; + splitter = it->first.c_str(); + splitter_values = &it->second; + } + } + if (next == NULL) { + parts.push_back(last); + break; + } + parts.push_back(std::string(last, next - last)); + next += strlen(splitter); + values.push_back(splitter_values); + } + + // Perform a recusion based permutation of each value at each split point. + // Cycle through the split points right to left, so the left side + // is the inner loop. That way, right sides can re-use portions of + // the node graph that are the same. + if (values.size() != 0) { + Permute(parts[parts.size() - 1], parts, values, values.size() - 1, + standalone, wildcards); + } else { + AddWord(parts[0], standalone, wildcards); + } +} + +void BadWords::Permute(const std::string& suffix, + const std::vector& parts, + const std::vector *>& values, + int index, bool standalone, bool wildcards) { + for (int i = 0; i < values[index]->size(); i++) { + std::string word = parts[index] + (*values[index])[i] + suffix; + if (index > 0) { + Permute(word, parts, values, index - 1, standalone, wildcards); + } else { + AddWord(word, standalone, wildcards); + } + } +} + +void BadWords::AddWord(const std::string& word, bool standalone, + bool wildcards) { + // Add this word to the graph, and expand the graph as necessary. + int current_index = 0; + for (int i = 0; i < word.size(); i++) { + char ch = word[i]; + if (ch >= 'A' && ch <= 'Z') { + ch += 'a' - 'A'; + } + if (ch < 'a' || ch > 'z') { + continue; + } + int char_index = ch - 'a'; + dword *next_index = &nodes_[current_index].indexes[char_index]; + if (i == word.size() - 1) { + dword ff = kfMatMatch; + if (wildcards) { + ff |= kfMatWildcards; + } + if (standalone) { + ff |= kfMatStandalone; + } + *next_index |= ff; + continue; + } + if ((*next_index & kfMatIndex) != 0) { + current_index = (*next_index & kfMatIndex); + continue; + } + *next_index |= (nodes_.size() & kfMatIndex); + current_index = nodes_.size(); + nodes_.push_back(MatchNode()); + } + LOG() << "added: " << word << " total nodes:" << nodes_.size(); +} + +std::vector BadWords::Split(const std::string& s, char ch) { + std::vector parts; + const char *next = s.c_str(); + while (true) { + const char *last = next; + next = strchr(next, ch); + if (next == NULL) { + parts.push_back(std::string(last)); + return parts; + } + parts.push_back(std::string(last, next - last)); + next += 1; + } +} + +bool BadWords::IsStandalone(const char *input, int start, int end) { + if (start > 0) { + char ch = input[start - 1]; + if (ch >= 'A' && ch <= 'Z') { + ch += 'a' - 'A'; + } + if (ch >= 'a' && ch <= 'z') { + return false; + } + } + if (input[end] != 0 && input[end + 1] != 0) { + char ch = input[end + 1]; + if (ch >= 'A' && ch <= 'Z') { + ch += 'a' - 'A'; + } + if (ch >= 'a' && ch <= 'z') { + return false; + } + } + return true; +} + +bool BadWords::IsMatch(const char *input, const MatchEntry& entry) { + // Can't have more wildcard replacements than not + if (entry.wildcard_replacements >= entry.char_count) { + return false; + } + + // Check for standalone if asked + if (entry.standalone && !IsStandalone(input, entry.start, entry.end)) { + return false; + } + + // Don't allow spaces in matches that have wildcards of either type + // (filler or replacement) + if (entry.wildcard_count != 0 && entry.last_space >= entry.start && + entry.last_space <= entry.end) { + return false; + } + return true; +} + +const char *BadWords::Filter(const char *input, int *cch_back) { + // Filter only if turned on (on is the default). + if (cch_back != NULL) { + *cch_back = -1; + } + if (!on_ || nodes_.size() == 0) { + strncpyz(buffer_, input, sizeof(buffer_)); + return buffer_; + } + + // Traverse the match graph, looking for matches. Handle non-alpha + // wildcards and insertions, like f*k vs. fu*k. + + std::list matches; + std::list progress; + + const char *pch = input; + for (; *pch != 0; pch++) { + // Ignore whitespace, but track it since it will cancel some matches + char ch = *pch; + + // Treat newlines specially. This marks the boundary between + // entered chat. This is tracked. + if (ch == '\n') { + std::list::iterator it = progress.begin(); + for (; it != progress.end(); it++) { + it->has_newline = true; + it->char_index_last = -1; + } + continue; + } + + if (isspace(ch)) { + std::list::iterator it = progress.begin(); + while (it != progress.end()) { + it->last_space = pch - input; + if (it->match) { + it->char_index_last = -1; + } + it++; + } + continue; + } + + if (ch >= 'A' && ch <= 'Z') { + ch += 'a' - 'A'; + } + + // If it is a letter, match against the match graph. + if (ch >= 'a' && ch <= 'z') { + int char_index = ch - 'a'; + + // Examine in-progress matches. + std::list::iterator it = progress.begin(); + while (it != progress.end()) { + // Match a trailing repeated char. + MatchEntry& entry = *it; + if (entry.match) { + if (entry.char_index_last == char_index) { + entry.end = pch - input; + if (*(pch + 1) != 0) { + it++; + continue; + } + } + if (IsMatch(input, entry)) { + matches.push_back(Match(entry.start, entry.end)); + } + it = progress.erase(it); + continue; + } + entry.char_count++; + + // Not matched yet. Is it a match now? + dword *next_index = + &nodes_[entry.current_index].indexes[char_index]; + if (*next_index & kfMatMatch) { + if ((!entry.has_newline && entry.wildcard_replacements == 0) + || (*next_index & kfMatWildcards) != 0) { + // Add a new entry that is the match, ahead of the + // iterator, so it gets processed even if this is the + // last char. + MatchEntry new_entry = entry; + new_entry.end = pch - input; + new_entry.char_index_last = char_index; + new_entry.standalone = + (*next_index & kfMatStandalone) != 0; + new_entry.standalone |= (new_entry.last_space != -1); + new_entry.standalone |= + ((new_entry.wildcard_count) != 0); + new_entry.match = true; + progress.insert(boost::next(it), new_entry); + } + } + + // Follow this char_index to the next node, if there one + if (*next_index & kfMatIndex) { + entry.end = pch - input; + entry.current_index = (*next_index & kfMatIndex); + entry.char_index_last = char_index; + it++; + continue; + } + // No next node; allow the entry to persist if it is letter + // doubling. + if (entry.char_index_last == char_index) { + entry.end = pch - input; + entry.char_count--; + it++; + continue; + } + + // Remove the entry + it = progress.erase(it); + } + + // Start a new match, if there is one + dword node_index = nodes_[0].indexes[char_index] & kfMatIndex; + if (node_index != 0) { + MatchEntry entry(pch - input, pch - input, node_index, + char_index); + entry.char_count = 1; + progress.push_back(entry); + } + continue; + } + + // Not a-z, handle skip and insertion. Insertion is easy, just + // preserve existing MatchEntrys. Skips require wildcarding: + // clone existing MatchEntrys, and step them for each next node. + // Add new match entries for all first char matches. + + std::list::iterator it = progress.begin(); + int next_indexes[26]; + int index_count; + + // Start new wildcard match entries only if this is a whitespace + // boundary. Otherwise it is easy to cause an explosion of match + // entries by simply typing "***************". + + if (pch == input || isspace(*(pch - 1))) { + index_count = GetNextCharIndexes(0, next_indexes); + for (int i = 0; i < index_count; i++) { + // Push to the front so these entries aren't enumerated again + dword *next_index = &nodes_[0].indexes[next_indexes[i]]; + MatchEntry entry(pch - input, pch - input, + (*next_index & kfMatIndex), -1); + entry.wildcard_count = 1; + entry.wildcard_replacements = 1; + progress.push_front(entry); + } + } + + // Handle wildcarding in existing entries + while (it != progress.end()) { + // Don't span existing matched entries over wildcard chars. + MatchEntry& entry = *it; + if (entry.match) { + if (IsMatch(input, entry)) { + matches.push_back(Match(entry.start, entry.end)); + } + it = progress.erase(it); + continue; + } + entry.wildcard_count++; + + // Enumerate next nodes, and make new stepped MatchEntrys for each. + index_count = GetNextCharIndexes(entry.current_index, next_indexes); + for (int i = 0; i < index_count; i++) { + dword *next_index = + &nodes_[entry.current_index].indexes[next_indexes[i]]; + // Only match if there is a trailing whitespace boundary + if (*(pch + 1) == 0 || isspace(*(pch + 1))) { + if ((*next_index & (kfMatMatch | kfMatWildcards)) == + (kfMatMatch | kfMatWildcards)) { + // And there is more string to be evaled + if (*(pch + 1) != 0) { + // Add a floating MatchEntry, in order to suck up + // repeated chars of the match. + MatchEntry new_entry = entry; + new_entry.end = pch - input; + + // Standalone, because since there is a wildcard, + // the entry is by definition standalone only. + new_entry.standalone = true; + new_entry.match = true; + new_entry.wildcard_replacements++; + progress.insert(it, new_entry); + } else { + // There is a wildcard, so by definition, it is + // standalone only. + MatchEntry new_entry = entry; + new_entry.standalone = true; + new_entry.end = pch - input; + new_entry.wildcard_replacements++; + if (IsMatch(input, new_entry)) { + matches.push_back(Match(new_entry.start, + new_entry.end)); + } + } + } + } + + // If there is a child at this char (already know there is) + if (*next_index & kfMatIndex) { + // And there has been kcWildcardsEntryMax wildcards only + if (entry.wildcard_count <= kcWildcardsEntryMax) { + // Then add a new match entry to track this path. + MatchEntry new_entry = entry; + new_entry.current_index = (*next_index & kfMatIndex); + new_entry.end = pch - input; + new_entry.wildcard_replacements++; + progress.insert(it, new_entry); + } + } + } + it++; + } + } + + if (matches.size() > 1) { + // See if the matches overlap; if so clean them up. + bool overlap = false; + std::list::iterator it = matches.begin(); + for (; it != matches.end(); it++) { + std::list::iterator it_next = boost::next(it); + if (it_next != matches.end()) { + if ((*it).end >= (*it_next).start) { + overlap = true; + break; + } + } + } + if (overlap) { + CleanupMatches(matches); + } + } + + // Find the earliest starting entry that spans the end. + // If this overlaps a match, start from the match. + if (cch_back != NULL) { + int earliest = -1; + std::list::iterator it = progress.begin(); + for (; it != progress.end(); it++) { + if (earliest == -1 || (*it).start < earliest) { + earliest = (*it).start; + } + } + if (earliest != -1) { + std::list::iterator itm = matches.begin(); + for (; itm != matches.end(); itm++) { + if (itm->end >= earliest) { + earliest = itm->start; + } + } + *cch_back = (pch - input) - earliest; + } + } + + // Any matches? If not, return the original line + if (matches.size() == 0) { + strncpyz(buffer_, input, sizeof(buffer_)); + return buffer_; + } + + // Finally, build the new string. + return BuildResult(input, matches); +} + +int BadWords::GetNextCharIndexes(int current_index, int *next_indexes) { + int count = 0; + for (int i = 0; i < 26; i++) { + if (nodes_[current_index].indexes[i] != 0) { + *next_indexes++ = i; + count++; + } + } + return count; +} + +const char *BadWords::BuildResult(const char *input, + std::list& matches) { + std::ostringstream s; + const char *in = input; + int cch = strlen(input); + std::list::iterator it = matches.begin(); + for (; it != matches.end(); it++) { + const Match& match = *it; + s << std::string(in, match.start - (in - input)); + s << std::string(match.end - match.start + 1, '*'); + in = &input[match.end + 1]; + } + s << in; + strncpyz(buffer_, s.str().c_str(), sizeof(buffer_)); + return buffer_; +} + +bool CompareMatches(const Match& match_a, const Match& match_b) { + // Sort so that earlier starting, and longer matches are first + if (match_a.start < match_b.start) { + return true; + } + if (match_a.start > match_b.start) { + return false; + } + // But between matches that start at the same index, sort matches + // that are longer first. + if (match_a.end > match_b.end) { + return true; + } + return false; +} + +void BadWords::CleanupMatches(std::list& matches) { + // Clean up the match list. First, remove smaller matches that are + // inside larger matches. + + // Sort so that earlier and longer matches are first + matches.sort(CompareMatches); + + // Remove matches that overlap with other matches. This is easy since + // the matches have been sorted. + std::list::iterator it_i = matches.begin(); + while (it_i != matches.end()) { + std::list::iterator it_j = boost::next(it_i); + while (it_j != matches.end()) { + if ((*it_i).end >= (*it_j).start) { + it_j = matches.erase(it_j); + continue; + } + it_j++; + } + it_i++; + } +} + +} // namespace wi diff --git a/server/badwords.h b/server/badwords.h new file mode 100644 index 0000000..d4392e3 --- /dev/null +++ b/server/badwords.h @@ -0,0 +1,84 @@ +#ifndef __BADWORDS_H__ +#define __BADWORDS_H__ + +#include "inc/basictypes.h" +#include "base/sigslot.h" +#include "server/filewatcher.h" +#include +#include +#include + +namespace wi { + +const dword kfMatMatch = 0x80000000; +const dword kfMatStandalone = 0x40000000; +const dword kfMatWildcards = 0x20000000; +const dword kfMatIndex = 0x1fffffff; + +struct Match { + Match(int start, int end) : start(start), end(end) {} + int start; + int end; +}; + +struct MatchNode { + MatchNode() { memset(this, 0, sizeof(*this)); } + dword indexes[26]; +}; + +struct MatchEntry { + MatchEntry(int start, int end, int current_index, int char_index_last) : + start(start), end(end), current_index(current_index), + char_index_last(char_index_last), char_count(0), + wildcard_count(0), wildcard_replacements(0), match(false), + standalone(false), last_space(-1), has_newline(false) {} + int start; + int end; + int current_index; + int char_index_last; + int char_count; + int wildcard_count; + int wildcard_replacements; + bool match; + bool standalone; + int last_space; + bool has_newline; +}; + +// Single pass bad word filter + +class BadWords : public base::has_slots<> { +public: + BadWords(const std::string& filename); + const char *Filter(const char *input, int *cch_back = NULL); + bool on() { return on_; } + void toggle() { on_ ^= true; } + +private: + std::map > ParsePairs( + const std::string& s); + void AddPermutations(const std::string& word, + const std::map >& pairs, + bool standalone, bool wildcards); + void Permute(const std::string& suffix, + const std::vector& parts, + const std::vector *>& values, + int index, bool standalone, bool wildcards); + void AddWord(const std::string& word, bool standalone, bool wildcards); + std::vector Split(const std::string& s, char ch); + bool IsStandalone(const char *input, int start, int end); + const char *BuildResult(const char *input, std::list& matches); + void CleanupMatches(std::list& matches); + int GetNextCharIndexes(int current_index, int *next_indexes); + void OnFileUpdated(ThreadedFileWatcher *watcher); + bool IsMatch(const char *input, const MatchEntry& entry); + + ThreadedFileWatcher watcher_; + std::vector nodes_; + char buffer_[2048]; + bool on_; +}; + +} // namespace wi + +#endif // __BADWORDS_H__ diff --git a/server/chatlimiter.cpp b/server/chatlimiter.cpp new file mode 100644 index 0000000..9b3e8bd --- /dev/null +++ b/server/chatlimiter.cpp @@ -0,0 +1,132 @@ +#include "server/chatlimiter.h" +#include "base/tick.h" + +namespace wi { + +ChatLimiter::ChatLimiter(int chat_count, int seconds_count, + int timeout_minutes, int repeat_limit) { + ms_per_token_ = seconds_count * 1000 / chat_count; + token_limit_ = chat_count; + timeout_minutes_ = timeout_minutes; + repeat_limit_ = repeat_limit; +} + +void ChatLimiter::OnEndpointDelete(Endpoint *endpoint) { + // Stop tracking this endpoint + ChatterMap::iterator it = map_.find(endpoint->id()); + if (it == map_.end()) { + return; + } + + // If currently blocked, remember the ip so it can be blocked if there + // is a reconnect. + long64 tCurrent = base::GetMillisecondCount(); + if (tCurrent < it->second.tExpires) { + endpoint->AddTracker(tracker_, it->second.tExpires); + } + map_.erase(it); +} + +Chatter *ChatLimiter::CreateChatter(Endpoint *endpoint) { + // Create a new chatter + map_.insert(ChatterMap::value_type(endpoint->id(), Chatter(token_limit_))); + endpoint->SignalOnDelete.connect(this, &ChatLimiter::OnEndpointDelete); + Chatter *chatter = FindChatter(endpoint); + if (chatter == NULL) { + return NULL; + } + + // See if this user is being tracked. If so, pick up the settings and + // remove the tracker. + if (endpoint->FindTracker(tracker_, &chatter->tExpires)) { + endpoint->RemoveTracker(tracker_); + } + return chatter; +} + +Chatter *ChatLimiter::FindChatter(Endpoint *endpoint) { + // First see if this endpoint is already being tracked. + ChatterMap::iterator it = map_.find(endpoint->id()); + if (it != map_.end()) { + return &it->second; + } + return NULL; +} + +void ChatLimiter::Mute(Endpoint *endpoint, int minutes) { + // See if this endpoint is already being tracked. If not, add it. + Chatter *chatter = FindChatter(endpoint); + if (chatter == NULL) { + chatter = CreateChatter(endpoint); + if (chatter == NULL) { + return; + } + } + if (minutes < 0) { + minutes = 0; + } + if (minutes > kcMinutesTimeoutMaximum) { + minutes = kcMinutesTimeoutMaximum; + } + chatter->repeat_count = 0; + chatter->tExpires = base::GetMillisecondCount() + minutes * 60000; +} + +int ChatLimiter::Submit(Endpoint *endpoint, const char *chat, + int *minutes_remaining) { + // See if this endpoint is already being tracked. If not, add it. + Chatter *chatter = FindChatter(endpoint); + if (chatter == NULL) { + chatter = CreateChatter(endpoint); + if (chatter == NULL) { + return knChatLimitResultNotLimited; + } + } + + // Calculate the # of new tokens to add to the bucket since last chat + long64 tCurrent = base::GetMillisecondCount(); + chatter->token_count += (dword)(tCurrent - chatter->tLast) / ms_per_token_; + if (chatter->token_count > token_limit_) { + chatter->token_count = token_limit_; + } + chatter->tLast = tCurrent; + + // Is this chatter rate limited already? If so, wait until the limit + // expires. + if (tCurrent < chatter->tExpires) { + *minutes_remaining = (int)(((chatter->tExpires - tCurrent + 500) / 1000 + + 59) / 60); + return knChatLimitResultLimited; + } + + // Is there a full token left? If not, this chatter has gone over + // the rate limit. + bool moderator = endpoint->IsModerator(); + if (chatter->token_count < 1.0 && !moderator) { + chatter->repeat_count = 0; + chatter->tExpires = tCurrent + timeout_minutes_ * 60000; + *minutes_remaining = timeout_minutes_; + return knChatLimitResultNewlyLimited; + } + chatter->token_count -= 1.0f; + + // Spamming the same string over and over? + if ((strcmp(chatter->last_chat.c_str(), chat) == 0 || strlen(chat) > 80) && + !moderator) { + chatter->repeat_count++; + if (chatter->repeat_count >= repeat_limit_) { + chatter->repeat_count = 0; + chatter->tExpires = tCurrent + timeout_minutes_ * 60000; + *minutes_remaining = timeout_minutes_; + return knChatLimitResultNewlyLimited; + } + } else { + chatter->last_chat = chat; + chatter->repeat_count = 0; + } + + // Looks good, let it go through + return knChatLimitResultNotLimited; +} + +} // namespace wi diff --git a/server/chatlimiter.h b/server/chatlimiter.h new file mode 100644 index 0000000..1f7e743 --- /dev/null +++ b/server/chatlimiter.h @@ -0,0 +1,66 @@ +#ifndef __CHATLIMITER_H__ +#define __CHATLIMITER_H__ + +#include "server/endpoint.h" +#include "server/tracker.h" +#include "base/sigslot.h" +#include + +namespace wi { + +// Uses a token bucket algorithm. Imagine a bucket being filled up with +// tokens at a constant rate. Each chat takes one token out of the bucket. +// If it's empty, the chat doesn't get sent. + +struct Chatter { + Chatter(int token_count) : token_count(token_count), tLast(0), + tExpires(0), repeat_count(0) {} + float token_count; + long64 tLast; + long64 tExpires; + std::string last_chat; + int repeat_count; +}; + +const int knChatLimitResultNotLimited = 0; +const int knChatLimitResultNewlyLimited = 1; +const int knChatLimitResultLimited = 2; +const int kcChatLimitResultRepeatLimit = 3; + +const int kcChatsPerDefault = 5; +const int kcSecondsPerDefault = 10; +const int kcMinutesTimeoutDefault = 3; +const int kcRepeatLimitDefault = 4; + +const int kcMinutesTimeoutMaximum = 2 * 60; + +class ChatLimiter : public base::has_slots<> { +public: + ChatLimiter(int chat_count = kcChatsPerDefault, + int seconds_count = kcSecondsPerDefault, + int timeout_minutes = kcMinutesTimeoutDefault, + int repeat_limit = kcRepeatLimitDefault); + + int Submit(Endpoint *endpoint, const char *chat, int *minutes_remaining); + void Mute(Endpoint *endpoint, int minutes = kcMinutesTimeoutDefault); + int timeout_minutes() { return timeout_minutes_ ; } + Tracker& tracker() { return tracker_; } + +private: + Chatter *FindChatter(Endpoint *endpoint); + Chatter *CreateChatter(Endpoint *endpoint); + void OnEndpointDelete(Endpoint *endpoint); + + int timeout_minutes_; + int token_limit_; + int repeat_limit_; + dword ms_per_token_; + + typedef std::map ChatterMap; + ChatterMap map_; + Tracker tracker_; +}; + +} // namespace wi + +#endif // __CHATLIMITER_H__ diff --git a/server/echo_client.py b/server/echo_client.py new file mode 100644 index 0000000..08e70cf --- /dev/null +++ b/server/echo_client.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +""" +A simple echo client +""" + +import socket +import time + +#host = '174.129.126.177' +host = '79.125.42.223' +port = 50000 +s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +s.connect((host,port)) + +while True: + start = time.time() + s.send('hello world') + data = s.recv(11) + end = time.time() + print 'RTT: %dms' % int((end - start) * 1000) + +s.close() diff --git a/server/echo_server.py b/server/echo_server.py new file mode 100644 index 0000000..42d7520 --- /dev/null +++ b/server/echo_server.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +""" +A simple echo server +""" + +import socket + +host = '' +port = 50000 +backlog = 5 +s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +s.bind((host,port)) +s.listen(backlog) +while True: + client, address = s.accept() + while True: + data = client.recv(11) + if not data: + break + client.send(data) diff --git a/server/endpoint.cpp b/server/endpoint.cpp new file mode 100644 index 0000000..140ac87 --- /dev/null +++ b/server/endpoint.cpp @@ -0,0 +1,1528 @@ +#include "server/endpoint.h" +#include "server/game.h" +#include "server/ids.h" +#include "server/tokenauth.h" +#include "server/room.h" +#include "mpshared/xpump.h" +#include "mpshared/messages.h" +#include "base/sigslot.h" +#include "base/log.h" +#include "base/tick.h" + +namespace wi { + +#define MISSED_ECHO_COUNT 3 +#define CHAT_FRAGMENT_LENGTH 250 + +const int kcMinutesBanTimeoutDefault = 5; +const int kcMinutesBanTimeoutMaximum = 60 * 24 * 2; + +Endpoint::Endpoint(Server& server, base::Socket *socket, dword id, + bool serverfull) : server_(server), id_(id), + serverfull_(serverfull), clientid_(0), + protocolid_(0), state_(ES_HANDSHAKE), game_(NULL), echo_(true), + roomid_(0), name_(NULL), anonymous_(true), missed_(0), okecho_(false), + admin_(false), muted_(false), sigvisible_(false), seechat_(false), + roomlimiter_(2, 120) { + did_[0] = 0; + xpump_.Attach(socket, this, server.log()); +} + +Endpoint::~Endpoint() { + LOG() << base::Log::Format("0x%08lx", this); + SignalOnDelete(this); + delete name_; +} + +void Endpoint::SetState(State state) +{ + LOG() << base::Log::Format("0x%08lx ", this) + << "From: " << EsLabels.Find(state_) + << " To: " << EsLabels.Find(state); + state_ = state; +} + +bool Endpoint::CheckState(State state0, State state1) +{ +#ifdef DEV_BUILD + Assert(state0 == state_ || state1 == state_); +#endif + if (state0 != state_ && state1 != state_) { + LOG() << base::Log::Format("0x%08lx ", this) + << "Warning! Current: " << EsLabels.Find(state_); + if (state1 == (State)-1) { + LOG() << base::Log::Format("0x%08lx ", this) + << " Expected: " << EsLabels.Find(state0); + } else { + LOG() << base::Log::Format("0x%08lx ", this) + << " Expected: " << EsLabels.Find(state0) + << " or " << EsLabels.Find(state1); + } + return false; + } + return true; +} + +void Endpoint::OnHandshake(dword clientid, dword protocolid) { + if (!CheckState(ES_HANDSHAKE)) { + xpump_.Send(XMsgHandshakeResult::ToBuffer(knHandshakeResultFail, 0)); + return; + } + + // Check protocol; let client know result + clientid_ = clientid; + protocolid_ = protocolid; + + // The server and client need to support the same protocol. + if (protocolid != kdwProtocolCurrent) { + xpump_.Send(XMsgHandshakeResult::ToBuffer(knHandshakeResultFail, 0)); + RLOG() << base::Log::Format("0x%08lx ", this) + << "wrong protocolid: " << protocolid + << " clientid: " << clientid; + return; + } + + if (clientid != kdwClientID) { + xpump_.Send(XMsgHandshakeResult::ToBuffer(knHandshakeResultFail, 0)); + RLOG() << base::Log::Format("0x%08lx ", this) + << "wrong clientid: " << clientid; + return; + } + + if (serverfull_) { + xpump_.Send(XMsgHandshakeResult::ToBuffer(knHandshakeResultServerFull, + 0)); + RLOG() << base::Log::Format("0x%08lx ", this) << "server is full."; + return; + } + + xpump_.Send(XMsgHandshakeResult::ToBuffer(knHandshakeResultSuccess, id_)); + SetState(ES_HANDSHAKESUCCESS); + + // Echos past this point will be acknowledged + okecho_ = true; +} + +void Endpoint::OnLogin(const char *username, const char *token, const char *did) { + LOG() << "username: " << username << " token: " << token << " did: " << did; + + if (!CheckState(ES_HANDSHAKESUCCESS)) { + xpump_.Send(XMsgLoginResult::ToBuffer(knLoginResultFail)); + return; + } + + // Check to see this user is blocked. Might have did_. + if (FindTracker(server_.tracker())) { + xpump_.Send(XMsgLoginResult::ToBuffer(knLoginResultFail)); + return; + } + + // Anonymous login always works, if that's what the user wants + if (TokenAuth::IsAnonymous(username, token)) { + RememberName(name_); + delete name_; + name_ = AllocString(base::Format::ToString("anon%d", id_)); + UpdateDid(did); + xpump_.Send(XMsgLoginResult::ToBuffer(knLoginResultAnonymousSuccess)); + anonymous_ = true; + SetState(ES_READY); + return; + } + + // Not anonymous; try real login + dword result = TokenAuth::Authenticate(username, token); + LOG() << AuthResults.Find(result) << ": " << username << ", " << token << ", " << did; + if (result == knAuthResultFail) { + RLOG() << "FAILED LOGIN: " << AuthResults.Find(result) << ": " << username << ", " << token << ", " << did; + xpump_.Send(XMsgLoginResult::ToBuffer(knLoginResultFail)); + return; + } + if (result == knAuthResultStaleToken) { + // Token hash is valid but timed out. + UpdateDid(did); + xpump_.Send(XMsgLoginResult::ToBuffer(knLoginResultStaleToken)); + return; + } + + // Update the did first, before seeing if the user is blocked + UpdateDid(did); + if (FindTracker(server_.tracker())) { + xpump_.Send(XMsgLoginResult::ToBuffer(knLoginResultFail)); + return; + } + + // Success. Take in the new name, transition to ES_READY + RememberName(name_); + delete name_; + name_ = AllocString(base::Format::ToString(username, id_)); + xpump_.Send(XMsgLoginResult::ToBuffer(knLoginResultSuccess)); + anonymous_ = false; + SetState(ES_READY); + + // Moderators see unfiltered chat by default + seechat_ = IsModerator(); +} + +void Endpoint::RememberName(const char *name) { + if (name == NULL) { + return; + } + old_names_.insert(old_names_.begin(), name); + while (old_names_.size() >= 10) { + old_names_.pop_back(); + } +} + +void Endpoint::OnSignOut() { + LOG(); + + // Only know how to do this from ES_READY, currently + if (!CheckState(ES_READY)) { + xpump_.Send(XMsgSignOutResult::ToBuffer(knSignOutResultFail)); + return; + } + + // Go back to before login state + xpump_.Send(XMsgSignOutResult::ToBuffer(knSignOutResultSuccess)); + SetState(ES_HANDSHAKESUCCESS); + anonymous_ = true; +} + +void Endpoint::OnLobbyJoin() { + if (!CheckState(ES_READY)) { + xpump_.Send(XMsgLobbyJoinResult::ToBuffer( + knLobbyJoinResultNotLoggedIn)); + return; + } + + // See if this endpoint will be allowed to enter the lobby + if (!server_.lobby().CanEnter(this)) { + xpump_.Send(XMsgLobbyJoinResult::ToBuffer(knLobbyJoinResultFail)); + } + + // Message that entering the lobby was a success, before actually + // entering the lobby, since that sends a bunch of messages. + xpump_.Send(XMsgLobbyJoinResult::ToBuffer(knLobbyJoinResultSuccess)); + server_.lobby().Enter(this); + SetState(ES_LOBBY); +} + +void Endpoint::OnLobbyCreateRoom(const char *name, const char *password) { + if (!CheckState(ES_LOBBY)) { + xpump_.Send(XMsgLobbyCreateRoomResult::ToBuffer( + knLobbyCreateRoomResultFail, 0)); + return; + } + + // Allow room creation at a pre-determined rate only + if (!IsModerator() && roomlimiter_.IsEmpty()) { + xpump_.Send(XMsgLobbyCreateRoomResult::ToBuffer( + knLobbyCreateRoomResultFail, 0)); + return; + } + + dword result = knLobbyCreateRoomResultFail; + Room *room = server_.lobby().NewRoom(this, name, password, + kroomidInvalid, false, &result); + if (room == NULL) { + xpump_.Send(XMsgLobbyCreateRoomResult::ToBuffer(result, 0)); + return; + } + + char ip[32]; + xpump_.socket()->GetRemoteAddress().IPAsString(ip, sizeof(ip)); + RLOG() << "NewRoom:" << name << " password:" << password + << " username:" << name_ << " id:" << id_ << " ip:" << ip; + + xpump_.Send(XMsgLobbyCreateRoomResult::ToBuffer(result, room->id())); +} + +void Endpoint::OnLobbyCanJoinRoom(dword roomid, const char *password) { + if (!CheckState(ES_LOBBY)) { + xpump_.Send(XMsgLobbyCanJoinRoomResult::ToBuffer( + knRoomJoinResultFail)); + return; + } + + Room *room = server_.lobby().FindRoom(roomid); + if (room == NULL) { + xpump_.Send(XMsgLobbyCanJoinRoomResult::ToBuffer( + knRoomJoinResultNotFound)); + return; + } + dword result = room->CanAddPlayer(this, password); + xpump_.Send(XMsgLobbyCanJoinRoomResult::ToBuffer(result)); +} + +void Endpoint::OnLobbyLeave() { + if (!CheckState(ES_LOBBY)) { + xpump_.Send(XMsgLobbyLeaveResult::ToBuffer(knLobbyLeaveResultFail)); + return; + } + + server_.lobby().Leave(this); + xpump_.Send(XMsgLobbyLeaveResult::ToBuffer(knLobbyLeaveResultSuccess)); + SetState(ES_READY); +} + +void Endpoint::OnRoomJoin(dword roomid, const char *password) { + if (!CheckState(ES_READY)) { + xpump_.Send(XMsgRoomJoinResult::ToBuffer(knRoomJoinResultFail)); + return; + } + + Room *room = server_.lobby().FindRoom(roomid); + if (room == NULL) { + xpump_.Send(XMsgRoomJoinResult::ToBuffer(knRoomJoinResultNotFound)); + return; + } + + // See if this endpoint will be allowed to join the room + dword result = room->CanAddPlayer(this, password); + if (result != knRoomJoinResultSuccess) { + xpump_.Send(XMsgRoomJoinResult::ToBuffer(result)); + return; + } + + // Message that joining the room was a success, before actually + // joining the room, since that sends a bunch of messages. + xpump_.Send(XMsgRoomJoinResult::ToBuffer(knRoomJoinResultSuccess)); + + // Remember the room and go into ROOM state. The room won't go away as + // long as this player is in it, or in a game that is in it. + roomid_ = room->id(); + SetState(ES_ROOM); + + // Add the player to the room. This will subscribe the endpoint to + // room changes. + room->AddPlayer(this); + + // Now tell the client that room status is complete, so it can track + // changes in room state. + xpump_.Send(XMsgRoomStatusComplete::ToBuffer()); + +#if 0 +// This causes the chat button to flash immediately, not good + + // If this room is being logged, tell the user + if (room->password()[0] == 0) { + const char *pszMsg = "Chat is subject to being logged."; + xpump_.Send(XMsgRoomReceiveChat::ToBuffer("", pszMsg)); + server_.logger().LogSystemMsg(this, pszMsg); + } +#endif +} + +void Endpoint::OnRoomSendChat(const char *chat) { + if (!CheckState(ES_ROOM)) { + return; + } + Room *room = server_.lobby().FindRoom(roomid_); + if (room == NULL) { + return; + } + std::string response; + if (ProcessCommand(chat, &response)) { + if (response.size() != 0) { + xpump_.Send(XMsgRoomReceiveChat::ToBuffer("", response.c_str())); + server_.logger().LogSystemMsg(this, response.c_str()); + } + return; + } + int minutes_remaining; + const char *pszMsg; + switch (server_.chatlimiter().Submit(this, chat, &minutes_remaining)) { + case knChatLimitResultNewlyLimited: + room->SendChat(NULL, base::Format::ToString("%s has been muted for %d minute(s) due to chat spamming.", name_, minutes_remaining)); + break; + + case knChatLimitResultLimited: + xpump_.Send(XMsgRoomReceiveChat::ToBuffer("", + base::Format::ToString("Chat blocked. %d minute(s) remaining.", + minutes_remaining))); + break; + + case knChatLimitResultNotLimited: + { + if (!room->moderated()) { + // Unmoderated rooms aren't filtered + room->SendChat(this, chat, NULL); + } else { + // Moderated rooms are filtered + const char *filtered; + if (FilterChat(chat, &filtered)) { + room->SendChat(this, filtered, chat); + } else { + room->SendChat(this, filtered, NULL); + } + } + } + break; + } +} + +bool Endpoint::FilterChat(const char *chat, const char **result) { + // Users swear vertically, and horizontally. Handle both cases. + // Maintain a chat fragment stream to handle multi-line chat swearing. + + // Add the newline, so that filtering can treat multiline appropriately. + std::string fragment = chat_fragment_ + '\n' + chat; + int cch_back = -1; + const char *filtered_frag = server_.badwords().Filter(fragment.c_str(), + &cch_back); + + const char *filtered_chat = filtered_frag + chat_fragment_.size() + 1; + + int cch_filtered = strlen(filtered_frag); + if (cch_back <= 0) { + chat_fragment_ = ""; + } else { + // Take the previous char too, for proper standalone checking + cch_back++; + if (cch_back > cch_filtered) { + cch_back = cch_filtered; + } + if (cch_back > CHAT_FRAGMENT_LENGTH) { + cch_back = CHAT_FRAGMENT_LENGTH; + } + chat_fragment_ = std::string(fragment.c_str() + + cch_filtered - cch_back); + } + + *result = filtered_chat; + return strcmp(chat, filtered_chat) != 0; +} + +void Endpoint::OnRoomCreateGame(const GameParams& params) { + Room *room = server_.lobby().FindRoom(roomid_); + if (!CheckState(ES_ROOM) || room == NULL) { + xpump_.Send(XMsgRoomCreateGameResult::ToBuffer(0, + knRoomCreateGameResultFail, NULL)); + return; + } + + // See if game params are valid + if (!ValidateGameParams(params)) { + xpump_.Send(XMsgRoomCreateGameResult::ToBuffer(0, + knRoomCreateGameResultFail, NULL)); + RLOG() << base::Log::Format("0x%08lx ", this) + << "game params invalid"; +#ifdef RELEASE_LOGGING + LogGameParams(params); +#endif + return; + } + + // Find this level in the cache. Does it exist? + PackId packidUpgrade; + switch (server_.cache().FindInfo(params.packid, params.szLvlFilename, + &info_, &packidUpgrade)) { + case 0: + // Don't know about this pack at all + xpump_.Send(XMsgRoomCreateGameResult::ToBuffer(0, + knRoomCreateGameResultUnknownMissionPack, NULL)); + RLOG() << base::Log::Format("0x%08lx ", this) + << "packid not found"; +#ifdef RELEASE_LOGGING + LogGameParams(params); +#endif + return; + + case 1: + // Know about this pack, and have this version + break; + + case 2: + // Know about this pack, but client has wrong version + xpump_.Send(XMsgRoomCreateGameResult::ToBuffer(0, + knRoomCreateGameResultUpgradeMissionPack, &packidUpgrade)); + LOG() << base::Log::Format("0x%08lx ", this) + << "client needs packid upgrade"; +#ifdef LOGGING + LogGameParams(params); +#endif + return; + } + params_ = params; + + // Make sure the room can accept a new game + if (!room->CanAddGame(this)) { + xpump_.Send(XMsgRoomCreateGameResult::ToBuffer(0, + knRoomCreateGameResultRoomFull, NULL)); + RLOG() << base::Log::Format("0x%08lx ", this) + << "room full: " << room->name(); + return; + } + + // Create a game. It'll be joined later. It'll time out and remove + // itself if there are no joiners after awhile. + Game *game = server_.NewGame(this, params_, info_, roomid_); + if (game == NULL) { + xpump_.Send(XMsgRoomCreateGameResult::ToBuffer(0, + knRoomCreateGameResultFail, NULL)); + return; + } + + // Add this game to the room + room->AddGame(this, game); + + // Tell the client about the result. + xpump_.Send(XMsgRoomCreateGameResult::ToBuffer(game->id(), + knRoomCreateGameResultSuccess, NULL)); +} + +void Endpoint::OnRoomCanJoinGame(dword gameid) { + Room *room = server_.lobby().FindRoom(roomid_); + if (!CheckState(ES_ROOM) || room == NULL) { + xpump_.Send(XMsgRoomCanJoinGameResult::ToBuffer( + knGameJoinResultFail)); + return; + } + + Game *game = room->FindGame(gameid); + if (game == NULL) { + xpump_.Send(XMsgRoomCanJoinGameResult::ToBuffer( + knGameJoinResultGameNotFound)); + return; + } + + dword result = game->CanAddPlayer(this); + xpump_.Send(XMsgRoomCanJoinGameResult::ToBuffer(result)); +} + +void Endpoint::OnRoomLeave(dword hint) { + Room *room = server_.lobby().FindRoom(roomid_); + if (!CheckState(ES_ROOM) || room == NULL) { + xpump_.Send(XMsgRoomLeaveResult::ToBuffer(knRoomLeaveResultFail)); + return; + } + + room->SignalOnDelete.disconnect(this); + room->RemovePlayer(this, hint); + roomid_ = 0; + + xpump_.Send(XMsgRoomLeaveResult::ToBuffer(knRoomLeaveResultSuccess)); + SetState(ES_READY); +} + +void Endpoint::OnGameJoin(dword gameid, dword roomid) { + // Games can be joined after leaving a room, which + // means ES_READY state. + + if (!CheckState(ES_READY)) { + xpump_.Send(XMsgGameJoinResult::ToBuffer(knGameJoinResultFail)); + return; + } + + Room *room = server_.lobby().FindRoom(roomid); + if (room == NULL) { + xpump_.Send(XMsgGameJoinResult::ToBuffer( + knGameJoinResultRoomNotFound)); + return; + } + + Game *game = room->FindGame(gameid); + if (game == NULL) { + xpump_.Send(XMsgGameJoinResult::ToBuffer( + knGameJoinResultRoomNotFound)); + return; + } + + dword result = game->CanAddPlayer(this); + if (result != knGameJoinResultSuccess) { + xpump_.Send(XMsgGameJoinResult::ToBuffer(result)); + return; + } + + xpump_.Send(XMsgGameJoinResult::ToBuffer(knGameJoinResultSuccess)); + + // Start logging with the game id + xpump_.SetLogId(game->id()); + + // This doesn't mean this player has "joined" the game. That + // is a separate message / state. + game_ = game; + game_->SignalOnDelete.connect(this, &Endpoint::OnGameDelete); + game->AddPlayer(this); + + SetState(ES_GAME); +} + +std::string Endpoint::GetChatName() { + if (anonymous_) { + return name_; + } + if (!sigvisible_) { + return name_; + } + + // Use a character that is illegal in usernames, so people know these tags + // are for real. The \xa0 and " characters are illegal in usernames, but + // double quotes can be simulated with two single quotes, so use \xa0. + if (IsAdmin()) { + return std::string("admin\xa0 ") + name_; + } + if (IsModerator()) { + return std::string("mod\xa0 ") + name_; + } + return name_; +} + +bool Endpoint::IsModerator() { + if (IsAdmin()) { + return true; + } + return server_.IsModerator(name_); +} + +bool Endpoint::IsAdmin() { + // Admin is sticky across usernames so stealth is possible + if (!admin_) { + admin_ = server_.IsAdmin(name_); + } + return admin_; +} + +bool Endpoint::GetArgument(const char *chat, int arg_index, std::string *arg, + const char **rest) { + const char *arg_end = chat; + const char *arg_start = NULL; + + for (int index = -1; index != arg_index; index++) { + // Scan past whitespace + const char *pch; + for (pch = arg_end; *pch != 0; pch++) { + if (!isspace(*pch)) { + break; + } + } + arg_start = pch; + + // Scan past non-whitespace + for (pch = arg_start; *pch != 0; pch++) { + if (isspace(*pch)) { + break; + } + } + arg_end = pch; + } + + if (arg_end <= arg_start) { + return false; + } + *arg = std::string(arg_start, arg_end - arg_start); + + if (rest != NULL) { + *rest = arg_end; + while (isspace(**rest)) { + (*rest)++; + } + } + return true; +} + +ModeratorCommand Endpoint::GetModeratorCommand(const char *chat) { + std::string arg; + if (!GetArgument(chat, 0, &arg)) { + return kModeratorCommandNone; + } + if (arg.size() == 0 || arg[0] != '/') { + return kModeratorCommandNone; + } + if (strcmp(arg.c_str(), "/mute") == 0) { + return kModeratorCommandMute; + } + if (strcmp(arg.c_str(), "/unmute") == 0) { + return kModeratorCommandUnmute; + } + if (strcmp(arg.c_str(), "/ids") == 0) { + return kModeratorCommandIds; + } + if (strcmp(arg.c_str(), "/ban") == 0) { + return kModeratorCommandBan; + } + if (strcmp(arg.c_str(), "/kick") == 0) { + return kModeratorCommandKick; + } + if (strcmp(arg.c_str(), "/rooms") == 0) { + return kModeratorCommandRooms; + } + if (strcmp(arg.c_str(), "/games") == 0) { + return kModeratorCommandGames; + } + if (strcmp(arg.c_str(), "/clear") == 0) { + return kModeratorCommandClear; + } + if (strcmp(arg.c_str(), "/sig") == 0) { + return kModeratorCommandSig; + } + if (strcmp(arg.c_str(), "/filter") == 0) { + return kModeratorCommandFilter; + } + if (strcmp(arg.c_str(), "/names") == 0) { + return kModeratorCommandNames; + } + if (strcmp(arg.c_str(), "/rules") == 0) { + return kModeratorCommandRules; + } + if (strcmp(arg.c_str(), "/help") == 0) { + return kModeratorCommandHelp; + } + if (strcmp(arg.c_str(), "/see") == 0) { + return kModeratorCommandSee; + } + if (strcmp(arg.c_str(), "/kill") == 0) { + return kModeratorCommandKill; + } + if (strcmp(arg.c_str(), "/perm") == 0) { + return kModeratorCommandPermanent; + } + if (strcmp(arg.c_str(), "/reg") == 0) { + return kModeratorCommandRegisteredOnly; + } + if (strcmp(arg.c_str(), "/w") == 0) { + return kModeratorCommandWhisper; + } + if (strcmp(arg.c_str(), "/m") == 0) { + return kModeratorCommandMods; + } + if (strcmp(arg.c_str(), "/title") == 0) { + return kModeratorCommandTitle; + } + if (strcmp(arg.c_str(), "/anon") == 0) { + return kModeratorCommandAnonBlock; + } + if (strcmp(arg.c_str(), "/swap") == 0) { + return kModeratorCommandSwap; + } + if (strcmp(arg.c_str(), "/flag") == 0) { + return kModeratorCommandFlag; + } + return kModeratorCommandUnknown; +} + +bool Endpoint::ProcessCommand(const char *chat, std::string *response) { + int id = GetModeratorCommand(chat); + if (id == kModeratorCommandNone) { + if (muted_) { + *response = "You previously muted yourself. To unmute and be able to chat again, type /unmute."; + return true; + } + return false; + } + + // A mod command has been entered + server_.logger().LogModCommand(this, chat); + + // Default response + *response = "Unknown command. /help for help."; + + // Command available to both mods / admins, and regular players + switch (id) { + case kModeratorCommandAnonBlock: + if (state_ == ES_GAME && game_ != NULL && + game_->ToggleAnonBlock(this)) { + if (game_->anonblock()) { + *response = "You have blocked anons."; + } else { + *response = "You have unblocked anons."; + } + } else { + *response = "This command is not allowed."; + } + return true; + + case kModeratorCommandSwap: + // This will get processed by the game. + return false; + + case kModeratorCommandFlag: + // Write an entry into the log + { + std::string msg; + GetArgument(chat, 1, &msg); + server().logger().LogMark(this, msg.c_str()); + *response = "You wrote a mark in the log."; + } + return true; + } + + if (!IsModerator()) { + switch (id) { + case kModeratorCommandMute: + *response = "You have muted yourself. You will not receive chat, or be able to send chat. /unmute to unmute yourself."; + muted_ = true; + break; + + case kModeratorCommandUnmute: + *response = "You have unmuted yourself."; + muted_ = false; + break; + + case kModeratorCommandHelp: + if (state_ == ES_GAME && game_ != NULL) { + bool anon = game_->IsAnonBlockAllowed(this); + bool swap = game_->IsSwapAllowed(this); + if (anon) { + if (swap) { + *response = "/mute, /unmute, /anon, /swap, /kick, /flag [msg], /help."; + } else { + *response = "/mute, /unmute, /anon, /kick, /flag [msg], /help."; + } + } else if (swap) { + *response = "/mute, /unmute, /swap, /kick, /flag [msg], /help."; + } else { + *response = "/mute, /unmute, /kick, /flag [msg], /help."; + } + } else { + *response = "/mute, /unmute, /kick, /flag [msg], /help."; + } + break; + + case kModeratorCommandKick: + *response = "Ouch that hurts."; + break; + } + return true; + } + + switch (id) { + case kModeratorCommandNames: + case kModeratorCommandBan: + case kModeratorCommandKill: + case kModeratorCommandFilter: + case kModeratorCommandClear: + case kModeratorCommandPermanent: + case kModeratorCommandRegisteredOnly: + if (!IsAdmin()) { + *response = "You need to be an admin to use this command."; + return true; + } + } + + RLOG() << "mod: " << name_ << " command: " << chat; + if (state_ == ES_GAME && game_ != NULL) { + RLOG() << "mod: " << name_ << " in game " + << " server id: " << server_.id() + << " server_start: " << server_.start_time() + << " game id: " << game_->id(); + } + + // Several of the commands take an endpoint id + Endpoint *endpoint = NULL; + std::string arg; + if (GetArgument(chat, 1, &arg)) { + dword chatter_id = 0; + base::Format::ToDword(arg.c_str(), 10, &chatter_id); + endpoint = server_.GetEndpointFromChatterId(chatter_id); + } + + char ip[32]; + memset(ip, 0, sizeof(ip)); + if (endpoint != NULL) { + endpoint->xpump().socket()->GetRemoteAddress().IPAsString(ip, + sizeof(ip)); + } + + switch (id) { + case kModeratorCommandMute: + if (endpoint != NULL) { + // Check the room the moderator is in. This still allows for + // remote action. + if (!CanModerate(roomid())) { + *response = "Can't moderate in this room."; + break; + } + if (endpoint->IsModerator()) { + *response = "You cannot mute another moderator."; + break; + } + std::string minutes_str; + int minutes = kcMinutesTimeoutDefault; + if (GetArgument(chat, 2, &minutes_str)) { + base::Format::ToInteger(minutes_str.c_str(), 10, &minutes); + if (minutes < 0) { + minutes = kcMinutesTimeoutDefault; + } + if (minutes > kcMinutesTimeoutMaximum) { + minutes = kcMinutesTimeoutMaximum; + } + } + RLOG() << "mod: " << name_ << " muted: " << endpoint->name() + << " minutes: " << minutes << " ip address: " << ip; + server_.chatlimiter().Mute(endpoint, minutes); + *response = base::Format::ToString("%s has been muted for %d minute(s). Action logged.", endpoint->name(), minutes); + } else { + *response = "Could not find player using that id."; + } + break; + + case kModeratorCommandUnmute: + if (endpoint != NULL) { + // Check the room the moderator is in. This still allows for + // remote action. + if (!CanModerate(roomid())) { + *response = "Can't moderate in this room."; + break; + } + RLOG() << "mod: " << name_ << " unmuted: " << endpoint->name() + << " ip address: " << ip; + server_.chatlimiter().Mute(endpoint, 0); + *response = base::Format::ToString("%s has been unmuted. Action logged.", endpoint->name()); + } else { + *response = "Could not find player using that id."; + } + break; + + case kModeratorCommandBan: + if (endpoint != NULL) { + // Check the room the moderator is in. This still allows for + // remote action. + if (!CanModerate(roomid())) { + *response = "Can't moderate in this room."; + break; + } + std::string minutes_str; + int minutes = kcMinutesBanTimeoutDefault; + if (endpoint->GetArgument(chat, 2, &minutes_str)) { + base::Format::ToInteger(minutes_str.c_str(), 10, &minutes); + if (minutes < 0) { + minutes = kcMinutesBanTimeoutDefault; + } + if (minutes > kcMinutesBanTimeoutMaximum) { + minutes = kcMinutesBanTimeoutMaximum; + } + } + RLOG() << "mod: " << name_ << " banned: " + << endpoint->name() << " minutes: " << minutes + << " ip address: " << ip; + long64 tExpires = base::GetMillisecondCount() + + minutes * 60 * 1000; + endpoint->AddTracker(server_.tracker(), tExpires); + *response = base::Format::ToString("%s has been banned from this server for %d minute(s). Action logged.", endpoint->name(), minutes); + endpoint->Dispose(); + } else { + *response = "Could not find player using that id."; + } + break; + + case kModeratorCommandRooms: + { + std::vector roomids = server_.lobby().GetRoomIds(); + std::vector::iterator it = roomids.begin(); + for (; it != roomids.end(); it++) { + Room *room = server_.lobby().FindRoom(*it); + if (room != NULL) { + std::ostringstream s; + s << "room id:" << (*it) << " name:" << room->name() + << " creator:" << room->creator() + << " id:" << room->creator_id(); + if (IsAdmin()) { + s << " password:" << room->password() + << " ip:" << room->creator_ip(); + } + if (state_ == ES_GAME) { + xpump_.Send(XMsgGameReceiveChat::ToBuffer("", s.str().c_str())); + } + if (state_ == ES_ROOM) { + xpump_.Send(XMsgRoomReceiveChat::ToBuffer("", s.str().c_str())); + } + server_.logger().LogSystemMsg(this, s.str().c_str()); + RLOG() << s.str(); + } + } + *response = ""; + } + break; + + case kModeratorCommandKill: + // Kill a room + { + Room *room = NULL; + std::string roomid_str; + if (!GetArgument(chat, 1, &roomid_str)) { + *response = "Room id not found."; + return true; + } + dword roomid = 0; + base::Format::ToDword(roomid_str.c_str(), 10, &roomid); + room = server_.lobby().FindRoom(roomid); + if (room == NULL) { + *response = "Room not found."; + return true; + } + if (room->Kill()) { + *response = "Room killed. Will be removed within 30 seconds."; + } else { + *response = "Room not killed."; + } + } + break; + + case kModeratorCommandGames: + { + Room *room = NULL; + std::string roomid_str; + if (!GetArgument(chat, 1, &roomid_str)) { + *response = "Room id not found."; + return true; + } + dword roomid = 0; + base::Format::ToDword(roomid_str.c_str(), 10, &roomid); + room = server_.lobby().FindRoom(roomid); + if (room == NULL) { + *response = "Room not found."; + return true; + } + std::vector gameids = room->GetGameIds(); + std::vector::iterator it = gameids.begin(); + for (; it != gameids.end(); it++) { + Game *game = room->FindGame(*it); + if (game != NULL) { + std::ostringstream s; + s << "game id:" << (*it) << " creator:" << game->creator() + << " title:" << game->info().title(); + if (state_ == ES_GAME) { + xpump_.Send(XMsgGameReceiveChat::ToBuffer("", + s.str().c_str())); + } + if (state_ == ES_ROOM) { + xpump_.Send(XMsgRoomReceiveChat::ToBuffer("", + s.str().c_str())); + } + server_.logger().LogSystemMsg(this, s.str().c_str()); + RLOG() << s.str(); + } + } + *response = ""; + } + break; + + case kModeratorCommandPermanent: + { + Room *room = NULL; + std::string roomid_str; + if (!GetArgument(chat, 1, &roomid_str)) { + *response = "Room id not found."; + return true; + } + dword roomid = 0; + base::Format::ToDword(roomid_str.c_str(), 10, &roomid); + room = server_.lobby().FindRoom(roomid); + if (room == NULL) { + *response = "Room not found."; + return true; + } + bool result; + if (!room->TogglePermanent(&result)) { + *response = "Could not toggle permanent status."; + return true; + } + if (result) { + *response = "Room is now marked permanent."; + } else { + *response = "Room is now marked non-permanent."; + } + } + break; + + case kModeratorCommandRegisteredOnly: + { + Room *room = NULL; + std::string roomid_str; + if (!GetArgument(chat, 1, &roomid_str)) { + *response = "Room id not found."; + return true; + } + dword roomid = 0; + base::Format::ToDword(roomid_str.c_str(), 10, &roomid); + room = server_.lobby().FindRoom(roomid); + if (room == NULL) { + *response = "Room not found."; + return true; + } + bool result; + if (!room->ToggleRegistered(&result)) { + *response = "Could not toggle registered status."; + return true; + } + if (result) { + *response = "Room now reg only."; + } else { + *response = "Room now open to all."; + } + } + break; + + case kModeratorCommandIds: + { + Room *room = server_.lobby().FindRoom(roomid_); + Game *game = game_; + + bool lobby = false; + dword roomid = roomid_; + std::string roomid_str; + if (GetArgument(chat, 1, &roomid_str)) { + if (strcmp(roomid_str.c_str(), "lobby") == 0) { + lobby = true; + } else { + base::Format::ToDword(roomid_str.c_str(), 10, &roomid); + room = server_.lobby().FindRoom(roomid); + if (room == NULL) { + *response = "Room not found."; + return true; + } + + game = NULL; + std::string gameid_str; + if (GetArgument(chat, 2, &gameid_str)) { + dword gameid = 0; + base::Format::ToDword(gameid_str.c_str(), 10, &gameid); + game = room->FindGame(gameid); + if (game == NULL) { + *response = "Game not found."; + return true; + } + } + } + } + + std::vector responses; + if (lobby) { + responses = server_.lobby().GetIdsString(this); + } else { + if (game != NULL) { + responses = game->GetIdsString(this); + } else if (room != NULL) { + responses = room->GetIdsString(this); + } + } + + std::vector::iterator it = responses.begin(); + for (; it != responses.end(); it++) { + RLOG() << "mod " << name_ << ": " << *it; + if (state_ == ES_GAME) { + xpump_.Send(XMsgGameReceiveChat::ToBuffer("", + (*it).c_str())); + } + if (state_ == ES_ROOM) { + xpump_.Send(XMsgRoomReceiveChat::ToBuffer("", + (*it).c_str())); + } + server_.logger().LogSystemMsg(this, (*it).c_str()); + } + } + *response = ""; + break; + + case kModeratorCommandClear: + { + server_.lobby().room_tracker().Clear(); + server_.chatlimiter().tracker().Clear(); + server_.tracker().Clear(); + std::vector roomids = server_.lobby().GetRoomIds(); + std::vector::iterator it = roomids.begin(); + for (; it != roomids.end(); it++) { + Room *room = server_.lobby().FindRoom(*it); + if (room != NULL) { + room->tracker().Clear(); + } + } + *response = "Cleared."; + } + break; + + case kModeratorCommandSig: + sigvisible_ ^= true; + *response = GetChatName(); + break; + + case kModeratorCommandSee: + seechat_ ^= true; + if (seechat_) { + *response = "You will see unfiltered chat."; + } else { + *response = "You will no longer see unfiltered chat."; + } + break; + + case kModeratorCommandFilter: + server_.badwords().toggle(); + if (server_.badwords().on()) { + *response = "Word filter is now on."; + } else { + *response = "Word filter is now off."; + } + break; + + case kModeratorCommandNames: + if (endpoint != NULL) { + if (endpoint->old_names().size() == 0) { + *response = base::Format::ToString("No other names for %s.", + endpoint->name()); + return true; + } + std::vector::const_iterator it = + endpoint->old_names().begin(); + for (; it != endpoint->old_names().end(); it++) { + const char *s = base::Format::ToString("%s = %s", + endpoint->name(), (*it).c_str()); + if (state_ == ES_GAME) { + xpump_.Send(XMsgGameReceiveChat::ToBuffer("", s)); + } + if (state_ == ES_ROOM) { + xpump_.Send(XMsgRoomReceiveChat::ToBuffer("", s)); + } + server_.logger().LogSystemMsg(this, s); + RLOG() << s; + } + *response = ""; + } else { + *response = "Could not find player using that id."; + } + break; + + case kModeratorCommandMods: + // Broadcast chat to all mods in every room / game + { + std::string dummy; + const char *rest; + if (GetArgument(chat, 0, &dummy, &rest) && *rest != 0) { + const char *s = base::Format::ToString("%s to mods: %s", + name_, rest); + server_.lobby().SendAdminChat("", s, true); + } + } + *response = ""; + break; + + case kModeratorCommandWhisper: + // Send to specific user id + if (endpoint == NULL) { + *response = "No id or invalid id."; + return true; + } else { + std::string dummy; + const char *rest; + if (GetArgument(chat, 1, &dummy, &rest) && *rest != 0) { + const char *s = base::Format::ToString("%s to %s: %s", + name_, endpoint->name(), rest); + if (endpoint->state_ == ES_GAME) { + endpoint->xpump().Send( + XMsgGameReceiveChat::ToBuffer("", s)); + } + if (endpoint->state_ == ES_ROOM) { + endpoint->xpump().Send( + XMsgRoomReceiveChat::ToBuffer("", s)); + } + server_.logger().LogSystemMsg(this, s); + if (endpoint != this) { + *response = s; + return true; + } + } + *response = ""; + } + break; + + case kModeratorCommandTitle: + { + const char *name; + std::string roomid_str; + if (!GetArgument(chat, 1, &roomid_str, &name)) { + *response = "No room id specified."; + return true; + } + + dword roomid = 0; + base::Format::ToDword(roomid_str.c_str(), 10, &roomid); + Room *room = server_.lobby().FindRoom(roomid); + if (room == NULL) { + *response = "Room not found."; + return true; + } + + if (!room->SetName(server_.badwords().Filter(name))) { + *response = "Failed setting room title."; + return true; + } + *response = "Success"; + } + break; + + case kModeratorCommandHelp: + if (IsAdmin()) { + if (state_ == ES_GAME) { + *response = "/ids [lobby] [roomid] [gameid], /mute [minutes], /unmute , /ban [minutes], /rooms, /kill , /games , /names , /w , /m, /title , /clear, /filter, /sig, /see, /perm , /reg , /swap, /anon, /rules, /flag [msg], /help."; + } else { + *response = "/ids [lobby] [roomid] [gameid], /mute [minutes], /unmute , /kick [minutes], /ban [minutes], /rooms, /kill , /games , /names , /w , /m, /title , /clear, /filter, /sig, /see, /perm , /reg , /rules, /flag [msg], /help."; + } + } else { + if (state_ == ES_GAME) { + *response = "/ids [lobby] [roomid] [gameid], /mute [minutes], /unmute , /rooms, /games , /w , /m, /title , /sig, /see, /swap, /anon, /rules, /flag [msg], /help."; + } else { + *response = "/ids [lobby] [roomid] [gameid], /mute [minutes], /unmute , /kick [minutes], /rooms, /games , /w , /m, /title , /sig, /see, /rules, /flag [msg], /help."; + } + } + break; + + case kModeratorCommandKick: + case kModeratorCommandRules: + // These command gets processed by the room, or game, since the + // enumeration is different, so pass it through. + return false; + + default: + break; + } + + return true; +} + +void Endpoint::OnGameSendChat(const char *chat) { + if (!CheckState(ES_GAME)) { + return; + } + if (game_ == NULL) { + return; + } + std::string response; + if (ProcessCommand(chat, &response)) { + if (response.size() != 0) { + xpump_.Send(XMsgGameReceiveChat::ToBuffer("", response.c_str())); + server_.logger().LogSystemMsg(this, response.c_str()); + } + return; + } + int minutes_remaining; + switch (server_.chatlimiter().Submit(this, chat, &minutes_remaining)) { + case knChatLimitResultNewlyLimited: + game_->SendChat(NULL, base::Format::ToString("%s has been muted for %d minute(s) due to chat spamming.", name_, minutes_remaining)); + break; + + case knChatLimitResultLimited: + xpump_.Send(XMsgGameReceiveChat::ToBuffer("", + base::Format::ToString("Chat blocked. %d minute(s) remaining.", + minutes_remaining))); + break; + + case knChatLimitResultNotLimited: + { + Room *room = server_.lobby().FindRoom(roomid_); + if (room != NULL && !room->moderated()) { + // Unmoderated rooms aren't filtered + game_->SendChat(this, chat, NULL); + } else { + // Moderated rooms are filtered + const char *filtered; + if (FilterChat(chat, &filtered)) { + game_->SendChat(this, filtered, chat); + } else { + game_->SendChat(this, filtered, NULL); + } + } + } + break; + } +} + +void Endpoint::OnGameLeave() { + // Check game_ for NULL first, before state_ is checked, + // since the server can force game_ to NULL legally. + if (game_ == NULL) { + xpump_.Send(XMsgGameLeaveResult::ToBuffer( + knGameLeaveResultNotFound)); + LOG() << base::Log::Format("0x%08lx ", this) + << "No game to disconnect from. " + << "Can happen when the server disconnects first"; + return; + } + + if (!CheckState(ES_GAME)) { + xpump_.Send(XMsgGameLeaveResult::ToBuffer( + knGameLeaveResultFail)); + LOG() << base::Log::Format("0x%08lx ", this) + << "Not in ES_GAME state."; + return; + } + + game_->SignalOnDelete.disconnect(this); + game_->RemovePlayer(this, knDisconnectReasonLeftGame); + game_ = NULL; + + // Stop logging with the game id + xpump_.SetLogId(0); + + xpump_.Send(XMsgGameLeaveResult::ToBuffer(knGameLeaveResultSuccess)); + + // Go back to ES_READY. From here the client will most likely rejoin + // the last room it was in. + SetState(ES_READY); +} + +void Endpoint::OnGameNetMessage(NetMessage **ppnm) { + // Check game_ for NULL first, before state_ is checked, + // since the server can force game_ to NULL legally. + if (game_ == NULL) { + LOG() << base::Log::Format("0x%08lx ", this) + << "No game for NetMessage. " + << "Can happen when the server disconnects first"; + return; + } + + if (!CheckState(ES_GAME)) { + LOG() << base::Log::Format("0x%08lx ", this) << "Not in ES_GAME!"; + return; + } + game_->OnNetMessage(this, *ppnm); +} + +void Endpoint::OnGameDelete(Game *game) { + LOG() << "game: " << game->info().title() << " created by: " << + game->creator(); + DropGame(game); +} + +void Endpoint::DropGame(Game *game, int reason) { + LOG() << base::Log::Format("0x%08lx ", this) + << "Dropping game, reason: " << reason; + + Assert(game == game_); + if (game == NULL || game != game_) { + return; + } + game_->RemovePlayer(this, reason); + game_->SignalOnDelete.disconnect(this); + xpump_.Send(XMsgGameKilled::ToBuffer(game_->id())); + game_ = NULL; + SetState(ES_READY); +} + +void Endpoint::OnError(int error) { + LOG() << base::Log::Format("0x%08lx ", this) << error; + Dispose(); +} + +void Endpoint::OnClose(int error) { + LOG() << base::Log::Format("0x%08lx ", this) << error; + Dispose(); +} + +void Endpoint::OnCloseOk() { + LOG() << base::Log::Format("0x%08lx ", this); + Dispose(); +} + +void Endpoint::OnHeartbeat() { + // If a game is playing, there is a game timer to monitor clients + if (game_ != NULL && game_->playing()) { + return; + } + + if (!echo_) { + LOG() << base::Log::Format("0x%08lx ", this) + << "client ping timeout"; +#ifndef DEV_BUILD + // When a user brings up audio controls (double click home), or + // gets a phone call, the iPhone OS freezes the underlying + // application, so it won't be returning echoes. Make it miss a + // few before killing it, since these cases happen. + missed_++; + if (missed_ == MISSED_ECHO_COUNT) { + Dispose(); + return; + } +#endif + } + xpump_.Send(XMsgEcho::ToBuffer()); + echo_ = false; +} + +void Endpoint::OnEcho() { + // Don't acknowledge echos until it is ok + if (!okecho_) { + return; + } + + // Echo received from client + echo_ = true; + missed_ = 0; +} + +void Endpoint::UpdateDid(const char *did) { + // The most accurate one is the first one seen + if (did_[0] == 0) { + strncpyz(did_, did, sizeof(did_)); + } +} + +void Endpoint::AddTracker(Tracker& tracker, long64 tExpires) { + const base::SocketAddress remote = xpump().socket()->GetRemoteAddress(); + char szT[64]; + remote.IPToString(remote.ip(), szT, sizeof(szT)); + tracker.Add(szT, tExpires); + if (did_[0] != 0) { + tracker.Add(did_, tExpires); + } +} + +void Endpoint::RemoveTracker(Tracker& tracker) { + const base::SocketAddress remote = xpump().socket()->GetRemoteAddress(); + char szT[64]; + remote.IPToString(remote.ip(), szT, sizeof(szT)); + tracker.Remove(szT); + if (did_[0] != 0) { + tracker.Remove(did_); + } +} + +bool Endpoint::FindTracker(Tracker& tracker, long64 *tExpires) { + const base::SocketAddress remote = xpump().socket()->GetRemoteAddress(); + char szT[64]; + remote.IPToString(remote.ip(), szT, sizeof(szT)); + if (tracker.Find(szT, tExpires)) { + return true; + } + if (did_[0] != 0) { + if (tracker.Find(did_, tExpires)) { + return true; + } + } + return false; +} + +bool Endpoint::CanModerate(dword roomid) { + if (IsAdmin()) { + return true; + } + + Room *room = server_.lobby().FindRoom(roomid); + if (room == NULL) { + return false; + } + + return room->moderated(); +} + +std::string Endpoint::GetRoomName() +{ + Room *room = server_.lobby().FindRoom(roomid_); + if (room == NULL) { + return ""; + } + return room->name(); +} + +std::string Endpoint::GetGameName() +{ + if (game_ == NULL) { + return ""; + } + return game_->info().title(); +} + +dword Endpoint::gameid() { + return game_ == NULL ? 0 : game_->id(); +} + +} // namespace wi diff --git a/server/endpoint.h b/server/endpoint.h new file mode 100644 index 0000000..98b0c57 --- /dev/null +++ b/server/endpoint.h @@ -0,0 +1,145 @@ +#ifndef __ENDPOINT_H__ +#define __ENDPOINT_H__ + +#include +#include "base/misc.h" +#include "base/socket.h" +#include "base/sigslot.h" +#include "base/messagequeue.h" +#include "inc/basictypes.h" +#include "mpshared/xpump.h" +#include "server/levelinfo.h" +#include "server/lobby.h" +#include "server/tokenbucket.h" +#include "server/tracker.h" +#include + +namespace wi { + +class Server; +class Lobby; +class Room; +class Game; + +enum ModeratorCommand { + kModeratorCommandNone, kModeratorCommandUnknown, kModeratorCommandIds, + kModeratorCommandMute, kModeratorCommandUnmute, + kModeratorCommandKick, kModeratorCommandBan, kModeratorCommandRules, + kModeratorCommandRooms, kModeratorCommandGames, kModeratorCommandClear, + kModeratorCommandSig, kModeratorCommandFilter, kModeratorCommandNames, + kModeratorCommandSee, kModeratorCommandKill, kModeratorCommandPermanent, + kModeratorCommandMods, kModeratorCommandWhisper, kModeratorCommandTitle, + kModeratorCommandRegisteredOnly, kModeratorCommandAnonBlock, + kModeratorCommandSwap, kModeratorCommandFlag, kModeratorCommandHelp +}; + +class Endpoint : public base::MessageHandler, XPumpNotify, + public base::has_slots<> { +public: + Endpoint(Server& server, base::Socket *socket, dword id, bool serverfull); + ~Endpoint(); + void DropGame(Game *game, int reason = knDisconnectReasonAbnormal); + bool HasHeartbeat() { return true; } + void OnHeartbeat(); + bool GetArgument(const char *chat, int arg_index, std::string *arg, + const char **rest = NULL); + ModeratorCommand GetModeratorCommand(const char *chat); + std::string GetChatName(); + bool IsModerator(); + bool IsAdmin(); + void AddTracker(Tracker& tracker, long64 tExpires); + void RemoveTracker(Tracker& tracker); + bool FindTracker(Tracker& tracker, long64 *tExpires = NULL); + bool CanModerate(dword roomid); + std::string GetRoomName(); + std::string GetGameName(); + + XPump& xpump() { return xpump_; } + Server& server() { return server_; } + dword id() { return id_; } + const char *name() { return name_; } + bool anonymous() { return anonymous_; } + dword clientid() { return clientid_; } + bool muted() { return muted_; } + bool sigvisible() { return sigvisible_; } + bool seechat() { return seechat_; } + const char *did() { return did_; } + dword roomid() { return roomid_; } + dword gameid(); + + std::vector& old_names() { return old_names_; } + + enum State { + ES_HANDSHAKE, ES_HANDSHAKESUCCESS, ES_READY, ES_LOBBY, ES_ROOM, ES_GAME + }; + base::signal1 SignalOnDelete; + +private: + void SetState(State state); + bool CheckState(State state0, State state1 = (State)-1); + void OnGameDelete(Game *game); + bool ProcessCommand(const char *chat, std::string *response); + void RememberName(const char *name); + bool FilterChat(const char *chat, const char **result); + void UpdateDid(const char *did); + + // XPumpNotify overrides + virtual void OnHandshake(dword clientid, dword protocolid); + virtual void OnEcho(); + virtual void OnLogin(const char *username, const char *token, const char *did); + virtual void OnSignOut(); + virtual void OnLobbyJoin(); + virtual void OnLobbyCreateRoom(const char *name, const char *password); + virtual void OnLobbyCanJoinRoom(dword roomid, const char *password); + virtual void OnLobbyLeave(); + virtual void OnRoomJoin(dword roomid, const char *password); + virtual void OnRoomSendChat(const char *chat); + virtual void OnRoomCreateGame(const GameParams& params); + virtual void OnRoomCanJoinGame(dword gameid); + virtual void OnRoomLeave(dword hint); + virtual void OnGameJoin(dword gameid, dword roomid); + virtual void OnGameSendChat(const char *chat); + virtual void OnGameNetMessage(NetMessage **ppnm); + virtual void OnGameLeave(); + virtual void OnError(int error); + virtual void OnClose(int error); + virtual void OnCloseOk(); + + XPump xpump_; + State state_; + dword clientid_; + dword protocolid_; + Server& server_; + Game *game_; + GameParams params_; + LevelInfo info_; + bool echo_; + bool okecho_; + int missed_; + dword id_; + dword roomid_; + const char *name_; + bool anonymous_; + bool serverfull_; + bool admin_; + bool muted_; + bool sigvisible_; + bool seechat_; + std::vector old_names_; + std::string chat_fragment_; + TokenBucket roomlimiter_; + char did_[64]; +}; + +STARTLABEL(EsLabels) + LABEL(Endpoint::ES_HANDSHAKE) + LABEL(Endpoint::ES_HANDSHAKESUCCESS) + LABEL(Endpoint::ES_READY) + LABEL(Endpoint::ES_LOBBY) + LABEL(Endpoint::ES_ROOM) + LABEL(Endpoint::ES_GAME) +ENDLABEL(EsLabels) + +} // namespace wi + +#endif // __ENDPOINT_H__ diff --git a/server/filewatcher.cpp b/server/filewatcher.cpp new file mode 100644 index 0000000..b2041b8 --- /dev/null +++ b/server/filewatcher.cpp @@ -0,0 +1,41 @@ +#include "base/thread.h" +#include "server/filewatcher.h" +#include + +namespace wi { + +ThreadedFileWatcher::ThreadedFileWatcher(const std::string& filename, + int interval_seconds) : filename_(filename), + interval_seconds_(interval_seconds) { + // Set mtime_ to 0 so the signal gets raised right away. + mtime_ = 0; + watcher_thread_.Start(this, &ThreadedFileWatcher::ThreadStart); +} + +void ThreadedFileWatcher::ThreadStart(void *pv) { + // Running on watcher_thread_, notifying thread_ + while (!watcher_thread_.IsStopping()) { + time_t mtime = GetFileTime(); + if (mtime != mtime_) { + mtime_ = mtime; + thread_.Post(1, this); + } + watcher_thread_.RunLoop(interval_seconds_ * 100); + } +} + +time_t ThreadedFileWatcher::GetFileTime() { + struct stat s; + memset(&s, 0, sizeof(s)); + if (stat(filename_.c_str(), &s) == 0) { + return s.st_mtime; + } + return 0; +} + +void ThreadedFileWatcher::OnMessage(base::Message *pmsg) { + // Running on thread_ + SignalOnFileUpdated(this); +} + +} // namespace wi diff --git a/server/filewatcher.h b/server/filewatcher.h new file mode 100644 index 0000000..9aab327 --- /dev/null +++ b/server/filewatcher.h @@ -0,0 +1,34 @@ +#ifndef __FILEWATCHER_H__ +#define __FILEWATCHER_H__ + +#include +#include "base/messagequeue.h" +#include "base/thread.h" +#include "base/sigslot.h" + +namespace wi { + +// Signals the creator thread of a file modified time change. Watches on a +// worker thread. + +class ThreadedFileWatcher : base::MessageHandler { +public: + ThreadedFileWatcher(const std::string& filename, + int interval_seconds = 60 * 2); + const std::string& filename() { return filename_; } + base::signal1 SignalOnFileUpdated; + +private: + time_t GetFileTime(); + void ThreadStart(void *pv); + virtual void OnMessage(base::Message *pmsg); + + base::Thread watcher_thread_; + std::string filename_; + int interval_seconds_; + time_t mtime_; +}; + +} // namespace wi + +#endif // __FILEWATCHER_H__ diff --git a/server/game.cpp b/server/game.cpp new file mode 100644 index 0000000..cb254a1 --- /dev/null +++ b/server/game.cpp @@ -0,0 +1,1174 @@ +#include "server/game.h" +#include "server/ids.h" +#include "server/statsposter.h" +#include "server/room.h" +#include "server/lobby.h" +#include "base/tick.h" +#include "base/md5.h" +#include +#include + +namespace wi { + +const long64 kctStartTimeout = 4500; // 45 second timeout to start a game + +// This is how much time to give all clients to acknowledge a +// lag notification message. +const long64 kctLagAckKill = 3000; + +dword Game::s_gameidCounter_; + +Game::Game(Endpoint *endpoint, const GameParams& params, const LevelInfo& info, + Server& server, dword roomid, dword ff) : playerMgr_(server), + params_(params), info_(info), server_(server), roomid_(roomid), + ff_(ff), tLastLagNotify_(0), playing_(false), cUpdatesBlock_(-1), + msClock_(0), cUpdatesWaitSend_(0), cmsRate_(kcmsRateMax), + msCommandsSent_(0), pidLagging_(kpidNeutral), syncerror_(false), + advertiser_(endpoint), advertiserId_(endpoint->id()), id_(NewGameId()), + cSecsStart_(0), cSecsEnd_(0) { + + // This only hangs around until this endpoint joins the game, + // so the game will go away if the endpoint goes away. + advertiser_->SignalOnDelete.connect(this, &Game::OnAdvertiserDelete); + + strncpyz(creator_, endpoint->name(), sizeof(creator_)); + tCreated_ = base::GetTickCount(); + playerMgr_.Init(info_); +} + +Game::~Game() { + LOG() << base::Log::Format("0x%08lx", this); + if (advertiser_ != NULL) { + advertiser_->SignalOnDelete.disconnect(this); + advertiser_ = NULL; + } + SignalOnDelete(this); + PostWinStats(); +} + +dword Game::NewGameId() { + s_gameidCounter_++; + if (s_gameidCounter_ == 0) { + s_gameidCounter_ = 1; + } + return s_gameidCounter_; +} + +void Game::PostWinStats() { + if (ff_ & kfGameDontPostWinStats) { + return; + } + if (!playing_) { + return; + } + + // Collect stats + + GameStats stats; + memset(&stats, 0, sizeof(stats)); + stats.server_id = server_.id(); + stats.server_start = server_.start_time(); + stats.gameid = id_; + stats.params = params_; + stats.info = info_; + stats.start_utc = cSecsStart_; + stats.end_utc = cSecsEnd_ != 0 ? cSecsEnd_ : base::GetSecondsUnixEpocUTC(); + int count = 0; + Player *pplr = NULL; + while ((pplr = playerMgr_.GetNextPlayer(pplr)) != NULL) { + pplr->GetPlayerStats(&stats.player_stats[count]); + count++; + } + stats.player_count = count; + server_.poster().Submit(stats); +} + +dword Game::CanAddPlayer(Endpoint *endpoint) { + if (disposed_) { + return knGameJoinResultGameNotFound; + } + if (playing_) { + return knGameJoinResultInProgress; + } + + int cpplrUnfulfilled = 0; + Player *pplr = NULL; + while ((pplr = playerMgr_.GetNextHumanPlayer(pplr)) != NULL) { + if (pplr->flags() & kfPlrUnfulfilled) { + cpplrUnfulfilled++; + } + } + if (cpplrUnfulfilled == 0) { + return knGameJoinResultGameFull; + } + if (endpoint->anonymous() && anonblock()) { + return knGameJoinResultFail; + } + + return knGameJoinResultSuccess; +} + +void Game::AddPlayer(Endpoint *endpoint) { + // Add to list; establish callbacks + ConnectedList::iterator it = std::find(connected_.begin(), + connected_.end(), endpoint); + if (it == connected_.end()) { + connected_.push_back(endpoint); + if (endpoint == advertiser_) { + endpoint->SignalOnDelete.disconnect(this); + advertiser_ = NULL; + } + endpoint->SignalOnDelete.connect(this, &Game::OnEndpointDelete); + } + + // Clients expect knmidScGameParams on connect + GameParamsNetMessage nm; + nm.nmid = knmidScGameParams; + nm.cb = sizeof(nm); + nm.rams = params_; + endpoint->xpump().Send(XMsgGameNetMessage::ToBuffer(&nm)); +} + +void Game::RemovePlayer(Endpoint *endpoint, int reason, bool disconnect) { + LOG() << base::Log::Format("0x%08lx ", endpoint) + << "endpoint disconnecting, reason:" << reason; + + if (endpoint->id() == advertiserId_) { + // If this game hasn't started yet, dispose the game + if (!playing_) { + LOG() << base::Log::Format("0x%08lx ", this) + << "advertiser leaving game, auto disposing!"; + Dispose(); + } + } + + // Find this endpoint in the connected_ list. + ConnectedList::iterator it = std::find(connected_.begin(), + connected_.end(), endpoint); + if (it == connected_.end()) { + return; + } + + // If this endpoint is assigned a player, unassign it, which is different + // if playing vs. not playing yet. + Player *pplr = playerMgr_.GetPlayer(endpoint); + if (pplr != NULL) { + if (!playing_) { + // Not yet playing the game. The player this endpoint was using + // is now available for anyone else. + pplr->SetFlags(pplr->flags() & ~(kfPlrReady | kfPlrHumanJoined)); + pplr->SetFlags(pplr->flags() | kfPlrUnfulfilled); + pplr->SetEndpoint(NULL); + playerMgr_.BroadcastPlayersUpdate(); + + // Notify that players have changes + SignalOnPlayersChange(this); + } else { + // The game is playing; turn this player into a computer player. + // Tell other players this player is disconnecting. + pplr->SetFlags(pplr->flags() | kfPlrComputer); + pplr->SetEndpoint(NULL); + QueuePlayerDisconnect(pplr->pid(), reason); + + // Notify that players have changes + SignalOnPlayersChange(this); + } + } + + // Remove signaling and remove from list. If the list size is now zero, + // schedule this game for auto-deletion. + connected_.erase(it); + if (disconnect) { + endpoint->SignalOnDelete.disconnect(this); + } + if (connected_.size() == 0) { + LOG() << base::Log::Format("0x%08lx ", this) + << "no endpoints connected - auto disposing!"; + Dispose(); + } +} + +void Game::OnAdvertiserDelete(Endpoint *endpoint) { + LOG() << "name: " << endpoint->name(); + + // This will only get called if the advertiser goes away before + // joining the game. Don't disconnect from this signal while it is + // being called. + Dispose(); + advertiser_ = NULL; +} + +void Game::OnEndpointDelete(Endpoint *endpoint) { + // Player should be gone by now; if not it's abnormal disconnect + LOG() << "removing " << endpoint->name(); + RemovePlayer(endpoint, knDisconnectReasonAbnormal, false); +} + +void Game::OnNetMessage(Endpoint *endpoint, NetMessage *pnm) { + switch (pnm->nmid) { + case knmidCsClientCommands: + OnClientCommands(endpoint, (ClientCommandsNetMessage *)pnm); + break; + + case knmidCsClientReady: + OnClientReady(endpoint, pnm); + break; + + case knmidCsPlayerJoin: + OnPlayerJoin(endpoint, (PlayerJoinNetMessage *)pnm); + break; + + case knmidCsUpdateResult: + OnUpdateResult(endpoint, (UpdateResultNetMessage *)pnm); + break; + + case knmidCsConnect: + RLOG() << base::Log::Format("0x%08lx ", endpoint) + << "Received knmidCsConnect unexpectedly!"; + break; + + case knmidCsPlayerResign: + OnPlayerResign(endpoint, (PlayerResignNetMessage *)pnm); + break; + + case knmidCsRequestBeginGame: + OnRequestBeginGame(endpoint, pnm); + break; + + case knmidCsKillLaggingPlayer: + OnKillLaggingPlayer(endpoint, (KillLaggingPlayerNetMessage *)pnm); + break; + + case knmidCsLagAcknowledge: + OnLagAcknowledge(endpoint); + break; + + case knmidCsWinStats: + OnWinStats(endpoint, (WinStatsNetMessage *)pnm); + break; + + case knmidCsChallengeWin: + OnChallengeWin(endpoint); + break; + + default: + Assert(false); + break; + } +} + +void Game::OnPlayerJoin(Endpoint *endpoint, PlayerJoinNetMessage *ppjnm) { + // If already playing, this message means nothing + if (playing_) { + RLOG() << base::Log::Format("0x%08lx ", endpoint) + << "received PlayerJoin while playing."; + NetMessage nmsg(knmidScCantAcceptMoreConnections); + endpoint->xpump().Send(XMsgGameNetMessage::ToBuffer(&nmsg)); + return; + } + + // Are there any unfulfilled slots for human players? + Player *applrUnfulfilled[kcPlayersMax]; + int cpplrUnfulfilled = 0; + Player *pplr = NULL; + while ((pplr = playerMgr_.GetNextHumanPlayer(pplr)) != NULL) { + if (pplr->flags() & kfPlrUnfulfilled) { + applrUnfulfilled[cpplrUnfulfilled++] = pplr; + } + } + + // Don't accept this joiner if all the human slots are in use + if (cpplrUnfulfilled == 0) { + NetMessage nmsg(knmidScCantAcceptMoreConnections); + endpoint->xpump().Send(XMsgGameNetMessage::ToBuffer(&nmsg)); + return; + } + + // Randomly assign this joiner to one of the unfulfilled player slots + pplr = applrUnfulfilled[rand() % cpplrUnfulfilled]; + pplr->SetFlags(pplr->flags() & ~kfPlrUnfulfilled); + pplr->SetFlags(pplr->flags() | kfPlrHumanJoined); + //pplr->SetName(ppjnm->szPlayerName); + pplr->SetEndpoint(endpoint); + + // Is this the first joining player? If so remember it is the creator. + if (playerMgr_.GetEndpointCount() == 1) { + pplr->SetFlags(pplr->flags() | kfPlrCreator); + } + + // Notify all players of the new player + all old players + playerMgr_.BroadcastPlayersUpdate(); + + // Player names have changed + SignalOnPlayersChange(this); +} + +void Game::OnClientReady(Endpoint *endpoint, NetMessage *pnm) { + // If already playing, this message means nothing + if (playing_) { + RLOG() << base::Log::Format("0x%08lx ", endpoint) + << "received ClientReady while playing."; + return; + } + + // Update this player's state in the master list and let all players know + Player *pplr = playerMgr_.GetPlayer(endpoint); + if (pplr == NULL) { + RLOG() << base::Log::Format("0x%08lx ", endpoint) + << "has no player."; + return; + } + pplr->SetFlags(pplr->flags() | kfPlrReady); + playerMgr_.BroadcastPlayersUpdate(); +} + +void Game::OnRequestBeginGame(Endpoint *endpoint, NetMessage *pnm) { + LOG(); + + // Sent by the creator to begin a game + NetMessage nmFail(knmidScBeginGameFail); + if (playing_) { + endpoint->xpump().Send(XMsgGameNetMessage::ToBuffer(&nmFail)); + RLOG() << base::Log::Format("0x%08lx ", endpoint) + << "received RequestBeginGame while playing."; + return; + } + + // Make sure there are enough players ready to begin the game + int cplrReady = 0; + Player *pplr = NULL; + while ((pplr = playerMgr_.GetNextHumanPlayer(pplr)) != NULL) { + word wf = pplr->flags(); + if ((wf & kfPlrUnfulfilled) != 0) { + continue; + } + + // Count # of players ready. If any player isn't ready, + // the game isn't ready to begin. + if (wf & kfPlrReady) { + cplrReady++; + } else { + endpoint->xpump().Send(XMsgGameNetMessage::ToBuffer(&nmFail)); + return; + } + } + if (cplrReady < info_.minplayers()) { + endpoint->xpump().Send(XMsgGameNetMessage::ToBuffer(&nmFail)); + return; + } + + // All ready. Convert unfulfilled Human players into Computer players. + // The client handles these by removing all pre-created units. + bool fPlayersChanged = false; + pplr = NULL; + while ((pplr = playerMgr_.GetNextHumanPlayer(pplr)) != NULL) { + word wf = pplr->flags(); + if (wf & kfPlrUnfulfilled) { + pplr->SetFlags(wf | kfPlrComputer | kfPlrRemovedAtGameStart); + fPlayersChanged = true; + } + } + + // Let everyone know the new player state + if (fPlayersChanged) { + playerMgr_.BroadcastPlayersUpdate(); + } + + // Broadcast the BeginGame notification + BeginGameNetMessage bgnm; + bgnm.ulRandomSeed = (dword)base::GetMillisecondCount(); + playerMgr_.Broadcast(&bgnm); + + // Remove all endpoints that aren't playing the game. + // These are endpoints that have connected to the game, but haven't + // joined. Normally there should be zero of these, but the protocol + // allows for it. This casuses synchronous changes to connected_, so + // re-enumerate if and endpoint is removed. + while (true) { + bool fDropped = false; + ConnectedList::iterator it = connected_.begin(); + for (; it != connected_.end(); it++) { + if (playerMgr_.GetPlayer(*it) == NULL) { + (*it)->DropGame(this, knDisconnectReasonKilled); + fDropped = true; + break; + } + } + if (!fDropped) { + break; + } + } + + // The game is now playing + cSecsStart_ = base::GetSecondsUnixEpocUTC(); + StartTiming(); + + // Let room subscribers know + SignalOnInProgress(this); +} + +void Game::KickPlayers() { + // Forcefully disconnect players, and therefore take down this game + ConnectedList::iterator it = connected_.begin(); + for (; it != connected_.end(); it++) { + if (!(*it)->IsModerator()) { + (*it)->Dispose(); + } + } +} + +void Game::StartTiming() { + // Calculate the timer rate. Rate limit it. This does not need to + // match the client update rate - the two run independently. + long cmsUpdate = params_.tGameSpeed * 10; + if (cmsUpdate < kcmsRateMax) { + cmsRate_ = kcmsRateMax; + } else { + cmsRate_ = cmsUpdate; + } + + // The client sends UR for 0 to start things off + + cUpdatesWaitSend_ = 0; + cUpdatesBlock_ = 0; + cUpdatesSync_ = 0; + msClock_ = cUpdatesBlock_ * cmsUpdate; + msCommandsSent_ = base::GetMillisecondCount(); + + // Calculate the lag amount: N seconds in terms of updates + + cUpdatesAllowedLag_ = kcmsLagging / cmsUpdate; + pidLagging_ = kpidNeutral; + + // Begin the game timer. Remember when the timer started, so that + // it is possible to detect lagging clients. + tStartTimeout_ = base::GetTickCount() + kctStartTimeout; + playing_ = true; +} + +bool Game::HasHeartbeat() { + // Need heartbeat during the game + if (playing_) { + return true; + } + + // After the game has been created but before endpoints have joined, + // the heartbeat will auto-destroy the game after awhile. + if (connected_.size() == 0) { + return true; + } + return false; +} + +void Game::OnHeartbeat() { + // Auto-dispose after awhile if no endpoints have joined this game + if (!playing_) { + if (connected_.size() != 0) { + return; + } + if (base::GetTickCount() > tCreated_ + 100 * 30) { + Dispose(); + } + return; + } + + OnTimer(); +} + +void Game::OnTimer() { + LOG() << "cUpdatesBlock_: " << cUpdatesBlock_; + + // If sync error, do nothing + + if (syncerror_) { + return; + } + + // Wait for initial UpdateResult from all players. + + if (StartWait()) { + return; + } + + // Wait for lagging players. cUpdatesAllowedLag_ gives grace period of + // "allowed lag". + if (LagWait(cUpdatesBlock_ - cUpdatesAllowedLag_)) { + return; + } + + // Send the next Update to clients + // Send two commands to start the overlap if just starting out + + if (cUpdatesWaitSend_ == 0) { + SendClientCommands(); + SendClientCommands(); + } else { + SendClientCommands(); + } +} + +bool Game::StartWait() { + // The first UpdateResult is sent spontaneously. If all players have + // sent it, no need to block. + + if (playerMgr_.HaveAllPlayersReachedUpdate(0)) { + return false; + } + + // Have players who haven't started the same as lagging is handled + // normally. + + LagWait(0); + return true; +} + +bool Game::LagWait(long cUpdatesOldest) { + // Find the laggy player, if there is one + + Player *pplrLagging = playerMgr_.FindLaggingPlayer(cUpdatesOldest); + + // Remember which player is lagging, for next time + + Player *pplrLaggingLast = playerMgr_.GetPlayerFromPid(pidLagging_); + if (pplrLagging == NULL) { + pidLagging_ = kpidNeutral; + } else { + pidLagging_ = pplrLagging->pid(); + } + + // If there is no lagging player, and there was no lagging player + // last time, we're done. + + if (pplrLagging == NULL && pplrLaggingLast == NULL) { + return false; + } + + // A message may need to be sent now. If there is an abrupt change, from + // lag to no lag, or vice versa, send a message now. + + bool fSendNow = false; + if (pplrLagging != pplrLaggingLast) { + // There is an abrupt change from lag to no lag or vice versa. + // Send a message so the client can change UI + + fSendNow = true; + + // If going into a lag state, mark in the players when this + // started, so it's easy to tell if a player is not responding + // to the LagNotify message + + playerMgr_.SetLastLagAcknowledge(base::GetTickCount()); + } else { + // Same lagging player as last time. Send occasionally so client has + // latest lag info. Send only if interval has expired. + + long64 tCurrent = base::GetTickCount(); + if (llabs(tCurrent - tLastLagNotify_) >= 50) { + tLastLagNotify_ = tCurrent; + fSendNow = true; + } + } + + // Send lag notification if necessary + + if (fSendNow) { + LagNotifyNetMessage lnnm; + lnnm.pidLagging = pidLagging_; + if (pplrLagging == NULL) { + lnnm.cSeconds = 0; + } else { + lnnm.cSeconds = (word)pplrLagging->GetLagTimeout(); + } + playerMgr_.Broadcast(&lnnm, lnnm.pidLagging); + + // At least one player needs to acknowledge the lag notification, or + // else the server will go on a killing spree (to prevent zombie + // endpoints and games). + + if (pplrLagging != NULL && pplrLagging->endpoint() != NULL) { + long64 tAcknowledge = playerMgr_.GetLastLagAcknowledge(); + long64 tCurrent = base::GetTickCount(); + if (tCurrent - tAcknowledge > kctLagAckKill) { + pplrLagging->endpoint()->DropGame(this, + knDisconnectReasonKilled); + } + } + } + + return pplrLagging != NULL; +} + +void Game::OnLagAcknowledge(Endpoint *endpoint) { + playerMgr_.SetLastLagAcknowledge(base::GetTickCount()); +} + +bool Game::ValidateWinStats(Endpoint *endpoint, WinStatsNetMessage *pnm) { + Player *pplrEndpoint = playerMgr_.GetPlayer(endpoint); + if (pplrEndpoint == NULL) { + return false; + } + + // Note: validate pnm->hash here, as needed to validate results sent + // from game. See game/Player.cpp. + + // All appears fine + return true; +} + +void Game::OnWinStats(Endpoint *endpoint, WinStatsNetMessage *pnm) { + LOG() << endpoint->name(); + Player *pplrEndpoint = playerMgr_.GetPlayer(endpoint); + if (pplrEndpoint == NULL) { + LOG() << base::Log::Format("0x%08lx ", endpoint) + << "Could not find player!"; + return; + } + + // When a client wins or loses, it sends stats for every player it knows + // about including computer players, which means every client is sending + // stats for every other client. The simple scenario is a two player game, + // where simultaneously one player wins, and one loses - the stats sent + // are consistent. If a player resigns in a 3 player game mid-way through, + // it will send stats but those stats will be stale by the time the game + // really ends. The server wants to record up to date stats, as much + // as possible, so clever updating is required. + if (!ValidateWinStats(endpoint, pnm)) { +#ifdef DEBUG_LOGGING + LOG() << base::Log::Format("0x%08lx ", endpoint) + << "WinStats are invalid! " << PszFromNetMessage(pnm); +#endif +#ifdef RELEASE_LOGGING + RLOG() << base::Log::Format("0x%08lx ", endpoint) + << "WinStats are invalid! username: " << endpoint->name() + << " ip address: " + << endpoint->xpump().socket()->GetRemoteAddress().ToString(); +#endif + return; + } + + // Find the player these stats are for + Player *pplrStats = playerMgr_.GetPlayerFromPid(pnm->pid); + if (pplrStats == NULL) { + return; + } + if (!(pplrStats->flags() & kfPlrInUse)) { + return; + } + + // Mark the game as ending when the first winner is announced + if (pnm->ws.ff & kfwsWinner) { + if (cSecsEnd_ == 0) { + cSecsEnd_ = base::GetSecondsUnixEpocUTC(); + } + } + + // If these stats are "owned" by the sender, treat them as + // authoritative, locking them in place. These come from + // human players. + if (pplrEndpoint == pplrStats) { + pplrStats->SaveWinStats(pnm->ws, true); + return; + } + + // Otherwise it's what one player thinks the stats are of another. + // Just save this as the most recent one. Since human players lock + // their own stats, this updates computer players (unless a human + // player lost network connection). + pplrStats->SaveWinStats(pnm->ws, false); + + // If this player is announcing win, allow other players to dispute it + if (pnm->ws.ff & kfwsWinner) { + CheckWinNetMessage cwnm; + cwnm.pid = pnm->pid; + playerMgr_.Broadcast(&cwnm); + } +} + +void Game::OnChallengeWin(Endpoint *endpoint) { + LOG() << base::Log::Format("0x%08lx ", endpoint) + << endpoint->name() << " challenges the win!"; + + Player *pplr = playerMgr_.GetPlayer(endpoint); + if (pplr == NULL) { + LOG() << base::Log::Format("0x%08lx ", endpoint) + << "Could not find player!"; + return; + } + +#if 0 +// Not implemented. + // An endpoint is challenging who the winner is, which will invalidate + // the winner. + pplr->SetFlags(pplr->flags() | kfPlrChallengeWinner); +#endif +} + +void Game::SendClientCommands() { + // Calculate when the client should next execute commands. If the commands + // are not there, the client will block. The server attempts to send the + // commands ahead of time, so they are there when the client needs them. + + // Update cUpdatesBlock_ + + msClock_ += cmsRate_; + long cmsUpdate = params_.tGameSpeed * 10; + long cUpdatesBlockNew = (msClock_ + cmsUpdate - 1) / cmsUpdate; + if (cUpdatesBlockNew == cUpdatesBlock_) { + return; + } + cUpdatesWaitSend_ = cUpdatesBlock_; + cUpdatesBlock_ = cUpdatesBlockNew; + msCommandsSent_ = base::GetMillisecondCount(); + + LOG() << "Sending ClientCommands: cUpdatesBlock_: " << cUpdatesBlock_; + + // Send UpdateNetMessage to client + int cmsg = cmds_.size(); + int cbUnm = sizeof(UpdateNetMessage) + ((cmsg - 1) * sizeof(Message)); + UpdateNetMessage *punm = (UpdateNetMessage *)new byte[cbUnm]; + if (punm == NULL) { + LOG() << base::Log::Format("0x%08lx ", this) + << "Allocation Error!"; + Dispose(); + return; + } + punm->nmid = knmidScUpdate; + punm->cb = cbUnm; + punm->cUpdatesBlock = cUpdatesBlock_; + punm->cUpdatesSync = cUpdatesSync_; + punm->cmsgCommands = cmsg; + Commands::const_iterator it = cmds_.begin(); + wi::Message *pcmds = punm->amsgCommands; + for (; it != cmds_.end(); it++) { + *pcmds++ = *it; + } + cmds_.clear(); + + // Send updates to all players with connections. + + Player *pplr = playerMgr_.GetNextPlayerWithEndpoint(NULL); + for (; pplr != NULL; pplr = playerMgr_.GetNextPlayerWithEndpoint(pplr)) { + pplr->endpoint()->xpump().Send(XMsgGameNetMessage::ToBuffer(punm)); + } + delete punm; +} + +void Game::OnKillLaggingPlayer(Endpoint *endpoint, + KillLaggingPlayerNetMessage *pnm) { + // If the player isn't lagging don't allow the kill + + Player *pplr = playerMgr_.GetPlayerFromPid(pnm->pid); + if (pplr == NULL || pplr->lag() != knLagKill) { + return; + } + + // Kill the player or reset the lag state for another timeout + + if (pnm->fYes != 0) { + if (pplr->endpoint() != NULL) { + pplr->endpoint()->DropGame(this, knDisconnectReasonNotResponding); + } + } else { + // Set player lag state back to guilty. This way this player gets to + // wait through the kill timeout again. + + pplr->SetLagState(knLagGuilty); + } +} + +void Game::OnClientCommands(Endpoint *endpoint, ClientCommandsNetMessage *pnm) { + // If not yet playing, this message means nothing + if (!playing_) { + RLOG() << base::Log::Format("0x%08lx ", endpoint) + << "ClientCommands received while not in game"; + return; + } + + // Queue the messages from the client. They'll be sent out + // on the next game timer most likely. + for (int i = 0; i < pnm->cmsgCommands; i++) { + cmds_.push_back(pnm->amsgCommands[i]); + } + + LOG() << base::Log::Format("0x%08lx seq: %d ccmds:%d", + endpoint, pnm->nSeq, pnm->cmsgCommands); + + // TODO: Validation +} + +void Game::OnUpdateResult(Endpoint *endpoint, UpdateResultNetMessage *pnm) { + // If sync error, ignore + if (syncerror_) { + return; + } + + // If not yet playing, this message means nothing + if (!playing_) { + RLOG() << base::Log::Format("0x%08lx ", endpoint) + << "UpdateResult received while not in game"; + return; + } + + Player *pplr = playerMgr_.GetPlayer(endpoint); + if (pplr == NULL) { + LOG() << base::Log::Format("0x%08lx ", endpoint) + << "Could not find player!"; + return; + } + + LOG() << "Received UpdateResult with cUpdatesBlock: " << + pnm->ur.cUpdatesBlock << " cmsLatency: " << pnm->ur.cmsLatency; + + LatencyRecord latr; + latr.cUpdatesBlock = pnm->ur.cUpdatesBlock; + latr.cmsLatency = pnm->ur.cmsLatency; + pplr->AddLatencyRecord(&latr); + + // Track the UpdateResult, in order to compare state hashes. If there + // is an error, inform the clients and stop the game. + + if (playerMgr_.CheckSyncError(pplr, pnm->ur, &cUpdatesSync_)) { + syncerror_ = true; + playerMgr_.BroadcastSyncError(pnm->ur); + return; + } +} + +void Game::OnPlayerResign(Endpoint *endpoint, PlayerResignNetMessage *pnm) { + // When the player resigns, the player can choose to continue to watch + // the game, so the connection must stay and receives updates. + Player *pplr = playerMgr_.GetPlayer(endpoint); + if (pplr != NULL) { + // Change to computer player and tell clients about this, but + // keep the connection. This way this client will continue to + // receive game updates, which will allow "observing". + + pplr->SetFlags(pplr->flags() | kfPlrComputer); + + // If the player has sent win stats already, then they aren't + // resigning, they are leaving the game. + if (pplr->statslocked()) { + QueuePlayerDisconnect(pplr->pid(), knDisconnectReasonLeftGame); + } else { + QueuePlayerDisconnect(pplr->pid(), knDisconnectReasonResign); + } + } +} + +void Game::QueuePlayerDisconnect(Pid pid, int nReason) { + if (!playing_) { + // Players don't disconnect this way when not playing + return; + } + + // Disconnects happen via a Message, so it is synched + // to the same update between clients via UpdateNetMessage. + wi::Message msg; + memset(&msg, 0, sizeof(msg)); + msg.mid = kmidPlayerDisconnect; + msg.PlayerDisconnect.pid = pid; + msg.PlayerDisconnect.nReason = (word)nReason; + cmds_.push_back(msg); +} + +int Game::GetNameCount() { + return playerMgr_.GetEndpointCount(); +} + +const PlayerName *Game::GetNames() { + return playerMgr_.GetEndpointNames(); +} + +std::vector Game::GetIdsString(Endpoint *endpoint) { + std::vector responses; + ConnectedList::iterator it = connected_.begin(); + for (; it != connected_.end(); it++) { + Player *pplr = playerMgr_.GetPlayer(*it); + if (pplr == NULL) { + continue; + } + if (endpoint->IsAdmin()) { + char ip[32]; + (*it)->xpump().socket()->GetRemoteAddress().IPAsString(ip, + sizeof(ip)); + responses.push_back(base::Format::ToString("%s: id %d ip %s\n", + (*it)->name(), server_.GetChatterId(endpoint, *it), ip)); + } else { + responses.push_back(base::Format::ToString("%s: id %d\n", + (*it)->name(), server_.GetChatterId(endpoint, *it))); + } + } + return responses; +} + +Player *Game::HumanPlayerFromColorChar(const char *psz) { + // 0: gray, 1: blue, 2: red, 3: yellow, 4: cyan + // Don't allow swapping gray because it is neutral. + + if (strlen(psz) == 0) { + return NULL; + } + + Player *pplr = NULL; + const char *s_ch2side = "\bbryc"; + Assert(strlen(s_ch2side) == kcSides); + for (int side = 0; s_ch2side[side] != 0; side++) { + if (s_ch2side[side] == psz[0]) { + pplr = playerMgr_.GetPlayer(side); + break; + } + } + if (pplr == NULL) { + return NULL; + } + if (pplr->endpoint() == NULL) { + return NULL; + } + if (pplr->flags() & (kfPlrComputer | kfPlrComputerOvermind | + kfPlrUnfulfilled | kfPlrObserver | kfPlrDisconnectBroadcasted)) { + return NULL; + } + if ((pplr->flags() & kfPlrInUse) == 0) { + return NULL; + } + return pplr; +} + +const char *Game::ColorFromSide(int side) { + // 0: gray, 1: blue, 2: red, 3: yellow, 4: cyan + static const char *s_colors[] = { + "neutral", "blue", "red", "yellow", "cyan" + }; + if (side < 0 || side >= ARRAYSIZE(s_colors)) { + return "unknown"; + } + return s_colors[side]; +} + +bool Game::IsSwapAllowed(Endpoint *endpoint) { + if (playing_) { + return false; + } + if (endpoint->id() != advertiserId_) { + return false; + } + return true; +} + +void Game::SwapPlayersCommand(Endpoint *endpoint, const char *chat, + std::string *response) { + if (playing_) { + *response = "Can't change sides after the game has begun."; + return; + } + + if (endpoint->id() != advertiserId_) { + *response = "Only the creator can issue this command."; + return; + } + + bool error = false; + std::string arg_a; + if (!endpoint->GetArgument(chat, 1, &arg_a)) { + error = true; + } + std::string arg_b; + if (!endpoint->GetArgument(chat, 2, &arg_b)) { + error = true; + } + Player *playerA = HumanPlayerFromColorChar(arg_a.c_str()); + Player *playerB = HumanPlayerFromColorChar(arg_b.c_str()); + if (playerA == NULL || playerB == NULL) { + error = true; + } + if (playerA == playerB) { + error = true; + } + if (error) { + *response = "This command takes two arguments of either b, r, y, or c, referring to the first letter of the color of each player."; + return; + } + + // Unset the ready state, so that players have to agree to the new + // arrangement, unless the player is the creator, who is already ready + // and literally doesn't have a ready button. + bool was_ready = false; + if ((playerA->flags() & (kfPlrCreator | kfPlrReady)) == kfPlrReady) { + was_ready = true; + playerA->SetFlags(playerA->flags() & ~kfPlrReady); + // HACK: this message is a hack-o-rama way to show the button again + NetMessage nmFail(knmidScBeginGameFail); + playerA->endpoint()->xpump().Send( + XMsgGameNetMessage::ToBuffer(&nmFail)); + } + if ((playerB->flags() & (kfPlrCreator | kfPlrReady)) == kfPlrReady) { + was_ready = true; + playerB->SetFlags(playerB->flags() & ~kfPlrReady); + // HACK: this message is a hack-o-rama way to show the button again + NetMessage nmFail(knmidScBeginGameFail); + playerB->endpoint()->xpump().Send( + XMsgGameNetMessage::ToBuffer(&nmFail)); + } + + // Swap player state. This also broadcasts the changes. + playerMgr_.SwapPlayers(playerA, playerB); + + // Use SendChat so that NULL can be sent for endpoint, to get gray text + if (!was_ready) { + SendChat(NULL, base::Format::ToString( + "Swapped colors. %s is %s, and %s is %s.", + playerA->name(), ColorFromSide(playerA->side()), + playerB->name(), ColorFromSide(playerB->side())), NULL); + } else { + // Make players press ready again, so the creator can't trick + // players into playing a certain color. + SendChat(NULL, base::Format::ToString( + "Swapped colors. %s is %s, and %s is %s. Players must press Ready again.", + playerA->name(), ColorFromSide(playerA->side()), + playerB->name(), ColorFromSide(playerB->side())), NULL); + } +} + +bool Game::ProcessCommand(Endpoint *endpoint, const char *chat, + std::string *response, bool *broadcast) { + + ModeratorCommand cmd = endpoint->GetModeratorCommand(chat); + if (cmd != kModeratorCommandNone) { + server_.logger().LogModCommand(endpoint, chat); + } + + switch (endpoint->GetModeratorCommand(chat)) { + case kModeratorCommandRules: + *response = endpoint->server().GetChatRules(); + *broadcast = true; + return true; + + case kModeratorCommandSwap: + SwapPlayersCommand(endpoint, chat, response); + return true; + + case kModeratorCommandNone: + return false; + + default: + break; + } + return true; +} + +void Game::SendChat(Endpoint *endpoint, const char *chat, + const char *unfiltered) { + bool broadcast = false; + std::string response; + if (endpoint != NULL && ProcessCommand(endpoint, chat, &response, + &broadcast)) { + if (!broadcast) { + if (response.size() != 0) { + endpoint->xpump().Send(XMsgGameReceiveChat::ToBuffer("", + response.c_str())); + server_.logger().LogSystemMsg(endpoint, response.c_str()); + } + return; + } + chat = response.c_str(); + } + + std::string from = ""; + if (endpoint != NULL) { + from = endpoint->GetChatName(); + } + + Room *room = server_.lobby().FindRoom(roomid_); + const char *pszRoomName = NULL; + bool private_room = false; + if (room != NULL) { + pszRoomName = room->name(); + private_room = (room->password()[0] != 0); + } + + if (unfiltered != NULL) { + server_.logger().LogGameChat(endpoint, roomid_, pszRoomName, + private_room, id(), info().title(), unfiltered); + } else { + server_.logger().LogGameChat(endpoint, roomid_, pszRoomName, + private_room, id(), info().title(), chat); + } + + ConnectedList::iterator it = connected_.begin(); + for (; it != connected_.end(); it++) { + Player *pplr = playerMgr_.GetPlayer(*it); + if (pplr == NULL) { + continue; + } + if ((*it)->muted()) { + continue; + } + (*it)->xpump().Send(XMsgGameReceiveChat::ToBuffer(from.c_str(), chat)); + if (unfiltered != NULL && (*it)->seechat()) { + (*it)->xpump().Send(XMsgGameReceiveChat::ToBuffer("", unfiltered)); + } + } +} + +void Game::SendAdminChat(const char *name, const char *chat, bool mods_only) { + ConnectedList::iterator it = connected_.begin(); + for (; it != connected_.end(); it++) { + Player *pplr = playerMgr_.GetPlayer(*it); + if (pplr == NULL) { + continue; + } + if (mods_only && !(*it)->IsModerator()) { + continue; + } + (*it)->xpump().Send(XMsgGameReceiveChat::ToBuffer(name, chat)); + } +} + +bool Game::ToggleAnonBlock(Endpoint *endpoint) { + if (!IsAnonBlockAllowed(endpoint)) { + return false; + } + + ff_ ^= kfGameAnonBlock; + if ((ff_ & kfGameAnonBlock) == 0) { + return true; + } + + // Remove all endpoints that are anons + while (true) { + bool dropped = false; + ConnectedList::iterator it = connected_.begin(); + for (; it != connected_.end(); it++) { + if ((*it)->anonymous()) { + (*it)->DropGame(this, knDisconnectReasonKilled); + dropped = true; + break; + } + } + if (!dropped) { + break; + } + } + return true; +} + +bool Game::IsAnonBlockAllowed(Endpoint *endpoint) { + if (endpoint->anonymous()) { + return false; + } + if (playing_) { + return false; + } + if (endpoint->id() != advertiserId_) { + return false; + } + + // For now don't allow toggling. Only set once to prevent abuse + if ((ff_ & kfGameAnonBlock) != 0) { + return false; + } + + return true; +} + +} // namespace wi diff --git a/server/game.h b/server/game.h new file mode 100644 index 0000000..7b7e88e --- /dev/null +++ b/server/game.h @@ -0,0 +1,137 @@ +#ifndef __GAME_H__ +#define __GAME_H__ + +#include "mpshared/mpht.h" +#include "mpshared/xpump.h" +#include "server/endpoint.h" +#include "server/playermgr.h" +#include "server/player.h" +#include "server/levelinfo.h" +#include "base/messagequeue.h" +#include "base/sigslot.h" +#include +#include +#include + +namespace wi { + +class Endpoint; + +struct GameStats { + dword server_id; + dword server_start; + dword gameid; + GameParams params; + LevelInfo info; + dword start_utc; + dword end_utc; + int player_count; + PlayerStats player_stats[kcPlayersMax]; +}; + +const dword kfGameDontPostWinStats = 1; +const dword kfGameAnonBlock = 2; + +class Game : public base::MessageHandler, public base::has_slots<> { +public: + Game(Endpoint *endpoint, const GameParams& params, const LevelInfo& info, + Server& server, dword roomid, dword ff); + ~Game(); + + dword id() { return id_; } + const GameParams& params() { return params_; } + bool playing() { return playing_; } + const LevelInfo& info() { return info_; } + dword roomid() { return roomid_; } + const char *creator() { return creator_; } + bool anonblock() { return (ff_ & kfGameAnonBlock) != 0; } + bool ToggleAnonBlock(Endpoint *endpoint); + bool IsAnonBlockAllowed(Endpoint *endpoint); + bool IsSwapAllowed(Endpoint *endpoint); + + dword CanAddPlayer(Endpoint *endpoint); + void AddPlayer(Endpoint *endpoint); + void RemovePlayer(Endpoint *endpoint, int nReason, bool disconnect = true); + void SendChat(Endpoint *endpoint, const char *chat, + const char *unfiltered = NULL); + void SendAdminChat(const char *name, const char *chat, + bool mods_only = false); + void OnNetMessage(Endpoint *endpoint, NetMessage *pnm); + int GetNameCount(); + const PlayerName *GetNames(); + bool HasHeartbeat(); + void OnHeartbeat(); + std::vector GetIdsString(Endpoint *endpoint); + void KickPlayers(); + + base::signal1 SignalOnDelete; + base::signal1 SignalOnInProgress; + base::signal1 SignalOnPlayersChange; + +private: + void OnPlayersChange(); + void OnClientCommands(Endpoint *endpoint, ClientCommandsNetMessage *pnm); + void OnClientReady(Endpoint *endpoint, NetMessage *pnm); + void OnPlayerJoin(Endpoint *endpoint, PlayerJoinNetMessage *pnm); + void OnUpdateResult(Endpoint *endpoint, UpdateResultNetMessage *pnm); + void OnPlayerResign(Endpoint *endpoint, PlayerResignNetMessage *pnm); + void OnRequestBeginGame(Endpoint *endpoint, NetMessage *pnm); + void OnKillLaggingPlayer(Endpoint *endpoint, + KillLaggingPlayerNetMessage *pnm); + void OnEndpointDelete(Endpoint *endpoint); + void OnAdvertiserDelete(Endpoint *endpoint); + void OnLagAcknowledge(Endpoint *endpoint); + void OnWinStats(Endpoint *endpoint, WinStatsNetMessage *pnm); + void OnChallengeWin(Endpoint *endpoint); + void OnTimer(); + bool StartWait(); + void StartTiming(); + bool LagWait(long cUpdatesOldest); + void SendClientCommands(); + dword NewGameId(); + void QueuePlayerDisconnect(Pid pid, int nReason); + void PostWinStats(); + bool ValidateWinStats(Endpoint *endpoint, WinStatsNetMessage *pnm); + bool ProcessCommand(Endpoint *endpoint, const char *chat, + std::string *response, bool *broadcast); + const char *ColorFromSide(int side); + Player *HumanPlayerFromColorChar(const char *psz); + void SwapPlayersCommand(Endpoint *endpoint, const char *chat, + std::string *response); + + dword id_; + GameParams params_; + LevelInfo info_; + dword roomid_; + typedef std::vector ConnectedList; + ConnectedList connected_; + PlayerMgr playerMgr_; + long cSecsStart_; + long cSecsEnd_; + long64 tCreated_; + long64 tLastLagNotify_; + long64 tStartTimeout_; + Server& server_; + bool playing_; + long cUpdatesBlock_; + long cUpdatesWaitSend_; + long cUpdatesSync_; + long msClock_; + long cmsRate_; + long64 msCommandsSent_; + long cUpdatesAllowedLag_; + Pid pidLagging_; + bool syncerror_; + Endpoint *advertiser_; + dword advertiserId_; + char creator_[kcbPlayerName]; + dword ff_; + + typedef std::vector Commands; + Commands cmds_; + static dword s_gameidCounter_; +}; + +} // namespace wi + +#endif // __GAME_H__ diff --git a/server/httppost.cpp b/server/httppost.cpp new file mode 100644 index 0000000..dbfe518 --- /dev/null +++ b/server/httppost.cpp @@ -0,0 +1,292 @@ +#include "server/httppost.h" +#include "mpshared/misc.h" + +// A "simple" asynchronous http POST helper. Asynchronous except for the +// dns resolution of the address. +// +// Example protocol sniff using this class to Google app engine +// +// Client to server: +// +// POST /api/posttest HTTP/1.0\r\n +// Accept-Encoding: identity\r\n +// Content-Length: 11\r\n +// Host: 127.0.0.1:8080\r\n +// Content-Type: binary/octet-stream\r\n +// Connection: close\r\n +// User-Agent: httppost/1.0\r\n +// \r\n +// hello world +// +// Server to client: +// +// HTTP/1.0 200 OK\r\n +// Server: Development/1.0\r\n +// Date: Tue, 16 Jun 2009 22:53:17 GMT\r\n +// Cache-Control: no-cache\r\n +// Content-Type: binary/octet-stream\r\n +// Content-Length: 11\r\n +// \r\n +// hello world + +namespace wi { + +const int MSG_POSTCOMPLETE = 1; +const int MSG_TIMEOUT = 2; + +HttpPost::HttpPost(base::SocketAddress& address, const std::string& path, + base::ByteBuffer *body) : address_(address), s_(NULL), + state_(HS_CLOSED), completed_(false), attempts_(0), written_(0) { + WrapBody(path, body); +} + +HttpPost::~HttpPost() { + delete s_; +} + +void HttpPost::SetState(State state) { +#if 0 + LOG() << base::Log::Format("0x%08lx ", this) + << "From: " << HsLabels.Find(state_) + << " To: " << HsLabels.Find(state); +#endif + state_ = state; +} + +void HttpPost::WrapBody(const std::string& path, base::ByteBuffer *body) { + write_.WriteString(base::Format::ToString("POST %s HTTP/1.0\r\n", + path.c_str()), false); + write_.WriteString("Accept-Encoding: identity\r\n", false); + write_.WriteString(base::Format::ToString("Content-Length: %d\r\n", + body->Length()), false); + write_.WriteString(base::Format::ToString("Host: %s\r\n", + address_.ToString()), false); + write_.WriteString("Content-Type: binary/octet-stream\r\n", false); + write_.WriteString("Connection: close\r\n", false); + write_.WriteString("User-Agent: httppost/1.0\r\n", false); + write_.WriteString("\r\n", false); + write_.WriteBytes(body->Data(), body->Length()); + delete body; +} + +void HttpPost::Submit(int seconds_timeout) { + if (!thread_.is_current()) { + return; + } + if (state_ != HS_CLOSED) { + return; + } + + written_ = 0; + completed_ = false; + attempts_++; + + base::Socket *s = thread_.ss().CreateSocket(SOCK_STREAM, this); + if (s == NULL) { + PostComplete(0, 0); + return; + } + s_ = s; + + // Force dns resolution. If this this request has been around for awhile + // we don't want to use stale ip + address_.Resolve(true); + + if (s->Connect(address_) < 0) { + if (!s->IsBlocking()) { + LOG() << "Connect(" << address_.ToString() << ") failed error: " << + base::Socket::GetErrorString(s->GetError()); + PostComplete(0, s->GetError()); + return; + } + } + + // Set timeout + thread_.PostDelayed(MSG_TIMEOUT, this, seconds_timeout * 100); + + SetState(HS_CONNECTING); +} + +const char *HttpPost::GetLine(const char *current, const char *end, + char *line, int cb) { + for (const char *pch = current; pch < end - 1; pch++) { + if (pch[0] == '\r' && pch[1] == '\n') { + int cbT = pch - current + 1; + if (cbT > cb) { + cbT = cb; + } + strncpyz(line, current, cbT); + return pch + 2; + } + } + return NULL; +} + +bool HttpPost::FindStatusCode(int *status_code) { + // Look for an empty line with \r\n - this marks the end of the headers + const char *current = (const char *)read_.Data(); + const char *end = (const char *)read_.Data() + read_.Length(); + bool found = false; + int cbBody = -1; + char line[512]; + while ((current = GetLine(current, end, line, sizeof(line))) != NULL) { + int cch = strlen(line); + if (cch >= 15) { + if (strncmp(line, "Content-Length:", 15) == 0) { + base::Format::ToInteger(&line[15], 10, &cbBody); + } + } + if (strlen(line) == 0) { + found = true; + break; + } + } + + // If not at end of headers, continue reading + if (!found) { + return false; + } + + // If there is a Content-Length, see if that many body bytes have been + // read yet. If not, continue reading. + if (cbBody != -1) { + if (((const char *)(read_.Data() + read_.Length()) - current) < + cbBody) { + return false; + } else { + if (result_.Length() == 0 && cbBody != 0) { + result_.WriteBytes((const byte *)current, cbBody); + } + } + } + + // Extract the status code + if (GetLine((const char *)read_.Data(), end, line, sizeof(line)) == NULL) { + return false; + } + + // Find the status code + end = &line[strlen(line)]; + for (current = line; current < end; current++) { + if (*current == ' ' && *(current + 1) >= '0' && *(current + 1) <= '9') { + current++; + break; + } + } + + char szT[32]; + szT[0] = 0; + for (int i = 0; i < sizeof(szT) - 1; i++) { + if (current[i] >= '0' && current[i] <= '9') { + szT[i] = current[i]; + szT[i + 1] = 0; + } + } + + *status_code = 0; + base::Format::ToInteger(szT, 10, status_code); + + return strlen(szT) != 0; +} + +void HttpPost::ProcessRead() { + if (state_ != HS_SENT && state_ != HS_READ) { + return; + } + SetState(HS_READ); + + while (true) { + byte buff[4096]; + int cb = s_->Recv(buff, sizeof(buff)); + if (cb < 0) { + if (s_->IsBlocking()) { + break; + } + PostComplete(0, s_->GetError()); + return; + } + read_.WriteBytes(buff, cb); + } + + // Check for status code. If present, close the connection. + int status_code; + if (FindStatusCode(&status_code)) { + PostComplete(status_code, 0); + } +} + +void HttpPost::ProcessWrite() { + if (state_ != HS_CONNECTED && state_ != HS_WRITE) { + return; + } + SetState(HS_WRITE); + + while (written_ < write_.Length()) { + int remaining = write_.Length() - written_; + int cb = s_->Send(write_.Data() + written_, remaining); + if (cb < 0) { + if (s_->IsBlocking()) { + return; + } + PostComplete(0, s_->GetError()); + return; + } + written_ += cb; + } + + SetState(HS_SENT); + ProcessRead(); +} + +void HttpPost::OnConnectEvent(base::Socket *socket) { + SetState(HS_CONNECTED); + ProcessWrite(); +} + +void HttpPost::OnReadEvent(base::Socket *socket) { + ProcessRead(); +} + +void HttpPost::OnWriteEvent(base::Socket *socket) { + ProcessWrite(); +} + +void HttpPost::OnCloseEvent(base::Socket *socket) { + PostComplete(0, 0); +} + +void HttpPost::PostComplete(int status_code, int error) { + base::Message msg; + msg.id = MSG_POSTCOMPLETE; + msg.handler = this; + msg.data = new CompleteParams(status_code, error); + thread_.Post(&msg); +} + +void HttpPost::OnMessage(base::Message *pmsg) { + switch (pmsg->id) { + case MSG_POSTCOMPLETE: + thread_.Clear(this, MSG_TIMEOUT); + if (!completed_) { + completed_ = true; + delete s_; + s_ = NULL; + SetState(HS_CLOSED); + CompleteParams *params = (CompleteParams *)pmsg->data; + if (params->status_code != 200) { + RLOG() << "post: " << (dword)this << " status_code: " + << params->status_code << " error: " << params->error; + } + SignalOnComplete(this, params->status_code, params->error, result_); + delete params; + } + break; + + case MSG_TIMEOUT: + PostComplete(0, 0); + break; + } +} + +} // namespace wi + diff --git a/server/httppost.h b/server/httppost.h new file mode 100644 index 0000000..0718082 --- /dev/null +++ b/server/httppost.h @@ -0,0 +1,83 @@ +#ifndef __HTTPPOST_H__ +#define __HTTPPOST_H__ + +#include "base/sigslot.h" +#include "inc/basictypes.h" +#include "base/bytebuffer.h" +#include "base/messagehandler.h" +#include "base/thread.h" +#include "base/misc.h" +#include "base/socket.h" +#include "base/socketaddress.h" +#include "base/bytebuffer.h" +#include + +namespace wi { + +struct CompleteParams : base::MessageData { + CompleteParams(int status_code, int error) : status_code(status_code), + error(error) { } + int status_code; + int error; +}; + +class HttpPost : base::MessageHandler, base::SocketNotify { +public: + HttpPost(base::SocketAddress& address, const std::string& path, + base::ByteBuffer *body); + ~HttpPost(); + + void Submit(int seconds_timeout = 60); + int attempts() { return attempts_; } + + // this, status code, error code + base::signal4 SignalOnComplete; + + enum State { + HS_CLOSED, HS_CONNECTING, HS_CONNECTED, HS_WRITE, HS_SENT, HS_READ + }; + +private: + void SetState(State state); + void WrapBody(const std::string& path, base::ByteBuffer *body); + bool FindStatusCode(int *status_code); + void ProcessRead(); + void ProcessWrite(); + void PostComplete(int status_code, int error); + const char *GetLine(const char *current, const char *end, char *line, + int cb); + + // SocketNotify + virtual void OnConnectEvent(base::Socket *socket); + virtual void OnReadEvent(base::Socket *socket); + virtual void OnWriteEvent(base::Socket *socket); + virtual void OnCloseEvent(base::Socket *socket); + + // MessageHandler interface + virtual void OnMessage(base::Message *pmsg); + + State state_; + base::Socket *s_; + base::SocketAddress address_; + base::ByteBuffer write_; + base::ByteBuffer read_; + base::ByteBuffer result_; + bool completed_; + int attempts_; + int written_; +}; + +STARTLABEL(HsLabels) + LABEL(HttpPost::HS_CLOSED) + LABEL(HttpPost::HS_CONNECTING) + LABEL(HttpPost::HS_CONNECTED) + LABEL(HttpPost::HS_WRITE) + LABEL(HttpPost::HS_SENT) + LABEL(HttpPost::HS_READ) +ENDLABEL(HsLabels) + +} // namespace wi + +#endif // __HTTPPOST_H__ + diff --git a/server/ids.h b/server/ids.h new file mode 100644 index 0000000..fa1f9f3 --- /dev/null +++ b/server/ids.h @@ -0,0 +1,16 @@ +#ifndef __IDS_H__ +#define __IDS_H__ + +namespace wi { + +const int kidmGameTimer = 1; +const int kidmPreGameTimer = 2; + +STARTLABEL(MsgLabels) + LABEL(kidmGameTimer) + LABEL(kidmPreGameTimer) +ENDLABEL(MsgLabels) + +} // namespace wi + +#endif // __IDS_H__ diff --git a/server/levelinfo.cpp b/server/levelinfo.cpp new file mode 100644 index 0000000..0e6201e --- /dev/null +++ b/server/levelinfo.cpp @@ -0,0 +1,66 @@ +#include "server/levelinfo.h" +#include "mpshared/ini.h" +#include "mpshared/misc.h" +#include "inc/rip.h" +#include "base/log.h" + +namespace wi { + +bool LevelInfo::Load(PackFileReader& pak, const char *file) { + IniReader *pini = LoadIniFile(pak, file); + + // Get level version + if (pini->GetPropertyValue("General", "Version", "%d", &version_) == 0) { + version_ = 0; + } + + // Can't run if the level version is newer than what the game supports + if (version_ > knVersionLevelSupported) { + delete pini; + return false; + } + + // Load General level data + if (!pini->GetPropertyValue("General", "Title", title_, sizeof(title_))) { + strcpy(title_, ""); + } + if (!pini->GetPropertyValue("General", "MinPlayers", "%d", &minplayers_)) { + delete pini; + return false; + } + if (!pini->GetPropertyValue("General", "MaxPlayers", "%d", &maxplayers_)) { + delete pini; + return false; + } + + // Read in the intelligence for each side + for (Side side = ksideNeutral; side < kcSides; side++) { + char name[16]; + if (side == ksideNeutral) { + strcpy(name, "sideNeutral"); + } else { + strcpy(name, "side0"); + name[4] = '0' + side; + } + intelligences_[side] = knIntelligenceComputer; + pini->GetPropertyValue(name, "Intelligence", "%d", + &intelligences_[side]); + } + delete pini; + + // If there is no human side, assume it is side 1 + bool fHumanFound = false; + for (Side side = ksideNeutral; side < kcSides; side++) { + if (intelligences_[side] == knIntelligenceHuman) { + fHumanFound = true; + } + } + if (!fHumanFound) { + intelligences_[kside1] = knIntelligenceHuman; + } + + strncpyz(filename_, file, sizeof(filename_)); + return true; +} + +} // namespace wi diff --git a/server/levelinfo.h b/server/levelinfo.h new file mode 100644 index 0000000..fdf2bd7 --- /dev/null +++ b/server/levelinfo.h @@ -0,0 +1,47 @@ +#ifndef __LEVELINFO_H__ +#define __LEVELINFO_H__ + +#include "mpshared/mpht.h" +#include "server/ncpackfile.h" +#include "inc/basictypes.h" +#include + +namespace wi { + +class LevelInfo { +public: + LevelInfo() { + Clear(); + } + + void Clear() { + memset(this, 0, sizeof(*this)); + } + + bool Load(PackFileReader& pak, const char *file); + + const char *title() const { return title_; } + const char *filename() const { return filename_; } + int minplayers() const { return minplayers_; } + int maxplayers() const { return maxplayers_; } + int version() const { return version_; } + int GetIntelligence(Side side) const { + if (side >= ARRAYSIZE(intelligences_)) { + return 0; + } + return intelligences_[side]; + } + +private: + char title_[kcbLevelTitle]; + char filename_[kcbFilename]; + int minplayers_; + int maxplayers_; + int version_; + dword revision_; + int intelligences_[5]; +}; + +} // namespace wi + +#endif // __LEVELINFO_H__ diff --git a/server/levelinfocache.cpp b/server/levelinfocache.cpp new file mode 100644 index 0000000..62c63bd --- /dev/null +++ b/server/levelinfocache.cpp @@ -0,0 +1,128 @@ +#include "server/levelinfocache.h" +#include "server/ncpackfile.h" +#include "mpshared/indexloader.h" +#include "base/log.h" + +namespace wi { + +void LevelInfoCache::SubmitIndex(const std::string& indexfile) { + IndexLoader index; + if (index.InitFromFile(indexfile.c_str())) { + int c = index.GetCount(); + for (int i = 0; i < c; i++) { + const IndexEntry *entry = index.GetEntry(i); + SubmitHelper(entry->packid); + } + } +} + +bool LevelInfoCache::SubmitHelper(const PackId& packid) { + bool success = false; + NoCachePackFileReader pakr; + if (packm_.Mount(pakr, &packid)) { + success = Submit(pakr, packid); + packm_.Unmount(pakr, &packid); + } + return success; +} + +bool LevelInfoCache::Submit(PackFileReader& pakr, const PackId& packid) { + // Create new infomap before removing old (if it exists), in case invalid + LevelInfoMap infomap; + CreateLevelInfoMap(pakr, packid, &infomap); + if (infomap.size() == 0) { + return false; + } + // Remove the old pack, if present, then add this pack + PackMap::iterator it = map_.find(packid.id); + if (it != map_.end()) { + map_.erase(it); + } + map_.insert(PackMap::value_type(packid.id, + std::make_pair(packid, infomap))); + RLOG() << "Added: " << PackManager::GetPackFilename(&packid); + + return true; +} + +void LevelInfoCache::CreateLevelInfoMap(PackFileReader& pakr, + const PackId& packid, LevelInfoMap *infomap) { + Enum enm; + char szFile[256]; + while (pakr.EnumFiles(&enm, PACKENUM_LAST, szFile, sizeof(szFile))) { + if (strlen(szFile) <= 4) { + continue; + } + if (strcmp(&szFile[strlen(szFile) - 4], ".lvl") != 0) { + continue; + } + LevelInfo info; + if (!info.Load(pakr, szFile)) { + continue; + } + infomap->insert(LevelInfoMap::value_type(std::string(szFile), info)); + } +} + +int LevelInfoCache::FindInfo(const PackId& packid, const std::string& level, + LevelInfo *pinfo, PackId *ppackidUpgrade) { + if (packid.id != PACKID_MAIN) { + switch (packm_.IsInstalled(&packid, ppackidUpgrade)) { + case 0: + // Don't know about this pack at all + return 0; + + case 1: + // Know about this pack, and have this version + break; + + case 2: + // Know about this pack, but client has wrong version + return 2; + } + } + + // Lazy load the LevelInfo if necessary + PackMap::iterator it0 = map_.find(packid.id); + if (it0 == map_.end()) { + if (!SubmitHelper(packid)) { + return 0; + } + it0 = map_.find(packid.id); + if (it0 == map_.end()) { + return 0; + } + } + + // Same packid? If not, re-submit + if (memcmp(&it0->second.first, &packid, sizeof(packid)) != 0) { + if (!SubmitHelper(packid)) { + return 0; + } + it0 = map_.find(packid.id); + if (it0 == map_.end()) { + return 0; + } + } + + LevelInfoMap::iterator it1 = it0->second.second.find(level); + if (it1 == it0->second.second.end()) { + return 0; + } + + if (pinfo != NULL) { + *pinfo = it1->second; + } + return 1; +} + +void LevelInfoCache::RemoveInfo(const PackId& packid) { + PackMap::iterator it = map_.find(packid.id); + if (it != map_.end()) { + if (memcmp(&it->second.first, &packid, sizeof(packid)) == 0) { + map_.erase(it); + } + } +} + +} // namespace wi diff --git a/server/levelinfocache.h b/server/levelinfocache.h new file mode 100644 index 0000000..dc60abd --- /dev/null +++ b/server/levelinfocache.h @@ -0,0 +1,41 @@ +#ifndef __LEVELINFOCACHE_H__ +#define __LEVELINFOCACHE_H__ + +#include "inc/basictypes.h" +#include "server/levelinfo.h" +#include "mpshared/packmanager.h" +#include "mpshared/mpht.h" +#include +#include +#include + +namespace wi { + +typedef std::map LevelInfoMap; +typedef std::map > PackMap; + +class LevelInfoCache +{ +public: + LevelInfoCache(PackManager& packm) : packm_(packm) {} + + bool Submit(PackFileReader& pakr, const PackId& packid); + bool SubmitHelper(const PackId& packid); + void SubmitIndex(const std::string& indexfile); + int FindInfo(const PackId& packid, const std::string& level, + LevelInfo *pinfo, PackId *ppackidUpgrade = NULL); + void RemoveInfo(const PackId& packid); + bool empty() { return map_.empty(); } + +private: + void BuildCache(); + void CreateLevelInfoMap(PackFileReader& pakr, const PackId& packid, + LevelInfoMap *infomap); + + PackMap map_; + PackManager& packm_; +}; + +} // namespace wi + +#endif // __LEVELINFOCACHE_H__ diff --git a/server/lobby.cpp b/server/lobby.cpp new file mode 100644 index 0000000..b99a584 --- /dev/null +++ b/server/lobby.cpp @@ -0,0 +1,258 @@ +#include "server/lobby.h" +#include "server/server.h" +#include "base/thread.h" +#include "base/tick.h" +#include + +namespace wi { + +const dword kidmRoomHeartbeat = 1; + +#define kctRoomHeartbeat (30 * 100) + +Lobby::Lobby(Server *server, int max_rooms, int max_games_per_room, + int max_players_per_room) : server_(server), max_rooms_(max_rooms), + max_games_per_room_(max_games_per_room), + max_players_per_room_(max_players_per_room) { + thread_.PostTimer(kidmRoomHeartbeat, this, kctRoomHeartbeat); +} + +Lobby::~Lobby() { +} + +bool Lobby::CanEnter(Endpoint *endpoint) { + return true; +} + +void Lobby::Enter(Endpoint *endpoint) { + endpointmap_.insert(EndpointMap::value_type(endpoint->id(), endpoint)); + endpoint->SignalOnDelete.connect(this, &Lobby::OnEndpointDelete); + + // Tell this endpoint about all the rooms + RoomMap::iterator it = roommap_.begin(); + for (; it != roommap_.end(); it++) { + Room *room = it->second; + endpoint->xpump().Send(XMsgLobbyAddRoom::ToBuffer(room->name(), + room->id(), room->password()[0] != 0, room->GetPlayerCount(), + room->cGames())); + } + + // Broadcast lurker count + Broadcast(XMsgLobbyLurkerCount::ToBuffer(endpointmap_.size())); + LOG() << endpointmap_.size() << " endpoints."; +} + +void Lobby::Leave(Endpoint *endpoint, bool disconnect) { + EndpointMap::iterator it = endpointmap_.find(endpoint->id()); + if (it == endpointmap_.end()) { + LOG() << "couldn't find endpoint."; + return; + } + endpointmap_.erase(it); + if (disconnect) { + endpoint->SignalOnDelete.disconnect(this); + } + + // Broadcast lurker count + Broadcast(XMsgLobbyLurkerCount::ToBuffer(endpointmap_.size())); + LOG() << endpointmap_.size() << " endpoints."; +} + +void Lobby::OnEndpointDelete(Endpoint *endpoint) { + LOG() << "leaving " << endpoint->name(); + Leave(endpoint, false); +} + +Room *Lobby::FindRoom(dword roomid) { + // Return the room if it's found and active (not in a disposed state) + + RoomMap::iterator it = roommap_.find(roomid); + if (it == roommap_.end()) { + return NULL; + } + Room *room = it->second; + if (room->active()) { + return room; + } + return NULL; +} + +Room *Lobby::NewRoom(Endpoint *endpoint, const char *name, + const char *password, dword roomid, dword ff, dword *result) { + // Default result + *result = knLobbyCreateRoomResultFail; + + // Anon's can't create rooms anymore because of the abuse + if (endpoint != NULL && !endpoint->IsAdmin() && endpoint->anonymous()) { + *result = knLobbyCreateRoomResultFail; + return NULL; + } + + std::string cleanname; + if (endpoint != NULL) { + cleanname = StripWhitespace(endpoint->server().badwords().Filter(name)); + } else { + cleanname = StripWhitespace(name); + } + if (strlen(cleanname.c_str()) == 0) { + *result = knLobbyCreateRoomResultFail; + return NULL; + } + + // See if a room exists already with this name + RoomMap::iterator it = roommap_.begin(); + for (; it != roommap_.end(); it++) { + if (strcmp(it->second->name(), cleanname.c_str()) == 0) { + *result = knLobbyCreateRoomResultExists; + return NULL; + } + } + + // Allow a fixed number of rooms + if (roommap_.size() >= max_rooms_) { + *result = knLobbyCreateRoomResultFull; + return NULL; + } + + // User can't create rooms? + if (endpoint != NULL) { + long64 tExpires; + if (endpoint->FindTracker(room_tracker_, &tExpires)) { + if (base::GetMillisecondCount() < tExpires) { + *result = knLobbyCreateRoomResultFail; + return NULL; + } + endpoint->RemoveTracker(room_tracker_); + } + } + + // No room exists by this name; create and return it + Room *room = new Room(server_, endpoint, cleanname.c_str(), password, + roomid, max_games_per_room_, max_players_per_room_, ff); + roommap_.insert(RoomMap::value_type(room->id(), room)); + + // Listen for these signals from the room. These get auto-deleted + // when the room goes away. + room->SignalOnDelete.connect(this, &Lobby::OnRoomDelete); + room->SignalOnPlayersChange.connect(this, &Lobby::OnPlayersChange); + room->SignalOnGamesChange.connect(this, &Lobby::OnGamesChange); + + // Tell the endpoints about this new room + Broadcast(XMsgLobbyAddRoom::ToBuffer(cleanname.c_str(), room->id(), + password[0] != 0, room->GetPlayerCount(), room->cGames())); + + *result = knLobbyCreateRoomResultSuccess; + return room; +} + +std::vector Lobby::GetRoomIds() { + std::vector ids; + RoomMap::iterator it = roommap_.begin(); + for (; it != roommap_.end(); it++) { + ids.push_back(it->first); + } + return ids; +} + +void Lobby::SendAdminChat(const char *name, const char *chat, bool mods_only) { + LOG() << "name: " << name << ", chat: " << chat; + RoomMap::iterator it = roommap_.begin(); + for (; it != roommap_.end(); it++) { + it->second->SendAdminChat(name, chat, mods_only); + } +} + +std::string Lobby::StripWhitespace(const char *name) { + // Strip whitespace from the start and end. + int cchName = strlen(name); + const char *start = name; + for (; start < &name[cchName]; start++) { + if (!isspace(*start)) { + break; + } + } + const char *end = &name[cchName - 1]; + for (; end > start; end--) { + if (!isspace(*end)) { + break; + } + } + if (end < start) { + return ""; + } + return std::string(start, end - start + 1); +} + +void Lobby::OnRoomDelete(Room *room) { + LOG() << "removing room " << room->name(); + RoomMap::iterator it = roommap_.find(room->id()); + if (it == roommap_.end()) { + RLOG() << "couldn't find room."; + return; + } + roommap_.erase(it); + LOG() << roommap_.size() << " rooms."; + + // Tell the endpoints about this room remove + Broadcast(XMsgLobbyRemoveRoom::ToBuffer(room->id())); +} + +void Lobby::OnPlayersChange(Room *room) { + Broadcast(XMsgLobbyUpdateRoom::ToBuffer(room->id(), + room->GetPlayerCount(), room->cGames())); +} + +void Lobby::OnGamesChange(Room *room) { + Broadcast(XMsgLobbyUpdateRoom::ToBuffer(room->id(), + room->GetPlayerCount(), room->cGames())); +} + +void Lobby::OnMessage(base::Message *pmsg) { + if (pmsg->id != kidmRoomHeartbeat) { + return; + } + + RoomMap::iterator it = roommap_.begin(); + while (it != roommap_.end()) { + if (it->second->HasHeartbeat()) { + it->second->OnHeartbeat(); + } + it++; + } +} + +void Lobby::Broadcast(base::ByteBuffer *bb) { + EndpointMap::iterator it = endpointmap_.begin(); + for (int i = 0; it != endpointmap_.end(); it++, i++) { + Endpoint *endpoint = it->second; + base::ByteBuffer *bbNext = NULL; + if (i < endpointmap_.size() - 1) { + bbNext = bb->Clone(); + } + endpoint->xpump().Send(bb); + bb = bbNext; + } +} + +std::vector Lobby::GetIdsString(Endpoint *endpoint) { + std::vector responses; + EndpointMap::iterator it = endpointmap_.begin(); + for (; it != endpointmap_.end(); it++) { + if (endpoint->IsAdmin()) { + char ip[32]; + it->second->xpump().socket()-> + GetRemoteAddress().IPAsString(ip, sizeof(ip)); + responses.push_back(base::Format::ToString("%s: id %d ip %s", + it->second->name(), + it->second->server().GetChatterId(endpoint, it->second), + ip)); + } else { + responses.push_back(base::Format::ToString("%s: id %d", + it->second->name(), + it->second->server().GetChatterId(endpoint, it->second))); + } + } + return responses; +} + +} diff --git a/server/lobby.h b/server/lobby.h new file mode 100644 index 0000000..87605f5 --- /dev/null +++ b/server/lobby.h @@ -0,0 +1,66 @@ +#ifndef __LOBBY_H__ +#define __LOBBY_H__ + +#include "inc/basictypes.h" +#include "server/endpoint.h" +#include "server/room.h" +#include "server/tracker.h" +#include "base/bytebuffer.h" +#include "base/messagehandler.h" +#include "base/sigslot.h" +#include +#include +#include + +namespace wi { + +struct RoomSorter { + bool operator()(dword a, dword b) const { + return a < b; + } +}; + +class Server; +class Lobby : base::MessageHandler, public base::has_slots<> { +public: + Lobby(Server *server, int max_rooms, int max_games_per_room, + int max_players_per_room); + ~Lobby(); + + bool CanEnter(Endpoint *endpoint); + void Enter(Endpoint *endpoint); + void Leave(Endpoint *endpoint, bool disconnect = true); + Room *FindRoom(dword idroom); + Room *NewRoom(Endpoint *endpoint, const char *name, const char *password, + dword roomid, dword ff, dword *result); + std::vector GetRoomIds(); + void SendAdminChat(const char *name, const char *chat, + bool mods_only = false); + std::vector GetIdsString(Endpoint *endpoint); + Tracker& room_tracker() { return room_tracker_; } + +private: + std::string StripWhitespace(const char *name); + void Broadcast(base::ByteBuffer *bb); + void OnRoomDelete(Room *room); + void OnEndpointDelete(Endpoint *endpoint); + void OnPlayersChange(Room *room); + void OnGamesChange(Room *room); + + // MessageHandler interface + virtual void OnMessage(base::Message *pmsg); + + typedef std::map EndpointMap; + EndpointMap endpointmap_; + typedef std::map RoomMap; + RoomMap roommap_; + int max_rooms_; + int max_games_per_room_; + int max_players_per_room_; + Tracker room_tracker_; + Server *server_; +}; + +} + +#endif // __LOBBY_H__ diff --git a/server/logger.cpp b/server/logger.cpp new file mode 100644 index 0000000..4a19866 --- /dev/null +++ b/server/logger.cpp @@ -0,0 +1,207 @@ +#include "server/logger.h" +#include "server/endpoint.h" +#include "server/server.h" +#include +#include + +namespace wi { + +#define MSG_LOGENTRY 1 + +Logger::Logger(const char *pszDir, dword server_id) : + base::MessageHandler(worker_), dir_(pszDir), pf_(NULL), + rotating_(false) { + + char szPrefix[64]; + sprintf(szPrefix, "server-%lu-%lu", base::GetSecondsUnixEpocUTC(), server_id); + prefix_ = szPrefix; + worker_.Start(this, &Logger::ThreadStart); +} + +Logger::~Logger() { + // Thread::Stop gets called in the Thread destructor, which does a join + // with the actual thread, which synchronizes exiting +} + +void Logger::ThreadStart(void *pv) { + // Start logging + OpenLog(); + + // Run and process messages. RunLoop will return when this thread exits + // When worker_ destructs, queued base::MessageData objects get destroyed + worker_.RunLoop(); + + // Close files + CloseLog(); +} + +std::string Logger::GetLogFilename() { + time_t t = time(NULL); + struct tm *tm = localtime(&t); + char szFilename[PATH_MAX]; + sprintf(szFilename, "%s/%s_%04d-%02d-%02d.log", dir_.c_str(), + prefix_.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); + return std::string(szFilename); +} + +bool Logger::OpenLog() { + // Create log dir if it doesn't exist + struct stat st; + if (stat(dir_.c_str(), &st) != 0) { + if (errno == ENOENT) { + // Dir doesn't exist; Assume one level directory to keep this easy + if (mkdir(dir_.c_str(), 0755) != 0) { + return false; + } + } else { + return false; + } + } else { + // Ensure it is a directory + if (!(st.st_mode & S_IFDIR)) { + return false; + } + } + + // Close it if it is open... shouldn't be the case but be safe + if (pf_ != NULL) { + CloseLog(); + } + + // Attempt open for writing. Allow append to the end of an existing file + std::string filename = GetLogFilename(); + pf_ = fopen(filename.c_str(), "ab"); + if (pf_ == NULL) { + return false; + } + filenameLast_ = filename; + + // Called on worker thread so handle directly + LogEntry entry(knLogEntryTypeLogger); + entry.message_ = "OPEN LOG"; + WriteLogEntry(entry); + return true; +} + +void Logger::CloseLog() { + // Called on worker thread so handle directly + LogEntry entry(knLogEntryTypeLogger); + entry.message_ = "CLOSE LOG"; + WriteLogEntry(entry); + + // Close log + if (pf_ != NULL) { + fclose(pf_); + pf_ = NULL; + } +} + +void Logger::RotateLog() { + if (!rotating_) { + std::string filename = GetLogFilename(); + if (filenameLast_.compare(filename) != 0) { + rotating_ = true; + CloseLog(); + OpenLog(); + rotating_ = false; + } + } +} + +void Logger::Submit(const LogEntry& entry) { + // Called on any thread, post to worker thread + worker_.Post(MSG_LOGENTRY, this, new LogEntryData(entry)); +} + +void Logger::InitLogEntryFromEndpoint(Endpoint *endpoint, LogEntry &entry) { + entry.gameid_ = endpoint->gameid(); + strncpyz(entry.did_, endpoint->did(), sizeof(entry.did_)); + entry.ip_ = endpoint->xpump().socket()->GetRemoteAddress().ip(); + entry.anon_ = endpoint->anonymous(); + entry.game_name_ = endpoint->GetGameName(); + entry.player_name_ = endpoint->name(); + entry.mod_ = endpoint->IsModerator(); + entry.admin_ = endpoint->IsAdmin(); + + Room *room = endpoint->server().lobby().FindRoom(endpoint->roomid()); + if (room != NULL) { + entry.roomid_ = endpoint->roomid(); + entry.room_name_ = room->name(); + entry.private_room_ = (room->password()[0] != 0); + } +} + +void Logger::Log(LogEntryType type, Endpoint *endpoint, const char *pszMsg) { + LogEntry entry(type); + InitLogEntryFromEndpoint(endpoint, entry); + entry.message_ = pszMsg; + Submit(entry); +} + +void Logger::LogRoomChat(Endpoint *endpoint, dword roomid, const char *pszRoomName, bool private_room, const char *pszMsg) { + if (strlen(pszMsg) != 0) { + LogEntry entry(knLogEntryTypeChat); + if (endpoint != NULL) { + InitLogEntryFromEndpoint(endpoint, entry); + } + entry.roomid_ = roomid; + entry.room_name_ = pszRoomName; + entry.private_room_ = private_room; + entry.message_ = pszMsg; + Submit(entry); + } +} + +void Logger::LogGameChat(Endpoint *endpoint, dword roomid, const char *pszRoomName, bool private_room, dword gameid, const char *pszGameName, const char *pszMsg) { + if (strlen(pszMsg) != 0) { + // TODO: Put roomid / room name in here + LogEntry entry(knLogEntryTypeChat); + if (endpoint != NULL) { + InitLogEntryFromEndpoint(endpoint, entry); + } + entry.roomid_ = roomid; + entry.room_name_ = pszRoomName; + entry.private_room_ = private_room; + entry.gameid_ = gameid; + entry.game_name_ = pszGameName; + entry.message_ = pszMsg; + Submit(entry); + } +} + +void Logger::OnMessage(base::Message *pmsg) { + // Worker thread message handler + if (pmsg->id == MSG_LOGENTRY) { + LogEntryData *data = (LogEntryData *)pmsg->data; + WriteLogEntry(data->entry_); + delete data; + } +} + +bool Logger::WriteLogEntry(const LogEntry& entry) { + // Executes on worker thread + + // Check for log rotation + RotateLog(); + + // If there is no open log file, fail + if (pf_ == NULL) { + return false; + } + + // Serialize the LogEntry + base::ByteBuffer bb; + entry.Serialize(bb); + + // Write to the log file and flush immediately + base::ByteBuffer bbT; + bbT.WriteDword(bb.Length()); + bbT.WriteBytes(bb.Data(), bb.Length()); + if (fwrite(bbT.Data(), bbT.Length(), 1, pf_) != 1) { + return false; + } + fflush(pf_); + return true; +} + +} // namespace wi diff --git a/server/logger.h b/server/logger.h new file mode 100644 index 0000000..672f212 --- /dev/null +++ b/server/logger.h @@ -0,0 +1,194 @@ +#ifndef __LOGGER_H__ +#define __LOGGER_H__ + +#include "base/thread.h" +#include "base/messagequeue.h" +#include "base/bytebuffer.h" +#include "base/tick.h" +#include +#include + +namespace wi { + +enum LogEntryType { + knLogEntryTypeNone, + knLogEntryTypeLogger, + knLogEntryTypeChat, + knLogEntryTypeModCommand, + knLogEntryTypeMark, + knLogEntryTypeSystemMsg, + knLogEntryTypeRoomInfo, + knLogEntryTypeGameInfo +}; + +#define knLogEntryVersion 1 + +struct LogEntry { + LogEntry(LogEntryType type) : version_(knLogEntryVersion), type_(type), + time_(base::GetSecondsUnixEpocUTC()), roomid_(0), gameid_(0), + ip_(0), anon_(false), private_room_(false), mod_(false), + admin_(false) { + memset(did_, 0, sizeof(did_)); + } + + void Serialize(base::ByteBuffer& bb) const { + bb.WriteByte(version_); + bb.WriteByte(type_); + bb.WriteByte(anon_); + bb.WriteByte(private_room_); + bb.WriteByte(mod_); + bb.WriteByte(admin_); + bb.WriteDword(time_); + bb.WriteDword(roomid_); + bb.WriteDword(gameid_); + bb.WriteDword(ip_); + bb.WriteString(did_); + bb.WriteString(room_name_.c_str()); + bb.WriteString(game_name_.c_str()); + bb.WriteString(player_name_.c_str()); + bb.WriteString(message_.c_str()); + } + + bool Unserialize(base::ByteBuffer& bb) { + byte bT; + if (!bb.ReadByte(&bT)) { + return false; + } + version_ = bT; + + if (!bb.ReadByte(&bT)) { + return false; + } + type_ = (LogEntryType)bT; + + if (!bb.ReadByte(&bT)) { + return false; + } + anon_ = (bT != 0); + + if (!bb.ReadByte(&bT)) { + return false; + } + private_room_ = (bT != 0); + + if (!bb.ReadByte(&bT)) { + return false; + } + mod_ = (bT != 0); + + if (!bb.ReadByte(&bT)) { + return false; + } + admin_ = (bT != 0); + + if (!bb.ReadDword(&time_)) { + return false; + } + + if (!bb.ReadDword(&roomid_)) { + return false; + } + + if (!bb.ReadDword(&gameid_)) { + return false; + } + + if (!bb.ReadDword(&ip_)) { + return false; + } + + if (!bb.ReadString(did_, sizeof(did_))) { + return false; + } + + char szT[2048]; + if (!bb.ReadString(szT, sizeof(szT))) { + return false; + } + room_name_ = szT; + + if (!bb.ReadString(szT, sizeof(szT))) { + return false; + } + game_name_ = szT; + + if (!bb.ReadString(szT, sizeof(szT))) { + return false; + } + player_name_ = szT; + + if (!bb.ReadString(szT, sizeof(szT))) { + return false; + } + message_ = szT; + + return true; + } + + dword version_; + LogEntryType type_; + dword time_; + dword roomid_; + dword gameid_; + char did_[64]; + dword ip_; + bool anon_; + bool private_room_; + bool mod_; + bool admin_; + std::string room_name_; + std::string game_name_; + std::string player_name_; + std::string message_; +}; + +struct LogEntryData : base::MessageData { + LogEntryData(const LogEntry& entry) : entry_(entry) { } + LogEntry entry_; +}; + +class Endpoint; +class Logger : base::MessageHandler { +public: + Logger(const char *pszDir, dword server_id); + ~Logger(); + + void Log(LogEntryType type, Endpoint *endpoint, const char *pszMsg); + void LogMark(Endpoint *endpoint, const char *pszMsg) { + Log(knLogEntryTypeMark, endpoint, pszMsg); + } + void LogModCommand(Endpoint *endpoint, const char *pszMsg) { + Log(knLogEntryTypeModCommand, endpoint, pszMsg); + } + void LogSystemMsg(Endpoint *endpoint, const char *pszMsg) { + if (strlen(pszMsg) != 0) { + Log(knLogEntryTypeSystemMsg, endpoint, pszMsg); + } + } + void LogRoomChat(Endpoint *endpoint, dword roomid, const char *pszRoomName, bool private_room, const char *pszMsg); + void LogGameChat(Endpoint *endpoint, dword roomid, const char *pszRoomName, bool private_room, dword gameid, const char *pszGameName, const char *pszMsg); + void Submit(const LogEntry& entry); + +private: + void ThreadStart(void *pv); + bool WriteLogEntry(const LogEntry& entry); + std::string GetLogFilename(); + bool OpenLog(); + void CloseLog(); + void RotateLog(); + void InitLogEntryFromEndpoint(Endpoint *endpoint, LogEntry &entry); + + // MessageHandler interface + virtual void OnMessage(base::Message *pmsg); + + bool rotating_; + std::string dir_; + std::string prefix_; + base::Thread worker_; + FILE *pf_; + std::string filenameLast_; +}; + +} // namespace wi + +#endif // __LOGGER_H__ diff --git a/server/main.cpp b/server/main.cpp new file mode 100644 index 0000000..d04ee03 --- /dev/null +++ b/server/main.cpp @@ -0,0 +1,227 @@ +#include "inc/basictypes.h" +#include "base/thread.h" +#include "base/socketaddress.h" +#include "base/tick.h" +#include "base/log.h" +#include "server/server.h" +#include "server/levelinfocache.h" +#include "server/ncpackfile.h" +#include "server/statsposter.h" +#include "server/serverinfoupdater.h" +#include "mpshared/constants.h" +#include "mpshared/packmanager.h" +#include "mpshared/xmsglog.h" +#include +#include +#include +#include + +// These are defaults that can be overridden +const dword kcRoomsMax = 200; +const dword kcGamesPerRoomMax = 100; +const dword kcPlayersPerRoomMax = 200; +const dword kcConnectedPlayersMax = 1500; + +std::vector ParseArgs(int argc, char **argv, const char *fmt) { + std::vector args; + for (int i = 0; i < argc; i++) { + if (strcmp(argv[i], fmt) == 0 && i <= argc - 2) { + args.push_back(argv[i + 1]); + i++; + } + } + return args; +} + +std::string ParseString(int argc, char **argv, const char *fmt) { + std::vector args = ParseArgs(argc, argv, fmt); + if (!args.empty()) { + return args[0]; + } + return ""; +} + +int ParseInteger(int argc, char **argv, const char *fmt, int def = 0) { + std::string s = ParseString(argc, argv, fmt); + if (s.length() == 0) { + return def; + } + int n = def; + base::Format::ToInteger(s.c_str(), 10, &n); + return n; +} + +int ParseDword(int argc, char **argv, const char *fmt, dword def = 0) { + std::string s = ParseString(argc, argv, fmt); + if (s.length() == 0) { + return def; + } + dword n = def; + base::Format::ToDword(s.c_str(), 10, &n); + return n; +} + +bool FindArg(int argc, char **argv, const char *fmt) { + for (int i = 0; i < argc; i++) { + if (strcmp(argv[i], fmt) == 0) { + return true; + } + } + return false; +} + +base::SocketAddress GetListenAddress(int argc, char **argv) { + // Accept address arg or default address, port arg or default port + // For default address, choose interface adapter. + + base::SocketAddress listen_address; + std::string listen_address_arg = ParseString(argc, argv, "--listen_address"); + if (listen_address_arg.size() != 0) { + listen_address = base::SocketAddress(listen_address_arg.c_str()); + } else { + // Choose a network, rather than using ANY, since it will be sent in server info + int n = 0; + char sz[32]; + dword ip; + while (base::SocketAddress::GetNetworkInfo(n, sz, sizeof(sz), &ip)) { + if (strncmp(sz, "eth", 3) == 0) { + listen_address.SetIP(ip); + break; + } + if (strncmp(sz, "en", 2) == 0) { + listen_address.SetIP(ip); + break; + } + n++; + } + } + int listen_port = ParseInteger(argc, argv, "--listen_port"); + if (listen_port != 0) { + listen_address.SetPort(listen_port); + } + return listen_address; +} + +int main(int argc, char **argv) +{ + // Log arguments + std::string args; + if (argc != 0) { + for (int i = 0; i < argc; i++) { + if (i != 0) { + args += " "; + } + args += argv[i]; + } + } else { + args = ""; + } + RLOG() << "Arguments: " << args.c_str(); + + // Seed random number generator + srand((int)base::GetTickCount()); + + // Root of the pack cache + std::string rootdir = ParseString(argc, argv, "--missionpack_dir"); + std::string packdir = rootdir + "/pack"; + std::string indexfile = rootdir + "/index"; + std::string modfile = rootdir + "/modlist"; + std::string badwordsfile = rootdir + "/badwords"; + + // Initialize the level info cache from the index so the server knows + // the latest and greatest mission packs. + wi::PackManager packm(packdir.c_str()); + packm.WatchIndex(indexfile.c_str()); + wi::LevelInfoCache cache(packm); + + // Submit main pack and packs from the index + wi::NoCachePackFileReader pakr; + if (pakr.Push(NULL, ParseString(argc, argv, "--htdata").c_str())) { + wi::PackId packid; + memset(&packid, 0, sizeof(packid)); + packid.id = PACKID_MAIN; + cache.Submit(pakr, packid); + pakr.Pop(); + } + cache.SubmitIndex(indexfile); + if (cache.empty()) { + RLOG() << "cache is empty!"; + exit(1); + } + + // Set up StatsPoster + base::SocketAddress stats_address(ParseString(argc, argv, + "--stats_address").c_str()); + std::string stats_path(ParseString(argc, argv, "--stats_path")); + wi::StatsPoster stats(stats_address, stats_path); + + // Set up the server + dword server_id = ParseDword(argc, argv, "--server_id"); + bool checksync = FindArg(argc, argv, "--checksync"); + RLOG() << "Sync checking turned " << (checksync ? "ON." : "OFF."); + + std::string log_file = ParseString(argc, argv, "--log_file"); + wi::XMsgLog *xmsglog = NULL; + if (log_file.size() != 0) { + xmsglog = new wi::XMsgLog(log_file); + } + + int max_players = ParseInteger(argc, argv, "--max_players", + kcConnectedPlayersMax); + int max_rooms = ParseInteger(argc, argv, "--max_rooms", kcRoomsMax); + int max_games_per_room = ParseInteger(argc, argv, "--max_games_per_room", + kcGamesPerRoomMax); + int max_players_per_room = ParseInteger(argc, argv, + "--max_players_per_room", kcPlayersPerRoomMax); + + wi::Server server(stats, xmsglog, cache, server_id, checksync, + max_rooms, max_games_per_room, max_players_per_room, + max_players, modfile, badwordsfile); + + base::SocketAddress listen_address = GetListenAddress(argc, argv); + if (!server.Listen(listen_address)) { + printf("Server failed to start.\n"); + return 1; + } + listen_address = server.listen_address(); + + // Create default rooms + dword result; + server.lobby().NewRoom(NULL, "Main", "", wi::kroomidMain, + wi::kfRmPermanent | wi::kfRmLocked, &result); + server.lobby().NewRoom(NULL, "Main / Registered", "", wi::kroomidRegistered, + wi::kfRmPermanent | wi::kfRmRegisteredOnly | wi::kfRmLocked, + &result); + server.lobby().NewRoom(NULL, "Main / Unmoderated", "", + wi::kroomidUnmoderated, + wi::kfRmPermanent | wi::kfRmLocked | wi::kfRmUnmoderated, + &result); + + // Get the public_address. This is the address that gets sent in + // ServerInfo. For example in AWS, the internal "private" ip + // is not the same as the public one. If this is not present, + // use the listen_address. + base::SocketAddress public_address; + std::string public_address_str = ParseString(argc, argv, "--public_address"); + if (public_address_str.size() != 0) { + public_address = base::SocketAddress(public_address_str.c_str()); + } else { + public_address = listen_address; + } + + // Start up ServerInfoUpdater + std::string server_info_path(ParseString(argc, argv, "--server_info_path")); + std::string server_name(ParseString(argc, argv, "--server_name")); + std::string server_location(ParseString(argc, argv, "--server_location")); + std::string server_type(ParseString(argc, argv, "--server_type")); + std::string server_info_extra(ParseString(argc, argv, "--server_info_extra")); + dword expires = ParseDword(argc, argv, "--server_info_expires", 60 * 5); + wi::ServerInfoUpdater updater(server, stats_address, server_info_path, + server_id, server_name, server_location, server_type, + public_address, server_info_extra, expires); + server.SetUpdater(&updater); + + // Pump messages and wait for network i/o + base::Thread::RunLoop(); + return 0; +} diff --git a/server/ncpackfile.cpp b/server/ncpackfile.cpp new file mode 100644 index 0000000..336a447 --- /dev/null +++ b/server/ncpackfile.cpp @@ -0,0 +1,44 @@ +#include "server/ncpackfile.h" +#include "server/ncpdbreader.h" +#include "base/log.h" +#include +#include + +namespace wi { + +PdbReader *NoCachePackFileReader::OpenPdb(const char *pszDir, + const char *pszFn) { + // Load data from this directory + + char szT[PATH_MAX]; + if (pszDir != NULL) { + strcpy(szT, pszDir); + strcat(szT, "/"); + strcat(szT, pszFn); + } else { + pszDir = ""; + strcpy(szT, pszFn); + } + + // Non-caching pdbReader over memory + NoCachePdbReader *ppdbReader = new NoCachePdbReader; + if (ppdbReader == NULL) { + RLOG() << "NoCachePdbReader null"; + return NULL; + } + if (!ppdbReader->Open(szT)) { + delete ppdbReader; + RLOG() << base::Log::Format("NoCachePdbReader::Open(%s, %s) failed", + pszDir, pszFn); + return NULL; + } + + return ppdbReader; +} + +bool NoCachePackFileReader::DeletePdb(const char *pszDir, const char *pszFn) { + // Delete isn't supported with this implementation + return false; +} + +} // namespace wi diff --git a/server/ncpackfile.h b/server/ncpackfile.h new file mode 100644 index 0000000..6dea761 --- /dev/null +++ b/server/ncpackfile.h @@ -0,0 +1,19 @@ +#ifndef __NCPACKFILE_H__ +#define __NCPACKFILE_H__ + +#include "inc/basictypes.h" +#include "mpshared/packfile.h" +#include "mpshared/pdbreader.h" + +namespace wi { + +class NoCachePackFileReader : public PackFileReader +{ +private: + virtual PdbReader *OpenPdb(const char *pszDir, const char *pszFn); + virtual bool DeletePdb(const char *pszDir, const char *pszFn); +}; + +} // namespace wi + +#endif // __NCPACKFILE_H__ diff --git a/server/ncpdbreader.cpp b/server/ncpdbreader.cpp new file mode 100644 index 0000000..cf7a698 --- /dev/null +++ b/server/ncpdbreader.cpp @@ -0,0 +1,200 @@ +#include "server/ncpdbreader.h" +#include "inc/rip.h" +#include "mpshared/misc.h" +#include "base/log.h" +#include +#include + +namespace wi { + +NoCachePdbReader::NoCachePdbReader() +{ + m_cb = 0; + m_pb = NULL; + m_cRecs = 0; + m_cMapped = 0; +} + +NoCachePdbReader::~NoCachePdbReader() +{ + Assert(m_pb == NULL); + Assert(m_cMapped == 0); +} + +bool NoCachePdbReader::Open(const char *pszFn) +{ + // Attempt to open + + Assert(m_pb == NULL); + FILE *pfil = fopen(pszFn, "rb"); + if (pfil == NULL) { + RLOG() << "fopen failed: " << pszFn; + return false; + } + + // Read in the entire thing + + fseek(pfil, 0, SEEK_END); + m_cb = ftell(pfil); + fseek(pfil, 0, SEEK_SET); + + m_pb = new byte[m_cb]; + if (m_pb == NULL) { + fclose(pfil); + return false; + } + if (fread(m_pb, m_cb, 1, pfil) != 1) { + fclose(pfil); + return false; + } + fclose(pfil); + + DatabaseHdrType *phdr = (DatabaseHdrType *)m_pb; + m_cRecs = BigWord(phdr->recordList.numRecords); + + return true; +} + +void NoCachePdbReader::Close() +{ + Assert(m_cMapped == 0); + Assert(m_pb != NULL); + delete[] m_pb; + m_pb = NULL; +} + +bool NoCachePdbReader::GetRecordSize(word nRec, word *pcb) +{ + CompressionHeader coh; + if (GetCompressionHeader(nRec, &coh) == 0) + return false; + *pcb = BigWord(coh.cbUncompressed); + return true; +} + +bool NoCachePdbReader::GetRecordEntry(word nRec, int cRec, + RecordEntryType *prece, dword *pcb) +{ + // Valid? + + Assert(cRec != 0); + Assert(nRec + cRec <= m_cRecs); + if (nRec + cRec > m_cRecs) + return false; + + // Seek to record header offset + + DatabaseHdrType hdr; + dword off = (sizeof(hdr) - sizeof(hdr.recordList.firstEntry)) + nRec * sizeof(RecordEntryType); + + // End of records case? + + if (nRec + cRec == m_cRecs) { + memcpy(prece, &m_pb[off], sizeof(RecordEntryType) * cRec); + if (pcb != NULL) { + *pcb = m_cb - BigDword(prece[cRec - 1].localChunkID); + } + return true; + } + + // 1 record case and not end of records + + if (cRec == 1) { + RecordEntryType arece[2]; + memcpy(arece, &m_pb[off], sizeof(RecordEntryType) * 2); + *prece = arece[0]; + if (pcb != NULL) + *pcb = BigDword(arece[1].localChunkID) - BigDword(arece[0].localChunkID); + return true; + } + + // N record case and not end of records + + memcpy(prece, &m_pb[off], sizeof(RecordEntryType) * cRec); + if (pcb != NULL) { + *pcb = BigDword(prece[cRec - 1].localChunkID) - + BigDword(prece[0].localChunkID); + } + return true; +} + +dword NoCachePdbReader::GetCompressionHeader(word nRec, CompressionHeader *pcoh) +{ + dword cbRecord; + RecordEntryType rece; + if (!GetRecordEntry(nRec, 1, &rece, &cbRecord)) + return 0; + memcpy(pcoh, &m_pb[BigDword(rece.localChunkID)], kcbCompressionHeader); + return BigDword(rece.localChunkID) + kcbCompressionHeader; +} + +bool NoCachePdbReader::ReadRecord(word nRec, word n, word cb, void *pv) +{ + // See if the data is cached + Assert(nRec < m_cRecs); + CompressionHeader coh; + dword offBytes = GetCompressionHeader(nRec, &coh); + if (offBytes == 0) { + return false; + } + + if (n + cb > BigWord(coh.cbUncompressed)) { + return false; + } + + // If not compressed, read data straight from file + if (!BigWord(coh.fCompressed)) { + memcpy(pv, &m_pb[offBytes + n], cb); + } else { + byte *pbT = new byte[BigWord(coh.cbUncompressed)]; + if (pbT == NULL) { + return false; + } + DecompressToMemory(&m_pb[offBytes], pbT, &coh); + memcpy(pv, &pbT[n], cb); + delete[] pbT; + } + return true; +} + +byte *NoCachePdbReader::MapRecord(word nRec, dword *pdwCookie, word *pcb) +{ + // First see if the record is cached + + Assert(m_pb != NULL); + Assert(nRec < m_cRecs); + + CompressionHeader coh; + dword offBytes = GetCompressionHeader(nRec, &coh); + if (offBytes == 0) + return NULL; + + word cbT = BigWord(coh.cbUncompressed); + byte *pbT = new byte[cbT]; + if (pbT == NULL) { + return NULL; + } + + if (BigWord(coh.fCompressed)) { + DecompressToMemory(&m_pb[offBytes], pbT, &coh); + } else { + memcpy(pbT, &m_pb[offBytes], cbT); + } + + m_cMapped++; + *pdwCookie = (dword)pbT; + *pcb = cbT; + return pbT; +} + +void NoCachePdbReader::UnmapRecord(word nRec, dword dwCookie) +{ + Assert(m_pb != NULL); + Assert(nRec < m_cRecs); + Assert(m_cMapped > 0); + m_cMapped--; + byte *pb = (byte *)dwCookie; + delete[] pb; +} + +} // namespace wi diff --git a/server/ncpdbreader.h b/server/ncpdbreader.h new file mode 100644 index 0000000..8bd6109 --- /dev/null +++ b/server/ncpdbreader.h @@ -0,0 +1,41 @@ +#ifndef __NCPDBREADER_H__ +#define __NCPDBREADER_H__ + +#include "inc/basictypes.h" +#include "mpshared/pdbreader.h" +#include "mpshared/decompress.h" +#include "mpshared/packfile.h" + +namespace wi { + +// +// Reads pdbs from memory +// + +class NoCachePdbReader : public PdbReader +{ +public: + NoCachePdbReader(); + ~NoCachePdbReader(); + bool Open(const char *pszFn); + + virtual void Close(); + virtual bool GetRecordSize(word nRec, word *pcb); + virtual bool ReadRecord(word nRec, word n, word cb, void *pv); + virtual byte *MapRecord(word nRec, dword *pdwCookie, word *pcb = NULL); + virtual void UnmapRecord(word nRec, dword dwCookie); + +private: + bool GetRecordEntry(word nRec, int cRec, RecordEntryType *prece, + dword *pcb); + dword GetCompressionHeader(word nRec, CompressionHeader *pcoh); + + dword m_cb; + byte *m_pb; + word m_cRecs; + word m_cMapped; +}; + +} // namespace wi + +#endif // __NCPDBREADER_H__ diff --git a/server/player.cpp b/server/player.cpp new file mode 100644 index 0000000..5acaf87 --- /dev/null +++ b/server/player.cpp @@ -0,0 +1,231 @@ +#include "server/player.h" +#include "base/tick.h" + +namespace wi { + +// lag time period to be declared laggy player +#define kctLagGrace 200 + +// to become non-laggy, the player must be not laggy for this period +#define kctLagRedemption 0 + +// if still laggy after this period, recommend kill +#define kctLagKill 3000 + +Player::Player() { + Init(0); +} + +bool Player::Init(Pid pid) { + anonymous_ = false; + havestats_ = false; + cur_ = 0; + wf_ = 0; + pid_ = pid; + side_ = ksideNeutral; + endpoint_ = NULL; + lag_ = knLagNone; + nLagState_ = knLagNone; + tLagStart_ = 0; + tLastLag_ = 0; + updates_ = -1; + strncpyz(name_, "Unfulfilled", sizeof(name_)); + memset(alatr_, 0, sizeof(alatr_)); + memset(&ws_, 0, sizeof(ws_)); + memset(did_, 0, sizeof(did_)); + return true; +} + +void Player::SetEndpoint(Endpoint *endpoint) { + endpoint_ = endpoint; + if (endpoint == NULL) { + return; + } + strncpyz(name_, endpoint->name(), sizeof(name_)); + anonymous_ = endpoint->anonymous(); + address_ = endpoint->xpump().socket()->GetRemoteAddress(); + strncpyz(did_, endpoint->did(), sizeof(did_)); + lag_ = knLagNone; + nLagState_ = knLagNone; + tLagStart_ = 0; + tLastLag_ = 0; +} + +int Player::GetLagTimeout() { + // Return seconds until this player gets to the kill state + + long64 ctElapsed = base::GetTickCount() - tLagStart_; + if (ctElapsed > kctLagKill) { + return 0; + } + return (int)((kctLagKill - ctElapsed + 50) / 100); +} + +void Player::SetLagState(int nLagState) { + nLagState_ = nLagState; + tLastLag_ = base::GetTickCount(); + tLagStart_ = tLastLag_; +} + +int Player::UpdateLagState(long cUpdates) { + if (wf_ & kfPlrDisconnectBroadcasted) { + SetLagState(knLagNone); + return knLagNone; + } + + if (updates_ < cUpdates) { + // Player is behind + + switch (nLagState_) { + case knLagNone: + // Remember that pplr is lagging and when this started + + tLagStart_ = base::GetTickCount(); + nLagState_ = knLagGrace; + break; + + case knLagGrace: + // This state is effectively a grace period. If the player remains + // laggy through this period, it becomes guilty of lag + + if (base::GetTickCount() - tLagStart_ >= kctLagGrace) { + nLagState_ = knLagGuilty; + tLastLag_ = base::GetTickCount(); + } + break; + + case knLagGuilty: + case knLagKill: + // This player has lagged long enough to be guilty as charged. Now + // the player needs to be *not* laggy over a fixed period to get + // out of this state + + tLastLag_ = base::GetTickCount(); + if (base::GetTickCount() - tLagStart_ >= kctLagKill) { + nLagState_ = knLagKill; + } + break; + } + } else { + // Player is not behind + + switch (nLagState_) { + case knLagNone: + // All is ok + + break; + + case knLagGrace: + // This player has "caught up" during the grace period. Assume + // there is no lag now. + + nLagState_ = knLagNone; + break; + + case knLagGuilty: + case knLagKill: + // Guilty of lag and yet the player has caught up. If the player + // can keep this state it will be declared not laggy + + if (base::GetTickCount() - tLastLag_ >= kctLagRedemption) { + nLagState_ = knLagNone; + break; + } + } + } + + return nLagState_; +} + +void Player::AddLatencyRecord(LatencyRecord *platr) { + memmove(&alatr_[1], &alatr_[0], sizeof(alatr_) - sizeof(alatr_[0])); + alatr_[0] = *platr; + clatr_++; + if (clatr_ > ARRAYSIZE(alatr_)) { + clatr_ = ARRAYSIZE(alatr_); + } + updates_ = platr->cUpdatesBlock; +} + +int Player::GetLatencyRecordCount() { + return clatr_; +} + +const LatencyRecord *Player::GetLatencyRecord(int i) { + if (i < 0 || i >= clatr_) { + return NULL; + } + return &alatr_[i]; +} + +void Player::SaveWinStats(const WinStats& ws, bool lock) { + // Ignore if already locked + if (ws_.ff & kfwsLocked) { + return; + } + + // Otherwise keep as most recent. Filter out flags from these win stats, + // since these came from the client. Flags will be added in later, when + // these results get posted. + ws_ = ws; + ws_.ff &= (kfwsWinner | kfwsLoser); + if (lock) { + ws_.ff |= kfwsLocked; + } + havestats_ = true; +} + +void Player::GetPlayerStats(PlayerStats *ps) { + // Players can be human or computer. Humans can be in a game, and leave + // before the game ends. Losers are given a chance to dispute winners. + // Clients may or may not have sent win stats. + + memset(ps, 0, sizeof(*ps)); + strncpyz(ps->name, name_, sizeof(ps->name)); + ps->pid = pid_; + address_.IPAsString(ps->ip, sizeof(ps->ip)); + strncpyz(ps->did, did_, sizeof(ps->did)); + + // If this player was never used, then there are no stats for it + if (!(wf_ & kfPlrInUse)) { + return; + } + + // Return stats that exist, if any + if (havestats_) { + ps->ws = ws_; + ps->ws.ff |= kfwsReceivedStats; + } + + // Humans can join, and then leave. When they leave, the player turns + // into a computer, with kfPlrComputer set. kfPlrHumanJoined means this + // player started out human (even if the player didn't stay till the end + // of the game). + if (wf_ & kfPlrHumanJoined) { + ps->ws.ff |= kfwsHuman; + } else { + ps->ws.ff |= kfwsComputer; + } + + // Remember if the player is anonymous. + if (anonymous_) { + ps->ws.ff |= kfwsAnonymous; + } + +#if 0 +// not implemented + // Winner/Loser is already encoded in WinStats. + // If this player challenged the winner, note it + if (wf_ & kfPlrChallengeWinner) { + ps->ws.ff |= kfwsWinChallenger; + } +#endif + + // If this player was removed at game start, remember that since the + // stats are invalid. + if (wf_ & kfPlrRemovedAtGameStart) { + ps->ws.ff |= kfwsRemovedAtGameStart; + } +} + +} // namespace wi diff --git a/server/player.h b/server/player.h new file mode 100644 index 0000000..6c8d57e --- /dev/null +++ b/server/player.h @@ -0,0 +1,87 @@ +#ifndef __PLAYER_H__ +#define __PLAYER_H__ + +#include "inc/basictypes.h" +#include "mpshared/mpht.h" +#include "server/endpoint.h" + +#define kcmsRateMax 250 +#define kcmsLagging 5000 + +namespace wi { + +struct PlayerStats { + char name[kcbPlayerName]; + Pid pid; + char ip[32]; + char did[64]; + WinStats ws; +}; + +struct LatencyRecord { // latr + long cUpdatesBlock; + long cmsLatency; +}; + +const int kcLatencyRecordsMax = 8; + +class Player { + friend class PlayerMgr; + +public: + Player(); + + bool Init(Pid pid); + Pid pid() const { return pid_; } + void SetEndpoint(Endpoint *endpoint); + Endpoint *endpoint() const { return endpoint_; } + void SetName(const char *name) { strncpyz(name_, name, sizeof(name_)); } + const char *name() const { return name_; } + void SetSide(Side side) { side_ = side; } + Side side() const { return side_; } + void SetFlags(word wf) { wf_ = wf; } + word flags() const { return wf_; } + void SaveWinStats(const WinStats& ws, bool lock); + const WinStats& winstats() { return ws_; } + void GetPlayerStats(PlayerStats *ps); + int lag() const { return nLagState_; } + int GetLagTimeout(); + void SetLagState(int nLagState); + int UpdateLagState(long updates); + void AddLatencyRecord(LatencyRecord *platr); + int GetLatencyRecordCount(); + void ClearLatencyRecords() { clatr_ = 0; } + const LatencyRecord *GetLatencyRecord(int i); + void SetDid(const char *did) { strncpyz(did_, did, sizeof(did_)); } + + long updates() { return updates_; } + bool havestats() { return havestats_; } + bool statslocked() { return havestats_ && (ws_.ff & kfwsLocked) != 0; } + const base::SocketAddress& address() { return address_; } + const char *did() { return did_; } + +private: + base::SocketAddress address_; + WinStats ws_; + word wf_; + Pid pid_; + Side side_; + int lag_; + Endpoint *endpoint_; + int nLagState_; + long64 tLagStart_; + long64 tLastLag_; + char name_[kcbPlayerName]; + LatencyRecord alatr_[kcLatencyRecordsMax]; + int clatr_; + long updates_; + UpdateResult aur_[2 * kcmsLagging / kcmsRateMax]; + int cur_; + bool havestats_; + bool anonymous_; + char did_[64]; +}; + +} // namespace wi + +#endif // __PLAYER_H__ diff --git a/server/playermgr.cpp b/server/playermgr.cpp new file mode 100644 index 0000000..268be23 --- /dev/null +++ b/server/playermgr.cpp @@ -0,0 +1,472 @@ +#include "server/playermgr.h" + +namespace wi { + +#define CHECK_SYNC_ERRORS + +PlayerMgr::PlayerMgr(Server& server) : server_(server), tLagAcknowledge_(0) { +} + +void PlayerMgr::Init(const LevelInfo& info) { + // Create Players for all sides. Until we know otherwise, assume + // the human players will be unfulfilled and need to be removed. + // Map authors can set up the players incorrectly. For example, + // a map may be set to 2 player, but there is 1 human and 3 + // computer players. Fix that up. + + int cHuman = 0; + for (Side side = kside1; side < kcSides; side++) { + word wf = 0; + int intelligence = info.GetIntelligence(side); + switch (intelligence) { + case knIntelligenceComputer: + case knIntelligenceComputerNeutral: + wf = kfPlrComputer | kfPlrReady; + break; + + case knIntelligenceComputerOvermind: + wf = kfPlrComputer | kfPlrComputerOvermind | kfPlrReady; + break; + + case knIntelligenceHuman: + cHuman++; + wf = kfPlrUnfulfilled; + break; + } + Player *player = AllocPlayer(wf); + if (player == NULL) { + continue; + } + player->SetSide(side); + } + + // Convert some computer players to human if necessary + + int cHumansNeeded = info.maxplayers() - cHuman; + if (cHumansNeeded > 0) { + for (Side side = kside1; cHumansNeeded > 0 && side < kcSides; side++) { + Player *pplr = GetPlayer(side); + if (pplr == NULL || !(pplr->wf_ & kfPlrComputer)) { + continue; + } + pplr->wf_ &= ~(kfPlrComputer | kfPlrComputerOvermind | kfPlrReady); + pplr->wf_ |= kfPlrUnfulfilled; + cHumansNeeded--; + } + } + + // Really only necessary for the computer players, but some players + // are slotted for human, but then get converted to computer players + // when the game starts (if there are unfulfilled slots), so name + // them all AI + + int count = 1; + for (Side side = kside1; side < kcSides; side++) { + Player *pplr = GetPlayer(side); + if (pplr == NULL) { + continue; + } + pplr->SetName("AI"); + } +} + +Player *PlayerMgr::AllocPlayer(word wf) { + Player *player = players_; + for (Pid pid = 0; pid < ARRAYSIZE(players_); pid++, player++) { + if (player->wf_ & kfPlrInUse) { + continue; + } + + player->Init(pid); + player->wf_ |= kfPlrInUse | wf; + return player; + } + + RLOG() << "this shouldn't happen!"; + return NULL; +} + +void PlayerMgr::SwapPlayers(Player *playerA, Player *playerB) { + Endpoint *endpointA = playerA->endpoint(); + word flagsA = playerA->flags(); + Endpoint *endpointB = playerB->endpoint(); + word flagsB = playerB->flags(); + playerA->SetEndpoint(endpointB); + playerA->SetFlags(flagsB); + playerB->SetEndpoint(endpointA); + playerB->SetFlags(flagsA); + BroadcastPlayersUpdate(); +} + +int PlayerMgr::GetEndpointCount() { + // This counts players with endpoints (that are receiving updates), + // whether human or observer (player with endpoint marked as computer). + + int cEndpoints = 0; + Player *pplr = players_; + for (int i = 0; i < kcPlayersMax; i++, pplr++) { + if (pplr->endpoint_ != NULL) { + cEndpoints++; + } + } + return cEndpoints; +} + +const PlayerName *PlayerMgr::GetEndpointNames() { + // The creator always goes first + Player *pplr = players_; + PlayerName *name = names_; + for (int i = 0; i < kcPlayersMax; i++, pplr++) { + if (pplr->endpoint_ != NULL && (pplr->wf_ & kfPlrCreator) != 0) { + strncpyz(name->szPlayerName, pplr->name_, + sizeof(name->szPlayerName)); + name++; + } + } + + // Then the other players + pplr = players_; + for (int i = 0; i < kcPlayersMax; i++, pplr++) { + if (pplr->endpoint_ != NULL && (pplr->wf_ & kfPlrCreator) == 0) { + strncpyz(name->szPlayerName, pplr->name_, + sizeof(name->szPlayerName)); + name++; + } + } + + return names_; +} + +Player *PlayerMgr::GetPlayer(Side side) { + Player *pplr = players_; + for (int i = 0; i < kcPlayersMax; i++, pplr++) { + if (pplr->side_ == side) { + return pplr; + } + } + return NULL; +} + +Player *PlayerMgr::GetPlayer(Endpoint *endpoint) { + Player *pplr = players_; + for (int i = 0; i < kcPlayersMax; i++, pplr++) { + if (pplr->endpoint() == endpoint) { + return pplr; + } + } + return NULL; +} + +Player *PlayerMgr::GetPlayerFromPid(Pid pid) { + // Pid is unsigned so this is a full range check + if (pid != kpidNeutral && pid < kcPlayersMax) { + return &players_[((int)pid)]; + } + return NULL; +} + +Player *PlayerMgr::GetNextHumanPlayer(Player *pplr) { + int i = pplr == NULL ? 0 : pplr->pid_ + 1; + pplr = &players_[i]; + for (; i < kcPlayersMax; i++, pplr++) { + if ((pplr->wf_ & (kfPlrInUse | kfPlrComputer)) == kfPlrInUse) { + return pplr; + } + } + + return NULL; +} + +Player *PlayerMgr::GetNextPlayerWithEndpoint(Player *pplr) { + // Players that resign but are observing have kfPlrComputer set + // and endpoint_ != NULL. These players still receive updates, + // participate in lag detection, can be kicked, etc. + + int i = pplr == NULL ? 0 : pplr->pid_ + 1; + pplr = &players_[i]; + for (; i < kcPlayersMax; i++, pplr++) { + if ((pplr->wf_ & kfPlrInUse) && pplr->endpoint_ != NULL) { + return pplr; + } + } + + return NULL; +} + +Player *PlayerMgr::GetNextPlayer(Player *pplr) { + int i = (pplr == NULL) ? 0 : pplr->pid_ + 1; + pplr = &players_[i]; + for (; i < kcPlayersMax; i++, pplr++) { + if (pplr->wf_ & kfPlrInUse) { + return pplr; + } + } + + return NULL; +} + +void PlayerMgr::Broadcast(NetMessage *pnm, Pid pidIgnore) { + for (int i = 0; i < ARRAYSIZE(players_); i++) { + Player *pplr = &players_[i]; + if (!(pplr->wf_ & kfPlrInUse)) { + continue; + } + if (pplr->pid_ == pidIgnore) { + continue; + } + Endpoint *endpoint = pplr->endpoint(); + if (endpoint != NULL) { + endpoint->xpump().Send(XMsgGameNetMessage::ToBuffer(pnm)); + } + } +} + +void PlayerMgr::BroadcastPlayersUpdate() { + // Allocate a properly sized PlayersUpdateNetMessage + + int cplrs = GetPlayerCount(); + int cb = sizeof(PlayersUpdateNetMessage) + sizeof(PlayerRecord) * + (cplrs - 1); + PlayersUpdateNetMessage *ppunm = (PlayersUpdateNetMessage *)new byte[cb]; + if (ppunm == NULL) + return; + + // Fill the message in with all the juicy pplr data + memset(ppunm, 0, cb); + ppunm->nmid = knmidScPlayersUpdate; + ppunm->cb = cb; + ppunm->cplrr = cplrs; + + Player *pplr = players_; + PlayerRecord *record = ppunm->aplrr; + for (int i = 0; i < kcPlayersMax; i++, pplr++) { + if (!(pplr->wf_ & kfPlrInUse)) { + continue; + } + strncpyz(record->szName, pplr->name_, sizeof(record->szName)); + record->side = pplr->side_; + record->wf = 0; + if (pplr->wf_ & kfPlrReady) + record->wf |= kfPlrrReady; + if (pplr->wf_ & kfPlrComputer) + record->wf |= kfPlrrComputer; + if (pplr->wf_ & kfPlrComputerOvermind) + record->wf |= kfPlrrComputerOvermind; + if (pplr->wf_ & kfPlrUnfulfilled) + record->wf |= kfPlrrUnfulfilled; + if (pplr->wf_ & kfPlrCreator) + record->wf |= kfPlrrCreator; + record->pid = pplr->pid_; + record++; + } + + // Send it off to all pplrs + pplr = players_; + for (int i = 0; i < kcPlayersMax; i++, pplr++) { + if (!(pplr->wf_ & kfPlrInUse)) { + continue; + } + + // Computer pplrs don't need to know about the other pplrs + if (pplr->endpoint_ == NULL) { + continue; + } + + // Mark this Connection's PlayerRecord so it knows which + // Player it is. + PlayerRecord *record = ppunm->aplrr; + for (int j = 0; j < cplrs; j++, record++) { + if (pplr->pid() == record->pid) { + record->wf |= kfPlrrLocal; + } else { + record->wf &= ~kfPlrrLocal; + } + } + + pplr->endpoint()->xpump().Send(XMsgGameNetMessage::ToBuffer(ppunm)); + } + + delete ppunm; +} + +void PlayerMgr::BroadcastPlayerDisconnect(Pid pid, int nReason) +{ + Player *pplr = GetPlayerFromPid(pid); + if (pplr == NULL) + return; + + // If already notified of disconnection, do nothing else + // This prevents knDisconnectReasonAbnormal after Kicked or Resigned. + + if (pplr->wf_ & kfPlrDisconnectBroadcasted) { + return; + } + pplr->wf_ |= kfPlrDisconnectBroadcasted; + + // Tell the other clients about it + + PlayerDisconnectNetMessage pdnm; + pdnm.pid = pid; + pdnm.nReason = nReason; + Broadcast(&pdnm); +} + +bool PlayerMgr::HaveAllPlayersReachedUpdate(long cUpdates) { + Player *pplr = players_; + for (int i = 0; i < kcPlayersMax; i++, pplr++) { + if ((pplr->wf_ & kfPlrInUse) && pplr->endpoint_ != NULL) { + if (pplr->updates() < cUpdates) { + return false; + } + } + } + return true; +} + +Player *PlayerMgr::FindLaggingPlayer(long cUpdates) { + // Update lag info for each player, return the most laggy + + long cUpdatesMostLagging = -999; + Player *pplrMostLagging = NULL; + + Player *pplr = players_; + for (int i = 0; i < kcPlayersMax; i++, pplr++) { + if (!(pplr->wf_ & kfPlrInUse) || pplr->endpoint_ == NULL) { + continue; + } + int nLagState = pplr->UpdateLagState(cUpdates); + if (nLagState == knLagGuilty || nLagState == knLagKill) { + long cUpdatesLagging = cUpdates - pplr->updates(); + if (cUpdatesLagging > cUpdatesMostLagging) { + cUpdatesMostLagging = cUpdatesLagging; + pplrMostLagging = pplr; + } + } + } + + return pplrMostLagging; +} + +bool PlayerMgr::AllPlayersLagging() { + Player *pplr = players_; + for (int i = 0; i < kcPlayersMax; i++, pplr++) { + if (!(pplr->wf_ & kfPlrInUse) || pplr->endpoint_ == NULL) { + continue; + } + if (pplr->lag() != knLagKill) { + return false; + } + } + return true; +} + +int PlayerMgr::GetPlayerCount() +{ + int cplr = 0; + Player *pplr = players_; + for (int i = 0; i < kcPlayersMax; i++, pplr++) { + if (pplr->wf_ & kfPlrInUse) { + cplr++; + } + } + + return cplr; +} + +bool PlayerMgr::CheckSyncError(Player *pplr, const UpdateResult& ur, + long *pcUpdatesSync) { + // Add to the end. It should always fit, but just in case, check for that. + + if (pplr->cur_ >= ARRAYSIZE(pplr->aur_)) { + LOG() << "UpdateResult array filled up!"; + return true; + } + pplr->aur_[pplr->cur_] = ur; + pplr->cur_++; + + // Starting from the beginning, march forward through URs for all players. + // If an UR has been received for all players, compare the hash. If there + // is a mismatch, then a sync error has been discovered. If not, left + // shift the UR arrays, and continue. + + long cUpdatesBlock = pplr->aur_[0].cUpdatesBlock; + long hash = pplr->aur_[0].hash; + bool syncerror = false; + bool advance = true; + + while (true) { + Player *pplrT = players_; + for (int i = 0; i < kcPlayersMax; i++, pplrT++) { + if (!(pplrT->wf_ & kfPlrInUse) || pplrT->endpoint_ == NULL) { + continue; + } + if (pplrT->cur_ == 0) { + advance = false; + break; + } + if (pplrT->aur_[0].cUpdatesBlock != cUpdatesBlock) { + advance = false; + break; + } + if (server_.checksync() && pplrT->aur_[0].hash != hash) { + advance = false; + syncerror = true; + break; + } + } + if (!advance) { + break; + } + + // Update the caller's idea of where the players are + *pcUpdatesSync = cUpdatesBlock; + + // All clients have reached this update result. Left shift. + pplrT = players_; + for (int i = 0; i < kcPlayersMax; i++, pplrT++) { + if (!(pplrT->wf_ & kfPlrInUse) || pplrT->endpoint_ == NULL) { + continue; + } + if (pplrT->cur_ == 0) { + LOG() << "shouldn't happen"; + // Shouldn't happen since this was checked already + continue; + } + + // Left shift by one entry + memmove(&pplrT->aur_[0], &pplrT->aur_[1], ELEMENTSIZE(pplrT->aur_) * + (pplrT->cur_ - 1)); + pplrT->cur_--; + } + + // Loop and try the next entry. If this is working correctly, it won't + // find advancement. + } + + return syncerror; +} + +void PlayerMgr::BroadcastSyncError(const UpdateResult& ur) { + // Prepare the net message + + SyncErrorNetMessage senm; + Player *pplrT = players_; + for (int i = 0; i < kcPlayersMax; i++, pplrT++) { + if (pplrT->cur_ != 0) { + senm.aur[i] = pplrT->aur_[0]; + } else { + memset(&senm.aur[i], 0xff, ELEMENTSIZE(senm.aur)); + } + } + senm.urLastStraw = ur; + + pplrT = players_; + for (int i = 0; i < kcPlayersMax; i++, pplrT++) { + if (!(pplrT->wf_ & kfPlrInUse) || pplrT->endpoint_ == NULL) { + continue; + } + pplrT->endpoint()->xpump().Send(XMsgGameNetMessage::ToBuffer(&senm)); + } +} + +} // namespace wi diff --git a/server/playermgr.h b/server/playermgr.h new file mode 100644 index 0000000..bc52e34 --- /dev/null +++ b/server/playermgr.h @@ -0,0 +1,53 @@ +#ifndef __PLAYERMGR_H__ +#define __PLAYERMGR_H__ + +#include "inc/basictypes.h" +#include "server/player.h" +#include "server/endpoint.h" +#include "server/levelinfo.h" +#include "server/server.h" +#include "mpshared/netmessage.h" +#include "mpshared/mpht.h" +#include "mpshared/side.h" + +namespace wi { + +class PlayerMgr +{ +public: + PlayerMgr(Server& server); + + void Init(const LevelInfo& info); + int GetPlayerCount(); + Player *AllocPlayer(word wf); + Player *GetPlayer(Side side); + Player *GetPlayer(Endpoint *endpoint); + Player *GetPlayerFromPid(Pid pid); + Player *GetNextPlayer(Player *pplr); + Player *GetNextHumanPlayer(Player *pplr); + Player *GetNextPlayerWithEndpoint(Player *pplr); + void BroadcastPlayersUpdate(); + void Broadcast(NetMessage *pnm, Pid pidIgnore = kpidNeutral); + void BroadcastPlayerDisconnect(Pid pid, int nReason); + bool HaveAllPlayersReachedUpdate(long cUpdates); + Player *FindLaggingPlayer(long cUpdates); + bool AllPlayersLagging(); + int GetEndpointCount(); + const PlayerName *GetEndpointNames(); + bool CheckSyncError(Player *pplr, const UpdateResult& ur, + long *pcUpdatesSync); + void BroadcastSyncError(const UpdateResult& ur); + void SetLastLagAcknowledge(long64 t) { tLagAcknowledge_ = t; } + long64 GetLastLagAcknowledge() { return tLagAcknowledge_; } + void SwapPlayers(Player *playerA, Player *playerB); + +private: + long64 tLagAcknowledge_; + Player players_[kcPlayersMax]; + PlayerName names_[kcPlayersMax]; + Server& server_; +}; + +} // namespace wi + +#endif // __PLAYERMGR_H__ diff --git a/server/room.cpp b/server/room.cpp new file mode 100644 index 0000000..4b27e3f --- /dev/null +++ b/server/room.cpp @@ -0,0 +1,502 @@ +#include "server/room.h" +#include "server/endpoint.h" +#include "server/game.h" +#include "server/levelinfo.h" +#include "base/tick.h" +#include "base/log.h" +#include "mpshared/mpht.h" + +namespace wi { + +#define kctTimeout (120 * 100) + +const int kcMinutesKickTimeoutDefault = 5; +const int kcMinutesKickTimeoutMaximum = 6 * 60; + +dword Room::s_roomidCounter_; + +Room::Room(Server *server, Endpoint *creator, const char *name, + const char *password, dword roomid, int max_games, int max_players, + dword ff) : server_(server), max_games_(max_games), + max_players_(max_players), ff_(ff), id_(NewRoomId(roomid)) { + name_ = AllocString(name); + password_ = AllocString(password); + timeout_ = base::GetTickCount() + kctTimeout; + + if (creator != NULL) { + creator_ = AllocString(creator->name()); + creator_id_ = creator->id(); + creator->xpump().socket()->GetRemoteAddress().IPAsString(creator_ip_, + sizeof(creator_ip_)); + } else { + creator_ = AllocString("wis"); + creator_id_ = 0; + strncpyz(creator_ip_, "0.0.0.0", sizeof(creator_ip_)); + } +} + +Room::~Room() { + LOG() << base::Log::Format("0x%08lx", this); + Assert(endpointmap_.size() == 0); + SignalOnDelete(this); + delete creator_; + delete name_; + delete password_; +} + +bool Room::SetName(const char *name) { + if (*name == 0) { + return false; + } + if (ff_ & kfRmLocked) { + return false; + } + delete name_; + name_ = AllocString(name); + return true; +} + +dword Room::NewRoomId(dword roomid) { + // Rather than roomid being a static here, it makes more sense for it to be + // an instance variable in lobby. + if (roomid != kroomidInvalid) { + return roomid; + } + s_roomidCounter_++; + while (s_roomidCounter_ == kroomidInvalid || + s_roomidCounter_ == kroomidAdmin || + s_roomidCounter_ == kroomidMain || + s_roomidCounter_ == kroomidRegistered || + s_roomidCounter_ == kroomidUnmoderated) { + s_roomidCounter_++; + } + return s_roomidCounter_; +} + +std::vector Room::GetIdsString(Endpoint *endpoint) { + std::vector responses; + EndpointMap::iterator it = endpointmap_.begin(); + for (; it != endpointmap_.end(); it++) { + if (endpoint->IsAdmin()) { + char ip[32]; + it->second->xpump().socket()-> + GetRemoteAddress().IPAsString(ip, sizeof(ip)); + responses.push_back(base::Format::ToString("%s: id %d ip %s", + it->second->name(), + it->second->server().GetChatterId(endpoint, it->second), + ip)); + } else { + responses.push_back(base::Format::ToString("%s: id %d", + it->second->name(), + it->second->server().GetChatterId(endpoint, it->second))); + } + } + return responses; +} + +bool Room::ProcessCommand(Endpoint *endpoint, const char *chat, + std::string *response, bool *broadcast) { + ModeratorCommand cmd = endpoint->GetModeratorCommand(chat); + if (cmd != kModeratorCommandNone) { + server_->logger().LogModCommand(endpoint, chat); + } + + switch (cmd) { + case kModeratorCommandKick: + { + // Check the room the moderator is in, not the room the player is + // in. This way, remote action is still available. + if (!endpoint->CanModerate(endpoint->roomid())) { + *response = "Can't moderate in this room."; + return true; + } + + Endpoint *endpointKick = NULL; + std::string arg; + if (endpoint->GetArgument(chat, 1, &arg)) { + dword chatter_id = 0; + base::Format::ToDword(arg.c_str(), 10, &chatter_id); + endpointKick = endpoint->server().GetEndpointFromChatterId( + chatter_id); + } + if (endpointKick == NULL) { + *response = "Could not find player using that id."; + break; + } + if (endpointKick->IsModerator()) { + *response = "You cannot kick another moderator."; + break; + } + std::string minutes_str; + int minutes = kcMinutesKickTimeoutDefault; + if (endpoint->GetArgument(chat, 2, &minutes_str)) { + base::Format::ToInteger(minutes_str.c_str(), 10, &minutes); + if (minutes < 0) { + minutes = kcMinutesKickTimeoutDefault; + } + if (minutes > kcMinutesKickTimeoutMaximum) { + minutes = kcMinutesKickTimeoutMaximum; + } + } + char ip[32]; + endpointKick->xpump().socket()->GetRemoteAddress().IPAsString(ip, + sizeof(ip)); + RLOG() << "mod: " << endpoint->name() << " kicked: " + << endpointKick->name() << " minutes: " << minutes + << " ip address: " << ip; + long64 tExpires = base::GetMillisecondCount() + + minutes * 60 * 1000; + endpointKick->AddTracker(tracker_, tExpires); + endpointKick->AddTracker(endpointKick->server().lobby().room_tracker(), tExpires); + endpointKick->server().chatlimiter().Mute(endpointKick, minutes); + + *response = base::Format::ToString("%s has been kicked from this room, is muted, and can't create new rooms for %d minute(s). Action logged.", endpointKick->name(), minutes); + endpointKick->Dispose(); + } + return true; + + case kModeratorCommandRules: + // Check the room the moderator is in, not the room the player is + // in. This way, remote action is still available. + if (!endpoint->CanModerate(endpoint->roomid())) { + *response = "Can't moderate in this room."; + return true; + } + + *response = endpoint->server().GetChatRules(); + *broadcast = true; + return true; + + case kModeratorCommandNone: + return false; + + default: + break; + } + return true; +} + +bool Room::Kill() { + if (ff_ & kfRmPermanent) { + return false; + } + + // Kick the players in the games, and the players in this room + { + GameMap::iterator it = gamemap_.begin(); + for (; it != gamemap_.end(); it++) { + it->second->KickPlayers(); + } + } + { + EndpointMap::iterator it = endpointmap_.begin(); + for (; it != endpointmap_.end(); it++) { + if (!it->second->IsModerator()) { + it->second->Dispose(); + } + } + } + + // Expire when possible + ff_ |= kfRmForceExpire; + return true; +} + +bool Room::TogglePermanent(bool *result) { + if (ff_ & (kfRmLocked | kfRmForceExpire)) { + return false; + } + ff_ ^= kfRmPermanent; + *result = ((ff_ & kfRmPermanent) != 0); + return true; +} + +bool Room::ToggleRegistered(bool *result) { + ff_ ^= kfRmRegisteredOnly; + *result = ((ff_ & kfRmRegisteredOnly) != 0); + return true; +} + +std::vector Room::GetGameIds() { + std::vector ids; + GameMap::iterator it = gamemap_.begin(); + for (; it != gamemap_.end(); it++) { + ids.push_back(it->first); + } + return ids; +} + +void Room::SendChat(Endpoint *endpoint, const char *chat, + const char *unfiltered) { + bool broadcast = false; + std::string response; + if (endpoint != NULL && ProcessCommand(endpoint, chat, &response, + &broadcast)) { + if (!broadcast) { + if (response.size() != 0) { + endpoint->xpump().Send(XMsgRoomReceiveChat::ToBuffer("", + response.c_str())); + server_->logger().LogSystemMsg(endpoint, response.c_str()); + } + return; + } + chat = response.c_str(); + } + + std::string from = ""; + if (endpoint != NULL) { + from = endpoint->GetChatName(); + } + + if (unfiltered != NULL) { + server_->logger().LogRoomChat(endpoint, id(), name(), + password()[0] != 0, unfiltered); + } else { + server_->logger().LogRoomChat(endpoint, id(), name(), + password()[0] != 0, chat); + } + + LOG() << from << " said: " << chat; + EndpointMap::iterator it = endpointmap_.begin(); + for (; it != endpointmap_.end(); it++) { + if (it->second->muted()) { + continue; + } + it->second->xpump().Send(XMsgRoomReceiveChat::ToBuffer(from.c_str(), + chat)); + if (unfiltered != NULL && it->second->seechat()) { + it->second->xpump().Send(XMsgRoomReceiveChat::ToBuffer("", + unfiltered)); + } + } +} + +void Room::SendAdminChat(const char *name, const char *chat, bool mods_only) { + for (EndpointMap::iterator it = endpointmap_.begin(); + it != endpointmap_.end(); it++) { + Endpoint *endpoint = it->second; + if (mods_only && !endpoint->IsModerator()) { + continue; + } + endpoint->xpump().Send(XMsgRoomReceiveChat::ToBuffer(name, chat)); + } + + for (GameMap::iterator it = gamemap_.begin(); it != gamemap_.end(); it++) { + Game *game = it->second; + game->SendAdminChat(name, chat, mods_only); + } +} + +bool Room::CanAddGame(Endpoint *endpoint) { + return gamemap_.size() <= max_games_; +} + +void Room::AddGame(Endpoint *endpoint, Game *game) { + gamemap_.insert(GameMap::value_type(game->id(), game)); + game->SignalOnDelete.connect(this, &Room::OnGameDelete); + game->SignalOnInProgress.connect(this, &Room::OnGameInProgress); + game->SignalOnPlayersChange.connect(this, &Room::OnGamePlayersChange); + Broadcast(XMsgRoomAddGame::ToBuffer(endpoint->name(), game->id(), + game->params(), game->info().minplayers(), + game->info().maxplayers(), game->info().title(), gamemap_.size())); + SignalOnGamesChange(this); + LOG() << gamemap_.size() << " games."; + + // Timeout the room in the future. This gives players joining this game + // time to join, since they need to leave the room in order to join. + timeout_ = base::GetTickCount() + kctTimeout; +} + +void Room::RemoveGame(Game *game, bool disconnect) { + GameMap::iterator it = gamemap_.find(game->id()); + if (it == gamemap_.end()) { + LOG() << "Game not found!"; + return; + } + gamemap_.erase(it); + if (disconnect) { + game->SignalOnDelete.disconnect(this); + game->SignalOnInProgress.disconnect(this); + game->SignalOnPlayersChange.disconnect(this); + } + Broadcast(XMsgRoomRemoveGame::ToBuffer(game->id(), gamemap_.size())); + SignalOnGamesChange(this); + LOG() << gamemap_.size() << " games."; + + // Timeout the room in the future. This gives players leaving this + // game time to come back to the room before the room times out. + timeout_ = base::GetTickCount() + kctTimeout; +} + +dword Room::CanAddPlayer(Endpoint *endpoint, const char *password) { + if (disposed_) { + return knRoomJoinResultNotFound; + } + // Admins can always join rooms + if (endpoint->IsAdmin()) { + return knRoomJoinResultSuccess; + } + + if ((ff_ & kfRmForceExpire) != 0) { + return knRoomJoinResultFail; + } + + bool check = true; + if (check && endpointmap_.size() >= max_players_) { + return knRoomJoinResultFull; + } + if (registeredonly() && endpoint->anonymous()) { + return knRoomJoinResultFail; + } + if (strcmp(password, password_) != 0) { + return knRoomJoinResultWrongPassword; + } + + // user blocked in this room? + long64 tExpires; + if (endpoint->FindTracker(tracker_, &tExpires)) { + if (base::GetMillisecondCount() < tExpires) { + return knRoomJoinResultFail; + } + endpoint->RemoveTracker(tracker_); + } + + return knRoomJoinResultSuccess; +} + +void Room::AddPlayer(Endpoint *endpoint) { + // Tell this endpoint about the games in this room + for (GameMap::iterator it = gamemap_.begin(); it != gamemap_.end(); it++) { + Game *game = it->second; + endpoint->xpump().Send(XMsgRoomAddGame::ToBuffer(game->creator(), + game->id(), game->params(), game->info().minplayers(), + game->info().maxplayers(), game->info().title(), + gamemap_.size())); + endpoint->xpump().Send(XMsgRoomGamePlayerNames::ToBuffer(game->id(), + game->GetNameCount(), game->GetNames())); + if (game->playing()) { + endpoint->xpump().Send(XMsgRoomGameInProgress::ToBuffer( + game->id())); + } + } + + // Tell this endpoint about the players in this room + EndpointMap::iterator it = endpointmap_.begin(); + for (; it != endpointmap_.end(); it++) { + Endpoint *endpointT = it->second; + endpoint->xpump().Send(XMsgRoomAddPlayer::ToBuffer(endpointT->name())); + } + + // Tell other endpoints about this player joining the room + Broadcast(XMsgRoomAddPlayer::ToBuffer(endpoint->name())); + + // This room wants to know when this endpoint goes away + endpointmap_.insert(EndpointMap::value_type(endpoint->id(), endpoint)); + endpoint->SignalOnDelete.connect(this, &Room::OnEndpointDelete); + + // Reset the timeout for this room + timeout_ = base::GetTickCount() + kctTimeout; + + // Tell listeners about the player change + SignalOnPlayersChange(this); +} + +void Room::RemovePlayer(Endpoint *endpoint, dword hint, bool disconnect) { + EndpointMap::iterator it = endpointmap_.find(endpoint->id()); + if (it == endpointmap_.end()) { + LOG() << "Player not found!"; + return; + } + endpointmap_.erase(it); + + if (disconnect) { + endpoint->SignalOnDelete.disconnect(this); + } + + // Tell other endpoints about this player going away + Broadcast(XMsgRoomRemovePlayer::ToBuffer(hint, endpoint->name())); + + // Tell listeners about the player change + SignalOnPlayersChange(this); +} + +void Room::OnEndpointDelete(Endpoint *endpoint) { + LOG() << "removing player " << endpoint->name(); + RemovePlayer(endpoint, 0, false); +} + +void Room::OnGameDelete(Game *game) { + LOG() << "removing game " << game->info().title() << " created by: " << + game->creator(); + RemoveGame(game, false); +} + +void Room::OnGameInProgress(Game *game) { + Broadcast(XMsgRoomGameInProgress::ToBuffer(game->id())); +} + +void Room::OnGamePlayersChange(Game *game) { + Broadcast(XMsgRoomGamePlayerNames::ToBuffer(game->id(), + game->GetNameCount(), game->GetNames())); + SignalOnPlayersChange(this); +} + +void Room::Broadcast(base::ByteBuffer *bb) { + // Once sent, byte buffers are owned by the xpump instance. Clone a + // new byte buffer for each xpump instance beyond the first one. + EndpointMap::iterator it = endpointmap_.begin(); + for (int i = 0; it != endpointmap_.end(); it++, i++) { + Endpoint *endpoint = it->second; + base::ByteBuffer *bbNext = NULL; + if (i < endpointmap_.size() - 1) { + bbNext = bb->Clone(); + } + endpoint->xpump().Send(bb); + bb = bbNext; + } +} + +Game *Room::FindGame(dword id) { + GameMap::iterator it = gamemap_.find(id); + if (it == gamemap_.end()) { + return NULL; + } + return it->second; +} + +void Room::OnHeartbeat() { + // Don't time out permanent rooms + if (ff_ & kfRmPermanent) { + return; + } + + // If there are players in the room, the timeout is always in the future + if (endpointmap_.size() != 0) { + timeout_ = base::GetTickCount() + kctTimeout; + return; + } + + // If games are present, timeout in the future + if (gamemap_.size() != 0) { + timeout_ = base::GetTickCount() + kctTimeout; + return; + } + + // If timed out, remove the room. + if ((ff_ & kfRmForceExpire) != 0 || base::GetTickCount() >= timeout_) { + // Dispose asynchronously destroys this room. If a player joins just + // before the dispose, it would be bad, so the dispose_ flag is checked + // here and there. + Dispose(); + } +} + +int Room::GetPlayerCount() { + int cPlayers = endpointmap_.size(); + for (GameMap::iterator it = gamemap_.begin(); it != gamemap_.end(); it++) { + cPlayers += it->second->GetNameCount(); + } + return cPlayers; +} + +} // namespace wi diff --git a/server/room.h b/server/room.h new file mode 100644 index 0000000..449cfb1 --- /dev/null +++ b/server/room.h @@ -0,0 +1,113 @@ +#ifndef __ROOM_H__ +#define __ROOM_H__ + +#include "inc/basictypes.h" +#include "base/sigslot.h" +#include "base/bytebuffer.h" +#include "base/messagehandler.h" +#include "mpshared/misc.h" +#include "server/tracker.h" +#include +#include +#include + +namespace wi { + +// Ensures Main is at top of sort, Admin is at bottom. +// Higher values are higher in the list. +const dword kroomidMain = (dword)-1; +const dword kroomidAdmin = 1; +const dword kroomidUnmoderated = 2; +const dword kroomidRegistered = 3; +const dword kroomidInvalid = 0; + +// Room flags +const dword kfRmPermanent = 1; +const dword kfRmUnlimited = 2; +const dword kfRmRegisteredOnly = 4; +const dword kfRmForceExpire = 8; +const dword kfRmLocked = 16; +const dword kfRmUnmoderated = 32; + +class Endpoint; +class Game; +class Server; + +class Room : base::MessageHandler, public base::has_slots<> { +public: + Room(Server *server, Endpoint *creator, const char *name, + const char *password, dword roomid, int max_games_, + int max_players_, dword ff); + ~Room(); + + void SendChat(Endpoint *endpoint, const char *chat, + const char *unfiltered = NULL); + void SendAdminChat(const char *name, const char *chat, + bool mods_only = false); + bool CanAddGame(Endpoint *endpoint); + void AddGame(Endpoint *endpoint, Game *game); + void RemoveGame(Game *game, bool disconnect = true); + dword CanAddPlayer(Endpoint *endpoint, const char *password); + void AddPlayer(Endpoint *endpoint); + void RemovePlayer(Endpoint *endpoint, dword hint = 0, + bool disconnect = true); + Game *FindGame(dword id); + std::vector GetGameIds(); + int GetPlayerCount(); + bool Kill(); + bool TogglePermanent(bool *result); + bool ToggleRegistered(bool *result); + bool HasHeartbeat() { return true; } + void OnHeartbeat(); + std::vector GetIdsString(Endpoint *endpoint); + bool SetName(const char *name); + + bool active() { return !disposed_; } + const char *name() { return name_; } + const char *password() { return password_; } + const char *creator() { return creator_; } + const char *creator_ip() { return creator_ip_; } + dword creator_id() { return creator_id_; } + dword id() { return id_; } + int cGames() { return gamemap_.size(); } + bool unlimited() { return (ff_ & kfRmUnlimited) != 0; } + bool registeredonly() { return (ff_ & kfRmRegisteredOnly) != 0; } + bool moderated() { return (ff_ & kfRmUnmoderated) == 0; } + Tracker& tracker() { return tracker_; } + + base::signal1 SignalOnDelete; + base::signal1 SignalOnPlayersChange; + base::signal1 SignalOnGamesChange; + +private: + void OnEndpointDelete(Endpoint *endpoint); + void OnGameDelete(Game *game); + void OnGameInProgress(Game *game); + void OnGamePlayersChange(Game *game); + void Broadcast(base::ByteBuffer *bb); + dword NewRoomId(dword roomid); + bool ProcessCommand(Endpoint *endpoint, const char *chat, + std::string *response, bool *broadcast); + + int max_games_; + int max_players_; + long64 timeout_; + const char *name_; + const char *password_; + const char *creator_; + char creator_ip_[32]; + dword creator_id_; + typedef std::map GameMap; + GameMap gamemap_; + typedef std::map EndpointMap; + EndpointMap endpointmap_; + dword ff_; + dword id_; + static dword s_roomidCounter_; + Tracker tracker_; + Server *server_; +}; + +} // namespace wi + +#endif // __ROOM_H__ diff --git a/server/secrets.cpp b/server/secrets.cpp new file mode 100644 index 0000000..859b7c4 --- /dev/null +++ b/server/secrets.cpp @@ -0,0 +1,17 @@ +#include "secrets.h" + +// Keep in sync with stats/config.py secrets +// Secrets should be in a shared file rather than maintained this way + +namespace wi { + +// For updating the leaderboard with this server's current state +const char *kszServerInfoSecret = "REPLACEME_SERVERINFOSECRET"; + +// For posting game results to leaderboard +const char *kszStatSecret = "REPLACEME_ADDGAMESTATSSECRET"; + +// For validating auth tokens +const char *kszTokenAuthSecret = "REPLACEME_AUTHGOODSECRET"; + +} diff --git a/server/secrets.h b/server/secrets.h new file mode 100644 index 0000000..01b0d73 --- /dev/null +++ b/server/secrets.h @@ -0,0 +1,12 @@ +#ifndef __SECRETS_H__ +#define __SECRETS_H__ + +namespace wi { + +extern const char *kszServerInfoSecret; +extern const char *kszStatSecret; +extern const char *kszTokenAuthSecret; + +}; + +#endif // __SECRETS_H__ diff --git a/server/server.cpp b/server/server.cpp new file mode 100644 index 0000000..de7d386 --- /dev/null +++ b/server/server.cpp @@ -0,0 +1,343 @@ +#include "inc/rip.h" +#include "base/log.h" +#include "base/tick.h" +#include "base/thread.h" +#include "server/game.h" +#include "server/server.h" + +namespace wi { + +const dword kidmFlushTimer = 1; +const dword kidmGameHeartbeat = 2; +const dword kidmEndpointHeartbeat = 3; + +#define kctFlushTimer (30 * 100) +#define kctGameHeartbeat 25 +#define kctEndpointHeartbeat (30 * 100) + +// Change ChatterId calculation if these are changed. +const dword kffEndpointIdMask = 0x7ff; +const dword kcBitsFreeShiftEndpointId = 5; +const dword kffEndpointIdFreeMaskAfterShift = 0x1f; + +Server::Server(StatsPoster& poster, XMsgLog *log, LevelInfoCache& cache, + dword id, bool checksync, int max_rooms, int max_games_per_room, + int max_players_per_room, int max_players, + const std::string& modlist_path, const std::string& badwords_path) : + poster_(poster), log_(log), cache_(cache), + lobby_(this, max_rooms, max_games_per_room, max_players_per_room), + id_(id), checksync_(checksync), max_players_(max_players), + modlist_watcher_(modlist_path), badwords_(badwords_path), + listener_(NULL), gameidCounter_(1), endpointidCounter_(1), + endpoint_count_thread_safe_(0), updater_(NULL), + logger_("log", id) { + + start_time_ = base::GetSecondsUnixEpocUTC(); + + // TODO: Move logging into separate logging class w/async thread + if (log_ != NULL) { + thread_.PostTimer(kidmFlushTimer, this, kctFlushTimer); + } + thread_.PostTimer(kidmGameHeartbeat, this, kctGameHeartbeat); + thread_.PostTimer(kidmEndpointHeartbeat, this, kctEndpointHeartbeat); + modlist_watcher_.SignalOnFileUpdated.connect(this, + &Server::OnModeratorListUpdated); +} + +Server::~Server() { + if (listener_ != NULL) { + listener_->Close(); + delete listener_; + } + if (updater_ != NULL) { + updater_->SignalOnResponse.disconnect(this); + } +} + +void Server::OnMessage(base::Message *pmsg) { + switch (pmsg->id) { + case kidmFlushTimer: + if (log_ != NULL) { + log_->Flush(); + } + break; + + case kidmGameHeartbeat: + { + GameMap::iterator it = gamemap_.begin(); + while (it != gamemap_.end()) { + if (it->second->HasHeartbeat()) { + it->second->OnHeartbeat(); + } + it++; + } + } + break; + + case kidmEndpointHeartbeat: + { + EndpointMap::iterator it = endpointmap_.begin(); + while (it != endpointmap_.end()) { + if (it->second->HasHeartbeat()) { + it->second->OnHeartbeat(); + } + it++; + } + } + break; + } +} + +bool Server::Listen(const base::SocketAddress& addr) { + if (listener_ == NULL) { + listener_ = thread_.ss().CreateSocket(SOCK_STREAM, this); + if (listener_ == NULL) { + return false; + } + } + + if (listener_->Bind(addr, true) < 0) { + return false; + } + if (listener_->Listen() < 0) { + return false; + } + + RLOG() << "Listening on: " << listener_->GetLocalAddress().ToString(); + return true; +} + +void Server::OnIncomingConnection(base::Socket *incoming) { + bool serverfull = (endpointmap_.size() >= max_players_); + dword id = NewEndpointId(); + Endpoint *endpoint = new Endpoint(*this, incoming, id, serverfull); + if (endpoint == NULL) { + RLOG() << "Error creating endpoint! Closing incoming socket."; + delete incoming; + return; + } + endpointmap_.insert(EndpointMap::value_type(id, endpoint)); + endpoint_count_thread_safe_ = endpointmap_.size(); + endpoint->SignalOnDelete.connect(this, &Server::OnEndpointDelete); + LOG() << endpointmap_.size() << " endpoints."; +} + +Endpoint *Server::GetEndpoint(dword id) { + EndpointMap::iterator it = endpointmap_.find(id); + if (it == endpointmap_.end()) { + return NULL; + } + return it->second; +} + +void Server::OnEndpointDelete(Endpoint *endpoint) { + EndpointMap::iterator it = endpointmap_.find(endpoint->id()); + if (it == endpointmap_.end()) { + RLOG() << "couldn't find endpoint."; + return; + } + endpointmap_.erase(it); + endpoint_count_thread_safe_ = endpointmap_.size(); + LOG() << endpointmap_.size() << " endpoints."; +} + +Game *Server::NewGame(Endpoint *endpoint, const GameParams& params, + const LevelInfo& info, dword roomid) { + Game *game = new Game(endpoint, params, info, *this, roomid, 0); + gamemap_.insert(GameMap::value_type(game->id(), game)); + game->SignalOnDelete.connect(this, &Server::OnGameDelete); + return game; +} + +void Server::OnGameDelete(Game *game) { + GameMap::iterator it = gamemap_.find(game->id()); + if (it == gamemap_.end()) { + RLOG() << "couldn't find game."; + return; + } + gamemap_.erase(it); + LOG() << gamemap_.size() << " games."; +} + +void Server::OnConnectEvent(base::Socket *socket) { + Assert(false); +} + +void Server::OnReadEvent(base::Socket *socket) { + Assert(socket == listener_); + while (true) { + base::Socket *incoming = socket->Accept(NULL); + if (incoming == NULL) { + if (socket->IsBlocking()) { + return; + } + continue; + } + + // If the ip is banned currently, don't allow the connection. + base::SocketAddress addr = incoming->GetRemoteAddress(); + char szT[64]; + addr.IPToString(addr.ip(), szT, sizeof(szT)); + if (tracker_.Find(szT)) { + delete incoming; + continue; + } + LOG() << "Incoming connection from: " << addr.ToString(); + OnIncomingConnection(incoming); + } +} + +void Server::OnWriteEvent(base::Socket *socket) { + LOG() << "Unexpected Write event on listening socket"; + Assert(false); +} + +void Server::OnCloseEvent(base::Socket *socket) { + LOG() << "Unexpected Close event on listening socket"; +} + +const char *Server::GetChatRules() { + return "RULES: No swearing, name calling, sexism, racism, offensive language, or offensive symbolism allowed. No spamming allowed. Repeat offenders may be muted, kicked, banned, and reported to the admin."; +} + +dword Server::GetChatterId(Endpoint *endpointAsker, Endpoint *endpoint) { + dword shifted = (endpoint->id() << kcBitsFreeShiftEndpointId); + dword h = 0; + if (!endpointAsker->IsAdmin()) { + h = HashString(endpoint->name()); + } + return (shifted | (h & kffEndpointIdFreeMaskAfterShift)); +} + +Endpoint *Server::GetEndpointFromChatterId(dword chatter_id) { + dword id = (chatter_id >> kcBitsFreeShiftEndpointId) & kffEndpointIdMask; + return GetEndpoint(id); +} + +dword Server::NewEndpointId() { + // This is user visible; it is used for the anonymous login id, + // so keep the integer as small as possible + + while (true) { + endpointidCounter_++; + if (endpointidCounter_ > kffEndpointIdMask) { + endpointidCounter_ = 1; + } + EndpointMap::iterator it = endpointmap_.find(endpointidCounter_); + if (it == endpointmap_.end()) { + return endpointidCounter_; + } + } +} + +void Server::SetUpdater(ServerInfoUpdater *updater) { + updater_ = updater; + updater_->SignalOnResponse.connect(this, &Server::OnUpdaterResponse); +} + +void Server::OnUpdaterResponse(ServerInfoUpdater *updater, + const base::ByteBuffer& response) { + LOG() << std::string((const char *)response.Data(), response.Length()); + + // Commands get sent to the game server as responses to ServerInfo + // requests. This is where the responses get processed. Try to + // parse as a json object, and if success do command dispatching. + json::JsonBuilder builder; + builder.Start(NULL); + builder.Update((const char *)response.Data(), response.Length()); + json::JsonObject *obj = builder.End(); + if (obj == NULL || obj->type() != json::JSONTYPE_MAP) { + delete obj; + return; + } + json::JsonMap *map = (json::JsonMap *)obj; + + // Look for a command + const json::JsonObject *objT = map->GetObject("command"); + if (objT == NULL || objT->type() != json::JSONTYPE_STRING) { + delete obj; + return; + } + + json::JsonString *obj_command = (json::JsonString *)objT; + OnCommand(obj_command->GetString(), map); + delete obj; +} + +void Server::OnCommand(const std::string command, const json::JsonMap *map) { + if (command == "drain") { + if (updater_ != NULL) { + updater_->set_drain(); + } + return; + } else if (command == "undrain") { + if (updater_ != NULL) { + updater_->clear_drain(); + } + return; + } else if (command == "chat") { + const json::JsonObject *objN = map->GetObject("name"); + if (objN == NULL || objN->type() != json::JSONTYPE_STRING) { + return; + } + + const json::JsonObject *objM = map->GetObject("message"); + if (objM == NULL || objM->type() != json::JSONTYPE_STRING) { + return; + } + + json::JsonString *name = (json::JsonString *)objN; + json::JsonString *message = (json::JsonString *)objM; + lobby_.SendAdminChat(name->GetString(), message->GetString()); + } +} + +void Server::OnModeratorListUpdated(ThreadedFileWatcher *watcher) { + RLOG() << watcher->filename() << " has changed!"; + FILE *f = fopen(watcher->filename().c_str(), "r"); + if (f != NULL) { + std::vector moderator_names; + std::vector admin_names; + char sz[256]; + while (fgets(sz, sizeof(sz), f) != NULL) { + int cch; + const char *start = StripWhitespace(sz, &cch); + if (cch == 0) { + continue; + } + if (strncmp(start, "mod,", 4) == 0) { + std::string name(start + 4, cch - 4); + RLOG() << "moderator: '" << name << "'"; + moderator_names.push_back(name); + } + if (strncmp(start, "admin,", 6) == 0) { + std::string name(start + 6, cch - 6); + RLOG() << "admin: '" << name << "'"; + admin_names.push_back(name); + } + } + fclose(f); + moderator_names_ = moderator_names; + admin_names_ = admin_names; + } +} + +bool Server::IsModerator(const char *name) { + for (int i = 0; i < moderator_names_.size(); i++) { + if (stricmp(name, moderator_names_[i].c_str()) == 0) { + return true; + } + } + return false; +} + +bool Server::IsAdmin(const char *name) { + for (int i = 0; i < admin_names_.size(); i++) { + if (stricmp(name, admin_names_[i].c_str()) == 0) { + return true; + } + } + return false; +} + +} // namespace wi diff --git a/server/server.h b/server/server.h new file mode 100644 index 0000000..7b12285 --- /dev/null +++ b/server/server.h @@ -0,0 +1,112 @@ +#ifndef __SERVER_H__ +#define __SERVER_H__ + +#include "inc/basictypes.h" +#include "base/socketserver.h" +#include "base/socketaddress.h" +#include "base/socket.h" +#include "base/sigslot.h" +#include "base/messagehandler.h" +#include "mpshared/xmsglog.h" +#include "server/chatlimiter.h" +#include "server/endpoint.h" +#include "server/room.h" +#include "server/levelinfocache.h" +#include "server/serverinfoupdater.h" +#include "server/filewatcher.h" +#include "server/tracker.h" +#include "server/badwords.h" +#include "server/logger.h" +#include "yajl/wrapper/jsonbuilder.h" +#include + +namespace wi { + +class Endpoint; +class Game; +class StatsPoster; +class ServerInfoUpdater; + +class Server : base::MessageHandler, base::SocketNotify, + public base::has_slots<> { +public: + Server(StatsPoster& stats, XMsgLog *log, LevelInfoCache& cache, + dword id, bool checksync, int max_rooms, int max_games_per_room, + int max_players_per_room, int max_players, + const std::string& modlist_path, const std::string& badwords_path); + ~Server(); + + bool Listen(const base::SocketAddress& addr); + Game *NewGame(Endpoint *endpoint, const GameParams& params, + const LevelInfo& info, dword roomid); + dword NewEndpointId(); + void SetUpdater(ServerInfoUpdater *updater); + Endpoint *GetEndpoint(dword id); + bool IsModerator(const char *name); + bool IsAdmin(const char *name); + dword GetChatterId(Endpoint *endpointAsker, Endpoint *endpoint); + Endpoint *GetEndpointFromChatterId(dword id); + const char *GetChatRules(); + + ChatLimiter& chatlimiter() { return chatlimiter_; } + LevelInfoCache& cache() { return cache_; } + Lobby& lobby() { return lobby_; } + XMsgLog *log() { return log_; } + StatsPoster& poster() { return poster_; } + bool checksync() { return checksync_; } + dword start_time() { return start_time_; } + dword id() { return id_; } + int endpoint_count_thread_safe() { return endpoint_count_thread_safe_; } + base::SocketAddress listen_address() { return listener_->GetLocalAddress(); } + Tracker& tracker() { return tracker_; } + BadWords& badwords() { return badwords_; } + Logger& logger() { return logger_; } + +private: + void OnEndpointDelete(Endpoint *endpoint); + void OnGameDelete(Game *game); + void OnIncomingConnection(base::Socket *incoming); + void OnUpdaterResponse(ServerInfoUpdater *updater, + const base::ByteBuffer& response); + void OnCommand(const std::string command, const json::JsonMap *map); + void OnModeratorListUpdated(ThreadedFileWatcher *watcher); + + // SocketNotify interface + virtual void OnConnectEvent(base::Socket *socket); + virtual void OnReadEvent(base::Socket *socket); + virtual void OnWriteEvent(base::Socket *socket); + virtual void OnCloseEvent(base::Socket *socket); + + // MessageHandler interface + virtual void OnMessage(base::Message *pmsg); + + XMsgLog *log_; + base::Socket *listener_; + LevelInfoCache& cache_; + bool checksync_; + dword gameidCounter_; + dword endpointidCounter_; + dword start_time_; + dword id_; + + Logger logger_; + ChatLimiter chatlimiter_; + Lobby lobby_; + typedef std::map EndpointMap; + EndpointMap endpointmap_; + int endpoint_count_thread_safe_; + typedef std::map GameMap; + GameMap gamemap_; + StatsPoster& poster_; + int max_players_; + ServerInfoUpdater *updater_; + ThreadedFileWatcher modlist_watcher_; + std::vector moderator_names_; + std::vector admin_names_; + Tracker tracker_; + BadWords badwords_; +}; + +} // namespace wi + +#endif // __SERVER_H__ diff --git a/server/serverinfoupdater.cpp b/server/serverinfoupdater.cpp new file mode 100644 index 0000000..10ef1c7 --- /dev/null +++ b/server/serverinfoupdater.cpp @@ -0,0 +1,188 @@ +#include "server/serverinfoupdater.h" +#include "server/secrets.h" +#include "base/base64.h" +#include "base/md5.h" +#include "base/log.h" +#include "base/tick.h" +#include "mpshared/constants.h" +#include "mpshared/misc.h" +#include "yajl/wrapper/jsonbuilder.h" +#include + +namespace wi { + +const int MSG_SERVERINFORESULT = 1; + +ServerInfoUpdater::ServerInfoUpdater(Server& server, + base::SocketAddress& post_address, const std::string& post_path, + int sort_key, const std::string& server_name, + const std::string& server_location, const std::string& server_type, + base::SocketAddress& public_address, const std::string& extra_json, + int expires) : server_(server), + post_address_(post_address), post_path_(post_path), + sort_key_(sort_key), server_name_(server_name), + server_location_(server_location), server_type_(server_type), + public_address_(public_address), expires_(expires), drain_(false) { + ParseExtra(extra_json); + worker_.Start(this, &ServerInfoUpdater::ThreadStart); +} + +ServerInfoUpdater::~ServerInfoUpdater() { + // Thread::Stop gets called in the Thread destructor, which does a join + // with the actual thread, which synchronizes exiting +} + +void ServerInfoUpdater::OnPostComplete(HttpPost *post, int status_code, + int error, const base::ByteBuffer& result) { + if (status_code != 200) { + RLOG() << "ERROR posting ServerInfo, status code: " << status_code; + return; + } + + // Pass on the result, if there is one. + if (result.Length() != 0) { + thread_.Post(MSG_SERVERINFORESULT, this, + new ServerInfoResult(result)); + } +} + +void ServerInfoUpdater::OnMessage(base::Message *pmsg) { + if (pmsg->id == MSG_SERVERINFORESULT) { + ServerInfoResult *res = (ServerInfoResult *)pmsg->data; + SignalOnResponse(this, res->result); + delete res; + } +} + +void ServerInfoUpdater::ThreadStart(void *pv) { + // Create and send a new ServerInfo post on an expires_ / 2 interval, + // to keep it fresh. expires_ in seconds, so convert to ticks (100/second). + while (true) { + HttpPost *post = new HttpPost(post_address_, post_path_, MakeBody()); + if (post != NULL) { + post->SignalOnComplete.connect(this, + &ServerInfoUpdater::OnPostComplete); + post->Submit(); + } + worker_.RunLoop(expires_ * 100 / 2); + if (post != NULL) { + post->SignalOnComplete.disconnect(this); + delete post; + post = NULL; + } + if (worker_.IsStopping()) { + break; + } + } +} + +base::ByteBuffer *ServerInfoUpdater::MakeBody() { + std::string json = MakeJson(); + + // Create HMAC like signature of hash(json + secret) so the receiver + // can ensure it is valid. + MD5_CTX md5; + MD5Init(&md5); + MD5Update(&md5, (const byte *)json.c_str(), json.size()); + MD5Update(&md5, (const byte *)kszServerInfoSecret, + strlen(kszServerInfoSecret)); + byte hash[16]; + MD5Final(hash, &md5); + char hash_str[33]; + strncpyz(hash_str, base::Format::ToHex(hash, sizeof(hash)), + sizeof(hash_str)); + base::ByteBuffer *bb = new base::ByteBuffer(32 + json.size()); + bb->WriteBytes((const byte *)hash_str, 32); + bb->WriteBytes((const byte *)json.c_str(), json.size()); + return bb; +} + +// See stats/serverinfo.py for json description. The poster generates +// one of the info maps. + +std::string ServerInfoUpdater::MakeJson() { +#ifdef DEBUG + yajl_gen_config yajl_config = { 1, " " }; +#else + yajl_gen_config yajl_config = { 0, NULL }; +#endif + yajl_gen g = yajl_gen_alloc(&yajl_config); + + yajl_gen_map_open(g); + GenNum(g, "sort_key", sort_key_); + GenString(g, "name", server_name_.c_str()); + GenString(g, "location", server_location_.c_str()); + GenString(g, "address", public_address_.ToString()); + GenNum(g, "protocol", kdwProtocolCurrent); + if (drain_) { + GenString(g, "status", "drain"); + } else { + GenString(g, "status", "ok"); + } + GenNum(g, "expires_utc", base::GetSecondsUnixEpocUTC() + expires_); + GenNum(g, "start_utc", server_.start_time()); + GenNum(g, "player_count", server_.endpoint_count_thread_safe()); + GenString(g, "type", server_type_.c_str()); + + // Add in the extras + ExtraMap::iterator it = extra_.begin(); + for (; it != extra_.end(); it++) { + GenString(g, it->first.c_str(), it->second.c_str()); + } + + yajl_gen_map_close(g); + + const char *buf; + unsigned int len; + yajl_gen_get_buf(g, (const unsigned char **)&buf, &len); + std::string result(buf); + yajl_gen_free(g); + return result; +} + +void ServerInfoUpdater::GenNum(yajl_gen g, const char *key, dword value) { + yajl_gen_string(g, (const unsigned char *)key, strlen(key)); + const char *s = base::Format::ToString("%lu", value); + yajl_gen_number(g, s, strlen(s)); +} + +void ServerInfoUpdater::GenString(yajl_gen g, const char *key, + const char *value) { + yajl_gen_string(g, (const unsigned char *)key, strlen(key)); + yajl_gen_string(g, (const unsigned char *)value, strlen(value)); +} + +void ServerInfoUpdater::ParseExtra(const std::string& extra_json) { + if (extra_json.size() == 0) { + return; + } + + // Parse json + json::JsonBuilder builder; + builder.Start(); + builder.Update(extra_json.c_str(), extra_json.size()); + json::JsonObject *obj = builder.End(); + + // Ensure a map results + if (obj->type() != json::JSONTYPE_MAP) { + delete obj; + return; + } + json::JsonMap *map = (json::JsonMap *)obj; + + // Pull out the strings and add them to the extra map. + Enum enm; + const char *key; + while ((key = map->EnumKeys(&enm)) != NULL) { + const json::JsonObject *value_obj = map->GetObject(key); + if (value_obj->type() != json::JSONTYPE_STRING) { + continue; + } + const char *value = ((const json::JsonString *)value_obj)->GetString(); + extra_.insert(ExtraMap::value_type(std::string(key), + std::string(value))); + } + delete map; +} + +} // namespace wi diff --git a/server/serverinfoupdater.h b/server/serverinfoupdater.h new file mode 100644 index 0000000..6f5d802 --- /dev/null +++ b/server/serverinfoupdater.h @@ -0,0 +1,72 @@ +#ifndef __SERVERINFOUPDATER_H__ +#define __SERVERINFOUPDATER_H__ + +#include "server/server.h" +#include "base/socketaddress.h" +#include "base/thread.h" +#include "base/messagequeue.h" +#include "base/bytebuffer.h" +#include "base/sigslot.h" +#include "server/httppost.h" +#include "yajl/api/yajl_gen.h" +#include "yajl/src/yajl_buf.h" +#include "yajl/src/yajl_encode.h" +#include + +namespace wi { + +struct ServerInfoResult : base::MessageData { + ServerInfoResult(const base::ByteBuffer& result) { + this->result.WriteBytes(result.Data(), result.Length()); + } + base::ByteBuffer result; +}; + +class ServerInfoUpdater : base::MessageHandler, public base::has_slots<> { +public: + ServerInfoUpdater(Server& server, base::SocketAddress& post_address, + const std::string& post_path, int sort_key, + const std::string& server_name, const std::string& server_location, + const std::string& server_type, + base::SocketAddress& public_address, const std::string& extra_json, + int expires); + ~ServerInfoUpdater(); + + void Start(); + + void set_drain() { drain_ = true; } + void clear_drain() { drain_ = false; } + + base::signal2 SignalOnResponse; + +private: + void ThreadStart(void *pv); + void OnPostComplete(HttpPost *post, int status_code, int error, + const base::ByteBuffer& result); + base::ByteBuffer *MakeBody(); + std::string MakeJson(); + void GenNum(yajl_gen g, const char *key, dword value); + void GenString(yajl_gen g, const char *key, const char *value); + void ParseExtra(const std::string& extra_json); + + // MessageHandler + virtual void OnMessage(base::Message *pmsg); + + Server& server_; + base::Thread worker_; + base::SocketAddress post_address_; + std::string post_path_; + int sort_key_; + std::string server_name_; + std::string server_location_; + std::string server_type_; + base::SocketAddress public_address_; + int expires_; + typedef std::map ExtraMap; + ExtraMap extra_; + bool drain_; +}; + +} // namespace wi + +#endif // __SERVERINFOUPDATER_H__ diff --git a/server/start.sh b/server/start.sh new file mode 100644 index 0000000..935e49e --- /dev/null +++ b/server/start.sh @@ -0,0 +1 @@ +debug/hts --listen_address 127.0.0.1:18516 --server_id 0 --missionpack_dir testdata --htdata ../game/htdata832.pdb --stats_address 127.0.0.1:8080 --stats_path /api/addgamestats --server_info_path /api/serverinfo --server_name Icarus --server_location "Seattle WA" --server_type beta --server_info_extra "{\"foo\":\"bar\"}" --server_info_expires 60 --checksync --max_players 2000 2>&1 | tee log.txt diff --git a/server/statsposter.cpp b/server/statsposter.cpp new file mode 100644 index 0000000..9e12651 --- /dev/null +++ b/server/statsposter.cpp @@ -0,0 +1,239 @@ +#include "server/statsposter.h" +#include "base/base64.h" +#include "base/md5.h" +#include "server/secrets.h" + +// Stats get posted to a remote server via http post. The http +// posts are asynchronous, but the dns lookups are not, so it is handled +// in a thread to insulate the callers of this class. + +// If the server is down or not responding, the posts are kept in memory, and +// retried. The kcPostsWaitingMax is the max # that will be kept, before old +// ones are thrown away. kcPostsPostingMax is the number that is allowed to be +// posted simultaneously, so the load is kept even. + +#define kcPostsWaitingMax 1000 +#define kcPostsPostingMax 10 +#define kcMaxAttempts 10 + +namespace wi { + +StatsPoster::StatsPoster(const base::SocketAddress &addr, + const std::string& path) : base::MessageHandler(worker_), addr_(addr), + path_(path) { + worker_.Start(this, &StatsPoster::ThreadStart); +} + +StatsPoster::~StatsPoster() { + // Thread::Stop gets called in the Thread destructor, which does a join + // with the actual thread, which synchronizes exiting +} + +void StatsPoster::ThreadStart(void *pv) { + // Run and process messages. RunLoop will return when this thread + // needs to exit. + worker_.RunLoop(); + + // Cleanup that needs to happen on this thread. + while (waiting_.size() != 0) { + delete waiting_[0]; + waiting_.pop_front(); + } + while (posting_.size() != 0) { + delete posting_[0]; + posting_.pop_front(); + } +} + +void StatsPoster::Submit(const GameStats& stats) { + worker_.Post(1, this, new GameStatsData(stats)); +} + +void StatsPoster::OnMessage(base::Message *pmsg) { + GameStatsData *stats_data = (GameStatsData *)pmsg->data; + OnPostRequest(stats_data->stats); + delete stats_data; +} + +void StatsPoster::OnPostRequest(const GameStats& stats) { + // Delete old ones, keep new ones if we have too many uncompleted + HttpPost *post = new HttpPost(addr_, path_, MakeBody(stats)); + waiting_.push_back(post); + if (waiting_.size() > kcPostsWaitingMax) { + delete waiting_[0]; + waiting_.pop_front(); + } + SchedulePosts(); +} + +void StatsPoster::OnPostComplete(HttpPost *post, int status_code, int error, + const base::ByteBuffer& response) { +#if 0 + RLOG() << "STATSPOSTER: PostComplete " << (dword)post << " status_code " + << status_code; +#endif + + // Find it in posting_ + std::deque::iterator it = std::find(posting_.begin(), + posting_.end(), post); + + // It should always be in posting_. + if (it != posting_.end()) { + posting_.erase(it); + post->SignalOnComplete.disconnect(this); + + // Success? If so delete it and schedule the next one. If not success, + // push it to the front of waiting_ for later. + if (status_code == 200) { + delete post; + } else { + RLOG() << "ERROR " << status_code + << " posting game stats. attempt:" << post->attempts() + << " waiting:" << waiting_.size() + << " posting:" << posting_.size(); + if (post->attempts() >= kcMaxAttempts) { + RLOG() << "ERROR attemped gamestat post " << kcMaxAttempts + << " times. Deleting."; + delete post; + } else { + waiting_.push_front(post); + } + } + } else { + RLOG() << "ERROR: Post " << (dword)post << " not found in posting_!"; + } + + // Schedule more posts + SchedulePosts(); +} + +void StatsPoster::SchedulePosts() { + while (waiting_.size() != 0 && posting_.size() < kcPostsPostingMax) { + HttpPost *post = waiting_[0]; + waiting_.pop_front(); + posting_.push_back(post); + post->SignalOnComplete.connect(this, &StatsPoster::OnPostComplete); + post->Submit(); +#if 0 + RLOG() << "STATSPOSTER: Submiting post: " << (dword)post + << " waiting:" << waiting_.size() + << " posting:" << posting_.size(); +#endif + } +} + +base::ByteBuffer *StatsPoster::MakeBody(const GameStats& stats) { + std::string json = ToJson(stats); + + // Create HMAC like signature of hash(json + secret) so the receiver + // can ensure it is valid. + MD5_CTX md5; + MD5Init(&md5); + MD5Update(&md5, (const byte *)json.c_str(), json.size()); + MD5Update(&md5, (const byte *)kszStatSecret, strlen(kszStatSecret)); + byte hash[16]; + MD5Final(hash, &md5); + char hash_str[33]; + strncpyz(hash_str, base::Format::ToHex(hash, sizeof(hash)), + sizeof(hash_str)); + base::ByteBuffer *bb = new base::ByteBuffer(32 + json.size()); + bb->WriteBytes((const byte *)hash_str, 32); + bb->WriteBytes((const byte *)json.c_str(), json.size()); + return bb; +} + +// See stats/addgamestats.py for json description + +std::string StatsPoster::ToJson(const GameStats& s) { + // Use yajl to get the string encoding right + +#ifdef DEBUG + yajl_gen_config yajl_config = { 1, " " }; +#else + yajl_gen_config yajl_config = { 0, NULL }; +#endif + yajl_gen g = yajl_gen_alloc(&yajl_config); + + yajl_gen_map_open(g); + GenNum(g, "server_id", s.server_id); + GenNum(g, "server_start", s.server_start); + GenNum(g, "gameid", s.gameid); + GenNum(g, "packid_id", s.params.packid.id); + GenString(g, "packid_hash", base::Format::ToHex(s.params.packid.hash, + sizeof(s.params.packid.hash))); + GenString(g, "title", s.info.title()); + GenString(g, "filename", s.info.filename()); + GenNum(g, "game_speed", s.params.tGameSpeed); + GenNum(g, "min_players", s.info.minplayers()); + GenNum(g, "max_players", s.info.maxplayers()); + GenNum(g, "start_utc", s.start_utc); + GenNum(g, "end_utc", s.end_utc); + + const char *str = "player_stats"; + yajl_gen_string(g, (const unsigned char *)str, strlen(str)); + yajl_gen_array_open(g); + + for (int i = 0; i < s.player_count; i++) { + yajl_gen_map_open(g); + GenString(g, "name", s.player_stats[i].name); + GenNum(g, "pid", s.player_stats[i].pid); + GenString(g, "ip", s.player_stats[i].ip); + GenString(g, "did", s.player_stats[i].did); + + str = "winstats"; + yajl_gen_string(g, (const unsigned char *)str, strlen(str)); + yajl_gen_map_open(g); + GenNum(g, "side_mask", s.player_stats[i].ws.sidm); + GenNum(g, "side_mask_allies", s.player_stats[i].ws.sidmAllies); + GenNum(g, "credits_acquired", s.player_stats[i].ws.cCreditsAcquired); + GenNum(g, "credits_consumed", s.player_stats[i].ws.cCreditsConsumed); + GenNum(g, "enemy_munts_killed", + s.player_stats[i].ws.cEnemyMobileUnitsKilled); + GenNum(g, "enemy_structs_killed", + s.player_stats[i].ws.cEnemyStructuresKilled); + GenNum(g, "munts_lost", s.player_stats[i].ws.cMobileUnitsLost); + GenNum(g, "structs_lost", s.player_stats[i].ws.cStructuresLost); + GenNum(g, "ff", s.player_stats[i].ws.ff); + + str = "unit_counts"; + yajl_gen_string(g, (const unsigned char *)str, strlen(str)); + yajl_gen_array_open(g); + for (int ut = 0; ut < ARRAYSIZE(s.player_stats[i].ws.acut); ut++) { + yajl_gen_integer(g, s.player_stats[i].ws.acut[ut]); + } + yajl_gen_array_close(g); + + str = "built_counts"; + yajl_gen_string(g, (const unsigned char *)str, strlen(str)); + yajl_gen_array_open(g); + for (int ut = 0; ut < ARRAYSIZE(s.player_stats[i].ws.acutBuilt); ut++) { + yajl_gen_integer(g, s.player_stats[i].ws.acutBuilt[ut]); + } + yajl_gen_array_close(g); + + yajl_gen_map_close(g); + yajl_gen_map_close(g); + } + yajl_gen_array_close(g); + yajl_gen_map_close(g); + + const char *buf; + unsigned int len; + yajl_gen_get_buf(g, (const unsigned char **)&buf, &len); + std::string result(buf); + yajl_gen_free(g); + return result; +} + +void StatsPoster::GenNum(yajl_gen g, const char *key, dword value) { + yajl_gen_string(g, (const unsigned char *)key, strlen(key)); + const char *s = base::Format::ToString("%lu", value); + yajl_gen_number(g, s, strlen(s)); +} + +void StatsPoster::GenString(yajl_gen g, const char *key, const char *value) { + yajl_gen_string(g, (const unsigned char *)key, strlen(key)); + yajl_gen_string(g, (const unsigned char *)value, strlen(value)); +} + +} // namespace wi diff --git a/server/statsposter.h b/server/statsposter.h new file mode 100644 index 0000000..48afc41 --- /dev/null +++ b/server/statsposter.h @@ -0,0 +1,55 @@ +#ifndef __STATSPOSTER_H__ +#define __STATSPOSTER_H__ + +#include "base/socketaddress.h" +#include "base/thread.h" +#include "base/messagequeue.h" +#include "base/bytebuffer.h" +#include "base/sigslot.h" +#include "server/player.h" +#include "server/game.h" +#include "server/httppost.h" +#include "yajl/api/yajl_gen.h" +#include "yajl/src/yajl_buf.h" +#include "yajl/src/yajl_encode.h" +#include +#include + +namespace wi { + +struct GameStatsData : base::MessageData { + GameStatsData(const GameStats& stats) : stats(stats) { } + GameStats stats; +}; + +class StatsPoster : base::MessageHandler, public base::has_slots<> { +public: + StatsPoster(const base::SocketAddress& addr, const std::string& path); + ~StatsPoster(); + + void Submit(const GameStats& stats); + +private: + void ThreadStart(void *pv); + void OnPostRequest(const GameStats& stats); + void SchedulePosts(); + void OnPostComplete(HttpPost *post, int status_code, int error, + const base::ByteBuffer& response); + base::ByteBuffer *MakeBody(const GameStats& stats); + std::string ToJson(const GameStats& stats); + void GenNum(yajl_gen g, const char *key, dword value); + void GenString(yajl_gen g, const char *key, const char *value); + + // MessageHandler interface + virtual void OnMessage(base::Message *pmsg); + + base::Thread worker_; + std::deque waiting_; + std::deque posting_; + base::SocketAddress addr_; + std::string path_; +}; + +} // namespace wi + +#endif // __STATSPOSTER_H__ diff --git a/server/testdata.tar.gz b/server/testdata.tar.gz new file mode 100644 index 0000000..6e001bf Binary files /dev/null and b/server/testdata.tar.gz differ diff --git a/server/tokenauth.cpp b/server/tokenauth.cpp new file mode 100644 index 0000000..9cc296d --- /dev/null +++ b/server/tokenauth.cpp @@ -0,0 +1,120 @@ +#include "server/tokenauth.h" +#include "server/secrets.h" +#include "mpshared/misc.h" +#include "mpshared/mpht.h" +#include "base/base64.h" +#include "base/format.h" +#include "base/md5.h" +#include + +namespace wi { + +dword TokenAuth::Authenticate(const char *username, const char *token) { + if (strlen(token) > kcbTokenMax) { + return knAuthResultFail; + } + + // base64 decode it + char output[kcbTokenMax * 2]; + int cb = base::base64decode((byte *)token, strlen(token), (byte *)output, + sizeof(output)); + if (cb == -1) { + return knAuthResultFail; + } + output[cb] = 0; + +// example +// [{"c":30782,"u":"c2NvdHRsdQ==","t":1239744982},"cad14dfc03ad28caa83d9bd298f91e31"] + + // Pull out the pieces we need. First, read out the bytes between the {}, + // inclusive + std::string t(output); + int start = t.find('{'); + if (start < 0) { + return knAuthResultFail; + } + int end = t.find('}'); + if (end < 0) { + return knAuthResultFail; + } + std::string s(t, start, end - start + 1); + + // Pull out the hash + end = t.rfind('"'); + if (end < 0) { + return knAuthResultFail; + } + start = t.rfind('"', end - 1); + if (start < 0) { + return knAuthResultFail; + } + std::string h(t, start + 1, end - (start + 1)); + + // Compare the passed username with the token username + // Need to base64 decode the name first. + start = t.find("\"u\":\""); + if (start < 0) { + return knAuthResultFail; + } + end = t.find("\",", start); + if (end < 0) { + return knAuthResultFail; + } + std::string username64(t, start + 5, end - (start + 5)); + // base64 decode it + cb = base::base64decode((byte *)username64.c_str(), username64.size(), + (byte *)output, sizeof(output)); + if (cb == -1) { + return knAuthResultFail; + } + output[cb] = 0; + if (strcmp(username, output) != 0) { + return knAuthResultFail; + } + + // Hash the first string with token auth secret appended + MD5_CTX md5; + MD5Init(&md5); + std::string sT(s + kszTokenAuthSecret); + MD5Update(&md5, (const byte *)sT.c_str(), sT.size()); + byte hash[16]; + MD5Final(hash, &md5); + + // Compare to the passed in hash. This validates the token. + if (strcmp(h.c_str(), base::Format::ToHex(hash, sizeof(hash))) != 0) { + return knAuthResultFail; + } + + // Compare the timestamp with the current time. + start = t.find("\"t\":"); + if (start < 0) { + return knAuthResultFail; + } + int startN = start + 4; + int endN = startN; + while (t[endN] >= '0' && t[endN] <= '9') { + endN++; + } + std::string ts(t, startN, endN - startN); + dword tToken; + if (!base::Format::ToDword(ts.c_str(), 10, &tToken)) { + return knAuthResultFail; + } + + // Compare to current time - seconds since epoch. Current time must be + // less than or equal. If this fails, return stale so the client + // knows to get a new token. + time_t tCurrent = time(NULL); + if (tCurrent > tToken) { + return knAuthResultStaleToken; + } + + // It's good! + return knAuthResultSuccess; +} + +bool TokenAuth::IsAnonymous(const char *username, const char *token) { + return *token == 0; +} + +} // namespace wi diff --git a/server/tokenauth.h b/server/tokenauth.h new file mode 100644 index 0000000..2d243dd --- /dev/null +++ b/server/tokenauth.h @@ -0,0 +1,27 @@ +#ifndef __TOKENAUTH_H__ +#define __TOKENAUTH_H__ + +#include "inc/basictypes.h" +#include "base/misc.h" + +namespace wi { + +const dword knAuthResultSuccess = 0; +const dword knAuthResultFail = 1; +const dword knAuthResultStaleToken = 2; + +STARTLABEL(AuthResults) + LABEL(knAuthResultSuccess) + LABEL(knAuthResultFail) + LABEL(knAuthResultStaleToken) +ENDLABEL(AuthResults) + +class TokenAuth { +public: + static dword Authenticate(const char *username, const char *token); + static bool IsAnonymous(const char *username, const char *token); +}; + +} // namespace wi + +#endif // __TOKENAUTH_H__ diff --git a/server/tokenbucket.cpp b/server/tokenbucket.cpp new file mode 100644 index 0000000..e62b278 --- /dev/null +++ b/server/tokenbucket.cpp @@ -0,0 +1,27 @@ +#include "server/tokenbucket.h" +#include "base/tick.h" + +namespace wi { + +TokenBucket::TokenBucket(int token_count, int seconds_count) { + token_count_ = token_count; + token_count_limit_ = token_count; + ms_per_token_ = seconds_count * 1000 / token_count; + ms_last_token_ = 0; +} + +bool TokenBucket::IsEmpty() { + long64 msCurrent = base::GetMillisecondCount(); + token_count_ += (float)(msCurrent - ms_last_token_) / (float)ms_per_token_; + if (token_count_ > token_count_limit_) { + token_count_ = token_count_limit_; + } + ms_last_token_ = msCurrent; + if (token_count_ < 1.0) { + return true; + } + token_count_ -= 1.0f; + return false; +} + +} // namespace wi diff --git a/server/tokenbucket.h b/server/tokenbucket.h new file mode 100644 index 0000000..2bee54d --- /dev/null +++ b/server/tokenbucket.h @@ -0,0 +1,23 @@ +#ifndef __TOKENBUCKET_H__ +#define __TOKENBUCKET_H__ + +#include "inc/basictypes.h" + +namespace wi { + +class TokenBucket { +public: + TokenBucket(int token_count, int seconds_count); + + bool IsEmpty(); + +private: + float token_count_; + int token_count_limit_; + dword ms_per_token_; + long64 ms_last_token_; +}; + +} // namespace wi + +#endif // __TOKENBUCKET_H__ diff --git a/server/tracker.cpp b/server/tracker.cpp new file mode 100644 index 0000000..53b3dbc --- /dev/null +++ b/server/tracker.cpp @@ -0,0 +1,71 @@ +#include "server/tracker.h" +#include "base/thread.h" +#include "base/tick.h" +#include + +const dword TIMEOUT_CHECK_SECONDS = 2 * 60; + +namespace wi { + +Tracker::Tracker() { + thread_.PostTimer(1, this, TIMEOUT_CHECK_SECONDS * 100); +} + +void Tracker::Add(const char *s, long64 tExpires) { + // If it exists, just update the entry, otherwise add new + std::string sT(s); + TrackMap::iterator it = map_.find(sT); + if (it != map_.end()) { + it->second = tExpires; + return; + } + map_.insert(TrackMap::value_type(sT, tExpires)); +} + + +void Tracker::Remove(const char *s) { + TrackMap::iterator it = map_.find(std::string(s)); + if (it != map_.end()) { + map_.erase(it); + } +} + +bool Tracker::Find(const char *s, long64 *tExpires) { + TrackMap::iterator it = map_.find(std::string(s)); + if (it == map_.end()) { + return false; + } + long64 tCurrent = base::GetMillisecondCount(); + if (tCurrent > it->second) { + map_.erase(it); + return false; + } + if (tExpires != NULL) { + *tExpires = it->second; + } + return true; +} + +void Tracker::OnMessage(base::Message *pmsg) { + // Remove stale entries + long64 tCurrent = base::GetMillisecondCount(); + std::vector v; + for (TrackMap::iterator it = map_.begin(); it != map_.end(); it++) { + if (tCurrent >= it->second) { + v.push_back(it->first); + } + } + std::vector::iterator it = v.begin(); + for (; it != v.end(); it++) { + TrackMap::iterator it_map = map_.find(*it); + if (it_map != map_.end()) { + map_.erase(it_map); + } + } +} + +void Tracker::Clear() { + map_.clear(); +} + +} // namespace wi diff --git a/server/tracker.h b/server/tracker.h new file mode 100644 index 0000000..ddfd139 --- /dev/null +++ b/server/tracker.h @@ -0,0 +1,27 @@ +#ifndef __TRACKER_H__ +#define __TRACKER_H__ + +#include "base/messagehandler.h" +#include "base/socketaddress.h" +#include + +namespace wi { + +class Tracker : base::MessageHandler { +public: + Tracker(); + void Add(const char *s, long64 tExpires); + void Remove(const char *s); + bool Find(const char *s, long64 *tExpires = NULL); + void Clear(); + +private: + virtual void OnMessage(base::Message *pmsg); + + typedef std::map TrackMap; + TrackMap map_; +}; + +} // namespace wi + +#endif // __TRACKER_H__ diff --git a/shadowmap/.cvsignore b/shadowmap/.cvsignore new file mode 100644 index 0000000..0ca40fa --- /dev/null +++ b/shadowmap/.cvsignore @@ -0,0 +1,2 @@ +bin +SpiffLib diff --git a/shadowmap/app.ico b/shadowmap/app.ico new file mode 100644 index 0000000..3a5525f Binary files /dev/null and b/shadowmap/app.ico differ diff --git a/shadowmap/assemblyinfo.cs b/shadowmap/assemblyinfo.cs new file mode 100644 index 0000000..9f89a32 --- /dev/null +++ b/shadowmap/assemblyinfo.cs @@ -0,0 +1,58 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.*")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] diff --git a/shadowmap/class1.cs b/shadowmap/class1.cs new file mode 100644 index 0000000..56aa974 --- /dev/null +++ b/shadowmap/class1.cs @@ -0,0 +1,99 @@ +using System; +using System.Drawing; +using System.IO; +using SpiffLib; + +namespace shadowmap +{ + /// + /// Summary description for Class1. + /// + class Class1 + { + /// + /// The main entry point for the application. + /// + [STAThread] + static unsafe void Main(string[] args) + { + // Get parameters + Palette palIn = new Palette(args[0]); + string strFileOut = args[1]; + double dAlpha = Double.Parse(args[2]); + + // Create mapping + byte[] ab = new byte[palIn.Length]; + Palette palInHSB = new Palette(palIn.Length); + for (int iclr = 0; iclr < palIn.Length; iclr++) { + Color clr = palIn[iclr]; + double h = clr.GetHue(); + double s = clr.GetSaturation(); + double l = clr.GetBrightness(); + double r; + double g; + double b; + MyHSLtoRGB(h, s, l * dAlpha, &r, &g, &b); + Color clrShadow = Color.FromArgb((int)(r * 255.0), (int)(g * 255.0), (int)(b * 255.0)); + ab[iclr] = (byte)palIn.FindClosestEntry(clrShadow); + } + + // Write palette mapping + Stream stm = new FileStream(strFileOut, FileMode.Create, FileAccess.Write, FileShare.None); + BinaryWriter bwtr = new BinaryWriter(stm); + bwtr.Write(ab); + bwtr.Close(); + +#if false + // Check it + Palette palCheck = new Palette(palIn.Length); + for (int iclr = 0; iclr < palIn.Length; iclr++) + palCheck[iclr] = palIn[ab[iclr]]; + palCheck.SaveJasc("shadow.pal"); +#endif + } + + // .NET doesn't seem to have HSL->RGB mapping, only RGB->HSL. + + /* + * given h,s,l on [0..1], + * return r,g,b on [0..1] + */ + unsafe static void + HSL_to_RGB(double h, double sl, double l, double *r, double *g, double *b) { + double v; + + v = (l <= 0.5) ? (l * (1.0 + sl)) : (l + sl - l * sl); + if (v <= 0) { + *r = *g = *b = 0.0; + } else { + double m; + double sv; + int sextant; + double fract, vsf, mid1, mid2; + + m = l + l - v; + sv = (v - m ) / v; + h *= 6.0; + sextant = (int)h; + fract = h - sextant; + vsf = v * sv * fract; + mid1 = m + vsf; + mid2 = v - vsf; + switch (sextant) { + case 0: *r = v; *g = mid1; *b = m; break; + case 1: *r = mid2; *g = v; *b = m; break; + case 2: *r = m; *g = v; *b = mid1; break; + case 3: *r = m; *g = mid2; *b = v; break; + case 4: *r = mid1; *g = m; *b = v; break; + case 5: *r = v; *g = m; *b = mid2; break; + } + } + } + + unsafe static void MyHSLtoRGB(double h, double s, double l, double *pr, double *pg, double *pb) { + // From Graphics Gems. Convert Foley's 0..360 to 0..1 + + HSL_to_RGB(h / 360.0, s, l, pr, pg, pb); + } + } +} diff --git a/shadowmap/shadowmap.csproj b/shadowmap/shadowmap.csproj new file mode 100644 index 0000000..1e7d919 --- /dev/null +++ b/shadowmap/shadowmap.csproj @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/shadowmap/shadowmap.csproj.user b/shadowmap/shadowmap.csproj.user new file mode 100644 index 0000000..9c49b34 --- /dev/null +++ b/shadowmap/shadowmap.csproj.user @@ -0,0 +1,48 @@ + + + + + + + + + + + + diff --git a/shadowmap/shadowmap.sln b/shadowmap/shadowmap.sln new file mode 100644 index 0000000..f9cb961 --- /dev/null +++ b/shadowmap/shadowmap.sln @@ -0,0 +1,21 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "shadowmap", "shadowmap.csproj", "{7354AEC7-43D5-4BCB-9FBD-989D82947B85}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Release = Release + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {7354AEC7-43D5-4BCB-9FBD-989D82947B85}.Debug.ActiveCfg = Debug|.NET + {7354AEC7-43D5-4BCB-9FBD-989D82947B85}.Debug.Build.0 = Debug|.NET + {7354AEC7-43D5-4BCB-9FBD-989D82947B85}.Release.ActiveCfg = Release|.NET + {7354AEC7-43D5-4BCB-9FBD-989D82947B85}.Release.Build.0 = Release|.NET + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/stats/.gitignore b/stats/.gitignore new file mode 100644 index 0000000..0d20b64 --- /dev/null +++ b/stats/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/stats/about.py b/stats/about.py new file mode 100644 index 0000000..9732e2b --- /dev/null +++ b/stats/about.py @@ -0,0 +1,27 @@ +import os +import config +import basehandler + +from google.appengine.ext import webapp +from google.appengine.ext.webapp import template +from google.appengine.ext import db + +# This page isn't dynamic +MAX_AGE_SECONDS = 60 * 60 * 24 + +class About(basehandler.BaseHandler): + def get(self): + # Get player name, if any + player_name = self.request.get('p') + + # Render the template and serve the response + template_values = { + 'tabs': config.get_tabs(player_name), + 'selected_tab': config.TAB_ABOUT, + } + + #self.set_caching_headers(MAX_AGE_SECONDS) + #self.response.headers['Content-Type'] = 'text/plain' + self.response.headers['Content-Type'] = 'application/xhtml+xml' + path = os.path.join(os.path.dirname(__file__), 'about.xhtml') + self.response.out.write(template.render(path, template_values)) diff --git a/stats/about.xhtml b/stats/about.xhtml new file mode 100644 index 0000000..3d8a861 --- /dev/null +++ b/stats/about.xhtml @@ -0,0 +1,68 @@ +{% extends "basepage.xhtml" %} +{% block html_title %}Hostile Takeover About{% endblock %} + +{% block head_css %} +.box {margin:20px;} +{% endblock %} + +{% block page_content %} +

ABOUT

+
+

Leaderboard

+The Leaderboard tracks player rating, global rankings, game history, and player statistics for all registered players, for multiplayer missions played in Hostile Takeover. Any player can become a registered player by simply registering for a permanent player name. Players who don't register are called anonymous players. Anonymous players are not tracked on the leaderboard. +

Registered Players

+There are many benefits to being a registered player: +
    +
  • Get a permanent player name of your choice
  • +
  • Get tracked by the leaderboard, including your rating, game history, and player statistics
  • +
  • More opportunity to play against top players (top players want to to play registered players)
  • +
  • Start with a ranking of 1500, and build from there (anonymous players are stuck at 1300)
  • +
+If you are an anonymous player, here is how you register: +
    +
  • When you get to the Multiplayer Login screen in the game, press Register
  • +
  • Fill out the page with your information. Be sure the email is correct! When ready, press Register
  • +
  • An email will be sent to the email address you provided. This email contains your Activation link. Go to your email, and click this link to activate your account. You won't be able to login until you've activated your account.
  • +
  • Now you are ready to login to Multiplayer!
  • +
+Please choose a non-offensive player name. The administrator reserves the right to ban players if enough complaints are received about a given player's name. +

Ratings

+

Hostile Takeover's rating system is based on the ELO rating system, with modifications. ELO was originally used for rating players in the game of Chess, starting in the 1960's and 1970's, and has been adopted by many games of skill to rate players.

+

ELO works by estimating the likelihood that a given player will win against an opponent, by comparing ratings (a higher score means more likely to win, a lower score means less likely to win). This is then used to calculate the new rating, in the following ways:

+
    +
  • The less likely a player will win against an opponent, the higher the ratings boost if the player wins.
  • +
  • The less likely a player will win against an opponent, the lower the ratings decline if the player loses.
  • +
  • The more likely a player will win against an opponent, the lower the ratings boost if the player wins.
  • +
  • The more likely a player will win against an opponent, the higher the ratings decline if the player loses.
  • +
+This has important implications for all players: +
    +
  • The best way to boost your rating is to win against players with ratings near yours (preferably higher than yours).
  • +
  • If you have a high rating and play an anonymous player (rating 1300) and win, you will get a small ratings boost.
  • +
  • If you have a high rating and lose to an anonymous player, you will get a large ratings decline.
  • +
+Summary: play players with ratings near or higher than yours. Registered players start out at 1500, and build from there. Anonymous players are fixed at 1300. +

Rating Rules

+There are a few important rating rules to be aware of: +
    +
  • Games must be at least 3 minutes in duration to be scored (wall clock time, not game simulation time). This gives non-expert players a chance against expert players.
  • +
  • Games must have a registered player winner, and a registered player loser to be scored. Anonymous or AI player wins and losses against registered players are not scored.
  • +
  • Games need at least one recorded winner to be scored.
  • +
+

Rating Freshness

+Players must keep their games fresh, to maintain their rating. If a player has not played a scoring game in over a week, their score will decline by 20 points each week after. This reflects the fact that maintaining a skill requires practice, and it gives a chance for active players to move up the leaderboard. +

Network Disconnects

+Occasional network disconnects are part of all networked multiplayer games. Some of the causes of disconnect are: +
    +
  1. The player indavertently loses a network connection. This can happen if the battery runs out, signal strength is low and / or spotty, or in the case of cellular data, if the player is moving between cell towers. Other players will see this player as lagging. The best thing to do is to find a location with good signal strength, and stay there for the duration of the game.
  2. +
  3. The player is playing over WiFi and purposefully disconnects from the network. This can happen by unplugging the WiFi access point, or moving out of range of the access point.
  4. +
+

When a disconnect occurs, it is scored as a loss for that player, because the server can't tell the difference between case #1 and case #2. If disconnect were not scored as a loss, losing players could take advantage of case #2 by forcing a disconnect, in order to avoid a point deduction.

+

Note that a lagging player does not mean that player has disconnected. It is best to give that player a chance to catch up. If after retrying several times the player has not caught up, it is likely that a disconnect has occured.

+

Contact

+Please visit the Multiplayer section of the Hostile Takeover forums for help (found on REPLACE ME: MAIN SITE URL). If that doesn't answer your question, mail REPLACE ME: CONTACT EMAIL. +
+
+Enjoy! +
+{% endblock %} diff --git a/stats/accounts.py b/stats/accounts.py new file mode 100644 index 0000000..e258eff --- /dev/null +++ b/stats/accounts.py @@ -0,0 +1,76 @@ +import logging +import models +from google.appengine.api import users + +rights = dict( + BLOCK_PLAYER_ACCESS_RIGHT = 0x00000001, + RESET_PLAYER_ACCESS_RIGHT = 0x00000002, + SEND_CHAT_ACCESS_RIGHT = 0x00000004, + DRAIN_ACCESS_RIGHT = 0x00000008, + ADJUST_SCORE_ACCESS_RIGHT = 0x00000010, + SEE_PLAYER_INFO_ACCESS_RIGHT = 0x00000020, + ADD_ACCOUNT_ACCESS_RIGHT = 0x00000040, + REMOVE_ACCOUNT_ACCESS_RIGHT = 0x00000080, + ADMIN_LINK_ACCESS_RIGHT = 0x00000100, + ADMIN_LOG_ACCESS_RIGHT = 0x00000200, + HIDE_PLAYER_ACCESS_RIGHT = 0x00000400, +) + +BASE_ADMIN_ACCESS_RIGHTS = \ + rights['SEE_PLAYER_INFO_ACCESS_RIGHT'] | \ + rights['BLOCK_PLAYER_ACCESS_RIGHT'] | \ + rights['ADMIN_LINK_ACCESS_RIGHT'] | \ + rights['ADJUST_SCORE_ACCESS_RIGHT'] | \ + rights['ADMIN_LOG_ACCESS_RIGHT'] | \ + rights['HIDE_PLAYER_ACCESS_RIGHT'] + +BASE_ADMIN_ACCESS_RIGHTS_PLUS = \ + BASE_ADMIN_ACCESS_RIGHTS | \ + rights['SEND_CHAT_ACCESS_RIGHT'] | \ + rights['DRAIN_ACCESS_RIGHT'] + +class Account(object): + def __init__(self, account): + self.__dict__['account'] = account + + def __getattr__(self, name): + if name == 'name': + return self.account.nickname + try: + return (self.account.access_rights & rights[name]) != 0 + except: + raise AttributeError + + def __setattr__(self, name, value): + try: + self.account.access_rights &= ~rights[name] + if value: + self.account.access_rights |= rights[name] + except: + raise AttributeError + + def save(self): + self.account.put() + +def load(user): + logging.info('nickname=' + user.nickname()) + key = models.accountmodel_key(user.nickname()) + if users.is_current_user_admin(): + account = models.AccountModel(key_name=key.name()) + account.nickname = user.nickname() + account.access_rights = -1 + else: + account = models.AccountModel.get(key) + if account: + return Account(account) + return None + +def account(): + user = None + try: + user = users.get_current_user() + except: + user = None + if not user: + return None + return load(user) diff --git a/stats/addgamestats.py b/stats/addgamestats.py new file mode 100644 index 0000000..b03a3c5 --- /dev/null +++ b/stats/addgamestats.py @@ -0,0 +1,20 @@ +import config +from hashlib import md5 +import gamestats + +from google.appengine.ext import webapp + +class AddGameStats(webapp.RequestHandler): + def post(self): + hash = self.request.body[:32] + json = self.request.body[32:] + m = md5(json + config.ADDGAMESTATS_SECRET) + if m.hexdigest() != hash: + return + + # Update player_stats without dids + g = gamestats.GameStats(json, load_players=True) + + # Don't save games that only have anonymous players + if g.get_nonanonymous_human_count() != 0: + g.save() diff --git a/stats/adjustplayer.py b/stats/adjustplayer.py new file mode 100644 index 0000000..04a7b22 --- /dev/null +++ b/stats/adjustplayer.py @@ -0,0 +1,66 @@ +import os +import config +import basehandler +import models +import accounts + +from google.appengine.api import users +from google.appengine.ext import webapp +from google.appengine.ext.webapp import template +from google.appengine.ext import db + +class BlockPlayer(basehandler.BaseHandler): + def get(self): + account = self.has_access() + if not account: + self.redirect(users.create_login_url(self.request.uri)) + return + player_name = self.request.get('p').lower() + + template_values = { + 'tabs': config.get_tabs(player_name, account), + 'selected_tab': config.TAB_NONE, + } + + self.response.headers['Content-Type'] = 'application/xhtml+xml' + path = os.path.join(os.path.dirname(__file__), 'blockplayer.xhtml') + self.response.out.write(template.render(path, template_values)) + + def post(self): + if not self.has_access(): + self.redirect(users.create_logout_url(config.ADMIN_URL)) + return + + self.response.headers['Content-Type'] = 'text/plain' + player_name = self.request.get('u') + if not player_name: + self.response.out.write('no player name entered') + return + + choice = self.request.get('choice') + if not choice: + self.response.out.write('no choice selected') + return + + p = models.PlayerModel.get(models.playermodel_key(player_name)) + if not p: + self.response.out.write('cannot find player in database') + return + + if choice == 'block': + p.blocked = True + elif choice == 'unblock': + p.blocked = False + else: + self.response.out.write('choice not quarantine or unquarantine') + return + + p.put() + self.response.out.write('success. %s is now quarantined=%s.' % (player_name, choice == 'block')) + + def has_access(self): + # Requires an authenticated user with proper access rights + account = accounts.account() + if account and account.BLOCK_PLAYER_ACCESS_RIGHT: + return account + return None diff --git a/stats/adjustplayer.xhtml b/stats/adjustplayer.xhtml new file mode 100644 index 0000000..831011e --- /dev/null +++ b/stats/adjustplayer.xhtml @@ -0,0 +1,29 @@ +{% extends "basepage.xhtml" %} +{% block html_title %}Block Player{% endblock %} + +{% block head_css %} +.sbar { padding:20px 0 20px 0; margin:0; text-align:center;} +.searchquery { font-size:13px; margin-right:10px; padding: 4px 3px; vertical-align:middle; width:190px;} +a.gpbutton { display:inline; margin: 0 auto; line-height:33px; font-size:13.5px; font-weight:bold; color:#fff; text-shadow:0px -1px 1px #666; padding:5px 8px; background:#818189; text-decoration: none; -webkit-border-radius:5px; -moz-border-radius:5px; white-space:nowrap; } +.vmiddle { vertical-align:middle;} +.message_p {text-align:center;font-size:14px;font-weight:bold;} +{% endblock %} + +{% block page_content %} +

QUARANTINE PLAYER

+
+

Quarantine or unquarantine a player from the leaderboard. Enter player name:

+
+
+
+ Quarantine + Unquarantine +
+
+ +{% endblock %} diff --git a/stats/adjustscore.py b/stats/adjustscore.py new file mode 100644 index 0000000..05f195e --- /dev/null +++ b/stats/adjustscore.py @@ -0,0 +1,111 @@ +import os +import config +import basehandler +import models +import accounts +import urllib +import admin + +from google.appengine.api import users +from google.appengine.ext import webapp +from google.appengine.ext.webapp import template +from google.appengine.ext import db + +class AdjustScore(basehandler.BaseHandler): + def get(self): + account = self.has_access() + if not account: + self.redirect(users.create_login_url(self.request.uri)) + return + type = self.request.get('t').lower() + u = self.request.get('u').lower() + player_name = self.request.get('p').lower() + message = '' + + # if no type specified, there is no query + rating = 0 + next_type = '' + if not type: + # The next request will be a query for a player name's score + next_type = 'q' + + # Validate the user. Is this a query or set score request? + p = None + if type == 'q' or type == 's': + # This is a query for a user. Make sure the user is valid + if not u: + message = 'No player name entered!' + type = '' + next_type = 'q' + else: + p = models.PlayerModel.get(models.playermodel_key(u)) + if not p: + message = 'Could not find player %s! Please try again:' % u + u = '' + type = '' + next_type = 'q' + + # Step through the states + rating = 0 + reason = '' + if type == 'q': + # The next request will be for changing a score + next_type = 's' + rating = p.rating + + if type == 's': + # Is the new rating valid? + rating = self.request.get('s') + reason = self.request.get('r') + success = True + try: + rating = int(rating) + except: + success = False + + if not success or rating >= 3000 or rating < 0: + message = '"%s" is an invalid score. Please try again:' % rating + rating = p.rating + type = 'q' + next_type = 's' + + elif not reason: + message = 'Must enter a reason. Please try again:' + rating = p.rating + type = 'q' + next_type = 's' + + else: + # Record this action + d = dict(action='adjust_score', player_name=u, old_rating=p.rating, new_rating=rating, reason=reason) + admin.save_action(account.name, self.request.remote_addr, d) + + p.rating = rating + p.put() + message = 'Successfully set the score of player %s to %s, reason: %s.' % (u, rating, reason) + type = '' + next_type = 'q' + + template_values = { + 'tabs': config.get_tabs(player_name, account), + 'selected_tab': config.TAB_NONE, + 'form_url': config.ADJUSTSCORE_URL, + 'message': message, + 'player_name': player_name, + 'u': u, + 'rating': rating, + 'type': type, + 'reason': reason, + 'next_type': next_type + } + + self.response.headers['Content-Type'] = 'application/xhtml+xml' + path = os.path.join(os.path.dirname(__file__), 'adjustscore.xhtml') + self.response.out.write(template.render(path, template_values)) + + def has_access(self): + # Requires an authenticated user with proper access rights + account = accounts.account() + if account and account.ADJUST_SCORE_ACCESS_RIGHT: + return account + return None diff --git a/stats/adjustscore.xhtml b/stats/adjustscore.xhtml new file mode 100644 index 0000000..1ededac --- /dev/null +++ b/stats/adjustscore.xhtml @@ -0,0 +1,53 @@ +{% extends "basepage.xhtml" %} +{% block html_title %}Adjust Score{% endblock %} + +{% block head_css %} +.sbar { padding:20px 0 20px 0; margin:0; text-align:center;} +.searchquery { font-size:13px; margin-right:10px; padding: 4px 3px; vertical-align:middle; width:190px;} +a.gpbutton { display:inline; margin: 0 auto; line-height:33px; font-size:13.5px; font-weight:bold; color:#fff; text-shadow:0px -1px 1px #666; padding:5px 8px; background:#818189; text-decoration: none; -webkit-border-radius:5px; -moz-border-radius:5px; white-space:nowrap; } +.vmiddle { vertical-align:middle;} +.message_p {text-align:center;font-size:14px;font-weight:bold;} +{% endblock %} + +{% block page_content %} +

ADJUST SCORE

+
+ +{% if message %} +

{{ message }}

+
+{% endif %} + +{% if not type %} +

Adjust the score of a player. Enter player name:

+
+
+
+ Submit
+ + {% if player_name %} + + {% endif %} +
+{% endif %} + +{% ifequal type "q" %} +

The current score of player {{ u|escape }} is {{ rating|escape }}. Enter new score:

+
+
+

Reason:

+ +
+ Submit
+ + + {% if player_name %} + + {% endif %} +
+{% endifequal %} + + +{% endblock %} diff --git a/stats/admin.py b/stats/admin.py new file mode 100644 index 0000000..2317d9d --- /dev/null +++ b/stats/admin.py @@ -0,0 +1,57 @@ +import os +import time +import models +import config +import basehandler +import accounts +import adminlog +import json + +from google.appengine.api import users +from google.appengine.ext import webapp +from google.appengine.ext.webapp import template + +class Admin(basehandler.BaseHandler): + def get(self): + account = accounts.account() + if not account or not account.ADMIN_LINK_ACCESS_RIGHT: + self.redirect(users.create_login_url(self.request.uri)) + return + self.player_name = self.request.get('p').lower() + + # Show admin UI + template_values = { + 'tabs': config.get_tabs(self.player_name, account), + 'selected_tab': config.TAB_NONE, + 'account': account, + 'adminlog_url': self.get_url(config.ADMINLOG_URL), + 'playerdetail_url': self.get_url(config.PLAYERDETAIL_URL), + 'adjust_score_url': self.get_url(config.ADJUSTSCORE_URL), + 'blockplayer_url': self.get_url(config.BLOCKPLAYER_URL), + 'hideplayer_url': self.get_url(config.HIDEPLAYER_URL), + 'resetplayer_url': self.get_url(config.RESETPLAYER_URL), + 'sendchat_url': self.get_url(config.SENDCHAT_URL), + 'drain_url': self.get_url(config.DRAIN_URL), + 'logout_url': users.create_logout_url(self.request.uri), + } + + self.response.headers['Content-Type'] = 'application/xhtml+xml' + path = os.path.join(os.path.dirname(__file__), 'admin.xhtml') + self.response.out.write(template.render(path, template_values)) + + def get_url(self, url): + if self.player_name: + return '%s?p=%s' % (url, self.player_name) + else: + return url + +def save_action(admin_user, ip, action, time_utc=int(time.time())): + try: + a = models.AdminActionModel() + a.admin_user = admin_user.lower() + a.ip_address = ip + a.action = json.dumps(action) + a.time_utc = time_utc + a.put() + except: + pass diff --git a/stats/admin.xhtml b/stats/admin.xhtml new file mode 100644 index 0000000..e320498 --- /dev/null +++ b/stats/admin.xhtml @@ -0,0 +1,52 @@ +{% extends "basepage.xhtml" %} + +{% block head_css %} +.messagebox {font-weight:bold;font-size:16px;padding:0;margin:15px 15px 15px 15px;} +{% endblock %} + +{% block html_title %}Hostile Takeover Administration{% endblock %} + +{% block page_content %} +

ADMINISTRATION

+
+ +{% if account.ADMIN_LOG_ACCESS_RIGHT %} + +{% endif %} + +{% if account.ADJUST_SCORE_ACCESS_RIGHT %} + +{% endif %} + +{% if account.SEE_PLAYER_INFO_ACCESS_RIGHT %} + +{% endif %} + +{% if account.HIDE_PLAYER_ACCESS_RIGHT %} + +{% endif %} + +{% if account.BLOCK_PLAYER_ACCESS_RIGHT %} + +{% endif %} + +{% if account.RESET_PLAYER_ACCESS_RIGHT %} + +{% endif %} + +{% if account.SEND_CHAT_ACCESS_RIGHT %} + +{% endif %} + +{% if account.DRAIN_ACCESS_RIGHT %} + +{% endif %} + +{% if account.ADD_ACCOUNT_ACCESS_RIGHT %} +{% endif %} + +{% if account.REMOVE_ACCOUNT_ACCESS_RIGHT %} +{% endif %} + + +{% endblock %} diff --git a/stats/adminlog.py b/stats/adminlog.py new file mode 100644 index 0000000..8f451e3 --- /dev/null +++ b/stats/adminlog.py @@ -0,0 +1,123 @@ +import os +import urllib +import config +import models +import basehandler +import accounts +import time +import re +import json + +from google.appengine.api import users +from google.appengine.ext.webapp import template + +MAX_QUERY_COUNT = 2000 + +class AdminLog(basehandler.BaseHandler): + def get(self): + # Requires an authenticated user with proper access rights + account = accounts.account() + if not account or not account.ADMIN_LOG_ACCESS_RIGHT: + self.redirect(users.create_logout_url(self.request.uri)) + + # Get args + self.get_args() + + # If json request, perform that response + if self.request.get('j'): + self.response.headers['Content-Type'] = 'text/plain' + data = dict(tables=self.collect_tables()) + self.response.out.write(json.dumps(data)) + return + + # Calc all-url + if self.player_name: + all_url = '%s?p=%s&c=%d' % (config.ADMINLOG_URL, self.player_name, self.count) + else: + all_url = '%s?c=%d' % (config.ADMINLOG_URL, self.count) + + # Calc count-url + if self.player_name: + count_url = '%s?p=%s' % (config.ADMINLOG_URL, self.player_name) + else: + count_url = '%s?x=1' % config.ADMINLOG_URL + + # Otherwise render the template and serve the response + template_values = { + 'tabs': config.get_tabs(self.player_name, account), + 'selected_tab': config.TAB_NONE, + 'all_url': all_url, + 'count_url': count_url, + 'tables': self.collect_tables(), + 'player_name': self.player_name, + 'count': self.count, + 'show_counts': [ 50, 100, 250, 500, 1000 ], + } + + self.set_caching_headers(60) + self.response.headers['Content-Type'] = 'application/xhtml+xml' + path = os.path.join(os.path.dirname(__file__), 'adminlog.xhtml') + self.response.out.write(template.render(path, template_values)) + + def get_args(self): + self.player_name = self.request.get('p').lower() + self.count = int(self.request.get('c', '50')) + if self.count > MAX_QUERY_COUNT: + self.count = MAX_QUERY_COUNT + + def get_results(self): + q = models.AdminActionModel.all() + q.order('-time_utc') + return q.fetch(self.count) + + def collect_tables(self): + # Perform the query + results = self.get_results() + + # Gather tables + tables = [] + + log_table = {} + log_table['title'] = 'Admin Log' + log_table['columns'] = ['Admin', 'Time', 'Action', 'Reason', 'Ip'] + log_table['rows'] = [] + for o in results: + row = [] + row.append([(o.admin_user, '')]) + row.append([(o.time_utc, 'time_utc')]) + if o.action: + a = json.loads(o.action) + row.append([self.get_action(a)]) + row.append([self.get_reason(a)]) + else: + row.append([('', '')]) + row.append([('', '')]) + row.append([(o.ip_address, '')]) + log_table['rows'].append(row) + tables.append(log_table) + + return tables + + def get_action(self, a): + if a['action'] == 'adjust_score': + s = 'adjust score of %s, old: %s, new: %s' % \ + (a['player_name'], a['old_rating'], a['new_rating']) + return (s, '') + if a['action'] == 'hide_player': + s = 'hide player %s from leaderboard' % a['player_name'] + return (s, '') + if a['action'] == 'show_player': + s = 'show player %s on leaderboard' % a['player_name'] + return (s, '') + if a['action'] == 'block_player': + s = 'block player %s from logging in' % a['player_name'] + return (s, '') + if a['action'] == 'unblock_player': + s = 'unblock player %s from logging in' % a['player_name'] + return (s, '') + return ('empty', '') + + def get_reason(self, a): + if 'reason' in a: + return (a['reason'], '') + return ('empty', '') diff --git a/stats/adminlog.xhtml b/stats/adminlog.xhtml new file mode 100644 index 0000000..c6a014b --- /dev/null +++ b/stats/adminlog.xhtml @@ -0,0 +1,131 @@ +{% extends "basepage.xhtml" %} +{% block html_title %}Hostile Takeover Admin Log{% endblock %} + +{% block head_css %} +.sbar { padding:15px 0 15px 0; margin:0; text-align:center;} +.searchquery { font-size:13px; margin-right:10px; padding: 4px 3px; vertical-align:middle; width:190px;} +a.gpbutton { display:inline; margin: 0 auto; line-height:33px; font-size:13.5px; font-weight:bold; color:#fff; text-shadow:0px -1px 1px #666; padding:5px 8px; background:#818189; text-decoration: none; -webkit-border-radius:5px; -moz-border-radius:5px; white-space:nowrap; } +.vmiddle { vertical-align:middle;} +.message_p {text-align:center;font-size:14px;font-weight:bold;} +.stats_table {margin:0 auto;padding:0;} +.stats_table_wide {margin:auto;padding:0;width:90%} +.bottom_border {background-color:#b5bbd5;width:100%;height:1px;margin:0;padding:0} +.stat_name {width:50%;padding:8px;font-weight:bold;} +.stat_value {text-align:center;padding:8px;font-size:11px;} +.round_left {-webkit-border-top-left-radius:10px;-moz-border-radius-topleft:10px} +.round_right {-webkit-border-top-right-radius:10px;-moz-border-radius-topright:10px} +.sechdr {background:url("data:image/gif;base64,R0lGODlhAgAdAMQAAPD2/+71/+30/uvz/ury/ujx/ufw/uTu/uXv/uLt/uHs/d/q/d7p/dzo/dvn/c3e+c7e+szd+cvc+Mrb+Mja+MfZ98bY98XX98TW9sLV9sDU9cHU9r/T9QAAAAAAAAAAACH5BAAHAP8ALAAAAAACAB0AAAUxIAAEgSAMA0EUhWEgyHEkiaIsC8M0jeNAkMcjEpFIJhMKpVKxWC4XDCaT2Ww0Gg4nBAA7");margin:20px 0 0 0;padding:5px;border-top:1px solid #ccc;border-bottom:1px solid #ccc;text-align:center;font-size:16px;font-weight:bold;} +.player_span {color:black;font-size:28px;font-weight:bold;} +.player_msg_span {color:#666;font-size:12px;font-weight:bold;line-height:100%;vertical-align:top;} +.linkblock {text-decoration:none;display:block;} +.arrow {border:none;} +.player_stat_name_value {padding:8px;text-align:left;font-size:16px;font-weight:bold;} +.message {text-align:center;width:80%;margin:0 auto;font-size:14px;font-weight:bold;} +a:link {text-decoration:none;} +{% endblock %} + +{% block page_content %} +

ADMIN LOG

+ +
+ +
+
+Count: +{% for c in show_counts %} + {% ifequal c count %} + [ {{ c }} ] + {% endifequal %} + {% ifnotequal c count %} + [ {{ c }} ] + {% endifnotequal %} +{% endfor %} +
+ +{% for t in tables %} +
+
+ {{ t.title|escape }} +
+ + + + {% for name in t.columns %} + {% if forloop.first %} + + {% else %}{% if forloop.last %} + + {% else %} + + {% endif %}{% endif %} + {% endfor %} + + + {% for row in t.rows %} + + {% for field in row %} + + {% endfor %} + + {% endfor %} + +
{{ name|escape }}
{{ name|escape }}
{{ name|escape }}
+ {% for subfield in field %} + {% if forloop.last %} + {% ifequal subfield.1 "time_utc" %} + + {% endifequal %} + {% ifnotequal subfield.1 "time_utc" %} + {% if not subfield.1 %} + {{ subfield.0|escape }} + {% else %} + {{ subfield.0|escape }} + {% endif %} + {% endifnotequal %} + {% else %} + {% ifequal subfield.1 "time_utc" %} + , + {% endifequal %} + {% ifnotequal subfield.1 "time_utc" %} + {% if not subfield.1 %} + {{ subfield.0|escape }}, + {% else %} + {{ subfield.0|escape }}, + {% endif %} + {% endifnotequal %} + {% endif %} + {% endfor %} +
+{% endfor %} + + + +{% endblock %} + diff --git a/stats/app.yaml b/stats/app.yaml new file mode 100644 index 0000000..de625df --- /dev/null +++ b/stats/app.yaml @@ -0,0 +1,41 @@ +application: htleaderboard +version: 11 +runtime: python27 +threadsafe: no +api_version: 1 + +handlers: +- url: /api/ratingjob + script: main.py + login: admin + +- url: /api/adduser + script: main.py + secure: always + +- url: /api/auth + script: main.py + secure: always + +- url: /api/.* + script: main.py + +- url: /stats/.* + script: main.py + +- url: /images + static_dir: images + expiration: "90d" + +- url: /favicon.ico + static_files: favicon.ico + upload: favicon.ico + expiration: "90d" + +- url: /accounts/.* + script: main.py + secure: always + +- url: /icarus/.* + script: main.py + secure: always diff --git a/stats/auth.py b/stats/auth.py new file mode 100644 index 0000000..c9c0052 --- /dev/null +++ b/stats/auth.py @@ -0,0 +1,66 @@ +import models +import config +import time +import base64 +import random +from hashlib import md5 +import serverinfo +import playerdetail + +from google.appengine.ext import webapp +from google.appengine.ext.webapp.util import run_wsgi_app + +# 24 hours +#COOKIE_TIMEOUT_SECONDS = 60*60*24 + +# 1 hour (so accounts can be disabled more easily) +COOKIE_TIMEOUT_SECONDS = 60*60 + +class AuthUser(webapp.RequestHandler): + def get(self): + self.response.headers['Content-Type'] = 'text/plain' + username = 'anonymous' + password = '' + t = None + try: + username,password,did = self.get_username_password_did() + if self.authenticate(username, password, did): + t = self.generate_token(username, config.AUTH_GOOD_SECRET) + self.save_action(username, did) + else: + t = self.generate_token(username, config.AUTH_BAD_SECRET) + except: + t = self.generate_token(username, config.AUTH_BAD_SECRET) + self.response.out.write(t) + + def get_username_password_did(self): + d = base64.b64decode(self.request.get('a')) + username = d[0:d.find('\0')] + password = d[d.find('\0')+1:] + did = self.request.get('d') + return username,password,did + + def generate_token(self, username, secret): + a = {} + a['u'] = base64.b64encode(username) + a['c'] = random.randint(1000, 65535) + a['t'] = int(time.time()) + COOKIE_TIMEOUT_SECONDS + s = ''.join(a.__str__().split(' ')).replace("'",'"') + m = md5(s + secret) + return base64.b64encode('[%s,"%s"]' % (s, m.hexdigest())) + + def authenticate(self, username, password, did): + m = models.PlayerModel.get_by_key_name('k' + username.lower()) + if m == None or m.blocked or not did: + return False + return m.password.lower() == md5(password).hexdigest().lower() + + def save_action(self, username, did): + try: + player_name = username + anonymous = False + ip = self.request.remote_addr + action = dict(action='auth') + playerdetail.save(player_name, anonymous, did, ip, action) + except: + pass diff --git a/stats/avatar.py b/stats/avatar.py new file mode 100644 index 0000000..c78a4b2 --- /dev/null +++ b/stats/avatar.py @@ -0,0 +1,56 @@ +import models +import datetime +import basehandler + +from google.appengine.ext import webapp +from google.appengine.api import images +from google.appengine.api import urlfetch + +MAX_AGE_SECONDS = 60 * 60 * 24 * 7 * 52 + +class AvatarHandler(basehandler.BaseHandler): + def get(self): + hash = self.request.get('h') + if hash == '': + self.response.set_status(400, 'need hash parameter') + return + obj = models.AvatarModel.get(models.avatarmodel_key(hash)) + if not obj: + self.response.set_status(400, 'avatar not found') + return + size = self.request.get('s') + if size == '' or size == '64': + self.set_caching_headers(MAX_AGE_SECONDS, public=True) + self.response.headers['Content-Type'] = 'image/jpeg' + self.response.out.write(obj.content) + return + self.response.set_status(400, 'size %s not supported' % size) + +def prepare_avatar(avatar_data): + # Want 64x64 avatar. For now letterbox a square at full res and scale. + try: + img = images.Image(image_data=avatar_data) + x_left = 0.0 + y_top = 0.0 + x_right = float(img.width) + y_bottom = float(img.height) + if img.width > img.height: + x_left = float(img.width - img.height) / 2 + x_right -= x_left + else: + y_top = float(img.height - img.width) / 2 + y_bottom -= y_top + img.crop(x_left / img.width, y_top / img.height, x_right / img.width, y_bottom / img.height) + img.resize(64, 64) + return img.execute_transforms(output_encoding=images.JPEG) + except: + return None + +def save_avatar(hash, avatar_data): + q = models.AvatarModel.all(keys_only=True) + q.filter("hash = ", hash) + r = q.fetch(1) + if len(r) == 0: + avatar_key = models.avatarmodel_key(hash) + kwds = dict(hash=hash, content=avatar_data) + models.AvatarModel.get_or_insert(avatar_key.name(), **kwds) diff --git a/stats/basehandler.py b/stats/basehandler.py new file mode 100644 index 0000000..7b62639 --- /dev/null +++ b/stats/basehandler.py @@ -0,0 +1,15 @@ +import datetime + +from google.appengine.ext import webapp + +class BaseHandler(webapp.RequestHandler): + def set_caching_headers(self, seconds_valid, public=False): + utcnow = datetime.datetime.utcnow() + expires_date = utcnow + datetime.timedelta(seconds=seconds_valid) + expires_str = expires_date.strftime('%a, %d %b %Y %H:%M:%S GMT') + self.response.headers['Expires'] = expires_str + if public: + cachectl_str = 'public, max-age=%d' % seconds_valid + else: + cachectl_str = 'max-age=%d' % seconds_valid + self.response.headers['Cache-Control'] = cachectl_str diff --git a/stats/basepage.xhtml b/stats/basepage.xhtml new file mode 100644 index 0000000..aebd6a3 --- /dev/null +++ b/stats/basepage.xhtml @@ -0,0 +1,44 @@ + + + + + + +{% block html_title %}Hostile Takeover{% endblock %} + + + + + + +
+ + {% for tab in tabs %} + {% ifequal tab.id selected_tab %} + {{ tab.title|escape }} + {% endifequal %} + {% ifnotequal tab.id selected_tab %} + {{ tab.title|escape }} + {% endifnotequal %} + {% endfor %} + +
+{% block page_content %}{% endblock %} + + diff --git a/stats/blockplayer.py b/stats/blockplayer.py new file mode 100644 index 0000000..26fd4f5 --- /dev/null +++ b/stats/blockplayer.py @@ -0,0 +1,77 @@ +import os +import config +import basehandler +import models +import accounts +import admin + +from google.appengine.api import users +from google.appengine.ext import webapp +from google.appengine.ext.webapp import template +from google.appengine.ext import db + +class BlockPlayer(basehandler.BaseHandler): + def get(self): + account = self.has_access() + if not account: + self.redirect(users.create_login_url(self.request.uri)) + return + player_name = self.request.get('p').lower() + + template_values = { + 'tabs': config.get_tabs(player_name, account), + 'selected_tab': config.TAB_NONE, + } + + self.response.headers['Content-Type'] = 'application/xhtml+xml' + path = os.path.join(os.path.dirname(__file__), 'blockplayer.xhtml') + self.response.out.write(template.render(path, template_values)) + + def post(self): + account = self.has_access() + if not account: + self.redirect(users.create_logout_url(config.ADMIN_URL)) + return + + self.response.headers['Content-Type'] = 'text/plain' + player_name = self.request.get('u') + if not player_name: + self.response.out.write('no player name entered') + return + + choice = self.request.get('choice') + if not choice: + self.response.out.write('no choice selected') + return + + reason = self.request.get('r') + if not reason: + self.response.out.write('no reason specified') + return + + p = models.PlayerModel.get(models.playermodel_key(player_name)) + if not p: + self.response.out.write('cannot find player in database') + return + + if choice == 'block': + p.blocked = True + elif choice == 'unblock': + p.blocked = False + else: + self.response.out.write('choice not block or unblock') + return + + # Record this action + d = dict(action='block_player' if choice == 'block' else 'unblock_player', player_name=player_name, reason=reason) + admin.save_action(account.name, self.request.remote_addr, d) + + p.put() + self.response.out.write('success. %s is now blocked=%s, reason: %s.' % (player_name, choice == 'block', reason)) + + def has_access(self): + # Requires an authenticated user with proper access rights + account = accounts.account() + if account and account.BLOCK_PLAYER_ACCESS_RIGHT: + return account + return None diff --git a/stats/blockplayer.xhtml b/stats/blockplayer.xhtml new file mode 100644 index 0000000..34aa80c --- /dev/null +++ b/stats/blockplayer.xhtml @@ -0,0 +1,36 @@ +{% extends "basepage.xhtml" %} +{% block html_title %}Block Player{% endblock %} + +{% block head_css %} +.sbar { padding:20px 0 20px 0; margin:0; text-align:center;} +.searchquery { font-size:13px; margin-right:10px; padding: 4px 3px; vertical-align:middle; width:190px;} +a.gpbutton { display:inline; margin: 0 auto; line-height:33px; font-size:13.5px; font-weight:bold; color:#fff; text-shadow:0px -1px 1px #666; padding:5px 8px; background:#818189; text-decoration: none; -webkit-border-radius:5px; -moz-border-radius:5px; white-space:nowrap; } +.vmiddle { vertical-align:middle;} +.message_p {text-align:center;font-size:14px;font-weight:bold;} +{% endblock %} + +{% block page_content %} +

BLOCK PLAYER

+
+

Block or unblock a player from logging in to the game. Enter player name:

+
+
+ +
+
+
+ Block + Unblock +
+
+

Reason:

+ +
+ Submit +
+
+
+ +{% endblock %} diff --git a/stats/command.py b/stats/command.py new file mode 100644 index 0000000..375fb7b --- /dev/null +++ b/stats/command.py @@ -0,0 +1,50 @@ +from hashlib import md5 +import basehandler +import serverinfo +import config + +from google.appengine.api import users +from google.appengine.ext.webapp import template + +class Command(basehandler.BaseHandler): + def have_access(self): + return users.is_current_user_admin() + + def get(self): + if not self.have_access(): + self.redirect(users.create_logout_url(config.ADMIN_URL)) + return + self.finish_get(self.get_template_values()) + + def post(self): + if not users.is_current_user_admin(): + self.redirect(users.create_logout_url(config.ADMIN_URL)) + return + infos = self.get_serverinfos() + selected = [] + for info in infos: + if self.request.arguments().__contains__(info['hash']): + selected.append(info) + self.finish_post(selected, self.get_template_values()) + + def get_template_values(self): + template_values = { + 'tabs': config.get_tabs(''), + 'selected_tab': config.TAB_NONE, + 'serverinfos': self.get_serverinfos(), + 'refresh_url': self.request.uri, + 'admin_url': config.ADMIN_URL, + 'logout_url': users.create_logout_url(config.ADMIN_URL), + } + return template_values + + def get_serverinfos(self): + infos = [] + for info in serverinfo.ServerInfo.get_serverinfo()['infos']: + server = {} + server['hash'] = md5('%s-%s' % (info['name'], info['start_utc'])).hexdigest() + server['name'] = info['name'] + server['start_utc'] = info['start_utc'] + server['props'] = ['%s: %s' % (key, info[key]) for key in info.keys()] + infos.append(server) + return infos diff --git a/stats/command.xhtml b/stats/command.xhtml new file mode 100644 index 0000000..6405d77 --- /dev/null +++ b/stats/command.xhtml @@ -0,0 +1,43 @@ +{% extends "basepage.xhtml" %} + +{% block head_css %} +{{ block.super }} +.messagebox {font-weight:bold;font-size:16px;padding:0;margin:15px 15px 15px 15px;} +{% endblock %} + +{% block page_content %} +
+
+ + +{% for server in serverinfos %} + + + + + +{% endfor %} +
NameProperties
+ + + {{ server.name }} + + {% for prop in server.props %} + {{ prop|escape }}
+ {% endfor %} +
+
+
+{% for error in errors %} +

{{ error }}

+ {% if forloop.last %} +
+ {% endif %} +{% endfor %} +
+
+{% block form_elements %}{% endblock %} +
+
+ +{% endblock %} diff --git a/stats/config.py b/stats/config.py new file mode 100644 index 0000000..355382c --- /dev/null +++ b/stats/config.py @@ -0,0 +1,91 @@ +import os + +# Misc config items + +def is_debug(): + try: + return os.environ['SERVER_SOFTWARE'].split('/')[0] == 'Development' + except: + return False + +# Caching timeout is the same for all info pages + +INFOPAGE_MAX_AGE_SECONDS = 5 * 60 + +# Must match mpshared/side.h:kcSides + +SIDE_COUNT_MAX = 5 + +# Must match mpshared/side.h:kutMax. Changing it means upgrading the db or +# at least handling the length differences gracefully + +BUILT_COUNTS_MAX = 23 + +ADDGAMESTATS_URL = '/api/addgamestats' +AUTH_URL = '/api/auth' +SERVERINFO_URL = '/api/serverinfo' +SYNCERROR_URL = '/api/syncerror' +SENDCOMMAND_URL = '/api/sendcommand' +RATINGJOB_URL = '/api/ratingjob' +GETGAMES_URL = '/api/getgames' +LEADERBOARD_URL = '/stats/leaderboard' +AVATAR_URL = '/stats/avatar' +STATS_URL = '/stats/stats' +GAMES_URL = '/stats/games' +SEARCH_URL = '/stats/search' +ABOUT_URL = '/stats/about' +GAMEDETAIL_URL = '/stats/gamedetail' +MESSAGE_URL = '/stats/message' +ADMIN_URL = '/private/admin' +SENDCHAT_URL = '/private/sendchat' +DRAIN_URL = '/private/drain' +BLOCKPLAYER_URL = '/private/blockplayer' +HIDEPLAYER_URL = '/private/hideplayer' +RESETPLAYER_URL = '/private/resetplayer' +CREATEACCOUNT_URL = '/accounts/createaccount' +UPDATEACCOUNT_URL = '/accounts/updateaccount' +PLAYERDETAIL_URL = '/private/playerdetail' +ADJUSTSCORE_URL = '/private/adjustscore' +ADMINLOG_URL = '/private/adminlog' + +CHEVRON_IMAGE_URL = '/images/list-arrow.gif' +UNDER_CONSTRUCTION_IMAGE_URL = '/images/under_construction.png' +WINNER_IMAGE_URL = '/images/star.png' +WINNER_SMALL_IMAGE_URL = '/images/star_small.png' +ANONYMOUS_AVATAR_URL = '/images/default_avatar.jpg' +COMPUTER_AVATAR_URL = '/images/computer_avatar.png' + +# Keep in sync with server/secrets.h +# Should be a loaded file rather than maintained like this. +ADDGAMESTATS_SECRET = 'REPLACEME_ADDGAMESTATSSECRET' +ADDUSER_SECRET = 'REPLACEME_ADDUSERSECRET' +AUTH_GOOD_SECRET = 'REPLACEME_AUTHGOODSECRET' +AUTH_BAD_SECRET = 'REPLACEME_AUTHBADSECRET' +SERVERINFO_SECRET = 'REPLACEME_SERVERINFOSECRET' +SENDCOMMAND_SECRET = 'REPLACEME_SENDCOMMANDSECRET' + +TAB_NONE = -1 +TAB_LEADERBOARD = 0 +TAB_STATS = 1 +TAB_GAMES = 2 +TAB_SEARCH = 3 +TAB_ABOUT = 4 +TAB_ADMIN = 5 + +import accounts +def get_tabs(player, account=accounts.account()): + tabs = [] + tabs.append({ 'title': 'Leaderboard', 'id' : TAB_LEADERBOARD, + 'url' : LEADERBOARD_URL }) + if player: + tabs.append({ 'title': 'Stats', 'id' : TAB_STATS, 'url' : STATS_URL }) + tabs.append({ 'title': 'Games', 'id' : TAB_GAMES, 'url' : GAMES_URL }) + tabs.append({ 'title': 'Search', 'id' : TAB_SEARCH, 'url' : SEARCH_URL }) + tabs.append({ 'title': 'About', 'id' : TAB_ABOUT, 'url' : ABOUT_URL }) + if account and account.ADMIN_LINK_ACCESS_RIGHT: + tabs.append({ 'title': 'Admin', 'id' : TAB_ADMIN, 'url' : ADMIN_URL }) + if player != '': + for tab in tabs: + tab['url'] = '%s?p=%s' % (tab['url'], player) + return tabs + diff --git a/stats/createaccount.py b/stats/createaccount.py new file mode 100644 index 0000000..e8c074d --- /dev/null +++ b/stats/createaccount.py @@ -0,0 +1,136 @@ +import os, StringIO, traceback +from hashlib import md5 + +import config, avatar, player, models + +from google.appengine.ext import db +from google.appengine.api import images +from google.appengine.ext import webapp +from google.appengine.ext.webapp import template + +kErrNone = 0 +kErrPlayerNameInvalid = 1 +kErrPasswordInvalid = 2 +kErrPasswordMismatch = 3 +kErrAccountExists = 4 +kErrAvatarInvalid = 5 +kErrUnexpectedError = 6 +kErrAccountNotFound = 7 +kErrIncorrectPassword = 8 + +error_strings = { + kErrNone : '', + kErrPlayerNameInvalid : "Player names must be between 3 and 31 characters, and not contain spaces or the characters ", &, <, or >.", + kErrPasswordInvalid : "Passwords must be between 5 and 31 characters.", + kErrPasswordMismatch : "Both password fields must match.", + kErrAccountExists : "Account with this name already exists.", + kErrAvatarInvalid : "The avatar image was invalid. Please try a different image.", + kErrUnexpectedError : "Unexpected error creating player account.", + kErrAccountNotFound : "Account not found.", + kErrIncorrectPassword : "Incorrect password." +} + +class CreateAccount(webapp.RequestHandler): + def get(self): + self.response.headers['Content-Type'] = 'application/xhtml+xml' + self.response.headers['Cache-Control'] = 'no-cache' + path = os.path.join(os.path.dirname(__file__), 'createaccount.xhtml') + self.response.out.write(template.render(path, dict(is_update=self.is_update()))) + + def post(self): + # Gather args + args = {} + for arg in self.request.arguments(): + args[arg] = self.request.get(arg).strip() + + # Perform action + error_id = self.do_action(args, **args) + + # Render template + self.response.headers['Content-Type'] = 'application/xhtml+xml' + self.response.headers['Cache-Control'] = 'no-cache' + if error_id == kErrNone: + path = os.path.join(os.path.dirname(__file__), 'createaccountsuccess.xhtml') + else: + path = os.path.join(os.path.dirname(__file__), 'createaccount.xhtml') + args['error_msg'] = error_strings[error_id] + args['is_update'] = self.is_update() + self.response.out.write(template.render(path, args)) + + def is_update(self): + return False + + def do_action(self, args, player_name, password, password2, avatar_data): + if not self.is_valid_player_name(player_name): + return kErrPlayerNameInvalid + if not self.is_valid_password(password): + return kErrPasswordInvalid + if password != password2: + return kErrPasswordMismatch + p = models.PlayerModel.get(models.playermodel_key(player_name)) + if p: + return kErrAccountExists + avatar_data_new = avatar.prepare_avatar(avatar_data) if avatar_data else None + if avatar_data and not avatar_data_new: + return kErrAvatarInvalid + p = self.create_player(player_name, password, avatar_data_new) + if not p: + return kErrUnexpectedError + args['player'] = player.Player(p) + return kErrNone + + def is_valid_player_name(self, player_name): + if not player_name: + return False + for ch in " \"&<>": + if player_name.find(ch) >= 0: + return False + if len(player_name) < 3 or len(player_name) > 31: + return False + return True + + def is_valid_password(self, password): + if not password: + return False + if len(password) < 5 or len(password) > 31: + return False + return True + + def get_avatar_hash(self, avatar_data): + if avatar_data: + return md5(avatar_data).hexdigest().lower() + return '' + + def create_player(self, player_name, password, avatar_data): + avatar_hash = self.get_avatar_hash(avatar_data) + try: + p = db.run_in_transaction(self.add_player_txn, player_name, password, avatar_hash) + if p and avatar_data: + avatar.save_avatar(avatar_hash, avatar_data) + return p + except: + obj = models.PlayerUpdateFailed() + obj.name = player_name + obj.password = password + obj.avatar_hash = avatar_hash + io = StringIO.StringIO() + traceback.print_exc(file=io) + io.seek(0) + obj.backtrace = io.read() + io.close() + obj.put() + return None + + def add_player_txn(self, player_name, password, avatar_hash): + # Player shouldn't exist yet + p = models.PlayerModel.get(models.playermodel_key(player_name)) + if p: + return None + player_key = models.playermodel_key(player_name) + p = models.PlayerModel(key_name=player_key.name()) + p.name = player_name + p.password = md5(password).hexdigest().lower() + p.avatar_hash = avatar_hash + p.put() + return p + diff --git a/stats/createaccount.xhtml b/stats/createaccount.xhtml new file mode 100644 index 0000000..543f6d0 --- /dev/null +++ b/stats/createaccount.xhtml @@ -0,0 +1,98 @@ + + + + + +{% if is_update %} + Hostile Takeover Update Account +{% else %} + Hostile Takeover Create Account +{% endif %} + + + + + + + + +{% if is_update %} +

Update Account

+{% else %} +

Create Account

+{% endif %} + +{% if error_msg %} +
+
+ + + +
+ {{ error_msg|escape }} +
+{% endif %} + +
+ + + + + + + + + + + + + + + + + +
+ Player name: + + +
+ Password: + + +
+ {% if is_update %} + New Password: + {% else %} + Confirm Password: + {% endif %} + + +
+ Optional Avatar: + + +
+ +
+
+ + {%if is_update %} + + {% else %} + + {% endif %} +
+
+ + + diff --git a/stats/createaccountsuccess.xhtml b/stats/createaccountsuccess.xhtml new file mode 100644 index 0000000..bc42f7b --- /dev/null +++ b/stats/createaccountsuccess.xhtml @@ -0,0 +1,58 @@ + + + + + +{% if is_update %} + Hostile Takeover Update Account +{% else %} + Hostile Takeover Create Account +{% endif %} + + + + + + + + +

Success!

+ +
+
+ + + +
+
Carefully remember your new account information.
+
+
+ + + + + + + + + + + +
+ +
+ Player name: {{ player_name|escape }} +
+ Password: {{ password|escape }} +
+ + + diff --git a/stats/cron.yaml b/stats/cron.yaml new file mode 100644 index 0000000..8658605 --- /dev/null +++ b/stats/cron.yaml @@ -0,0 +1,4 @@ +cron: +- description: ratings adjust job + url: /api/ratingjob + schedule: every 15 minutes diff --git a/stats/drain.py b/stats/drain.py new file mode 100644 index 0000000..12bd278 --- /dev/null +++ b/stats/drain.py @@ -0,0 +1,42 @@ +import os +import command +import serverinfo +import accounts + +from google.appengine.ext.webapp import template + +class Drain(command.Command): + def have_access(self): + account = accounts.account() + if not account or not account.DRAIN_ACCESS_RIGHT: + return False + return True + + def finish_get(self, template_values): + self.response.headers['Content-Type'] = 'application/xhtml+xml' + path = os.path.join(os.path.dirname(__file__), 'drain.xhtml') + self.response.out.write(template.render(path, template_values)) + + def finish_post(self, selected, template_values): + drain_command = None + if self.request.get('drain'): + drain_command = 'drain' + if self.request.get('undrain'): + drain_command = 'undrain' + + errors = [] + if len(selected) == 0: + errors.append('Must select at least one server.') + if drain_command == None: + errors.append('Must select drain or undrain.') + else: + for info in selected: + serverinfo.ServerInfo.send_command(info, '{"command": "%s"}' % drain_command) + errors.append('Server %s sent %s command.' % (info['name'], drain_command)) + + template_values['errors'] = errors + + self.response.headers['Content-Type'] = 'application/xhtml+xml' + path = os.path.join(os.path.dirname(__file__), 'drain.xhtml') + self.response.out.write(template.render(path, template_values)) + diff --git a/stats/drain.xhtml b/stats/drain.xhtml new file mode 100644 index 0000000..6889b4a --- /dev/null +++ b/stats/drain.xhtml @@ -0,0 +1,13 @@ +{% extends "command.xhtml" %} + +{% block html_title %}Drain / Undrain{% endblock %} + +{% block page_content %} +

DRAIN / UNDRAIN

+{{ block.super }} +{% endblock %} + +{% block form_elements %} + + +{% endblock %} diff --git a/stats/favicon.ico b/stats/favicon.ico new file mode 100644 index 0000000..c24cbcd Binary files /dev/null and b/stats/favicon.ico differ diff --git a/stats/gamedetail.py b/stats/gamedetail.py new file mode 100644 index 0000000..a268fd7 --- /dev/null +++ b/stats/gamedetail.py @@ -0,0 +1,96 @@ +import os +import config +import models +import gamestats +import message +import addgamestats +import basehandler + +from wrap import PropWrap +from wrap import DictWrap + +from google.appengine.ext import webapp +from google.appengine.ext.webapp import template +from google.appengine.ext import db + +class GameDetail(basehandler.BaseHandler): + def get(self): + # If a game key is specified, use that. If no game key and a player + # is specified, use the last game of that player. If neither, show + # a message to the user. The common error case is a non-logged in + # player, who hasn't specified a game. + + # Get player name, if any + player_name = self.request.get('p') + + # Get game key. If missing, get it from the player's last game + key_name = self.request.get('g') + if key_name == '': + # No game key name. Is there a player? + if player_name == '': + message.show(self, message.GAME_NOT_FOUND) + return + obj = models.PlayerModel.get(models.playermodel_key(player_name)) + if not obj: + message.show(self, message.PLAYER_NOT_FOUND) + return + key_name = obj.last_game_key_name + if key_name == '': + message.show(self, message.PLAYER_NOT_PLAYED_GAME) + return + g, game_obj = gamestats.load_from_key_name(key_name, player_name, + load_players=True) + if not g: + message.show(self, message.GAME_NOT_FOUND) + return + + if self.request.get('j') == '1': + self.response.headers['Content-Type'] = 'text/plain' + self.response.out.write(g.json) + return + + if self.request.get('i') == '1': + self.response.headers['Content-Type'] = 'text/plain' + for player_stat in g.player_stats: + ip = 'unknown' + if 'ip' in player_stat.__dict__ and len(player_stat.ip) != 0: + ip = player_stat.ip + did = 'unknown' + if 'did' in player_stat.__dict__ and len(player_stat.did) != 0: + did = player_stat.did + winner = '' + if player_stat.win_result == gamestats.PLAYER_RESULT_WIN: + winner = ' (winner)' + self.response.out.write('Player: %s ip: %s did: %s %s\n' % (player_stat.name, ip, did, winner)) + if game_obj.dids: + self.response.out.write('\n') + self.response.out.write(game_obj.dids) + return + + # Render the template and serve the response + template_values = { + 'tabs': config.get_tabs(player_name), + 'selected_tab': config.TAB_NONE, + 'gamestats': g, + 'units_built_sums': self.get_units_built_sums(g), + 'computer_default_rating': gamestats.COMPUTER_DEFAULT_RATING, + 'anonymous_default_rating': gamestats.ANONYMOUS_DEFAULT_RATING, + 'player_default_rating': gamestats.PLAYER_DEFAULT_RATING, + 'computer_avatar_url': config.COMPUTER_AVATAR_URL, + 'anonymous_avatar_url': config.ANONYMOUS_AVATAR_URL, + 'winner_image_url': config.WINNER_IMAGE_URL, + 'chevron_image_url': config.CHEVRON_IMAGE_URL, + } + + self.set_caching_headers(config.INFOPAGE_MAX_AGE_SECONDS) + self.response.headers['Content-Type'] = 'application/xhtml+xml' + path = os.path.join(os.path.dirname(__file__), 'gamedetail.xhtml') + self.response.out.write(template.render(path, template_values)) + + def get_units_built_sums(self, g): + units_built_sums = [0] * config.BUILT_COUNTS_MAX + for i in xrange(config.BUILT_COUNTS_MAX): + for player_stat in g.player_stats: + units_built_sums[i] += player_stat.winstats.built_counts[i] + return units_built_sums + diff --git a/stats/gamedetail.xhtml b/stats/gamedetail.xhtml new file mode 100644 index 0000000..4b52342 --- /dev/null +++ b/stats/gamedetail.xhtml @@ -0,0 +1,469 @@ +{% extends "basepage.xhtml" %} +{% block html_title %}Hostile Takeover Game Detail{% endblock %} + +{% block head_css %} +.stats_table {margin:0 auto;padding:0;} +.stats_table_wide {margin:auto;padding:0;width:90%} +.bottom_border {background-color:#b5bbd5;width:100%;height:1px;margin:0;padding:0} +.stat_name {width:50%;padding:8px;font-weight:bold;} +.stat_value {text-align:center;padding:8px;font-weight:bold;} +.round_left {-webkit-border-top-left-radius:10px;-moz-border-radius-topleft:10px} +.round_right {-webkit-border-top-right-radius:10px;-moz-border-radius-topright:10px} +.sechdr {background:url("data:image/gif;base64,R0lGODlhAgAdAMQAAPD2/+71/+30/uvz/ury/ujx/ufw/uTu/uXv/uLt/uHs/d/q/d7p/dzo/dvn/c3e+c7e+szd+cvc+Mrb+Mja+MfZ98bY98XX98TW9sLV9sDU9cHU9r/T9QAAAAAAAAAAACH5BAAHAP8ALAAAAAACAB0AAAUxIAAEgSAMA0EUhWEgyHEkiaIsC8M0jeNAkMcjEpFIJhMKpVKxWC4XDCaT2Ww0Gg4nBAA7");margin:20px 0 0 0;padding:5px;border-top:1px solid #ccc;border-bottom:1px solid #ccc;text-align:center;font-size:16px;font-weight:bold;} +.avatar_td {border:none;width:64px;height:64px;} +.winner_td {border:none;width:48px;height:64px;} +.player_td {padding:8px;width:270px;max-width:270px;overflow:hidden;white-space:nowrap;} +.player_span {color:black;font-size:28px;font-weight:bold;} +.player_msg_span {color:#666;font-size:12px;font-weight:bold;line-height:100%;vertical-align:top;} +.avatar_img {border:none;vertical-align:bottom;} +.chevron_td {padding:8px;font-size:20px;font-weight:bold;} +.linkblock {text-decoration:none;display:block;} +.arrow {border:none;} +.player_stat_name_value {padding:8px;text-align:left;font-size:16px;font-weight:bold;} +.message {text-align:center;width:80%;margin:0 auto;font-size:12px;font-weight:bold;} +{% endblock %} + +{% block page_content %} +

GAME DETAIL

+ + + + + + + + + + + + + + + + + + + +
Mission
+ Title: + + {{ gamestats.title|escape }} +
+ Speed: + + {{ gamestats.game_speed_multiplier|escape }} +
+ Duration: + + {{ gamestats.duration_minutes|escape }} minutes +
+ Played: + + +
+ + + +{% ifequal gamestats.game_result 1 %} +
+
+NOTE: This game did not affect ratings because it was less than 3 minutes long. +
+{% endifequal %} + +{% ifequal gamestats.game_result 2 %} +
+
+NOTE: This game did not affect ratings because there must be a registered player winner, and a registered player loser. +
+{% endifequal %} + +{% ifequal gamestats.game_result 3 %} +
+
+NOTE: This game did not affect ratings because no winners were recorded. +
+{% endifequal %} + + + + {% for player_stat in gamestats.player_stats %} + + + {% ifnotequal player_stat.is_computer 0 %} + + + + + + {% endifnotequal %} + {% ifnotequal player_stat.is_anonymous 0 %} + + + + + + {% endifnotequal %} + {% ifnotequal player_stat.is_user 0 %} + + + + + + + {% endifnotequal %} + + {% endfor %} + +
Players
+ {% ifequal player_stat.win_result 2 %} + + {% endifequal %} +
+ + +
+ AI +
rating: {{ computer_default_rating }}, computer player
+
+
+ + +
+ {{ player_stat.name|escape }} +
rating: {{ anonymous_default_rating }}, anonymous player
+
+
+ + +
+ {{ player_stat.player.name|escape }} +
+ {% ifequal player_stat.player.rating 0 %} + rating: {{ player_default_rating }}, games: {{ player_stat.player.game_count }}, won: {{ player_stat.player.games_won }} + {% else %} + rating: {{ player_stat.player.rating }}, games: {{ player_stat.player.game_count }}, won: {{ player_stat.player.games_won }} + {% endifequal %} +
+
+
+ +
+ +{% ifnotequal gamestats.has_multiplayer_teams 0 %} + + + {% for team in gamestats.teams %} + + + + + {% endfor %} +
Teams
+ {% for player_stat in team %} + {% if forloop.last %} + {{ player_stat.name }} + {% else %} + {{ player_stat.name }}, + {% endif %} + {% endfor %} + + {% for player_stat in team %} +
+ {% endfor %} +
+{% endifnotequal %} + + + + + {% for player_stat in gamestats.player_stats %} + {% if forloop.last %} + + {% else %} + + {% endif %} + {% endfor %} + + + + {% for player_stat in gamestats.player_stats %} + + {% endfor %} + + + + {% for player_stat in gamestats.player_stats %} + + {% endfor %} + + + + {% for player_stat in gamestats.player_stats %} + + {% endfor %} + + + + {% for player_stat in gamestats.player_stats %} + + {% endfor %} + + + + {% for player_stat in gamestats.player_stats %} + + {% endfor %} + + + + {% for player_stat in gamestats.player_stats %} + + {% endfor %} + + + + {% for player_stat in gamestats.player_stats %} + + {% endfor %} + + + + {% for player_stat in gamestats.player_stats %} + + {% endfor %} + + + + {% for player_stat in gamestats.player_stats %} + + {% endfor %} + + +
Mission Stats
Mobile Units Built{{ player_stat.munts_built }}
Structures Built{{ player_stat.structs_built }}
Mobile Units Lost{{ player_stat.winstats.munts_lost }}
Structures Lost{{ player_stat.winstats.structs_lost }}
Mobile Units Killed{{ player_stat.winstats.enemy_munts_killed }}
Structures Killed{{ player_stat.winstats.enemy_structs_killed }}
Units Remaining{{ player_stat.units_remaining }}
Credits Acquired{{ player_stat.winstats.credits_acquired }}
Credits Consumed{{ player_stat.winstats.credits_consumed }}
+ + + + + {% for player_stat in gamestats.player_stats %} + {% if forloop.last %} + + {% else %} + + {% endif %} + {% endfor %} + + + {% ifnotequal units_built_sums.0 0 %} + + + {% for player_stat in gamestats.player_stats %} + + {% endfor %} + + {% endifnotequal %} + {% ifnotequal units_built_sums.1 0 %} + + + {% for player_stat in gamestats.player_stats %} + + {% endfor %} + + {% endifnotequal %} + {% ifnotequal units_built_sums.2 0 %} + + + {% for player_stat in gamestats.player_stats %} + + {% endfor %} + + {% endifnotequal %} + {% ifnotequal units_built_sums.19 0 %} + + + {% for player_stat in gamestats.player_stats %} + + {% endfor %} + + {% endifnotequal %} + {% ifnotequal units_built_sums.22 0 %} + + + {% for player_stat in gamestats.player_stats %} + + {% endfor %} + + {% endifnotequal %} + {% ifnotequal units_built_sums.3 0 %} + + + {% for player_stat in gamestats.player_stats %} + + {% endfor %} + + {% endifnotequal %} + {% ifnotequal units_built_sums.4 0 %} + + + {% for player_stat in gamestats.player_stats %} + + {% endfor %} + + {% endifnotequal %} + {% ifnotequal units_built_sums.5 0 %} + + + {% for player_stat in gamestats.player_stats %} + + {% endfor %} + + {% endifnotequal %} + {% ifnotequal units_built_sums.6 0 %} + + + {% for player_stat in gamestats.player_stats %} + + {% endfor %} + + {% endifnotequal %} + {% ifnotequal units_built_sums.7 0 %} + + + {% for player_stat in gamestats.player_stats %} + + {% endfor %} + + {% endifnotequal %} + {% ifnotequal units_built_sums.8 0 %} + + + {% for player_stat in gamestats.player_stats %} + + {% endfor %} + + {% endifnotequal %} + {% ifnotequal units_built_sums.20 0 %} + + + {% for player_stat in gamestats.player_stats %} + + {% endfor %} + + {% endifnotequal %} + {% ifnotequal units_built_sums.9 0 %} + + + {% for player_stat in gamestats.player_stats %} + + {% endfor %} + + {% endifnotequal %} + {% ifnotequal units_built_sums.10 0 %} + + + {% for player_stat in gamestats.player_stats %} + + {% endfor %} + + {% endifnotequal %} + {% ifnotequal units_built_sums.11 0 %} + + + {% for player_stat in gamestats.player_stats %} + + {% endfor %} + + {% endifnotequal %} + {% ifnotequal units_built_sums.12 0 %} + + + {% for player_stat in gamestats.player_stats %} + + {% endfor %} + + {% endifnotequal %} + {% ifnotequal units_built_sums.13 0 %} + + + {% for player_stat in gamestats.player_stats %} + + {% endfor %} + + {% endifnotequal %} + {% ifnotequal units_built_sums.14 0 %} + + + {% for player_stat in gamestats.player_stats %} + + {% endfor %} + + {% endifnotequal %} + {% ifnotequal units_built_sums.15 0 %} + + + {% for player_stat in gamestats.player_stats %} + + {% endfor %} + + {% endifnotequal %} + {% ifnotequal units_built_sums.16 0 %} + + + {% for player_stat in gamestats.player_stats %} + + {% endfor %} + + {% endifnotequal %} + {% ifnotequal units_built_sums.17 0 %} + + + {% for player_stat in gamestats.player_stats %} + + {% endfor %} + + {% endifnotequal %} + {% ifnotequal units_built_sums.18 0 %} + + + {% for player_stat in gamestats.player_stats %} + + {% endfor %} + + {% endifnotequal %} + {% ifnotequal units_built_sums.21 0 %} + + + {% for player_stat in gamestats.player_stats %} + + {% endfor %} + + {% endifnotequal %} + +
Unit Built Stats
Security Guard{{ player_stat.winstats.built_counts.0 }}
Rocket Trooper{{ player_stat.winstats.built_counts.1 }}
Corporate Raider{{ player_stat.winstats.built_counts.2 }}
Andy{{ player_stat.winstats.built_counts.19 }}
Fox{{ player_stat.winstats.built_counts.22 }}
SR-98 Eagle{{ player_stat.winstats.built_counts.3 }}
T-29 Broadsword{{ player_stat.winstats.built_counts.4 }}
M-18 Hydra{{ player_stat.winstats.built_counts.5 }}
T-33 Liberator{{ player_stat.winstats.built_counts.6 }}
G-4 Bullpup{{ player_stat.winstats.built_counts.7 }}
H-7 Dominion{{ player_stat.winstats.built_counts.8 }}
A-3 Cyclops{{ player_stat.winstats.built_counts.20 }}
Power Generator{{ player_stat.winstats.built_counts.9 }}
Galaxite Processor{{ player_stat.winstats.built_counts.10 }}
Galaxite Warehouse{{ player_stat.winstats.built_counts.11 }}
Human Resource Center{{ player_stat.winstats.built_counts.12 }}
Vehicle Transport Station{{ player_stat.winstats.built_counts.13 }}
Surveillance Center{{ player_stat.winstats.built_counts.14 }}
R&D Center{{ player_stat.winstats.built_counts.15 }}
Headquarters{{ player_stat.winstats.built_counts.16 }}
Gatling Tower{{ player_stat.winstats.built_counts.17 }}
Rocket Tower{{ player_stat.winstats.built_counts.18 }}
Replicator{{ player_stat.winstats.built_counts.21 }}
+{% endblock %} + diff --git a/stats/games.py b/stats/games.py new file mode 100644 index 0000000..194f7db --- /dev/null +++ b/stats/games.py @@ -0,0 +1,102 @@ +import os +import config +import gamestats +import models +import basehandler + +from google.appengine.ext import webapp +from google.appengine.ext.webapp import template +from google.appengine.ext import db + +SHOW_COUNT_MAX = 100 +SHOW_COUNT_INCREMENT = 25 + +class Games(basehandler.BaseHandler): + def get(self): + # Get player name, if any + player_name = self.request.get('p') + + # Get user name, if any. If none, use player name + user_name = self.request.get('u') + if not user_name: + user_name = player_name + + # All or not + all = False + if self.request.get('a') == '1': + all = True + if not player_name and not user_name: + all = True + + # Get the row count and validate + count_str = self.request.get('count') + if count_str == '': + count = SHOW_COUNT_INCREMENT + else: + count = int(count_str) + if count < 0: + count = 0 + if count > SHOW_COUNT_MAX: + count = SHOW_COUNT_MAX + + # Retrieve gamestats + rows = self.get_gamestats_rows(player_name, user_name, count, all) + + # Figure out the next show url, if any (Show top N) + show_count = count + SHOW_COUNT_INCREMENT + if show_count > SHOW_COUNT_MAX: + show_count = SHOW_COUNT_MAX + show_url = '' + if len(rows) == count and show_count > count: + show_url = self.get_games_url(player_name, user_name, all, + show_count) + + # If player_name and user_name are the same, the Games tab is selected + selected_tab = config.TAB_NONE + if player_name == user_name: + selected_tab = config.TAB_GAMES + + # Render the template and serve the response + template_values = { + 'tabs': config.get_tabs(player_name), + 'selected_tab': selected_tab, + 'rows': rows, + 'chevron_image_url': config.CHEVRON_IMAGE_URL, + 'winner_small_image_url': config.WINNER_SMALL_IMAGE_URL, + 'show_url': show_url, + 'show_count': show_count, + 'all': all, + 'user_name': user_name, + 'selector_url': self.get_games_url(player_name, user_name, not all) + } + + self.set_caching_headers(config.INFOPAGE_MAX_AGE_SECONDS) + self.response.headers['Content-Type'] = 'application/xhtml+xml' + path = os.path.join(os.path.dirname(__file__), 'games.xhtml') + self.response.out.write(template.render(path, template_values)) + + def get_gamestats_rows(self, player_name, user_name, count, all): + q = models.GameStatsModel.all() + if not all and user_name: + key_name = models.playermodel_key(user_name).name() + q.filter('player_key_names = ', key_name) + q.order('-date') + results = q.fetch(count) + return [gamestats.GameStats(obj.json, player_name) for obj in results] + + def get_games_url(self, player_name, user_name, all, count = -1): + d = {} + if player_name: + d['p'] = player_name + if user_name and user_name != player_name: + d['u'] = user_name + if all: + d['a'] = 1 + if count > 0: + d['count'] = count + keys = d.keys() + keys.sort() + a = '&'.join(['%s=%s' % (k, d[k]) for k in keys]) + if a: + return '%s?%s' % (config.GAMES_URL, a) + return config.GAMES_URL diff --git a/stats/games.xhtml b/stats/games.xhtml new file mode 100644 index 0000000..beb4e58 --- /dev/null +++ b/stats/games.xhtml @@ -0,0 +1,117 @@ +{% extends "basepage.xhtml" %} +{% block html_title %}Hostile Takeover Game List{% endblock %} + +{% block head_css %} +.cllist {width:100%;border-collapse:collapse;border-spacing:0px;-webkit-border-vertical-spacing:0px;} +.row_title {padding:0;color:black;font-size:20px;font-weight:bold;} +.row_smallgray {padding:0;color:#666;font-size:12px;font-weight:bold;} +.arrow {border:none;} +.bottom_border {background-color:#b5bbd5;width:100%;height:1px;margin:0;padding:0} +.linkblock {text-decoration:none;display:block;} +.clshowrow {width:100%;height:40px;} +.clshow {color:black;font-size:14px;font-weight:bold;} +.nospace {letter-spacing:0.0cm;} +.bottombar { -webkit-text-size-adjust: none; background:#EFF1FA url("data:image/gif;base64,R0lGODlhAQAjAMQAAPL0/evt+O/x+uns9+7w+ufq9OXp9PL0/Nnf7PDy++jr9tbc6ePn8+Hm8tTb6dng7NXb6e3v+N/k8PX2/uTo8+Hl8NXc6dvh7t3j7/Hz/Ozu+Nvg7d3i7vT1/vb3/9fd6yH5BAAAAAAALAAAAAABACMAAAUdoOdNXQccWSIQkRYMSmFQTFNJGHdtD/J9CwvEEQIAOw==") repeat-x; border-bottom:1px solid #B5BBD5; line-height:33px; height:35px; margin:0; padding:0; width:100%; overflow:hidden; } +.bottombar a { text-decoration: none; } +a.nav-left { display:inline; margin: 0 auto; line-height:33px; font-family:Arial, Helvetica, sans-serif; font-size:13.5px; padding:5px 8px; background-repeat:repeat-x; border:1px solid #B5BBD5; -webkit-border-top-left-radius:5px; -moz-border-radius-topleft:5px; -webkit-border-bottom-left-radius:5px; -moz-border-radius-bottomleft:5px; } +a.nav-on { font-weight:bold; color:#fff; text-shadow:1px 2px 1px #666; background:#A6A8B3 url("data:image/gif;base64,R0lGODlhAQAaAMQAAI+RopyfrZGUpLCxu6qstoyPoI2Qopmbqq6xuYqOn6uuuK2wuairtbK0vJqdq6aos5OWppWYp5eaqJ+hrpCTo7GzvLO1vaCjsKKlsaWnswAAAAAAAAAAAAAAAAAAAAAAACH5BAAAAAAALAAAAAABABoAAAUVoNVUA7IoBPNk2DUFziFFkEABRpGEADs=") repeat-x; } +a.nav-off { color:#333; text-shadow:2px 2px 2px #C5C7D3; background:#E7EAF5 url("data:image/gif;base64,R0lGODlhAQAaAMQAANXa6ejr9efq9ODl8Nzi7tbc6dfe6+Xq9Nnf7NTa6Nfd6t3i7+Po8uLm8ufq9ens9tje69rg7ODl8dTc6tvg7ePn8+jr9t7k8OXo8////wAAAAAAAAAAAAAAAAAAAAAAACH5BAAAAAAALAAAAAABABoAAAUVYPZYgSMcGFM10nAtBBUhkKEUE5CEADs=") repeat-x; } +a.nav-right { display:inline; margin: 0 auto; line-height:33px; font-family:Arial, Helvetica, sans-serif; font-size:13.5px; padding:5px 8px; background-repeat:repeat-x; border-top:1px solid #B5BBD5; border-right:1px solid #B5BBD5; border-bottom:1px solid #B5BBD5; -webkit-border-top-right-radius:5px; -moz-border-radius-topright:5px; -webkit-border-bottom-right-radius:5px; -moz-border-radius-bottomright:5px; } +{% endblock %} + +{% block page_content %} + +
+ + + + + + +
+ {% if user_name %} + {% if all %} + {{ user_name|escape }}'s games + All games + {% else %} + {{ user_name|escape }}'s games + All games + {% endif %} + {% else %} + All games + {% endif %} +
+ + + {% for gamestats in rows %} + + + {% ifequal gamestats.game_result 0 %} + + {% else %} + + {% endifequal %} + + + + + + {% endfor %} + + {% ifnotequal show_url "" %} + + + + + + {% endifnotequal %} +
+ {{ gamestats.title|escape }}
+ + + {% for player_stat in gamestats.player_stats %} + {% ifequal player_stat.win_result 2 %} + + {% endifequal %} + {% ifequal forloop.last 0 %} + {{ player_stat.name|escape }}, + {% else %} + {{ player_stat.name|escape }} + {% endifequal %} + {% endfor %} + +
+ +
+ + Show Last {{ show_count }} Games +
+ +{% endblock %} diff --git a/stats/gamestats.py b/stats/gamestats.py new file mode 100644 index 0000000..07e3a36 --- /dev/null +++ b/stats/gamestats.py @@ -0,0 +1,544 @@ +import models +import player +import config +import playerstat +import wrap +import ratingjob +import serverinfo +import playerdetail +import json +# import logging + +import time +import traceback +import StringIO +from google.appengine.ext import db + +""" +AddGameStat json: +{ + "server_id": , + "server_start": , + "gameid": , + "packid_id": , + "packid_hash": "", + "title": "" + "filename": "" + "game_speed": , + "min_players": , + "max_players": , + "start_utc": , + "end_utc": , + "player_stats": [ + { + "name": "", + "pid": , + "winstats": { + "side_mask": , + "side_mask_allies": , + "credits_acquired": , + "credits_consumed": , + "enemy_munts_killed": , + "enemy_structs_killed": , + "munts_lost": , + "structs_lost": , + "unit_counts": [ ], + "built_counts": [ ], + "ff": + } + } + ] +} +""" + +# 0: Security Guard +# 1: Rocket Trooper +# 2: Corporate Raider +# 3: SR-98 Eagle +# 4: T-29 Broadsword +# 5: M-18 Hydra +# 6: T-33 Liberator +# 7: G-4 Bullpup +# 8: H-7 Dominion +# 9: Power Generator +# 10: Galaxite Processor +# 11: Galaxite Storage Warehouse +# 12: Human Resource Center +# 13: Vehicle Transport Station +# 14: Surveillance Center +# 15: Research & Development Center +# 16: Headquarters +# 17: Gatling Tower +# 18: Rocket Tower +# 19: Andy +# 20: A-3 Cyclops +# 21: Replicator +# 22: Fox + +GAME_RESULT_SCOREABLE = 0 +GAME_RESULT_TOO_SHORT = 1 +GAME_RESULT_NEED_REGISTERED_OPPONENTS = 2 +GAME_RESULT_NO_WINNERS = 3 + +PLAYER_RESULT_NONE = 0 +PLAYER_RESULT_LOSE = 1 +PLAYER_RESULT_WIN = 2 + +kfwsReceivedStats = 0x0001 +kfwsWinner = 0x0002 +kfwsLoser = 0x0004 +kfwsWinChallenger = 0x0008 +kfwsComputer = 0x0010 +kfwsHuman = 0x0020 +kfwsAnonymous = 0x0040 +kfwsLocked = 0x0080 +kfwsRemovedAtGameStart = 0x0100 +kfwsMask = 0x1ff + +COMPUTER_DEFAULT_RATING = 1200 +ANONYMOUS_DEFAULT_RATING = 1300 +PLAYER_DEFAULT_RATING = 1500 + +DURATION_SECS_MINIMUM = 60 * 3 + +class GameStats(wrap.DictWrap): + def __init__(self, json, viewing_player_name='', load_players=False): + super(GameStats, self).__init__(json.loads(json)) + self.json = json + self.viewing_player_name = viewing_player_name + + # Prune out non-contributing players + self.prune_zombie_players() + + # Calc results + self.init_win_results() + + # Initialize PlayerStats objects + self.init_player_stats() + + # Create pid->player_stat map + self.pid_player_stat_map = {} + for player_stat in self.player_stats: + self.pid_player_stat_map[player_stat.pid] = player_stat + + # Create key->pid map + self.key_pid_map = {} + for player_stat in self.player_stats: + if player_stat.is_user: + key = models.playermodel_key(player_stat.name) + self.key_pid_map[key] = player_stat.pid + + # If asked, load and push Player objects into PlayerStat objects + if load_players: + self.load_player_objects() + + # Remember teams + self.teams = self.get_teams() + + def init_player_stats(self): + # Replace player_stats with PlayerStats objects and sort by side + player_stats = [] + for player_stat in self.player_stats: + player_stat = playerstat.PlayerStat(player_stat, + self.pid_win_result_map[player_stat.pid]) + player_stats.append(player_stat) + player_stats.sort(lambda x, y: cmp(x.side, y.side)) + self.player_stats = player_stats + + def load_player_objects(self): + # Get all the player models at once + keys = self.key_pid_map.keys() + objs = models.PlayerModel.get(keys) + + # Append player objects to player_stat objects + for i in xrange(len(keys)): + pid = self.key_pid_map[keys[i]] + player_stat = self.pid_player_stat_map[pid] + player_stat.player = player.Player(objs[i], + self.viewing_player_name) + + def prune_zombie_players(self): + # Remove players that had no contribution. This removes computer + # players that were allocated but never used. + i = 0 + while i < len(self.player_stats): + if sum(self.player_stats[i].winstats.built_counts) == 0: + self.player_stats.pop(i) + continue + if self.player_stats[i].winstats.ff & kfwsRemovedAtGameStart: + self.player_stats.pop(i) + continue + i += 1 + + def get_nonanonymous_human_count(self): + humans = 0 + for team in self.teams: + for player_stat in team: + if (player_stat.winstats.ff & kfwsComputer): + continue + if (player_stat.winstats.ff & kfwsAnonymous): + continue + if (player_stat.winstats.ff & kfwsHuman): + humans += 1 + return humans + + def get_game_result(self): + # Games must run for a minimum time to be scored + elapsed_secs = self.end_utc - self.start_utc + if elapsed_secs < DURATION_SECS_MINIMUM: + return GAME_RESULT_TOO_SHORT; + + # There must be at least one winner + winner_tids = self.get_winner_tids() + if len(winner_tids) == 0: + return GAME_RESULT_NO_WINNERS + + winners_registered = False + for tid in winner_tids: + for player_stat in self.teams[tid]: + if player_stat.winstats.ff & (kfwsComputer | kfwsAnonymous): + continue + winners_registered = True + + losers_registered = False + for tid in xrange(len(self.teams)): + if winner_tids.__contains__(tid): + continue + for player_stat in self.teams[tid]: + if player_stat.winstats.ff & (kfwsComputer | kfwsAnonymous): + continue + losers_registered = True + + # For a registered player to win, there must be a registered loser too, + # and vice versa. + if not winners_registered or not losers_registered: + return GAME_RESULT_NEED_REGISTERED_OPPONENTS + + return GAME_RESULT_SCOREABLE + + def init_win_results(self): + self.pid_win_result_map = {} + winner_tids = self.get_winner_tids() + for player_stat in self.player_stats: + tid = self.find_tid(player_stat.pid) + if winner_tids.__contains__(tid): + self.pid_win_result_map[player_stat.pid] = PLAYER_RESULT_WIN + else: + self.pid_win_result_map[player_stat.pid] = PLAYER_RESULT_LOSE + + def get_winner_tids(self): + # Players on the same team as winners are winners too + winner_tids = [] + for tid in xrange(len(self.teams)): + for player_stat in self.teams[tid]: + if player_stat.winstats.ff & kfwsWinner: + winner_tids.append(tid) + break + return winner_tids + + def get_winner_count(self): + count = 0 + for value in self.pid_win_result_map.values(): + if value == PLAYER_RESULT_WIN: + count += 1 + return count + + def get_first_winner_color(self): + for pid in self.pid_win_result_map.keys(): + if self.pid_win_result_map[pid] == PLAYER_RESULT_WIN: + return self.pid_player_stat_map[pid].get_side_color() + return 'black' + + def find_tid(self, pid): + for tid in xrange(len(self.teams)): + for player_stat in self.teams[tid]: + if player_stat.pid == pid: + return tid + return -1 + + def get_duration_minutes(self): + return '%.2f' % ((self.end_utc - self.start_utc) / 60.0) + + def get_duration_seconds(self): + return self.end_utc - self.start_utc + + def get_game_speed_multiplier(self): + n = 0 + if self.game_speed != 0: + n = (8 * 100) / self.game_speed + return '%d.%dx' % (n / 100, n % 100) + + def get_teams(self): + # Return a list of player_stat tuples, each tuple representing a team. + # Most of the time these tuples will have one player in them each, + # but if there are allies, there may be more. Only check for ally + # equality, to prune out enemies being on the same team. + + # Calc unique masks. + unique_masks = [] + for player_stat in self.player_stats: + ally_mask = player_stat.winstats.side_mask_allies + if not unique_masks.__contains__(ally_mask): + unique_masks.append(ally_mask) + + # Calc team tuples (of player_stats) + teams = [] + for mask in unique_masks: + team = [] + for player_stat in self.player_stats: + ally_mask = player_stat.winstats.side_mask_allies + if ally_mask == mask: + team.append(player_stat) + teams.append(tuple(team)) + return teams + + def get_has_multiplayer_teams(self): + return len(self.teams) != len(self.player_stats) + + def get_game_key(self): + return models.gamestatsmodel_key(self.server_id, self.server_start, + self.gameid) + + def get_game_key_name(self): + return self.get_game_key().name() + + def get_detail_url(self): + url = '%s?g=%s' % (config.GAMEDETAIL_URL, self.get_game_key_name()) + if self.viewing_player_name: + url = '%s&p=%s' % (url, self.viewing_player_name) + return url + + def get_team_ratings(self, win_shares): + # Players with usernames have default ratings. Anonymous users + # have a lower rating, so users prefer to play real users. + # Computer players have an even lower rating. + # Team rating is a weighted total based on kill contribution + r = {} + for tid in xrange(len(self.teams)): + team_rating = 0 + for player_stat in self.teams[tid]: + # Win share for this player. If not present, + # this player had no kill contribution + win_share = win_shares[player_stat.pid] + + # Add up weighted rating + if player_stat.is_computer: + team_rating += COMPUTER_DEFAULT_RATING * win_share + continue + if player_stat.is_anonymous: + team_rating += ANONYMOUS_DEFAULT_RATING * win_share + continue + rating = player_stat.player.rating + if rating == 0: + team_rating += PLAYER_DEFAULT_RATING * win_share + else: + team_rating += rating * win_share + r[tid] = team_rating + # logging.info('+++ team: %d rating: %d' % (tid, team_rating)) + return r + + def calc_win_share(self, team, pid, winner_team): + # A player's win share on a team is the % of units killed by that + # player. + stat_map = {} + total_killed = 0 + for player_stat in team: + total_killed += player_stat.winstats.enemy_munts_killed + total_killed += player_stat.winstats.enemy_structs_killed + stat_map[player_stat.pid] = player_stat + pid_killed = 0 + pid_killed += stat_map[pid].winstats.enemy_munts_killed + pid_killed += stat_map[pid].winstats.enemy_structs_killed + if total_killed != 0: + return float(pid_killed) / float(total_killed) + + return 1.0 / len(team) + + def get_ratings(self): + # Get winner team ids + winner_tids = self.get_winner_tids() + + # Calc win shares for each pid + win_shares = {} + for tid in xrange(len(self.teams)): + for player_stat in self.teams[tid]: + win_shares[player_stat.pid] = self.calc_win_share(self.teams[tid], player_stat.pid, winner_tids.__contains__(tid)) + + # logging + for pid in win_shares.keys(): + name = self.pid_player_stat_map[pid].name + # logging.info('+++ win_share %s: %f' % (name, win_shares[pid])) + + # Get team ratings + r = self.get_team_ratings(win_shares) + + # Estimate win percentages [0..1] for each team, which indicates a + # percentage likelihood of a win. Estimate across all teams (this is + # a bit different than standard ELO which is 2 player only). See: + # http://en.wikipedia.org/wiki/Elo_rating_system#Mathematical_details + q = {} + qt = 0.0 + for tid in r.keys(): + q[tid] = 10**(float(r[tid])/400) + qt += q[tid] + e = {} + for tid in r.keys(): + e[tid] = q[tid] / qt + + # Calc actual win percentages [0..1] for each team. + s = {} + for tid in r.keys(): + if winner_tids.__contains__(tid): + s[tid] = 1.0 + else: + s[tid] = 0.0 + + # Calc per-team actual minus expected rating deltas [0..1] + d = {} + for tid in r.keys(): + d[tid] = s[tid] - e[tid] + # logging.info('+++ actual - expected tid: %d = %f' % (tid, d[tid])) + + # Now calculate per-player rating percentages based on percentage + # contribution to kills for that team. + ratings = [] + for key in self.key_pid_map.keys(): + pid = self.key_pid_map[key] + tid = self.find_tid(pid) + rating = (key, d[tid] * win_shares[pid], winner_tids.__contains__(tid)) + ratings.append(rating) + return ratings + + def lookup_dids(self): + dids = {} + for player_stat in self.player_stats: + if 'did' in player_stat.__dict__ and len(player_stat.did) != 0: + dids[player_stat.name] = player_stat.did + continue + dids[player_stat.name] = '' + return dids + + def save_actions(self, dids): + # For every legit player, add a player action + for player_stat in self.player_stats: + if player_stat.winstats.ff & kfwsComputer: + continue + player_name = player_stat.name + anonymous = (player_stat.winstats.ff & kfwsAnonymous) != 0 + did = dids[player_name] if player_name in dids else '' + ip = player_stat.ip if 'ip' in player_stat.__dict__ else '' + action = dict(action='game', key=self.get_game_key_name()) + utc = self.end_utc + playerdetail.save(player_name, anonymous, did, ip, action, utc) + + def save(self, update_player_stats=True, lookup_dids=True, save_actions=True): + try: + # Lookup dids if asked + dids = self.lookup_dids() if lookup_dids else {} + + # If not saved, it's already in the db. + if not db.run_in_transaction(self.add_txn, dids): + return False + if update_player_stats: + self.update_player_stats() + if save_actions: + self.save_actions(dids) + return True + except: + # Save game in a special place for later analysis + # This should be rare but it'll help for chasing down bugs. + obj = models.GameStatsFailed() + obj.json = self.json + io = StringIO.StringIO() + traceback.print_exc(file=io) + io.seek(0) + obj.backtrace = io.read() + io.close() + obj.put() + raise + + def update_player_stats(self): + if self.get_game_result() != GAME_RESULT_SCOREABLE: + return + for player_key, rating_percent, winner in self.get_ratings(): + db.run_in_transaction(self.update_player_txn, player_key, + rating_percent, winner) + + def update_player_txn(self, player_key, rating_percent, winner): + p = models.PlayerModel.get(player_key) + + # Update rating + rating = p.rating + if rating == 0: + rating = PLAYER_DEFAULT_RATING + k = 32 + if rating >= 2000: + k = 20 + if rating >= 2400: + k = 10 + p.rating = int(round(rating + k * rating_percent)) + + # Winners get "match point" + if winner: + p.rating += 1 + + # Update misc totals + pid = self.key_pid_map[player_key] + player_stat = self.pid_player_stat_map[pid] + p.credits_acquired_total += player_stat.winstats.credits_acquired + p.credits_consumed_total += player_stat.winstats.credits_consumed + p.munts_killed_total += player_stat.winstats.enemy_munts_killed + p.structs_killed_total += player_stat.winstats.enemy_structs_killed + p.munts_lost_total += player_stat.winstats.munts_lost + p.structs_lost_total += player_stat.winstats.structs_lost + + # Update built unit counts. Adding units to this will be a pain. + for index in xrange(len(player_stat.winstats.built_counts)): + count = int(player_stat.winstats.built_counts[index]) + p.built_counts_total[index] += count + + # Update game count, last game, and elapsed time + p.game_count += 1 + if winner: + p.games_won += 1 + + p.last_game_key_name = self.get_game_key_name() + p.elapsed_seconds_total += self.get_duration_seconds() + if self.get_game_result() == GAME_RESULT_SCOREABLE: + p.last_game_utc = self.end_utc + p.rating_check_utc = ratingjob.next_rating_check_utc(self.end_utc) + p.put() + + def add_txn(self, dids): + game_key = self.get_game_key() + if models.GameStatsModel.get(game_key): + return False + + # Collect key names in game + key_names = [key.name() for key in self.key_pid_map.keys()] + + # Collect old ratings of these players, for debugging purposes + old_ratings = {} + for key in self.key_pid_map.keys(): + pid = self.key_pid_map[key] + player_stat = self.pid_player_stat_map[pid] + old_ratings[key.name()] = player_stat.player.rating + + obj = models.GameStatsModel(key_name=game_key.name(), + player_key_names = key_names, + mission_title = self.title, + start_utc = self.start_utc, + end_utc = self.end_utc, + old_ratings = json.dumps(old_ratings), + dids = json.dumps(dids), + json = self.json) + obj.put() + return True + +def load_from_key_name(key_name, viewing_player_name, load_players=False): + key = models.gamestatsmodel_key_from_name(key_name) + obj = models.GameStatsModel.get(key) + if obj: + return GameStats(obj.json, viewing_player_name, + load_players=load_players), obj + return None, None diff --git a/stats/getgames.py b/stats/getgames.py new file mode 100644 index 0000000..9d4fc35 --- /dev/null +++ b/stats/getgames.py @@ -0,0 +1,38 @@ +import os +import datetime +import models +import config +import json + +from google.appengine.ext import webapp +from google.appengine.ext import db + +class GetGames(webapp.RequestHandler): + def get(self): + last_end_utc_str = self.request.get('end_utc') + if last_end_utc_str: + last_end_utc = int(last_end_utc_str) + else: + last_end_utc = 0 + + count_str = self.request.get('count') + if count_str: + count = int(count_str) + else: + count = 1000 + + q = db.GqlQuery('SELECT * FROM GameStatsModel WHERE end_utc >= :1 ORDER BY end_utc', last_end_utc) + results = q.fetch(count) + + self.response.headers['Content-Type'] = 'text/plain' + + filtered_list = [] + for o in results: + if o.old_ratings: + ratings_json = json.loads(o.old_ratings) + else: + ratings_json = {} + stats_json = json.loads(o.json) + filtered_list.append([ratings_json, stats_json]) + + self.response.out.write(json.dumps(filtered_list)) diff --git a/stats/hideplayer.py b/stats/hideplayer.py new file mode 100644 index 0000000..50a69ea --- /dev/null +++ b/stats/hideplayer.py @@ -0,0 +1,78 @@ +import os +import config +import basehandler +import models +import accounts +import admin + +from google.appengine.api import users +from google.appengine.ext import webapp +from google.appengine.ext.webapp import template +from google.appengine.ext import db + +class HidePlayer(basehandler.BaseHandler): + def get(self): + account = self.has_access() + if not account: + self.redirect(users.create_login_url(self.request.uri)) + return + player_name = self.request.get('p').lower() + + template_values = { + 'tabs': config.get_tabs(player_name, account), + 'selected_tab': config.TAB_NONE, + } + + self.response.headers['Content-Type'] = 'application/xhtml+xml' + path = os.path.join(os.path.dirname(__file__), 'hideplayer.xhtml') + self.response.out.write(template.render(path, template_values)) + + def post(self): + account = self.has_access() + if not account: + self.redirect(users.create_logout_url(config.ADMIN_URL)) + return + + self.response.headers['Content-Type'] = 'text/plain' + player_name = self.request.get('u') + if not player_name: + self.response.out.write('no player name entered') + return + + choice = self.request.get('choice') + if not choice: + self.response.out.write('no choice selected') + return + + reason = self.request.get('r') + if not reason: + self.response.out.write('no reason specified') + return + + p = models.PlayerModel.get(models.playermodel_key(player_name)) + if not p: + self.response.out.write('cannot find player in database') + return + + if choice == 'hide': + p.hidden = True + elif choice == 'show': + p.hidden = False + else: + self.response.out.write('choice not hide or show') + return + p.put() + + # Record this action + d = dict(action='hide_player' if choice == 'hide' else 'show_player', player_name=player_name, reason=reason) + admin.save_action(account.name, self.request.remote_addr, d) + + # Return the response + self.response.out.write('success. %s is now hidden=%s, reason: %s.' % (player_name, choice == 'hide', reason)) + + def has_access(self): + # Requires an authenticated user with proper access rights + account = accounts.account() + if account and account.HIDE_PLAYER_ACCESS_RIGHT: + return account + return None diff --git a/stats/hideplayer.xhtml b/stats/hideplayer.xhtml new file mode 100644 index 0000000..c55d554 --- /dev/null +++ b/stats/hideplayer.xhtml @@ -0,0 +1,39 @@ +{% extends "basepage.xhtml" %} +{% block html_title %}Hide Player{% endblock %} + +{% block head_css %} +.sbar { padding:20px 0 20px 0; margin:0; text-align:center;} +.searchquery { font-size:13px; margin-right:10px; padding: 4px 3px; vertical-align:middle; width:190px;} +a.gpbutton { display:inline; margin: 0 auto; line-height:33px; font-size:13.5px; font-weight:bold; color:#fff; text-shadow:0px -1px 1px #666; padding:5px 8px; background:#818189; text-decoration: none; -webkit-border-radius:5px; -moz-border-radius:5px; white-space:nowrap; } +.vmiddle { vertical-align:middle;} +.message_p {text-align:center;font-size:14px;font-weight:bold;} +{% endblock %} + +{% block page_content %} +

HIDE PLAYER

+
+

Hide or show a player from the leaderboard. Enter player name:

+
+
+ +
+
+
+ Hide + Show +
+
+

Reason:

+ +
+ Submit +
+
+
+ +{% endblock %} diff --git a/stats/images/computer_avatar.png b/stats/images/computer_avatar.png new file mode 100644 index 0000000..ca4af15 Binary files /dev/null and b/stats/images/computer_avatar.png differ diff --git a/stats/images/default_avatar.jpg b/stats/images/default_avatar.jpg new file mode 100644 index 0000000..7ee4b80 Binary files /dev/null and b/stats/images/default_avatar.jpg differ diff --git a/stats/images/list-arrow.gif b/stats/images/list-arrow.gif new file mode 100644 index 0000000..6ed200d Binary files /dev/null and b/stats/images/list-arrow.gif differ diff --git a/stats/images/star.png b/stats/images/star.png new file mode 100644 index 0000000..b642dbf Binary files /dev/null and b/stats/images/star.png differ diff --git a/stats/images/star_small.png b/stats/images/star_small.png new file mode 100644 index 0000000..3276d14 Binary files /dev/null and b/stats/images/star_small.png differ diff --git a/stats/images/under_construction.png b/stats/images/under_construction.png new file mode 100644 index 0000000..9d7ceb0 Binary files /dev/null and b/stats/images/under_construction.png differ diff --git a/stats/images/winner.jpg b/stats/images/winner.jpg new file mode 100644 index 0000000..7f82295 Binary files /dev/null and b/stats/images/winner.jpg differ diff --git a/stats/index.yaml b/stats/index.yaml new file mode 100644 index 0000000..79596f8 --- /dev/null +++ b/stats/index.yaml @@ -0,0 +1,65 @@ +indexes: + +# AUTOGENERATED + +# This index.yaml is automatically updated whenever the dev_appserver +# detects that a new type of query is run. If you want to manage the +# index.yaml file manually, remove the above marker line (the line +# saying "# AUTOGENERATED"). If you want to manage some indexes +# manually, move them above the marker line. The index.yaml file is +# automatically uploaded to the admin console when you next deploy +# your application using appcfg.py. + +- kind: GameStatsModel + properties: + - name: player_key_names + - name: date + direction: desc + +- kind: PlayerActionModel + properties: + - name: did + - name: time_utc + direction: desc + +- kind: PlayerActionModel + properties: + - name: ip_address + - name: time_utc + direction: desc + +- kind: PlayerActionModel + properties: + - name: ip_address= + - name: time_utc + direction: desc + +- kind: PlayerActionModel + properties: + - name: player_name + - name: time_utc + direction: desc + +- kind: PlayerActionModel + properties: + - name: player_name= + - name: time_utc + direction: desc + +- kind: PlayerModel + properties: + - name: blocked + - name: hidden + - name: rating + direction: desc + +- kind: PlayerModel + properties: + - name: blocked + - name: rating + +- kind: PlayerModel + properties: + - name: blocked + - name: rating + direction: desc diff --git a/stats/leaderboard.py b/stats/leaderboard.py new file mode 100644 index 0000000..09ab115 --- /dev/null +++ b/stats/leaderboard.py @@ -0,0 +1,107 @@ +import os +import cgi +import models +import urllib +import config +import player +import basehandler +import playerdetail + +from google.appengine.ext.webapp import template + +SHOW_COUNT_MAX = 100 +SHOW_COUNT_INCREMENT = 25 + +class Leaderboard(basehandler.BaseHandler): + def get(self): + # Save this action + self.save_action() + + # Get player name, if any + player_name = self.request.get('p') + + # Get the row count and validate + count_str = self.request.get('count') + if count_str == '': + count = 25 + else: + count = int(count_str) + if count < 0: + count = 0 + if count > SHOW_COUNT_MAX: + count = SHOW_COUNT_MAX + + # Get row data + + q = models.PlayerModel.all() + q.filter('blocked =', False) + q.filter('hidden =', False) + q.order('-rating') + results = q.fetch(count) + players = [player.Player(p, player_name) for p in results] + + # Get game data with one call + game_keys = [] + for p in players: + if p.last_game_key_name: + key_name = p.last_game_key_name + key = models.gamestatsmodel_key_from_name(key_name) + game_keys.append(key) + game_objs = models.GameStatsModel.get(game_keys) + game_map = {} + for game_obj in game_objs: + game_map[game_obj.key().name()] = game_obj + + rows = [] + for index in xrange(len(players)): + p = players[index] + if p.rating == 0: + break + row = {} + row['profile_url'] = p.get_player_stats_url() + row['rank'] = index + 1 + row['name'] = p.name + row['avatar_url'] = p.get_avatar_url() + row['game_count'] = p.game_count + if game_map.has_key(p.last_game_key_name): + title = game_map[p.last_game_key_name].mission_title + row['last_mission_name'] = title + else: + row['last_mission_name'] = None + row['player_rating'] = p.rating + rows.append(row) + + # Figure out the next show url, if any (Show top N) + show_count = count + SHOW_COUNT_INCREMENT + if show_count > SHOW_COUNT_MAX: + show_count = SHOW_COUNT_MAX + show_url = '' + if len(rows) == count and show_count > count: + show_url = '%s?count=%d' % (config.LEADERBOARD_URL, show_count) + if player_name != '': + show_url = '%s&p=%s' % (show_url, player_name) + + # Render the template and serve the response + template_values = { + 'tabs': config.get_tabs(player_name), + 'selected_tab': config.TAB_LEADERBOARD, + 'rows': rows, + 'chevron_image_url': config.CHEVRON_IMAGE_URL, + 'show_count' : show_count, + 'show_url' : show_url, + } + + self.set_caching_headers(config.INFOPAGE_MAX_AGE_SECONDS) + self.response.headers['Content-Type'] = 'application/xhtml+xml' + path = os.path.join(os.path.dirname(__file__), 'leaderboard.xhtml') + self.response.out.write(template.render(path, template_values)) + + def save_action(self): + try: + p = self.request.get('p') + anon = False if p else True + did = self.request.get('d') + ip = self.request.remote_addr + playerdetail.save(p, anon, did, ip, dict(action='leaderboard')) + except: + pass diff --git a/stats/leaderboard.xhtml b/stats/leaderboard.xhtml new file mode 100644 index 0000000..f06ff9e --- /dev/null +++ b/stats/leaderboard.xhtml @@ -0,0 +1,54 @@ +{% extends "basepage.xhtml" %} + +{% block head_css %} +.cllist {width:100%;border-collapse:collapse;border-spacing:0px;-webkit-border-vertical-spacing:0px;} +.cltitle {color:black;font-size:20px;font-weight:bold;} +.linkblock {text-decoration:none;display:block;} +.smallgray {color:#666;font-size:12px;font-weight:bold;} +.td_avatar {width:64px;height:64px;} +.img_avatar {border:none;vertical-align:bottom;} +.clplayer {padding:12px;width:283px;} +.clrating_label {color:#666;font-size:12px;font-weight:bold;width:35px;} +.clrating {color:black;font-size:20px;font-weight:bold;width:55px;} +.clchevron {font-size:20px;font-weight:bold;width:15px;} +.clshowrow {width:100%;height:40px;} +.clshow {color:black;font-size:14px;font-weight:bold;} +.arrow {border:none;} +{% endblock %} + +{% block html_title %}Hostile Takeover Leaderboard{% endblock %} + +{% block page_content %} +

LEADERBOARD

+ + + {% for row in rows %} + + + + + + + + + + {% endfor %} + {% ifnotequal show_url "" %} + + + + + + {% endifnotequal %} +
+ + + {{ row.rank }}. {{ row.name|escape }}
+ games: {{ row.game_count }}, last: {{ row.last_mission_name|escape }} +
rating{{ row.player_rating }} + +
+ + Show Top {{ show_count }} Players +
+{% endblock %} diff --git a/stats/main.py b/stats/main.py new file mode 100644 index 0000000..47336db --- /dev/null +++ b/stats/main.py @@ -0,0 +1,65 @@ +import addgamestats +import auth +import leaderboard +import avatar +import config +import games +import stats +import search +import gamedetail +import message +import admin +import createaccount +import updateaccount +import syncerror +import serverinfo +import sendchat +import drain +import sendcommand +import ratingjob +import about +import blockplayer +import resetplayer +import getgames +import playerdetail +import adjustscore +import adminlog +import hideplayer + +from google.appengine.ext import webapp +from google.appengine.ext.webapp.util import run_wsgi_app + +def main(): + handlers = [ + (config.ADDGAMESTATS_URL, addgamestats.AddGameStats), + (config.AUTH_URL, auth.AuthUser), + (config.LEADERBOARD_URL, leaderboard.Leaderboard), + (config.AVATAR_URL, avatar.AvatarHandler), + (config.STATS_URL, stats.Stats), + (config.GAMES_URL, games.Games), + (config.SEARCH_URL, search.Search), + (config.ABOUT_URL, about.About), + (config.GAMEDETAIL_URL, gamedetail.GameDetail), + (config.MESSAGE_URL, message.Message), + (config.CREATEACCOUNT_URL, createaccount.CreateAccount), + (config.UPDATEACCOUNT_URL, updateaccount.UpdateAccount), + (config.SERVERINFO_URL, serverinfo.ServerInfo), + (config.SYNCERROR_URL, syncerror.SyncError), + (config.ADMIN_URL, admin.Admin), + (config.SENDCHAT_URL, sendchat.SendChat), + (config.DRAIN_URL, drain.Drain), + (config.SENDCOMMAND_URL, sendcommand.SendCommand), + (config.BLOCKPLAYER_URL, blockplayer.BlockPlayer), + (config.HIDEPLAYER_URL, hideplayer.HidePlayer), + (config.RESETPLAYER_URL, resetplayer.ResetPlayer), + (config.RATINGJOB_URL, ratingjob.RatingJob), + (config.GETGAMES_URL, getgames.GetGames), + (config.PLAYERDETAIL_URL, playerdetail.PlayerDetail), + (config.ADJUSTSCORE_URL, adjustscore.AdjustScore), + (config.ADMINLOG_URL, adminlog.AdminLog), + ] + application = webapp.WSGIApplication(handlers, debug=config.is_debug()) + run_wsgi_app(application) + +if __name__ == "__main__": + main() diff --git a/stats/message.py b/stats/message.py new file mode 100644 index 0000000..f238594 --- /dev/null +++ b/stats/message.py @@ -0,0 +1,36 @@ +import os +import config +import urllib + +from google.appengine.ext import webapp +from google.appengine.ext.webapp import template +from google.appengine.ext import db + +GAME_NOT_FOUND = 0 +PLAYER_NOT_FOUND = 1 +PLAYER_NOT_PLAYED_GAME = 2 + +class Message(webapp.RequestHandler): + def get(self): + # Get player name, if any + player_name = self.request.get('p') + # Get message id, if any + id = int(self.request.get('id')) + + # Render the template and serve the response + template_values = { + 'tabs': config.get_tabs(player_name), + 'selected_tab': config.TAB_NONE, + 'id': id, + } + + self.response.headers['Content-Type'] = 'application/xhtml+xml' + path = os.path.join(os.path.dirname(__file__), 'message.xhtml') + self.response.out.write(template.render(path, template_values)) + +def show(handler, id): + q = 'id=%d' % id + viewing_player_name = handler.request.get('p') + if viewing_player_name: + q = '%s&p=%s' % (q, urllib.quote_plus(viewing_player_name)) + handler.redirect('%s?%s' % (config.MESSAGE_URL, q)) diff --git a/stats/message.xhtml b/stats/message.xhtml new file mode 100644 index 0000000..fbda568 --- /dev/null +++ b/stats/message.xhtml @@ -0,0 +1,17 @@ +{% extends "basepage.xhtml" %} +{% block html_title %}Hostile Takeover Message{% endblock %} +{% block page_content %} +

MESSAGE

+
+
+{% ifequal id 0 %} +Game not found. +{% endifequal %} +{% ifequal id 1 %} +Player not found. +{% endifequal %} +{% ifequal id 2 %} +Player hasn't played a game yet. +{% endifequal %} +
+{% endblock %} diff --git a/stats/models.py b/stats/models.py new file mode 100644 index 0000000..ec00b52 --- /dev/null +++ b/stats/models.py @@ -0,0 +1,116 @@ +import config + +from google.appengine.ext import db + +# +++ + +class PlayerModel(db.Model): + name = db.StringProperty() + password = db.StringProperty() + rating = db.IntegerProperty(default = 0) + game_count = db.IntegerProperty(default = 0) + games_won = db.IntegerProperty(default = 0) + avatar_hash = db.StringProperty() + last_game_key_name = db.StringProperty() + credits_acquired_total = db.IntegerProperty(default = 0) + credits_consumed_total = db.IntegerProperty(default = 0) + munts_killed_total = db.IntegerProperty(default = 0) + structs_killed_total = db.IntegerProperty(default = 0) + munts_lost_total = db.IntegerProperty(default = 0) + structs_lost_total = db.IntegerProperty(default = 0) + built_counts_total = db.ListProperty(int, default = [0] * config.BUILT_COUNTS_MAX) + elapsed_seconds_total = db.IntegerProperty(default = 0) + rating_check_utc = db.IntegerProperty(default = 0) + last_game_utc = db.IntegerProperty(default = 0) + blocked = db.BooleanProperty(default = False) + hidden = db.BooleanProperty(default = False) + history = db.TextProperty(default = '[]') + sequence_number = db.IntegerProperty(default = 0) + +def playermodel_key_from_key_name(key_name): + return db.Key.from_path("PlayerModel", key_name) + +def playermodel_key(name): + return db.Key.from_path("PlayerModel", 'k' + name.lower()) + +# +++ + +class PlayerUpdateFailed(db.Model): + name = db.StringProperty() + password = db.StringProperty() + avatar_hash = db.StringProperty() + date = db.DateTimeProperty(auto_now_add=True) + backtrace = db.TextProperty() + +# +++ + +class AvatarModel(db.Model): + hash = db.StringProperty() + content = db.BlobProperty() + +def avatarmodel_key(hash): + return db.Key.from_path("AvatarModel", 'k' + hash.lower()) + +# +++ + +class GameStatsModel(db.Model): + date = db.DateTimeProperty(auto_now_add=True) + player_key_names = db.StringListProperty() + mission_title = db.StringProperty() + start_utc = db.IntegerProperty() + end_utc = db.IntegerProperty() + old_ratings = db.TextProperty() + dids = db.TextProperty() + json = db.TextProperty() + +def gamestatsmodel_key_from_name(key_name): + return db.Key.from_path("GameStatsModel", key_name) + +def gamestatsmodel_key(server_id, server_start, gameid): + key_name = 'k%02x-%08x-%08x' % (server_id, server_start, gameid) + return gamestatsmodel_key_from_name(key_name) + +# +++ + +class GameStatsFailed(db.Model): + json = db.TextProperty() + date = db.DateTimeProperty(auto_now_add=True) + backtrace = db.TextProperty() + +# +++ + +class ServerInfoModel(db.Model): + json = db.TextProperty() + command = db.TextProperty() + expires_utc = db.IntegerProperty() + +def serverinfomodel_key(name, start_utc): + key_name = 'k%s-%u' % (name.lower(), start_utc) + return db.Key.from_path('ServerInfoModel', key_name) + +# +++ + +class AccountModel(db.Model): + nickname = db.StringProperty() + access_rights = db.IntegerProperty() + +def accountmodel_key(user_id): + return db.Key.from_path("AccountModel", 'k' + user_id.lower()) + +# +++ + +class PlayerActionModel(db.Model): + player_name = db.StringProperty() + anonymous = db.BooleanProperty() + did = db.StringProperty() + ip_address = db.StringProperty() + action = db.StringProperty() + time_utc = db.IntegerProperty() + +# +++ + +class AdminActionModel(db.Model): + admin_user = db.StringProperty() + ip_address = db.StringProperty() + action = db.TextProperty() + time_utc = db.IntegerProperty() diff --git a/stats/player.py b/stats/player.py new file mode 100644 index 0000000..ee635c3 --- /dev/null +++ b/stats/player.py @@ -0,0 +1,104 @@ +import wrap +import config +import models + +class Player(wrap.ObjWrap): + def __init__(self, player_obj, viewing_player_name=''): + super(Player, self).__init__(player_obj) + self.viewing_player_name = viewing_player_name + self.units_built_per_game = [self.per_game(x) for x in self.built_counts_total] + + def get_avatar_url(self): + if self.avatar_hash: + return '%s?s=64&h=%s' % (config.AVATAR_URL, self.avatar_hash) + else: + return config.ANONYMOUS_AVATAR_URL + + def is_viewer_also_player(self): + key1 = models.playermodel_key(self.name) + key2 = models.playermodel_key(self.viewing_player_name) + return key1.name() == key2.name() + + def get_player_formatted_url(self, base_url): + if self.is_viewer_also_player(): + return '%s?p=%s' % (base_url, self.viewing_player_name) + url = '%s?u=%s' % (base_url, self.name) + if self.viewing_player_name: + url = '%s&p=%s' % (url, self.viewing_player_name) + return url + + def get_player_stats_url(self): + return self.get_player_formatted_url(config.STATS_URL) + + def get_games_url(self): + return self.get_player_formatted_url(config.GAMES_URL) + + def get_units_built_sum_per_game(self): + return self.per_game(sum(self.built_counts_total)) + + def get_munts_lost_per_game(self): + return self.per_game(self.munts_lost_total) + + def get_structs_lost_per_game(self): + return self.per_game(self.structs_lost_total) + + def get_munts_killed_per_game(self): + return self.per_game(self.munts_killed_total) + + def get_structs_killed_per_game(self): + return self.per_game(self.structs_killed_total) + + def get_credits_acquired_per_game(self): + return self.per_game(self.credits_acquired_total) + + def get_credits_consumed_per_game(self): + return self.per_game(self.credits_consumed_total) + + def get_minutes_per_game(self): + return self.per_game(self.elapsed_seconds_total / 60.0) + + def get_units_built_per_game(self): + return self.units_built_per_game + + def per_game(self, count): + if not self.game_count: + return 0 + value = float(count) / self.game_count + if value >= 10: + return self.thousands(str(int(round(value)))) + if value < 1: + if value == 0: + return '0' + return '%.2f' % value + if value == int(value): + return str(int(value)) + return '%.1f' % value + + def thousands(self, s, sep=','): + if len(s) <= 3: + return s + return self.thousands(s[:-3], sep) + sep + s[-3:] + +def reset_stats(p): + p.rating = 0 + p.game_count = 0 + p.games_won = 0 + p.last_game_key_name = '' + p.credits_acquired_total = 0 + p.credits_consumed_total = 0 + p.munts_killed_total = 0 + p.structs_killed_total = 0 + p.munts_lost_total = 0 + p.structs_lost_total = 0 + p.built_counts_total = [0] * config.BUILT_COUNTS_MAX + p.elapsed_seconds_total = 0 + p.rating_check_utc = 0 + p.last_game_utc = 0 + p.blocked = False + p.hidden = False + +def load_from_name(name, viewing_player_name=''): + p = models.PlayerModel.get(models.playermodel_key(name)) + if p: + return Player(p, viewing_player_name) + return None diff --git a/stats/playerdetail.py b/stats/playerdetail.py new file mode 100644 index 0000000..77f4228 --- /dev/null +++ b/stats/playerdetail.py @@ -0,0 +1,282 @@ +import os +import urllib +import config +import models +import basehandler +import accounts +import time +import re +import json + +from google.appengine.api import users +from google.appengine.ext.webapp import template + +MAX_QUERY_COUNT = 2000 + +class PlayerDetail(basehandler.BaseHandler): + def get(self): + # Requires an authenticated user with proper access rights + account = accounts.account() + if not account or not account.SEE_PLAYER_INFO_ACCESS_RIGHT: + self.redirect(users.create_logout_url(self.request.uri)) + + # Get args + self.get_args() + + # Query for results + keys = [] + if self.user_name: + keys.append('player: %s' % self.user_name) + if self.did: + keys.append('did: %s' % self.did) + if self.ip_address: + keys.append('ip: %s' % self.ip_address) + if len(keys) != 0: + query_string = '%s' % ', '.join(keys) + else: + query_string = 'all' + + # If json request, perform that response + if self.request.get('j'): + self.response.headers['Content-Type'] = 'text/plain' + data = dict(query_string=query_string,tables=self.collect_tables()) + self.response.out.write(json.dumps(data)) + return + + # Calc all-url + if self.player_name: + all_url = '%s?p=%s&c=%d' % (config.PLAYERDETAIL_URL, self.player_name, self.count) + else: + all_url = '%s?c=%d' % (config.PLAYERDETAIL_URL, self.count) + + # Calc count-url + if self.player_name: + if self.q: + count_url = '%s?p=%s&q=%s' % (config.PLAYERDETAIL_URL, self.player_name, self.q) + else: + count_url = '%s?p=%s' % (config.PLAYERDETAIL_URL, self.player_name) + else: + if self.q: + count_url = '%s?q=%s' % (config.PLAYERDETAIL_URL, self.q) + else: + count_url = '%s?x=1' % config.PLAYERDETAIL_URL + + # Otherwise render the template and serve the response + template_values = { + 'tabs': config.get_tabs(self.player_name, account), + 'selected_tab': config.TAB_NONE, + 'query_string': query_string, + 'show_all': query_string != 'all', + 'all_url': all_url, + 'count_url': count_url, + 'tables': self.collect_tables(), + 'player_name': self.player_name, + 'count': self.count, + 'show_counts': [ 50, 100, 250, 500, 1000 ], + } + + self.set_caching_headers(60) + self.response.headers['Content-Type'] = 'application/xhtml+xml' + path = os.path.join(os.path.dirname(__file__), 'playerdetail.xhtml') + self.response.out.write(template.render(path, template_values)) + + def get_args(self): + self.q = self.request.get('q').lower() + self.player_name = self.request.get('p').lower() + self.count = int(self.request.get('c', '50')) + if self.count > MAX_QUERY_COUNT: + self.count = MAX_QUERY_COUNT + + self.user_name = None + self.ip_address = None + self.did = None + + # Is there a query string? If so override other args + if not self.q: + return + + # Is an ip address? + if re.search(r'^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$', self.q): + self.ip_address = self.q + return + + # Is a did? + if len(self.q) == 32 and re.search(r'^[a-f0-9]+$', self.q): + self.did = self.q + return + + # Otherwise treat as a user name + self.user_name = self.q + + def get_results(self): + q = models.PlayerActionModel.all() + if self.user_name: + q.filter('player_name', self.user_name) + if self.did: + q.filter('did', self.did) + if self.ip_address: + q.filter('ip_address', self.ip_address) + q.order('-time_utc') + return q.fetch(self.count) + + def collect_tables(self): + # Perform the query + results = self.get_results() + + # Sort by field values + player_names = {} + dids = {} + ips = {} + for r in results: + if self.user_name: + if r.player_name and not r.anonymous: + if r.player_name in player_names: + player_names[r.player_name].append(r) + else: + player_names[r.player_name] = [r] + + if self.did: + if r.did: + if r.did in dids: + dids[r.did].append(r) + else: + dids[r.did] = [r] + + if self.ip_address: + if r.ip_address: + if r.ip_address in ips: + ips[r.ip_address].append(r) + else: + ips[r.ip_address] = [r] + + # Gather tables + tables = [] + + if self.user_name: + player_table = {} + player_table['title'] = 'Player Usage' + player_table['columns'] = ['Player', 'Last', 'Dids', 'Ips'] + player_table['rows'] = [] + for player_name in sorted(player_names.keys()): + r = player_names[player_name] + row = [] + row.append([(player_name, self.get_url(player_name))]) + row.append([(r[0].time_utc, 'time_utc')]) + row.append(self.get_row_dids(r)) + row.append(self.get_row_ips(r)) + player_table['rows'].append(row) + tables.append(player_table) + + if self.did: + did_table = {} + did_table['title'] = 'Did Usage' + did_table['columns'] = ['Did', 'Last', 'Players', 'Ips'] + did_table['rows'] = [] + for did in sorted(dids.keys()): + r = dids[did] + row = [] + row.append([(did, self.get_url(did))]) + row.append([(r[0].time_utc, 'time_utc')]) + row.append(self.get_row_players(r)) + row.append(self.get_row_ips(r)) + did_table['rows'].append(row) + tables.append(did_table) + + if self.ip_address: + ip_table = {} + ip_table['title'] = 'Ip Usage' + ip_table['columns'] = ['Ip', 'Last', 'Players', 'Dids'] + ip_table['rows'] = [] + for ip in sorted(ips.keys()): + r = ips[ip] + row = [] + row.append([(ip, self.get_url(ip))]) + row.append([(r[0].time_utc, 'time_utc')]) + row.append(self.get_row_players(r)) + row.append(self.get_row_dids(r)) + ip_table['rows'].append(row) + tables.append(ip_table) + + history_table = {} + history_table['title'] = 'History Table' + history_table['columns'] = ['Time', 'Player', 'Did', 'Ip', 'Action'] + history_table['rows'] = [] + for r in results: + row = [] + row.append([(r.time_utc, 'time_utc')]) + row.append([(r.player_name, self.get_url(r.player_name))]) + row.append([(r.did, self.get_url(r.did))]) + row.append([(r.ip_address, self.get_url(r.ip_address))]) + row.append([self.get_action_field(r)]) + history_table['rows'].append(row) + tables.append(history_table) + + return tables + + def get_row_players(self, actions): + players = [] + for r in actions: + if not r.player_name or r.anonymous: + continue + players.append(r.player_name) + results = [] + for player in list(set(players)): + results.append((player, self.get_url(player))) + return results + + def get_row_dids(self, actions): + dids = [] + for r in actions: + if not r.did: + continue + dids.append(r.did) + results = [] + for did in list(set(dids)): + results.append((did, self.get_url(did))) + return results + + def get_row_ips(self, actions): + ips = [] + for r in actions: + if not r.ip_address: + continue + ips.append(r.ip_address) + results = [] + for ip in list(set(ips)): + results.append((ip, self.get_url(ip))) + return results + + def get_url(self, q): + if not q: + return '' + if self.player_name: + return '%s?p=%s&q=%s&c=%d' % (config.PLAYERDETAIL_URL, + self.player_name, urllib.quote_plus(q), + self.count) + else: + return '%s?q=%s&c=%d' % (config.PLAYERDETAIL_URL, + urllib.quote_plus(q), self.count) + + def get_action_field(self, action): + url = '' + a = json.loads(action.action) + if a['action'] == 'game': + if self.player_name: + url = '%s?p=%s&g=%s' % (config.GAMEDETAIL_URL, + self.player_name, a['key']) + else: + url = '%s?g=%s' % (config.GAMEDETAIL_URL, a['key']) + return a['action'], url + +def save(player_name, anonymous, did, ip, action, utc=int(time.time())): + try: + a = models.PlayerActionModel() + a.player_name = player_name.lower() + a.anonymous = anonymous + a.did = did + a.ip_address = ip + a.action = json.dumps(action) + a.time_utc = utc + a.put() + except: + pass diff --git a/stats/playerdetail.xhtml b/stats/playerdetail.xhtml new file mode 100644 index 0000000..95e80d5 --- /dev/null +++ b/stats/playerdetail.xhtml @@ -0,0 +1,154 @@ +{% extends "basepage.xhtml" %} +{% block html_title %}Hostile Takeover Player Detail{% endblock %} + +{% block head_css %} +.sbar { padding:15px 0 15px 0; margin:0; text-align:center;} +.searchquery { font-size:13px; margin-right:10px; padding: 4px 3px; vertical-align:middle; width:190px;} +a.gpbutton { display:inline; margin: 0 auto; line-height:33px; font-size:13.5px; font-weight:bold; color:#fff; text-shadow:0px -1px 1px #666; padding:5px 8px; background:#818189; text-decoration: none; -webkit-border-radius:5px; -moz-border-radius:5px; white-space:nowrap; } +.vmiddle { vertical-align:middle;} +.message_p {text-align:center;font-size:14px;font-weight:bold;} +.stats_table {margin:0 auto;padding:0;} +.stats_table_wide {margin:auto;padding:0;width:90%} +.bottom_border {background-color:#b5bbd5;width:100%;height:1px;margin:0;padding:0} +.stat_name {width:50%;padding:8px;font-weight:bold;} +.stat_value {text-align:center;padding:8px;font-size:11px;} +.round_left {-webkit-border-top-left-radius:10px;-moz-border-radius-topleft:10px} +.round_right {-webkit-border-top-right-radius:10px;-moz-border-radius-topright:10px} +.sechdr {background:url("data:image/gif;base64,R0lGODlhAgAdAMQAAPD2/+71/+30/uvz/ury/ujx/ufw/uTu/uXv/uLt/uHs/d/q/d7p/dzo/dvn/c3e+c7e+szd+cvc+Mrb+Mja+MfZ98bY98XX98TW9sLV9sDU9cHU9r/T9QAAAAAAAAAAACH5BAAHAP8ALAAAAAACAB0AAAUxIAAEgSAMA0EUhWEgyHEkiaIsC8M0jeNAkMcjEpFIJhMKpVKxWC4XDCaT2Ww0Gg4nBAA7");margin:20px 0 0 0;padding:5px;border-top:1px solid #ccc;border-bottom:1px solid #ccc;text-align:center;font-size:16px;font-weight:bold;} +.player_span {color:black;font-size:28px;font-weight:bold;} +.player_msg_span {color:#666;font-size:12px;font-weight:bold;line-height:100%;vertical-align:top;} +.linkblock {text-decoration:none;display:block;} +.arrow {border:none;} +.player_stat_name_value {padding:8px;text-align:left;font-size:16px;font-weight:bold;} +.message {text-align:center;width:80%;margin:0 auto;font-size:14px;font-weight:bold;} +a:link {text-decoration:none;} +{% endblock %} + +{% block page_content %} +

PLAYER DETAIL

+ +
+
Search +{% if player_name %} + +{% endif %} +{% if count %} + +{% endif %} +
+
+ + +
+Showing {{ query_string|escape }} +{% if show_all %} +
+[ Show All ] +{% endif %} +
+ +
+
+Count: +{% for c in show_counts %} + {% ifequal c count %} + [ {{ c }} ] + {% endifequal %} + {% ifnotequal c count %} + [ {{ c }} ] + {% endifnotequal %} +{% endfor %} +
+ +{% for t in tables %} +
+
+ {{ t.title|escape }} +
+ + + + {% for name in t.columns %} + {% if forloop.first %} + + {% else %}{% if forloop.last %} + + {% else %} + + {% endif %}{% endif %} + {% endfor %} + + + {% for row in t.rows %} + + {% for field in row %} + + {% endfor %} + + {% endfor %} + +
{{ name|escape }}
{{ name|escape }}
{{ name|escape }}
+ {% for subfield in field %} + {% if forloop.last %} + {% ifequal subfield.1 "time_utc" %} + + {% endifequal %} + {% ifnotequal subfield.1 "time_utc" %} + {% if not subfield.1 %} + {{ subfield.0|escape }} + {% else %} + {{ subfield.0|escape }} + {% endif %} + {% endifnotequal %} + {% else %} + {% ifequal subfield.1 "time_utc" %} + , + {% endifequal %} + {% ifnotequal subfield.1 "time_utc" %} + {% if not subfield.1 %} + {{ subfield.0|escape }}, + {% else %} + {{ subfield.0|escape }}, + {% endif %} + {% endifnotequal %} + {% endif %} + {% endfor %} +
+{% endfor %} + + + +{% endblock %} + diff --git a/stats/playerstat.py b/stats/playerstat.py new file mode 100644 index 0000000..bea5ed4 --- /dev/null +++ b/stats/playerstat.py @@ -0,0 +1,36 @@ +from wrap import DictWrap +import gamestats +import config +import serverinfo + +mobile_unit_indexes = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 19, 20, 22 ] +struct_unit_indexes = [ 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 21 ] + +class PlayerStat(DictWrap): + def __init__(self, d, win_result): + super(PlayerStat, self).__init__(d) + self.win_result = win_result + self.is_anonymous = (self.winstats.ff & gamestats.kfwsAnonymous) != 0 + self.is_computer = (self.winstats.ff & gamestats.kfwsComputer) != 0 + self.is_user = (not self.is_anonymous and not self.is_computer) + + def get_side(self): + for side in xrange(config.SIDE_COUNT_MAX): + if self.winstats.side_mask & (1 << side): + return side + return 0 + + def get_side_color(self): + # 0: gray, 1: blue, 2: red, 3: yellow, 4: cyan + # These colors match the ones used in the game + colors = [ '#d8d8d8', '#0074e8', '#e82000', '#e8e400', '#68fcfc' ] + return colors[self.side] + + def get_munts_built(self): + return sum([self.winstats.built_counts[i] for i in mobile_unit_indexes]) + + def get_structs_built(self): + return sum([self.winstats.built_counts[i] for i in struct_unit_indexes]) + + def get_units_remaining(self): + return sum(self.winstats.unit_counts) diff --git a/stats/ratingjob.py b/stats/ratingjob.py new file mode 100644 index 0000000..8f26729 --- /dev/null +++ b/stats/ratingjob.py @@ -0,0 +1,46 @@ +import os +import time +import models +import config +import random + +from google.appengine.ext import webapp +from google.appengine.api import users +from google.appengine.ext import db + +# Adjust ratings on this interval +CHECK_INTERVAL = 1 * 60 * 60 * 24 * 7 + +# A player's rating is stale if the last game is this old +STALE_DELTA = 1 * 60 * 60 * 24 * 7 + +# Delta to adjust a stale rating +RATING_DELTA = 20 + +# Ten years +TEN_YEARS = 1 * 60 * 60 * 24 * 365 * 10 + +class RatingJob(webapp.RequestHandler): + def get(self): + now_utc = long(time.time()) + def txn(key): + p = models.PlayerModel.get(key) + if p.rating <= 1500: + p.rating_check_utc = now_utc + random.randint(0, TEN_YEARS) + elif p.last_game_utc > now_utc - STALE_DELTA: + p.rating_check_utc = next_rating_check_utc(p.last_game_utc) + else: + rating = p.rating - RATING_DELTA + if rating < 1500: + rating = 1500 + p.rating = rating + p.rating_check_utc = now_utc + p.put() + + q = db.GqlQuery('SELECT __key__ FROM PlayerModel WHERE rating_check_utc < :1', now_utc - CHECK_INTERVAL) + + for key in q.fetch(100): + db.run_in_transaction(txn, key) + +def next_rating_check_utc(end_utc): + return end_utc + STALE_DELTA - CHECK_INTERVAL diff --git a/stats/resetplayer.py b/stats/resetplayer.py new file mode 100644 index 0000000..2a51abd --- /dev/null +++ b/stats/resetplayer.py @@ -0,0 +1,49 @@ +import os +import config +import player +import models +import basehandler + +from google.appengine.api import users +from google.appengine.ext import webapp +from google.appengine.ext.webapp import template +from google.appengine.ext import db + +class ResetPlayer(basehandler.BaseHandler): + def get(self): + if not users.is_current_user_admin(): + self.redirect(users.create_logout_url(config.ADMIN_URL)) + return + + template_values = { + 'tabs': config.get_tabs(''), + 'selected_tab': config.TAB_NONE, + } + + self.response.headers['Content-Type'] = 'application/xhtml+xml' + path = os.path.join(os.path.dirname(__file__), 'resetplayer.xhtml') + self.response.out.write(template.render(path, template_values)) + + def post(self): + if not users.is_current_user_admin(): + self.redirect(users.create_logout_url(config.ADMIN_URL)) + return + + self.response.headers['Content-Type'] = 'text/plain' + player_name = self.request.get('u') + if not player_name: + self.response.out.write('no player name entered') + return + + def txn(): + p = models.PlayerModel.get(models.playermodel_key(player_name)) + if not p: + return False + player.reset_stats(p) + p.put() + return True + + if not db.run_in_transaction(txn): + self.response.out.write('cannot find player in database') + else: + self.response.out.write('%s\'s stats reset.' % player_name) diff --git a/stats/resetplayer.xhtml b/stats/resetplayer.xhtml new file mode 100644 index 0000000..74d2780 --- /dev/null +++ b/stats/resetplayer.xhtml @@ -0,0 +1,25 @@ +{% extends "basepage.xhtml" %} +{% block html_title %}Reset Player Stats{% endblock %} + +{% block head_css %} +.sbar { padding:20px 0 20px 0; margin:0; text-align:center;} +.searchquery { font-size:13px; margin-right:10px; padding: 4px 3px; vertical-align:middle; width:190px;} +a.gpbutton { display:inline; margin: 0 auto; line-height:33px; font-size:13.5px; font-weight:bold; color:#fff; text-shadow:0px -1px 1px #666; padding:5px 8px; background:#818189; text-decoration: none; -webkit-border-radius:5px; -moz-border-radius:5px; white-space:nowrap; } +.vmiddle { vertical-align:middle;} +.message_p {text-align:center;font-size:14px;font-weight:bold;} +{% endblock %} + +{% block page_content %} +

RESET PLAYER STATS

+
+

Reset a player's stats. Enter player name:

+
+
+
+ +{% endblock %} diff --git a/stats/search.py b/stats/search.py new file mode 100644 index 0000000..512accb --- /dev/null +++ b/stats/search.py @@ -0,0 +1,28 @@ +import os +import config +import basehandler + +from google.appengine.ext import webapp +from google.appengine.ext.webapp import template +from google.appengine.ext import db + +# This page isn't dynamic +MAX_AGE_SECONDS = 60 * 60 * 24 + +class Search(basehandler.BaseHandler): + def get(self): + # Get player name, if any + player_name = self.request.get('p') + + # Render the template and serve the response + template_values = { + 'tabs': config.get_tabs(player_name), + 'selected_tab': config.TAB_SEARCH, + 'player_name': player_name, + 'games_url': config.GAMES_URL, + } + + self.set_caching_headers(MAX_AGE_SECONDS) + self.response.headers['Content-Type'] = 'application/xhtml+xml' + path = os.path.join(os.path.dirname(__file__), 'search.xhtml') + self.response.out.write(template.render(path, template_values)) diff --git a/stats/search.xhtml b/stats/search.xhtml new file mode 100644 index 0000000..41a29f5 --- /dev/null +++ b/stats/search.xhtml @@ -0,0 +1,25 @@ +{% extends "basepage.xhtml" %} +{% block html_title %}Hostile Takeover Game Search{% endblock %} + +{% block head_css %} +.sbar { padding:20px 0 20px 0; margin:0; text-align:center;} +.searchquery { font-size:13px; margin-right:10px; padding: 4px 3px; vertical-align:middle; width:190px;} +a.gpbutton { display:inline; margin: 0 auto; line-height:33px; font-size:13.5px; font-weight:bold; color:#fff; text-shadow:0px -1px 1px #666; padding:5px 8px; background:#818189; text-decoration: none; -webkit-border-radius:5px; -moz-border-radius:5px; white-space:nowrap; } +.vmiddle { vertical-align:middle;} +.message_p {text-align:center;font-size:14px;font-weight:bold;} +{% endblock %} + +{% block page_content %} +

SEARCH

+
+

Search for a player's games. Enter player name:

+
+
+
+ +{% endblock %} diff --git a/stats/sendchat.py b/stats/sendchat.py new file mode 100644 index 0000000..7f85ff2 --- /dev/null +++ b/stats/sendchat.py @@ -0,0 +1,46 @@ +import os +import command +import serverinfo +import accounts + +from google.appengine.ext.webapp import template + +class SendChat(command.Command): + def have_access(self): + account = accounts.account() + if not account or not account.SEND_CHAT_ACCESS_RIGHT: + return False + return True + + def finish_get(self, template_values): + template_values['name'] = 'From Admin' + self.response.headers['Content-Type'] = 'application/xhtml+xml' + path = os.path.join(os.path.dirname(__file__), 'sendchat.xhtml') + self.response.out.write(template.render(path, template_values)) + + def finish_post(self, selected, template_values): + name = self.request.get('name') + message = self.request.get('message') + + errors = [] + if len(selected) == 0: + errors.append('Must select at least one server.') + + if not name or not message: + errors.append('Must have non-empty name and message.') + + if len(errors) == 0: + for info in selected: + command = '{"command": "chat", "name": "%s", "message": "%s"}' % (name, message) + serverinfo.ServerInfo.send_command(info, command) + errors.append('Message "%s: %s" successfully sent to server %s.' % (name, message, info['name'])) + message = '' + + template_values['name'] = name + template_values['message'] = message + template_values['errors'] = errors + + self.response.headers['Content-Type'] = 'application/xhtml+xml' + path = os.path.join(os.path.dirname(__file__), 'sendchat.xhtml') + self.response.out.write(template.render(path, template_values)) + diff --git a/stats/sendchat.xhtml b/stats/sendchat.xhtml new file mode 100644 index 0000000..ff6bce6 --- /dev/null +++ b/stats/sendchat.xhtml @@ -0,0 +1,14 @@ +{% extends "command.xhtml" %} + +{% block html_title %}Send Chat{% endblock %} + +{% block page_content %} +

SEND CHAT

+{{ block.super }} +{% endblock %} + +{% block form_elements %} +

Name:

+

Message:

+ +{% endblock %} diff --git a/stats/sendcommand.py b/stats/sendcommand.py new file mode 100644 index 0000000..3c67bba --- /dev/null +++ b/stats/sendcommand.py @@ -0,0 +1,43 @@ +import os +import models +import config +import time +from hashlib import md5 +import json + +import serverinfo + +from google.appengine.ext import webapp + +""" +{ + "info": { + "name": "", + "start_utc": + }, + "command": { + "command": "", + "": "", + "": "" + // etc + } +} +""" + +class SendCommand(webapp.RequestHandler): + def post(self): + hash = self.request.body[:32] + j = self.request.body[32:] + m = md5(json + config.SENDCOMMAND_SECRET) + if m.hexdigest() == hash: + c = json.loads(j) + serverinfo.ServerInfo.send_command(c['info'], + json.dumps(c['command'])) + if config.is_debug(): + self.response.headers['Content-Type'] = 'text/plain' + self.response.out.write('ok') + else: + if config.is_debug(): + self.response.headers['Content-Type'] = 'text/plain' + self.response.out.write('not ok') + diff --git a/stats/serverinfo.py b/stats/serverinfo.py new file mode 100644 index 0000000..162f344 --- /dev/null +++ b/stats/serverinfo.py @@ -0,0 +1,189 @@ +import os +import models +import config +import time +from hashlib import md5 +import playerdetail +import json + +from google.appengine.runtime.apiproxy_errors import CapabilityDisabledError +from google.appengine.ext import webapp +from google.appengine.api import memcache +from google.appengine.api import urlfetch + +"""cached json format: +{ + "updated_utc": , + "expires_utc": , + "infos": [ + { + "sort_key", , + "name": "", + "location": "", + "address": "", + "protocol": "", + "status": "status", + "expires_utc": , + "start_utc": , + "player_count": , + "type", "beta|production", + } + ] +} +""" + +SERVERINFO_KEY = 'serverinfo_info' + +class ServerInfo(webapp.RequestHandler): + def get(self): + # Get did, check for banned dids + ip = self.request.remote_addr + did = self.request.get('d') + if did and self.is_banned_did(did): + self.save_action(did, ip, True) + return + + # Check for banned ips + if ip and self.is_banned_ip(ip): + self.save_action(did, ip, True) + return + + # Not banned + self.save_action(did, ip, False) + + # Attempt to retrieve serverinfo from cache + j = memcache.get(SERVERINFO_KEY) + if not j: + j = self.gen_json() + + # Return response + self.response.headers['Content-Type'] = 'text/plain' + self.response.out.write(j) + + def post(self): + # Update server info + j = self.request.body[32:] + info = self.validate_request() + if not info: + if config.is_debug(): + self.response.set_status(400, 'not valid') + return + + # Update the model. + key = models.serverinfomodel_key(info['name'], info['start_utc']) + obj = models.ServerInfoModel.get(key) + if not obj: + obj = models.ServerInfoModel(key_name=key.name()) + + # Save the command + command = obj.command + + obj.json = j + obj.command = '' + obj.expires_utc = info['expires_utc'] + obj.put() + + # Update the cache now rather than just invalidating, so the latency + # isn't on a client request + self.gen_json() + + # Return command if there is one + if command: + # Use this mime type for simplicity, and so GAE doesn't strip + # out Content-Length + self.response.headers['Content-Type'] = 'binary/octet-stream' + self.response.headers['Content-Length'] = len(command) + self.response.out.write(command) + + def validate_request(self): + # Validate hash + hash = self.request.body[:32] + j = self.request.body[32:] + m = md5(j + config.SERVERINFO_SECRET) + if m.hexdigest() != hash: + return None + + # Make sure json parses + try: + j = json.loads(j) + except: + return None + + # Make sure it hasn't expired + if j['expires_utc'] <= long(time.time()): + return None + return j + + def gen_json(self): + info = ServerInfo.get_serverinfo() + j = json.dumps(info) + memcache.set(SERVERINFO_KEY, j, info['expires_utc']) + return j + + def save_action(self, did, ip, banned): + try: + p = self.request.get('p') + anon = False if p else True + d = dict(action='serverinfo', banned=banned) + playerdetail.save(p, anon, did, ip, d) + except: + pass + + @staticmethod + def get_serverinfo(): + # Separate keepers and ones to delete + now_utc = long(time.time()) + keep = [] + delete = [] + for obj in models.ServerInfoModel.all(): + if obj.expires_utc < now_utc: + delete.append(obj) + else: + keep.append(obj) + + # Delete the ones to delete + # During app engine maintenance, CapabilityDisabledError + # is thrown for writes. Keep infos through maintenance. + try: + for obj in delete: + obj.delete() + except CapabilityDisabledError: + keep.extend(delete) + + # Collect json of keepers and calc expires + infos = [] + expires_utc = 0 + for obj in keep: + infos.append(json.loads(obj.json)) + if expires_utc == 0: + expires_utc = obj.expires_utc + else: + if obj.expires_utc != 0 and obj.expires_utc < expires_utc: + expires_utc = obj.expires_utc + + # Sort because the client will display in the order received + infos.sort(lambda x, y: cmp(x['sort_key'], y['sort_key'])) + + # Note expires_utc = 0 is the same as memcache forever, otherwise + # memcache takes it as an unix epoc time. Forever is ok, since updating + # a server entity will cause the cache to be updated. + info = {} + info['updated_utc'] = now_utc + info['expires_utc'] = expires_utc + info['infos'] = infos + return info + + @staticmethod + def send_command(info, command): + # Update the model with the command + key = models.serverinfomodel_key(info['name'], info['start_utc']) + obj = models.ServerInfoModel.get(key) + if obj: + obj.command = command + obj.put() + + def is_banned_ip(self, ip): + return False + + def is_banned_did(self, did): + return False diff --git a/stats/stats.py b/stats/stats.py new file mode 100644 index 0000000..6b5b807 --- /dev/null +++ b/stats/stats.py @@ -0,0 +1,54 @@ +import os +import config +import models +import player +import gamestats +import message +import basehandler +import accounts + +from google.appengine.ext.webapp import template + +class Stats(basehandler.BaseHandler): + def get(self): + # player_name is the player that is browsing. The tabs will be + # populated with query args with this player name + player_name = self.request.get('p') + + # stats_player is the player to get stats on. If 'u' isn't set, + # use player_name. + stats_player = self.request.get('u') + if stats_player == '': + stats_player = player_name + + p = player.load_from_name(stats_player, player_name) + if not p: + message.show(self, message.PLAYER_NOT_FOUND) + return + + selected_tab = config.TAB_NONE + if p.is_viewer_also_player(): + selected_tab = config.TAB_STATS + + # get player detail url + detail_url = '' + account = accounts.account() + if account and account.SEE_PLAYER_INFO_ACCESS_RIGHT: + if player_name: + detail_url = '%s?p=%s&q=%s' % (config.PLAYERDETAIL_URL, player_name, stats_player) + else: + detail_url = '%s?q=%s' % (config.PLAYERDETAIL_URL, stats_player) + + # Fill in the template + template_values = { + 'tabs': config.get_tabs(player_name, account), + 'selected_tab': selected_tab, + 'player': p, + 'player_default_rating': gamestats.PLAYER_DEFAULT_RATING, + 'detail_url': detail_url + } + + self.set_caching_headers(config.INFOPAGE_MAX_AGE_SECONDS) + self.response.headers['Content-Type'] = 'application/xhtml+xml' + path = os.path.join(os.path.dirname(__file__), 'stats.xhtml') + self.response.out.write(template.render(path, template_values)) diff --git a/stats/stats.xhtml b/stats/stats.xhtml new file mode 100644 index 0000000..0e0732c --- /dev/null +++ b/stats/stats.xhtml @@ -0,0 +1,247 @@ +{% extends "basepage.xhtml" %} +{% block html_title %}Hostile Takeover Player Statistics{% endblock %} + +{% block head_css %} +.player_table {margin:auto;padding:0;} +.stats_table {margin:auto;padding:0;width:80%} +.stat_name {width:70%;padding:8px;font-weight:bold;} +.stat_value {padding:8px;font-weight:bold;} +.round_left {-webkit-border-top-left-radius:10px;-moz-border-radius-topleft:10px} +.round_right {-webkit-border-top-right-radius:10px;-moz-border-radius-topright:10px} +.sechdr {background:url("data:image/gif;base64,R0lGODlhAgAdAMQAAPD2/+71/+30/uvz/ury/ujx/ufw/uTu/uXv/uLt/uHs/d/q/d7p/dzo/dvn/c3e+c7e+szd+cvc+Mrb+Mja+MfZ98bY98XX98TW9sLV9sDU9cHU9r/T9QAAAAAAAAAAACH5BAAHAP8ALAAAAAACAB0AAAUxIAAEgSAMA0EUhWEgyHEkiaIsC8M0jeNAkMcjEpFIJhMKpVKxWC4XDCaT2Ww0Gg4nBAA7");margin:20px 0 0 0;padding:5px;border-top:1px solid #ccc;border-bottom:1px solid #ccc;text-align:center;font-size:16px;font-weight:bold;} +.recent_games {text-align:center;padding:5px;margin:20px 0 0 0;font-weight:bold;} +.avatar_td {border:none;width:64px;height:64px;} +.avatar_img {border:none;vertical-align:bottom;} +.text_td {padding:8px} +.player_name {color:black;font-size:28px;font-weight:bold;} +.bottom_border {background-color:#b5bbd5;width:100%;height:1px;margin:0;padding:0} +{% endblock %} + +{% block page_content %} +

STATS

+
+ + + + + + + +
+ + + {{ player.name|escape }}
+
+ +

+See {{ player.name }}'s games +{% if detail_url %} + | See detail +{% endif %} +

+ + + + + + + {% ifequal player.rating 0 %} + + {% else %} + + {% endifequal %} + + + + + + + + + + +
Player Stats
Rating{{ player_default_rating|escape }}{{ player.rating|escape }}
Games Scored{{ player.game_count|escape }}
Games Won{{ player.games_won|escape }}
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Misc Stats, Average Per Game
Units Built{{ player.units_built_sum_per_game }}
Mobile Units Lost{{ player.munts_lost_per_game }}
Structures Lost{{ player.structs_lost_per_game }}
Mobile Units Killed{{ player.munts_killed_per_game }}
Structures Killed{{ player.structs_killed_per_game }}
Credits Acquired{{ player.credits_acquired_per_game }}
Credits Consumed{{ player.credits_consumed_per_game }}
Duration{{ player.minutes_per_game }} minutes
+ + + + + {% ifnotequal player.units_built_per_game.0 '0' %} + + + + + {% endifnotequal %} + {% ifnotequal player.units_built_per_game.1 '0' %} + + + + + {% endifnotequal %} + {% ifnotequal player.units_built_per_game.2 '0' %} + + + + + {% endifnotequal %} + {% ifnotequal player.units_built_per_game.19 '0' %} + + + + + {% endifnotequal %} + {% ifnotequal player.units_built_per_game.22 '0' %} + + + + + {% endifnotequal %} + {% ifnotequal player.units_built_per_game.3 '0' %} + + + + + {% endifnotequal %} + {% ifnotequal player.units_built_per_game.4 '0' %} + + + + + {% endifnotequal %} + {% ifnotequal player.units_built_per_game.5 '0' %} + + + + + {% endifnotequal %} + {% ifnotequal player.units_built_per_game.6 '0' %} + + + + + {% endifnotequal %} + {% ifnotequal player.units_built_per_game.7 '0' %} + + + + + {% endifnotequal %} + {% ifnotequal player.units_built_per_game.8 '0' %} + + + + + {% endifnotequal %} + {% ifnotequal player.units_built_per_game.20 '0' %} + + + + + {% endifnotequal %} + {% ifnotequal player.units_built_per_game.9 '0' %} + + + + + {% endifnotequal %} + {% ifnotequal player.units_built_per_game.10 '0' %} + + + + + {% endifnotequal %} + {% ifnotequal player.units_built_per_game.11 '0' %} + + + + + {% endifnotequal %} + {% ifnotequal player.units_built_per_game.12 '0' %} + + + + + {% endifnotequal %} + {% ifnotequal player.units_built_per_game.13 '0' %} + + + + + {% endifnotequal %} + {% ifnotequal player.units_built_per_game.14 '0' %} + + + + + {% endifnotequal %} + {% ifnotequal player.units_built_per_game.15 '0' %} + + + + + {% endifnotequal %} + {% ifnotequal player.units_built_per_game.16 '0' %} + + + + + {% endifnotequal %} + {% ifnotequal player.units_built_per_game.17 '0' %} + + + + + {% endifnotequal %} + {% ifnotequal player.units_built_per_game.18 '0' %} + + + + + {% endifnotequal %} + {% ifnotequal player.units_built_per_game.21 '0' %} + + + + + {% endifnotequal %} + +
Units Built, Average Per Game
Security Guard{{ player.units_built_per_game.0 }}
Rocket Trooper{{ player.units_built_per_game.1 }}
Corporate Raider{{ player.units_built_per_game.2 }}
Andy{{ player.units_built_per_game.19 }}
Fox{{ player.units_built_per_game.22 }}
SR-98 Eagle{{ player.units_built_per_game.3 }}
T-29 Broadsword{{ player.units_built_per_game.4 }}
M-18 Hydra{{ player.units_built_per_game.5 }}
T-33 Liberator{{ player.units_built_per_game.6 }}
G-4 Bullpup{{ player.units_built_per_game.7 }}
H-7 Dominion{{ player.units_built_per_game.8 }}
A-3 Cyclops{{ player.units_built_per_game.20 }}
Power Generator{{ player.units_built_per_game.9 }}
Galaxite Processor{{ player.units_built_per_game.10 }}
Galaxite Warehouse{{ player.units_built_per_game.11 }}
Human Resource Center{{ player.units_built_per_game.12 }}
Vehicle Transport Station{{ player.units_built_per_game.13 }}
Surveillance Center{{ player.units_built_per_game.14 }}
R&D Center{{ player.units_built_per_game.15 }}
Headquarters{{ player.units_built_per_game.16 }}
Gatling Tower{{ player.units_built_per_game.17 }}
Rocket Tower{{ player.units_built_per_game.18 }}
Replicator{{ player.units_built_per_game.21 }}
+ +{% endblock %} diff --git a/stats/syncerror.py b/stats/syncerror.py new file mode 100644 index 0000000..99c8bfe --- /dev/null +++ b/stats/syncerror.py @@ -0,0 +1,8 @@ +import config + +from google.appengine.ext import webapp + +class SyncError(webapp.RequestHandler): + def post(self): + self.response.headers['Content-Type'] = 'text/plain' + self.response.out.write('not implemented') diff --git a/stats/underconstruction.xhtml b/stats/underconstruction.xhtml new file mode 100644 index 0000000..2f395dc --- /dev/null +++ b/stats/underconstruction.xhtml @@ -0,0 +1,6 @@ +{% extends "basepage.xhtml" %} + +{% block page_content %} +
+
+{% endblock %} diff --git a/stats/updateaccount.py b/stats/updateaccount.py new file mode 100644 index 0000000..56a5b16 --- /dev/null +++ b/stats/updateaccount.py @@ -0,0 +1,47 @@ +import os, StringIO, traceback +from hashlib import md5 + +import createaccount, config, avatar, player, models + +from google.appengine.ext import db +from google.appengine.api import images +from google.appengine.ext import webapp +from google.appengine.ext.webapp import template + +class UpdateAccount(createaccount.CreateAccount): + def do_action(self, args, player_name, password, password2, avatar_data): + if not self.is_valid_player_name(player_name): + return createaccount.kErrPlayerNameInvalid + p = models.PlayerModel.get(models.playermodel_key(player_name)) + if not p: + return createaccount.kErrAccountNotFound + if p.password != md5(password).hexdigest().lower(): + return createaccount.kErrIncorrectPassword + if password2 and not self.is_valid_password(password2): + return createaccount.kErrPasswordInvalid + + # If no avatar was offered, don't change the existing one. + avatar_data_new = avatar.prepare_avatar(avatar_data) if avatar_data else None + if avatar_data and not avatar_data_new: + return createaccount.kErrAvatarInvalid + + # Perform transaction + avatar_hash = self.get_avatar_hash(avatar_data_new) + db.run_in_transaction(self.update_player_txn, p, password2, avatar_hash) + if avatar_data_new: + avatar.save_avatar(avatar_hash, avatar_data_new) + + # Set up args for the template + args['player'] = player.Player(p) + args['password'] = password2 if password2 else '' + return createaccount.kErrNone + + def update_player_txn(self, p, password, avatar_hash): + if password: + p.password = md5(password).hexdigest().lower() + if avatar_hash: + p.avatar_hash = avatar_hash + p.put() + + def is_update(self): + return True diff --git a/stats/wrap.py b/stats/wrap.py new file mode 100644 index 0000000..fb216a5 --- /dev/null +++ b/stats/wrap.py @@ -0,0 +1,56 @@ +import copy + +# Get instancemethod type. Is there a faster way? +class Foo(): + def method(self): + pass +type_instancemethod = type(Foo().method) + +# Makes get_ methods accessible as read only properties. o.get_foo() can be +# accessed as o.foo. Useful for computed properties. + +class PropWrap(object): + def __getattr__(self, name): + # require read-only computed properties to start with get_ + try: + v = object.__getattribute__(self, 'get_' + name) + if type(v) == type_instancemethod: + v = v() + return v + except: + raise + +# Makes a dict accessible by property syntax. d[key] can be accessed as +# d.key. Derives from PropWrap, for computed properties. + +class DictWrap(PropWrap): + def __init__(self, d): + if isinstance(d, DictWrap): + d = d.__dict__ + self.__dict__ = copy.deepcopy(d) + for key in self.__dict__.keys(): + if isinstance(self.__dict__[key], dict): + self.__dict__[key] = DictWrap(self.__dict__[key]) + if isinstance(self.__dict__[key], list): + for i in xrange(len(self.__dict__[key])): + if isinstance(self.__dict__[key][i], dict): + self.__dict__[key][i] = DictWrap(self.__dict__[key][i]) + + def __repr__(self): + return self.__dict__.__repr__() + +# Wraps an object, so that properties on that object are accessible via +# this wrapped object, and adds computed properties. Useful for wrapping +# db.Model objects, for example. + +class ObjWrap(PropWrap): + def __init__(self, obj): + self.obj = obj + + def __getattr__(self, name): + try: + return super(ObjWrap, self).__getattr__(name) + except: + pass + return getattr(self.obj, name) + diff --git a/syncerror/README.txt b/syncerror/README.txt new file mode 100644 index 0000000..9215bb0 --- /dev/null +++ b/syncerror/README.txt @@ -0,0 +1,8 @@ +These scripts are for debugging sync errors. Sync errors are caused by one +of the clients going out of sync with the other clients in an multiplayer game. +Since multiplayer is done as a synchronized simulation, all clients must have +exactly the same simulation state. Each client uploads a hash to the server of +the simulation state. For each game step, the server checks the hashes for all +clients and if there is a mismatch, tells the clients and they upload their +state to the sync error url. This state can be parsed with these tools to find +where the state divergence occured. diff --git a/syncerror/find.py b/syncerror/find.py new file mode 100644 index 0000000..7541775 --- /dev/null +++ b/syncerror/find.py @@ -0,0 +1,52 @@ +import glob +import sys +import time +from os.path import join +import sync + +def FindFiles(files, file): + fmt = '%Y.%m.%d-%H.%M.%S' + seconds = time.mktime(time.strptime(file[11:30], fmt)) + group = [] + for f in files: + if not f.startswith(file[:8]): + continue + s = time.mktime(time.strptime(f[11:30], fmt)) + if abs(s - seconds) >= 60*60: + continue + group.append(f) + group.sort() + return group + +if __name__ == '__main__': + dumpdir = sys.argv[1] + outdir = sys.argv[2] + files = glob.glob1(dumpdir, '????????-?-????.??.??-??.??.??.json') + + while len(files) != 0: + file = files[0] + todiff = FindFiles(files, file) + if len(todiff) < 2: + print 'Solo file: %s' % file + files.remove(file) + continue + + # Diff these files against each other + for i in xrange(len(todiff) - 1): + for j in xrange(i + 1, len(todiff)): + print 'Diffing %s with %s' % (todiff[i], todiff[j]) + d = sync.Differ(join(dumpdir, todiff[i]), join(dumpdir, todiff[j])) + badupdate = d.GetBadUpdateNumber() + if badupdate == -1: + continue + print 'Bad Update %d' % badupdate + str = 'update-%d' % badupdate + frame0file = todiff[i][:11] + str + '.json' + frame1file = todiff[j][:11] + str + '.json' + outfile = todiff[i][:11] + todiff[j][9:11] + str + '.diff' + d.Diff(join(outdir, frame0file), join(outdir, frame1file), join(outdir, outfile)) + + # Now remove these files from the list + for file in todiff: + files.remove(file) + diff --git a/syncerror/simplejson/__init__.py b/syncerror/simplejson/__init__.py new file mode 100644 index 0000000..6313254 --- /dev/null +++ b/syncerror/simplejson/__init__.py @@ -0,0 +1,316 @@ +r"""JSON (JavaScript Object Notation) is a subset of +JavaScript syntax (ECMA-262 3rd edition) used as a lightweight data +interchange format. + +:mod:`simplejson` exposes an API familiar to users of the standard library +:mod:`marshal` and :mod:`pickle` modules. It is the externally maintained +version of the :mod:`json` library contained in Python 2.6, but maintains +compatibility with Python 2.4 and Python 2.5 and (currently) has +significant performance advantages, even without using the optional C +extension for speedups. + +Encoding basic Python object hierarchies:: + + >>> import simplejson as json + >>> json.dumps(['foo', {'bar': ('baz', None, 1.0, 2)}]) + '["foo", {"bar": ["baz", null, 1.0, 2]}]' + >>> print json.dumps("\"foo\bar") + "\"foo\bar" + >>> print json.dumps(u'\u1234') + "\u1234" + >>> print json.dumps('\\') + "\\" + >>> print json.dumps({"c": 0, "b": 0, "a": 0}, sort_keys=True) + {"a": 0, "b": 0, "c": 0} + >>> from StringIO import StringIO + >>> io = StringIO() + >>> json.dump(['streaming API'], io) + >>> io.getvalue() + '["streaming API"]' + +Compact encoding:: + + >>> import simplejson as json + >>> json.dumps([1,2,3,{'4': 5, '6': 7}], separators=(',',':')) + '[1,2,3,{"4":5,"6":7}]' + +Pretty printing:: + + >>> import simplejson as json + >>> s = json.dumps({'4': 5, '6': 7}, sort_keys=True, indent=4) + >>> print '\n'.join([l.rstrip() for l in s.splitlines()]) + { + "4": 5, + "6": 7 + } + +Decoding JSON:: + + >>> import simplejson as json + >>> obj = [u'foo', {u'bar': [u'baz', None, 1.0, 2]}] + >>> json.loads('["foo", {"bar":["baz", null, 1.0, 2]}]') == obj + True + >>> json.loads('"\\"foo\\bar"') == u'"foo\x08ar' + True + >>> from StringIO import StringIO + >>> io = StringIO('["streaming API"]') + >>> json.load(io)[0] == 'streaming API' + True + +Specializing JSON object decoding:: + + >>> import simplejson as json + >>> def as_complex(dct): + ... if '__complex__' in dct: + ... return complex(dct['real'], dct['imag']) + ... return dct + ... + >>> json.loads('{"__complex__": true, "real": 1, "imag": 2}', + ... object_hook=as_complex) + (1+2j) + >>> import decimal + >>> json.loads('1.1', parse_float=decimal.Decimal) == decimal.Decimal('1.1') + True + +Specializing JSON object encoding:: + + >>> import simplejson as json + >>> def encode_complex(obj): + ... if isinstance(obj, complex): + ... return [obj.real, obj.imag] + ... raise TypeError("%r is not JSON serializable" % (o,)) + ... + >>> json.dumps(2 + 1j, default=encode_complex) + '[2.0, 1.0]' + >>> json.JSONEncoder(default=encode_complex).encode(2 + 1j) + '[2.0, 1.0]' + >>> ''.join(json.JSONEncoder(default=encode_complex).iterencode(2 + 1j)) + '[2.0, 1.0]' + + +Using simplejson.tool from the shell to validate and pretty-print:: + + $ echo '{"json":"obj"}' | python -msimplejson.tool + { + "json": "obj" + } + $ echo '{ 1.2:3.4}' | python -msimplejson.tool + Expecting property name: line 1 column 2 (char 2) +""" +__version__ = '2.0.7' +__all__ = [ + 'dump', 'dumps', 'load', 'loads', + 'JSONDecoder', 'JSONEncoder', +] + +from decoder import JSONDecoder +from encoder import JSONEncoder + +_default_encoder = JSONEncoder( + skipkeys=False, + ensure_ascii=True, + check_circular=True, + allow_nan=True, + indent=None, + separators=None, + encoding='utf-8', + default=None, +) + +def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True, + allow_nan=True, cls=None, indent=None, separators=None, + encoding='utf-8', default=None, **kw): + """Serialize ``obj`` as a JSON formatted stream to ``fp`` (a + ``.write()``-supporting file-like object). + + If ``skipkeys`` is ``True`` then ``dict`` keys that are not basic types + (``str``, ``unicode``, ``int``, ``long``, ``float``, ``bool``, ``None``) + will be skipped instead of raising a ``TypeError``. + + If ``ensure_ascii`` is ``False``, then the some chunks written to ``fp`` + may be ``unicode`` instances, subject to normal Python ``str`` to + ``unicode`` coercion rules. Unless ``fp.write()`` explicitly + understands ``unicode`` (as in ``codecs.getwriter()``) this is likely + to cause an error. + + If ``check_circular`` is ``False``, then the circular reference check + for container types will be skipped and a circular reference will + result in an ``OverflowError`` (or worse). + + If ``allow_nan`` is ``False``, then it will be a ``ValueError`` to + serialize out of range ``float`` values (``nan``, ``inf``, ``-inf``) + in strict compliance of the JSON specification, instead of using the + JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``). + + If ``indent`` is a non-negative integer, then JSON array elements and object + members will be pretty-printed with that indent level. An indent level + of 0 will only insert newlines. ``None`` is the most compact representation. + + If ``separators`` is an ``(item_separator, dict_separator)`` tuple + then it will be used instead of the default ``(', ', ': ')`` separators. + ``(',', ':')`` is the most compact JSON representation. + + ``encoding`` is the character encoding for str instances, default is UTF-8. + + ``default(obj)`` is a function that should return a serializable version + of obj or raise TypeError. The default simply raises TypeError. + + To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the + ``.default()`` method to serialize additional types), specify it with + the ``cls`` kwarg. + + """ + # cached encoder + if (skipkeys is False and ensure_ascii is True and + check_circular is True and allow_nan is True and + cls is None and indent is None and separators is None and + encoding == 'utf-8' and default is None and not kw): + iterable = _default_encoder.iterencode(obj) + else: + if cls is None: + cls = JSONEncoder + iterable = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii, + check_circular=check_circular, allow_nan=allow_nan, indent=indent, + separators=separators, encoding=encoding, + default=default, **kw).iterencode(obj) + # could accelerate with writelines in some versions of Python, at + # a debuggability cost + for chunk in iterable: + fp.write(chunk) + + +def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True, + allow_nan=True, cls=None, indent=None, separators=None, + encoding='utf-8', default=None, **kw): + """Serialize ``obj`` to a JSON formatted ``str``. + + If ``skipkeys`` is ``True`` then ``dict`` keys that are not basic types + (``str``, ``unicode``, ``int``, ``long``, ``float``, ``bool``, ``None``) + will be skipped instead of raising a ``TypeError``. + + If ``ensure_ascii`` is ``False``, then the return value will be a + ``unicode`` instance subject to normal Python ``str`` to ``unicode`` + coercion rules instead of being escaped to an ASCII ``str``. + + If ``check_circular`` is ``False``, then the circular reference check + for container types will be skipped and a circular reference will + result in an ``OverflowError`` (or worse). + + If ``allow_nan`` is ``False``, then it will be a ``ValueError`` to + serialize out of range ``float`` values (``nan``, ``inf``, ``-inf``) in + strict compliance of the JSON specification, instead of using the + JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``). + + If ``indent`` is a non-negative integer, then JSON array elements and + object members will be pretty-printed with that indent level. An indent + level of 0 will only insert newlines. ``None`` is the most compact + representation. + + If ``separators`` is an ``(item_separator, dict_separator)`` tuple + then it will be used instead of the default ``(', ', ': ')`` separators. + ``(',', ':')`` is the most compact JSON representation. + + ``encoding`` is the character encoding for str instances, default is UTF-8. + + ``default(obj)`` is a function that should return a serializable version + of obj or raise TypeError. The default simply raises TypeError. + + To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the + ``.default()`` method to serialize additional types), specify it with + the ``cls`` kwarg. + + """ + # cached encoder + if (skipkeys is False and ensure_ascii is True and + check_circular is True and allow_nan is True and + cls is None and indent is None and separators is None and + encoding == 'utf-8' and default is None and not kw): + return _default_encoder.encode(obj) + if cls is None: + cls = JSONEncoder + return cls( + skipkeys=skipkeys, ensure_ascii=ensure_ascii, + check_circular=check_circular, allow_nan=allow_nan, indent=indent, + separators=separators, encoding=encoding, default=default, + **kw).encode(obj) + + +_default_decoder = JSONDecoder(encoding=None, object_hook=None) + + +def load(fp, encoding=None, cls=None, object_hook=None, parse_float=None, + parse_int=None, parse_constant=None, **kw): + """Deserialize ``fp`` (a ``.read()``-supporting file-like object containing + a JSON document) to a Python object. + + If the contents of ``fp`` is encoded with an ASCII based encoding other + than utf-8 (e.g. latin-1), then an appropriate ``encoding`` name must + be specified. Encodings that are not ASCII based (such as UCS-2) are + not allowed, and should be wrapped with + ``codecs.getreader(fp)(encoding)``, or simply decoded to a ``unicode`` + object and passed to ``loads()`` + + ``object_hook`` is an optional function that will be called with the + result of any object literal decode (a ``dict``). The return value of + ``object_hook`` will be used instead of the ``dict``. This feature + can be used to implement custom decoders (e.g. JSON-RPC class hinting). + + To use a custom ``JSONDecoder`` subclass, specify it with the ``cls`` + kwarg. + + """ + return loads(fp.read(), + encoding=encoding, cls=cls, object_hook=object_hook, + parse_float=parse_float, parse_int=parse_int, + parse_constant=parse_constant, **kw) + + +def loads(s, encoding=None, cls=None, object_hook=None, parse_float=None, + parse_int=None, parse_constant=None, **kw): + """Deserialize ``s`` (a ``str`` or ``unicode`` instance containing a JSON + document) to a Python object. + + If ``s`` is a ``str`` instance and is encoded with an ASCII based encoding + other than utf-8 (e.g. latin-1) then an appropriate ``encoding`` name + must be specified. Encodings that are not ASCII based (such as UCS-2) + are not allowed and should be decoded to ``unicode`` first. + + ``object_hook`` is an optional function that will be called with the + result of any object literal decode (a ``dict``). The return value of + ``object_hook`` will be used instead of the ``dict``. This feature + can be used to implement custom decoders (e.g. JSON-RPC class hinting). + + ``parse_float``, if specified, will be called with the string + of every JSON float to be decoded. By default this is equivalent to + float(num_str). This can be used to use another datatype or parser + for JSON floats (e.g. decimal.Decimal). + + ``parse_int``, if specified, will be called with the string + of every JSON int to be decoded. By default this is equivalent to + int(num_str). This can be used to use another datatype or parser + for JSON integers (e.g. float). + + ``parse_constant``, if specified, will be called with one of the + following strings: -Infinity, Infinity, NaN, null, true, false. + This can be used to raise an exception if invalid JSON numbers + are encountered. + + To use a custom ``JSONDecoder`` subclass, specify it with the ``cls`` + kwarg. + + """ + if (cls is None and encoding is None and object_hook is None and + parse_int is None and parse_float is None and + parse_constant is None and not kw): + return _default_decoder.decode(s) + if cls is None: + cls = JSONDecoder + if object_hook is not None: + kw['object_hook'] = object_hook + if parse_float is not None: + kw['parse_float'] = parse_float + if parse_int is not None: + kw['parse_int'] = parse_int + if parse_constant is not None: + kw['parse_constant'] = parse_constant + return cls(encoding=encoding, **kw).decode(s) diff --git a/syncerror/simplejson/_speedups.c b/syncerror/simplejson/_speedups.c new file mode 100644 index 0000000..679a4c8 --- /dev/null +++ b/syncerror/simplejson/_speedups.c @@ -0,0 +1,2265 @@ +#include "Python.h" +#include "structmember.h" +#if PY_VERSION_HEX < 0x02060000 && !defined(Py_TYPE) +#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type) +#endif +#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN) +typedef int Py_ssize_t; +#define PY_SSIZE_T_MAX INT_MAX +#define PY_SSIZE_T_MIN INT_MIN +#define PyInt_FromSsize_t PyInt_FromLong +#define PyInt_AsSsize_t PyInt_AsLong +#endif +#ifndef Py_IS_FINITE +#define Py_IS_FINITE(X) (!Py_IS_INFINITY(X) && !Py_IS_NAN(X)) +#endif + +#ifdef __GNUC__ +#define UNUSED __attribute__((__unused__)) +#else +#define UNUSED +#endif + +#define DEFAULT_ENCODING "utf-8" + +#define PyScanner_Check(op) PyObject_TypeCheck(op, &PyScannerType) +#define PyScanner_CheckExact(op) (Py_TYPE(op) == &PyScannerType) +#define PyEncoder_Check(op) PyObject_TypeCheck(op, &PyEncoderType) +#define PyEncoder_CheckExact(op) (Py_TYPE(op) == &PyEncoderType) + +static PyTypeObject PyScannerType; +static PyTypeObject PyEncoderType; + +typedef struct _PyScannerObject { + PyObject_HEAD + PyObject *encoding; + PyObject *strict; + PyObject *object_hook; + PyObject *parse_float; + PyObject *parse_int; + PyObject *parse_constant; +} PyScannerObject; + +static PyMemberDef scanner_members[] = { + {"encoding", T_OBJECT, offsetof(PyScannerObject, encoding), READONLY, "encoding"}, + {"strict", T_OBJECT, offsetof(PyScannerObject, strict), READONLY, "strict"}, + {"object_hook", T_OBJECT, offsetof(PyScannerObject, object_hook), READONLY, "object_hook"}, + {"parse_float", T_OBJECT, offsetof(PyScannerObject, parse_float), READONLY, "parse_float"}, + {"parse_int", T_OBJECT, offsetof(PyScannerObject, parse_int), READONLY, "parse_int"}, + {"parse_constant", T_OBJECT, offsetof(PyScannerObject, parse_constant), READONLY, "parse_constant"}, + {NULL} +}; + +typedef struct _PyEncoderObject { + PyObject_HEAD + PyObject *markers; + PyObject *defaultfn; + PyObject *encoder; + PyObject *indent; + PyObject *key_separator; + PyObject *item_separator; + PyObject *sort_keys; + PyObject *skipkeys; + int fast_encode; + int allow_nan; +} PyEncoderObject; + +static PyMemberDef encoder_members[] = { + {"markers", T_OBJECT, offsetof(PyEncoderObject, markers), READONLY, "markers"}, + {"default", T_OBJECT, offsetof(PyEncoderObject, defaultfn), READONLY, "default"}, + {"encoder", T_OBJECT, offsetof(PyEncoderObject, encoder), READONLY, "encoder"}, + {"indent", T_OBJECT, offsetof(PyEncoderObject, indent), READONLY, "indent"}, + {"key_separator", T_OBJECT, offsetof(PyEncoderObject, key_separator), READONLY, "key_separator"}, + {"item_separator", T_OBJECT, offsetof(PyEncoderObject, item_separator), READONLY, "item_separator"}, + {"sort_keys", T_OBJECT, offsetof(PyEncoderObject, sort_keys), READONLY, "sort_keys"}, + {"skipkeys", T_OBJECT, offsetof(PyEncoderObject, skipkeys), READONLY, "skipkeys"}, + {NULL} +}; + +static Py_ssize_t +ascii_escape_char(Py_UNICODE c, char *output, Py_ssize_t chars); +static PyObject * +ascii_escape_unicode(PyObject *pystr); +static PyObject * +ascii_escape_str(PyObject *pystr); +static PyObject * +py_encode_basestring_ascii(PyObject* self UNUSED, PyObject *pystr); +void init_speedups(void); +static PyObject * +scan_once_str(PyScannerObject *s, PyObject *pystr, Py_ssize_t idx, Py_ssize_t *next_idx_ptr); +static PyObject * +scan_once_unicode(PyScannerObject *s, PyObject *pystr, Py_ssize_t idx, Py_ssize_t *next_idx_ptr); +static PyObject * +_build_rval_index_tuple(PyObject *rval, Py_ssize_t idx); +static int +scanner_init(PyObject *self, PyObject *args, PyObject *kwds); +static void +scanner_dealloc(PyObject *self); +static int +encoder_init(PyObject *self, PyObject *args, PyObject *kwds); +static void +encoder_dealloc(PyObject *self); +static int +encoder_listencode_list(PyEncoderObject *s, PyObject *rval, PyObject *seq, Py_ssize_t indent_level); +static int +encoder_listencode_obj(PyEncoderObject *s, PyObject *rval, PyObject *obj, Py_ssize_t indent_level); +static int +encoder_listencode_dict(PyEncoderObject *s, PyObject *rval, PyObject *dct, Py_ssize_t indent_level); +static PyObject * +_encoded_const(PyObject *const); +static void +raise_errmsg(char *msg, PyObject *s, Py_ssize_t end); +static PyObject * +encoder_encode_string(PyEncoderObject *s, PyObject *obj); +static int +_convertPyInt_AsSsize_t(PyObject *o, Py_ssize_t *size_ptr); +static PyObject * +_convertPyInt_FromSsize_t(Py_ssize_t *size_ptr); +static PyObject * +encoder_encode_float(PyEncoderObject *s, PyObject *obj); + +#define S_CHAR(c) (c >= ' ' && c <= '~' && c != '\\' && c != '"') +#define IS_WHITESPACE(c) (((c) == ' ') || ((c) == '\t') || ((c) == '\n') || ((c) == '\r')) + +#define MIN_EXPANSION 6 +#ifdef Py_UNICODE_WIDE +#define MAX_EXPANSION (2 * MIN_EXPANSION) +#else +#define MAX_EXPANSION MIN_EXPANSION +#endif + +static int +_convertPyInt_AsSsize_t(PyObject *o, Py_ssize_t *size_ptr) +{ + /* PyObject to Py_ssize_t converter */ + *size_ptr = PyInt_AsSsize_t(o); + if (*size_ptr == -1 && PyErr_Occurred()); + return 1; + return 0; +} + +static PyObject * +_convertPyInt_FromSsize_t(Py_ssize_t *size_ptr) +{ + /* Py_ssize_t to PyObject converter */ + return PyInt_FromSsize_t(*size_ptr); +} + +static Py_ssize_t +ascii_escape_char(Py_UNICODE c, char *output, Py_ssize_t chars) +{ + /* Escape unicode code point c to ASCII escape sequences + in char *output. output must have at least 12 bytes unused to + accommodate an escaped surrogate pair "\uXXXX\uXXXX" */ + output[chars++] = '\\'; + switch (c) { + case '\\': output[chars++] = (char)c; break; + case '"': output[chars++] = (char)c; break; + case '\b': output[chars++] = 'b'; break; + case '\f': output[chars++] = 'f'; break; + case '\n': output[chars++] = 'n'; break; + case '\r': output[chars++] = 'r'; break; + case '\t': output[chars++] = 't'; break; + default: +#ifdef Py_UNICODE_WIDE + if (c >= 0x10000) { + /* UTF-16 surrogate pair */ + Py_UNICODE v = c - 0x10000; + c = 0xd800 | ((v >> 10) & 0x3ff); + output[chars++] = 'u'; + output[chars++] = "0123456789abcdef"[(c >> 12) & 0xf]; + output[chars++] = "0123456789abcdef"[(c >> 8) & 0xf]; + output[chars++] = "0123456789abcdef"[(c >> 4) & 0xf]; + output[chars++] = "0123456789abcdef"[(c ) & 0xf]; + c = 0xdc00 | (v & 0x3ff); + output[chars++] = '\\'; + } +#endif + output[chars++] = 'u'; + output[chars++] = "0123456789abcdef"[(c >> 12) & 0xf]; + output[chars++] = "0123456789abcdef"[(c >> 8) & 0xf]; + output[chars++] = "0123456789abcdef"[(c >> 4) & 0xf]; + output[chars++] = "0123456789abcdef"[(c ) & 0xf]; + } + return chars; +} + +static PyObject * +ascii_escape_unicode(PyObject *pystr) +{ + /* Take a PyUnicode pystr and return a new ASCII-only escaped PyString */ + Py_ssize_t i; + Py_ssize_t input_chars; + Py_ssize_t output_size; + Py_ssize_t max_output_size; + Py_ssize_t chars; + PyObject *rval; + char *output; + Py_UNICODE *input_unicode; + + input_chars = PyUnicode_GET_SIZE(pystr); + input_unicode = PyUnicode_AS_UNICODE(pystr); + + /* One char input can be up to 6 chars output, estimate 4 of these */ + output_size = 2 + (MIN_EXPANSION * 4) + input_chars; + max_output_size = 2 + (input_chars * MAX_EXPANSION); + rval = PyString_FromStringAndSize(NULL, output_size); + if (rval == NULL) { + return NULL; + } + output = PyString_AS_STRING(rval); + chars = 0; + output[chars++] = '"'; + for (i = 0; i < input_chars; i++) { + Py_UNICODE c = input_unicode[i]; + if (S_CHAR(c)) { + output[chars++] = (char)c; + } + else { + chars = ascii_escape_char(c, output, chars); + } + if (output_size - chars < (1 + MAX_EXPANSION)) { + /* There's more than four, so let's resize by a lot */ + Py_ssize_t new_output_size = output_size * 2; + /* This is an upper bound */ + if (new_output_size > max_output_size) { + new_output_size = max_output_size; + } + /* Make sure that the output size changed before resizing */ + if (new_output_size != output_size) { + output_size = new_output_size; + if (_PyString_Resize(&rval, output_size) == -1) { + return NULL; + } + output = PyString_AS_STRING(rval); + } + } + } + output[chars++] = '"'; + if (_PyString_Resize(&rval, chars) == -1) { + return NULL; + } + return rval; +} + +static PyObject * +ascii_escape_str(PyObject *pystr) +{ + /* Take a PyString pystr and return a new ASCII-only escaped PyString */ + Py_ssize_t i; + Py_ssize_t input_chars; + Py_ssize_t output_size; + Py_ssize_t chars; + PyObject *rval; + char *output; + char *input_str; + + input_chars = PyString_GET_SIZE(pystr); + input_str = PyString_AS_STRING(pystr); + + /* Fast path for a string that's already ASCII */ + for (i = 0; i < input_chars; i++) { + Py_UNICODE c = (Py_UNICODE)(unsigned char)input_str[i]; + if (!S_CHAR(c)) { + /* If we have to escape something, scan the string for unicode */ + Py_ssize_t j; + for (j = i; j < input_chars; j++) { + c = (Py_UNICODE)(unsigned char)input_str[j]; + if (c > 0x7f) { + /* We hit a non-ASCII character, bail to unicode mode */ + PyObject *uni; + uni = PyUnicode_DecodeUTF8(input_str, input_chars, "strict"); + if (uni == NULL) { + return NULL; + } + rval = ascii_escape_unicode(uni); + Py_DECREF(uni); + return rval; + } + } + break; + } + } + + if (i == input_chars) { + /* Input is already ASCII */ + output_size = 2 + input_chars; + } + else { + /* One char input can be up to 6 chars output, estimate 4 of these */ + output_size = 2 + (MIN_EXPANSION * 4) + input_chars; + } + rval = PyString_FromStringAndSize(NULL, output_size); + if (rval == NULL) { + return NULL; + } + output = PyString_AS_STRING(rval); + output[0] = '"'; + + /* We know that everything up to i is ASCII already */ + chars = i + 1; + memcpy(&output[1], input_str, i); + + for (; i < input_chars; i++) { + Py_UNICODE c = (Py_UNICODE)(unsigned char)input_str[i]; + if (S_CHAR(c)) { + output[chars++] = (char)c; + } + else { + chars = ascii_escape_char(c, output, chars); + } + /* An ASCII char can't possibly expand to a surrogate! */ + if (output_size - chars < (1 + MIN_EXPANSION)) { + /* There's more than four, so let's resize by a lot */ + output_size *= 2; + if (output_size > 2 + (input_chars * MIN_EXPANSION)) { + output_size = 2 + (input_chars * MIN_EXPANSION); + } + if (_PyString_Resize(&rval, output_size) == -1) { + return NULL; + } + output = PyString_AS_STRING(rval); + } + } + output[chars++] = '"'; + if (_PyString_Resize(&rval, chars) == -1) { + return NULL; + } + return rval; +} + +static void +raise_errmsg(char *msg, PyObject *s, Py_ssize_t end) +{ + /* Use the Python function simplejson.decoder.errmsg to raise a nice + looking ValueError exception */ + static PyObject *errmsg_fn = NULL; + PyObject *pymsg; + if (errmsg_fn == NULL) { + PyObject *decoder = PyImport_ImportModule("simplejson.decoder"); + if (decoder == NULL) + return; + errmsg_fn = PyObject_GetAttrString(decoder, "errmsg"); + Py_DECREF(decoder); + if (errmsg_fn == NULL) + return; + } + pymsg = PyObject_CallFunction(errmsg_fn, "(zOO&)", msg, s, _convertPyInt_FromSsize_t, &end); + if (pymsg) { + PyErr_SetObject(PyExc_ValueError, pymsg); + Py_DECREF(pymsg); + } +} + +static PyObject * +join_list_unicode(PyObject *lst) +{ + /* return u''.join(lst) */ + static PyObject *joinfn = NULL; + if (joinfn == NULL) { + PyObject *ustr = PyUnicode_FromUnicode(NULL, 0); + if (ustr == NULL) + return NULL; + + joinfn = PyObject_GetAttrString(ustr, "join"); + Py_DECREF(ustr); + if (joinfn == NULL) + return NULL; + } + return PyObject_CallFunctionObjArgs(joinfn, lst, NULL); +} + +static PyObject * +join_list_string(PyObject *lst) +{ + /* return ''.join(lst) */ + static PyObject *joinfn = NULL; + if (joinfn == NULL) { + PyObject *ustr = PyString_FromStringAndSize(NULL, 0); + if (ustr == NULL) + return NULL; + + joinfn = PyObject_GetAttrString(ustr, "join"); + Py_DECREF(ustr); + if (joinfn == NULL) + return NULL; + } + return PyObject_CallFunctionObjArgs(joinfn, lst, NULL); +} + +static PyObject * +_build_rval_index_tuple(PyObject *rval, Py_ssize_t idx) { + /* return (rval, idx) tuple, stealing reference to rval */ + PyObject *tpl; + PyObject *pyidx; + /* + steal a reference to rval, returns (rval, idx) + */ + if (rval == NULL) { + return NULL; + } + pyidx = PyInt_FromSsize_t(idx); + if (pyidx == NULL) { + Py_DECREF(rval); + return NULL; + } + tpl = PyTuple_New(2); + if (tpl == NULL) { + Py_DECREF(pyidx); + Py_DECREF(rval); + return NULL; + } + PyTuple_SET_ITEM(tpl, 0, rval); + PyTuple_SET_ITEM(tpl, 1, pyidx); + return tpl; +} + +static PyObject * +scanstring_str(PyObject *pystr, Py_ssize_t end, char *encoding, int strict, Py_ssize_t *next_end_ptr) +{ + /* Read the JSON string from PyString pystr. + end is the index of the first character after the quote. + encoding is the encoding of pystr (must be an ASCII superset) + if strict is zero then literal control characters are allowed + *next_end_ptr is a return-by-reference index of the character + after the end quote + + Return value is a new PyString (if ASCII-only) or PyUnicode + */ + PyObject *rval; + Py_ssize_t len = PyString_GET_SIZE(pystr); + Py_ssize_t begin = end - 1; + Py_ssize_t next = begin; + int has_unicode = 0; + char *buf = PyString_AS_STRING(pystr); + PyObject *chunks = PyList_New(0); + if (chunks == NULL) { + goto bail; + } + if (end < 0 || len <= end) { + PyErr_SetString(PyExc_ValueError, "end is out of bounds"); + goto bail; + } + while (1) { + /* Find the end of the string or the next escape */ + Py_UNICODE c = 0; + PyObject *chunk = NULL; + for (next = end; next < len; next++) { + c = (unsigned char)buf[next]; + if (c == '"' || c == '\\') { + break; + } + else if (strict && c <= 0x1f) { + raise_errmsg("Invalid control character at", pystr, next); + goto bail; + } + else if (c > 0x7f) { + has_unicode = 1; + } + } + if (!(c == '"' || c == '\\')) { + raise_errmsg("Unterminated string starting at", pystr, begin); + goto bail; + } + /* Pick up this chunk if it's not zero length */ + if (next != end) { + PyObject *strchunk = PyString_FromStringAndSize(&buf[end], next - end); + if (strchunk == NULL) { + goto bail; + } + if (has_unicode) { + chunk = PyUnicode_FromEncodedObject(strchunk, encoding, NULL); + Py_DECREF(strchunk); + if (chunk == NULL) { + goto bail; + } + } + else { + chunk = strchunk; + } + if (PyList_Append(chunks, chunk)) { + Py_DECREF(chunk); + goto bail; + } + Py_DECREF(chunk); + } + next++; + if (c == '"') { + end = next; + break; + } + if (next == len) { + raise_errmsg("Unterminated string starting at", pystr, begin); + goto bail; + } + c = buf[next]; + if (c != 'u') { + /* Non-unicode backslash escapes */ + end = next + 1; + switch (c) { + case '"': break; + case '\\': break; + case '/': break; + case 'b': c = '\b'; break; + case 'f': c = '\f'; break; + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 't': c = '\t'; break; + default: c = 0; + } + if (c == 0) { + raise_errmsg("Invalid \\escape", pystr, end - 2); + goto bail; + } + } + else { + c = 0; + next++; + end = next + 4; + if (end >= len) { + raise_errmsg("Invalid \\uXXXX escape", pystr, next - 1); + goto bail; + } + /* Decode 4 hex digits */ + for (; next < end; next++) { + Py_UNICODE digit = buf[next]; + c <<= 4; + switch (digit) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + c |= (digit - '0'); break; + case 'a': case 'b': case 'c': case 'd': case 'e': + case 'f': + c |= (digit - 'a' + 10); break; + case 'A': case 'B': case 'C': case 'D': case 'E': + case 'F': + c |= (digit - 'A' + 10); break; + default: + raise_errmsg("Invalid \\uXXXX escape", pystr, end - 5); + goto bail; + } + } +#ifdef Py_UNICODE_WIDE + /* Surrogate pair */ + if ((c & 0xfc00) == 0xd800) { + Py_UNICODE c2 = 0; + if (end + 6 >= len) { + raise_errmsg("Unpaired high surrogate", pystr, end - 5); + goto bail; + } + if (buf[next++] != '\\' || buf[next++] != 'u') { + raise_errmsg("Unpaired high surrogate", pystr, end - 5); + goto bail; + } + end += 6; + /* Decode 4 hex digits */ + for (; next < end; next++) { + c2 <<= 4; + Py_UNICODE digit = buf[next]; + switch (digit) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + c2 |= (digit - '0'); break; + case 'a': case 'b': case 'c': case 'd': case 'e': + case 'f': + c2 |= (digit - 'a' + 10); break; + case 'A': case 'B': case 'C': case 'D': case 'E': + case 'F': + c2 |= (digit - 'A' + 10); break; + default: + raise_errmsg("Invalid \\uXXXX escape", pystr, end - 5); + goto bail; + } + } + if ((c2 & 0xfc00) != 0xdc00) { + raise_errmsg("Unpaired high surrogate", pystr, end - 5); + goto bail; + } + c = 0x10000 + (((c - 0xd800) << 10) | (c2 - 0xdc00)); + } + else if ((c & 0xfc00) == 0xdc00) { + raise_errmsg("Unpaired low surrogate", pystr, end - 5); + goto bail; + } +#endif + } + if (c > 0x7f) { + has_unicode = 1; + } + if (has_unicode) { + chunk = PyUnicode_FromUnicode(&c, 1); + if (chunk == NULL) { + goto bail; + } + } + else { + char c_char = Py_CHARMASK(c); + chunk = PyString_FromStringAndSize(&c_char, 1); + if (chunk == NULL) { + goto bail; + } + } + if (PyList_Append(chunks, chunk)) { + Py_DECREF(chunk); + goto bail; + } + Py_DECREF(chunk); + } + + rval = join_list_string(chunks); + if (rval == NULL) { + goto bail; + } + Py_CLEAR(chunks); + *next_end_ptr = end; + return rval; +bail: + *next_end_ptr = -1; + Py_XDECREF(chunks); + return NULL; +} + + +static PyObject * +scanstring_unicode(PyObject *pystr, Py_ssize_t end, int strict, Py_ssize_t *next_end_ptr) +{ + /* Read the JSON string from PyUnicode pystr. + end is the index of the first character after the quote. + encoding is the encoding of pystr (must be an ASCII superset) + if strict is zero then literal control characters are allowed + *next_end_ptr is a return-by-reference index of the character + after the end quote + + Return value is a new PyUnicode + */ + PyObject *rval; + Py_ssize_t len = PyUnicode_GET_SIZE(pystr); + Py_ssize_t begin = end - 1; + Py_ssize_t next = begin; + const Py_UNICODE *buf = PyUnicode_AS_UNICODE(pystr); + PyObject *chunks = PyList_New(0); + if (chunks == NULL) { + goto bail; + } + if (end < 0 || len <= end) { + PyErr_SetString(PyExc_ValueError, "end is out of bounds"); + goto bail; + } + while (1) { + /* Find the end of the string or the next escape */ + Py_UNICODE c = 0; + PyObject *chunk = NULL; + for (next = end; next < len; next++) { + c = buf[next]; + if (c == '"' || c == '\\') { + break; + } + else if (strict && c <= 0x1f) { + raise_errmsg("Invalid control character at", pystr, next); + goto bail; + } + } + if (!(c == '"' || c == '\\')) { + raise_errmsg("Unterminated string starting at", pystr, begin); + goto bail; + } + /* Pick up this chunk if it's not zero length */ + if (next != end) { + chunk = PyUnicode_FromUnicode(&buf[end], next - end); + if (chunk == NULL) { + goto bail; + } + if (PyList_Append(chunks, chunk)) { + Py_DECREF(chunk); + goto bail; + } + Py_DECREF(chunk); + } + next++; + if (c == '"') { + end = next; + break; + } + if (next == len) { + raise_errmsg("Unterminated string starting at", pystr, begin); + goto bail; + } + c = buf[next]; + if (c != 'u') { + /* Non-unicode backslash escapes */ + end = next + 1; + switch (c) { + case '"': break; + case '\\': break; + case '/': break; + case 'b': c = '\b'; break; + case 'f': c = '\f'; break; + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 't': c = '\t'; break; + default: c = 0; + } + if (c == 0) { + raise_errmsg("Invalid \\escape", pystr, end - 2); + goto bail; + } + } + else { + c = 0; + next++; + end = next + 4; + if (end >= len) { + raise_errmsg("Invalid \\uXXXX escape", pystr, next - 1); + goto bail; + } + /* Decode 4 hex digits */ + for (; next < end; next++) { + Py_UNICODE digit = buf[next]; + c <<= 4; + switch (digit) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + c |= (digit - '0'); break; + case 'a': case 'b': case 'c': case 'd': case 'e': + case 'f': + c |= (digit - 'a' + 10); break; + case 'A': case 'B': case 'C': case 'D': case 'E': + case 'F': + c |= (digit - 'A' + 10); break; + default: + raise_errmsg("Invalid \\uXXXX escape", pystr, end - 5); + goto bail; + } + } +#ifdef Py_UNICODE_WIDE + /* Surrogate pair */ + if ((c & 0xfc00) == 0xd800) { + Py_UNICODE c2 = 0; + if (end + 6 >= len) { + raise_errmsg("Unpaired high surrogate", pystr, end - 5); + goto bail; + } + if (buf[next++] != '\\' || buf[next++] != 'u') { + raise_errmsg("Unpaired high surrogate", pystr, end - 5); + goto bail; + } + end += 6; + /* Decode 4 hex digits */ + for (; next < end; next++) { + c2 <<= 4; + Py_UNICODE digit = buf[next]; + switch (digit) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + c2 |= (digit - '0'); break; + case 'a': case 'b': case 'c': case 'd': case 'e': + case 'f': + c2 |= (digit - 'a' + 10); break; + case 'A': case 'B': case 'C': case 'D': case 'E': + case 'F': + c2 |= (digit - 'A' + 10); break; + default: + raise_errmsg("Invalid \\uXXXX escape", pystr, end - 5); + goto bail; + } + } + if ((c2 & 0xfc00) != 0xdc00) { + raise_errmsg("Unpaired high surrogate", pystr, end - 5); + goto bail; + } + c = 0x10000 + (((c - 0xd800) << 10) | (c2 - 0xdc00)); + } + else if ((c & 0xfc00) == 0xdc00) { + raise_errmsg("Unpaired low surrogate", pystr, end - 5); + goto bail; + } +#endif + } + chunk = PyUnicode_FromUnicode(&c, 1); + if (chunk == NULL) { + goto bail; + } + if (PyList_Append(chunks, chunk)) { + Py_DECREF(chunk); + goto bail; + } + Py_DECREF(chunk); + } + + rval = join_list_unicode(chunks); + if (rval == NULL) { + goto bail; + } + Py_DECREF(chunks); + *next_end_ptr = end; + return rval; +bail: + *next_end_ptr = -1; + Py_XDECREF(chunks); + return NULL; +} + +PyDoc_STRVAR(pydoc_scanstring, + "scanstring(basestring, end, encoding, strict=True) -> (str, end)\n" + "\n" + "Scan the string s for a JSON string. End is the index of the\n" + "character in s after the quote that started the JSON string.\n" + "Unescapes all valid JSON string escape sequences and raises ValueError\n" + "on attempt to decode an invalid string. If strict is False then literal\n" + "control characters are allowed in the string.\n" + "\n" + "Returns a tuple of the decoded string and the index of the character in s\n" + "after the end quote." +); + +static PyObject * +py_scanstring(PyObject* self UNUSED, PyObject *args) +{ + PyObject *pystr; + PyObject *rval; + Py_ssize_t end; + Py_ssize_t next_end = -1; + char *encoding = NULL; + int strict = 1; + if (!PyArg_ParseTuple(args, "OO&|zi:scanstring", &pystr, _convertPyInt_AsSsize_t, &end, &encoding, &strict)) { + return NULL; + } + if (encoding == NULL) { + encoding = DEFAULT_ENCODING; + } + if (PyString_Check(pystr)) { + rval = scanstring_str(pystr, end, encoding, strict, &next_end); + } + else if (PyUnicode_Check(pystr)) { + rval = scanstring_unicode(pystr, end, strict, &next_end); + } + else { + PyErr_Format(PyExc_TypeError, + "first argument must be a string, not %.80s", + Py_TYPE(pystr)->tp_name); + return NULL; + } + return _build_rval_index_tuple(rval, next_end); +} + +PyDoc_STRVAR(pydoc_encode_basestring_ascii, + "encode_basestring_ascii(basestring) -> str\n" + "\n" + "Return an ASCII-only JSON representation of a Python string" +); + +static PyObject * +py_encode_basestring_ascii(PyObject* self UNUSED, PyObject *pystr) +{ + /* Return an ASCII-only JSON representation of a Python string */ + /* METH_O */ + if (PyString_Check(pystr)) { + return ascii_escape_str(pystr); + } + else if (PyUnicode_Check(pystr)) { + return ascii_escape_unicode(pystr); + } + else { + PyErr_Format(PyExc_TypeError, + "first argument must be a string, not %.80s", + Py_TYPE(pystr)->tp_name); + return NULL; + } +} + +static void +scanner_dealloc(PyObject *self) +{ + /* Deallocate scanner object */ + PyScannerObject *s; + assert(PyScanner_Check(self)); + s = (PyScannerObject *)self; + Py_CLEAR(s->encoding); + Py_CLEAR(s->strict); + Py_CLEAR(s->object_hook); + Py_CLEAR(s->parse_float); + Py_CLEAR(s->parse_int); + Py_CLEAR(s->parse_constant); + self->ob_type->tp_free(self); +} + +static PyObject * +_parse_object_str(PyScannerObject *s, PyObject *pystr, Py_ssize_t idx, Py_ssize_t *next_idx_ptr) { + /* Read a JSON object from PyString pystr. + idx is the index of the first character after the opening curly brace. + *next_idx_ptr is a return-by-reference index to the first character after + the closing curly brace. + + Returns a new PyObject (usually a dict, but object_hook can change that) + */ + char *str = PyString_AS_STRING(pystr); + Py_ssize_t end_idx = PyString_GET_SIZE(pystr) - 1; + PyObject *rval = PyDict_New(); + PyObject *key = NULL; + PyObject *val = NULL; + char *encoding = PyString_AS_STRING(s->encoding); + int strict = PyObject_IsTrue(s->strict); + Py_ssize_t next_idx; + if (rval == NULL) + return NULL; + + /* skip whitespace after { */ + while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++; + + /* only loop if the object is non-empty */ + if (idx <= end_idx && str[idx] != '}') { + while (idx <= end_idx) { + /* read key */ + if (str[idx] != '"') { + raise_errmsg("Expecting property name", pystr, idx); + goto bail; + } + key = scanstring_str(pystr, idx + 1, encoding, strict, &next_idx); + if (key == NULL) + goto bail; + idx = next_idx; + + /* skip whitespace between key and : delimiter, read :, skip whitespace */ + while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++; + if (idx > end_idx || str[idx] != ':') { + raise_errmsg("Expecting : delimiter", pystr, idx); + goto bail; + } + idx++; + while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++; + + /* read any JSON data type */ + val = scan_once_str(s, pystr, idx, &next_idx); + if (val == NULL) + goto bail; + + if (PyDict_SetItem(rval, key, val) == -1) + goto bail; + + Py_CLEAR(key); + Py_CLEAR(val); + idx = next_idx; + + /* skip whitespace before } or , */ + while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++; + + /* bail if the object is closed or we didn't get the , delimiter */ + if (idx > end_idx) break; + if (str[idx] == '}') { + break; + } + else if (str[idx] != ',') { + raise_errmsg("Expecting , delimiter", pystr, idx); + goto bail; + } + idx++; + + /* skip whitespace after , delimiter */ + while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++; + } + } + /* verify that idx < end_idx, str[idx] should be '}' */ + if (idx > end_idx || str[idx] != '}') { + raise_errmsg("Expecting object", pystr, end_idx); + goto bail; + } + /* if object_hook is not None: rval = object_hook(rval) */ + if (s->object_hook != Py_None) { + val = PyObject_CallFunctionObjArgs(s->object_hook, rval, NULL); + if (val == NULL) + goto bail; + Py_DECREF(rval); + rval = val; + val = NULL; + } + *next_idx_ptr = idx + 1; + return rval; +bail: + Py_XDECREF(key); + Py_XDECREF(val); + Py_DECREF(rval); + return NULL; +} + +static PyObject * +_parse_object_unicode(PyScannerObject *s, PyObject *pystr, Py_ssize_t idx, Py_ssize_t *next_idx_ptr) { + /* Read a JSON object from PyUnicode pystr. + idx is the index of the first character after the opening curly brace. + *next_idx_ptr is a return-by-reference index to the first character after + the closing curly brace. + + Returns a new PyObject (usually a dict, but object_hook can change that) + */ + Py_UNICODE *str = PyUnicode_AS_UNICODE(pystr); + Py_ssize_t end_idx = PyUnicode_GET_SIZE(pystr) - 1; + PyObject *val = NULL; + PyObject *rval = PyDict_New(); + PyObject *key = NULL; + int strict = PyObject_IsTrue(s->strict); + Py_ssize_t next_idx; + if (rval == NULL) + return NULL; + + /* skip whitespace after { */ + while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++; + + /* only loop if the object is non-empty */ + if (idx <= end_idx && str[idx] != '}') { + while (idx <= end_idx) { + /* read key */ + if (str[idx] != '"') { + raise_errmsg("Expecting property name", pystr, idx); + goto bail; + } + key = scanstring_unicode(pystr, idx + 1, strict, &next_idx); + if (key == NULL) + goto bail; + idx = next_idx; + + /* skip whitespace between key and : delimiter, read :, skip whitespace */ + while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++; + if (idx > end_idx || str[idx] != ':') { + raise_errmsg("Expecting : delimiter", pystr, idx); + goto bail; + } + idx++; + while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++; + + /* read any JSON term */ + val = scan_once_unicode(s, pystr, idx, &next_idx); + if (val == NULL) + goto bail; + + if (PyDict_SetItem(rval, key, val) == -1) + goto bail; + + Py_CLEAR(key); + Py_CLEAR(val); + idx = next_idx; + + /* skip whitespace before } or , */ + while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++; + + /* bail if the object is closed or we didn't get the , delimiter */ + if (idx > end_idx) break; + if (str[idx] == '}') { + break; + } + else if (str[idx] != ',') { + raise_errmsg("Expecting , delimiter", pystr, idx); + goto bail; + } + idx++; + + /* skip whitespace after , delimiter */ + while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++; + } + } + + /* verify that idx < end_idx, str[idx] should be '}' */ + if (idx > end_idx || str[idx] != '}') { + raise_errmsg("Expecting object", pystr, end_idx); + goto bail; + } + + /* if object_hook is not None: rval = object_hook(rval) */ + if (s->object_hook != Py_None) { + val = PyObject_CallFunctionObjArgs(s->object_hook, rval, NULL); + if (val == NULL) + goto bail; + Py_DECREF(rval); + rval = val; + val = NULL; + } + *next_idx_ptr = idx + 1; + return rval; +bail: + Py_XDECREF(key); + Py_XDECREF(val); + Py_DECREF(rval); + return NULL; +} + +static PyObject * +_parse_array_str(PyScannerObject *s, PyObject *pystr, Py_ssize_t idx, Py_ssize_t *next_idx_ptr) { + /* Read a JSON array from PyString pystr. + idx is the index of the first character after the opening brace. + *next_idx_ptr is a return-by-reference index to the first character after + the closing brace. + + Returns a new PyList + */ + char *str = PyString_AS_STRING(pystr); + Py_ssize_t end_idx = PyString_GET_SIZE(pystr) - 1; + PyObject *val = NULL; + PyObject *rval = PyList_New(0); + Py_ssize_t next_idx; + if (rval == NULL) + return NULL; + + /* skip whitespace after [ */ + while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++; + + /* only loop if the array is non-empty */ + if (idx <= end_idx && str[idx] != ']') { + while (idx <= end_idx) { + + /* read any JSON term and de-tuplefy the (rval, idx) */ + val = scan_once_str(s, pystr, idx, &next_idx); + if (val == NULL) + goto bail; + + if (PyList_Append(rval, val) == -1) + goto bail; + + Py_CLEAR(val); + idx = next_idx; + + /* skip whitespace between term and , */ + while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++; + + /* bail if the array is closed or we didn't get the , delimiter */ + if (idx > end_idx) break; + if (str[idx] == ']') { + break; + } + else if (str[idx] != ',') { + raise_errmsg("Expecting , delimiter", pystr, idx); + goto bail; + } + idx++; + + /* skip whitespace after , */ + while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++; + } + } + + /* verify that idx < end_idx, str[idx] should be ']' */ + if (idx > end_idx || str[idx] != ']') { + raise_errmsg("Expecting object", pystr, end_idx); + goto bail; + } + *next_idx_ptr = idx + 1; + return rval; +bail: + Py_XDECREF(val); + Py_DECREF(rval); + return NULL; +} + +static PyObject * +_parse_array_unicode(PyScannerObject *s, PyObject *pystr, Py_ssize_t idx, Py_ssize_t *next_idx_ptr) { + /* Read a JSON array from PyString pystr. + idx is the index of the first character after the opening brace. + *next_idx_ptr is a return-by-reference index to the first character after + the closing brace. + + Returns a new PyList + */ + Py_UNICODE *str = PyUnicode_AS_UNICODE(pystr); + Py_ssize_t end_idx = PyUnicode_GET_SIZE(pystr) - 1; + PyObject *val = NULL; + PyObject *rval = PyList_New(0); + Py_ssize_t next_idx; + if (rval == NULL) + return NULL; + + /* skip whitespace after [ */ + while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++; + + /* only loop if the array is non-empty */ + if (idx <= end_idx && str[idx] != ']') { + while (idx <= end_idx) { + + /* read any JSON term */ + val = scan_once_unicode(s, pystr, idx, &next_idx); + if (val == NULL) + goto bail; + + if (PyList_Append(rval, val) == -1) + goto bail; + + Py_CLEAR(val); + idx = next_idx; + + /* skip whitespace between term and , */ + while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++; + + /* bail if the array is closed or we didn't get the , delimiter */ + if (idx > end_idx) break; + if (str[idx] == ']') { + break; + } + else if (str[idx] != ',') { + raise_errmsg("Expecting , delimiter", pystr, idx); + goto bail; + } + idx++; + + /* skip whitespace after , */ + while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++; + } + } + + /* verify that idx < end_idx, str[idx] should be ']' */ + if (idx > end_idx || str[idx] != ']') { + raise_errmsg("Expecting object", pystr, end_idx); + goto bail; + } + *next_idx_ptr = idx + 1; + return rval; +bail: + Py_XDECREF(val); + Py_DECREF(rval); + return NULL; +} + +static PyObject * +_parse_constant(PyScannerObject *s, char *constant, Py_ssize_t idx, Py_ssize_t *next_idx_ptr) { + /* Read a JSON constant from PyString pystr. + constant is the constant string that was found + ("NaN", "Infinity", "-Infinity"). + idx is the index of the first character of the constant + *next_idx_ptr is a return-by-reference index to the first character after + the constant. + + Returns the result of parse_constant + */ + PyObject *cstr; + PyObject *rval; + /* constant is "NaN", "Infinity", or "-Infinity" */ + cstr = PyString_InternFromString(constant); + if (cstr == NULL) + return NULL; + + /* rval = parse_constant(constant) */ + rval = PyObject_CallFunctionObjArgs(s->parse_constant, cstr, NULL); + idx += PyString_GET_SIZE(cstr); + Py_DECREF(cstr); + *next_idx_ptr = idx; + return rval; +} + +static PyObject * +_match_number_str(PyScannerObject *s, PyObject *pystr, Py_ssize_t start, Py_ssize_t *next_idx_ptr) { + /* Read a JSON number from PyString pystr. + idx is the index of the first character of the number + *next_idx_ptr is a return-by-reference index to the first character after + the number. + + Returns a new PyObject representation of that number: + PyInt, PyLong, or PyFloat. + May return other types if parse_int or parse_float are set + */ + char *str = PyString_AS_STRING(pystr); + Py_ssize_t end_idx = PyString_GET_SIZE(pystr) - 1; + Py_ssize_t idx = start; + int is_float = 0; + PyObject *rval; + PyObject *numstr; + + /* read a sign if it's there, make sure it's not the end of the string */ + if (str[idx] == '-') { + idx++; + if (idx > end_idx) { + PyErr_SetNone(PyExc_StopIteration); + return NULL; + } + } + + /* read as many integer digits as we find as long as it doesn't start with 0 */ + if (str[idx] >= '1' && str[idx] <= '9') { + idx++; + while (idx <= end_idx && str[idx] >= '0' && str[idx] <= '9') idx++; + } + /* if it starts with 0 we only expect one integer digit */ + else if (str[idx] == '0') { + idx++; + } + /* no integer digits, error */ + else { + PyErr_SetNone(PyExc_StopIteration); + return NULL; + } + + /* if the next char is '.' followed by a digit then read all float digits */ + if (idx < end_idx && str[idx] == '.' && str[idx + 1] >= '0' && str[idx + 1] <= '9') { + is_float = 1; + idx += 2; + while (idx <= end_idx && str[idx] >= '0' && str[idx] <= '9') idx++; + } + + /* if the next char is 'e' or 'E' then maybe read the exponent (or backtrack) */ + if (idx < end_idx && (str[idx] == 'e' || str[idx] == 'E')) { + + /* save the index of the 'e' or 'E' just in case we need to backtrack */ + Py_ssize_t e_start = idx; + idx++; + + /* read an exponent sign if present */ + if (idx < end_idx && (str[idx] == '-' || str[idx] == '+')) idx++; + + /* read all digits */ + while (idx <= end_idx && str[idx] >= '0' && str[idx] <= '9') idx++; + + /* if we got a digit, then parse as float. if not, backtrack */ + if (str[idx - 1] >= '0' && str[idx - 1] <= '9') { + is_float = 1; + } + else { + idx = e_start; + } + } + + /* copy the section we determined to be a number */ + numstr = PyString_FromStringAndSize(&str[start], idx - start); + if (numstr == NULL) + return NULL; + if (is_float) { + /* parse as a float using a fast path if available, otherwise call user defined method */ + if (s->parse_float != (PyObject *)&PyFloat_Type) { + rval = PyObject_CallFunctionObjArgs(s->parse_float, numstr, NULL); + } + else { + rval = PyFloat_FromDouble(PyOS_ascii_atof(PyString_AS_STRING(numstr))); + } + } + else { + /* parse as an int using a fast path if available, otherwise call user defined method */ + if (s->parse_int != (PyObject *)&PyInt_Type) { + rval = PyObject_CallFunctionObjArgs(s->parse_int, numstr, NULL); + } + else { + rval = PyInt_FromString(PyString_AS_STRING(numstr), NULL, 10); + } + } + Py_DECREF(numstr); + *next_idx_ptr = idx; + return rval; +} + +static PyObject * +_match_number_unicode(PyScannerObject *s, PyObject *pystr, Py_ssize_t start, Py_ssize_t *next_idx_ptr) { + /* Read a JSON number from PyUnicode pystr. + idx is the index of the first character of the number + *next_idx_ptr is a return-by-reference index to the first character after + the number. + + Returns a new PyObject representation of that number: + PyInt, PyLong, or PyFloat. + May return other types if parse_int or parse_float are set + */ + Py_UNICODE *str = PyUnicode_AS_UNICODE(pystr); + Py_ssize_t end_idx = PyUnicode_GET_SIZE(pystr) - 1; + Py_ssize_t idx = start; + int is_float = 0; + PyObject *rval; + PyObject *numstr; + + /* read a sign if it's there, make sure it's not the end of the string */ + if (str[idx] == '-') { + idx++; + if (idx > end_idx) { + PyErr_SetNone(PyExc_StopIteration); + return NULL; + } + } + + /* read as many integer digits as we find as long as it doesn't start with 0 */ + if (str[idx] >= '1' && str[idx] <= '9') { + idx++; + while (idx <= end_idx && str[idx] >= '0' && str[idx] <= '9') idx++; + } + /* if it starts with 0 we only expect one integer digit */ + else if (str[idx] == '0') { + idx++; + } + /* no integer digits, error */ + else { + PyErr_SetNone(PyExc_StopIteration); + return NULL; + } + + /* if the next char is '.' followed by a digit then read all float digits */ + if (idx < end_idx && str[idx] == '.' && str[idx + 1] >= '0' && str[idx + 1] <= '9') { + is_float = 1; + idx += 2; + while (idx < end_idx && str[idx] >= '0' && str[idx] <= '9') idx++; + } + + /* if the next char is 'e' or 'E' then maybe read the exponent (or backtrack) */ + if (idx < end_idx && (str[idx] == 'e' || str[idx] == 'E')) { + Py_ssize_t e_start = idx; + idx++; + + /* read an exponent sign if present */ + if (idx < end_idx && (str[idx] == '-' || str[idx] == '+')) idx++; + + /* read all digits */ + while (idx <= end_idx && str[idx] >= '0' && str[idx] <= '9') idx++; + + /* if we got a digit, then parse as float. if not, backtrack */ + if (str[idx - 1] >= '0' && str[idx - 1] <= '9') { + is_float = 1; + } + else { + idx = e_start; + } + } + + /* copy the section we determined to be a number */ + numstr = PyUnicode_FromUnicode(&str[start], idx - start); + if (numstr == NULL) + return NULL; + if (is_float) { + /* parse as a float using a fast path if available, otherwise call user defined method */ + if (s->parse_float != (PyObject *)&PyFloat_Type) { + rval = PyObject_CallFunctionObjArgs(s->parse_float, numstr, NULL); + } + else { + rval = PyFloat_FromString(numstr, NULL); + } + } + else { + /* no fast path for unicode -> int, just call */ + rval = PyObject_CallFunctionObjArgs(s->parse_int, numstr, NULL); + } + Py_DECREF(numstr); + *next_idx_ptr = idx; + return rval; +} + +static PyObject * +scan_once_str(PyScannerObject *s, PyObject *pystr, Py_ssize_t idx, Py_ssize_t *next_idx_ptr) +{ + /* Read one JSON term (of any kind) from PyString pystr. + idx is the index of the first character of the term + *next_idx_ptr is a return-by-reference index to the first character after + the number. + + Returns a new PyObject representation of the term. + */ + char *str = PyString_AS_STRING(pystr); + Py_ssize_t length = PyString_GET_SIZE(pystr); + if (idx >= length) { + PyErr_SetNone(PyExc_StopIteration); + return NULL; + } + switch (str[idx]) { + case '"': + /* string */ + return scanstring_str(pystr, idx + 1, + PyString_AS_STRING(s->encoding), + PyObject_IsTrue(s->strict), + next_idx_ptr); + case '{': + /* object */ + return _parse_object_str(s, pystr, idx + 1, next_idx_ptr); + case '[': + /* array */ + return _parse_array_str(s, pystr, idx + 1, next_idx_ptr); + case 'n': + /* null */ + if ((idx + 3 < length) && str[idx + 1] == 'u' && str[idx + 2] == 'l' && str[idx + 3] == 'l') { + Py_INCREF(Py_None); + *next_idx_ptr = idx + 4; + return Py_None; + } + break; + case 't': + /* true */ + if ((idx + 3 < length) && str[idx + 1] == 'r' && str[idx + 2] == 'u' && str[idx + 3] == 'e') { + Py_INCREF(Py_True); + *next_idx_ptr = idx + 4; + return Py_True; + } + break; + case 'f': + /* false */ + if ((idx + 4 < length) && str[idx + 1] == 'a' && str[idx + 2] == 'l' && str[idx + 3] == 's' && str[idx + 4] == 'e') { + Py_INCREF(Py_False); + *next_idx_ptr = idx + 5; + return Py_False; + } + break; + case 'N': + /* NaN */ + if ((idx + 2 < length) && str[idx + 1] == 'a' && str[idx + 2] == 'N') { + return _parse_constant(s, "NaN", idx, next_idx_ptr); + } + break; + case 'I': + /* Infinity */ + if ((idx + 7 < length) && str[idx + 1] == 'n' && str[idx + 2] == 'f' && str[idx + 3] == 'i' && str[idx + 4] == 'n' && str[idx + 5] == 'i' && str[idx + 6] == 't' && str[idx + 7] == 'y') { + return _parse_constant(s, "Infinity", idx, next_idx_ptr); + } + break; + case '-': + /* -Infinity */ + if ((idx + 8 < length) && str[idx + 1] == 'I' && str[idx + 2] == 'n' && str[idx + 3] == 'f' && str[idx + 4] == 'i' && str[idx + 5] == 'n' && str[idx + 6] == 'i' && str[idx + 7] == 't' && str[idx + 8] == 'y') { + return _parse_constant(s, "-Infinity", idx, next_idx_ptr); + } + break; + } + /* Didn't find a string, object, array, or named constant. Look for a number. */ + return _match_number_str(s, pystr, idx, next_idx_ptr); +} + +static PyObject * +scan_once_unicode(PyScannerObject *s, PyObject *pystr, Py_ssize_t idx, Py_ssize_t *next_idx_ptr) +{ + /* Read one JSON term (of any kind) from PyUnicode pystr. + idx is the index of the first character of the term + *next_idx_ptr is a return-by-reference index to the first character after + the number. + + Returns a new PyObject representation of the term. + */ + Py_UNICODE *str = PyUnicode_AS_UNICODE(pystr); + Py_ssize_t length = PyUnicode_GET_SIZE(pystr); + if (idx >= length) { + PyErr_SetNone(PyExc_StopIteration); + return NULL; + } + switch (str[idx]) { + case '"': + /* string */ + return scanstring_unicode(pystr, idx + 1, + PyObject_IsTrue(s->strict), + next_idx_ptr); + case '{': + /* object */ + return _parse_object_unicode(s, pystr, idx + 1, next_idx_ptr); + case '[': + /* array */ + return _parse_array_unicode(s, pystr, idx + 1, next_idx_ptr); + case 'n': + /* null */ + if ((idx + 3 < length) && str[idx + 1] == 'u' && str[idx + 2] == 'l' && str[idx + 3] == 'l') { + Py_INCREF(Py_None); + *next_idx_ptr = idx + 4; + return Py_None; + } + break; + case 't': + /* true */ + if ((idx + 3 < length) && str[idx + 1] == 'r' && str[idx + 2] == 'u' && str[idx + 3] == 'e') { + Py_INCREF(Py_True); + *next_idx_ptr = idx + 4; + return Py_True; + } + break; + case 'f': + /* false */ + if ((idx + 4 < length) && str[idx + 1] == 'a' && str[idx + 2] == 'l' && str[idx + 3] == 's' && str[idx + 4] == 'e') { + Py_INCREF(Py_False); + *next_idx_ptr = idx + 5; + return Py_False; + } + break; + case 'N': + /* NaN */ + if ((idx + 2 < length) && str[idx + 1] == 'a' && str[idx + 2] == 'N') { + return _parse_constant(s, "NaN", idx, next_idx_ptr); + } + break; + case 'I': + /* Infinity */ + if ((idx + 7 < length) && str[idx + 1] == 'n' && str[idx + 2] == 'f' && str[idx + 3] == 'i' && str[idx + 4] == 'n' && str[idx + 5] == 'i' && str[idx + 6] == 't' && str[idx + 7] == 'y') { + return _parse_constant(s, "Infinity", idx, next_idx_ptr); + } + break; + case '-': + /* -Infinity */ + if ((idx + 8 < length) && str[idx + 1] == 'I' && str[idx + 2] == 'n' && str[idx + 3] == 'f' && str[idx + 4] == 'i' && str[idx + 5] == 'n' && str[idx + 6] == 'i' && str[idx + 7] == 't' && str[idx + 8] == 'y') { + return _parse_constant(s, "-Infinity", idx, next_idx_ptr); + } + break; + } + /* Didn't find a string, object, array, or named constant. Look for a number. */ + return _match_number_unicode(s, pystr, idx, next_idx_ptr); +} + +static PyObject * +scanner_call(PyObject *self, PyObject *args, PyObject *kwds) +{ + /* Python callable interface to scan_once_{str,unicode} */ + PyObject *pystr; + PyObject *rval; + Py_ssize_t idx; + Py_ssize_t next_idx = -1; + static char *kwlist[] = {"string", "idx", NULL}; + PyScannerObject *s; + assert(PyScanner_Check(self)); + s = (PyScannerObject *)self; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO&:scan_once", kwlist, &pystr, _convertPyInt_AsSsize_t, &idx)) + return NULL; + + if (PyString_Check(pystr)) { + rval = scan_once_str(s, pystr, idx, &next_idx); + } + else if (PyUnicode_Check(pystr)) { + rval = scan_once_unicode(s, pystr, idx, &next_idx); + } + else { + PyErr_Format(PyExc_TypeError, + "first argument must be a string, not %.80s", + Py_TYPE(pystr)->tp_name); + return NULL; + } + return _build_rval_index_tuple(rval, next_idx); +} + +static int +scanner_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + /* Initialize Scanner object */ + PyObject *ctx; + static char *kwlist[] = {"context", NULL}; + PyScannerObject *s; + + assert(PyScanner_Check(self)); + s = (PyScannerObject *)self; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:make_scanner", kwlist, &ctx)) + return -1; + + s->encoding = NULL; + s->strict = NULL; + s->object_hook = NULL; + s->parse_float = NULL; + s->parse_int = NULL; + s->parse_constant = NULL; + + /* PyString_AS_STRING is used on encoding */ + s->encoding = PyObject_GetAttrString(ctx, "encoding"); + if (s->encoding == Py_None) { + Py_DECREF(Py_None); + s->encoding = PyString_InternFromString(DEFAULT_ENCODING); + } + else if (PyUnicode_Check(s->encoding)) { + PyObject *tmp = PyUnicode_AsEncodedString(s->encoding, NULL, NULL); + Py_DECREF(s->encoding); + s->encoding = tmp; + } + if (s->encoding == NULL || !PyString_Check(s->encoding)) + goto bail; + + /* All of these will fail "gracefully" so we don't need to verify them */ + s->strict = PyObject_GetAttrString(ctx, "strict"); + if (s->strict == NULL) + goto bail; + s->object_hook = PyObject_GetAttrString(ctx, "object_hook"); + if (s->object_hook == NULL) + goto bail; + s->parse_float = PyObject_GetAttrString(ctx, "parse_float"); + if (s->parse_float == NULL) + goto bail; + s->parse_int = PyObject_GetAttrString(ctx, "parse_int"); + if (s->parse_int == NULL) + goto bail; + s->parse_constant = PyObject_GetAttrString(ctx, "parse_constant"); + if (s->parse_constant == NULL) + goto bail; + + return 0; + +bail: + Py_CLEAR(s->encoding); + Py_CLEAR(s->strict); + Py_CLEAR(s->object_hook); + Py_CLEAR(s->parse_float); + Py_CLEAR(s->parse_int); + Py_CLEAR(s->parse_constant); + return -1; +} + +PyDoc_STRVAR(scanner_doc, "JSON scanner object"); + +static +PyTypeObject PyScannerType = { + PyObject_HEAD_INIT(0) + 0, /* tp_internal */ + "Scanner", /* tp_name */ + sizeof(PyScannerObject), /* tp_basicsize */ + 0, /* tp_itemsize */ + scanner_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + scanner_call, /* tp_call */ + 0, /* tp_str */ + 0,/* PyObject_GenericGetAttr, */ /* tp_getattro */ + 0,/* PyObject_GenericSetAttr, */ /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + scanner_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + scanner_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + scanner_init, /* tp_init */ + 0,/* PyType_GenericAlloc, */ /* tp_alloc */ + 0,/* PyType_GenericNew, */ /* tp_new */ + 0,/* _PyObject_Del, */ /* tp_free */ +}; + +static int +encoder_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + /* initialize Encoder object */ + static char *kwlist[] = {"markers", "default", "encoder", "indent", "key_separator", "item_separator", "sort_keys", "skipkeys", "allow_nan", NULL}; + + PyEncoderObject *s; + PyObject *allow_nan; + + assert(PyEncoder_Check(self)); + s = (PyEncoderObject *)self; + + s->markers = NULL; + s->defaultfn = NULL; + s->encoder = NULL; + s->indent = NULL; + s->key_separator = NULL; + s->item_separator = NULL; + s->sort_keys = NULL; + s->skipkeys = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "OOOOOOOOO:make_encoder", kwlist, + &s->markers, &s->defaultfn, &s->encoder, &s->indent, &s->key_separator, &s->item_separator, &s->sort_keys, &s->skipkeys, &allow_nan)) + return -1; + + Py_INCREF(s->markers); + Py_INCREF(s->defaultfn); + Py_INCREF(s->encoder); + Py_INCREF(s->indent); + Py_INCREF(s->key_separator); + Py_INCREF(s->item_separator); + Py_INCREF(s->sort_keys); + Py_INCREF(s->skipkeys); + s->fast_encode = (PyCFunction_Check(s->encoder) && PyCFunction_GetFunction(s->encoder) == (PyCFunction)py_encode_basestring_ascii); + s->allow_nan = PyObject_IsTrue(allow_nan); + return 0; +} + +static PyObject * +encoder_call(PyObject *self, PyObject *args, PyObject *kwds) +{ + /* Python callable interface to encode_listencode_obj */ + static char *kwlist[] = {"obj", "_current_indent_level", NULL}; + PyObject *obj; + PyObject *rval; + Py_ssize_t indent_level; + PyEncoderObject *s; + assert(PyEncoder_Check(self)); + s = (PyEncoderObject *)self; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO&:_iterencode", kwlist, + &obj, _convertPyInt_AsSsize_t, &indent_level)) + return NULL; + rval = PyList_New(0); + if (rval == NULL) + return NULL; + if (encoder_listencode_obj(s, rval, obj, indent_level)) { + Py_DECREF(rval); + return NULL; + } + return rval; +} + +static PyObject * +_encoded_const(PyObject *obj) +{ + /* Return the JSON string representation of None, True, False */ + if (obj == Py_None) { + static PyObject *s_null = NULL; + if (s_null == NULL) { + s_null = PyString_InternFromString("null"); + } + Py_INCREF(s_null); + return s_null; + } + else if (obj == Py_True) { + static PyObject *s_true = NULL; + if (s_true == NULL) { + s_true = PyString_InternFromString("true"); + } + Py_INCREF(s_true); + return s_true; + } + else if (obj == Py_False) { + static PyObject *s_false = NULL; + if (s_false == NULL) { + s_false = PyString_InternFromString("false"); + } + Py_INCREF(s_false); + return s_false; + } + else { + PyErr_SetString(PyExc_ValueError, "not a const"); + return NULL; + } +} + +static PyObject * +encoder_encode_float(PyEncoderObject *s, PyObject *obj) +{ + /* Return the JSON representation of a PyFloat */ + double i = PyFloat_AS_DOUBLE(obj); + if (!Py_IS_FINITE(i)) { + if (!s->allow_nan) { + PyErr_SetString(PyExc_ValueError, "Out of range float values are not JSON compliant"); + return NULL; + } + if (i > 0) { + return PyString_FromString("Infinity"); + } + else if (i < 0) { + return PyString_FromString("-Infinity"); + } + else { + return PyString_FromString("NaN"); + } + } + /* Use a better float format here? */ + return PyObject_Repr(obj); +} + +static PyObject * +encoder_encode_string(PyEncoderObject *s, PyObject *obj) +{ + /* Return the JSON representation of a string */ + if (s->fast_encode) + return py_encode_basestring_ascii(NULL, obj); + else + return PyObject_CallFunctionObjArgs(s->encoder, obj, NULL); +} + +static int +_steal_list_append(PyObject *lst, PyObject *stolen) +{ + /* Append stolen and then decrement its reference count */ + int rval = PyList_Append(lst, stolen); + Py_DECREF(stolen); + return rval; +} + +static int +encoder_listencode_obj(PyEncoderObject *s, PyObject *rval, PyObject *obj, Py_ssize_t indent_level) +{ + /* Encode Python object obj to a JSON term, rval is a PyList */ + PyObject *newobj; + int rv; + + if (obj == Py_None || obj == Py_True || obj == Py_False) { + PyObject *cstr = _encoded_const(obj); + if (cstr == NULL) + return -1; + return _steal_list_append(rval, cstr); + } + else if (PyString_Check(obj) || PyUnicode_Check(obj)) + { + PyObject *encoded = encoder_encode_string(s, obj); + if (encoded == NULL) + return -1; + return _steal_list_append(rval, encoded); + } + else if (PyInt_Check(obj) || PyLong_Check(obj)) { + PyObject *encoded = PyObject_Str(obj); + if (encoded == NULL) + return -1; + return _steal_list_append(rval, encoded); + } + else if (PyFloat_Check(obj)) { + PyObject *encoded = encoder_encode_float(s, obj); + if (encoded == NULL) + return -1; + return _steal_list_append(rval, encoded); + } + else if (PyList_Check(obj) || PyTuple_Check(obj)) { + return encoder_listencode_list(s, rval, obj, indent_level); + } + else if (PyDict_Check(obj)) { + return encoder_listencode_dict(s, rval, obj, indent_level); + } + else { + PyObject *ident = NULL; + if (s->markers != Py_None) { + int has_key; + ident = PyLong_FromVoidPtr(obj); + if (ident == NULL) + return -1; + has_key = PyDict_Contains(s->markers, ident); + if (has_key) { + if (has_key != -1) + PyErr_SetString(PyExc_ValueError, "Circular reference detected"); + Py_DECREF(ident); + return -1; + } + if (PyDict_SetItem(s->markers, ident, obj)) { + Py_DECREF(ident); + return -1; + } + } + newobj = PyObject_CallFunctionObjArgs(s->defaultfn, obj, NULL); + if (newobj == NULL) { + Py_XDECREF(ident); + return -1; + } + rv = encoder_listencode_obj(s, rval, newobj, indent_level); + Py_DECREF(newobj); + if (rv) { + Py_XDECREF(ident); + return -1; + } + if (ident != NULL) { + if (PyDict_DelItem(s->markers, ident)) { + Py_XDECREF(ident); + return -1; + } + Py_XDECREF(ident); + } + return rv; + } +} + +static int +encoder_listencode_dict(PyEncoderObject *s, PyObject *rval, PyObject *dct, Py_ssize_t indent_level) +{ + /* Encode Python dict dct a JSON term, rval is a PyList */ + static PyObject *open_dict = NULL; + static PyObject *close_dict = NULL; + static PyObject *empty_dict = NULL; + PyObject *kstr = NULL; + PyObject *ident = NULL; + PyObject *key, *value; + Py_ssize_t pos; + int skipkeys; + Py_ssize_t idx; + + if (open_dict == NULL || close_dict == NULL || empty_dict == NULL) { + open_dict = PyString_InternFromString("{"); + close_dict = PyString_InternFromString("}"); + empty_dict = PyString_InternFromString("{}"); + if (open_dict == NULL || close_dict == NULL || empty_dict == NULL) + return -1; + } + if (PyDict_Size(dct) == 0) + return PyList_Append(rval, empty_dict); + + if (s->markers != Py_None) { + int has_key; + ident = PyLong_FromVoidPtr(dct); + if (ident == NULL) + goto bail; + has_key = PyDict_Contains(s->markers, ident); + if (has_key) { + if (has_key != -1) + PyErr_SetString(PyExc_ValueError, "Circular reference detected"); + goto bail; + } + if (PyDict_SetItem(s->markers, ident, dct)) { + goto bail; + } + } + + if (PyList_Append(rval, open_dict)) + goto bail; + + if (s->indent != Py_None) { + /* TODO: DOES NOT RUN */ + indent_level += 1; + /* + newline_indent = '\n' + (' ' * (_indent * _current_indent_level)) + separator = _item_separator + newline_indent + buf += newline_indent + */ + } + + /* TODO: C speedup not implemented for sort_keys */ + + pos = 0; + skipkeys = PyObject_IsTrue(s->skipkeys); + idx = 0; + while (PyDict_Next(dct, &pos, &key, &value)) { + PyObject *encoded; + + if (PyString_Check(key) || PyUnicode_Check(key)) { + Py_INCREF(key); + kstr = key; + } + else if (PyFloat_Check(key)) { + kstr = encoder_encode_float(s, key); + if (kstr == NULL) + goto bail; + } + else if (PyInt_Check(key) || PyLong_Check(key)) { + kstr = PyObject_Str(key); + if (kstr == NULL) + goto bail; + } + else if (key == Py_True || key == Py_False || key == Py_None) { + kstr = _encoded_const(key); + if (kstr == NULL) + goto bail; + } + else if (skipkeys) { + continue; + } + else { + /* TODO: include repr of key */ + PyErr_SetString(PyExc_ValueError, "keys must be a string"); + goto bail; + } + + if (idx) { + if (PyList_Append(rval, s->item_separator)) + goto bail; + } + + encoded = encoder_encode_string(s, kstr); + Py_CLEAR(kstr); + if (encoded == NULL) + goto bail; + if (PyList_Append(rval, encoded)) { + Py_DECREF(encoded); + goto bail; + } + Py_DECREF(encoded); + if (PyList_Append(rval, s->key_separator)) + goto bail; + if (encoder_listencode_obj(s, rval, value, indent_level)) + goto bail; + idx += 1; + } + if (ident != NULL) { + if (PyDict_DelItem(s->markers, ident)) + goto bail; + Py_CLEAR(ident); + } + if (s->indent != Py_None) { + /* TODO: DOES NOT RUN */ + indent_level -= 1; + /* + yield '\n' + (' ' * (_indent * _current_indent_level)) + */ + } + if (PyList_Append(rval, close_dict)) + goto bail; + return 0; + +bail: + Py_XDECREF(kstr); + Py_XDECREF(ident); + return -1; +} + + +static int +encoder_listencode_list(PyEncoderObject *s, PyObject *rval, PyObject *seq, Py_ssize_t indent_level) +{ + /* Encode Python list seq to a JSON term, rval is a PyList */ + static PyObject *open_array = NULL; + static PyObject *close_array = NULL; + static PyObject *empty_array = NULL; + PyObject *ident = NULL; + PyObject *s_fast = NULL; + Py_ssize_t num_items; + PyObject **seq_items; + Py_ssize_t i; + + if (open_array == NULL || close_array == NULL || empty_array == NULL) { + open_array = PyString_InternFromString("["); + close_array = PyString_InternFromString("]"); + empty_array = PyString_InternFromString("[]"); + if (open_array == NULL || close_array == NULL || empty_array == NULL) + return -1; + } + ident = NULL; + s_fast = PySequence_Fast(seq, "_iterencode_list needs a sequence"); + if (s_fast == NULL) + return -1; + num_items = PySequence_Fast_GET_SIZE(s_fast); + if (num_items == 0) { + Py_DECREF(s_fast); + return PyList_Append(rval, empty_array); + } + + if (s->markers != Py_None) { + int has_key; + ident = PyLong_FromVoidPtr(seq); + if (ident == NULL) + goto bail; + has_key = PyDict_Contains(s->markers, ident); + if (has_key) { + if (has_key != -1) + PyErr_SetString(PyExc_ValueError, "Circular reference detected"); + goto bail; + } + if (PyDict_SetItem(s->markers, ident, seq)) { + goto bail; + } + } + + seq_items = PySequence_Fast_ITEMS(s_fast); + if (PyList_Append(rval, open_array)) + goto bail; + if (s->indent != Py_None) { + /* TODO: DOES NOT RUN */ + indent_level += 1; + /* + newline_indent = '\n' + (' ' * (_indent * _current_indent_level)) + separator = _item_separator + newline_indent + buf += newline_indent + */ + } + for (i = 0; i < num_items; i++) { + PyObject *obj = seq_items[i]; + if (i) { + if (PyList_Append(rval, s->item_separator)) + goto bail; + } + if (encoder_listencode_obj(s, rval, obj, indent_level)) + goto bail; + } + if (ident != NULL) { + if (PyDict_DelItem(s->markers, ident)) + goto bail; + Py_CLEAR(ident); + } + if (s->indent != Py_None) { + /* TODO: DOES NOT RUN */ + indent_level -= 1; + /* + yield '\n' + (' ' * (_indent * _current_indent_level)) + */ + } + if (PyList_Append(rval, close_array)) + goto bail; + Py_DECREF(s_fast); + return 0; + +bail: + Py_XDECREF(ident); + Py_DECREF(s_fast); + return -1; +} + +static void +encoder_dealloc(PyObject *self) +{ + /* Deallocate Encoder */ + PyEncoderObject *s; + assert(PyEncoder_Check(self)); + s = (PyEncoderObject *)self; + Py_CLEAR(s->markers); + Py_CLEAR(s->defaultfn); + Py_CLEAR(s->encoder); + Py_CLEAR(s->indent); + Py_CLEAR(s->key_separator); + Py_CLEAR(s->item_separator); + Py_CLEAR(s->sort_keys); + Py_CLEAR(s->skipkeys); + self->ob_type->tp_free(self); +} + +PyDoc_STRVAR(encoder_doc, "_iterencode(obj, _current_indent_level) -> iterable"); + +static +PyTypeObject PyEncoderType = { + PyObject_HEAD_INIT(0) + 0, /* tp_internal */ + "Encoder", /* tp_name */ + sizeof(PyEncoderObject), /* tp_basicsize */ + 0, /* tp_itemsize */ + encoder_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + encoder_call, /* tp_call */ + 0, /* tp_str */ + 0,/* PyObject_GenericGetAttr, */ /* tp_getattro */ + 0,/* PyObject_GenericSetAttr, */ /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + encoder_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + encoder_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + encoder_init, /* tp_init */ + 0,/* PyType_GenericAlloc, */ /* tp_alloc */ + 0,/* PyType_GenericNew, */ /* tp_new */ + 0,/* _PyObject_Del, */ /* tp_free */ +}; + +static PyMethodDef speedups_methods[] = { + {"encode_basestring_ascii", + (PyCFunction)py_encode_basestring_ascii, + METH_O, + pydoc_encode_basestring_ascii}, + {"scanstring", + (PyCFunction)py_scanstring, + METH_VARARGS, + pydoc_scanstring}, + {NULL, NULL, 0, NULL} +}; + +PyDoc_STRVAR(module_doc, +"simplejson speedups\n"); + +void +init_speedups(void) +{ + PyObject *m; + PyScannerType.tp_getattro = PyObject_GenericGetAttr; + PyScannerType.tp_setattro = PyObject_GenericSetAttr; + PyScannerType.tp_alloc = PyType_GenericAlloc; + PyScannerType.tp_new = PyType_GenericNew; + PyScannerType.tp_free = _PyObject_Del; + if (PyType_Ready(&PyScannerType) < 0) + return; + PyEncoderType.tp_getattro = PyObject_GenericGetAttr; + PyEncoderType.tp_setattro = PyObject_GenericSetAttr; + PyEncoderType.tp_alloc = PyType_GenericAlloc; + PyEncoderType.tp_new = PyType_GenericNew; + PyEncoderType.tp_free = _PyObject_Del; + if (PyType_Ready(&PyEncoderType) < 0) + return; + m = Py_InitModule3("_speedups", speedups_methods, module_doc); + Py_INCREF((PyObject*)&PyScannerType); + PyModule_AddObject(m, "make_scanner", (PyObject*)&PyScannerType); + Py_INCREF((PyObject*)&PyEncoderType); + PyModule_AddObject(m, "make_encoder", (PyObject*)&PyEncoderType); +} diff --git a/syncerror/simplejson/decoder.py b/syncerror/simplejson/decoder.py new file mode 100644 index 0000000..de3911f --- /dev/null +++ b/syncerror/simplejson/decoder.py @@ -0,0 +1,352 @@ +"""Implementation of JSONDecoder +""" +import re +import sys +import struct + +from simplejson.scanner import make_scanner +try: + from simplejson._speedups import scanstring as c_scanstring +except ImportError: + c_scanstring = None + +__all__ = ['JSONDecoder'] + +FLAGS = re.VERBOSE | re.MULTILINE | re.DOTALL + +def _floatconstants(): + _BYTES = '7FF80000000000007FF0000000000000'.decode('hex') + if sys.byteorder != 'big': + _BYTES = _BYTES[:8][::-1] + _BYTES[8:][::-1] + nan, inf = struct.unpack('dd', _BYTES) + return nan, inf, -inf + +NaN, PosInf, NegInf = _floatconstants() + + +def linecol(doc, pos): + lineno = doc.count('\n', 0, pos) + 1 + if lineno == 1: + colno = pos + else: + colno = pos - doc.rindex('\n', 0, pos) + return lineno, colno + + +def errmsg(msg, doc, pos, end=None): + # Note that this function is called from _speedups + lineno, colno = linecol(doc, pos) + if end is None: + return '%s: line %d column %d (char %d)' % (msg, lineno, colno, pos) + endlineno, endcolno = linecol(doc, end) + return '%s: line %d column %d - line %d column %d (char %d - %d)' % ( + msg, lineno, colno, endlineno, endcolno, pos, end) + + +_CONSTANTS = { + '-Infinity': NegInf, + 'Infinity': PosInf, + 'NaN': NaN, +} + +STRINGCHUNK = re.compile(r'(.*?)(["\\\x00-\x1f])', FLAGS) +BACKSLASH = { + '"': u'"', '\\': u'\\', '/': u'/', + 'b': u'\b', 'f': u'\f', 'n': u'\n', 'r': u'\r', 't': u'\t', +} + +DEFAULT_ENCODING = "utf-8" + +def py_scanstring(s, end, encoding=None, strict=True, _b=BACKSLASH, _m=STRINGCHUNK.match): + """Scan the string s for a JSON string. End is the index of the + character in s after the quote that started the JSON string. + Unescapes all valid JSON string escape sequences and raises ValueError + on attempt to decode an invalid string. If strict is False then literal + control characters are allowed in the string. + + Returns a tuple of the decoded string and the index of the character in s + after the end quote.""" + if encoding is None: + encoding = DEFAULT_ENCODING + chunks = [] + _append = chunks.append + begin = end - 1 + while 1: + chunk = _m(s, end) + if chunk is None: + raise ValueError( + errmsg("Unterminated string starting at", s, begin)) + end = chunk.end() + content, terminator = chunk.groups() + # Content is contains zero or more unescaped string characters + if content: + if not isinstance(content, unicode): + content = unicode(content, encoding) + _append(content) + # Terminator is the end of string, a literal control character, + # or a backslash denoting that an escape sequence follows + if terminator == '"': + break + elif terminator != '\\': + if strict: + msg = "Invalid control character %r at" % (terminator,) + raise ValueError(msg, s, end) + else: + _append(terminator) + continue + try: + esc = s[end] + except IndexError: + raise ValueError( + errmsg("Unterminated string starting at", s, begin)) + # If not a unicode escape sequence, must be in the lookup table + if esc != 'u': + try: + char = _b[esc] + except KeyError: + raise ValueError( + errmsg("Invalid \\escape: %r" % (esc,), s, end)) + end += 1 + else: + # Unicode escape sequence + esc = s[end + 1:end + 5] + next_end = end + 5 + if len(esc) != 4: + msg = "Invalid \\uXXXX escape" + raise ValueError(errmsg(msg, s, end)) + uni = int(esc, 16) + # Check for surrogate pair on UCS-4 systems + if 0xd800 <= uni <= 0xdbff and sys.maxunicode > 65535: + msg = "Invalid \\uXXXX\\uXXXX surrogate pair" + if not s[end + 5:end + 7] == '\\u': + raise ValueError(errmsg(msg, s, end)) + esc2 = s[end + 7:end + 11] + if len(esc2) != 4: + raise ValueError(errmsg(msg, s, end)) + uni2 = int(esc2, 16) + uni = 0x10000 + (((uni - 0xd800) << 10) | (uni2 - 0xdc00)) + next_end += 6 + char = unichr(uni) + end = next_end + # Append the unescaped character + _append(char) + return u''.join(chunks), end + + +# Use speedup if available +scanstring = c_scanstring or py_scanstring + +WHITESPACE = re.compile(r'[ \t\n\r]*', FLAGS) +WHITESPACE_STR = ' \t\n\r' + +def JSONObject((s, end), encoding, strict, scan_once, object_hook, _w=WHITESPACE.match, _ws=WHITESPACE_STR): + counter = 0 + pairs = {} + # Use a slice to prevent IndexError from being raised, the following + # check will raise a more specific ValueError if the string is empty + nextchar = s[end:end + 1] + # Normally we expect nextchar == '"' + if nextchar != '"': + if nextchar in _ws: + end = _w(s, end).end() + nextchar = s[end:end + 1] + # Trivial empty object + if nextchar == '}': + return pairs, end + 1 + elif nextchar != '"': + raise ValueError(errmsg("Expecting property name", s, end)) + end += 1 + while True: + key, end = scanstring(s, end, encoding, strict) + + # To skip some function call overhead we optimize the fast paths where + # the JSON key separator is ": " or just ":". + if s[end:end + 1] != ':': + end = _w(s, end).end() + if s[end:end + 1] != ':': + raise ValueError(errmsg("Expecting : delimiter", s, end)) + + end += 1 + + try: + if s[end] in _ws: + end += 1 + if s[end] in _ws: + end = _w(s, end + 1).end() + except IndexError: + pass + + try: + value, end = scan_once(s, end) + except StopIteration: + raise ValueError(errmsg("Expecting object", s, end)) + + pairs['__%08x__-%s' % (counter, key)] = value + counter = counter + 1 + #pairs[key] = value + + try: + nextchar = s[end] + if nextchar in _ws: + end = _w(s, end + 1).end() + nextchar = s[end] + except IndexError: + nextchar = '' + end += 1 + + if nextchar == '}': + break + elif nextchar != ',': + raise ValueError(errmsg("Expecting , delimiter", s, end - 1)) + + try: + nextchar = s[end] + if nextchar in _ws: + end += 1 + nextchar = s[end] + if nextchar in _ws: + end = _w(s, end + 1).end() + nextchar = s[end] + except IndexError: + nextchar = '' + + end += 1 + if nextchar != '"': + raise ValueError(errmsg("Expecting property name", s, end - 1)) + + if object_hook is not None: + pairs = object_hook(pairs) + return pairs, end + +def JSONArray((s, end), scan_once, _w=WHITESPACE.match, _ws=WHITESPACE_STR): + values = [] + nextchar = s[end:end + 1] + if nextchar in _ws: + end = _w(s, end + 1).end() + nextchar = s[end:end + 1] + # Look-ahead for trivial empty array + if nextchar == ']': + return values, end + 1 + _append = values.append + while True: + try: + value, end = scan_once(s, end) + except StopIteration: + raise ValueError(errmsg("Expecting object", s, end)) + _append(value) + nextchar = s[end:end + 1] + if nextchar in _ws: + end = _w(s, end + 1).end() + nextchar = s[end:end + 1] + end += 1 + if nextchar == ']': + break + elif nextchar != ',': + raise ValueError(errmsg("Expecting , delimiter", s, end)) + + try: + if s[end] in _ws: + end += 1 + if s[end] in _ws: + end = _w(s, end + 1).end() + except IndexError: + pass + + return values, end + +class JSONDecoder(object): + """Simple JSON decoder + + Performs the following translations in decoding by default: + + +---------------+-------------------+ + | JSON | Python | + +===============+===================+ + | object | dict | + +---------------+-------------------+ + | array | list | + +---------------+-------------------+ + | string | unicode | + +---------------+-------------------+ + | number (int) | int, long | + +---------------+-------------------+ + | number (real) | float | + +---------------+-------------------+ + | true | True | + +---------------+-------------------+ + | false | False | + +---------------+-------------------+ + | null | None | + +---------------+-------------------+ + + It also understands ``NaN``, ``Infinity``, and ``-Infinity`` as + their corresponding ``float`` values, which is outside the JSON spec. + + """ + + def __init__(self, encoding=None, object_hook=None, parse_float=None, + parse_int=None, parse_constant=None, strict=True): + """``encoding`` determines the encoding used to interpret any ``str`` + objects decoded by this instance (utf-8 by default). It has no + effect when decoding ``unicode`` objects. + + Note that currently only encodings that are a superset of ASCII work, + strings of other encodings should be passed in as ``unicode``. + + ``object_hook``, if specified, will be called with the result + of every JSON object decoded and its return value will be used in + place of the given ``dict``. This can be used to provide custom + deserializations (e.g. to support JSON-RPC class hinting). + + ``parse_float``, if specified, will be called with the string + of every JSON float to be decoded. By default this is equivalent to + float(num_str). This can be used to use another datatype or parser + for JSON floats (e.g. decimal.Decimal). + + ``parse_int``, if specified, will be called with the string + of every JSON int to be decoded. By default this is equivalent to + int(num_str). This can be used to use another datatype or parser + for JSON integers (e.g. float). + + ``parse_constant``, if specified, will be called with one of the + following strings: -Infinity, Infinity, NaN. + This can be used to raise an exception if invalid JSON numbers + are encountered. + + """ + self.encoding = encoding + self.object_hook = object_hook + self.parse_float = parse_float or float + self.parse_int = parse_int or int + self.parse_constant = parse_constant or _CONSTANTS.__getitem__ + self.strict = strict + self.parse_object = JSONObject + self.parse_array = JSONArray + self.parse_string = scanstring + self.scan_once = make_scanner(self) + + def decode(self, s, _w=WHITESPACE.match): + """Return the Python representation of ``s`` (a ``str`` or ``unicode`` + instance containing a JSON document) + + """ + obj, end = self.raw_decode(s, idx=_w(s, 0).end()) + end = _w(s, end).end() + if end != len(s): + raise ValueError(errmsg("Extra data", s, end, len(s))) + return obj + + def raw_decode(self, s, idx=0): + """Decode a JSON document from ``s`` (a ``str`` or ``unicode`` beginning + with a JSON document) and return a 2-tuple of the Python + representation and the index in ``s`` where the document ended. + + This can be used to decode a JSON document from a string that may + have extraneous data at the end. + + """ + try: + obj, end = self.scan_once(s, idx) + except StopIteration: + raise ValueError("No JSON object could be decoded") + return obj, end diff --git a/syncerror/simplejson/encoder.py b/syncerror/simplejson/encoder.py new file mode 100644 index 0000000..d28298f --- /dev/null +++ b/syncerror/simplejson/encoder.py @@ -0,0 +1,436 @@ +"""Implementation of JSONEncoder +""" +import re + +try: + from simplejson._speedups import encode_basestring_ascii as c_encode_basestring_ascii +except ImportError: + c_encode_basestring_ascii = None +try: + from simplejson._speedups import make_encoder as c_make_encoder +except ImportError: + c_make_encoder = None + +ESCAPE = re.compile(r'[\x00-\x1f\\"\b\f\n\r\t]') +ESCAPE_ASCII = re.compile(r'([\\"]|[^\ -~])') +HAS_UTF8 = re.compile(r'[\x80-\xff]') +ESCAPE_DCT = { + '\\': '\\\\', + '"': '\\"', + '\b': '\\b', + '\f': '\\f', + '\n': '\\n', + '\r': '\\r', + '\t': '\\t', +} +for i in range(0x20): + ESCAPE_DCT.setdefault(chr(i), '\\u%04x' % (i,)) + +# Assume this produces an infinity on all machines (probably not guaranteed) +INFINITY = float('1e66666') +FLOAT_REPR = repr + +def encode_basestring(s): + """Return a JSON representation of a Python string + + """ + def replace(match): + return ESCAPE_DCT[match.group(0)] + return '"' + ESCAPE.sub(replace, s) + '"' + + +def py_encode_basestring_ascii(s): + """Return an ASCII-only JSON representation of a Python string + + """ + if isinstance(s, str) and HAS_UTF8.search(s) is not None: + s = s.decode('utf-8') + def replace(match): + s = match.group(0) + try: + return ESCAPE_DCT[s] + except KeyError: + n = ord(s) + if n < 0x10000: + return '\\u%04x' % (n,) + else: + # surrogate pair + n -= 0x10000 + s1 = 0xd800 | ((n >> 10) & 0x3ff) + s2 = 0xdc00 | (n & 0x3ff) + return '\\u%04x\\u%04x' % (s1, s2) + return '"' + str(ESCAPE_ASCII.sub(replace, s)) + '"' + + +encode_basestring_ascii = c_encode_basestring_ascii or py_encode_basestring_ascii + +class JSONEncoder(object): + """Extensible JSON encoder for Python data structures. + + Supports the following objects and types by default: + + +-------------------+---------------+ + | Python | JSON | + +===================+===============+ + | dict | object | + +-------------------+---------------+ + | list, tuple | array | + +-------------------+---------------+ + | str, unicode | string | + +-------------------+---------------+ + | int, long, float | number | + +-------------------+---------------+ + | True | true | + +-------------------+---------------+ + | False | false | + +-------------------+---------------+ + | None | null | + +-------------------+---------------+ + + To extend this to recognize other objects, subclass and implement a + ``.default()`` method with another method that returns a serializable + object for ``o`` if possible, otherwise it should call the superclass + implementation (to raise ``TypeError``). + + """ + item_separator = ', ' + key_separator = ': ' + def __init__(self, skipkeys=False, ensure_ascii=True, + check_circular=True, allow_nan=True, sort_keys=False, + indent=None, separators=None, encoding='utf-8', default=None): + """Constructor for JSONEncoder, with sensible defaults. + + If skipkeys is False, then it is a TypeError to attempt + encoding of keys that are not str, int, long, float or None. If + skipkeys is True, such items are simply skipped. + + If ensure_ascii is True, the output is guaranteed to be str + objects with all incoming unicode characters escaped. If + ensure_ascii is false, the output will be unicode object. + + If check_circular is True, then lists, dicts, and custom encoded + objects will be checked for circular references during encoding to + prevent an infinite recursion (which would cause an OverflowError). + Otherwise, no such check takes place. + + If allow_nan is True, then NaN, Infinity, and -Infinity will be + encoded as such. This behavior is not JSON specification compliant, + but is consistent with most JavaScript based encoders and decoders. + Otherwise, it will be a ValueError to encode such floats. + + If sort_keys is True, then the output of dictionaries will be + sorted by key; this is useful for regression tests to ensure + that JSON serializations can be compared on a day-to-day basis. + + If indent is a non-negative integer, then JSON array + elements and object members will be pretty-printed with that + indent level. An indent level of 0 will only insert newlines. + None is the most compact representation. + + If specified, separators should be a (item_separator, key_separator) + tuple. The default is (', ', ': '). To get the most compact JSON + representation you should specify (',', ':') to eliminate whitespace. + + If specified, default is a function that gets called for objects + that can't otherwise be serialized. It should return a JSON encodable + version of the object or raise a ``TypeError``. + + If encoding is not None, then all input strings will be + transformed into unicode using that encoding prior to JSON-encoding. + The default is UTF-8. + + """ + + self.skipkeys = skipkeys + self.ensure_ascii = ensure_ascii + self.check_circular = check_circular + self.allow_nan = allow_nan + self.sort_keys = sort_keys + self.indent = indent + if separators is not None: + self.item_separator, self.key_separator = separators + if default is not None: + self.default = default + self.encoding = encoding + + def default(self, o): + """Implement this method in a subclass such that it returns + a serializable object for ``o``, or calls the base implementation + (to raise a ``TypeError``). + + For example, to support arbitrary iterators, you could + implement default like this:: + + def default(self, o): + try: + iterable = iter(o) + except TypeError: + pass + else: + return list(iterable) + return JSONEncoder.default(self, o) + + """ + raise TypeError("%r is not JSON serializable" % (o,)) + + def encode(self, o): + """Return a JSON string representation of a Python data structure. + + >>> JSONEncoder().encode({"foo": ["bar", "baz"]}) + '{"foo": ["bar", "baz"]}' + + """ + # This is for extremely simple cases and benchmarks. + if isinstance(o, basestring): + if isinstance(o, str): + _encoding = self.encoding + if (_encoding is not None + and not (_encoding == 'utf-8')): + o = o.decode(_encoding) + if self.ensure_ascii: + return encode_basestring_ascii(o) + else: + return encode_basestring(o) + # This doesn't pass the iterator directly to ''.join() because the + # exceptions aren't as detailed. The list call should be roughly + # equivalent to the PySequence_Fast that ''.join() would do. + chunks = self.iterencode(o, _one_shot=True) + if not isinstance(chunks, (list, tuple)): + chunks = list(chunks) + return ''.join(chunks) + + def iterencode(self, o, _one_shot=False): + """Encode the given object and yield each string + representation as available. + + For example:: + + for chunk in JSONEncoder().iterencode(bigobject): + mysocket.write(chunk) + + """ + if self.check_circular: + markers = {} + else: + markers = None + if self.ensure_ascii: + _encoder = encode_basestring_ascii + else: + _encoder = encode_basestring + if self.encoding != 'utf-8': + def _encoder(o, _orig_encoder=_encoder, _encoding=self.encoding): + if isinstance(o, str): + o = o.decode(_encoding) + return _orig_encoder(o) + + def floatstr(o, allow_nan=self.allow_nan, _repr=FLOAT_REPR, _inf=INFINITY, _neginf=-INFINITY): + # Check for specials. Note that this type of test is processor- and/or + # platform-specific, so do tests which don't depend on the internals. + + if o != o: + text = 'NaN' + elif o == _inf: + text = 'Infinity' + elif o == _neginf: + text = '-Infinity' + else: + return _repr(o) + + if not allow_nan: + raise ValueError("Out of range float values are not JSON compliant: %r" + % (o,)) + + return text + + + if _one_shot and c_make_encoder is not None and not self.indent and not self.sort_keys: + _iterencode = c_make_encoder( + markers, self.default, _encoder, self.indent, + self.key_separator, self.item_separator, self.sort_keys, + self.skipkeys, self.allow_nan) + else: + _iterencode = _make_iterencode( + markers, self.default, _encoder, self.indent, floatstr, + self.key_separator, self.item_separator, self.sort_keys, + self.skipkeys, _one_shot) + return _iterencode(o, 0) + +def _make_iterencode(markers, _default, _encoder, _indent, _floatstr, _key_separator, _item_separator, _sort_keys, _skipkeys, _one_shot, + ## HACK: hand-optimized bytecode; turn globals into locals + False=False, + True=True, + ValueError=ValueError, + basestring=basestring, + dict=dict, + float=float, + id=id, + int=int, + isinstance=isinstance, + list=list, + long=long, + str=str, + tuple=tuple, + ): + + def _iterencode_list(lst, _current_indent_level): + if not lst: + yield '[]' + return + if markers is not None: + markerid = id(lst) + if markerid in markers: + raise ValueError("Circular reference detected") + markers[markerid] = lst + buf = '[' + if _indent is not None: + _current_indent_level += 1 + newline_indent = '\n' + (' ' * (_indent * _current_indent_level)) + separator = _item_separator + newline_indent + buf += newline_indent + else: + newline_indent = None + separator = _item_separator + first = True + for value in lst: + if first: + first = False + else: + buf = separator + if isinstance(value, basestring): + yield buf + _encoder(value) + elif value is None: + yield buf + 'null' + elif value is True: + yield buf + 'true' + elif value is False: + yield buf + 'false' + elif isinstance(value, (int, long)): + yield buf + str(value) + elif isinstance(value, float): + yield buf + _floatstr(value) + else: + yield buf + if isinstance(value, (list, tuple)): + chunks = _iterencode_list(value, _current_indent_level) + elif isinstance(value, dict): + chunks = _iterencode_dict(value, _current_indent_level) + else: + chunks = _iterencode(value, _current_indent_level) + for chunk in chunks: + yield chunk + if newline_indent is not None: + _current_indent_level -= 1 + yield '\n' + (' ' * (_indent * _current_indent_level)) + yield ']' + if markers is not None: + del markers[markerid] + + def _iterencode_dict(dct, _current_indent_level): + if not dct: + yield '{}' + return + if markers is not None: + markerid = id(dct) + if markerid in markers: + raise ValueError("Circular reference detected") + markers[markerid] = dct + yield '{' + if _indent is not None: + _current_indent_level += 1 + newline_indent = '\n' + (' ' * (_indent * _current_indent_level)) + item_separator = _item_separator + newline_indent + yield newline_indent + else: + newline_indent = None + item_separator = _item_separator + first = True + if _sort_keys: + items = dct.items() + items.sort(key=lambda kv: kv[0]) + else: + items = dct.iteritems() + for key, value in items: + if isinstance(key, basestring): + pass + # JavaScript is weakly typed for these, so it makes sense to + # also allow them. Many encoders seem to do something like this. + elif isinstance(key, float): + key = _floatstr(key) + elif isinstance(key, (int, long)): + key = str(key) + elif key is True: + key = 'true' + elif key is False: + key = 'false' + elif key is None: + key = 'null' + elif _skipkeys: + continue + else: + raise TypeError("key %r is not a string" % (key,)) + if first: + first = False + else: + yield item_separator + yield _encoder(key) + yield _key_separator + if isinstance(value, basestring): + yield _encoder(value) + elif value is None: + yield 'null' + elif value is True: + yield 'true' + elif value is False: + yield 'false' + elif isinstance(value, (int, long)): + yield str(value) + elif isinstance(value, float): + yield _floatstr(value) + else: + if isinstance(value, (list, tuple)): + chunks = _iterencode_list(value, _current_indent_level) + elif isinstance(value, dict): + chunks = _iterencode_dict(value, _current_indent_level) + else: + chunks = _iterencode(value, _current_indent_level) + for chunk in chunks: + yield chunk + if newline_indent is not None: + _current_indent_level -= 1 + yield '\n' + (' ' * (_indent * _current_indent_level)) + yield '}' + if markers is not None: + del markers[markerid] + + def _iterencode(o, _current_indent_level): + if isinstance(o, basestring): + yield _encoder(o) + elif o is None: + yield 'null' + elif o is True: + yield 'true' + elif o is False: + yield 'false' + elif isinstance(o, (int, long)): + yield str(o) + elif isinstance(o, float): + yield _floatstr(o) + elif isinstance(o, (list, tuple)): + for chunk in _iterencode_list(o, _current_indent_level): + yield chunk + elif isinstance(o, dict): + for chunk in _iterencode_dict(o, _current_indent_level): + yield chunk + else: + if markers is not None: + markerid = id(o) + if markerid in markers: + raise ValueError("Circular reference detected") + markers[markerid] = o + o = _default(o) + for chunk in _iterencode(o, _current_indent_level): + yield chunk + if markers is not None: + del markers[markerid] + + return _iterencode diff --git a/syncerror/simplejson/scanner.py b/syncerror/simplejson/scanner.py new file mode 100644 index 0000000..adbc6ec --- /dev/null +++ b/syncerror/simplejson/scanner.py @@ -0,0 +1,65 @@ +"""JSON token scanner +""" +import re +try: + from simplejson._speedups import make_scanner as c_make_scanner +except ImportError: + c_make_scanner = None + +__all__ = ['make_scanner'] + +NUMBER_RE = re.compile( + r'(-?(?:0|[1-9]\d*))(\.\d+)?([eE][-+]?\d+)?', + (re.VERBOSE | re.MULTILINE | re.DOTALL)) + +def py_make_scanner(context): + parse_object = context.parse_object + parse_array = context.parse_array + parse_string = context.parse_string + match_number = NUMBER_RE.match + encoding = context.encoding + strict = context.strict + parse_float = context.parse_float + parse_int = context.parse_int + parse_constant = context.parse_constant + object_hook = context.object_hook + + def _scan_once(string, idx): + try: + nextchar = string[idx] + except IndexError: + raise StopIteration + + if nextchar == '"': + return parse_string(string, idx + 1, encoding, strict) + elif nextchar == '{': + return parse_object((string, idx + 1), encoding, strict, _scan_once, object_hook) + elif nextchar == '[': + return parse_array((string, idx + 1), _scan_once) + elif nextchar == 'n' and string[idx:idx + 4] == 'null': + return None, idx + 4 + elif nextchar == 't' and string[idx:idx + 4] == 'true': + return True, idx + 4 + elif nextchar == 'f' and string[idx:idx + 5] == 'false': + return False, idx + 5 + + m = match_number(string, idx) + if m is not None: + integer, frac, exp = m.groups() + if frac or exp: + res = parse_float(integer + (frac or '') + (exp or '')) + else: + res = parse_int(integer) + return res, m.end() + elif nextchar == 'N' and string[idx:idx + 3] == 'NaN': + return parse_constant('NaN'), idx + 3 + elif nextchar == 'I' and string[idx:idx + 8] == 'Infinity': + return parse_constant('Infinity'), idx + 8 + elif nextchar == '-' and string[idx:idx + 9] == '-Infinity': + return parse_constant('-Infinity'), idx + 9 + else: + raise StopIteration + + return _scan_once + +make_scanner = c_make_scanner or py_make_scanner diff --git a/syncerror/simplejson/tool.py b/syncerror/simplejson/tool.py new file mode 100644 index 0000000..b0ca9d1 --- /dev/null +++ b/syncerror/simplejson/tool.py @@ -0,0 +1,35 @@ +r"""Using simplejson from the shell to validate and +pretty-print:: + + $ echo '{"json":"obj"}' | python -msimplejson.tool + { + "json": "obj" + } + $ echo '{ 1.2:3.4}' | python -msimplejson.tool + Expecting property name: line 1 column 2 (char 2) +""" +import simplejson + +def main(): + import sys + if len(sys.argv) == 1: + infile = sys.stdin + outfile = sys.stdout + elif len(sys.argv) == 2: + infile = open(sys.argv[1], 'rb') + outfile = sys.stdout + elif len(sys.argv) == 3: + infile = open(sys.argv[1], 'rb') + outfile = open(sys.argv[2], 'wb') + else: + raise SystemExit("%s [infile [outfile]]" % (sys.argv[0],)) + try: + obj = simplejson.load(infile) + except ValueError, e: + raise SystemExit(e) + simplejson.dump(obj, outfile, sort_keys=True, indent=4) + outfile.write('\n') + + +if __name__ == '__main__': + main() diff --git a/syncerror/sync.py b/syncerror/sync.py new file mode 100644 index 0000000..f4d94c7 --- /dev/null +++ b/syncerror/sync.py @@ -0,0 +1,129 @@ +import simplejson as json +import sys +import re +import os + +# Uses a customized version of simplejson, because the dump json has entries +# with the same key, and the order of the keys is important. The customized +# version adds a counter to each key name, for uniqueness, and to recover +# order by sorting. Then at pretty print time, this is removed. Hacky but it +# works. + +sys.path.insert(0, '.') + +class Frame: + def __init__(self, d): + self.d = d + + def GetHash(self): + keys = self.d.keys() + keys.sort() + for key in keys: + if key[13:] == 'hash': + return self.d[key] + return None + + def GetUpdate(self): + keys = self.d.keys() + keys.sort() + for key in keys: + if key[13:] == 'updates': + return int(self.d[key]) + return 0 + + def PrettyPrint(self): + # Example: "__00000000__-updates": "9951", + s = json.dumps(self.d, sort_keys=True, indent=4) + p = re.compile('^.*(?P__.*__-).*$') + lines = [] + for line in s.splitlines(): + line = line.rstrip() + m = p.match(line) + if not m: + lines.append(line) + else: + index = line.find(m.group('remove')) + line2 = line[0:index] + line[index+len(m.group('remove')):] + lines.append(line2) + return '\n'.join(lines) + +class Dump: + def __init__(self, filename): + f = file(filename) + self.j = json.loads(f.read().rstrip('\x00')) + f.close() + + def GetFrameNumbers(self): + numbers = [] + keys = self.j.keys() + keys.sort() + for key in keys: + if key[13:].startswith('frame'): + numbers.append(int(key[18:])) + return numbers + + def GetFrame(self, framenumber): + keys = self.j.keys() + keys.sort() + for key in keys: + if key[13:].startswith('frame'): + if framenumber == int(key[18:]): + return Frame(self.j[key]) + return None + +class Differ: + def __init__(self, file0, file1): + self.badupdate = -1 + + try: + dump0 = Dump(file0) + dump1 = Dump(file1) + + for number in dump0.GetFrameNumbers(): + frame0 = dump0.GetFrame(number) + frame1 = dump1.GetFrame(number) + if not frame0: + break + if not frame1: + break + if frame0.GetHash() != frame1.GetHash(): + self.badupdate = frame0.GetUpdate() + self.frame0 = frame0 + self.frame1 = frame1 + break + except: + print 'ERROR loading %s or %s' % (file0, file1) + + def GetBadUpdateNumber(self): + return self.badupdate + + def Diff(self, frame0file, frame1file, outfile): + f = file(frame0file, 'w') + f.write(self.frame0.PrettyPrint()) + f.close() + + f = file(frame1file, 'w') + f.write(self.frame1.PrettyPrint()) + f.close() + + os.system('diff %s %s > %s' % (frame0file, frame1file, outfile)) + +if __name__ == '__main__': + # Find the frame that is not matching + + d = Differ(sys.argv[1], sys.argv[2]) + + out0 = '/tmp/foo' + if len(sys.argv) > 3: + out0 = sys.argv[3] + + out1 = '/tmp/bar' + if len(sys.argv) > 4: + out1 = sys.argv[4] + + d.Diff(out0, out1, '/tmp/diff') + + f = file('/tmp/diff') + for line in f.read().splitlines(): + print line + f.close() diff --git a/texpack/sidemap.py b/texpack/sidemap.py new file mode 100644 index 0000000..06bd425 --- /dev/null +++ b/texpack/sidemap.py @@ -0,0 +1,78 @@ +import sys +import Image +import colorsys + +class Sidemap: + def __init__(self): + pass + + def shift_hue(self, i0, shift): + width = i0.size[0] + height = i0.size[1] + ir = Image.new('RGBA', (width, height)) + + for y in xrange(height): + for x in xrange(width): + p0 = i0.getpixel((x, y)) + hsl = colorsys.rgb_to_hls(p0[0] / 255.0, p0[1] / 255.0, p0[2] / 255.0) + huenew = hsl[0] + (shift / 255.0) + if huenew > 1.0: + huenew = huenew - 1.0 + rgb = colorsys.hls_to_rgb(huenew, hsl[1], hsl[2]) + rgba = (int(rgb[0] * 255.0), int(rgb[1] * 255.0), int(rgb[2] * 255.0), p0[3]) + ir.putpixel((x, y), rgba) + + return ir + + def image_add(self, i0, i1): + width = i0.size[0] + height = i0.size[1] + ir = Image.new('RGBA', (width, height)) + for y in xrange(height): + for x in xrange(width): + p0 = i0.getpixel((x, y)) + p1 = i1.getpixel((x, y)) + r = p0[0] + p1[0] + if r > 255: + r = 255 + g = p0[1] + p1[1] + if g > 255: + g = 255 + b = p0[2] + p1[2] + if b > 255: + b = 255 + a = p0[3] + ir.putpixel((x, y), (r, g, b, a)) + return ir + + def image_subtract(self, i0, i1): + width = i0.size[0] + height = i0.size[1] + ir = Image.new('RGBA', (width, height)) + for y in xrange(height): + for x in xrange(width): + p0 = i0.getpixel((x, y)) + p1 = i1.getpixel((x, y)) + r = p0[0] - p1[0] + if r < 0: + r = 0 + g = p0[1] - p1[1] + if g < 0: + g = 0 + b = p0[2] - p1[2] + if b < 0: + b = 0 + a = p0[3] + ir.putpixel((x, y), (r, g, b, a)) + return ir + + def gen_unit_image(self, white, black, hue): + sidepixels = self.image_subtract(white, black) + sidecolors = self.shift_hue(sidepixels, hue) + return self.image_add(black, sidecolors) + + def shift_file(self, whitefile, blackfile, hue, outfile): + white = Image.open(whitefile).convert('RGBA') + black = Image.open(blackfile).convert('RGBA') + result = self.gen_unit_image(white, black, hue) + result.save(outfile) diff --git a/texpack/subtract.py b/texpack/subtract.py new file mode 100644 index 0000000..20fd92e --- /dev/null +++ b/texpack/subtract.py @@ -0,0 +1,86 @@ +import sys +import Image +import colorsys + +def shift_hue(i0, shift): + width = i0.size[0] + height = i0.size[1] + ir = Image.new('RGBA', (width, height)) + + for y in xrange(height): + for x in xrange(width): + p0 = i0.getpixel((x, y)) + hsl = colorsys.rgb_to_hls(p0[0] / 255.0, p0[1] / 255.0, p0[2] / 255.0) + huenew = hsl[0] + (shift / 255.0) + if huenew > 1.0: + huenew = huenew - 1.0 + rgb = colorsys.hls_to_rgb(huenew, hsl[1], hsl[2]) + rgba = (int(rgb[0] * 255.0), int(rgb[1] * 255.0), int(rgb[2] * 255.0), p0[3]) + ir.putpixel((x, y), rgba) + + return ir + +def image_add(i0, i1): + width = i0.size[0] + height = i0.size[1] + ir = Image.new('RGBA', (width, height)) + + for y in xrange(height): + for x in xrange(width): + p0 = i0.getpixel((x, y)) + p1 = i1.getpixel((x, y)) + r = p0[0] + p1[0] + if r > 255: + r = 255 + g = p0[1] + p1[1] + if g > 255: + g = 255 + b = p0[2] + p1[2] + if b > 255: + b = 255 + a = p0[3] + ir.putpixel((x, y), (r, g, b, a)) + + return ir + +if __name__ == "__main__": + i0 = Image.open(sys.argv[1]).convert('RGBA') + i1 = Image.open(sys.argv[2]).convert('RGBA') + + width = i0.size[0] + height = i0.size[1] + print width, height + + ir = Image.new('RGBA', (width, height)) + + for y in xrange(height): + for x in xrange(width): + p0 = i0.getpixel((x, y)) + p1 = i1.getpixel((x, y)) + + r = p0[0] - p1[0] + if r < 0: + r = 0 + g = p0[1] - p1[1] + if g < 0: + g = 0 + b = p0[2] - p1[2] + if b < 0: + b = 0 + a = p0[3] + ir.putpixel((x, y), (r, g, b, a)) + + bigwidth = width * 16 + bigheight = height * 16 + ibig = Image.new('RGBA', (bigwidth, bigheight)) + + hueshift = 0 + for ys in xrange(0, bigheight, width): + for xs in xrange(0, bigwidth, width): + inew = shift_hue(ir, hueshift) + inew = image_add(i1, inew) + ibig.paste(inew, (xs, ys, xs + width, ys + height)) + print hueshift + hueshift += 1 + + ibig.save('result.png') diff --git a/texpack/texpack.py b/texpack/texpack.py new file mode 100644 index 0000000..77cf524 --- /dev/null +++ b/texpack/texpack.py @@ -0,0 +1,270 @@ +#!/usr/bin/python + +import math +import copy +import random +import Image +import ImageDraw +import sys + + +class RectBase(object): + def __init__(self): + self.x = 0 + self.y = 0 + self.width = 0 + self.height = 0 + + def area(self): + return self.width * self.height + + +class Rectangle(RectBase): + def __init__(self, width, height, image): + super(Rectangle,self).__init__() + + self.width = width + self.height = height + self.image = image + + +class Packer: + def __init__(self, width, height, search_resolution): + self.rect_list = [] + self.packed_rect_list = [] + self.search_step = 0 + self.area_covered = 0 + + self.width = width + self.height = height + + self.image = Image.new("RGBA", (width, height)) + self.draw = ImageDraw.ImageDraw(self.image) + + self.search_resolution = search_resolution + + def add_rect(self, width, height, image): + self.rect_list.append(Rectangle(width, height, image)) + + def compile(self): + # sort rectangles by surface area + self._sort_rects() + + # generate node tree + #self._build_tree(self.node) + self._build_tree((0 ,0, self.width, self.height)) + + # returns list of rectangles that did not fit + return self.rect_list + + def save_image(self, filename): + a = 0 + for r in self.packed_rect_list: + #self.draw.rectangle(((r.x, r.y), (r.x + r.width-1, r.y + r.height-1)), fill=r.color, outline=(r.color[0]*2/3, r.color[1]*2/3, r.color[2]*2/3)) + #self.draw.rectangle(((r.x, r.y), (r.x + r.width-1, r.y + r.height-1)), fill=r.color, outline=((255+r.color[0])/2, (255+r.color[1])/2, (255+r.color[2])/2)) + + #self.draw.bitmap((r.x, r.y), r.image) + self.image.paste(r.image, (r.x, r.y, r.x+r.width, r.y+r.height)) + a += r.area() + + self.image.save(filename) + print float(a) / float(self.width * self.height) + + def _sort_rects(self): + self.rect_list.sort(lambda x, y: cmp(x.area(), y.area()), reverse=True) + #self.rect_list.sort(lambda x, y: cmp(x.area()+(x.width/x.height), y.area()+(y.width/y.height)), reverse=True) + + def _build_tree(self, free_space): + global search_step, search_resolution + + if free_space[2] <= 0 or free_space[3] <= 0: + return + + if len(self.rect_list) == 0: + return + + #self.draw.rectangle(((free_space[0], free_space[1]), (free_space[0] + free_space[2], free_space[1] + free_space[3])), fill=None, outline=(255,255,255)) + + # Find rectangle that fits in current node + rect_index = 0 + done = False + step = max(rect_index + len(self.rect_list) / self.search_resolution, 1) + while not done: + if (self.rect_list[rect_index].width <= free_space[2]) and (self.rect_list[rect_index].height <= free_space[3]): + done = True + else: + self.search_step += 1 + rect_index += step + #rect_index = max(rect_index + len(self.rect_list) / 20, 1) + if rect_index >= len(self.rect_list): + return + + #print rect_index + + # Move rectangle from rect_list to packed_rect_list + rect = self.rect_list.pop(rect_index) + self.packed_rect_list.append(rect) + + # Set rectangle x, y + rect.x = free_space[0] + rect.y = free_space[1] + + self.area_covered += rect.width * rect.height + + # Determine cutting direction (horizontal or vertical) + # Split current node + if (free_space[2] - rect.height) > (free_space[3] - rect.width): + # cut into two nodes side-by-side + # Shrink first node of spit nodes + # call _build_tree for each new node + #self._build_tree((free_space[0] + rect.width, free_space[1], free_space[2] - rect.width, free_space[3])) + self._build_tree((free_space[0], free_space[1] + rect.height, rect.width, free_space[3] - rect.height)) + self._build_tree((free_space[0] + rect.width, free_space[1], free_space[2] - rect.width, free_space[3])) + else: + # cut into two nodes one on top of the other + # Shrink first node of spit nodes + # call _build_tree for each new node + #self._build_tree((free_space[0], free_space[1] + rect.height, free_space[2], free_space[3] - rect.height)) + self._build_tree((free_space[0] + rect.width, free_space[1], free_space[2] - rect.width, rect.height)) + self._build_tree((free_space[0], free_space[1] + rect.height, free_space[2], free_space[3] - rect.height)) + + +def special_convert(i, is_transparent): + # Find a tight bounds, create a new image from this + # Convert magenta to alpha transparent + + w = i.size[0] + h = i.size[1] + + # top + t = 0 + stop = False + for y in xrange(h): + for x in xrange(w): + p = i.getpixel((x,y)) + if not is_transparent(p): + stop = True + break + if stop: + break + t = y + 1 + + # left + l = 0 + stop = False + for x in xrange(w): + for y in xrange(h): + p = i.getpixel((x,y)) + if not is_transparent(p): + stop = True + break + if stop: + break + l = x + 1 + + # right + r = w + stop = False + for x in xrange(w-1, -1, -1): + for y in xrange(h): + p = i.getpixel((x,y)) + if not is_transparent(p): + stop = True + break + if stop: + break + r = x - 1 + + # bottom + b = h + stop = False + for y in xrange(h-1, -1, -1): + for x in xrange(w): + p = i.getpixel((x,y)) + if not is_transparent(p): + stop = True + break + if stop: + break + b = y - 1 + + # crop + i = i.crop((l, t, r, b)) + w = i.size[0] + h = i.size[1] + + # convert transparent + for y in xrange(h): + for x in xrange(w): + p = i.getpixel((x,y)) + if is_transparent(p): + i.putpixel((x,y), (0, 0, 0, 0)) + + # convert shadow - black at 40% + for y in xrange(h): + for x in xrange(w): + p = i.getpixel((x,y)) + if p == (156, 212, 248, 255): + i.putpixel((x,y), (0, 0, 0, 102)) + + # resize + w = w * 32 / 24 + h = h * 32 / 24 + i = i.resize((w,h), resample=1) + + return i + +def is_transparent_color(p): + if p == (255, 0, 255, 255): + return True + return False + +def is_not_side_color(p): + if p == (0, 116, 232, 255): + return False + if p == (0, 96, 196, 255): + return False + if p == (0, 64, 120, 255): + return False + if p == (0, 32, 64, 255): + return False + return True + +def get_hash(i): + h = 0 + for y in xrange(i.size[1]): + for x in xrange(i.size[0]): + p = i.getpixel((x,y)) + v = (p[3] << 24) + (p[0] << 16) + (p[1] << 8) + p[0] + h = (h + v) ^ (v << 2); + return h + +if __name__ == "__main__": + p = Packer(1024, 1024, 2000) + f = file(sys.argv[1]) + names = f.read().split('\n') + f.close() + + imap = {} + for filename in names: + if filename == '': + continue + i = Image.open(filename).convert('RGBA') + #i = special_convert(i, is_transparent_color) + i = special_convert(i, is_not_side_color) + h = get_hash(i) + if not imap.has_key(h): + imap[h] = i + p.add_rect(i.size[0], i.size[1], i) + + print "Compiling" + r = p.compile() + print "rectangles that did not fit:", len(r) + print [(i.width, i.height) for i in r] + #for i in r: + # print "%i x %i" % (i.width, i.height) + print "wasted search steps:", p.search_step + print "area used:", float(p.area_covered) / float(p.width*p.height) + + p.save_image("rect_pack.png") + diff --git a/utilities/action.py b/utilities/action.py new file mode 100644 index 0000000..d2678e2 --- /dev/null +++ b/utilities/action.py @@ -0,0 +1,222 @@ +class Action: + def __init__(self, txt): + self.txt = txt + + def __str__(self): + return '%s: %s' % (self.__class__.__name__, self.__dict__) + +class UndoneAction(Action): + def __str__(self): + return '%s: UNDONE' % self.__class__.__name__ + +class SetResourcesAction(UndoneAction): + pass + +class MoveUnitTriggerAction(UndoneAction): + pass + +class SetPlayerControlsTriggerAction(UndoneAction): + pass + +class PanViewTriggerAction(UndoneAction): + pass + +class TargetUnitTriggerAction(UndoneAction): + pass + +class ModifyCounterTriggerAction(UndoneAction): + pass + +class SetAllowedUnitsTriggerAction(Action): + def __init__(self, txt): + args = txt.split(',') + self.sidemask = int(args[0]) + self.unitmask = int(args[1]) + Action.__init__(self, txt) + +class EcomTriggerAction(Action): + def __init__(self, txt): + args = txt.split(',') + self.background = int(args[0]) + self.parsed = int(args[1]) + self.charfrom = int(args[2]) + self.charto = int(args[3]) + self.message = args[4] + Action.__init__(self, txt) + +class SetObjectiveTriggerAction(Action): + def __init__(self, txt): + args = txt.split(',') + self.sidemask = int(args[0]) + self.objective = args[1] + Action.__init__(self, txt) + +class SetNextMissionTriggerAction(Action): + def __init__(self, txt): + self.level = '' + if txt != '[none]': + self.level = txt + Action.__init__(self, txt) + +class EndMissionTriggerAction(Action): + def __init__(self, txt): + self.winlose = int(txt) + Action.__init__(self, txt) + +class WaitTriggerAction(Action): + def __init__(self, txt): + self.seconds = int(txt) + Action.__init__(self, txt) + +class SetSwitchTriggerAction(Action): + def __init__(self, txt): + args = txt.split(',') + self.switch = int(args[0]) + self.on = (int(args[1]) == 1) + Action.__init__(self, txt) + +class PreserveTriggerTriggerAction(Action): + pass + +class CenterViewTriggerAction(Action): + def __init__(self, txt): + self.area = int(txt) + Action.__init__(self, txt) + +class DefogAreaTriggerAction(Action): + def __init__(self, txt): + self.area = int(txt) + Action.__init__(self, txt) + +class CreateUnitGroupTriggerAction(Action): + def __init__(self, txt): + self.unitgroup = int(txt) + Action.__init__(self, txt) + +class HuntTriggerAction(Action): + def __init__(self, txt): + args = txt.split(',') + self.unitmask1 = int(args[0]) + self.sidemask1 = int(args[1]) + self.unitmask2 = int(args[2]) + self.sidemask2 = int(args[3]) + Action.__init__(self, txt) + +class CreateRandomUnitGroupTriggerAction(Action): + pass + +class AlliesTriggerAction(Action): + def __init__(self, txt): + args = txt.split(',') + self.sidemaskA = int(args[0]) + self.sidemaskB = int(args[1]) + Action.__init__(self, txt) + +class StartCountdownTimerTriggerAction(Action): + def __init__(self, txt): + args = txt.split(',') + self.seconds = int(args[0]) + self.message = args[1] + Action.__init__(self, txt) + +class ModifyCountdownTimerTriggerAction(Action): + def __init__(self, txt): + self.action = int(txt) + Action.__init__(self, txt) + +class RepairTriggerAction(Action): + def __init__(self, txt): + args = txt.split(',') + self.sidemask = int(args[0]) + self.on = (int(args[1]) == 1) + Action.__init__(self, txt) + +class EnableReplicatorTriggerAction(Action): + def __init__(self, txt): + args = txt.split(',') + self.sidemask = int(args[0]) + self.on = (int(args[1]) == 1) + Action.__init__(self, txt) + +class ModifyCreditsTriggerAction(Action): + def __init__(self, txt): + args = txt.split(',') + self.action = int(args[0]) + self.sidemask = int(args[1]) + self.amount = int(args[2]) + Action.__init__(self, txt) + + def __str__(self): + l = ['ModifyNumberNone','ModifyNumberSet','ModifyNumberAdd','ModifyNumberSubtract'] + return "%s: {'action': '%s', 'amount': %d 'sidemask': %d 'txt': '%s'}" % (self.__class__.__name__, l[self.action], self.amount, self.sidemask, self.txt) + +class MoveUnitsInAreaTriggerAction(Action): + def __init__(self, txt): + args = txt.split(',') + self.sidemask = int(args[0]) + self.unitmask = int(args[1]) + self.areasrc = int(args[2]) + self.areadst = int(args[3]) + Action.__init__(self, txt) + +class SetFormalObjectiveTextTriggerAction(Action): + def __init__(self, txt): + args = txt.split(',') + self.objective = int(args[0]) + self.message = args[1] + Action.__init__(self, txt) + +class SetFormalObjectiveStatusTriggerAction(Action): + def __init__(self, txt): + args = txt.split(',') + self.objective = int(args[0]) + self.message = args[1] + Action.__init__(self, txt) + +class ShowObjectivesTriggerAction(Action): + pass + +class SetFormalObjectiveInfoTriggerAction(Action): + def __init__(self, txt): + self.info = txt + Action.__init__(self, txt) + +class CutSceneTriggerAction(Action): + def __init__(self, txt): + self.message = txt + Action.__init__(self, txt) + +class JumpToMissionTriggerAction(Action): + def __init__(self, txt): + self.level = txt + Action.__init__(self, txt) + +class ModifyPvarTriggerAction(Action): + def __init__(self, txt): + Action.__init__(self, txt) + +class SetPvarTextTriggerAction(Action): + def __init__(self, txt): + args = txt.split(',') + self.name = args[0] + self.value = args[1] + Action.__init__(self, txt) + +class ShowAlertTriggerAction(Action): + def __init__(self, txt): + self.alert = txt + Action.__init__(self, txt) + +class SetAllowedUpgradesTriggerAction(Action): + def __init__(self, txt): + args = txt.split(',') + self.sidemask = int(args[0]) + self.upgrademask = int(args[1]) + Action.__init__(self, txt) + +class SetUpgradesTriggerAction(Action): + def __init__(self, txt): + args = txt.split(',') + self.sidemask = int(args[0]) + self.upgrademask = int(args[1]) + Action.__init__(self, txt) diff --git a/utilities/condition.py b/utilities/condition.py new file mode 100644 index 0000000..950f36a --- /dev/null +++ b/utilities/condition.py @@ -0,0 +1,119 @@ +class QualifiedNumber: + def __init__(self, txt): + args = txt.split(',') + self.txt = txt + self.qualifier = int(args[0]) + self.number = int(args[1]) + + def __repr__(self): + d = ['AtLeast', 'AtMost', 'Exactly'] + return '%s: %s %d' % (self.__class__.__name__, d[self.qualifier], self.number) + +class Condition: + def __init__(self, txt): + self.txt = txt + + def __str__(self): + return '%s: %s' % (self.__class__.__name__, self.__dict__) + +class UndoneCondition(Condition): + def __str__(self): + return '%s: UNDONE' % self.__class__.__name__ + +class MobileHQDeployedCondition(UndoneCondition): + pass + +class UnitDestroyedCondition(UndoneCondition): + pass + +class UnitSeesUnitCondition(UndoneCondition): + pass + +class CounterCondition(UndoneCondition): + pass + +class MissionLoadedCondition(Condition): + pass + +class PlaceStructureModeCondition(Condition): + pass + +class CreditsCondition(Condition): + def __init__(self, txt): + args = txt.split(',') + self.sidemask = int(args[0]) + self.qnum = QualifiedNumber(','.join(args[1:])) + Condition.__init__(self, txt) + +class AreaContainsUnitsCondition(Condition): + def __init__(self, txt): + args = txt.split(',') + self.area = int(args[0]) + self.qnum = QualifiedNumber(','.join(args[1:3])) + self.unitmask = int(args[3]) + self.sidemask = int(args[4]) + Condition.__init__(self, txt) + +class GalaxiteCapacityReachedCondition(Condition): + def __init__(self, txt): + args = txt.split(',') + self.sidemask = int(args[0]) + Condition.__init__(self, txt) + +class ElapsedTimeCondition(Condition): + def __init__(self, txt): + self.qnum = QualifiedNumber(txt) + Condition.__init__(self, txt) + +class OwnsUnitsCondition(Condition): + def __init__(self, txt): + args = txt.split(',') + self.sidemask = int(args[0]) + self.qnum = QualifiedNumber(','.join(args[1:3])) + self.unitmask = int(args[3]) + Condition.__init__(self, txt) + +class MinerCantFindGalaxiteCondition(Condition): + def __init__(self, txt): + args = txt.split(',') + self.sidemask = int(args[0]) + Condition.__init__(self, txt) + +class SwitchCondition(Condition): + def __init__(self, txt): + args = txt.split(',') + self.switch = int(args[0]) + self.on = (int(args[1]) == 1) + Condition.__init__(self, txt) + +class PeriodicTimerCondition(Condition): + def __init__(self, txt): + args = txt.split(',') + self.seconds = int(args[0]) + Condition.__init__(self, txt) + +class DiscoversSideCondition(Condition): + def __init__(self, txt): + args = txt.split(',') + self.sidemask_a = int(args[0]) + self.sidemask_b = int(args[1]) + Condition.__init__(self, txt) + +class CountdownTimerCondition(Condition): + def __init__(self, txt): + self.qnum = QualifiedNumber(txt) + Condition.__init__(self, txt) + +class TestPvarCondition(Condition): + def __init__(self, txt): + args = txt.split(',') + self.name = args[0] + self.qnum = QualifiedNumber(','.join(args[1:])) + Condition.__init__(self, txt) + +class HasUpgradesCondition(Condition): + def __init__(self, txt): + args = txt.split(',') + self.sidemask = int(args[0]) + self.upgrademask = int(args[1]) + Condition.__init__(self, txt) diff --git a/utilities/dumpfile.py b/utilities/dumpfile.py new file mode 100644 index 0000000..4240bf8 --- /dev/null +++ b/utilities/dumpfile.py @@ -0,0 +1,9 @@ +import pack +import sys + +filename = sys.argv[1] +#f = file('%s%s' % ('/Library/WebServer/Documents/wi/pack/', filename)) +f = file(filename) +p = pack.Pack(f.read()) +f.close() +print p.GetFile(sys.argv[2]) diff --git a/utilities/dumpfilenames.py b/utilities/dumpfilenames.py new file mode 100644 index 0000000..a49772e --- /dev/null +++ b/utilities/dumpfilenames.py @@ -0,0 +1,12 @@ +import pack +import sys + +filename = sys.argv[1] +print filename +#f = file('%s%s' % ('/Library/WebServer/Documents/wi/pack/', filename)) +f = file(filename) +p = pack.Pack(f.read()) +f.close() +for f in p.GetFilenames(): + print f + diff --git a/utilities/dumpini.py b/utilities/dumpini.py new file mode 100644 index 0000000..d2c7a2c --- /dev/null +++ b/utilities/dumpini.py @@ -0,0 +1,10 @@ +import pack +import sys +import ini + +filename = sys.argv[1] +#f = file('%s%s' % ('/Library/WebServer/Documents/wi/pack/', filename)) +f = file(filename) +p = pack.Pack(f.read()) +f.close() +print ini.Ini(p.GetFile(sys.argv[2])) diff --git a/utilities/ini.py b/utilities/ini.py new file mode 100644 index 0000000..6946635 --- /dev/null +++ b/utilities/ini.py @@ -0,0 +1,44 @@ +import struct +import cStringIO + +# +# Reads binary serialized ini files +# +class Ini: + def __init__(self, bytes): + self.bytes = bytes + self.sections = {} + + pos = 0 + while True: + nextSection, = struct.unpack('>H', bytes[pos : pos + 2]) + pos += 2 + countProps, = struct.unpack('>H', bytes[pos : pos + 2]) + pos += 2 + nameSection = bytes[pos : pos + bytes[pos:].find('\0')] + pos += len(nameSection) + 1 + section = [] + for index in xrange(countProps): + key = bytes[pos : pos + bytes[pos:].find('\0')] + pos += len(key) + 1 + value = bytes[pos : pos + bytes[pos:].find('\0')] + pos += len(value) + 1 + section.append((key,value)) + self.sections[nameSection] = section + if (pos & 1) != 0: + pos += 1 + if nextSection == 0: + break + + def __getitem__(self, key): + return self.sections[key] + + def __str__(self): + io = cStringIO.StringIO() + for key in self.sections.keys(): + io.write('%s\n' % key) + section = self.sections[key] + for prop in section: + io.write('\t%s: %s\n' % (prop[0], prop[1])) + io.write('\n') + return io.getvalue() diff --git a/utilities/inidump.py b/utilities/inidump.py new file mode 100644 index 0000000..7f1d0c8 --- /dev/null +++ b/utilities/inidump.py @@ -0,0 +1,10 @@ +import pack +import ini +import sys + +filename = sys.argv[1] +f = file(filename) +p = pack.Pack(f.read()) +f.close() +ini = ini.Ini(p.GetFile(sys.argv[2])) +print ini diff --git a/utilities/level.py b/utilities/level.py new file mode 100644 index 0000000..944dda3 --- /dev/null +++ b/utilities/level.py @@ -0,0 +1,49 @@ +import trigger + +class Level: + def __init__(self, ini): + self.ini = ini + self.sidetriggers = ([], [], [], [], []) + self.LoadTriggers() + + def GetTriggers(self, side): + return self.sidetriggers[side] + + def LoadTriggers(self): + section = self.ini['Triggers'] + triggermap = {} + triggerCurrent = None + for prop in section: + if prop[0] == 'Count': + continue + if prop[0] == 'T': + triggerCurrent = trigger.Trigger() + for key in prop[1].split(','): + triggermap[key] = triggerCurrent + continue + if prop[0] == 'C': + triggerCurrent.LoadCondition(prop[1]) + continue + if prop[0] == 'A': + triggerCurrent.LoadAction(prop[1]) + continue + + # Make lists of triggers for each side, from triggermap + for side in xrange(len(self.sidetriggers)): + index = 0 + while True: + found = False + for key in triggermap.keys(): + sideT = int(key.split(':')[0]) + if sideT != side: + continue + indexT = int(key.split(':')[1]) + if indexT != index: + continue + found = True + self.sidetriggers[side].append(triggermap[key]) + if not found: + break + index = index + 1 + + diff --git a/utilities/missions.py b/utilities/missions.py new file mode 100644 index 0000000..20a896e --- /dev/null +++ b/utilities/missions.py @@ -0,0 +1,149 @@ +import struct + +class Pack: + def __init__(self, bytes): + self.bytes = bytes + self.directory = self.BuildDirectory() + + def BuildDirectory(self): + bytes = self.GetRecord(0) + directory = [] + for index in xrange(len(bytes) / 32): + entrybytes = bytes[index * 32 : index * 32 + 32] + filename, count, first = struct.unpack('>29sBH', entrybytes) + directory.append((filename[:filename.find('\0')], first, count)) + return directory + + def Decompress(self, bytes): + out = '' + ib = 0 + done = False + while not done: + flags, = struct.unpack('B', bytes[ib]) + ib += 1 + for ibit in xrange(7, -1, -1): + if (flags & (1 << ibit)) != 0: + out += bytes[ib] + ib += 1 + continue + code, = struct.unpack('>H', bytes[ib:ib+2]) + ib += 2 + ibBack = code & 0x1fff + if (code & 0xe000) != 0: + cb = (((code & 0xe000) >> 13) & 0xff) + 1 + else: + if ibBack == 0: + done = True + break; + part = struct.unpack('B', bytes[ib])[0] >> 7 + ibBack = (code << 1) | part + cb = (struct.unpack('B', bytes[ib])[0] & 0x7f) + 2; + ib += 1 + ibCopy = len(out) - ibBack + out += out[ibCopy : ibCopy + cb] + return out + + def GetRecordOffset(self, index): + offset = 72 + 6 + index * 8 + return struct.unpack('>L', self.bytes[offset:offset+4])[0] + + def GetRecordCount(self): + offset = 72 + 4 + return struct.unpack('>H', self.bytes[offset:offset+2])[0] + + def GetRawRecord(self, index): + offset = self.GetRecordOffset(index) + if (index == self.GetRecordCount() - 1): + size = len(self.bytes) - offset + else: + size = self.GetRecordOffset(index + 1) - offset + return self.bytes[offset:offset+size] + + def GetRecord(self, index): + record = self.GetRawRecord(index) + header = struct.unpack('>3H', record[0:6]) + compressed, cbUncompressed, cbCompressed = header + if compressed == 0: + return record[6:] + else: + return self.Decompress(record[6:]) + + def FindDirectoryEntry(self, filename): + for entry in self.directory: + if entry[0] == filename: + return entry + return None + + def GetFilenames(self): + filenames = [] + for entry in self.directory: + filenames.append(entry[0]) + return filenames + + def GetFile(self, filename): + entry = self.FindDirectoryEntry(filename) + bytes = '' + for index in xrange(entry[2]): + bytes += self.GetRecord(entry[1] + index) + return bytes + +class Ini: + def __init__(self, bytes): + self.bytes = bytes + self.sections = {} + + pos = 0 + while True: + nextSection, = struct.unpack('>H', bytes[pos : pos + 2]) + pos += 2 + countProps, = struct.unpack('>H', bytes[pos : pos + 2]) + pos += 2 + nameSection = bytes[pos : pos + bytes[pos:].find('\0')] + pos += len(nameSection) + 1 + section = {} + for index in xrange(countProps): + key = bytes[pos : pos + bytes[pos:].find('\0')] + pos += len(key) + 1 + value = bytes[pos : pos + bytes[pos:].find('\0')] + pos += len(value) + 1 + section[key] = value + self.sections[nameSection] = section + if (pos & 1) != 0: + pos += 1 + if nextSection == 0: + break + + def __getitem__(self, key): + return self.sections[key] + +if __name__ == '__main__': + filename = 'htdata832.pdb' + f = file(filename) + pack = Pack(f.read()) + f.close() + + # Get filename list + filenames = pack.GetFilenames() + + # Look for missions + missions = [] + for filename in filenames: + if not filename.endswith(".lvl"): + continue + + print filename + bytes = pack.GetFile(filename) + ini = Ini(bytes) + print ini.sections + break + + + """ + ini = Ini(pack.GetFile(filename)) + mission = {} + mission['filename'] = filename + mission['title'] = ini['General']['Title'] + mission['minplayers'] = ini['General']['MinPlayers'] + mission['maxplayers'] = ini['General']['MaxPlayers'] + missions.append(mission) + """ diff --git a/utilities/pack.py b/utilities/pack.py new file mode 100644 index 0000000..001f1bf --- /dev/null +++ b/utilities/pack.py @@ -0,0 +1,93 @@ +import struct + +# Reads packed pdb files +# +class Pack: + def __init__(self, bytes): + self.bytes = bytes + self.directory = self.BuildDirectory() + + def BuildDirectory(self): + bytes = self.GetRecord(0) + directory = [] + for index in xrange(len(bytes) / 32): + entrybytes = bytes[index * 32 : index * 32 + 32] + filename, count, first = struct.unpack('>29sBH', entrybytes) + directory.append((filename[:filename.find('\0')], first, count)) + return directory + + def Decompress(self, bytes): + out = '' + ib = 0 + done = False + while not done: + flags, = struct.unpack('B', bytes[ib]) + ib += 1 + for ibit in xrange(7, -1, -1): + if (flags & (1 << ibit)) != 0: + out += bytes[ib] + ib += 1 + continue + code, = struct.unpack('>H', bytes[ib:ib+2]) + ib += 2 + ibBack = code & 0x1fff + if (code & 0xe000) != 0: + cb = (((code & 0xe000) >> 13) & 0xff) + 1 + else: + if ibBack == 0: + done = True + break; + part = struct.unpack('B', bytes[ib])[0] >> 7 + ibBack = (code << 1) | part + cb = (struct.unpack('B', bytes[ib])[0] & 0x7f) + 2; + ib += 1 + ibCopy = len(out) - ibBack + out += out[ibCopy : ibCopy + cb] + return out + + def GetRecordOffset(self, index): + offset = 72 + 6 + index * 8 + return struct.unpack('>L', self.bytes[offset:offset+4])[0] + + def GetRecordCount(self): + offset = 72 + 4 + return struct.unpack('>H', self.bytes[offset:offset+2])[0] + + def GetRawRecord(self, index): + offset = self.GetRecordOffset(index) + if (index == self.GetRecordCount() - 1): + size = len(self.bytes) - offset + else: + size = self.GetRecordOffset(index + 1) - offset + return self.bytes[offset:offset+size] + + def GetRecord(self, index): + record = self.GetRawRecord(index) + header = struct.unpack('>3H', record[0:6]) + compressed, cbUncompressed, cbCompressed = header + if compressed == 0: + return record[6:] + else: + return self.Decompress(record[6:]) + + def FindDirectoryEntry(self, filename): + for entry in self.directory: + if entry[0] == filename: + return entry + return None + + def GetFilenames(self): + filenames = [] + for entry in self.directory: + filenames.append(entry[0]) + return filenames + + def GetFile(self, filename): + entry = self.FindDirectoryEntry(filename) + if not entry: + return None + bytes = '' + for index in xrange(entry[2]): + bytes += self.GetRecord(entry[1] + index) + return bytes + diff --git a/utilities/pal2png.py b/utilities/pal2png.py new file mode 100644 index 0000000..90f99df --- /dev/null +++ b/utilities/pal2png.py @@ -0,0 +1,23 @@ +from PIL import Image +import sys + +palfile = sys.argv[1] +pngfile = sys.argv[2] + +f = open(palfile) +palbytes = f.read() +f.close() + +img = Image.new('RGB', (20,256)) + +for i in xrange(256): + r = ord(palbytes[2 + i * 3 + 0]) + g = ord(palbytes[2 + i * 3 + 1]) + b = ord(palbytes[2 + i * 3 + 2]) + + print 'index: %d == (%d, %d, %d)' % (i, r, g, b) + + for x in xrange(20): + img.putpixel((x, i), (r, g, b)) + +img.save(pngfile, format='PNG') diff --git a/utilities/trigger.py b/utilities/trigger.py new file mode 100644 index 0000000..1f750d6 --- /dev/null +++ b/utilities/trigger.py @@ -0,0 +1,83 @@ +import condition +import action +import cStringIO + +class Trigger: + def __init__(self): + self.conditions = [] + self.actions = [] + + def LoadCondition(self, txt): + c = { + 0 : lambda t: condition.MissionLoadedCondition(t), + 1 : lambda t: condition.CreditsCondition(t), + 2 : lambda t: condition.AreaContainsUnitsCondition(t), + 3 : lambda t: condition.GalaxiteCapacityReachedCondition(t), + 4 : lambda t: condition.MobileHQDeployedCondition(t), + 5 : lambda t: condition.PlaceStructureModeCondition(t), + 6 : lambda t: condition.ElapsedTimeCondition(t), + 7 : lambda t: condition.OwnsUnitsCondition(t), + 8 : lambda t: condition.MinerCantFindGalaxiteCondition(t), + 9 : lambda t: condition.UnitSeesUnitCondition(t), + 10 : lambda t: condition.UnitDestroyedCondition(t), + 11 : lambda t: condition.SwitchCondition(t), + 12 : lambda t: condition.PeriodicTimerCondition(t), + 13 : lambda t: condition.DiscoversSideCondition(t), + 14 : lambda t: condition.CountdownTimerCondition(t), + 15 : lambda t: condition.CounterCondition(t), + 16 : lambda t: condition.TestPvarCondition(t), + 17 : lambda t: condition.HasUpgradesCondition(t) + }[int(txt.split(',')[0])](','.join(txt.split(',')[1:])) + self.conditions.append(c) + + def LoadAction(self, txt): + a = { + 0 : lambda t: action.SetResourcesTriggerAction(t), + 1 : lambda t: action.SetAllowedUnitsTriggerAction(t), + 2 : lambda t: action.EcomTriggerAction(t), + 3 : lambda t: action.SetObjectiveTriggerAction(t), + 4 : lambda t: action.SetNextMissionTriggerAction(t), + 5 : lambda t: action.EndMissionTriggerAction(t), + 6 : lambda t: action.WaitTriggerAction(t), + 7 : lambda t: action.SetSwitchTriggerAction(t), + 8 : lambda t: action.SetPlayerControlsTriggerAction(t), + 9 : lambda t: action.PreserveTriggerTriggerAction(t), + 10 : lambda t: action.CenterViewTriggerAction(t), + 11 : lambda t: action.PanViewTriggerAction(t), + 12 : lambda t: action.DefogAreaTriggerAction(t), + 13 : lambda t: action.MoveUnitTriggerAction(t), + 14 : lambda t: action.TargetUnitTriggerAction(t), + 15 : lambda t: action.CreateUnitGroupTriggerAction(t), + 16 : lambda t: action.HuntTriggerAction(t), + 17 : lambda t: action.CreateRandomUnitGroupTriggerAction(t), + 18 : lambda t: action.AlliesTriggerAction(t), + 19 : lambda t: action.StartCountdownTimerTriggerAction(t), + 20 : lambda t: action.ModifyCountdownTimerTriggerAction(t), + 21 : lambda t: action.RepairTriggerAction(t), + 22 : lambda t: action.EnableReplicatorTriggerAction(t), + 23 : lambda t: action.ModifyCreditsTriggerAction(t), + 24 : lambda t: action.ModifyCounterTriggerAction(t), + 25 : lambda t: action.MoveUnitsInAreaTriggerAction(t), + 26 : lambda t: action.SetFormalObjectiveTextTriggerAction(t), + 27 : lambda t: action.SetFormalObjectiveStatusTriggerAction(t), + 28 : lambda t: action.ShowObjectivesTriggerAction(t), + 29 : lambda t: action.SetFormalObjectiveInfoTriggerAction(t), + 30 : lambda t: action.CutSceneTriggerAction(t), + 31 : lambda t: action.JumpToMissionTriggerAction(t), + 32 : lambda t: action.ModifyPvarTriggerAction(t), + 33 : lambda t: action.SetPvarTextTriggerAction(t), + 34 : lambda t: action.ShowAlertTriggerAction(t), + 35 : lambda t: action.SetAllowedUpgradesTriggerAction(t), + 36 : lambda t: action.SetUpgradesTriggerAction(t) + }[int(txt.split(',')[0])](','.join(txt.split(',')[1:])) + self.actions.append(a) + + def __str__(self): + io = cStringIO.StringIO() + for condition in self.conditions: + io.write('%s\n' % condition) + for action in self.actions: + io.write('%s\n' % action) + io.write('\n') + return io.getvalue() + diff --git a/utilities/triggerdump.py b/utilities/triggerdump.py new file mode 100644 index 0000000..5661152 --- /dev/null +++ b/utilities/triggerdump.py @@ -0,0 +1,20 @@ +import pack +import sys +import ini +import level + +if __name__ == '__main__': + filename = sys.argv[1] + f = file(filename) + p = pack.Pack(f.read()) + f.close() + ini = ini.Ini(p.GetFile(sys.argv[2])) + level = level.Level(ini) + for side in xrange(5): + print 'Side %d' % side + triggers = level.GetTriggers(side) + count = 0 + for trigger in triggers: + print 'Trigger %d:' % count + print trigger + count = count + 1 diff --git a/utilities/unpack.py b/utilities/unpack.py new file mode 100644 index 0000000..57c898c --- /dev/null +++ b/utilities/unpack.py @@ -0,0 +1,11 @@ +import pack +import sys + +filename = sys.argv[1] +f = file(filename) +p = pack.Pack(f.read()) +f.close() +for filename in p.GetFilenames(): + f = open(filename, 'w') + f.write(p.GetFile(filename)) + f.close() diff --git a/wavcrunch/.cvsignore b/wavcrunch/.cvsignore new file mode 100644 index 0000000..ba077a4 --- /dev/null +++ b/wavcrunch/.cvsignore @@ -0,0 +1 @@ +bin diff --git a/wavcrunch/App.ico b/wavcrunch/App.ico new file mode 100644 index 0000000..3a5525f Binary files /dev/null and b/wavcrunch/App.ico differ diff --git a/wavcrunch/AssemblyInfo.cs b/wavcrunch/AssemblyInfo.cs new file mode 100644 index 0000000..9f89a32 --- /dev/null +++ b/wavcrunch/AssemblyInfo.cs @@ -0,0 +1,58 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.*")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] diff --git a/wavcrunch/Class1.cs b/wavcrunch/Class1.cs new file mode 100644 index 0000000..f8ea5fc --- /dev/null +++ b/wavcrunch/Class1.cs @@ -0,0 +1,92 @@ +using System; +using System.IO; +using System.Collections; +using SpiffLib; + +namespace wavcrunch +{ + class Class1 + { + [STAThread] + static void Main(string[] args) + { + if (args[0] == "-g") { + GenTables(); + return; + } + + // Get source + + ArrayList alsFiles = new ArrayList(); + for (int n = 0; n < args.Length; n++) { + string strFileT = Path.GetFileName(args[n]); + string strDirT = Path.GetDirectoryName(args[n]); + if (strDirT == "") + strDirT = "."; + string[] astrFiles = Directory.GetFiles(strDirT, strFileT); + alsFiles.AddRange(astrFiles); + } + + foreach (string strFileWav in alsFiles) { + // Read in wav + + Pcm pcm = new Pcm(strFileWav); + + // Write out .snd file + + string strFileSnd = Path.ChangeExtension(strFileWav, ".snd"); + Console.WriteLine(Path.GetFileName(strFileWav) + " -> " + Path.GetFileName(strFileSnd)); + BinaryWriter bwtr = new BinaryWriter(new FileStream(strFileSnd, FileMode.Create, FileAccess.Write, FileShare.None)); + bwtr.Write(pcm.GetSndEncoding()); + bwtr.Close(); + + } + } + + static void GenTables() { + Console.WriteLine("gmp2SumAverage:"); + for (int n = 0; n < 512; n++) { + if (n % 16 == 0) + Console.Write(".byte "); + int m = (int)((float)n / 2.0 + 0.5); + if (m > 255) + m = 255; + if (n % 16 == 15) { + Console.WriteLine(m); + } else { + Console.Write(m + ", "); + } + } + Console.WriteLine(""); + + Console.WriteLine("gmp3SumAverage:"); + for (int n = 0; n < 768; n++) { + if (n % 16 == 0) + Console.Write(".byte "); + int m = (int)((float)n / 3.0 + 0.5); + if (m > 255) + m = 255; + if (n % 16 == 15) { + Console.WriteLine(m); + } else { + Console.Write(m + ", "); + } + } + Console.WriteLine(""); + + Console.WriteLine("gmp4SumAverage:"); + for (int n = 0; n < 1024; n++) { + if (n % 16 == 0) + Console.Write(".byte "); + int m = (int)((float)n / 4.0 + 0.5); + if (m > 255) + m = 255; + if (n % 16 == 15) { + Console.WriteLine(m); + } else { + Console.Write(m + ", "); + } + } + } + } +} diff --git a/wavcrunch/wavcrunch.csproj b/wavcrunch/wavcrunch.csproj new file mode 100644 index 0000000..6e496d3 --- /dev/null +++ b/wavcrunch/wavcrunch.csproj @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wavcrunch/wavcrunch.sln b/wavcrunch/wavcrunch.sln new file mode 100644 index 0000000..b2738ed --- /dev/null +++ b/wavcrunch/wavcrunch.sln @@ -0,0 +1,21 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "wavcrunch", "wavcrunch.csproj", "{3F6EEDB0-982D-42E9-B098-34ABD50B60D2}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Release = Release + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {3F6EEDB0-982D-42E9-B098-34ABD50B60D2}.Debug.ActiveCfg = Debug|.NET + {3F6EEDB0-982D-42E9-B098-34ABD50B60D2}.Debug.Build.0 = Debug|.NET + {3F6EEDB0-982D-42E9-B098-34ABD50B60D2}.Release.ActiveCfg = Release|.NET + {3F6EEDB0-982D-42E9-B098-34ABD50B60D2}.Release.Build.0 = Release|.NET + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/yajl/COPYING b/yajl/COPYING new file mode 100644 index 0000000..1e95b1b --- /dev/null +++ b/yajl/COPYING @@ -0,0 +1,29 @@ +Copyright 2007, Lloyd Hilaiel. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + 3. Neither the name of Lloyd Hilaiel nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/yajl/api/yajl_common.h b/yajl/api/yajl_common.h new file mode 100644 index 0000000..a97b3c0 --- /dev/null +++ b/yajl/api/yajl_common.h @@ -0,0 +1,51 @@ +/* + * Copyright 2007, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __YAJL_COMMON_H__ +#define __YAJL_COMMON_H__ + +#define YAJL_MAX_DEPTH 128 + +/* msft dll export gunk. To build a DLL on windows, you + * must define WIN32, YAJL_SHARED, and YAJL_BUILD. To use a shared + * DLL, you must define YAJL_SHARED and WIN32 */ +#if defined(WIN32) && defined(YAJL_SHARED) +# ifdef YAJL_BUILD +# define YAJL_API __declspec(dllexport) +# else +# define YAJL_API __declspec(dllimport) +# endif +#else +# define YAJL_API +#endif + +#endif diff --git a/yajl/api/yajl_gen.h b/yajl/api/yajl_gen.h new file mode 100644 index 0000000..c9de168 --- /dev/null +++ b/yajl/api/yajl_gen.h @@ -0,0 +1,113 @@ +/* + * Copyright 2007, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * \file yajl_gen.h + * Interface to YAJL's JSON generation facilities. + */ + +#include "yajl/api/yajl_common.h" + +#ifndef __YAJL_GEN_H__ +#define __YAJL_GEN_H__ + +#ifdef __cplusplus +extern "C" { +#endif + /** generator status codes */ + typedef enum { + /** no error */ + yajl_gen_status_ok = 0, + /** at a point where a map key is generated, a function other than + * yajl_gen_string was called */ + yajl_gen_keys_must_be_strings, + /** YAJL's maximum generation depth was exceeded. see + * YAJL_MAX_DEPTH */ + yajl_max_depth_exceeded, + /** A generator function (yajl_gen_XXX) was called while in an error + * state */ + yajl_gen_in_error_state, + /** A complete JSON document has been generated */ + yajl_gen_generation_complete + } yajl_gen_status; + + /** an opaque handle to a generator */ + typedef struct yajl_gen_t * yajl_gen; + + /** configuration structure for the generator */ + typedef struct { + /** generate indented (beautiful) output */ + unsigned int beautify; + /** an opportunity to define an indent string. such as \\t or + * some number of spaces. default is four spaces ' '. This + * member is only relevant when beautify is true */ + const char * indentString; + } yajl_gen_config; + + /** allocate a generator handle */ + yajl_gen YAJL_API yajl_gen_alloc(const yajl_gen_config * config); + + /** free a generator handle */ + void YAJL_API yajl_gen_free(yajl_gen handle); + + yajl_gen_status YAJL_API yajl_gen_integer(yajl_gen hand, long int number); + yajl_gen_status YAJL_API yajl_gen_double(yajl_gen hand, double number); + yajl_gen_status YAJL_API yajl_gen_number(yajl_gen hand, + const char * num, + unsigned int len); + yajl_gen_status YAJL_API yajl_gen_string(yajl_gen hand, + const unsigned char * str, + unsigned int len); + yajl_gen_status YAJL_API yajl_gen_null(yajl_gen hand); + yajl_gen_status YAJL_API yajl_gen_bool(yajl_gen hand, int boolean); + yajl_gen_status YAJL_API yajl_gen_map_open(yajl_gen hand); + yajl_gen_status YAJL_API yajl_gen_map_close(yajl_gen hand); + yajl_gen_status YAJL_API yajl_gen_array_open(yajl_gen hand); + yajl_gen_status YAJL_API yajl_gen_array_close(yajl_gen hand); + + /** access the null terminated generator buffer. If incrementally + * outputing JSON, one should call yajl_gen_clear to clear the + * buffer. This allows stream generation. */ + yajl_gen_status YAJL_API yajl_gen_get_buf(yajl_gen hand, + const unsigned char ** buf, + unsigned int * len); + + /** clear yajl's output buffer, but maintain all internal generation + * state. This function will not "reset" the generator state, and is + * intended to enable incremental JSON outputing. */ + void YAJL_API yajl_gen_clear(yajl_gen hand); + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/yajl/api/yajl_parse.h b/yajl/api/yajl_parse.h new file mode 100644 index 0000000..b196018 --- /dev/null +++ b/yajl/api/yajl_parse.h @@ -0,0 +1,167 @@ +/* + * Copyright 2007, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * \file yajl_parse.h + * Interface to YAJL's JSON parsing facilities. + */ + +#include "yajl/api/yajl_common.h" + +#ifndef __YAJL_PARSE_H__ +#define __YAJL_PARSE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + /** error codes returned from this interface */ + typedef enum { + /** no error was encountered */ + yajl_status_ok, + /** a client callback returned zero, stopping the parse */ + yajl_status_client_canceled, + /** The parse cannot yet complete because more json input text + * is required, call yajl_parse with the next buffer of input text. + * (pertinent only when stream parsing) */ + yajl_status_insufficient_data, + /** An error occured during the parse. Call yajl_get_error for + * more information about the encountered error */ + yajl_status_error + } yajl_status; + + /** attain a human readable, english, string for an error */ + const char * YAJL_API yajl_status_to_string(yajl_status code); + + /** an opaque handle to a parser */ + typedef struct yajl_handle_t * yajl_handle; + + /** yajl is an event driven parser. this means as json elements are + * parsed, you are called back to do something with the data. The + * functions in this table indicate the various events for which + * you will be called back. Each callback accepts a "context" + * pointer, this is a void * that is passed into the yajl_parse + * function which the client code may use to pass around context. + * + * All callbacks return an integer. If non-zero, the parse will + * continue. If zero, the parse will be canceled and + * yajl_status_client_canceled will be returned from the parse. + * + * Note about handling of numbers: + * yajl will only convert numbers that can be represented in a double + * or a long int. All other numbers will be passed to the client + * in string form using the yajl_number callback. Furthermore, if + * yajl_number is not NULL, it will always be used to return numbers, + * that is yajl_integer and yajl_double will be ignored. If + * yajl_number is NULL but one of yajl_integer or yajl_double are + * defined, parsing of a number larger than is representable + * in a double or long int will result in a parse error. + */ + typedef struct { + int (* yajl_null)(void * ctx); + int (* yajl_boolean)(void * ctx, int boolVal); + int (* yajl_integer)(void * ctx, long integerVal); + int (* yajl_double)(void * ctx, double doubleVal); + /** A callback which passes the string representation of the number + * back to the client. Will be used for all numbers when present */ + int (* yajl_number)(void * ctx, const char * numberVal, + unsigned int numberLen); + + /** strings are returned as pointers into the JSON text when, + * possible, as a result, they are _not_ null padded */ + int (* yajl_string)(void * ctx, const unsigned char * stringVal, + unsigned int stringLen); + + int (* yajl_start_map)(void * ctx); + int (* yajl_map_key)(void * ctx, const unsigned char * key, + unsigned int stringLen); + int (* yajl_end_map)(void * ctx); + + int (* yajl_start_array)(void * ctx); + int (* yajl_end_array)(void * ctx); + } yajl_callbacks; + + /** configuration structure for the generator */ + typedef struct { + /** if nonzero, javascript style comments will be allowed in + * the json input, both slash star and slash slash */ + unsigned int allowComments; + /** if nonzero, invalid UTF8 strings will cause a parse + * error */ + unsigned int checkUTF8; + } yajl_parser_config; + + /** allocate a parser handle + * \param callbacks a yajl callbacks structure specifying the + * functions to call when different JSON entities + * are encountered in the input text. May be NULL, + * which is only useful for validation. + * \param config configuration parameters for the parse. + * \param ctx a context pointer that will be passed to callbacks. + */ + yajl_handle YAJL_API yajl_alloc(const yajl_callbacks * callbacks, + const yajl_parser_config * config, + void * ctx); + + /** free a parser handle */ + void YAJL_API yajl_free(yajl_handle handle); + + /** Parse some json! + * \param hand - a handle to the json parser allocated with yajl_alloc + * \param jsonText - a pointer to the UTF8 json text to be parsed + * \param jsonTextLength - the length, in bytes, of input text + */ + yajl_status YAJL_API yajl_parse(yajl_handle hand, + const unsigned char * jsonText, + unsigned int jsonTextLength); + + /** get an error string describing the state of the + * parse. + * + * If verbose is non-zero, the message will include the JSON + * text where the error occured, along with an arrow pointing to + * the specific char. + * + * A dynamically allocated string will be returned which should + * be freed with yajl_free_error + */ + unsigned char * YAJL_API yajl_get_error(yajl_handle hand, int verbose, + const unsigned char * jsonText, + unsigned int jsonTextLength); + + /** free an error returned from yajl_get_error */ + void YAJL_API yajl_free_error(unsigned char * str); + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/yajl/src/yajl.c b/yajl/src/yajl.c new file mode 100644 index 0000000..ec2748c --- /dev/null +++ b/yajl/src/yajl.c @@ -0,0 +1,121 @@ +/* + * Copyright 2007, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "yajl/api/yajl_parse.h" +#include "yajl/src/yajl_lex.h" +#include "yajl/src/yajl_parser.h" + +#include +#include +#include + +const char * +yajl_status_to_string(yajl_status stat) +{ + const char * statStr = "unknown"; + switch (stat) { + case yajl_status_ok: + statStr = "ok, no error"; + break; + case yajl_status_client_canceled: + statStr = "client canceled parse"; + break; + case yajl_status_insufficient_data: + statStr = "eof was met before the parse could complete"; + break; + case yajl_status_error: + statStr = "parse error"; + break; + } + return statStr; +} + +yajl_handle +yajl_alloc(const yajl_callbacks * callbacks, + const yajl_parser_config * config, + void * ctx) +{ + unsigned int allowComments = 0; + unsigned int validateUTF8 = 0; + yajl_handle hand = (yajl_handle) malloc(sizeof(struct yajl_handle_t)); + + if (config != NULL) { + allowComments = config->allowComments; + validateUTF8 = config->checkUTF8; + } + + hand->callbacks = callbacks; + hand->ctx = ctx; + hand->lexer = yajl_lex_alloc(allowComments, validateUTF8); + hand->errorOffset = 0; + hand->decodeBuf = yajl_buf_alloc(); + hand->stateBuf = yajl_buf_alloc(); + + yajl_state_push(hand, yajl_state_start); + + return hand; +} + +void +yajl_free(yajl_handle handle) +{ + yajl_buf_free(handle->stateBuf); + yajl_buf_free(handle->decodeBuf); + yajl_lex_free(handle->lexer); + free(handle); +} + +yajl_status +yajl_parse(yajl_handle hand, const unsigned char * jsonText, + unsigned int jsonTextLen) +{ + unsigned int offset = 0; + yajl_status status; + status = yajl_do_parse(hand, &offset, jsonText, jsonTextLen); + return status; +} + +unsigned char * +yajl_get_error(yajl_handle hand, int verbose, + const unsigned char * jsonText, unsigned int jsonTextLen) +{ + return yajl_render_error_string(hand, jsonText, jsonTextLen, verbose); +} + +void +yajl_free_error(unsigned char * str) +{ + /* XXX: use memory allocation functions if set */ + free(str); +} + +/* XXX: add utility routines to parse from file */ diff --git a/yajl/src/yajl_buf.c b/yajl/src/yajl_buf.c new file mode 100644 index 0000000..5f11677 --- /dev/null +++ b/yajl/src/yajl_buf.c @@ -0,0 +1,115 @@ +/* + * Copyright 2007, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "yajl/src/yajl_buf.h" + +#include +#include +#include + +#define YAJL_BUF_INIT_SIZE 2048 + +struct yajl_buf_t { + unsigned int len; + unsigned int used; + unsigned char * data; +}; + +static +void yajl_buf_ensure_available(yajl_buf buf, unsigned int want) +{ + unsigned int need; + + assert(buf != NULL); + + /* first call */ + if (buf->data == NULL) { + buf->len = YAJL_BUF_INIT_SIZE; + buf->data = (unsigned char *) malloc(buf->len); + buf->data[0] = 0; + } + + need = buf->len; + + while (want >= (need - buf->used)) need <<= 1; + + if (need != buf->len) { + buf->data = (unsigned char *) realloc(buf->data, need); + buf->len = need; + } +} + +yajl_buf yajl_buf_alloc(void) +{ + return (yajl_buf) calloc(1, sizeof(struct yajl_buf_t)); +} + +void yajl_buf_free(yajl_buf buf) +{ + assert(buf != NULL); + if (buf->data) free(buf->data); + free(buf); +} + +void yajl_buf_append(yajl_buf buf, const void * data, unsigned int len) +{ + yajl_buf_ensure_available(buf, len); + if (len > 0) { + assert(data != NULL); + memcpy(buf->data + buf->used, data, len); + buf->used += len; + buf->data[buf->used] = 0; + } +} + +void yajl_buf_clear(yajl_buf buf) +{ + buf->used = 0; + if (buf->data) buf->data[buf->used] = 0; +} + +const unsigned char * yajl_buf_data(yajl_buf buf) +{ + return buf->data; +} + +unsigned int yajl_buf_len(yajl_buf buf) +{ + return buf->used; +} + +void +yajl_buf_truncate(yajl_buf buf, unsigned int len) +{ + assert(len <= buf->used); + buf->used = len; +} diff --git a/yajl/src/yajl_buf.h b/yajl/src/yajl_buf.h new file mode 100644 index 0000000..e2c08b5 --- /dev/null +++ b/yajl/src/yajl_buf.h @@ -0,0 +1,63 @@ +/* + * Copyright 2007, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __YAJL_BUF_H__ +#define __YAJL_BUF_H__ + +/** + * yajl_buf is a buffer with exponential growth. the buffer ensures that + * you are always null padded. + */ +typedef struct yajl_buf_t * yajl_buf; + +/* allocate a new buffer */ +yajl_buf yajl_buf_alloc(void); + +/* free the buffer */ +void yajl_buf_free(yajl_buf buf); + +/* append a number of bytes to the buffer */ +void yajl_buf_append(yajl_buf buf, const void * data, unsigned int len); + +/* empty the buffer */ +void yajl_buf_clear(yajl_buf buf); + +/* get a pointer to the beginning of the buffer */ +const unsigned char * yajl_buf_data(yajl_buf buf); + +/* get the length of the buffer */ +unsigned int yajl_buf_len(yajl_buf buf); + +/* truncate the buffer */ +void yajl_buf_truncate(yajl_buf buf, unsigned int len); + +#endif diff --git a/yajl/src/yajl_encode.c b/yajl/src/yajl_encode.c new file mode 100644 index 0000000..d91d846 --- /dev/null +++ b/yajl/src/yajl_encode.c @@ -0,0 +1,179 @@ +/* + * Copyright 2007, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "yajl/src/yajl_encode.h" + +#include +#include +#include +#include + +static void CharToHex(unsigned char c, char * hexBuf) +{ + const char * hexchar = "0123456789ABCDEF"; + hexBuf[0] = hexchar[c >> 4]; + hexBuf[1] = hexchar[c & 0x0F]; +} + +void +yajl_string_encode(yajl_buf buf, const unsigned char * str, + unsigned int len) +{ + unsigned int beg = 0; + unsigned int end = 0; + char hexBuf[7]; + hexBuf[0] = '\\'; hexBuf[1] = 'u'; hexBuf[2] = '0'; hexBuf[3] = '0'; + hexBuf[6] = 0; + + while (end < len) { + char * escaped = NULL; + switch (str[end]) { + case '\r': escaped = "\\r"; break; + case '\n': escaped = "\\n"; break; + case '\\': escaped = "\\\\"; break; + /* case '/': escaped = "\\/"; break; */ + case '"': escaped = "\\\""; break; + case '\f': escaped = "\\f"; break; + case '\b': escaped = "\\b"; break; + case '\t': escaped = "\\t"; break; + default: + if ((unsigned char) str[end] < 32) { + CharToHex(str[end], hexBuf + 4); + escaped = hexBuf; + } + break; + } + if (escaped != NULL) { + yajl_buf_append(buf, str + beg, end - beg); + yajl_buf_append(buf, escaped, strlen(escaped)); + beg = ++end; + } else { + ++end; + } + } + yajl_buf_append(buf, str + beg, end - beg); +} + +static void hexToDigit(unsigned int * val, const unsigned char * hex) +{ + unsigned int i; + for (i=0;i<4;i++) { + unsigned char c = hex[i]; + if (c >= 'A') c = (c & ~0x20) - 7; + c -= '0'; + assert(!(c & 0xF0)); + *val = (*val << 4) | c; + } +} + +static void Utf32toUtf8(unsigned int codepoint, char * utf8Buf) +{ + if (codepoint < 0x80) { + utf8Buf[0] = (char) codepoint; + utf8Buf[1] = 0; + } else if (codepoint < 0x0800) { + utf8Buf[0] = (char) ((codepoint >> 6) | 0xC0); + utf8Buf[1] = (char) ((codepoint & 0x3F) | 0x80); + utf8Buf[2] = 0; + } else if (codepoint < 0x10000) { + utf8Buf[0] = (char) ((codepoint >> 12) | 0xE0); + utf8Buf[1] = (char) (((codepoint >> 6) & 0x3F) | 0x80); + utf8Buf[2] = (char) ((codepoint & 0x3F) | 0x80); + utf8Buf[3] = 0; + } else if (codepoint < 0x200000) { + utf8Buf[0] =(char)((codepoint >> 18) | 0xF0); + utf8Buf[1] =(char)(((codepoint >> 12) & 0x3F) | 0x80); + utf8Buf[2] =(char)(((codepoint >> 6) & 0x3F) | 0x80); + utf8Buf[3] =(char)((codepoint & 0x3F) | 0x80); + utf8Buf[4] = 0; + } else { + utf8Buf[0] = '?'; + utf8Buf[1] = 0; + } +} + +void yajl_string_decode(yajl_buf buf, const unsigned char * str, + unsigned int len) +{ + unsigned int beg = 0; + unsigned int end = 0; + + while (end < len) { + if (str[end] == '\\') { + char utf8Buf[5]; + const char * unescaped = "?"; + yajl_buf_append(buf, str + beg, end - beg); + switch (str[++end]) { + case 'r': unescaped = "\r"; break; + case 'n': unescaped = "\n"; break; + case '\\': unescaped = "\\"; break; + case '/': unescaped = "/"; break; + case '"': unescaped = "\""; break; + case 'f': unescaped = "\f"; break; + case 'b': unescaped = "\b"; break; + case 't': unescaped = "\t"; break; + case 'u': { + unsigned int codepoint = 0; + hexToDigit(&codepoint, str + ++end); + end+=3; + /* check if this is a surrogate */ + if ((codepoint & 0xFC00) == 0xD800) { + end++; + if (str[end] == '\\' && str[end + 1] == 'u') { + unsigned int surrogate = 0; + hexToDigit(&surrogate, str + end + 2); + codepoint = + (((codepoint & 0x3F) << 10) | + ((((codepoint >> 6) & 0xF) + 1) << 16) | + (surrogate & 0x3FF)); + end += 5; + } else { + unescaped = "?"; + break; + } + } + + Utf32toUtf8(codepoint, utf8Buf); + unescaped = utf8Buf; + break; + } + default: + assert("this should never happen" == NULL); + } + yajl_buf_append(buf, unescaped, strlen(unescaped)); + beg = ++end; + } else { + end++; + } + } + yajl_buf_append(buf, str + beg, end - beg); +} diff --git a/yajl/src/yajl_encode.h b/yajl/src/yajl_encode.h new file mode 100644 index 0000000..5d14944 --- /dev/null +++ b/yajl/src/yajl_encode.h @@ -0,0 +1,44 @@ +/* + * Copyright 2007, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __YAJL_ENCODE_H__ +#define __YAJL_ENCODE_H__ + +#include "yajl/src/yajl_buf.h" + +void yajl_string_encode(yajl_buf buf, const unsigned char * str, + unsigned int length); + +void yajl_string_decode(yajl_buf buf, const unsigned char * str, + unsigned int length); + +#endif diff --git a/yajl/src/yajl_gen.c b/yajl/src/yajl_gen.c new file mode 100644 index 0000000..ba2f034 --- /dev/null +++ b/yajl/src/yajl_gen.c @@ -0,0 +1,274 @@ +/* + * Copyright 2007, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "yajl/api/yajl_gen.h" +#include "yajl/src/yajl_buf.h" +#include "yajl/src/yajl_encode.h" + +#include +#include +#include + +typedef enum { + yajl_gen_start, + yajl_gen_map_start, + yajl_gen_map_key, + yajl_gen_map_val, + yajl_gen_array_start, + yajl_gen_in_array, + yajl_gen_complete, + yajl_gen_error +} yajl_gen_state; + +struct yajl_gen_t +{ + unsigned int depth; + unsigned int pretty; + const char * indentString; + yajl_gen_state state[YAJL_MAX_DEPTH]; + yajl_buf buf; +}; + +yajl_gen +yajl_gen_alloc(const yajl_gen_config * config) +{ + yajl_gen g = (yajl_gen) malloc(sizeof(struct yajl_gen_t)); + memset((void *) g, 0, sizeof(struct yajl_gen_t)); + if (config) { + g->pretty = config->beautify; + g->indentString = config->indentString ? config->indentString : " "; + } + g->buf = yajl_buf_alloc(); + return g; +} + +void +yajl_gen_free(yajl_gen g) +{ + yajl_buf_free(g->buf); + free(g); +} + +#define INSERT_SEP \ + if (g->state[g->depth] == yajl_gen_map_key || \ + g->state[g->depth] == yajl_gen_in_array) { \ + yajl_buf_append(g->buf, ",", 1); \ + if (g->pretty) yajl_buf_append(g->buf, "\n", 1); \ + } else if (g->state[g->depth] == yajl_gen_map_val) { \ + yajl_buf_append(g->buf, ":", 1); \ + if (g->pretty) yajl_buf_append(g->buf, " ", 1); \ + } + +#define INSERT_WHITESPACE \ + if (g->pretty) { \ + if (g->state[g->depth] != yajl_gen_map_val) { \ + unsigned int i; \ + for (i=0;idepth;i++) \ + yajl_buf_append(g->buf, g->indentString, \ + strlen(g->indentString)); \ + } \ + } + +#define ENSURE_NOT_KEY \ + if (g->state[g->depth] == yajl_gen_map_key) { \ + return yajl_gen_keys_must_be_strings; \ + } \ + +/* check that we're not complete, or in error state. in a valid state + * to be generating */ +#define ENSURE_VALID_STATE \ + if (g->state[g->depth] == yajl_gen_error) { \ + return yajl_gen_in_error_state;\ + } else if (g->state[g->depth] == yajl_gen_complete) { \ + return yajl_gen_generation_complete; \ + } + +#define INCREMENT_DEPTH \ + if (++(g->depth) >= YAJL_MAX_DEPTH) return yajl_max_depth_exceeded; + +#define APPENDED_ATOM \ + switch (g->state[g->depth]) { \ + case yajl_gen_start: \ + g->state[g->depth] = yajl_gen_complete; \ + break; \ + case yajl_gen_map_start: \ + case yajl_gen_map_key: \ + g->state[g->depth] = yajl_gen_map_val; \ + break; \ + case yajl_gen_array_start: \ + g->state[g->depth] = yajl_gen_in_array; \ + break; \ + case yajl_gen_map_val: \ + g->state[g->depth] = yajl_gen_map_key; \ + break; \ + default: \ + break; \ + } \ + +#define FINAL_NEWLINE \ + if (g->pretty && g->state[g->depth] == yajl_gen_complete) \ + yajl_buf_append(g->buf, "\n", 1); + +yajl_gen_status +yajl_gen_integer(yajl_gen g, long int number) +{ + char i[32]; + ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; + sprintf(i, "%ld", number); + yajl_buf_append(g->buf, i, strlen(i)); + APPENDED_ATOM; + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_double(yajl_gen g, double number) +{ + char i[32]; + ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; + sprintf(i, "%g", number); + yajl_buf_append(g->buf, i, strlen(i)); + APPENDED_ATOM; + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_number(yajl_gen g, const char * s, unsigned int l) +{ + ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; + yajl_buf_append(g->buf, s, l); + APPENDED_ATOM; + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_string(yajl_gen g, const unsigned char * str, + unsigned int len) +{ + ENSURE_VALID_STATE; INSERT_SEP; INSERT_WHITESPACE; + yajl_buf_append(g->buf, "\"", 1); + yajl_string_encode(g->buf, str, len); + yajl_buf_append(g->buf, "\"", 1); + APPENDED_ATOM; + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_null(yajl_gen g) +{ + ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; + yajl_buf_append(g->buf, "null", strlen("null")); + APPENDED_ATOM; + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_bool(yajl_gen g, int boolean) +{ + const char * val = boolean ? "true" : "false"; + + ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; + yajl_buf_append(g->buf, val, strlen(val)); + APPENDED_ATOM; + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_map_open(yajl_gen g) +{ + ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; + INCREMENT_DEPTH; + + g->state[g->depth] = yajl_gen_map_start; + yajl_buf_append(g->buf, "{", 1); + if (g->pretty) yajl_buf_append(g->buf, "\n", 1); + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_map_close(yajl_gen g) +{ + ENSURE_VALID_STATE; + (g->depth)--; + if (g->pretty) yajl_buf_append(g->buf, "\n", 1); + APPENDED_ATOM; + INSERT_WHITESPACE; + yajl_buf_append(g->buf, "}", 1); + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_array_open(yajl_gen g) +{ + ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; + INCREMENT_DEPTH; + g->state[g->depth] = yajl_gen_array_start; + yajl_buf_append(g->buf, "[", 1); + if (g->pretty) yajl_buf_append(g->buf, "\n", 1); + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_array_close(yajl_gen g) +{ + ENSURE_VALID_STATE; + if (g->pretty) yajl_buf_append(g->buf, "\n", 1); + (g->depth)--; + APPENDED_ATOM; + INSERT_WHITESPACE; + yajl_buf_append(g->buf, "]", 1); + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_get_buf(yajl_gen g, const unsigned char ** buf, + unsigned int * len) +{ + *buf = yajl_buf_data(g->buf); + *len = yajl_buf_len(g->buf); + return yajl_gen_status_ok; +} + +void +yajl_gen_clear(yajl_gen g) +{ + yajl_buf_clear(g->buf); +} diff --git a/yajl/src/yajl_lex.c b/yajl/src/yajl_lex.c new file mode 100644 index 0000000..b9d495c --- /dev/null +++ b/yajl/src/yajl_lex.c @@ -0,0 +1,743 @@ +/* + * Copyright 2007, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "yajl/src/yajl_lex.h" +#include "yajl/src/yajl_buf.h" + +#include +#include +#include +#include + +#ifdef YAJL_LEXER_DEBUG +static const char * +tokToStr(yajl_tok tok) +{ + switch (tok) { + case yajl_tok_bool: return "bool"; + case yajl_tok_colon: return "colon"; + case yajl_tok_comma: return "comma"; + case yajl_tok_eof: return "eof"; + case yajl_tok_error: return "error"; + case yajl_tok_left_brace: return "brace"; + case yajl_tok_left_bracket: return "bracket"; + case yajl_tok_null: return "null"; + case yajl_tok_integer: return "integer"; + case yajl_tok_double: return "double"; + case yajl_tok_right_brace: return "brace"; + case yajl_tok_right_bracket: return "bracket"; + case yajl_tok_string: return "string"; + case yajl_tok_string_with_escapes: return "string_with_escapes"; + } + return "unknown"; +} +#endif + +/* Impact of the stream parsing feature on the lexer: + * + * YAJL support stream parsing. That is, the ability to parse the first + * bits of a chunk of JSON before the last bits are available (still on + * the network or disk). This makes the lexer more complex. The + * responsibility of the lexer is to handle transparently the case where + * a chunk boundary falls in the middle of a token. This is + * accomplished is via a buffer and a character reading abstraction. + * + * Overview of implementation + * + * When we lex to end of input string before end of token is hit, we + * copy all of the input text composing the token into our lexBuf. + * + * Every time we read a character, we do so through the readChar function. + * readChar's responsibility is to handle pulling all chars from the buffer + * before pulling chars from input text + */ + +struct yajl_lexer_t { + /* the overal line and char offset into the data */ + unsigned int lineOff; + unsigned int charOff; + + /* error */ + yajl_lex_error error; + + /* a input buffer to handle the case where a token is spread over + * multiple chunks */ + yajl_buf buf; + + /* in the case where we have data in the lexBuf, bufOff holds + * the current offset into the lexBuf. */ + unsigned int bufOff; + + /* are we using the lex buf? */ + unsigned int bufInUse; + + /* shall we allow comments? */ + unsigned int allowComments; + + /* shall we validate utf8 inside strings? */ + unsigned int validateUTF8; +}; + +static unsigned char +readChar(yajl_lexer lxr, const unsigned char * txt, unsigned int *off) +{ + if (lxr->bufInUse && yajl_buf_len(lxr->buf) && + lxr->bufOff < yajl_buf_len(lxr->buf)) + { + return *((unsigned char *) yajl_buf_data(lxr->buf) + (lxr->bufOff)++); + } + return txt[(*off)++]; +} + +static void +unreadChar(yajl_lexer lxr, unsigned int *off) +{ + if (*off > 0) (*off)--; + else (lxr->bufOff)--; +} + +yajl_lexer +yajl_lex_alloc(unsigned int allowComments, unsigned int validateUTF8) +{ + yajl_lexer lxr = (yajl_lexer) calloc(1, sizeof(struct yajl_lexer_t)); + lxr->buf = yajl_buf_alloc(); + lxr->allowComments = allowComments; + lxr->validateUTF8 = validateUTF8; + return lxr; +} + +void +yajl_lex_free(yajl_lexer lxr) +{ + yajl_buf_free(lxr->buf); + free(lxr); + return; +} + +/* a lookup table which lets us quickly determine three things: + * VEC - valid escaped conrol char + * IJC - invalid json char + * VHC - valid hex char + * note. the solidus '/' may be escaped or not. + * note. the + */ +#define VEC 1 +#define IJC 2 +#define VHC 4 +const static char charLookupTable[256] = +{ +/*00*/ IJC , IJC , IJC , IJC , IJC , IJC , IJC , IJC , +/*08*/ IJC , IJC , IJC , IJC , IJC , IJC , IJC , IJC , +/*10*/ IJC , IJC , IJC , IJC , IJC , IJC , IJC , IJC , +/*18*/ IJC , IJC , IJC , IJC , IJC , IJC , IJC , IJC , + +/*20*/ 0 , 0 , VEC|IJC, 0 , 0 , 0 , 0 , 0 , +/*28*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , VEC , +/*30*/ VHC , VHC , VHC , VHC , VHC , VHC , VHC , VHC , +/*38*/ VHC , VHC , 0 , 0 , 0 , 0 , 0 , 0 , + +/*40*/ 0 , VHC , VHC , VHC , VHC , VHC , VHC , 0 , +/*48*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , +/*50*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , +/*58*/ 0 , 0 , 0 , 0 , VEC|IJC, 0 , 0 , 0 , + +/*60*/ 0 , VHC , VEC|VHC, VHC , VHC , VHC , VEC|VHC, 0 , +/*68*/ 0 , 0 , 0 , 0 , 0 , 0 , VEC , 0 , +/*70*/ 0 , 0 , VEC , 0 , VEC , 0 , 0 , 0 , +/*78*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + +/* include these so we don't have to always check the range of the char */ + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 +}; + +/** process a variable length utf8 encoded codepoint. + * + * returns: + * yajl_tok_string - if valid utf8 char was parsed and offset was + * advanced + * yajl_tok_eof - if end of input was hit before validation could + * complete + * yajl_tok_error - if invalid utf8 was encountered + * + * NOTE: on error the offset will point to the first char of the + * invalid utf8 */ +#define UTF8_CHECK_EOF if (*offset >= jsonTextLen) { return yajl_tok_eof; } + +static yajl_tok +yajl_lex_utf8_char(yajl_lexer lexer, const unsigned char * jsonText, + unsigned int jsonTextLen, unsigned int * offset, + unsigned char curChar) +{ + if (curChar <= 0x7f) { + /* single byte */ + return yajl_tok_string; + } else if ((curChar >> 5) == 0x6) { + /* two byte */ + UTF8_CHECK_EOF; + curChar = readChar(lexer, jsonText, offset); + if ((curChar >> 6) == 0x2) return yajl_tok_string; + } else if ((curChar >> 4) == 0x0d) { + /* three byte */ + UTF8_CHECK_EOF; + curChar = readChar(lexer, jsonText, offset); + if ((curChar >> 6) == 0x2) { + UTF8_CHECK_EOF; + curChar = readChar(lexer, jsonText, offset); + if ((curChar >> 6) == 0x2) return yajl_tok_string; + } + } else if ((curChar >> 3) == 0x1d) { + /* four byte */ + UTF8_CHECK_EOF; + curChar = readChar(lexer, jsonText, offset); + if ((curChar >> 6) == 0x2) { + UTF8_CHECK_EOF; + curChar = readChar(lexer, jsonText, offset); + if ((curChar >> 6) == 0x2) { + UTF8_CHECK_EOF; + curChar = readChar(lexer, jsonText, offset); + if ((curChar >> 6) == 0x2) return yajl_tok_string; + } + } + } + + return yajl_tok_error; +} + +/* lex a string. input is the lexer, pointer to beginning of + * json text, and start of string (offset). + * a token is returned which has the following meanings: + * yajl_tok_string: lex of string was successful. offset points to + * terminating '"'. + * yajl_tok_eof: end of text was encountered before we could complete + * the lex. + * yajl_tok_error: embedded in the string were unallowable chars. offset + * points to the offending char + */ +#define STR_CHECK_EOF \ +if (*offset >= jsonTextLen) { \ + tok = yajl_tok_eof; \ + goto finish_string_lex; \ +} + +static yajl_tok +yajl_lex_string(yajl_lexer lexer, const unsigned char * jsonText, + unsigned int jsonTextLen, unsigned int * offset) +{ + yajl_tok tok = yajl_tok_error; + int hasEscapes = 0; + + for (;;) { + unsigned char curChar; + + STR_CHECK_EOF; + + curChar = readChar(lexer, jsonText, offset); + + /* quote terminates */ + if (curChar == '"') { + tok = yajl_tok_string; + break; + } + /* backslash escapes a set of control chars, */ + else if (curChar == '\\') { + hasEscapes = 1; + STR_CHECK_EOF; + + /* special case \u */ + curChar = readChar(lexer, jsonText, offset); + if (curChar == 'u') { + unsigned int i = 0; + + for (i=0;i<4;i++) { + STR_CHECK_EOF; + curChar = readChar(lexer, jsonText, offset); + if (!(charLookupTable[curChar] & VHC)) { + /* back up to offending char */ + unreadChar(lexer, offset); + lexer->error = yajl_lex_string_invalid_hex_char; + goto finish_string_lex; + } + } + } else if (!(charLookupTable[curChar] & VEC)) { + /* back up to offending char */ + unreadChar(lexer, offset); + lexer->error = yajl_lex_string_invalid_escaped_char; + goto finish_string_lex; + } + } + /* when not validating UTF8 it's a simple table lookup to determine + * if the present character is invalid */ + else if(charLookupTable[curChar] & IJC) { + /* back up to offending char */ + unreadChar(lexer, offset); + lexer->error = yajl_lex_string_invalid_json_char; + goto finish_string_lex; + } + /* when in validate UTF8 mode we need to do some extra work */ + else if (lexer->validateUTF8) { + yajl_tok t = yajl_lex_utf8_char(lexer, jsonText, jsonTextLen, + offset, curChar); + + if (t == yajl_tok_eof) { + tok = yajl_tok_eof; + goto finish_string_lex; + } else if (t == yajl_tok_error) { + lexer->error = yajl_lex_string_invalid_utf8; + goto finish_string_lex; + } + } + /* accept it, and move on */ + } + finish_string_lex: + /* tell our buddy, the parser, wether he needs to process this string + * again */ + if (hasEscapes && tok == yajl_tok_string) { + tok = yajl_tok_string_with_escapes; + } + + return tok; +} + +#define RETURN_IF_EOF if (*offset >= jsonTextLen) return yajl_tok_eof; + +static yajl_tok +yajl_lex_number(yajl_lexer lexer, const unsigned char * jsonText, + unsigned int jsonTextLen, unsigned int * offset) +{ + /** XXX: numbers are the only entities in json that we must lex + * _beyond_ in order to know that they are complete. There + * is an ambiguous case for integers at EOF. */ + + unsigned char c; + + yajl_tok tok = yajl_tok_integer; + + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + + /* optional leading minus */ + if (c == '-') { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + } + + /* a single zero, or a series of integers */ + if (c == '0') { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + } else if (c >= '1' && c <= '9') { + do { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + } while (c >= '0' && c <= '9'); + } else { + unreadChar(lexer, offset); + lexer->error = yajl_lex_missing_integer_after_minus; + return yajl_tok_error; + } + + /* optional fraction (indicates this is floating point) */ + if (c == '.') { + int numRd = 0; + + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + + while (c >= '0' && c <= '9') { + numRd++; + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + } + + if (!numRd) { + unreadChar(lexer, offset); + lexer->error = yajl_lex_missing_integer_after_decimal; + return yajl_tok_error; + } + tok = yajl_tok_double; + } + + /* optional exponent (indicates this is floating point) */ + if (c == 'e' || c == 'E') { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + + /* optional sign */ + if (c == '+' || c == '-') { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + } + + if (c >= '0' && c <= '9') { + do { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + } while (c >= '0' && c <= '9'); + } else { + unreadChar(lexer, offset); + lexer->error = yajl_lex_missing_integer_after_exponent; + return yajl_tok_error; + } + tok = yajl_tok_double; + } + + /* we always go "one too far" */ + unreadChar(lexer, offset); + + return tok; +} + +static yajl_tok +yajl_lex_comment(yajl_lexer lexer, const unsigned char * jsonText, + unsigned int jsonTextLen, unsigned int * offset) +{ + unsigned char c; + + yajl_tok tok = yajl_tok_comment; + + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + + /* either slash or star expected */ + if (c == '/') { + /* now we throw away until end of line */ + do { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + } while (c != '\n'); + } else if (c == '*') { + /* now we throw away until end of comment */ + for (;;) { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + if (c == '*') { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + if (c == '/') { + break; + } else { + unreadChar(lexer, offset); + } + } + } + } else { + lexer->error = yajl_lex_invalid_char; + tok = yajl_tok_error; + } + + return tok; +} + +yajl_tok +yajl_lex_lex(yajl_lexer lexer, const unsigned char * jsonText, + unsigned int jsonTextLen, unsigned int * context, + const unsigned char ** outBuf, unsigned int * outLen) +{ + yajl_tok tok = yajl_tok_error; + unsigned char c; + unsigned int startCtx = *context; + + *outBuf = NULL; + *outLen = 0; + + for (;;) { + assert(*context <= jsonTextLen); + + if (*context >= jsonTextLen) { + tok = yajl_tok_eof; + goto lexed; + } + + c = readChar(lexer, jsonText, context); + + switch (c) { + case '{': + tok = yajl_tok_left_bracket; + goto lexed; + case '}': + tok = yajl_tok_right_bracket; + goto lexed; + case '[': + tok = yajl_tok_left_brace; + goto lexed; + case ']': + tok = yajl_tok_right_brace; + goto lexed; + case ',': + tok = yajl_tok_comma; + goto lexed; + case ':': + tok = yajl_tok_colon; + goto lexed; + case '\t': case '\n': case '\v': case '\f': case '\r': case ' ': + startCtx++; + break; + case 't': { + const char * want = "rue"; + do { + if (*context >= jsonTextLen) { + tok = yajl_tok_eof; + goto lexed; + } + c = readChar(lexer, jsonText, context); + if (c != *want) { + unreadChar(lexer, context); + lexer->error = yajl_lex_invalid_string; + tok = yajl_tok_error; + goto lexed; + } + } while (*(++want)); + tok = yajl_tok_bool; + goto lexed; + } + case 'f': { + const char * want = "alse"; + do { + if (*context >= jsonTextLen) { + tok = yajl_tok_eof; + goto lexed; + } + c = readChar(lexer, jsonText, context); + if (c != *want) { + unreadChar(lexer, context); + lexer->error = yajl_lex_invalid_string; + tok = yajl_tok_error; + goto lexed; + } + } while (*(++want)); + tok = yajl_tok_bool; + goto lexed; + } + case 'n': { + const char * want = "ull"; + do { + if (*context >= jsonTextLen) { + tok = yajl_tok_eof; + goto lexed; + } + c = readChar(lexer, jsonText, context); + if (c != *want) { + unreadChar(lexer, context); + lexer->error = yajl_lex_invalid_string; + tok = yajl_tok_error; + goto lexed; + } + } while (*(++want)); + tok = yajl_tok_null; + goto lexed; + } + case '"': { + tok = yajl_lex_string(lexer, (unsigned char *) jsonText, + jsonTextLen, context); + goto lexed; + } + case '-': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': { + /* integer parsing wants to start from the beginning */ + unreadChar(lexer, context); + tok = yajl_lex_number(lexer, (unsigned char *) jsonText, + jsonTextLen, context); + goto lexed; + } + case '/': + /* hey, look, a probable comment! If comments are disabled + * it's an error. */ + if (!lexer->allowComments) { + unreadChar(lexer, context); + lexer->error = yajl_lex_unallowed_comment; + tok = yajl_tok_error; + goto lexed; + } + /* if comments are enabled, then we should try to lex + * the thing. possible outcomes are + * - successful lex (tok_comment, which means continue), + * - malformed comment opening (slash not followed by + * '*' or '/') (tok_error) + * - eof hit. (tok_eof) */ + tok = yajl_lex_comment(lexer, (unsigned char *) jsonText, + jsonTextLen, context); + if (tok == yajl_tok_comment) { + /* "error" is silly, but that's the initial + * state of tok. guilty until proven innocent. */ + tok = yajl_tok_error; + yajl_buf_clear(lexer->buf); + lexer->bufInUse = 0; + startCtx = *context; + break; + } + /* hit error or eof, bail */ + goto lexed; + default: + lexer->error = yajl_lex_invalid_char; + tok = yajl_tok_error; + goto lexed; + } + } + + + lexed: + /* need to append to buffer if the buffer is in use or + * if it's an EOF token */ + if (tok == yajl_tok_eof || lexer->bufInUse) { + if (!lexer->bufInUse) yajl_buf_clear(lexer->buf); + lexer->bufInUse = 1; + yajl_buf_append(lexer->buf, jsonText + startCtx, *context - startCtx); + lexer->bufOff = 0; + + if (tok != yajl_tok_eof) { + *outBuf = yajl_buf_data(lexer->buf); + *outLen = yajl_buf_len(lexer->buf); + lexer->bufInUse = 0; + } + } else if (tok != yajl_tok_error) { + *outBuf = jsonText + startCtx; + *outLen = *context - startCtx; + } + + /* special case for strings. skip the quotes. */ + if (tok == yajl_tok_string || tok == yajl_tok_string_with_escapes) + { + assert(*outLen >= 2); + (*outBuf)++; + *outLen -= 2; + } + + +#ifdef YAJL_LEXER_DEBUG + if (tok == yajl_tok_error) { + printf("lexical error: %s\n", + yajl_lex_error_to_string(yajl_lex_get_error(lexer))); + } else if (tok == yajl_tok_eof) { + printf("EOF hit\n"); + } else { + printf("lexed %s: '", tokToStr(tok)); + fwrite(*outBuf, 1, *outLen, stdout); + printf("'\n"); + } +#endif + + return tok; +} + +const char * +yajl_lex_error_to_string(yajl_lex_error error) +{ + switch (error) { + case yajl_lex_e_ok: + return "ok, no error"; + case yajl_lex_string_invalid_utf8: + return "invalid bytes in UTF8 string."; + case yajl_lex_string_invalid_escaped_char: + return "inside a string, '\\' occurs before a character " + "which it may not."; + case yajl_lex_string_invalid_json_char: + return "invalid character inside string."; + case yajl_lex_string_invalid_hex_char: + return "invalid (non-hex) character occurs after '\\u' inside " + "string."; + case yajl_lex_invalid_char: + return "invalid char in json text."; + case yajl_lex_invalid_string: + return "invalid string in json text."; + case yajl_lex_missing_integer_after_exponent: + return "malformed number, a digit is required after the exponent."; + case yajl_lex_missing_integer_after_decimal: + return "malformed number, a digit is required after the " + "decimal point."; + case yajl_lex_missing_integer_after_minus: + return "malformed number, a digit is required after the " + "minus sign."; + case yajl_lex_unallowed_comment: + return "probable comment found in input text, comments are " + "not enabled."; + } + return "unknown error code"; +} + + +/** allows access to more specific information about the lexical + * error when yajl_lex_lex returns yajl_tok_error. */ +yajl_lex_error +yajl_lex_get_error(yajl_lexer lexer) +{ + if (lexer == NULL) return (yajl_lex_error) -1; + return lexer->error; +} + +unsigned int yajl_lex_current_line(yajl_lexer lexer) +{ + return lexer->lineOff; +} + +unsigned int yajl_lex_current_char(yajl_lexer lexer) +{ + return lexer->charOff; +} + +yajl_tok yajl_lex_peek(yajl_lexer lexer, const unsigned char * jsonText, + unsigned int jsonTextLen, unsigned int offset) +{ + const unsigned char * outBuf; + unsigned int outLen; + unsigned int bufLen = yajl_buf_len(lexer->buf); + unsigned int bufOff = lexer->bufOff; + unsigned int bufInUse = lexer->bufInUse; + yajl_tok tok; + + tok = yajl_lex_lex(lexer, jsonText, jsonTextLen, &offset, + &outBuf, &outLen); + + lexer->bufOff = bufOff; + lexer->bufInUse = bufInUse; + yajl_buf_truncate(lexer->buf, bufLen); + + return tok; +} diff --git a/yajl/src/yajl_lex.h b/yajl/src/yajl_lex.h new file mode 100644 index 0000000..e84131a --- /dev/null +++ b/yajl/src/yajl_lex.h @@ -0,0 +1,130 @@ +/* + * Copyright 2007, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __YAJL_LEX_H__ +#define __YAJL_LEX_H__ + +typedef enum { + yajl_tok_bool, + yajl_tok_colon, + yajl_tok_comma, + yajl_tok_eof, + yajl_tok_error, + yajl_tok_left_brace, + yajl_tok_left_bracket, + yajl_tok_null, + yajl_tok_right_brace, + yajl_tok_right_bracket, + + /* we differentiate between integers and doubles to allow the + * parser to interpret the number without re-scanning */ + yajl_tok_integer, + yajl_tok_double, + + /* we differentiate between strings which require further processing, + * and strings that do not */ + yajl_tok_string, + yajl_tok_string_with_escapes, + + /* comment tokens are not currently returned to the parser, ever */ + yajl_tok_comment +} yajl_tok; + +typedef struct yajl_lexer_t * yajl_lexer; + +yajl_lexer yajl_lex_alloc(unsigned int allowComments, + unsigned int validateUTF8); + +void yajl_lex_free(yajl_lexer lexer); + +/** + * run/continue a lex. "offset" is an input/output parameter. + * It should be initialized to zero for a + * new chunk of target text, and upon subsetquent calls with the same + * target text should passed with the value of the previous invocation. + * + * the client may be interested in the value of offset when an error is + * returned from the lexer. This allows the client to render useful +n * error messages. + * + * When you pass the next chunk of data, context should be reinitialized + * to zero. + * + * Finally, the output buffer is usually just a pointer into the jsonText, + * however in cases where the entity being lexed spans multiple chunks, + * the lexer will buffer the entity and the data returned will be + * a pointer into that buffer. + * + * This behavior is abstracted from client code except for the performance + * implications which require that the client choose a reasonable chunk + * size to get adequate performance. + */ +yajl_tok yajl_lex_lex(yajl_lexer lexer, const unsigned char * jsonText, + unsigned int jsonTextLen, unsigned int * offset, + const unsigned char ** outBuf, unsigned int * outLen); + +/** have a peek at the next token, but don't move the lexer forward */ +yajl_tok yajl_lex_peek(yajl_lexer lexer, const unsigned char * jsonText, + unsigned int jsonTextLen, unsigned int offset); + + +typedef enum { + yajl_lex_e_ok = 0, + yajl_lex_string_invalid_utf8, + yajl_lex_string_invalid_escaped_char, + yajl_lex_string_invalid_json_char, + yajl_lex_string_invalid_hex_char, + yajl_lex_invalid_char, + yajl_lex_invalid_string, + yajl_lex_missing_integer_after_decimal, + yajl_lex_missing_integer_after_exponent, + yajl_lex_missing_integer_after_minus, + yajl_lex_unallowed_comment +} yajl_lex_error; + +const char * yajl_lex_error_to_string(yajl_lex_error error); + +/** allows access to more specific information about the lexical + * error when yajl_lex_lex returns yajl_tok_error. */ +yajl_lex_error yajl_lex_get_error(yajl_lexer lexer); + +/** get the current offset into the most recently lexed json string. */ +unsigned int yajl_lex_current_offset(yajl_lexer lexer); + +/** get the number of lines lexed by this lexer instance */ +unsigned int yajl_lex_current_line(yajl_lexer lexer); + +/** get the number of chars lexed by this lexer instance since the last + * \n or \r */ +unsigned int yajl_lex_current_char(yajl_lexer lexer); + +#endif diff --git a/yajl/src/yajl_parser.c b/yajl/src/yajl_parser.c new file mode 100644 index 0000000..dc8c5c7 --- /dev/null +++ b/yajl/src/yajl_parser.c @@ -0,0 +1,476 @@ +/* + * Copyright 2007, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "yajl/src/yajl_lex.h" +#include "yajl/src/yajl_parser.h" +#include "yajl/src/yajl_encode.h" +#include +#include +#include +#include +#include +#include +#include +#include + +unsigned char * +yajl_render_error_string(yajl_handle hand, const unsigned char * jsonText, + unsigned int jsonTextLen, int verbose) +{ + unsigned int offset = hand->errorOffset; + unsigned char * str; + const char * errorType = NULL; + const char * errorText = NULL; + char text[72]; + const char * arrow = " (right here) ------^\n"; + + if (yajl_state_current(hand) == yajl_state_parse_error) { + errorType = "parse"; + errorText = hand->parseError; + } else if (yajl_state_current(hand) == yajl_state_lexical_error) { + errorType = "lexical"; + errorText = yajl_lex_error_to_string(yajl_lex_get_error(hand->lexer)); + } else { + errorType = "unknown"; + } + + { + unsigned int memneeded = 0; + memneeded += strlen(errorType); + memneeded += strlen(" error"); + if (errorText != NULL) { + memneeded += strlen(": "); + memneeded += strlen(errorText); + } + str = (unsigned char *) malloc(memneeded + 2); + str[0] = 0; + strcat((char *) str, errorType); + strcat((char *) str, " error"); + if (errorText != NULL) { + strcat((char *) str, ": "); + strcat((char *) str, errorText); + } + strcat((char *) str, "\n"); + } + + /* now we append as many spaces as needed to make sure the error + * falls at char 41, if verbose was specified */ + if (verbose) { + unsigned int start, end, i; + unsigned int spacesNeeded; + + spacesNeeded = (offset < 30 ? 40 - offset : 10); + start = (offset >= 30 ? offset - 30 : 0); + end = (offset + 30 > jsonTextLen ? jsonTextLen : offset + 30); + + for (i=0;iparseError = \ + "client cancelled parse via callback return value"; \ + return yajl_status_client_canceled; \ + } + + +yajl_status +yajl_do_parse(yajl_handle hand, unsigned int * offset, + const unsigned char * jsonText, unsigned int jsonTextLen) +{ + yajl_tok tok; + const unsigned char * buf; + unsigned int bufLen; + + around_again: + switch (yajl_state_current(hand)) { + case yajl_state_parse_complete: + return yajl_status_ok; + case yajl_state_lexical_error: + case yajl_state_parse_error: + hand->errorOffset = *offset; + return yajl_status_error; + case yajl_state_start: + case yajl_state_map_need_val: + case yajl_state_array_need_val: + case yajl_state_array_start: { + /* for arrays and maps, we advance the state for this + * depth, then push the state of the next depth. + * If an error occurs during the parsing of the nesting + * enitity, the state at this level will not matter. + * a state that needs pushing will be anything other + * than state_start */ + yajl_state stateToPush = yajl_state_start; + + tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, + offset, &buf, &bufLen); + + switch (tok) { + case yajl_tok_eof: + return yajl_status_insufficient_data; + case yajl_tok_error: + yajl_state_set(hand, yajl_state_lexical_error); + goto around_again; + case yajl_tok_string: + if (hand->callbacks && hand->callbacks->yajl_string) { + _CC_CHK(hand->callbacks->yajl_string(hand->ctx, + buf, bufLen)); + } + break; + case yajl_tok_string_with_escapes: + if (hand->callbacks && hand->callbacks->yajl_string) { + yajl_buf_clear(hand->decodeBuf); + yajl_string_decode(hand->decodeBuf, buf, bufLen); + _CC_CHK(hand->callbacks->yajl_string( + hand->ctx, yajl_buf_data(hand->decodeBuf), + yajl_buf_len(hand->decodeBuf))); + } + break; + case yajl_tok_bool: + if (hand->callbacks && hand->callbacks->yajl_boolean) { + _CC_CHK(hand->callbacks->yajl_boolean(hand->ctx, + *buf == 't')); + } + break; + case yajl_tok_null: + if (hand->callbacks && hand->callbacks->yajl_null) { + _CC_CHK(hand->callbacks->yajl_null(hand->ctx)); + } + break; + case yajl_tok_left_bracket: + if (hand->callbacks && hand->callbacks->yajl_start_map) { + _CC_CHK(hand->callbacks->yajl_start_map(hand->ctx)); + } + stateToPush = yajl_state_map_start; + break; + case yajl_tok_left_brace: + if (hand->callbacks && hand->callbacks->yajl_start_array) { + _CC_CHK(hand->callbacks->yajl_start_array(hand->ctx)); + } + stateToPush = yajl_state_array_start; + break; + case yajl_tok_integer: + /* + * note. strtol does not respect the length of + * the lexical token. in a corner case where the + * lexed number is a integer with a trailing zero, + * immediately followed by the end of buffer, + * sscanf could run off into oblivion and cause a + * crash. for this reason we copy the integer + * (and doubles), into our parse buffer (the same + * one used for unescaping strings), before + * calling strtol. yajl_buf ensures null padding, + * so we're safe. + */ + if (hand->callbacks) { + if (hand->callbacks->yajl_number) { + _CC_CHK(hand->callbacks->yajl_number(hand->ctx, + (char *) buf, + bufLen)); + } else if (hand->callbacks->yajl_integer) { + long int i = 0; + yajl_buf_clear(hand->decodeBuf); + yajl_buf_append(hand->decodeBuf, buf, bufLen); + buf = yajl_buf_data(hand->decodeBuf); + i = strtol((char *) buf, NULL, 10); + if ((i == LONG_MIN || i == LONG_MAX) && + errno == ERANGE) + { + yajl_state_set(hand, yajl_state_parse_error); + hand->parseError = "integer overflow" ; + /* try to restore error offset */ + if (*offset >= bufLen) *offset -= bufLen; + else *offset = 0; + goto around_again; + } + _CC_CHK(hand->callbacks->yajl_integer(hand->ctx, + i)); + } + } + break; + case yajl_tok_double: + if (hand->callbacks) { + if (hand->callbacks->yajl_number) { + _CC_CHK(hand->callbacks->yajl_number(hand->ctx, + (char *) buf, + bufLen)); + } else if (hand->callbacks->yajl_double) { + double d = 0.0; + yajl_buf_clear(hand->decodeBuf); + yajl_buf_append(hand->decodeBuf, buf, bufLen); + buf = yajl_buf_data(hand->decodeBuf); + d = strtod((char *) buf, NULL); + if ((d == HUGE_VAL || d == -HUGE_VAL) && + errno == ERANGE) + { + yajl_state_set(hand, yajl_state_parse_error); + hand->parseError = "numeric (floating point) " + "overflow"; + /* try to restore error offset */ + if (*offset >= bufLen) *offset -= bufLen; + else *offset = 0; + goto around_again; + } + _CC_CHK(hand->callbacks->yajl_double(hand->ctx, + d)); + } + } + break; + case yajl_tok_right_brace: { + if (yajl_state_current(hand) == yajl_state_array_start) { + if (hand->callbacks && + hand->callbacks->yajl_end_array) + { + _CC_CHK(hand->callbacks->yajl_end_array(hand->ctx)); + } + (void) yajl_state_pop(hand); + goto around_again; + } + /* intentional fall-through */ + } + case yajl_tok_colon: + case yajl_tok_comma: + case yajl_tok_right_bracket: + yajl_state_set(hand, yajl_state_parse_error); + hand->parseError = + "unallowed token at this point in JSON text"; + goto around_again; + default: + yajl_state_set(hand, yajl_state_parse_error); + hand->parseError = "invalid token, internal error"; + goto around_again; + } + /* got a value. transition depends on the state we're in. */ + { + yajl_state s = yajl_state_current(hand); + if (s == yajl_state_start) { + yajl_state_set(hand, yajl_state_parse_complete); + } else if (s == yajl_state_map_need_val) { + yajl_state_set(hand, yajl_state_map_got_val); + } else { + yajl_state_set(hand, yajl_state_array_got_val); + } + } + if (stateToPush != yajl_state_start) { + yajl_state_push(hand, stateToPush); + } + + goto around_again; + } + case yajl_state_map_start: + case yajl_state_map_need_key: { + /* only difference between these two states is that in + * start '}' is valid, whereas in need_key, we've parsed + * a comma, and a string key _must_ follow */ + tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, + offset, &buf, &bufLen); + switch (tok) { + case yajl_tok_eof: + return yajl_status_insufficient_data; + case yajl_tok_error: + yajl_state_set(hand, yajl_state_lexical_error); + goto around_again; + case yajl_tok_string_with_escapes: + if (hand->callbacks && hand->callbacks->yajl_map_key) { + yajl_buf_clear(hand->decodeBuf); + yajl_string_decode(hand->decodeBuf, buf, bufLen); + buf = yajl_buf_data(hand->decodeBuf); + bufLen = yajl_buf_len(hand->decodeBuf); + } + /* intentional fall-through */ + case yajl_tok_string: + if (hand->callbacks && hand->callbacks->yajl_map_key) { + _CC_CHK(hand->callbacks->yajl_map_key(hand->ctx, buf, + bufLen)); + } + yajl_state_set(hand, yajl_state_map_sep); + goto around_again; + case yajl_tok_right_bracket: + if (yajl_state_current(hand) == yajl_state_map_start) { + if (hand->callbacks && hand->callbacks->yajl_end_map) { + _CC_CHK(hand->callbacks->yajl_end_map(hand->ctx)); + } + (void) yajl_state_pop(hand); + goto around_again; + } + default: + yajl_state_set(hand, yajl_state_parse_error); + hand->parseError = + "invalid object key (must be a string)"; + goto around_again; + } + } + case yajl_state_map_sep: { + tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, + offset, &buf, &bufLen); + switch (tok) { + case yajl_tok_colon: + yajl_state_set(hand, yajl_state_map_need_val); + goto around_again; + case yajl_tok_eof: + return yajl_status_insufficient_data; + case yajl_tok_error: + yajl_state_set(hand, yajl_state_lexical_error); + goto around_again; + default: + yajl_state_set(hand, yajl_state_parse_error); + hand->parseError = "object key and value must " + "be separated by a colon (':')"; + goto around_again; + } + } + case yajl_state_map_got_val: { + tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, + offset, &buf, &bufLen); + switch (tok) { + case yajl_tok_right_bracket: + if (hand->callbacks && hand->callbacks->yajl_end_map) { + _CC_CHK(hand->callbacks->yajl_end_map(hand->ctx)); + } + (void) yajl_state_pop(hand); + goto around_again; + case yajl_tok_comma: + yajl_state_set(hand, yajl_state_map_need_key); + goto around_again; + case yajl_tok_eof: + return yajl_status_insufficient_data; + case yajl_tok_error: + yajl_state_set(hand, yajl_state_lexical_error); + goto around_again; + default: + yajl_state_set(hand, yajl_state_parse_error); + hand->parseError = "after key and value, inside map, " + "I expect ',' or '}'"; + /* try to restore error offset */ + if (*offset >= bufLen) *offset -= bufLen; + else *offset = 0; + goto around_again; + } + } + case yajl_state_array_got_val: { + tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, + offset, &buf, &bufLen); + switch (tok) { + case yajl_tok_right_brace: + if (hand->callbacks && hand->callbacks->yajl_end_array) { + _CC_CHK(hand->callbacks->yajl_end_array(hand->ctx)); + } + (void) yajl_state_pop(hand); + goto around_again; + case yajl_tok_comma: + yajl_state_set(hand, yajl_state_array_need_val); + goto around_again; + case yajl_tok_eof: + return yajl_status_insufficient_data; + case yajl_tok_error: + yajl_state_set(hand, yajl_state_lexical_error); + goto around_again; + default: + yajl_state_set(hand, yajl_state_parse_error); + hand->parseError = + "after array element, I expect ',' or ']'"; + goto around_again; + } + } + } + + abort(); + return yajl_status_error; +} + +/* state stack maintenence routines */ +yajl_state +yajl_state_current(yajl_handle h) +{ + assert(yajl_buf_len(h->stateBuf) > 0); + return (yajl_state) *(yajl_buf_data(h->stateBuf) + + yajl_buf_len(h->stateBuf) - 1); +} + +void yajl_state_push(yajl_handle h, yajl_state s) +{ + unsigned char c = (unsigned char) s; + yajl_buf_append(h->stateBuf, &c, sizeof(c)); +} + +yajl_state yajl_state_pop(yajl_handle h) +{ + yajl_state s; + unsigned int len = yajl_buf_len(h->stateBuf); + /* start state is never popped */ + assert(len > 1); + s = (yajl_state) *(yajl_buf_data(h->stateBuf) + len - 1); + yajl_buf_truncate(h->stateBuf, len - 1); + return s; +} + +unsigned int yajl_parse_depth(yajl_handle h) +{ + assert(yajl_buf_len(h->stateBuf) > 0); + return (yajl_buf_len(h->stateBuf) - 1); +} + +void yajl_state_set(yajl_handle h, yajl_state state) +{ + assert(yajl_buf_len(h->stateBuf) > 0); + *(unsigned char *) (yajl_buf_data(h->stateBuf) + + yajl_buf_len(h->stateBuf) - 1) = (unsigned char) state; +} diff --git a/yajl/src/yajl_parser.h b/yajl/src/yajl_parser.h new file mode 100644 index 0000000..5b26b60 --- /dev/null +++ b/yajl/src/yajl_parser.h @@ -0,0 +1,84 @@ +/* + * Copyright 2007, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __YAJL_PARSER_H__ +#define __YAJL_PARSER_H__ + +#include "yajl/api/yajl_parse.h" +#include "yajl/src/yajl_buf.h" + +typedef enum { + yajl_state_start = 0, + yajl_state_parse_complete, + yajl_state_parse_error, + yajl_state_lexical_error, + yajl_state_map_start, + yajl_state_map_sep, + yajl_state_map_need_val, + yajl_state_map_got_val, + yajl_state_map_need_key, + yajl_state_array_start, + yajl_state_array_got_val, + yajl_state_array_need_val +} yajl_state; + +struct yajl_handle_t { + const yajl_callbacks * callbacks; + void * ctx; + yajl_lexer lexer; + const char * parseError; + unsigned int errorOffset; + /* temporary storage for decoded strings */ + yajl_buf decodeBuf; + /* a stack of states. access with yajl_state_XXX routines */ + yajl_buf stateBuf; +}; + +yajl_status +yajl_do_parse(yajl_handle handle, unsigned int * offset, + const unsigned char * jsonText, unsigned int jsonTextLen); + +unsigned char * +yajl_render_error_string(yajl_handle hand, const unsigned char * jsonText, + unsigned int jsonTextLen, int verbose); + +yajl_state yajl_state_current(yajl_handle handle); + +void yajl_state_push(yajl_handle handle, yajl_state state); + +yajl_state yajl_state_pop(yajl_handle handle); + +unsigned int yajl_parse_depth(yajl_handle handle); + +void yajl_state_set(yajl_handle handle, yajl_state state); + +#endif diff --git a/yajl/wrapper/Makefile b/yajl/wrapper/Makefile new file mode 100644 index 0000000..5e0ee1f --- /dev/null +++ b/yajl/wrapper/Makefile @@ -0,0 +1,39 @@ +CC=g++ +LD=$(CC) +CPP_INCS=-I../.. +OUTDIR= +LDFLAGS=-lpthread +BINARY=jsonbuilder_test + +# Debug is default + +ifdef REL +CPP_FLAGS=-DLOGGING +OUTDIR=release +OPTFLAGS=-O2 +else +CPP_FLAGS=-DLOGGING -DDEBUG -g +OUTDIR=debug +OPTFLAGS=-O0 +endif + +CPPFLAGS=$(OPTFLAGS) $(CPP_FLAGS) $(CPP_INCS) -fsigned-char + +MAINFILES=jsonbuilder_test jsonbuilder jsontypes +YAJLFILES=yajl yajl_buf yajl_encode yajl_lex yajl_parser + +ALLFILES=$(YAJLFILES) $(MAINFILES) + +all: $(OUTDIR)/$(BINARY) + +clean: + rm -f $(OUTDIR)/* + +$(OUTDIR)/$(BINARY): $(ALLFILES:%=$(OUTDIR)/%.o) + $(LD) $(LDFLAGS) -o $@ $^ + +$(MAINFILES:%=$(OUTDIR)/%.o): $(MAINFILES:%=%.cpp) + $(CC) $(CPPFLAGS) -c $(OnString((const char *)pch, cb); +} + +int yajl_string(void *ctx, const unsigned char *pch, unsigned int cb) { + JsonBuilder *builder = (JsonBuilder *)ctx; + return builder->OnString((const char *)pch, cb); +} + +int yajl_map_key(void *ctx, const unsigned char *pch, unsigned int cb) { + JsonBuilder *builder = (JsonBuilder *)ctx; + return builder->OnKey((const char *)pch, cb); +} + +int yajl_start_map(void *ctx) { + JsonBuilder *builder = (JsonBuilder *)ctx; + return builder->OnMapStart(); +} + +int yajl_end_map(void *ctx) { + JsonBuilder *builder = (JsonBuilder *)ctx; + return builder->OnMapEnd(); +} + +int yajl_start_array(void *ctx) { + JsonBuilder *builder = (JsonBuilder *)ctx; + return builder->OnArrayStart(); +} + +int yajl_end_array(void *ctx) { + JsonBuilder *builder = (JsonBuilder *)ctx; + return builder->OnArrayEnd(); +} + +static yajl_callbacks callbacks = { + yajl_null, + yajl_boolean, + NULL, + NULL, + yajl_number, + yajl_string, + yajl_start_map, + yajl_map_key, + yajl_end_map, + yajl_start_array, + yajl_end_array +}; + +// +// JsonBuilder implementation +// + +JsonBuilder::JsonBuilder() { + callback_ = NULL; + alloced_ = false; +} + +JsonBuilder::~JsonBuilder() { + Reset(); +} + +void JsonBuilder::Reset() { + while (stack_.size() > 0) { + JsonObject *obj = stack_[stack_.size() - 1]; + stack_.pop_back(); + delete obj; + } + if (alloced_) { + yajl_free(handle_); + alloced_ = false; + } +} + +bool JsonBuilder::Start(ArrayItemsCallback *callback) { + Reset(); + callback_ = callback; + yajl_parser_config cfg = { 0, 1 }; + handle_ = yajl_alloc(&callbacks, &cfg, this); + alloced_ = true; + return true; +} + +bool JsonBuilder::Update(const char *pch, int cb) { + yajl_status stat = yajl_parse(handle_, (const unsigned char *)pch, cb); + if (stat != yajl_status_insufficient_data && + stat != yajl_status_ok) { + return false; + } + return true; +} + +JsonObject *JsonBuilder::End() { + JsonObject *obj = NULL; + if (stack_.size() == 1) { + obj = stack_[0]; + stack_.pop_back(); + } + Reset(); + if (callback_ == NULL) { + return obj; + } + delete obj; + return NULL; +} + +json::JsonString *JsonBuilder::NewJsonString(const char *ach, int cb) { + char szT[1024]; + char *pchT = szT; + char *pchTerm = &szT[sizeof(szT) - 1]; + + const char *apszEnt[] = { + "&", "'", """, "<", ">" + }; + int acchEnt[] = { + 5, 6, 6, 4, 4 + }; + char achRep[] = { + '&', '\'', '\"', '<', '>' + }; + + for (int i = 0; i < cb && pchT < pchTerm; i++) { + char ch = ach[i]; + if (ch == '&') { + bool fReplaced = false; + for (int n = 0; n < ARRAYSIZE(apszEnt); n++) { + if (strncmp(&ach[i], apszEnt[n], acchEnt[n]) == 0) { + *pchT++ = achRep[n]; + i += acchEnt[n] - 1; + fReplaced = true; + break; + } + } + if (fReplaced) { + continue; + } + } + + *pchT++ = ch; + } + *pchT = 0; + return new JsonString(szT); +} + +int JsonBuilder::OnString(const char *pch, int cb) { + CombineItem(NewJsonString(pch, cb)); + return 1; +} + +int JsonBuilder::OnKey(const char *pch, int cb) { + int last = (int)stack_.size() - 1; + if (last < 0 || stack_[last]->type() != JSONTYPE_MAP) { + return 0; + } + stack_.push_back(NewJsonString(pch, cb)); + return 1; +} + +int JsonBuilder::OnMapStart() { + stack_.push_back(new JsonMap()); + return 1; +} + +int JsonBuilder::OnMapEnd() { + int last = (int)stack_.size() - 1; + if (last < 0 || stack_[last]->type() != JSONTYPE_MAP) { + return 0; + } + JsonObject *obj = stack_[last]; + stack_.pop_back(); + CombineItem(obj); + return 1; +} + +int JsonBuilder::OnArrayStart() { + stack_.push_back(new JsonArray()); + return 1; +} + +int JsonBuilder::OnArrayEnd() { + int last = (int)stack_.size() - 1; + if (last < 0 || stack_[last]->type() != JSONTYPE_ARRAY) { + return 0; + } + JsonObject *obj = stack_[last]; + stack_.pop_back(); + + if (stack_.size() == 1 && stack_[0]->type() == JSONTYPE_ARRAY && + callback_ != NULL) { + callback_->OnObject(obj); + } else { + CombineItem(obj); + } + return 1; +} + +int JsonBuilder::CombineItem(JsonObject *obj) { + int last = (int)stack_.size() - 1; + if (last >= 0 && stack_[last]->type() == JSONTYPE_ARRAY) { + JsonArray *array = (JsonArray *)stack_[last]; + array->AddObject(obj); + return 1; + } + if (last >= 1 && stack_[last]->type() == JSONTYPE_STRING && + stack_[last - 1]->type() == JSONTYPE_MAP) { + JsonMap *map = (JsonMap *)stack_[last - 1]; + JsonString *key = (JsonString *)stack_[last]; + map->SetObject(key->GetString(), obj); + delete key; + stack_.pop_back(); + return 1; + } + stack_.push_back(obj); + return 1; +} + +} // namespace json diff --git a/yajl/wrapper/jsonbuilder.h b/yajl/wrapper/jsonbuilder.h new file mode 100644 index 0000000..97b8d70 --- /dev/null +++ b/yajl/wrapper/jsonbuilder.h @@ -0,0 +1,43 @@ +#ifndef __JSONBUILDER_H__ +#define __JSONBUILDER_H__ + +#include "inc/basictypes.h" +#include "yajl/api/yajl_parse.h" +#include "yajl/wrapper/jsontypes.h" + +namespace json { + +class JsonBuilder { +public: + JsonBuilder(); + ~JsonBuilder(); + + class ArrayItemsCallback { + public: + virtual void OnObject(JsonObject *obj) = 0; + }; + bool Start(ArrayItemsCallback *callback = NULL); + bool Update(const char *pch, int cb); + JsonObject *End(); + void Reset(); + + int OnString(const char *pch, int cb); + int OnKey(const char *pch, int cb); + int OnMapStart(); + int OnMapEnd(); + int OnArrayStart(); + int OnArrayEnd(); + +private: + int CombineItem(JsonObject *obj); + json::JsonString *NewJsonString(const char *ach, int cb); + + ArrayItemsCallback *callback_; + yajl_handle handle_; + bool alloced_; + std::vector stack_; +}; + +} // namespace json + +#endif // __JSONBUILDER_H__ diff --git a/yajl/wrapper/jsonbuilder_test.cpp b/yajl/wrapper/jsonbuilder_test.cpp new file mode 100644 index 0000000..3870efe --- /dev/null +++ b/yajl/wrapper/jsonbuilder_test.cpp @@ -0,0 +1,74 @@ +#include "yajl/wrapper/jsonbuilder.h" +#include "base/log.h" +#include + +class JsonBuilderTest : json::JsonBuilder::ArrayItemsCallback { +public: + bool DoTest(bool fCallback, const char *filename) { + json::JsonBuilder builder; + builder.Start(fCallback ? this : NULL); + + FILE *fd = fopen(filename, "rb"); + if (fd == NULL) { + return false; + } + char ach[32]; + while (true) { + size_t cb = fread(ach, 1, sizeof(ach), fd); + if (cb == 0) { + if (ferror(fd)) { + fclose(fd); + return false; + } + if (feof(fd)) { + break; + } + } + builder.Update(ach, cb); + } + json::JsonObject *obj = builder.End(); + if (obj != NULL) { + LOG() << "Object returned from JsonBuilder::End()"; + DumpObject(obj); + delete obj; + } + fclose(fd); + return true; + } + +private: + void OnObject(const json::JsonObject *obj) { + LOG() << "OnObject called with object"; + DumpObject(obj); + delete obj; + } + + void DumpObject(const json::JsonObject *obj) { + std::string s = obj->ToString(); + LOG() << base::Log::Format("Object Dump: %s", s.c_str()); + } +}; + +int main(int argc, char **argv) { + if (argc != 3) { + LOG() << base::Log::Format("usage: %s <0|1> ", argv[0]); + exit(1); + } + bool fSuccess = false; + JsonBuilderTest test; + if (strcmp(argv[1], "0") == 0) { + LOG() << base::Log::Format("Test no callback on %s", argv[2]); + fSuccess = test.DoTest(false, argv[2]); + } + if (strcmp(argv[1], "1") == 0) { + LOG() << base::Log::Format("Test callback on %s\n", argv[2]); + fSuccess = test.DoTest(true, argv[2]); + } + if (fSuccess) { + LOG() << "tests successful."; + return 0; + } else { + LOG() << "tests failed."; + return 1; + } +} diff --git a/yajl/wrapper/jsontypes.cpp b/yajl/wrapper/jsontypes.cpp new file mode 100644 index 0000000..b9a0e45 --- /dev/null +++ b/yajl/wrapper/jsontypes.cpp @@ -0,0 +1,146 @@ +#include "yajl/wrapper/jsontypes.h" + +namespace json { + +// +// JsonObject +// + +JsonObject::~JsonObject() { +} + +#ifdef RELEASE_LOGGING +std::string JsonObject::ToString() const { + return base::Log::Format("JsonObject <0x%08lx>", this); +} +#endif + +// +// JsonString +// + +JsonString::~JsonString() { +} + +JsonString::JsonString(const char *pch, int cb) : JsonObject(JSONTYPE_STRING) { + if (cb < 0) { + s_ = std::string(pch); + } else { + s_ = std::string(pch, cb); + } +} + +int JsonString::GetLength() const { + return s_.length(); +} + +const char *JsonString::GetString() const { + return s_.c_str(); +} + +#ifdef RELEASE_LOGGING +std::string JsonString::ToString() const { + return base::Log::Format("JsonString: %s", s_.c_str()); +} +#endif + +// +// JsonMap +// + +JsonMap::~JsonMap() { + while (map_.size() != 0) { + KeyMap::iterator it = map_.begin(); + delete it->second; + map_.erase(it); + } +} + +const JsonObject *JsonMap::GetObject(const char *key) const { + KeyMap::const_iterator it = map_.find(key); + if (it == map_.end()) { + return NULL; + } + return it->second; +} + +void JsonMap::SetObject(const char *key, JsonObject *obj) { + // Erase old key of this value, if it exists + KeyMap::iterator it = map_.find(key); + if (it != map_.end()) { + delete it->second; + map_.erase(it); + } + + // Insert new + map_.insert(KeyMap::value_type(key, obj)); +} + +const char *JsonMap::EnumKeys(wi::Enum *penm) const { + if (penm->m_dwUser == wi::kEnmFirst) { + penm->m_dwUser = 0; + } + KeyMap::const_iterator it = map_.begin(); + for (int i = 0; it != map_.end(); i++, it++) { + if (penm->m_dwUser == i) { + penm->m_dwUser++; + return it->first.c_str(); + } + } + return NULL; +} + +#ifdef RELEASE_LOGGING +std::string JsonMap::ToString() const { + std::string s = "JsonMap: { "; + wi::Enum enm; + const char *key; + while ((key = EnumKeys(&enm)) != NULL) { + const JsonObject *obj = GetObject(key); + s += base::Log::Format("%s : %s, ", key, obj->ToString().c_str()); + } + s += "}"; + return s; +} +#endif + +// +// JsonArray +// + +JsonArray::~JsonArray() { + while (list_.size() != 0) { + delete list_[list_.size() - 1]; + list_.pop_back(); + } +} + +int JsonArray::GetCount() const { + return (int)list_.size(); +} + +const JsonObject *JsonArray::GetObject(int i) const { + if (i < 0 || i > (int)list_.size()) { + return NULL; + } + return list_[i]; +} + +bool JsonArray::AddObject(const JsonObject *obj) { + list_.push_back(obj); + return true; +} + +#ifdef RELEASE_LOGGING +std::string JsonArray::ToString() const { + std::string s = "JsonArray: [ "; + for (int i = 0; i < GetCount(); i++) { + const JsonObject *obj = GetObject(i); + s += base::Log::Format("%s, ", obj->ToString().c_str()); + } + s += "]"; + return s; +} +#endif + +} // namespace json diff --git a/yajl/wrapper/jsontypes.h b/yajl/wrapper/jsontypes.h new file mode 100644 index 0000000..19bc8de --- /dev/null +++ b/yajl/wrapper/jsontypes.h @@ -0,0 +1,85 @@ +#ifndef __JSONTYPES_H__ +#define __JSONTYPES_H__ + +#include "inc/basictypes.h" +#include "mpshared/enum.h" +#include "base/log.h" +#include +#include +#include + +namespace json { + +enum JsonType { + JSONTYPE_OBJECT, JSONTYPE_MAP, JSONTYPE_ARRAY, JSONTYPE_STRING +}; + +class JsonObject { +public: + JsonObject(JsonType type) : type_(type) {} + virtual ~JsonObject(); + int type() const { return type_; } + +#ifdef RELEASE_LOGGING + virtual std::string ToString() const; +#endif + +protected: + JsonType type_; +}; + +class JsonString : public JsonObject { +public: + JsonString(const char *pch, int cb = -1); + virtual ~JsonString(); + + int GetLength() const; + const char *GetString() const; + +#ifdef RELEASE_LOGGING + virtual std::string ToString() const; +#endif + +private: + std::string s_; +}; + +class JsonMap : public JsonObject { +public: + JsonMap() : JsonObject(JSONTYPE_MAP) {} + virtual ~JsonMap(); + + const JsonObject *GetObject(const char *key) const; + void SetObject(const char *key, JsonObject *obj); + const char *EnumKeys(wi::Enum *penm) const; + +#ifdef RELEASE_LOGGING + virtual std::string ToString() const; +#endif + +private: + typedef std::map KeyMap; + KeyMap map_; +}; + +class JsonArray : public JsonObject { +public: + JsonArray() : JsonObject(JSONTYPE_ARRAY) {} + virtual ~JsonArray(); + + int GetCount() const; + const JsonObject *GetObject(int i) const; + bool AddObject(const JsonObject *obj); + +#ifdef RELEASE_LOGGING + virtual std::string ToString() const; +#endif + +private: + typedef std::vector ObjList; + ObjList list_; +}; + +} // namespace json + +#endif // __JSONTYPES_H__ diff --git a/yajl/yajl-0.4.0.tgz b/yajl/yajl-0.4.0.tgz new file mode 100644 index 0000000..59a4072 Binary files /dev/null and b/yajl/yajl-0.4.0.tgz differ